import BoundingSphere from './BoundingSphere.js'; import buildModuleUrl from './buildModuleUrl.js'; import Cartesian2 from './Cartesian2.js'; import Cartesian3 from './Cartesian3.js'; import Cartographic from './Cartographic.js'; import Check from './Check.js'; import defaultValue from './defaultValue.js'; import defined from './defined.js'; import defineProperties from './defineProperties.js'; import DeveloperError from './DeveloperError.js'; import Ellipsoid from './Ellipsoid.js'; import GeographicTilingScheme from './GeographicTilingScheme.js'; import Rectangle from './Rectangle.js'; import Resource from './Resource.js'; var scratchDiagonalCartesianNE = new Cartesian3(); var scratchDiagonalCartesianSW = new Cartesian3(); var scratchDiagonalCartographic = new Cartographic(); var scratchCenterCartesian = new Cartesian3(); var scratchSurfaceCartesian = new Cartesian3(); var scratchBoundingSphere = new BoundingSphere(); var tilingScheme = new GeographicTilingScheme(); var scratchCorners = [new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic()]; var scratchTileXY = new Cartesian2(); /** * A collection of functions for approximating terrain height * @private */ var ApproximateTerrainHeights = {}; /** * Initializes the minimum and maximum terrain heights * @return {Promise} */ ApproximateTerrainHeights.initialize = function() { var initPromise = ApproximateTerrainHeights._initPromise; if (defined(initPromise)) { return initPromise; } initPromise = Resource.fetchJson(buildModuleUrl('Assets/approximateTerrainHeights.json')) .then(function(json) { ApproximateTerrainHeights._terrainHeights = json; }); ApproximateTerrainHeights._initPromise = initPromise; return initPromise; }; /** * Computes the minimum and maximum terrain heights for a given rectangle * @param {Rectangle} rectangle The bounding rectangle * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid * @return {{minimumTerrainHeight: Number, maximumTerrainHeight: Number}} */ ApproximateTerrainHeights.getMinimumMaximumHeights = function(rectangle, ellipsoid) { //>>includeStart('debug', pragmas.debug); Check.defined('rectangle', rectangle); if (!defined(ApproximateTerrainHeights._terrainHeights)) { throw new DeveloperError('You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function'); } //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var xyLevel = getTileXYLevel(rectangle); // Get the terrain min/max for that tile var minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight; var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight; if (defined(xyLevel)) { var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y; var heights = ApproximateTerrainHeights._terrainHeights[key]; if (defined(heights)) { minTerrainHeight = heights[0]; maxTerrainHeight = heights[1]; } // Compute min by taking the center of the NE->SW diagonal and finding distance to the surface ellipsoid.cartographicToCartesian(Rectangle.northeast(rectangle, scratchDiagonalCartographic), scratchDiagonalCartesianNE); ellipsoid.cartographicToCartesian(Rectangle.southwest(rectangle, scratchDiagonalCartographic), scratchDiagonalCartesianSW); Cartesian3.midpoint(scratchDiagonalCartesianSW, scratchDiagonalCartesianNE, scratchCenterCartesian); var surfacePosition = ellipsoid.scaleToGeodeticSurface(scratchCenterCartesian, scratchSurfaceCartesian); if (defined(surfacePosition)) { var distance = Cartesian3.distance(scratchCenterCartesian, surfacePosition); minTerrainHeight = Math.min(minTerrainHeight, -distance); } else { minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight; } } minTerrainHeight = Math.max(ApproximateTerrainHeights._defaultMinTerrainHeight, minTerrainHeight); return { minimumTerrainHeight: minTerrainHeight, maximumTerrainHeight: maxTerrainHeight }; }; /** * Computes the bounding sphere based on the tile heights in the rectangle * @param {Rectangle} rectangle The bounding rectangle * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid * @return {BoundingSphere} The result bounding sphere */ ApproximateTerrainHeights.getBoundingSphere = function(rectangle, ellipsoid) { //>>includeStart('debug', pragmas.debug); Check.defined('rectangle', rectangle); if (!defined(ApproximateTerrainHeights._terrainHeights)) { throw new DeveloperError('You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function'); } //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var xyLevel = getTileXYLevel(rectangle); // Get the terrain max for that tile var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight; if (defined(xyLevel)) { var key = xyLevel.level + '-' + xyLevel.x + '-' + xyLevel.y; var heights = ApproximateTerrainHeights._terrainHeights[key]; if (defined(heights)) { maxTerrainHeight = heights[1]; } } var result = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0); BoundingSphere.fromRectangle3D(rectangle, ellipsoid, maxTerrainHeight, scratchBoundingSphere); return BoundingSphere.union(result, scratchBoundingSphere, result); }; function getTileXYLevel(rectangle) { Cartographic.fromRadians(rectangle.east, rectangle.north, 0.0, scratchCorners[0]); Cartographic.fromRadians(rectangle.west, rectangle.north, 0.0, scratchCorners[1]); Cartographic.fromRadians(rectangle.east, rectangle.south, 0.0, scratchCorners[2]); Cartographic.fromRadians(rectangle.west, rectangle.south, 0.0, scratchCorners[3]); // Determine which tile the bounding rectangle is in var lastLevelX = 0, lastLevelY = 0; var currentX = 0, currentY = 0; var maxLevel = ApproximateTerrainHeights._terrainHeightsMaxLevel; var i; for(i = 0; i <= maxLevel; ++i) { var failed = false; for(var j = 0; j < 4; ++j) { var corner = scratchCorners[j]; tilingScheme.positionToTileXY(corner, i, scratchTileXY); if (j === 0) { currentX = scratchTileXY.x; currentY = scratchTileXY.y; } else if(currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) { failed = true; break; } } if (failed) { break; } lastLevelX = currentX; lastLevelY = currentY; } if (i === 0) { return undefined; } return { x : lastLevelX, y : lastLevelY, level : (i > maxLevel) ? maxLevel : (i - 1) }; } ApproximateTerrainHeights._terrainHeightsMaxLevel = 6; ApproximateTerrainHeights._defaultMaxTerrainHeight = 9000.0; ApproximateTerrainHeights._defaultMinTerrainHeight = -100000.0; ApproximateTerrainHeights._terrainHeights = undefined; ApproximateTerrainHeights._initPromise = undefined; defineProperties(ApproximateTerrainHeights, { /** * Determines if the terrain heights are initialized and ready to use. To initialize the terrain heights, * call {@link ApproximateTerrainHeights#initialize} and wait for the returned promise to resolve. * @type {Boolean} * @readonly * @memberof ApproximateTerrainHeights */ initialized: { get: function() { return defined(ApproximateTerrainHeights._terrainHeights); } } }); export default ApproximateTerrainHeights;