From 7882325d4081698c5f7a18971e0c6f141436766d Mon Sep 17 00:00:00 2001 From: gaetanbrl Date: Tue, 6 Aug 2024 10:03:50 +0200 Subject: [PATCH] Config, button, reux, filter for restricted area --- web/client/actions/featuregrid.js | 6 ++ .../data/featuregrid/FeatureGrid.jsx | 6 ++ .../data/featuregrid/toolbars/Toolbar.jsx | 23 +++++++- web/client/epics/featuregrid.js | 58 +++++++++++++++++-- web/client/plugins/FeatureEditor.jsx | 4 +- .../plugins/featuregrid/panels/index.jsx | 4 +- web/client/reducers/featuregrid.js | 10 +++- web/client/selectors/featuregrid.js | 38 +++++++++++- 8 files changed, 137 insertions(+), 12 deletions(-) diff --git a/web/client/actions/featuregrid.js b/web/client/actions/featuregrid.js index ec62c14b6c..c6e7e10ede 100644 --- a/web/client/actions/featuregrid.js +++ b/web/client/actions/featuregrid.js @@ -7,6 +7,7 @@ */ export const SET_UP = 'FEATUREGRID:SET_UP'; +export const SET_RESTRICTED_AREA = "FEATUREGRID:SET_RESTRICTED_AREA"; export const SELECT_FEATURES = 'FEATUREGRID:SELECT_FEATURES'; export const DESELECT_FEATURES = 'FEATUREGRID:DESELECT_FEATURES'; export const CLEAR_SELECTION = 'FEATUREGRID:CLEAR_SELECTION'; @@ -404,3 +405,8 @@ export const setViewportFilter = (viewportFilter) => ({ type: SET_VIEWPORT_FILTER, value: viewportFilter }); + +export const setRestrictedArea = (area) => ({ + type: SET_RESTRICTED_AREA, + area: area +}); diff --git a/web/client/components/data/featuregrid/FeatureGrid.jsx b/web/client/components/data/featuregrid/FeatureGrid.jsx index 19168b7e20..4448462b22 100644 --- a/web/client/components/data/featuregrid/FeatureGrid.jsx +++ b/web/client/components/data/featuregrid/FeatureGrid.jsx @@ -33,6 +33,9 @@ class FeatureGrid extends React.PureComponent { static propTypes = { autocompleteEnabled: PropTypes.bool, editingAllowedRoles: PropTypes.array, + restrictedArea: PropTypes.object, + restrictedAreaUrl: PropTypes.string, + restrictedAreaOperator: PropTypes.string, gridOpts: PropTypes.object, changes: PropTypes.object, selectBy: PropTypes.object, @@ -56,6 +59,9 @@ class FeatureGrid extends React.PureComponent { }; static defaultProps = { editingAllowedRoles: ["ADMIN"], + restrictedArea: {}, + restrictedAreaUrl: "", + restrictedAreaOperator: "CONTAINS", autocompleteEnabled: false, gridComponent: AdaptiveGrid, changes: {}, diff --git a/web/client/components/data/featuregrid/toolbars/Toolbar.jsx b/web/client/components/data/featuregrid/toolbars/Toolbar.jsx index 47a5b15e19..dda9c9416c 100644 --- a/web/client/components/data/featuregrid/toolbars/Toolbar.jsx +++ b/web/client/components/data/featuregrid/toolbars/Toolbar.jsx @@ -1,7 +1,7 @@ import React from 'react'; import './toolbar.css'; -import { sortBy } from 'lodash'; -import {ButtonGroup, Checkbox, Glyphicon, FormControl, FormGroup, Col} from 'react-bootstrap'; +import { sortBy, isEmpty } from 'lodash'; +import {ButtonGroup, Checkbox, Glyphicon, FormControl, FormGroup, Col, Button} from 'react-bootstrap'; import Message from '../../../I18N/Message'; import withHint from '../enhancers/withHint'; @@ -35,7 +35,23 @@ const standardButtons = { disabled={disabled} visible={mode === "VIEW" && isEditingAllowed && areLayerFeaturesEditable(layer)} onClick={events.switchEditMode} - glyph="pencil"/>), + glyph="pencil" />), + isRestrictedByArea: ({ restrictedArea }) => { + return + }, filter: ({isFilterActive = false, viewportFilter, disabled, isSearchAllowed, mode, showAdvancedFilterButton = true, events = {}}) => ( { const defaultFeatureProj = getDefaultFeatureProjection(); @@ -205,7 +212,16 @@ const setupDrawSupport = (state, original) => { }); // Remove features with geometry null or id "empty_row" - const cleanFeatures = features.filter(ft => ft.geometry !== null || ft.id !== 'empty_row'); + const cleanFeatures = features.filter(ft => { + console.log("clean features"); + const restrictedArea = restrictedAreaSelector(state); + let isValidFeature = ft.geometry !== null || ft.id !== 'empty_row'; + if (isValidFeature && !isEmpty(restrictedArea)) { + // allow only feature inside restricted area + isValidFeature = booleanIntersects(restrictedArea, ft.geometry); + } + return isValidFeature + }); if (cleanFeatures.length > 0) { return Rx.Observable.from([ @@ -262,7 +278,7 @@ const createLoadPageFlow = (store) => ({page, size, reason} = {}) => { wfsURL(state), addPagination({ ...(wfsFilter(state)), - ...viewportFilter(state) + ...additionnalGridFilters(state) }, getPagination(state, {page, size}) ), @@ -487,6 +503,7 @@ export const enableGeometryFilterOnEditMode = (action$, store) => action$.ofType(TOGGLE_MODE) .filter(() => modeSelector(store.getState()) === MODES.EDIT) .switchMap(() => { + console.log("enableGeometryFilterOnEditMode") const currentFilter = find(getAttributeFilters(store.getState()), f => f.type === 'geometry') || {}; return currentFilter.value ? Rx.Observable.empty() : Rx.Observable.of(updateFilter({ attribute: findGeometryProperty(describeSelector(store.getState())).name, @@ -1283,3 +1300,36 @@ export const resetViewportFilter = (action$, store) => return viewportFilter(store.getState()) !== null ? Rx.Observable.of(setViewportFilter(null)) : Rx.Observable.empty(); }); + + export const requestRestrictedArea = (action$, store) => + action$.ofType(OPEN_FEATURE_GRID, LOGIN_SUCCESS) + .filter(() => + { + return !isAdminUserSelector(store.getState()) + && isLoggedIn(store.getState()) + && !isEmpty(restrictedAreaSrcSelector(store.getState()))} + ) + .switchMap((action) => { + const src = restrictedAreaSrcSelector(store.getState()); + if (src.url) { + return Rx.Observable.defer(() => fetch(src?.url).then(r => r?.json?.())) + .switchMap(result => { + return Rx.Observable.of( + setRestrictedArea(result), + changePage(0) + ) + }) + } else { + return Rx.Observable.of( + setRestrictedArea(src?.raw || {}), + changePage(0) + ) + } + }) + +export const resetRestrictedArea = (action$, store) => + action$.ofType(LOGOUT, CLOSE_FEATURE_GRID) + .filter((a) => !isEmpty(restrictedAreaSrcSelector(store.getState()))) + .switchMap(() => Rx.Observable.of( + setRestrictedArea({}) + )) diff --git a/web/client/plugins/FeatureEditor.jsx b/web/client/plugins/FeatureEditor.jsx index 0f141132e2..78ce960d7a 100644 --- a/web/client/plugins/FeatureEditor.jsx +++ b/web/client/plugins/FeatureEditor.jsx @@ -175,7 +175,9 @@ const EditorPlugin = connect( virtualScroll: this.props.virtualScroll ?? true, editingAllowedRoles: this.props.editingAllowedRoles, editingAllowedGroups: this.props.editingAllowedGroups, - maxStoredPages: this.props.maxStoredPages + maxStoredPages: this.props.maxStoredPages, + restrictedAreaUrl: this.props.restrictedAreaUrl, + restrictedArea: this.props.restrictedArea }); }, componentDidUpdate(prevProps) { diff --git a/web/client/plugins/featuregrid/panels/index.jsx b/web/client/plugins/featuregrid/panels/index.jsx index d07fc5af1b..8b5b2ef6a4 100644 --- a/web/client/plugins/featuregrid/panels/index.jsx +++ b/web/client/plugins/featuregrid/panels/index.jsx @@ -47,7 +47,8 @@ import { timeSyncActive, isViewportFilterActive, isFilterByViewportSupported, - selectedLayerSelector + selectedLayerSelector, + restrictedAreaSelector } from '../../../selectors/featuregrid'; import { mapLayoutValuesSelector } from '../../../selectors/maplayout'; import {isCesium, mapTypeSelector} from '../../../selectors/maptype'; @@ -95,6 +96,7 @@ const Toolbar = connect( disableZoomAll: (state) => state && state.featuregrid.virtualScroll || featureCollectionResultSelector(state).features.length === 0, isSearchAllowed: (state) => !isCesium(state), isEditingAllowed: isEditingAllowedSelector, + restrictedArea: restrictedAreaSelector, hasSupportedGeometry, isFilterActive, showTimeSyncButton: showTimeSync, diff --git a/web/client/reducers/featuregrid.js b/web/client/reducers/featuregrid.js index 4ecbd3438f..5d27311f5f 100644 --- a/web/client/reducers/featuregrid.js +++ b/web/client/reducers/featuregrid.js @@ -47,7 +47,8 @@ import { SET_TIME_SYNC, UPDATE_EDITORS_OPTIONS, SET_PAGINATION, - SET_VIEWPORT_FILTER + SET_VIEWPORT_FILTER, + SET_RESTRICTED_AREA, } from '../actions/featuregrid'; import { MAP_CONFIG_LOADED } from '../actions/config'; @@ -156,7 +157,9 @@ function featuregrid(state = emptyResultsState, action) { editingAllowedRoles: action.options.editingAllowedRoles || state.editingAllowedRoles || ["ADMIN"], editingAllowedGroups: action.options.editingAllowedGroups || state.editingAllowedGroups || [], virtualScroll: !!action.options.virtualScroll, - maxStoredPages: action.options.maxStoredPages || 5 + maxStoredPages: action.options.maxStoredPages || 5, + restrictedAreaUrl: action.options.restrictedAreaUrl || "", + restrictedArea: action.options.restrictedArea || {} }); } case LOAD_MORE_FEATURES: @@ -440,6 +443,9 @@ function featuregrid(state = emptyResultsState, action) { } case MAP_CONFIG_LOADED: { return {...state, ...get(action, 'config.featureGrid', {})}; + } + case SET_RESTRICTED_AREA: { + return { ...state, restrictedArea: { ...state.restrictedArea, geometry: action.area } }; } default: return state; diff --git a/web/client/selectors/featuregrid.js b/web/client/selectors/featuregrid.js index 8f460fa483..e7ba0342ea 100644 --- a/web/client/selectors/featuregrid.js +++ b/web/client/selectors/featuregrid.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -import { head, get, isObject } from 'lodash'; +import { head, get, isObject, isEmpty } from 'lodash'; import { getLayerFromId } from './layers'; import { findGeometryProperty } from '../utils/ogc/WFS/base'; @@ -202,6 +202,11 @@ export const isEditingAllowedSelector = (state) => { })(state); return (canEdit || isAllowed) && !isCesium(state); }; + +export const restrictedAreaSrcSelector = state => get(state, "featuregrid.restrictedArea"); +export const restrictedAreaOperatorSelector = state => get(state, "featuregrid.restrictedArea.operator"); +export const restrictedAreaSelector = state => get(state, "featuregrid.restrictedArea.geometry"); + export const paginationSelector = state => get(state, "featuregrid.pagination"); export const useLayerFilterSelector = state => get(state, "featuregrid.useLayerFilter", true); @@ -235,3 +240,34 @@ export const viewportFilter = createShallowSelectorCreator(isEqual)( } : {}; } ); + +export const restrictedAreaFilter = createShallowSelectorCreator(isEqual)( + restrictedAreaSelector, + projectionSelector, + describeSelector, + state => restrictedAreaOperatorSelector(state), + (restrictedArea, projection, describeLayer, operator) => { + const attribute = findGeometryProperty(describeLayer)?.name; + console.log(restrictedArea); + return !isEmpty(restrictedArea) ? { + spatialField: [ + { + geometry: { + ...restrictedArea, + projection: "EPSG:4326" + }, + attribute: attribute, + method: "Polygon", + operation: operator || "CONTAINS", + restrictedArea: true + } + ] + } : {}; + } +) + +export const additionnalGridFilters = (state) => { + const restrictedArea = restrictedAreaFilter(state)?.spatialField || []; + const viewport = viewportFilter(state)?.spatialField || []; + return {spatialField: [...restrictedArea, ...viewport]} +}