@loaders.gl/tiles
Version:
Common components for different tiles loaders.
83 lines (82 loc) • 3.82 kB
JavaScript
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import { Matrix4, Vector3 } from '@math.gl/core';
import { Ellipsoid } from '@math.gl/geospatial';
const cameraPositionCartesian = new Vector3();
const toEye = new Vector3();
const cameraPositionEnu = new Vector3();
const extraVertexEnu = new Vector3();
const projectedOriginVector = new Vector3();
const enuToCartesianMatrix = new Matrix4();
const cartesianToEnuMatrix = new Matrix4();
/**
* For the maxScreenThreshold error metric, maxError means that you should replace the node with it's children
as soon as the nodes bounding sphere has a screen radius larger than maxError pixels.
In this sense a value of 0 means you should always load it's children,
or if it's a leaf node, you should always display it.
* @param tile
* @param frameState
* @returns
*/
export function getLodStatus(tile, frameState) {
if (tile.lodMetricValue === 0 || isNaN(tile.lodMetricValue)) {
return 'DIG';
}
const screenSize = 2 * getProjectedRadius(tile, frameState);
if (screenSize < 2) {
return 'OUT';
}
if (!tile.header.children || screenSize <= tile.lodMetricValue) {
return 'DRAW';
}
else if (tile.header.children) {
return 'DIG';
}
return 'OUT';
}
/**
* Calculate size of MBS radius projected on the screen plane
* @param tile
* @param frameState
* @returns
*/
// eslint-disable-next-line max-statements
export function getProjectedRadius(tile, frameState) {
const { topDownViewport: viewport } = frameState;
const mbsLat = tile.header.mbs[1];
const mbsLon = tile.header.mbs[0];
const mbsZ = tile.header.mbs[2];
const mbsR = tile.header.mbs[3];
const mbsCenterCartesian = [...tile.boundingVolume.center];
const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, cameraPositionCartesian);
// ---------------------------
// Calculate mbs border vertex
// ---------------------------
toEye.copy(cameraPositionCartesian).subtract(mbsCenterCartesian).normalize();
// Add extra vector to form plane
Ellipsoid.WGS84.eastNorthUpToFixedFrame(mbsCenterCartesian, enuToCartesianMatrix);
cartesianToEnuMatrix.copy(enuToCartesianMatrix).invert();
cameraPositionEnu.copy(cameraPositionCartesian).transform(cartesianToEnuMatrix);
// Mean Proportionals in Right Triangles - Altitude rule
// https://mathbitsnotebook.com/Geometry/RightTriangles/RTmeanRight.html
const projection = Math.sqrt(cameraPositionEnu[0] * cameraPositionEnu[0] + cameraPositionEnu[1] * cameraPositionEnu[1]);
const extraZ = (projection * projection) / cameraPositionEnu[2];
extraVertexEnu.copy([cameraPositionEnu[0], cameraPositionEnu[1], extraZ]);
const extraVertexCartesian = extraVertexEnu.transform(enuToCartesianMatrix);
const extraVectorCartesian = extraVertexCartesian.subtract(mbsCenterCartesian).normalize();
// We need radius vector orthogonal to toEye vector
const radiusVector = toEye.cross(extraVectorCartesian).normalize().scale(mbsR);
const sphereMbsBorderVertexCartesian = radiusVector.add(mbsCenterCartesian);
const sphereMbsBorderVertexCartographic = Ellipsoid.WGS84.cartesianToCartographic(sphereMbsBorderVertexCartesian);
// ---------------------------
// Project center vertex and border vertex and calculate projected radius of MBS
const projectedOrigin = viewport.project([mbsLon, mbsLat, mbsZ]);
const projectedMbsBorderVertex = viewport.project(sphereMbsBorderVertexCartographic);
const projectedRadius = projectedOriginVector
.copy(projectedOrigin)
.subtract(projectedMbsBorderVertex)
.magnitude();
return projectedRadius;
}