ApproximateTerrainHeights.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import BoundingSphere from './BoundingSphere.js';
  2. import buildModuleUrl from './buildModuleUrl.js';
  3. import Cartesian2 from './Cartesian2.js';
  4. import Cartesian3 from './Cartesian3.js';
  5. import Cartographic from './Cartographic.js';
  6. import Check from './Check.js';
  7. import defaultValue from './defaultValue.js';
  8. import defined from './defined.js';
  9. import defineProperties from './defineProperties.js';
  10. import DeveloperError from './DeveloperError.js';
  11. import Ellipsoid from './Ellipsoid.js';
  12. import GeographicTilingScheme from './GeographicTilingScheme.js';
  13. import Rectangle from './Rectangle.js';
  14. import Resource from './Resource.js';
  15. var scratchDiagonalCartesianNE = new Cartesian3();
  16. var scratchDiagonalCartesianSW = new Cartesian3();
  17. var scratchDiagonalCartographic = new Cartographic();
  18. var scratchCenterCartesian = new Cartesian3();
  19. var scratchSurfaceCartesian = new Cartesian3();
  20. var scratchBoundingSphere = new BoundingSphere();
  21. var tilingScheme = new GeographicTilingScheme();
  22. var scratchCorners = [new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic()];
  23. var scratchTileXY = new Cartesian2();
  24. /**
  25. * A collection of functions for approximating terrain height
  26. * @private
  27. */
  28. var ApproximateTerrainHeights = {};
  29. /**
  30. * Initializes the minimum and maximum terrain heights
  31. * @return {Promise}
  32. */
  33. ApproximateTerrainHeights.initialize = function() {
  34. var initPromise = ApproximateTerrainHeights._initPromise;
  35. if (defined(initPromise)) {
  36. return initPromise;
  37. }
  38. initPromise = Resource.fetchJson(buildModuleUrl('Assets/approximateTerrainHeights.json'))
  39. .then(function(json) {
  40. ApproximateTerrainHeights._terrainHeights = json;
  41. });
  42. ApproximateTerrainHeights._initPromise = initPromise;
  43. return initPromise;
  44. };
  45. /**
  46. * Computes the minimum and maximum terrain heights for a given rectangle
  47. * @param {Rectangle} rectangle The bounding rectangle
  48. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
  49. * @return {{minimumTerrainHeight: Number, maximumTerrainHeight: Number}}
  50. */
  51. ApproximateTerrainHeights.getMinimumMaximumHeights = function(rectangle, ellipsoid) {
  52. //>>includeStart('debug', pragmas.debug);
  53. Check.defined('rectangle', rectangle);
  54. if (!defined(ApproximateTerrainHeights._terrainHeights)) {
  55. throw new DeveloperError('You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function');
  56. }
  57. //>>includeEnd('debug');
  58. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  59. var xyLevel = getTileXYLevel(rectangle);
  60. // Get the terrain min/max for that tile
  61. var minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
  62. var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
  63. if (defined(xyLevel)) {
  64. var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
  65. var heights = ApproximateTerrainHeights._terrainHeights[key];
  66. if (defined(heights)) {
  67. minTerrainHeight = heights[0];
  68. maxTerrainHeight = heights[1];
  69. }
  70. // Compute min by taking the center of the NE->SW diagonal and finding distance to the surface
  71. ellipsoid.cartographicToCartesian(Rectangle.northeast(rectangle, scratchDiagonalCartographic),
  72. scratchDiagonalCartesianNE);
  73. ellipsoid.cartographicToCartesian(Rectangle.southwest(rectangle, scratchDiagonalCartographic),
  74. scratchDiagonalCartesianSW);
  75. Cartesian3.midpoint(scratchDiagonalCartesianSW, scratchDiagonalCartesianNE, scratchCenterCartesian);
  76. var surfacePosition = ellipsoid.scaleToGeodeticSurface(scratchCenterCartesian, scratchSurfaceCartesian);
  77. if (defined(surfacePosition)) {
  78. var distance = Cartesian3.distance(scratchCenterCartesian, surfacePosition);
  79. minTerrainHeight = Math.min(minTerrainHeight, -distance);
  80. } else {
  81. minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
  82. }
  83. }
  84. minTerrainHeight = Math.max(ApproximateTerrainHeights._defaultMinTerrainHeight, minTerrainHeight);
  85. return {
  86. minimumTerrainHeight: minTerrainHeight,
  87. maximumTerrainHeight: maxTerrainHeight
  88. };
  89. };
  90. /**
  91. * Computes the bounding sphere based on the tile heights in the rectangle
  92. * @param {Rectangle} rectangle The bounding rectangle
  93. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid
  94. * @return {BoundingSphere} The result bounding sphere
  95. */
  96. ApproximateTerrainHeights.getBoundingSphere = function(rectangle, ellipsoid) {
  97. //>>includeStart('debug', pragmas.debug);
  98. Check.defined('rectangle', rectangle);
  99. if (!defined(ApproximateTerrainHeights._terrainHeights)) {
  100. throw new DeveloperError('You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function');
  101. }
  102. //>>includeEnd('debug');
  103. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  104. var xyLevel = getTileXYLevel(rectangle);
  105. // Get the terrain max for that tile
  106. var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
  107. if (defined(xyLevel)) {
  108. var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y;
  109. var heights = ApproximateTerrainHeights._terrainHeights[key];
  110. if (defined(heights)) {
  111. maxTerrainHeight = heights[1];
  112. }
  113. }
  114. var result = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0);
  115. BoundingSphere.fromRectangle3D(rectangle, ellipsoid, maxTerrainHeight, scratchBoundingSphere);
  116. return BoundingSphere.union(result, scratchBoundingSphere, result);
  117. };
  118. function getTileXYLevel(rectangle) {
  119. Cartographic.fromRadians(rectangle.east, rectangle.north, 0.0, scratchCorners[0]);
  120. Cartographic.fromRadians(rectangle.west, rectangle.north, 0.0, scratchCorners[1]);
  121. Cartographic.fromRadians(rectangle.east, rectangle.south, 0.0, scratchCorners[2]);
  122. Cartographic.fromRadians(rectangle.west, rectangle.south, 0.0, scratchCorners[3]);
  123. // Determine which tile the bounding rectangle is in
  124. var lastLevelX = 0, lastLevelY = 0;
  125. var currentX = 0, currentY = 0;
  126. var maxLevel = ApproximateTerrainHeights._terrainHeightsMaxLevel;
  127. var i;
  128. for(i = 0; i <= maxLevel; ++i) {
  129. var failed = false;
  130. for(var j = 0; j < 4; ++j) {
  131. var corner = scratchCorners[j];
  132. tilingScheme.positionToTileXY(corner, i, scratchTileXY);
  133. if (j === 0) {
  134. currentX = scratchTileXY.x;
  135. currentY = scratchTileXY.y;
  136. } else if(currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) {
  137. failed = true;
  138. break;
  139. }
  140. }
  141. if (failed) {
  142. break;
  143. }
  144. lastLevelX = currentX;
  145. lastLevelY = currentY;
  146. }
  147. if (i === 0) {
  148. return undefined;
  149. }
  150. return {
  151. x : lastLevelX,
  152. y : lastLevelY,
  153. level : (i > maxLevel) ? maxLevel : (i - 1)
  154. };
  155. }
  156. ApproximateTerrainHeights._terrainHeightsMaxLevel = 6;
  157. ApproximateTerrainHeights._defaultMaxTerrainHeight = 9000.0;
  158. ApproximateTerrainHeights._defaultMinTerrainHeight = -100000.0;
  159. ApproximateTerrainHeights._terrainHeights = undefined;
  160. ApproximateTerrainHeights._initPromise = undefined;
  161. defineProperties(ApproximateTerrainHeights, {
  162. /**
  163. * Determines if the terrain heights are initialized and ready to use. To initialize the terrain heights,
  164. * call {@link ApproximateTerrainHeights#initialize} and wait for the returned promise to resolve.
  165. * @type {Boolean}
  166. * @readonly
  167. * @memberof ApproximateTerrainHeights
  168. */
  169. initialized: {
  170. get: function() {
  171. return defined(ApproximateTerrainHeights._terrainHeights);
  172. }
  173. }
  174. });
  175. export default ApproximateTerrainHeights;