From 73cece8cc4e15d4c5a405ce31eb9283b9741b5c9 Mon Sep 17 00:00:00 2001 From: Edgar Vitus Mlowe Date: Thu, 17 May 2018 11:07:48 +0300 Subject: [PATCH] Showing spatial filter selection area when FeatureGrid is open (#2906) --- .../actions/__tests__/featuregrid-test.js | 9 ++ web/client/actions/featuregrid.js | 8 ++ web/client/plugins/FeatureEditor.jsx | 13 ++- web/client/plugins/Map.jsx | 5 +- .../reducers/__tests__/featuregrid-test.js | 6 +- web/client/reducers/featuregrid.js | 7 +- .../selectors/__tests__/highlight-test.js | 83 ++++++++++++++++++- web/client/selectors/highlight.js | 50 ++++++++++- 8 files changed, 170 insertions(+), 11 deletions(-) diff --git a/web/client/actions/__tests__/featuregrid-test.js b/web/client/actions/__tests__/featuregrid-test.js index 6dcb055aa8..cb9d730da1 100644 --- a/web/client/actions/__tests__/featuregrid-test.js +++ b/web/client/actions/__tests__/featuregrid-test.js @@ -40,6 +40,7 @@ const { sizeChange, SIZE_CHANGE, START_SYNC_WMS, startSyncWMS, storeAdvancedSearchFilter, STORE_ADVANCED_SEARCH_FILTER, + setShowCurrentFilter, SET_SHOW_CURRENT_FILTER, fatureGridQueryResult, GRID_QUERY_RESULT, moreFeatures, LOAD_MORE_FEATURES, hideSyncPopover, HIDE_SYNC_POPOVER, @@ -294,4 +295,12 @@ describe('Test correctness of featurgrid actions', () => { expect(retval.type).toBe(LOAD_MORE_FEATURES); expect(retval.pages).toBe(pages); }); + + it('Test setShowCurrentFilter', () => { + const showFilteredObject = true; + const retval = setShowCurrentFilter(showFilteredObject); + expect(retval).toExist(); + expect(retval.type).toBe(SET_SHOW_CURRENT_FILTER); + expect(retval.showFilteredObject).toBe(showFilteredObject); + }); }); diff --git a/web/client/actions/featuregrid.js b/web/client/actions/featuregrid.js index 0e5899cda6..db93df7077 100644 --- a/web/client/actions/featuregrid.js +++ b/web/client/actions/featuregrid.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. */ +const SET_SHOW_CURRENT_FILTER = 'SET_SHOW_CURRENT_FILTER'; const SELECT_FEATURES = 'FEATUREGRID:SELECT_FEATURES'; const DESELECT_FEATURES = 'FEATUREGRID:DESELECT_FEATURES'; const CLEAR_SELECTION = 'FEATUREGRID:CLEAR_SELECTION'; @@ -106,6 +107,12 @@ function selectFeatures(features, append) { append }; } +function setShowCurrentFilter(showFilteredObject) { + return { + type: SET_SHOW_CURRENT_FILTER, + showFilteredObject + }; +} function geometryChanged(features) { return { type: GEOMETRY_CHANGED, @@ -364,6 +371,7 @@ module.exports = { OPEN_FEATURE_GRID, openFeatureGrid, CLOSE_FEATURE_GRID_CONFIRM, closeFeatureGridConfirm, FEATURE_GRID_CLOSE_CONFIRMED, closeFeatureGridConfirmed, + SET_SHOW_CURRENT_FILTER, setShowCurrentFilter, DISABLE_TOOLBAR, disableToolbar, OPEN_ADVANCED_SEARCH, openAdvancedSearch, ZOOM_ALL, zoomAll, diff --git a/web/client/plugins/FeatureEditor.jsx b/web/client/plugins/FeatureEditor.jsx index 40d2473074..d45004fc15 100644 --- a/web/client/plugins/FeatureEditor.jsx +++ b/web/client/plugins/FeatureEditor.jsx @@ -11,7 +11,12 @@ const {createSelector, createStructuredSelector} = require('reselect'); const {bindActionCreators} = require('redux'); const {get} = require('lodash'); -const Grid = require('../components/data/featuregrid/FeatureGrid'); +const {lifecycle} = require('recompose'); +const Grid = lifecycle({ + componentDidMount() { + this.props.onMount(this.props.showFilteredObject); + } +})(require('../components/data/featuregrid/FeatureGrid')); const {paginationInfo, describeSelector, wfsURLSelector, typeNameSelector} = require('../selectors/query'); const {modeSelector, changesSelector, newFeaturesSelector, hasChangesSelector, selectedFeaturesSelector, getDockSize} = require('../selectors/featuregrid'); const { toChangesMap} = require('../utils/FeatureGridUtils'); @@ -20,7 +25,7 @@ const BorderLayout = require('../components/layout/BorderLayout'); const EMPTY_ARR = []; const EMPTY_OBJ = {}; const {gridTools, gridEvents, pageEvents, toolbarEvents} = require('./featuregrid/index'); -const {initPlugin, sizeChange} = require('../actions/featuregrid'); +const {initPlugin, sizeChange, setShowCurrentFilter} = require('../actions/featuregrid'); const ContainerDimensions = require('react-container-dimensions').default; const {mapLayoutValuesSelector} = require('../selectors/maplayout'); const Dock = connect(createSelector( @@ -42,6 +47,7 @@ const Dock = connect(createSelector( * @prop {number} cfg.maxStoredPages default 5. In virtual Scroll mode determines the size of the loaded pages cache * @prop {number} cfg.vsOverScan default 20. Number of rows to load above/below the visible slice of the grid * @prop {number} cfg.scrollDebounce default 50. milliseconds of debounce interval between two scroll event + * @prop {boolean} cfg.showFilteredObject default false. Displays spatial filter selection area when true * @classdesc * FeatureEditor Plugin Provides functionalities to browse/edit data via WFS. The grid can be configured to use paging or *
virtual scroll mechanisms. By defualt virtual scroll is enabled. When on virtual scroll mode, the maxStoredPages param @@ -139,6 +145,8 @@ const FeatureDock = (props = { footer={getFooter(props)}> {getDialogs(props.tools)} ({ + onMount: bindActionCreators(setShowCurrentFilter, dispatch), gridEvents: bindActionCreators(gridEvents, dispatch), pageEvents: bindActionCreators(pageEvents, dispatch), initPlugin: bindActionCreators((options) => initPlugin(options), dispatch), diff --git a/web/client/plugins/Map.jsx b/web/client/plugins/Map.jsx index 2f9d30d39e..741df828b7 100644 --- a/web/client/plugins/Map.jsx +++ b/web/client/plugins/Map.jsx @@ -207,6 +207,7 @@ class MapPlugin extends React.Component { key={feature.id} crs={projection} type={feature.type} + style={feature.style || null } geometry={feature.geometry}/>); })} ); @@ -318,7 +319,7 @@ class MapPlugin extends React.Component { const {mapSelector, projectionDefsSelector} = require('../selectors/map'); const { mapTypeSelector } = require('../selectors/maptype'); const {layerSelectorWithMarkers} = require('../selectors/layers'); -const {selectedFeatures} = require('../selectors/highlight'); +const {highlighedFeatures} = require('../selectors/highlight'); const {securityTokenSelector} = require('../selectors/security'); const selector = createSelector( @@ -327,7 +328,7 @@ const selector = createSelector( mapSelector, mapTypeSelector, layerSelectorWithMarkers, - selectedFeatures, + highlighedFeatures, (state) => state.mapInitialConfig && state.mapInitialConfig.loadingError && state.mapInitialConfig.loadingError.data, securityTokenSelector, (state) => state.mousePosition && state.mousePosition.enabled diff --git a/web/client/reducers/__tests__/featuregrid-test.js b/web/client/reducers/__tests__/featuregrid-test.js index 0970cb0dc9..b62fc1d4e3 100644 --- a/web/client/reducers/__tests__/featuregrid-test.js +++ b/web/client/reducers/__tests__/featuregrid-test.js @@ -47,7 +47,7 @@ const featuregrid = require('../featuregrid'); const {setFeatures, dockSizeFeatures, setLayer, toggleTool, customizeAttribute, selectFeatures, deselectFeatures, createNewFeatures, updateFilter, featureSaving, toggleSelection, clearSelection, MODES, toggleEditMode, toggleViewMode, saveSuccess, clearChanges, saveError, startDrawingFeature, deleteGeometryFeature, geometryChanged, setSelectionOptions, changePage, featureModified, setPermission, disableToolbar, openFeatureGrid, closeFeatureGrid, - toggleShowAgain, hideSyncPopover, initPlugin, sizeChange, storeAdvancedSearchFilter} = require('../../actions/featuregrid'); + toggleShowAgain, hideSyncPopover, initPlugin, sizeChange, storeAdvancedSearchFilter, setShowCurrentFilter} = require('../../actions/featuregrid'); const {featureTypeLoaded, createQuery} = require('../../actions/wfsquery'); const {changeDrawingStatus} = require('../../actions/draw'); @@ -324,4 +324,8 @@ describe('Test the featuregrid reducer', () => { let state = featuregrid({selectedLayer: "test_layer"}, storeAdvancedSearchFilter(filterObj)); expect(state.advancedFilters.test_layer).toBe(filterObj); }); + it('SET_SHOW_CURRENT_FILTER', () => { + let state = featuregrid({}, setShowCurrentFilter(true)); + expect(state.showFilteredObject).toBe(true); + }); }); diff --git a/web/client/reducers/featuregrid.js b/web/client/reducers/featuregrid.js index 84aa167c68..1bb067fedd 100644 --- a/web/client/reducers/featuregrid.js +++ b/web/client/reducers/featuregrid.js @@ -40,7 +40,8 @@ const { SIZE_CHANGE, STORE_ADVANCED_SEARCH_FILTER, GRID_QUERY_RESULT, - LOAD_MORE_FEATURES + LOAD_MORE_FEATURES, + SET_SHOW_CURRENT_FILTER } = require('../actions/featuregrid'); const{ FEATURE_TYPE_LOADED, @@ -57,6 +58,7 @@ const emptyResultsState = { filters: {}, editingAllowedRoles: ["ADMIN"], enableColumnFilters: true, + showFilteredObject: true, open: false, canEdit: false, focusOnEdit: true, @@ -179,6 +181,9 @@ function featuregrid(state = emptyResultsState, action) { case SET_SELECTION_OPTIONS: { return assign({}, state, {multiselect: action.multiselect}); } + case SET_SHOW_CURRENT_FILTER: { + return assign({}, state, { showFilteredObject: action.showFilteredObject}); + } case CLEAR_SELECTION: return assign({}, state, {select: [], changes: []}); case SET_FEATURES: diff --git a/web/client/selectors/__tests__/highlight-test.js b/web/client/selectors/__tests__/highlight-test.js index 88affde660..9ca7d44d84 100644 --- a/web/client/selectors/__tests__/highlight-test.js +++ b/web/client/selectors/__tests__/highlight-test.js @@ -8,8 +8,9 @@ const expect = require('expect'); const { - selectedFeatures -} = require('../highlight'); + selectedFeatures, filteredspatialObject, filteredspatialObjectCoord, + filteredGeometry, filteredSpatialObjectId, filteredSpatialObjectCrs, + filteredspatialObjectType, filteredFeatures, highlighedFeatures} = require('../highlight'); const idFt1 = "idFt1"; const idFt2 = "idFt2"; @@ -36,14 +37,42 @@ let feature2 = { someProp: "someValue" } }; + +let feature3 = [{ + type: "Feature", + geometry: { + type: 'Polygon', + coordinates: [ [ 0.000008983152841195214, 0.000017966305681987637 ] ] + }, + style: { + fillColor: 'rgba(255, 255, 255, 0.2)', + color: '#ffcc33' + }, + id: 'spatial_object' +}]; const initialState = { featuregrid: { mode: modeEdit, select: [feature1, feature2], - changes: [feature2] + changes: [feature2], + showFilteredObject: true, + open: true }, highlight: { featuresPath: "featuregrid.select" + }, + query: { + filterObj: { + spatialField: { + geometry: { + type: 'Polygon', + coordinates: [[ 1, 2]], + projection: 'EPSG:3857', + id: 'spatial_object' + + } + } + } } }; @@ -60,4 +89,52 @@ describe('Test highlight selectors', () => { expect(features).toExist(); expect(features.length).toBe(0); }); + it('test filteredspatialObject', () => { + const spatialObject = initialState.query.filterObj.spatialField; + const features = filteredspatialObject(initialState); + expect(features).toExist(); + expect(features).toBe(spatialObject); + }); + it('test filteredGeometry', () => { + const geometry = initialState.query.filterObj.spatialField.geometry; + const features = filteredGeometry(initialState); + expect(features).toExist(); + expect(features).toBe(geometry); + }); + it('test filteredspatialObjectCoord', () => { + const coordinates = initialState.query.filterObj.spatialField.geometry.coordinates; + const features = filteredspatialObjectCoord(initialState); + expect(features).toExist(); + expect(features).toBe(coordinates); + }); + it('test filteredSpatialObjectId', () => { + const geometryId = initialState.query.filterObj.spatialField.geometry.id; + const features = filteredSpatialObjectId(initialState); + expect(features).toExist(); + expect(features).toBe(geometryId); + }); + it('test filteredSpatialObjectCrs', () => { + const geometryCrs = initialState.query.filterObj.spatialField.geometry.projection; + const features = filteredSpatialObjectCrs(initialState); + expect(features).toExist(); + expect(features).toBe(geometryCrs); + }); + it('test filteredspatialObjectType', () => { + const geometryType = initialState.query.filterObj.spatialField.geometry.type; + const features = filteredspatialObjectType(initialState); + expect(features).toExist(); + expect(features).toBe(geometryType); + }); + it('test filteredFeatures', () => { + const features = filteredFeatures(initialState); + expect(features).toExist(); + expect(features).toEqual(feature3); + }); + it('test highlighedFeatures', () => { + const features = highlighedFeatures(initialState); + const featuresSelected = initialState.featuregrid.select; + const combinedFeatures = [...featuresSelected, ...feature3]; + expect(features).toExist(); + expect(features).toEqual(combinedFeatures); + }); }); diff --git a/web/client/selectors/highlight.js b/web/client/selectors/highlight.js index f42c36d942..a9084beb62 100644 --- a/web/client/selectors/highlight.js +++ b/web/client/selectors/highlight.js @@ -1,7 +1,53 @@ const {get} = require('lodash'); +const {createSelector} = require('reselect'); +const {reprojectGeoJson} = require('../utils/CoordinatesUtils'); +const selectedFeatures = (state) => get(state, state && state.highlight && state.highlight.featuresPath || "highlight.emptyFeatures"); +const filteredspatialObject = (state) => get(state, state && state.featuregrid.open && state.featuregrid.showFilteredObject && "query.filterObj.spatialField" || "emptyObject"); +const filteredGeometry = (state) => filteredspatialObject(state) && filteredspatialObject(state).geometry; +const filteredspatialObjectType = (state) => filteredGeometry(state) && filteredGeometry(state).type || "Polygon"; +const filteredspatialObjectCoord = (state) => filteredGeometry(state) && filteredGeometry(state).coordinates || []; +const filteredSpatialObjectCrs = (state) => filteredGeometry(state) && filteredGeometry(state).projection || "EPSG:3857"; +const filteredSpatialObjectId = (state) => filteredGeometry(state) && filteredGeometry(state).id || "spatial_object"; +const filteredFeatures = createSelector( + [ + filteredspatialObjectCoord, + filteredspatialObjectType, + filteredSpatialObjectId, + filteredSpatialObjectCrs + ], + ( geometryCoordinates, geometryType, geometryId, geometryCrs) => { + let geometry = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { + type: geometryType, + coordinates: geometryCoordinates + }, + style: { + fillColor: 'rgba(255, 255, 255, 0.2)', + color: '#ffcc33' + }, + id: geometryId + } + ] + }; + return geometryCoordinates.length > 0 && geometryType ? reprojectGeoJson(geometry, geometryCrs, 'EPSG:4326').features : []; + } -module.exports = { - selectedFeatures: (state) => get(state, state && state.highlight && state.highlight.featuresPath || "highlight.emptyFeatures") +); + +const highlighedFeatures = createSelector( + [ + filteredFeatures, + selectedFeatures + ], + (featuresFiltered, featuresSelected) => [ ...featuresSelected, ...featuresFiltered] +); +module.exports = { + selectedFeatures, filteredFeatures, filteredSpatialObjectId, filteredSpatialObjectCrs, filteredspatialObjectCoord, + filteredspatialObjectType, filteredGeometry, filteredspatialObject, highlighedFeatures };