terriajs
Version:
Geospatial data visualization platform.
187 lines (152 loc) • 6.37 kB
JSX
'use strict';
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import L from 'leaflet';
import Cartesian2 from 'terriajs-cesium/Source/Core/Cartesian2';
import defined from 'terriajs-cesium/Source/Core/defined';
import EllipsoidGeodesic from 'terriajs-cesium/Source/Core/EllipsoidGeodesic';
import getTimestamp from 'terriajs-cesium/Source/Core/getTimestamp';
import ObserveModelMixin from '../../ObserveModelMixin';
import Styles from './legend.scss';
const geodesic = new EllipsoidGeodesic();
const distances = [
1, 2, 3, 5,
10, 20, 30, 50,
100, 200, 300, 500,
1000, 2000, 3000, 5000,
10000, 20000, 30000, 50000,
100000, 200000, 300000, 500000,
1000000, 2000000, 3000000, 5000000,
10000000, 20000000, 30000000, 50000000];
const DistanceLegend = createReactClass({
displayName: 'DistanceLegend',
mixins: [ObserveModelMixin],
propTypes: {
terria: PropTypes.object,
},
getInitialState() {
return {
distanceLabel: undefined,
barWidth: 0
};
},
/* eslint-disable-next-line camelcase */
UNSAFE_componentWillMount() {
this.viewerSubscriptions = [];
this.removeUpdateSubscription = undefined;
this._lastLegendUpdate = undefined;
this.viewerSubscriptions.push(this.props.terria.beforeViewerChanged.addEventListener(()=>{
if (defined(this.removeUpdateSubscription)) {
this.removeUpdateSubscription();
this.removeUpdateSubscription = undefined;
}
}));
this.addUpdateSubscription();
this.viewerSubscriptions.push(this.props.terria.afterViewerChanged.addEventListener(()=>{
this.addUpdateSubscription();
}));
},
componentWillUnmount() {
this.removeUpdateSubscription && this.removeUpdateSubscription();
this.viewerSubscriptions.forEach(remove => remove());
},
addUpdateSubscription() {
const that = this;
if (defined(this.props.terria.cesium)) {
const scene = this.props.terria.cesium.scene;
this.removeUpdateSubscription = scene.postRender.addEventListener(()=>{
this.updateDistanceLegendCesium(scene);
});
} else if (defined(this.props.terria.leaflet)) {
const map = this.props.terria.leaflet.map;
const potentialChangeCallback = function potentialChangeCallback() {
that.updateDistanceLegendLeaflet(map);
};
that.removeUpdateSubscription = function() {
map.off('zoomend', potentialChangeCallback);
map.off('moveend', potentialChangeCallback);
};
map.on('zoomend', potentialChangeCallback);
map.on('moveend', potentialChangeCallback);
that.updateDistanceLegendLeaflet(map);
}
},
updateDistanceLegendCesium(scene) {
const now = getTimestamp();
if (now < this._lastLegendUpdate + 250) {
return;
}
this._lastLegendUpdate = now;
// Find the distance between two pixels at the bottom center of the screen.
const width = scene.canvas.clientWidth;
const height = scene.canvas.clientHeight;
const left = scene.camera.getPickRay(new Cartesian2((width / 2) | 0, height - 1));
const right = scene.camera.getPickRay(new Cartesian2(1 + (width / 2) | 0, height - 1));
const globe = scene.globe;
const leftPosition = globe.pick(left, scene);
const rightPosition = globe.pick(right, scene);
if (!defined(leftPosition) || !defined(rightPosition)) {
this.setState({
barWidth: undefined,
distanceLabel: undefined
});
return;
}
const leftCartographic = globe.ellipsoid.cartesianToCartographic(leftPosition);
const rightCartographic = globe.ellipsoid.cartesianToCartographic(rightPosition);
geodesic.setEndPoints(leftCartographic, rightCartographic);
const pixelDistance = geodesic.surfaceDistance;
// Find the first distance that makes the scale bar less than 100 pixels.
const maxBarWidth = 100;
let distance;
for (let i = distances.length - 1; !defined(distance) && i >= 0; --i) {
if (distances[i] / pixelDistance < maxBarWidth) {
distance = distances[i];
}
}
if (defined(distance)) {
let label;
if (distance >= 1000) {
label = (distance / 1000).toString() + ' km';
} else {
label = distance.toString() + ' m';
}
this.setState({
barWidth: (distance / pixelDistance) | 0,
distanceLabel: label
});
} else {
this.setState({
barWidth: undefined,
distanceLabel: undefined
});
}
},
updateDistanceLegendLeaflet(map) {
const halfHeight = map.getSize().y / 2;
const maxPixelWidth = 100;
const maxMeters = map.containerPointToLatLng([0, halfHeight]).distanceTo(
map.containerPointToLatLng([maxPixelWidth, halfHeight]));
const meters = L.control.scale()._getRoundNum(maxMeters);
const label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
this.setState({
barWidth: (meters / maxMeters) * maxPixelWidth,
distanceLabel: label
});
},
render() {
const barStyle = {
width: this.state.barWidth + 'px',
left: (5 + (125 - this.state.barWidth) / 2) + 'px',
height: '2px'
};
const distanceLabel = this.state.distanceLabel ?
(<div className={Styles.distanceLegend}>
<label>{this.state.distanceLabel}</label>
<div className={Styles.bar} style={barStyle}></div>
</div>) : null;
return distanceLabel;
},
});
module.exports = DistanceLegend;