From fcf02dbaded1589e4de22b4409b3dde38c344e70 Mon Sep 17 00:00:00 2001 From: kappu Date: Fri, 9 Mar 2018 07:52:05 +0100 Subject: [PATCH] Fix #2668 --- .../components/data/query/GeometryDetails.jsx | 88 ++++++++++++------- .../components/data/query/QueryBuilder.jsx | 6 +- .../components/data/query/SpatialFilter.jsx | 8 +- .../__tests__/geometrydetails-test.jsx | 46 ++++++++++ .../data/query/enhancers/geometrydetails.js | 24 +++++ web/client/plugins/QueryPanel.jsx | 5 +- web/client/translations/data.de-DE | 2 +- web/client/translations/data.en-US | 2 +- web/client/translations/data.es-ES | 2 +- web/client/translations/data.fr-FR | 2 +- web/client/translations/data.it-IT | 2 +- web/client/translations/data.nl-NL | 2 +- web/client/translations/data.zh-ZH | 2 +- 13 files changed, 145 insertions(+), 46 deletions(-) create mode 100644 web/client/components/data/query/enhancers/__tests__/geometrydetails-test.jsx create mode 100644 web/client/components/data/query/enhancers/geometrydetails.js diff --git a/web/client/components/data/query/GeometryDetails.jsx b/web/client/components/data/query/GeometryDetails.jsx index 23edd46a85..59df2b69a1 100644 --- a/web/client/components/data/query/GeometryDetails.jsx +++ b/web/client/components/data/query/GeometryDetails.jsx @@ -16,6 +16,7 @@ const assign = require('object-assign'); const CoordinatesUtils = require("../../../utils/CoordinatesUtils"); + class GeometryDetails extends React.Component { static propTypes = { useMapProjection: PropTypes.bool, @@ -23,7 +24,8 @@ class GeometryDetails extends React.Component { type: PropTypes.string, onShowPanel: PropTypes.func, onChangeDrawingStatus: PropTypes.func, - onEndDrawing: PropTypes.func + onEndDrawing: PropTypes.func, + zoom: PropTypes.number }; static defaultProps = { @@ -77,7 +79,7 @@ class GeometryDetails extends React.Component { projection: this.props.geometry.projection }; - this.props.onChangeDrawingStatus("replace", undefined, "queryform", [geometry]); + this.props.onChangeDrawingStatus({geometry:[geometry]}); }; onUpdateCircle = (value, name) => { @@ -96,12 +98,11 @@ class GeometryDetails extends React.Component { projection: this.props.geometry.projection }; - this.props.onChangeDrawingStatus("replace", undefined, "queryform", [geometry]); + this.props.onChangeDrawingStatus({geometry: [geometry]}); }; onModifyGeometry = () => { let geometry; - // Update the geometry if (this.props.type === "BBOX") { this.extent = this.tempExtent; @@ -168,28 +169,44 @@ class GeometryDetails extends React.Component { }; onClosePanel = () => { - if (this.props.type === "BBOX") { - this.resetBBOX(); - } else if (this.props.type === "Circle") { - this.resetCircle(); - } - + this.resetGeom(); this.props.onShowPanel(false); }; - + roundValue = (val, prec = 1000000) => Math.round(val * prec) / prec; + getStep = (zoom = 1) => { + if ( zoom >= 21 ) { + return 0.00001 + }else if( zoom >= 18) { + return 0.0001 + }else if( zoom >= 15) { + return 0.001 + }else if( zoom >= 12) { + return 0.01 + }else if( zoom >= 9) { + return 0.1 + }else if( zoom >= 6) { + return 1 + } + return 10; + }; + getStepCircle = (zoom, name) => { + const step = this.getStep(zoom); + return name === 'radius' && step * 100000 || step; + }; + isWGS84 = () => (this.props.geometry || {}).projection === 'EPSG:4326' || !this.props.useMapProjection; getBBOXDimensions = (geometry) => { const extent = geometry.projection !== 'EPSG:4326' && !this.props.useMapProjection ? CoordinatesUtils.reprojectBbox(geometry.extent, geometry.projection, 'EPSG:4326') : geometry.extent; return { // minx - west: Math.round(extent[0] * 100) / 100, + west: extent[0], // miny - sud: Math.round(extent[1] * 100) / 100, + sud: extent[1], // maxx - est: Math.round(extent[2] * 100) / 100, + est: extent[2], // maxy - north: Math.round(extent[3] * 100) / 100 + north: extent[3] }; }; getCircleDimensions = (geometry) => { @@ -201,9 +218,9 @@ class GeometryDetails extends React.Component { center = (center.x === undefined) ? {x: center[0], y: center[1]} : center; return { - x: Math.round(center.x * 100) / 100, - y: Math.round(center.y * 100) / 100, - radius: Math.round(geometry.radius * 100) / 100 + x: center.x, + y: center.y, + radius: geometry.radius }; }; renderCoordinateField = (value, name) => { @@ -214,18 +231,19 @@ class GeometryDetails extends React.Component { style={{minWidth: '105px', margin: 'auto'}} type="number" id={"queryform_bbox_" + name} - defaultValue={value} + step={!this.isWGS84() ? 1 : this.getStep(this.props.zoom)} + defaultValue={this.roundValue(value, !this.isWGS84() ? 100 : 1000000)} onChange={(evt) => this.onUpdateBBOX(evt.target.value, name)}/> ); }; - renderCircleField = (value, name) => { return ( this.onUpdateCircle(evt.target.value, name)}/> ); }; @@ -352,7 +370,7 @@ class GeometryDetails extends React.Component { key: 'reset', tooltipId: 'queryform.reset', glyph: 'clear-filter', - onClick: () => this.resetBBOX() + onClick: () => this.resetGeom() }, { key: 'close', glyph: '1-close', @@ -362,30 +380,36 @@ class GeometryDetails extends React.Component { ); } - + resetGeom = () => { + if (this.props.type === "BBOX") { + this.resetBBOX(); + } else if (this.props.type === "Circle") { + this.resetCircle(); + } + }; resetBBOX = () => { for (let prop in this.extent) { if (prop) { let coordinateInput = document.getElementById("queryform_bbox_" + prop); - coordinateInput.value = this.extent[prop]; - this.onUpdateBBOX(coordinateInput.value, prop); + coordinateInput.value = this.roundValue(this.extent[prop], !this.isWGS84() ? 100 : 1000000); + this.onUpdateBBOX(this.extent[prop], prop); } } }; resetCircle = () => { let radiusInput = document.getElementById("queryform_circle_radius"); - radiusInput.value = this.circle.radius; - this.onUpdateCircle(radiusInput.value, "radius"); + radiusInput.value = this.roundValue(this.circle.radius, 100); + this.onUpdateCircle(this.circle.radius, "radius"); let coordinateXInput = document.getElementById("queryform_circle_x"); - coordinateXInput.value = this.circle.x; - this.onUpdateCircle(coordinateXInput.value, "x"); + coordinateXInput.value = this.roundValue(this.circle.x, !this.isWGS84() ? 100 : 1000000); + this.onUpdateCircle(this.circle.x, "x"); let coordinateYInput = document.getElementById("queryform_circle_y"); - coordinateYInput.value = this.circle.y; - this.onUpdateCircle(coordinateYInput.value, "y"); + coordinateYInput.value = this.roundValue(this.circle.y, !this.isWGS84() ? 100 : 1000000); + this.onUpdateCircle(this.circle.y, "y"); }; } -module.exports = GeometryDetails; +module.exports = GeometryDetails; diff --git a/web/client/components/data/query/QueryBuilder.jsx b/web/client/components/data/query/QueryBuilder.jsx index 7e527d3d67..98ad7fdb63 100644 --- a/web/client/components/data/query/QueryBuilder.jsx +++ b/web/client/components/data/query/QueryBuilder.jsx @@ -62,7 +62,8 @@ class QueryBuilder extends React.Component { allowEmptyFilter: PropTypes.bool, autocompleteEnabled: PropTypes.bool, emptyFilterWarning: PropTypes.bool, - header: PropTypes.node + header: PropTypes.node, + zoom: PropTypes.number }; static defaultProps = { @@ -180,7 +181,8 @@ class QueryBuilder extends React.Component { spatialMethodOptions={this.props.spatialMethodOptions} spatialPanelExpanded={this.props.spatialPanelExpanded} showDetailsPanel={this.props.showDetailsPanel} - actions={this.props.spatialFilterActions}/> + actions={this.props.spatialFilterActions} + zoom={this.props.zoom}/> ) + onEndDrawing={this.props.actions.onEndDrawing} + zoom={this.props.zoom}/>) : ; diff --git a/web/client/components/data/query/enhancers/__tests__/geometrydetails-test.jsx b/web/client/components/data/query/enhancers/__tests__/geometrydetails-test.jsx new file mode 100644 index 0000000000..8f0ade57f0 --- /dev/null +++ b/web/client/components/data/query/enhancers/__tests__/geometrydetails-test.jsx @@ -0,0 +1,46 @@ +/* + * Copyright 2018, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); +const ReactDOM = require('react-dom'); +const {createSink} = require('recompose'); +const expect = require('expect'); +const geometrydetails = require('../geometrydetails'); + +describe('geometrydetails enhancer', () => { + beforeEach((done) => { + document.body.innerHTML = '
'; + setTimeout(done); + }); + afterEach((done) => { + ReactDOM.unmountComponentAtNode(document.getElementById("container")); + document.body.innerHTML = ''; + setTimeout(done); + }); + it('geometrydetails onChangeDrawingStatus debounce', (done) => { + const action = (status, method, owner, features) => { + expect(status).toExist(); + expect(status).toBe("replace"); + expect(method).toNotExist(); + expect(owner).toExist(); + expect(owner).toBe("queryform"); + expect(features).toExist(); + expect(features).toBe("geom1"); + done(); + } + const Sink = geometrydetails(createSink( props => { + expect(props).toExist(); + expect(props.onChangeDrawingStatus).toExist(); + props.onChangeDrawingStatus({geometry: "geom"}); + props.onChangeDrawingStatus({geometry: "geom1"}); + })); + ReactDOM.render((), document.getElementById("container")); + + }); +}); diff --git a/web/client/components/data/query/enhancers/geometrydetails.js b/web/client/components/data/query/enhancers/geometrydetails.js new file mode 100644 index 0000000000..59097e8f1f --- /dev/null +++ b/web/client/components/data/query/enhancers/geometrydetails.js @@ -0,0 +1,24 @@ +/** +* Copyright 2018, GeoSolutions Sas. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. +*/ +const {compose, createEventHandler, defaultProps} = require('recompose'); + +const propsStreamFactory = require('../../../misc/enhancers/propsStreamFactory'); + + +const dataStreamFactory = $props => { + const {handler: onChangeDrawingStatus, stream: onChangeDrawingStatus$} = createEventHandler(); + return $props.map(o => ({...o, onChangeDrawingStatus$ })).switchMap(({onChangeDrawingStatus$, onChangeDrawingStatus}) => onChangeDrawingStatus$.debounceTime(1000).map(({geometry}) => onChangeDrawingStatus("replace", undefined, "queryform", geometry))) + .startWith({}).map( o => ({...o, onChangeDrawingStatus})); +}; + + +module.exports = compose( + defaultProps({ + dataStreamFactory + }), + propsStreamFactory); diff --git a/web/client/plugins/QueryPanel.jsx b/web/client/plugins/QueryPanel.jsx index 016f0d992e..dfed92166a 100644 --- a/web/client/plugins/QueryPanel.jsx +++ b/web/client/plugins/QueryPanel.jsx @@ -21,6 +21,7 @@ const {zoomToExtent} = require('../actions/map'); const {toggleControl} = require('../actions/controls'); const {groupsSelector} = require('../selectors/layers'); +const {mapSelector} = require('../selectors/map'); const { crossLayerFilterSelector, availableCrossLayerFilterLayersSelector @@ -111,11 +112,11 @@ const SmartQueryForm = connect((state) => { showGeneratedFilter: false, allowEmptyFilter: true, emptyFilterWarning: true, - maxHeight: state.map && state.map.present && state.map.present.size && state.map.present.size.height + maxHeight: state.map && state.map.present && state.map.present.size && state.map.present.size.height, + zoom: (mapSelector(state) || {}).zoom }; }, dispatch => { return { - attributeFilterActions: bindActionCreators({ onAddGroupField: addGroupField, onAddFilterField: addFilterField, diff --git a/web/client/translations/data.de-DE b/web/client/translations/data.de-DE index 566a962891..f23c473b35 100644 --- a/web/client/translations/data.de-DE +++ b/web/client/translations/data.de-DE @@ -652,7 +652,7 @@ "reset_bbox": "Zurücksetzen", "save_bbox": "BBOX Änderungen speichern", "save_radius": "Radius/Kreiszentrum Änderungen speichern", - "radius": "Radius" + "radius": "Radius(m)" }, "methods": { "zone": "Zone", diff --git a/web/client/translations/data.en-US b/web/client/translations/data.en-US index 568b4a419b..6f9ecfefef 100644 --- a/web/client/translations/data.en-US +++ b/web/client/translations/data.en-US @@ -653,7 +653,7 @@ "reset_bbox": "Reset", "save_bbox": "Save BBOX modifications", "save_radius": "Save the radius/center modifications", - "radius": "Radius" + "radius": "Radius(m)" }, "methods": { "zone": "Zone", diff --git a/web/client/translations/data.es-ES b/web/client/translations/data.es-ES index 6c47e7339f..9851562c5f 100644 --- a/web/client/translations/data.es-ES +++ b/web/client/translations/data.es-ES @@ -652,7 +652,7 @@ "reset_bbox": "Reset", "save_bbox": "Guardar los cambios", "save_radius": "Guardar los cambios", - "radius": "Radio" + "radius": "Radio(m)" }, "methods": { "zone": "Zona", diff --git a/web/client/translations/data.fr-FR b/web/client/translations/data.fr-FR index d1107349b5..61f240336a 100644 --- a/web/client/translations/data.fr-FR +++ b/web/client/translations/data.fr-FR @@ -653,7 +653,7 @@ "reset_bbox": "Recommencer", "save_bbox": "Sauver les modifications", "save_radius": "Sauver les modifications", - "radius": "Rayon" + "radius": "Rayon(m)" }, "methods": { "zone": "Zone", diff --git a/web/client/translations/data.it-IT b/web/client/translations/data.it-IT index f03a9a82e2..61df49f9e5 100644 --- a/web/client/translations/data.it-IT +++ b/web/client/translations/data.it-IT @@ -652,7 +652,7 @@ "reset_bbox": "Reset", "save_bbox": "Salva le modifiche al BBOX", "save_radius": "Salva le modifiche al raggio e/o al centro", - "radius": "Raggio" + "radius": "Raggio(m)" }, "methods": { "zone": "Zona", diff --git a/web/client/translations/data.nl-NL b/web/client/translations/data.nl-NL index e3c0f8cf3c..6b28063827 100644 --- a/web/client/translations/data.nl-NL +++ b/web/client/translations/data.nl-NL @@ -559,7 +559,7 @@ "reset_bbox": "Herbegin", "save_bbox": "Wijzigingen opslaan", "save_radius": "Wijzigingen opslaan", - "radius": "Straal" + "radius": "Straal(m)" }, "methods": { "zone": "Zone", diff --git a/web/client/translations/data.zh-ZH b/web/client/translations/data.zh-ZH index 2150cd9fed..e2ef8d3355 100644 --- a/web/client/translations/data.zh-ZH +++ b/web/client/translations/data.zh-ZH @@ -562,7 +562,7 @@ "reset_bbox": "Reset", "save_bbox": "Save BBOX modifications", "save_radius": "Save the radius/center modifications", - "radius": "Radius" + "radius": "Radius(m)" }, "methods": { "zone": "Zone",