@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
256 lines (230 loc) • 7.68 kB
JavaScript
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 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.default] 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 = ellipsoid ?? Ellipsoid.default;
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.default] 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 = ellipsoid ?? Ellipsoid.default;
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;