| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 | 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 DeveloperError from "./DeveloperError.js";import Ellipsoid from "./Ellipsoid.js";import GeographicTilingScheme from "./GeographicTilingScheme.js";import Rectangle from "./Rectangle.js";import Resource from "./Resource.js";const scratchDiagonalCartesianNE = new Cartesian3();const scratchDiagonalCartesianSW = new Cartesian3();const scratchDiagonalCartographic = new Cartographic();const scratchCenterCartesian = new Cartesian3();const scratchSurfaceCartesian = new Cartesian3();const scratchBoundingSphere = new BoundingSphere();const tilingScheme = new GeographicTilingScheme();const scratchCorners = [  new Cartographic(),  new Cartographic(),  new Cartographic(),  new Cartographic(),];const scratchTileXY = new Cartesian2();/** * A collection of functions for approximating terrain height * @private */const ApproximateTerrainHeights = {};/** * Initializes the minimum and maximum terrain heights * @return {Promise.<void>} */ApproximateTerrainHeights.initialize = function () {  let 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);  const xyLevel = getTileXYLevel(rectangle);  // Get the terrain min/max for that tile  let minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;  let maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;  if (defined(xyLevel)) {    const key = `${xyLevel.level}-${xyLevel.x}-${xyLevel.y}`;    const 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    );    const surfacePosition = ellipsoid.scaleToGeodeticSurface(      scratchCenterCartesian,      scratchSurfaceCartesian    );    if (defined(surfacePosition)) {      const 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);  const xyLevel = getTileXYLevel(rectangle);  // Get the terrain max for that tile  let maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;  if (defined(xyLevel)) {    const key = `${xyLevel.level}-${xyLevel.x}-${xyLevel.y}`;    const heights = ApproximateTerrainHeights._terrainHeights[key];    if (defined(heights)) {      maxTerrainHeight = heights[1];    }  }  const 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  let lastLevelX = 0,    lastLevelY = 0;  let currentX = 0,    currentY = 0;  const maxLevel = ApproximateTerrainHeights._terrainHeightsMaxLevel;  let i;  for (i = 0; i <= maxLevel; ++i) {    let failed = false;    for (let j = 0; j < 4; ++j) {      const 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;Object.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;
 |