terriajs
Version:
Geospatial data visualization platform.
150 lines (129 loc) • 7.42 kB
JavaScript
import Cartographic from 'terriajs-cesium/Source/Core/Cartographic';
import CesiumMath from 'terriajs-cesium/Source/Core/Math';
import debounce from 'lodash.debounce';
import defined from 'terriajs-cesium/Source/Core/defined';
import EarthGravityModel1996 from '../Map/EarthGravityModel1996';
import EllipsoidTerrainProvider from 'terriajs-cesium/Source/Core/EllipsoidTerrainProvider';
import Intersections2D from 'terriajs-cesium/Source/Core/Intersections2D';
import knockout from 'terriajs-cesium/Source/ThirdParty/knockout';
import prettifyCoordinates from '../Map/prettifyCoordinates';
import prettifyProjection from '../Map/prettifyProjection';
import sampleTerrainMostDetailed from 'terriajs-cesium/Source/Core/sampleTerrainMostDetailed';
import when from 'terriajs-cesium/Source/ThirdParty/when';
export default class MouseCoords {
constructor() {
this.geoidModel = new EarthGravityModel1996(require('file-loader!../../wwwroot/data/WW15MGH.DAC'));
this.proj4Projection = '+proj=utm +ellps=GRS80 +units=m +no_defs';
this.projectionUnits = 'm';
this.proj4longlat = '+proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees +no_defs';
this.lastHeightSamplePosition = new Cartographic();
this.accurateSamplingDebounceTime = 250;
this.tileRequestInFlight = undefined;
this.elevation = undefined;
this.utmZone = undefined;
this.latitude = undefined;
this.longitude = undefined;
this.north = undefined;
this.east = undefined;
this.useProjection = false;
this.debounceSampleAccurateHeight = debounce(this.sampleAccurateHeight, this.accurateSamplingDebounceTime);
knockout.track(this, ['elevation', 'utmZone', 'latitude', 'longitude', 'north', 'east', 'useProjection']);
}
toggleUseProjection() {
this.useProjection = !this.useProjection;
}
updateCoordinatesFromCesium(terria, position) {
const scene = terria.cesium.scene;
const camera = scene.camera;
const pickRay = camera.getPickRay(position);
const globe = scene.globe;
const pickedTriangle = globe.pickTriangle(pickRay, scene);
if (defined(pickedTriangle)) {
// Get a fast, accurate-ish height every time the mouse moves.
const ellipsoid = globe.ellipsoid;
const v0 = ellipsoid.cartesianToCartographic(pickedTriangle.v0);
const v1 = ellipsoid.cartesianToCartographic(pickedTriangle.v1);
const v2 = ellipsoid.cartesianToCartographic(pickedTriangle.v2);
const intersection = ellipsoid.cartesianToCartographic(pickedTriangle.intersection);
let errorBar;
if (globe.terrainProvider instanceof EllipsoidTerrainProvider) {
intersection.height = undefined;
} else {
const barycentric = Intersections2D.computeBarycentricCoordinates(
intersection.longitude, intersection.latitude,
v0.longitude, v0.latitude,
v1.longitude, v1.latitude,
v2.longitude, v2.latitude);
if (barycentric.x >= -1e-15 && barycentric.y >= -1e-15 && barycentric.z >= -1e-15) {
const height = barycentric.x * v0.height +
barycentric.y * v1.height +
barycentric.z * v2.height;
intersection.height = height;
}
const geometricError = globe.terrainProvider.getLevelMaximumGeometricError(pickedTriangle.tile.level);
const approximateHeight = intersection.height;
const minHeight = Math.max(pickedTriangle.tile.data.minimumHeight, approximateHeight - geometricError);
const maxHeight = Math.min(pickedTriangle.tile.data.maximumHeight, approximateHeight + geometricError);
const minHeightGeoid = minHeight - (this.geoidModel ? this.geoidModel.minimumHeight : 0.0);
const maxHeightGeoid = maxHeight + (this.geoidModel ? this.geoidModel.maximumHeight : 0.0);
errorBar = Math.max(Math.abs(approximateHeight - minHeightGeoid), Math.abs(maxHeightGeoid - approximateHeight));
}
Cartographic.clone(intersection, this.lastHeightSamplePosition);
const terrainProvider = globe.terrainProvider;
this.cartographicToFields(intersection, errorBar);
if (!(terrainProvider instanceof EllipsoidTerrainProvider)) {
this.debounceSampleAccurateHeight(terrainProvider, intersection);
}
} else {
this.elevation = undefined;
this.utmZone = undefined;
this.latitude = undefined;
this.longitude = undefined;
this.north = undefined;
this.east = undefined;
}
}
updateCoordinatesFromLeaflet(terria, mouseMoveEvent) {
const latLng = terria.leaflet.map.mouseEventToLatLng(mouseMoveEvent);
const coordinates = Cartographic.fromDegrees(latLng.lng, latLng.lat);
coordinates.height = undefined;
this.cartographicToFields(coordinates);
}
cartographicToFields(coordinates, errorBar) {
const latitude = CesiumMath.toDegrees(coordinates.latitude);
const longitude = CesiumMath.toDegrees(coordinates.longitude);
if (this.useProjection) {
var prettyProjection = prettifyProjection(longitude, latitude, this.proj4Projection, this.proj4longlat, this.projectionUnits);
this.utmZone = prettyProjection.utmZone;
this.north = prettyProjection.north;
this.east = prettyProjection.east;
}
var prettyCoordinate = prettifyCoordinates(longitude, latitude, {height: coordinates.height, errorBar: errorBar});
this.latitude = prettyCoordinate.latitude;
this.longitude = prettyCoordinate.longitude;
this.elevation = prettyCoordinate.elevation;
}
sampleAccurateHeight(terrainProvider, position) {
if (this.tileRequestInFlight) {
// A tile request is already in flight, so reschedule for later.
this.debounceSampleAccurateHeight.cancel();
this.debounceSampleAccurateHeight(terrainProvider, position);
return;
}
const positionWithHeight = Cartographic.clone(position);
const geoidHeightPromise = this.geoidModel ? this.geoidModel.getHeight(position.longitude, position.latitude) : undefined;
const terrainPromise = sampleTerrainMostDetailed(terrainProvider, [positionWithHeight]);
this.tileRequestInFlight = when.all([geoidHeightPromise, terrainPromise], result => {
const geoidHeight = result[0] || 0.0;
this.tileRequestInFlight = undefined;
if (Cartographic.equals(position, this.lastHeightSamplePosition)) {
position.height = positionWithHeight.height - geoidHeight;
this.cartographicToFields(position);
} else {
// Mouse moved since we started this request, so the result isn't useful. Try again next time.
}
}, () => {
this.tileRequestInFlight = undefined;
});
}
}