From f995801be7e523fbb3fe8dea2c79cee1458e5b87 Mon Sep 17 00:00:00 2001 From: Matteo V Date: Mon, 15 May 2017 12:38:54 +0200 Subject: [PATCH] Fix #1392 added viewport into spatial filter list (#1822) * Fix #1392 added viewport into spatial filter list * Fixed pagination of docker feature grid * added tests and fixed wfsquery epic --- .../actions/__tests__/queryform-test.js | 29 +++++++ web/client/actions/queryform.js | 18 +++++ web/client/actions/wfsquery.js | 39 ++++----- .../components/data/query/QueryBuilder.jsx | 1 + .../components/data/query/SpatialFilter.jsx | 20 +++-- web/client/epics/wfsquery.js | 43 +++++++++- web/client/plugins/QueryPanel.jsx | 6 +- web/client/plugins/TOC.jsx | 2 + .../reducers/__tests__/queryform-test.js | 79 +++++++++++++++++++ web/client/reducers/queryform.js | 4 + web/client/translations/data.de-DE | 1 + web/client/translations/data.en-US | 1 + web/client/translations/data.fr-FR | 1 + web/client/translations/data.it-IT | 1 + 14 files changed, 209 insertions(+), 36 deletions(-) diff --git a/web/client/actions/__tests__/queryform-test.js b/web/client/actions/__tests__/queryform-test.js index 9772029af6..f29b15acd0 100644 --- a/web/client/actions/__tests__/queryform-test.js +++ b/web/client/actions/__tests__/queryform-test.js @@ -19,6 +19,7 @@ const { EXPAND_ATTRIBUTE_PANEL, EXPAND_SPATIAL_PANEL, SELECT_SPATIAL_METHOD, + UPDATE_GEOMETRY, SELECT_SPATIAL_OPERATION, REMOVE_SPATIAL_SELECT, SHOW_SPATIAL_DETAILS, @@ -35,6 +36,8 @@ const { ADD_SIMPLE_FILTER_FIELD, REMOVE_SIMPLE_FILTER_FIELD, REMOVE_ALL_SIMPLE_FILTER_FIELDS, + SELECT_VIEWPORT_SPATIAL_METHOD, + CHANGE_SPATIAL_ATTRIBUTE, changeDwithinValue, resetZones, zoneChange, @@ -56,8 +59,11 @@ const { expandAttributeFilterPanel, expandSpatialFilterPanel, selectSpatialMethod, + updateGeometrySpatialField, + selectViewportSpatialMethod, selectSpatialOperation, removeSpatialSelection, + changeSpatialAttribute, showSpatialSelectionDetails, simpleFilterFieldUpdate, addSimpleFilterField, @@ -89,6 +95,29 @@ describe('Test correctness of the queryform actions', () => { expect(retval.index).toBe(0); }); + it('updateGeometrySpatialField', () => { + const geometry = {center: [0, 1], coordinates: []}; + const retval = updateGeometrySpatialField(geometry); + + expect(retval).toExist(); + expect(retval.type).toBe(UPDATE_GEOMETRY); + expect(retval.geometry).toBe(geometry); + }); + + it('selectViewportSpatialMethod', () => { + const retval = selectViewportSpatialMethod(); + expect(retval).toExist(); + expect(retval.type).toBe(SELECT_VIEWPORT_SPATIAL_METHOD); + }); + + it('changeSpatialAttribute', () => { + const attribute = "some value"; + const retval = changeSpatialAttribute(attribute); + expect(retval).toExist(); + expect(retval.type).toBe(CHANGE_SPATIAL_ATTRIBUTE); + expect(retval.attribute).toBe(attribute); + }); + it('removeFilterField', () => { let rowId = 100; diff --git a/web/client/actions/queryform.js b/web/client/actions/queryform.js index 74541a2992..5da1f56af5 100644 --- a/web/client/actions/queryform.js +++ b/web/client/actions/queryform.js @@ -16,6 +16,8 @@ const CHANGE_CASCADING_VALUE = 'CHANGE_CASCADING_VALUE'; const EXPAND_ATTRIBUTE_PANEL = 'EXPAND_ATTRIBUTE_PANEL'; const EXPAND_SPATIAL_PANEL = 'EXPAND_SPATIAL_PANEL'; const SELECT_SPATIAL_METHOD = 'SELECT_SPATIAL_METHOD'; +const SELECT_VIEWPORT_SPATIAL_METHOD = 'SELECT_VIEWPORT_SPATIAL_METHOD'; +const UPDATE_GEOMETRY = 'UPDATE_GEOMETRY'; const SELECT_SPATIAL_OPERATION = 'SELECT_SPATIAL_OPERATION'; const CHANGE_SPATIAL_ATTRIBUTE = 'CHANGE_SPATIAL_ATTRIBUTE'; const REMOVE_SPATIAL_SELECT = 'REMOVE_SPATIAL_SELECT'; @@ -126,6 +128,18 @@ function selectSpatialMethod(method, fieldName) { }; } +function selectViewportSpatialMethod() { + return { + type: SELECT_VIEWPORT_SPATIAL_METHOD + }; +} +function updateGeometrySpatialField(geometry) { + return { + type: UPDATE_GEOMETRY, + geometry + }; +} + function selectSpatialOperation(operation, fieldName) { return { type: SELECT_SPATIAL_OPERATION, @@ -322,6 +336,10 @@ module.exports = { ADD_SIMPLE_FILTER_FIELD, REMOVE_SIMPLE_FILTER_FIELD, REMOVE_ALL_SIMPLE_FILTER_FIELDS, + SELECT_VIEWPORT_SPATIAL_METHOD, + UPDATE_GEOMETRY, + updateGeometrySpatialField, + selectViewportSpatialMethod, resetZones, zoneChange, // openMenu, diff --git a/web/client/actions/wfsquery.js b/web/client/actions/wfsquery.js index 0c537f4869..ab4eeb08e1 100644 --- a/web/client/actions/wfsquery.js +++ b/web/client/actions/wfsquery.js @@ -1,4 +1,4 @@ -/** +/* * Copyright 2016, GeoSolutions Sas. * All rights reserved. * @@ -103,12 +103,11 @@ function createQuery(searchUrl, filterObj) { }; } -function query(searchUrl, filterObj, retry) { +function query(searchUrl, filterObj) { return { type: QUERY, searchUrl, - filterObj, - retry + filterObj }; } @@ -147,28 +146,18 @@ function closeResponse() { } module.exports = { - FEATURE_TYPE_SELECTED, - FEATURE_TYPE_LOADED, + FEATURE_TYPE_SELECTED, featureTypeSelected, + FEATURE_TYPE_LOADED, featureTypeLoaded, + FEATURE_TYPE_ERROR, featureTypeError, + FEATURE_ERROR, featureError, + FEATURE_CLOSE, featureClose, + QUERY_CREATE, createQuery, + QUERY_RESULT, querySearchResponse, + QUERY_ERROR, queryError, + RESET_QUERY, resetQuery, + QUERY, query, FEATURE_LOADED, - FEATURE_TYPE_ERROR, - FEATURE_ERROR, - FEATURE_CLOSE, - QUERY_CREATE, - QUERY_RESULT, - QUERY_ERROR, - RESET_QUERY, - QUERY, - featureTypeSelected, - featureTypeLoaded, - featureTypeError, - featureError, loadFeature, - createQuery, - query, - featureClose, - resetQuery, toggleQueryPanel, - closeResponse, - queryError, - querySearchResponse + closeResponse }; diff --git a/web/client/components/data/query/QueryBuilder.jsx b/web/client/components/data/query/QueryBuilder.jsx index 33786527ee..450cddbf49 100644 --- a/web/client/components/data/query/QueryBuilder.jsx +++ b/web/client/components/data/query/QueryBuilder.jsx @@ -96,6 +96,7 @@ const QueryBuilder = React.createClass({ onChangeDrawingStatus: () => {}, onRemoveSpatialSelection: () => {}, onShowSpatialSelectionDetails: () => {}, + onSelectViewportSpatialMethod: () => {}, onEndDrawing: () => {}, onChangeDwithinValue: () => {} }, diff --git a/web/client/components/data/query/SpatialFilter.jsx b/web/client/components/data/query/SpatialFilter.jsx index e2e760e5ae..4d684f2153 100644 --- a/web/client/components/data/query/SpatialFilter.jsx +++ b/web/client/components/data/query/SpatialFilter.jsx @@ -38,6 +38,7 @@ const SpatialFilter = React.createClass({ showDetailsPanel: false, withContainer: true, spatialMethodOptions: [ + {id: "Viewport", name: "queryform.spatialfilter.methods.viewport"}, {id: "BBOX", name: "queryform.spatialfilter.methods.box"}, {id: "Circle", name: "queryform.spatialfilter.methods.circle"}, {id: "Polygon", name: "queryform.spatialfilter.methods.poly"} @@ -57,6 +58,7 @@ const SpatialFilter = React.createClass({ onRemoveSpatialSelection: () => {}, onShowSpatialSelectionDetails: () => {}, onEndDrawing: () => {}, + onSelectViewportSpatialMethod: () => {}, onChangeDwithinValue: () => {}, zoneFilter: () => {}, zoneSearch: () => {}, @@ -211,7 +213,7 @@ const SpatialFilter = React.createClass({ const selectedOperation = this.props.spatialOperations.filter((opt) => this.props.spatialField.operation === opt.id)[0]; let drawLabel = (); - if (this.props.spatialField.method && this.props.spatialField.method !== "ZONE") { + if (this.props.spatialField.method && this.props.spatialField.method !== "ZONE" && this.props.spatialField.method !== "Viewport") { drawLabel = !this.props.spatialField.geometry ? (
@@ -314,10 +316,18 @@ const SpatialFilter = React.createClass({ this.props.actions.onSelectSpatialMethod(method, name); - if (method !== "ZONE") { - this.changeDrawingStatus('start', method, "queryform", []); - } else { - this.changeDrawingStatus('clean', null, "queryform", []); + switch (method) { + case "ZONE": { + this.changeDrawingStatus('clean', null, "queryform", []); break; + } + case "Viewport": { + this.changeDrawingStatus('clean', null, "queryform", []); + this.props.actions.onSelectViewportSpatialMethod(); + break; + } + default: { + this.changeDrawingStatus('start', method, "queryform", []); + } } }, updateSpatialOperation(id, name, value) { diff --git a/web/client/epics/wfsquery.js b/web/client/epics/wfsquery.js index 8c4beda6e5..b143e992d9 100644 --- a/web/client/epics/wfsquery.js +++ b/web/client/epics/wfsquery.js @@ -8,8 +8,9 @@ const Rx = require('rxjs'); const axios = require('../libs/ajax'); -const {changeSpatialAttribute} = require('../actions/queryform'); -const {FEATURE_TYPE_SELECTED, QUERY, featureTypeLoaded, featureTypeError, createQuery, querySearchResponse, queryError, featureClose} = require('../actions/wfsquery'); +const {changeSpatialAttribute, SELECT_VIEWPORT_SPATIAL_METHOD, updateGeometrySpatialField} = require('../actions/queryform'); +const {CHANGE_MAP_VIEW} = require('../actions/map'); +const {FEATURE_TYPE_SELECTED, QUERY, featureTypeLoaded, featureTypeError, querySearchResponse, queryError, featureClose} = require('../actions/wfsquery'); const FilterUtils = require('../utils/FilterUtils'); const assign = require('object-assign'); const {isString} = require('lodash'); @@ -197,7 +198,6 @@ const wfsQueryEpic = (action$, store) => .switchMap(action => { return Rx.Observable.merge( - Rx.Observable.of(createQuery(action.searchUrl, action.filterObj)), Rx.Observable.of(setControlProperty('drawer', 'enabled', false)), getWFSFeature(action.searchUrl, action.filterObj) .switchMap((response) => { @@ -227,6 +227,40 @@ const closeFeatureEpic = action$ => return action.control && action.control === 'drawer' ? Rx.Observable.of(featureClose()) : Rx.Observable.empty(); }); +function validateExtent(extent) { + if (extent[0] <= -180.0 || extent[2] >= 180.0) { + extent[0] = -180.0; + extent[2] = 180.0; + } + return extent; +} +const viewportSelectedEpic = (action$, store) => + action$.ofType(SELECT_VIEWPORT_SPATIAL_METHOD, CHANGE_MAP_VIEW) + .switchMap((action) => { + // calculate new geometry from map properties only for viewport + const map = action.type === CHANGE_MAP_VIEW ? action : store.getState().map.present; + if (action.type === SELECT_VIEWPORT_SPATIAL_METHOD || + action.type === CHANGE_MAP_VIEW && + store.getState().queryform && + store.getState().queryform.spatialField && + store.getState().queryform.spatialField.method === "viewport") { + const bounds = Object.keys(map.bbox.bounds).reduce((p, c) => { + return assign({}, p, {[c]: parseFloat(map.bbox.bounds[c])}); + }, {}); + const extent = validateExtent([bounds.minx, bounds.miny, bounds.maxx, bounds.maxy]); + const center = [(extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2]; + const start = [extent[0], extent[1]]; + const end = [extent[2], extent[3]]; + const coordinates = [[start, [start[0], end[1]], end, [end[0], start[1]], start]]; + let geometry = { + type: "Polygon", radius: 0, projection: "EPSG:4326", + extent, center, coordinates + }; + return Rx.Observable.of(updateGeometrySpatialField(geometry)); + } + return Rx.Observable.empty(); + }); + /** * Epics for WFS query requests * @name epics.wfsquery @@ -236,5 +270,6 @@ const closeFeatureEpic = action$ => module.exports = { featureTypeSelectedEpic, wfsQueryEpic, - closeFeatureEpic + closeFeatureEpic, + viewportSelectedEpic }; diff --git a/web/client/plugins/QueryPanel.jsx b/web/client/plugins/QueryPanel.jsx index 458739f38e..2e24bf556f 100644 --- a/web/client/plugins/QueryPanel.jsx +++ b/web/client/plugins/QueryPanel.jsx @@ -24,7 +24,7 @@ const LayersUtils = require('../utils/LayersUtils'); // include application component const QueryBuilder = require('../components/data/query/QueryBuilder'); -const {featureTypeSelectedEpic, wfsQueryEpic, closeFeatureEpic} = require('../epics/wfsquery'); +const {featureTypeSelectedEpic, wfsQueryEpic, closeFeatureEpic, viewportSelectedEpic} = require('../epics/wfsquery'); const {bindActionCreators} = require('redux'); const { @@ -40,6 +40,7 @@ const { expandAttributeFilterPanel, expandSpatialFilterPanel, selectSpatialMethod, + selectViewportSpatialMethod, selectSpatialOperation, removeSpatialSelection, showSpatialSelectionDetails, @@ -95,6 +96,7 @@ const SmartQueryForm = connect((state) => { spatialFilterActions: bindActionCreators({ onExpandSpatialFilterPanel: expandSpatialFilterPanel, onSelectSpatialMethod: selectSpatialMethod, + onSelectViewportSpatialMethod: selectViewportSpatialMethod, onSelectSpatialOperation: selectSpatialOperation, onChangeDrawingStatus: changeDrawingStatus, onRemoveSpatialSelection: removeSpatialSelection, @@ -237,5 +239,5 @@ module.exports = { queryform: require('../reducers/queryform'), query: require('../reducers/query') }, - epics: {featureTypeSelectedEpic, wfsQueryEpic, closeFeatureEpic} + epics: {featureTypeSelectedEpic, wfsQueryEpic, closeFeatureEpic, viewportSelectedEpic} }; diff --git a/web/client/plugins/TOC.jsx b/web/client/plugins/TOC.jsx index f61d991c5f..c6b567fac1 100644 --- a/web/client/plugins/TOC.jsx +++ b/web/client/plugins/TOC.jsx @@ -42,6 +42,7 @@ const { expandAttributeFilterPanel, expandSpatialFilterPanel, selectSpatialMethod, + selectViewportSpatialMethod, selectSpatialOperation, removeSpatialSelection, showSpatialSelectionDetails, @@ -105,6 +106,7 @@ const SmartQueryForm = connect((state) => { spatialFilterActions: bindActionCreators({ onExpandSpatialFilterPanel: expandSpatialFilterPanel, onSelectSpatialMethod: selectSpatialMethod, + onSelectViewportSpatialMethod: selectViewportSpatialMethod, onSelectSpatialOperation: selectSpatialOperation, onChangeDrawingStatus: changeDrawingStatus, onRemoveSpatialSelection: removeSpatialSelection, diff --git a/web/client/reducers/__tests__/queryform-test.js b/web/client/reducers/__tests__/queryform-test.js index d4fd1acc01..f623ce64e2 100644 --- a/web/client/reducers/__tests__/queryform-test.js +++ b/web/client/reducers/__tests__/queryform-test.js @@ -418,6 +418,85 @@ describe('Test the queryform reducer', () => { expect(state.showDetailsPanel).toEqual(true); }); + it('test UPDATE_GEOMETRY', () => { + const geometry = {center: [0, 1], coordinates: []}; + let testAction = { + type: "UPDATE_GEOMETRY", + geometry + }; + const initialState = { spatialField: {geometry: {}} }; + let state = queryform(initialState, testAction); + expect(state).toExist(); + expect(state.spatialField.geometry).toEqual(geometry); + }); + + it('test CHANGE_SPATIAL_ATTRIBUTE', () => { + const attribute = "some value"; + let testAction = { + type: "CHANGE_SPATIAL_ATTRIBUTE", + attribute + }; + const initialState = { spatialField: {attribute: {}} }; + let state = queryform(initialState, testAction); + expect(state).toExist(); + expect(state.spatialField.attribute).toEqual(attribute); + }); + + it('test CHANGE_DRAWING_STATUS', () => { + const initialState = { toolbarEnabled: true }; + const testAction1 = { + type: "CHANGE_DRAWING_STATUS", + owner: "queryform", + status: "start" + }; + const state = queryform(initialState, testAction1); + expect(state).toExist(); + expect(state.toolbarEnabled).toBeFalsy(); + const testAction2 = { + type: "CHANGE_DRAWING_STATUS", + owner: "measure", + status: "start" + }; + const state2 = queryform(initialState, testAction2); + expect(state2).toExist(); + expect(state2.toolbarEnabled).toBeTruthy(); + }); + + it('test END_DRAWING', () => { + const geometry = {center: [0, 1], coordinates: []}; + const initialState = { toolbarEnabled: false, spatialField: {geometry: {}} }; + const testAction1 = { + type: "END_DRAWING", + owner: "queryform", + geometry + }; + const state = queryform(initialState, testAction1); + expect(state).toExist(); + expect(state.toolbarEnabled).toBeTruthy(); + expect(state.spatialField.geometry).toBe(geometry); + const testAction2 = { + type: "END_DRAWING", + owner: "measure", + status: "start" + }; + const state2 = queryform(initialState, testAction2); + expect(state2).toExist(); + expect(state2.toolbarEnabled).toBeFalsy(); + expect(state2.spatialField.geometry).toEqual({}); + }); + + it('test CHANGE_DWITHIN_VALUE', () => { + const initialState = { spatialField: {geometry: { distance: {}}} }; + const distance = 123; + const testAction1 = { + type: "CHANGE_DWITHIN_VALUE", + distance + }; + const state = queryform(initialState, testAction1); + expect(state).toExist(); + expect(state.spatialField.geometry.distance).toBe(distance); + }); + it('Query Form Reset', () => { let testAction = { type: "QUERY_FORM_RESET" diff --git a/web/client/reducers/queryform.js b/web/client/reducers/queryform.js index 0ed268517c..0ca7caa072 100644 --- a/web/client/reducers/queryform.js +++ b/web/client/reducers/queryform.js @@ -27,6 +27,7 @@ const { CHANGE_DWITHIN_VALUE, ZONE_FILTER, ZONE_SEARCH, + UPDATE_GEOMETRY, // OPEN_MENU, ZONE_CHANGE, ZONES_RESET, @@ -165,6 +166,9 @@ function queryform(state = initialState, action) { case SELECT_SPATIAL_METHOD: { return assign({}, state, {spatialField: assign({}, state.spatialField, {[action.fieldName]: action.method, geometry: null})}); } + case UPDATE_GEOMETRY: { + return assign({}, state, {spatialField: assign({}, state.spatialField, {geometry: action.geometry})}, {toolbarEnabled: true}); + } case SELECT_SPATIAL_OPERATION: { return assign({}, state, {spatialField: assign({}, state.spatialField, {[action.fieldName]: action.operation})}); } diff --git a/web/client/translations/data.de-DE b/web/client/translations/data.de-DE index 7eff6553a5..ea8a0086ff 100644 --- a/web/client/translations/data.de-DE +++ b/web/client/translations/data.de-DE @@ -456,6 +456,7 @@ }, "methods": { "zone": "Zone", + "viewport": "Viewport", "box": "BoundingBox", "buffer": "Buffer", "circle": "Kreis", diff --git a/web/client/translations/data.en-US b/web/client/translations/data.en-US index 86fca979ba..fae566375f 100644 --- a/web/client/translations/data.en-US +++ b/web/client/translations/data.en-US @@ -456,6 +456,7 @@ }, "methods": { "zone": "Zone", + "viewport": "Viewport", "box": "BoundingBox", "buffer": "Buffer", "circle": "Circle", diff --git a/web/client/translations/data.fr-FR b/web/client/translations/data.fr-FR index 60807514d6..d6a3a478a3 100644 --- a/web/client/translations/data.fr-FR +++ b/web/client/translations/data.fr-FR @@ -458,6 +458,7 @@ }, "methods": { "zone": "Zone", + "viewport": "Viewport", "box": "Cadre d'objet", "buffer": "Zone tampon", "circle": "Cercle", diff --git a/web/client/translations/data.it-IT b/web/client/translations/data.it-IT index 0e481f1f5c..57f5414aca 100644 --- a/web/client/translations/data.it-IT +++ b/web/client/translations/data.it-IT @@ -456,6 +456,7 @@ }, "methods": { "zone": "Zona", + "viewport": "Viewport", "box": "BoundingBox", "buffer": "Buffer", "circle": "Cerchio",