terriajs
Version:
Geospatial data visualization platform.
175 lines (151 loc) • 6.32 kB
JSX
'use strict';
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import URI from 'urijs';
import Rectangle from 'terriajs-cesium/Source/Core/Rectangle';
import GeoJsonCatalogItem from '../../../Models/GeoJsonCatalogItem';
import ObserveModelMixin from '../../ObserveModelMixin';
import Styles from './tool_button.scss';
import TerriaError from '../../../Core/TerriaError';
import CesiumCartographic from 'terriajs-cesium/Source/Core/Cartographic.js';
import Icon from "../../Icon.jsx";
import defined from 'terriajs-cesium/Source/Core/defined';
const MyLocation = createReactClass({
displayName: 'MyLocation',
mixins: [ObserveModelMixin],
propTypes: {
terria: PropTypes.object.isRequired
},
_marker: undefined,
/* eslint-disable-next-line camelcase */
UNSAFE_componentWillMount() {
this._marker = new GeoJsonCatalogItem(this.props.terria);
},
getInitialState() {
return {};
},
getLocation() {
if (navigator.geolocation) {
const options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
if (!this.augmentedVirtualityEnabled()) {
// When Augmented Virtuality is not enabled then just get a single position update.
navigator.geolocation.getCurrentPosition(
this.zoomToMyLocation,
this.handleLocationError,
options
);
} else {
// When Augmented Virtuality is enabled then we effectively toggle into watch mode and the position is repeatedly updated.
const watchId = navigator.geolocation.watchPosition(
this.zoomToMyLocation,
this.handleLocationError,
options
);
this.setState({watchId: watchId});
}
} else {
this.props.terria.error.raiseEvent(new TerriaError({
sender: this,
title: 'Error getting location',
message: 'Your browser cannot provide your location.'
}));
}
},
zoomToMyLocation(position) {
const longitude = position.coords.longitude;
const latitude = position.coords.latitude;
if (this.augmentedVirtualityEnabled()) {
// Note: Specifiying the value of 27500m here enables this function to approximately mimic the behaviour of
// the else case from the cameras inital view and when the viewer pan/zooms out to much.
// We use the flag variable flown so that the user is flown to the current location when this function is
// first fired, but subsuquently the updates are jump location moves, since we assume that the movements are
// small and flyTo performs badly when the increments are small (slow and unresponsive).
this.props.terria.augmentedVirtuality.moveTo(CesiumCartographic.fromDegrees(longitude, latitude), 27500, !defined(this.state.flown));
this.setState({flown: true});
} else {
// west, south, east, north, result
const rectangle = Rectangle.fromDegrees(longitude - 0.1, latitude - 0.1, longitude + 0.1, latitude + 0.1);
this.props.terria.currentViewer.zoomTo(rectangle);
}
this._marker.name = 'My Location';
this._marker.data = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [longitude, latitude]
},
properties: {
title: 'Location',
longitude: longitude,
latitude: latitude
}
};
this._marker.style = {
'marker-size': 25,
'marker-color': '#08ABD5',
'stroke': '#ffffff',
'stroke-width': 3
};
this._marker.load();
if (this._marker.isEnabled !== true) {
this._marker.isEnabled = true;
}
},
handleLocationError(err) {
let message = err.message;
if (message && message.indexOf('Only secure origins are allowed') === 0) {
// This is actually the recommended way to check for this error.
// https://developers.google.com/web/updates/2016/04/geolocation-on-secure-contexts-only
const uri = new URI(window.location);
const secureUrl = uri.protocol('https').toString();
message = 'Your browser can only provide your location when using https. You may be able to use ' + secureUrl + ' instead.';
}
this.props.terria.error.raiseEvent(new TerriaError({
sender: this,
title: 'Error getting location',
message: message
}));
},
augmentedVirtualityEnabled() {
return defined(this.props.terria.augmentedVirtuality) && this.props.terria.augmentedVirtuality.enabled;
},
followMeEnabled() {
if (defined(this.state.watchId)) {
return true;
}
return false;
},
disableFollowMe() {
if (defined(this.state.watchId)) {
navigator.geolocation.clearWatch(this.state.watchId);
this.setState({watchId: undefined});
this.setState({flown: undefined});
}
},
handleCick() {
if (this.followMeEnabled()) {
this.disableFollowMe();
} else {
this.getLocation();
}
},
render() {
let toggleStyle = Styles.btn;
if (this.followMeEnabled()) {
toggleStyle = Styles.btnPrimary;
}
return <div className={Styles.toolButton}>
<button type='button' className={toggleStyle}
title='go to my location'
onClick={this.handleCick}>
<Icon glyph={Icon.GLYPHS.geolocation}/>
</button>
</div>;
},
});
export default MyLocation;