UNPKG

terriajs

Version:

Geospatial data visualization platform.

170 lines (143 loc) 6.35 kB
'use strict'; import React from 'react'; import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import Cartographic from 'terriajs-cesium/Source/Core/Cartographic'; import CesiumMath from 'terriajs-cesium/Source/Core/Math'; import defined from 'terriajs-cesium/Source/Core/defined'; import Ellipsoid from 'terriajs-cesium/Source/Core/Ellipsoid'; import knockout from 'terriajs-cesium/Source/ThirdParty/knockout'; import MapInteractionMode from '../../Models/MapInteractionMode'; import ObserveModelMixin from '../ObserveModelMixin'; import Styles from './parameter-editors.scss'; const PointParameterEditor = createReactClass({ displayName: 'PointParameterEditor', mixins: [ObserveModelMixin], propTypes: { previewed: PropTypes.object, parameter: PropTypes.object, viewState: PropTypes.object, parameterViewModel: PropTypes.object }, inputOnChange(e) { const text = e.target.value; this.props.parameterViewModel.userValue = text; this.props.parameterViewModel.isValueValid = PointParameterEditor.setValueFromText(e, this.props.parameter); }, inputOnBlur(e) { const isCurrentlyInvalid = !this.props.parameterViewModel.isValueValid; this.props.parameterViewModel.wasEverBlurredWhileInvalid = this.props.parameterViewModel.wasEverBlurredWhileInvalid || isCurrentlyInvalid; }, selectPointOnMap() { PointParameterEditor.selectOnMap(this.props.previewed.terria, this.props.viewState, this.props.parameter); }, getDisplayValue() { // Show the user's value if they've done any editing. if (defined(this.props.parameterViewModel.userValue)) { return this.props.parameterViewModel.userValue; } // Show the parameter's value if there is one. return PointParameterEditor.getDisplayValue(this.props.parameter.value); }, render() { const parameterViewModel = this.props.parameterViewModel; const showErrorMessage = !parameterViewModel.isValueValid && parameterViewModel.wasEverBlurredWhileInvalid; const style = showErrorMessage ? Styles.fieldInvalid : Styles.field; return ( <div> <If condition={showErrorMessage}> <div className={Styles.warningText}> Please enter valid coordinates (e.g. 131.0361, -25.3450). </div> </If> <input className={style} type="text" onChange={this.inputOnChange} onBlur={this.inputOnBlur} value={this.getDisplayValue()} placeholder="131.0361, -25.3450"/> <button type="button" onClick={this.selectPointOnMap} className={Styles.btnSelector}> Select location </button> </div> ); }, }); /** * Triggered when user types value directly into field. * @param {String} e Text that user has entered manually. * @param {FunctionParameter} parameter Parameter to set value on. * @returns {Boolean} True if the value was set successfully; false if the value could not be parsed. */ PointParameterEditor.setValueFromText = function(e, parameter) { const text = e.target.value; if (text.trim().length === 0 && !parameter.isRequired) { parameter.value = undefined; return true; } // parseFloat will ignore non-numeric characters at the end of the string. // e.g. "5asdf" will be parsed successfully as "5". // So we reject the text if there are any characters in it other than // 0-9, whitespace, plus, minus, comma, and period. // This isn't perfect - some strings may still parse even though they // don't make sense, like "0..9,1.2.3" - but it will at least eliminate // common errors like trying to specify degrees/minutes/seconds or // specifying W or E rather than using positive or negative numbers // for longitude. if (/[^\d\s\.,+-]/.test(text)) { return false; } const coordinates = text.split(','); if (coordinates.length === 2) { const longitude = parseFloat(coordinates[0]); const latitude = parseFloat(coordinates[1]); if (isNaN(longitude) || isNaN(latitude)) { return false; } parameter.value = Cartographic.fromDegrees(parseFloat(coordinates[0]), parseFloat(coordinates[1])); return true; } else { return false; } }; /** * Given a value, return it in human readable form for display. * @param {Object} value Native format of parameter value. * @return {String} String for display */ PointParameterEditor.getDisplayValue = function(value) { const digits = 5; if (defined(value)) { return CesiumMath.toDegrees(value.longitude).toFixed(digits) + ',' + CesiumMath.toDegrees(value.latitude).toFixed(digits); } else { return ''; } }; /** * Prompt user to select/draw on map in order to define parameter. * @param {Terria} terria Terria instance. * @param {Object} viewState ViewState. * @param {FunctionParameter} parameter Parameter. */ PointParameterEditor.selectOnMap = function(terria, viewState, parameter) { // Cancel any feature picking already in progress. terria.pickedFeatures = undefined; const pickPointMode = new MapInteractionMode({ message: 'Select a point by clicking on the map.', onCancel: function () { terria.mapInteractionModeStack.pop(); viewState.openAddData(); } }); terria.mapInteractionModeStack.push(pickPointMode); knockout.getObservable(pickPointMode, 'pickedFeatures').subscribe(function(pickedFeatures) { if (defined(pickedFeatures.pickPosition)) { const value = Ellipsoid.WGS84.cartesianToCartographic(pickedFeatures.pickPosition); terria.mapInteractionModeStack.pop(); parameter.value = value; viewState.openAddData(); } }); viewState.explorerPanelIsVisible = false; }; module.exports = PointParameterEditor;