From 0da5c97195ade88fe6a125111a631beb52efb4fc Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 16:01:52 +0100 Subject: [PATCH 01/18] Add documentation for soon to be added action setFeatureInformation --- packages/plugins/Gfi/CHANGELOG.md | 4 +++ packages/plugins/Gfi/README.md | 46 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/packages/plugins/Gfi/CHANGELOG.md b/packages/plugins/Gfi/CHANGELOG.md index 5b91da3ad..58c1e3b33 100644 --- a/packages/plugins/Gfi/CHANGELOG.md +++ b/packages/plugins/Gfi/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## unpublished + +- Feature: Add new action `setFeatureInformation` to be able to set feature information in the store and trigger all relevant processes so that the information displayed to the user is as if he has selected the features himself. + ## 2.0.0 - Breaking: Upgrade `@masterportal/masterportalapi` from `2.8.0` to `2.40.0` and subsequently `ol` from `^7.1.0` to `^9.2.4`. diff --git a/packages/plugins/Gfi/README.md b/packages/plugins/Gfi/README.md index 1f7bef7cc..99b627844 100644 --- a/packages/plugins/Gfi/README.md +++ b/packages/plugins/Gfi/README.md @@ -178,6 +178,52 @@ featureList: { ## Store +### Actions + +#### setFeatureInformation + +This method can be used to set the feature information in the store and trigger all relevant processes so that the information displayed to the user is as if he has selected the features himself. + +```js +map.$store.dispatch('plugin/gfi/setFeatureInformation', { + "anotherLayer": [], + "yetAnotherLayer": [], + "relevantInformation": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 565669.6521397199, + 5930516.358614317 + ] + }, + "properties": { + "propertyOne": "B0", + "propertyTwo": "B1" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 565594.9377660984, + 5930524.52634174 + ] + }, + "properties": { + propertyOne: "A0", + propertyTwo: "A1" + } + } + ] +}) +``` + +The payload object has to include all layers configured to be used with this plugin. +The value assigned to those keys is an array of GeoJSON-Features. + ### State If a successful query has been sent and a response has been received, the result will be saved in the store and can be subscribed through the path `'plugin/gfi/featureInformation'`. If, however, a query for a layer fails, a `Symbol` containing the error will be saved in the store instead to indicate the error. From efebbc00e849963899481ec7efdb80cf6e93f649 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 16:05:36 +0100 Subject: [PATCH 02/18] Move action setupTooltip to a separate file --- .../plugins/Gfi/src/store/actions/index.ts | 53 +----------------- .../plugins/Gfi/src/store/actions/setup.ts | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 packages/plugins/Gfi/src/store/actions/setup.ts diff --git a/packages/plugins/Gfi/src/store/actions/index.ts b/packages/plugins/Gfi/src/store/actions/index.ts index f4ea15fe8..35568b669 100644 --- a/packages/plugins/Gfi/src/store/actions/index.ts +++ b/packages/plugins/Gfi/src/store/actions/index.ts @@ -1,18 +1,17 @@ import debounce from 'lodash.debounce' import { Style, Fill, Stroke } from 'ol/style' -import Overlay from 'ol/Overlay' import { GeoJSON } from 'ol/format' import { Feature } from 'ol' import { Feature as GeoJsonFeature, GeoJsonProperties } from 'geojson' import { PolarActionTree } from '@polar/lib-custom-types' import getCluster from '@polar/lib-get-cluster' -import { getTooltip, Tooltip } from '@polar/lib-tooltip' import { DragBox } from 'ol/interaction' import { platformModifierKeyOnly } from 'ol/events/condition' import { getFeatureDisplayLayer, clear } from '../../utils/displayFeatureLayer' import { GfiGetters, GfiState } from '../../types' import { getOriginalFeature } from '../../utils/getOriginalFeature' import { debouncedGfiRequest } from './debouncedGfiRequest' +import { setupTooltip } from './setup' // OK for module action set creation // eslint-disable-next-line max-lines-per-function @@ -143,55 +142,7 @@ export const makeActions = () => { ) } }, - setupTooltip({ getters: { gfiConfiguration }, rootGetters: { map } }) { - const tooltipLayerIds = Object.keys(gfiConfiguration.layers).filter( - (key) => gfiConfiguration.layers[key].showTooltip - ) - if (!tooltipLayerIds.length) { - return - } - - let element: Tooltip['element'], unregister: Tooltip['unregister'] - const overlay = new Overlay({ - positioning: 'bottom-center', - offset: [0, -5], - }) - map.addOverlay(overlay) - map.on('pointermove', ({ pixel, dragging, originalEvent }) => { - if (dragging || ['touch', 'pen'].includes(originalEvent.pointerType)) { - return - } - let hasFeatureAtPixel = false - // stops on return `true`, thus only using the uppermost feature - map.forEachFeatureAtPixel( - pixel, - (feature, layer) => { - if (!(feature instanceof Feature)) { - return false - } - hasFeatureAtPixel = true - overlay.setPosition(map.getCoordinateFromPixel(pixel)) - if (unregister) { - unregister() - } - ;({ element, unregister } = getTooltip({ - localeKeys: - // @ts-expect-error | it exists by virtue of layerFilter below - gfiConfiguration.layers[layer.get('id')].showTooltip( - feature, - map - ), - })) - overlay.setElement(element) - return true - }, - { layerFilter: (layer) => tooltipLayerIds.includes(layer.get('id')) } - ) - if (!hasFeatureAtPixel) { - overlay.setPosition(undefined) - } - }) - }, + setupTooltip, setupFeatureVisibilityUpdates({ commit, state, getters, rootGetters }) { // debounce to prevent update spam debouncedVisibilityChangeIndicator = debounce( diff --git a/packages/plugins/Gfi/src/store/actions/setup.ts b/packages/plugins/Gfi/src/store/actions/setup.ts new file mode 100644 index 000000000..7df148332 --- /dev/null +++ b/packages/plugins/Gfi/src/store/actions/setup.ts @@ -0,0 +1,55 @@ +import { getTooltip, Tooltip } from '@polar/lib-tooltip' +import Overlay from 'ol/Overlay' +import { Feature } from 'ol' +import { PolarActionContext } from '@polar/lib-custom-types' +import { GfiGetters, GfiState } from '../../types' + +export function setupTooltip({ + getters: { gfiConfiguration }, + rootGetters: { map }, +}: PolarActionContext) { + const tooltipLayerIds = Object.keys(gfiConfiguration.layers).filter( + (key) => gfiConfiguration.layers[key].showTooltip + ) + if (!tooltipLayerIds.length) { + return + } + + let element: Tooltip['element'], unregister: Tooltip['unregister'] + const overlay = new Overlay({ + positioning: 'bottom-center', + offset: [0, -5], + }) + map.addOverlay(overlay) + map.on('pointermove', ({ pixel, dragging, originalEvent }) => { + if (dragging || ['touch', 'pen'].includes(originalEvent.pointerType)) { + return + } + let hasFeatureAtPixel = false + // stops on return `true`, thus only using the uppermost feature + map.forEachFeatureAtPixel( + pixel, + (feature, layer) => { + if (!(feature instanceof Feature)) { + return false + } + hasFeatureAtPixel = true + overlay.setPosition(map.getCoordinateFromPixel(pixel)) + if (unregister) { + unregister() + } + ;({ element, unregister } = getTooltip({ + localeKeys: + // @ts-expect-error | it exists by virtue of layerFilter below + gfiConfiguration.layers[layer.get('id')].showTooltip(feature, map), + })) + overlay.setElement(element) + return true + }, + { layerFilter: (layer) => tooltipLayerIds.includes(layer.get('id')) } + ) + if (!hasFeatureAtPixel) { + overlay.setPosition(undefined) + } + }) +} From cd273f8064f2a16dd8f99d166bafb6cc9d40850a Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 16:12:27 +0100 Subject: [PATCH 03/18] Move action setupCoreListener to setup action file --- .../plugins/Gfi/src/store/actions/index.ts | 18 +++--------------- .../plugins/Gfi/src/store/actions/setup.ts | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/plugins/Gfi/src/store/actions/index.ts b/packages/plugins/Gfi/src/store/actions/index.ts index 35568b669..08f73d492 100644 --- a/packages/plugins/Gfi/src/store/actions/index.ts +++ b/packages/plugins/Gfi/src/store/actions/index.ts @@ -11,7 +11,7 @@ import { getFeatureDisplayLayer, clear } from '../../utils/displayFeatureLayer' import { GfiGetters, GfiState } from '../../types' import { getOriginalFeature } from '../../utils/getOriginalFeature' import { debouncedGfiRequest } from './debouncedGfiRequest' -import { setupTooltip } from './setup' +import { setupCoreListener, setupTooltip } from './setup' // OK for module action set creation // eslint-disable-next-line max-lines-per-function @@ -66,19 +66,8 @@ export const makeActions = () => { dispatch('setupZoomListeners') dispatch('setupMultiSelection') }, - setupCoreListener({ - getters: { gfiConfiguration }, - rootGetters, - dispatch, - }) { - if (gfiConfiguration.featureList?.bindWithCoreHoverSelect) { - this.watch( - () => rootGetters.selected, - (feature) => dispatch('setOlFeatureInformation', { feature }), - { deep: true } - ) - } - }, + setupCoreListener, + setupTooltip, setupMultiSelection({ dispatch, getters, rootGetters }) { if (getters.gfiConfiguration.boxSelect) { const dragBox = new DragBox({ condition: platformModifierKeyOnly }) @@ -142,7 +131,6 @@ export const makeActions = () => { ) } }, - setupTooltip, setupFeatureVisibilityUpdates({ commit, state, getters, rootGetters }) { // debounce to prevent update spam debouncedVisibilityChangeIndicator = debounce( diff --git a/packages/plugins/Gfi/src/store/actions/setup.ts b/packages/plugins/Gfi/src/store/actions/setup.ts index 7df148332..f9bd43312 100644 --- a/packages/plugins/Gfi/src/store/actions/setup.ts +++ b/packages/plugins/Gfi/src/store/actions/setup.ts @@ -1,9 +1,26 @@ import { getTooltip, Tooltip } from '@polar/lib-tooltip' import Overlay from 'ol/Overlay' import { Feature } from 'ol' -import { PolarActionContext } from '@polar/lib-custom-types' +import { PolarActionContext, PolarStore } from '@polar/lib-custom-types' import { GfiGetters, GfiState } from '../../types' +export function setupCoreListener( + this: PolarStore, + { + getters: { gfiConfiguration }, + rootGetters, + dispatch, + }: PolarActionContext +) { + if (gfiConfiguration.featureList?.bindWithCoreHoverSelect) { + this.watch( + () => rootGetters.selected, + (feature) => dispatch('setOlFeatureInformation', { feature }), + { deep: true } + ) + } +} + export function setupTooltip({ getters: { gfiConfiguration }, rootGetters: { map }, From 74fcb886855ef090fe956ea04a6c0418c068df8d Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 16:14:49 +0100 Subject: [PATCH 04/18] Move action setupMultiSelection to setup action file --- .../plugins/Gfi/src/store/actions/index.ts | 28 ++--------------- .../plugins/Gfi/src/store/actions/setup.ts | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/packages/plugins/Gfi/src/store/actions/index.ts b/packages/plugins/Gfi/src/store/actions/index.ts index 08f73d492..34c816b71 100644 --- a/packages/plugins/Gfi/src/store/actions/index.ts +++ b/packages/plugins/Gfi/src/store/actions/index.ts @@ -5,13 +5,11 @@ import { Feature } from 'ol' import { Feature as GeoJsonFeature, GeoJsonProperties } from 'geojson' import { PolarActionTree } from '@polar/lib-custom-types' import getCluster from '@polar/lib-get-cluster' -import { DragBox } from 'ol/interaction' -import { platformModifierKeyOnly } from 'ol/events/condition' import { getFeatureDisplayLayer, clear } from '../../utils/displayFeatureLayer' import { GfiGetters, GfiState } from '../../types' import { getOriginalFeature } from '../../utils/getOriginalFeature' import { debouncedGfiRequest } from './debouncedGfiRequest' -import { setupCoreListener, setupTooltip } from './setup' +import { setupCoreListener, setupMultiSelection, setupTooltip } from './setup' // OK for module action set creation // eslint-disable-next-line max-lines-per-function @@ -67,30 +65,8 @@ export const makeActions = () => { dispatch('setupMultiSelection') }, setupCoreListener, + setupMultiSelection, setupTooltip, - setupMultiSelection({ dispatch, getters, rootGetters }) { - if (getters.gfiConfiguration.boxSelect) { - const dragBox = new DragBox({ condition: platformModifierKeyOnly }) - dragBox.on('boxend', () => - dispatch('getFeatureInfo', { - coordinateOrExtent: dragBox.getGeometry().getExtent(), - modifierPressed: true, - }) - ) - rootGetters.map.addInteraction(dragBox) - } - if (getters.gfiConfiguration.directSelect) { - rootGetters.map.on('click', ({ coordinate, originalEvent }) => - dispatch('getFeatureInfo', { - coordinateOrExtent: coordinate, - modifierPressed: - navigator.userAgent.indexOf('Mac') !== -1 - ? originalEvent.metaKey - : originalEvent.ctrlKey, - }) - ) - } - }, setupZoomListeners({ dispatch, getters, rootGetters }) { if (getters.gfiConfiguration.featureList) { this.watch( diff --git a/packages/plugins/Gfi/src/store/actions/setup.ts b/packages/plugins/Gfi/src/store/actions/setup.ts index f9bd43312..4bb658905 100644 --- a/packages/plugins/Gfi/src/store/actions/setup.ts +++ b/packages/plugins/Gfi/src/store/actions/setup.ts @@ -2,6 +2,8 @@ import { getTooltip, Tooltip } from '@polar/lib-tooltip' import Overlay from 'ol/Overlay' import { Feature } from 'ol' import { PolarActionContext, PolarStore } from '@polar/lib-custom-types' +import { DragBox } from 'ol/interaction' +import { platformModifierKeyOnly } from 'ol/events/condition' import { GfiGetters, GfiState } from '../../types' export function setupCoreListener( @@ -21,6 +23,34 @@ export function setupCoreListener( } } +export function setupMultiSelection({ + dispatch, + getters, + rootGetters, +}: PolarActionContext) { + if (getters.gfiConfiguration.boxSelect) { + const dragBox = new DragBox({ condition: platformModifierKeyOnly }) + dragBox.on('boxend', () => + dispatch('getFeatureInfo', { + coordinateOrExtent: dragBox.getGeometry().getExtent(), + modifierPressed: true, + }) + ) + rootGetters.map.addInteraction(dragBox) + } + if (getters.gfiConfiguration.directSelect) { + rootGetters.map.on('click', ({ coordinate, originalEvent }) => + dispatch('getFeatureInfo', { + coordinateOrExtent: coordinate, + modifierPressed: + navigator.userAgent.indexOf('Mac') !== -1 + ? originalEvent.metaKey + : originalEvent.ctrlKey, + }) + ) + } +} + export function setupTooltip({ getters: { gfiConfiguration }, rootGetters: { map }, From 4fad81dfd48caa168b8e65d8e20f14466874f8b0 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 16:19:12 +0100 Subject: [PATCH 05/18] Move action setupZoomListeners to setup action file --- .../plugins/Gfi/src/store/actions/index.ts | 52 +++---------------- .../plugins/Gfi/src/store/actions/setup.ts | 50 +++++++++++++++++- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/packages/plugins/Gfi/src/store/actions/index.ts b/packages/plugins/Gfi/src/store/actions/index.ts index 34c816b71..4e01ce5a5 100644 --- a/packages/plugins/Gfi/src/store/actions/index.ts +++ b/packages/plugins/Gfi/src/store/actions/index.ts @@ -2,14 +2,17 @@ import debounce from 'lodash.debounce' import { Style, Fill, Stroke } from 'ol/style' import { GeoJSON } from 'ol/format' import { Feature } from 'ol' -import { Feature as GeoJsonFeature, GeoJsonProperties } from 'geojson' +import { Feature as GeoJsonFeature } from 'geojson' import { PolarActionTree } from '@polar/lib-custom-types' -import getCluster from '@polar/lib-get-cluster' import { getFeatureDisplayLayer, clear } from '../../utils/displayFeatureLayer' import { GfiGetters, GfiState } from '../../types' -import { getOriginalFeature } from '../../utils/getOriginalFeature' import { debouncedGfiRequest } from './debouncedGfiRequest' -import { setupCoreListener, setupMultiSelection, setupTooltip } from './setup' +import { + setupCoreListener, + setupMultiSelection, + setupTooltip, + setupZoomListeners, +} from './setup' // OK for module action set creation // eslint-disable-next-line max-lines-per-function @@ -67,46 +70,7 @@ export const makeActions = () => { setupCoreListener, setupMultiSelection, setupTooltip, - setupZoomListeners({ dispatch, getters, rootGetters }) { - if (getters.gfiConfiguration.featureList) { - this.watch( - () => rootGetters.zoomLevel, - () => { - const { - featureInformation, - listableLayerSources, - visibleWindowFeatureIndex, - windowFeatures, - } = getters - - if (windowFeatures.length) { - const layerId: string = - // @ts-expect-error | if windowFeatures has features, visibleWindowFeatureIndex is in the range of possible features - windowFeatures[visibleWindowFeatureIndex].polarInternalLayerKey - const selectedFeatureProperties: GeoJsonProperties = { - // eslint-disable-next-line @typescript-eslint/naming-convention - _gfiLayerId: layerId, - ...featureInformation[layerId][visibleWindowFeatureIndex] - .properties, - } - const originalFeature = getOriginalFeature( - listableLayerSources, - selectedFeatureProperties - ) - if (originalFeature) { - dispatch('setOlFeatureInformation', { - feature: getCluster( - rootGetters.map, - originalFeature, - '_gfiLayerId' - ), - }) - } - } - } - ) - } - }, + setupZoomListeners, setupFeatureVisibilityUpdates({ commit, state, getters, rootGetters }) { // debounce to prevent update spam debouncedVisibilityChangeIndicator = debounce( diff --git a/packages/plugins/Gfi/src/store/actions/setup.ts b/packages/plugins/Gfi/src/store/actions/setup.ts index 4bb658905..8247cc59e 100644 --- a/packages/plugins/Gfi/src/store/actions/setup.ts +++ b/packages/plugins/Gfi/src/store/actions/setup.ts @@ -1,10 +1,14 @@ +import { PolarActionContext, PolarStore } from '@polar/lib-custom-types' +import { GeoJsonProperties } from 'geojson' + +import getCluster from '@polar/lib-get-cluster' import { getTooltip, Tooltip } from '@polar/lib-tooltip' import Overlay from 'ol/Overlay' import { Feature } from 'ol' -import { PolarActionContext, PolarStore } from '@polar/lib-custom-types' import { DragBox } from 'ol/interaction' import { platformModifierKeyOnly } from 'ol/events/condition' import { GfiGetters, GfiState } from '../../types' +import { getOriginalFeature } from '../../utils/getOriginalFeature' export function setupCoreListener( this: PolarStore, @@ -100,3 +104,47 @@ export function setupTooltip({ } }) } + +export function setupZoomListeners( + this: PolarStore, + { dispatch, getters, rootGetters }: PolarActionContext +) { + if (getters.gfiConfiguration.featureList) { + this.watch( + () => rootGetters.zoomLevel, + () => { + const { + featureInformation, + listableLayerSources, + visibleWindowFeatureIndex, + windowFeatures, + } = getters + + if (windowFeatures.length) { + const layerId: string = + // @ts-expect-error | if windowFeatures has features, visibleWindowFeatureIndex is in the range of possible features + windowFeatures[visibleWindowFeatureIndex].polarInternalLayerKey + const selectedFeatureProperties: GeoJsonProperties = { + // eslint-disable-next-line @typescript-eslint/naming-convention + _gfiLayerId: layerId, + ...featureInformation[layerId][visibleWindowFeatureIndex] + .properties, + } + const originalFeature = getOriginalFeature( + listableLayerSources, + selectedFeatureProperties + ) + if (originalFeature) { + dispatch('setOlFeatureInformation', { + feature: getCluster( + rootGetters.map, + originalFeature, + '_gfiLayerId' + ), + }) + } + } + } + ) + } +} From 1c0f8b1de751f1317ac79493496321b2a387b8c9 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 16:55:10 +0100 Subject: [PATCH 06/18] Move type FeatureByLayerId to central file --- packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts | 4 +--- packages/plugins/Gfi/src/types.ts | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts b/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts index e0d86b37b..9f3920d73 100644 --- a/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts +++ b/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts @@ -13,15 +13,13 @@ import compare from 'just-compare' import { addFeature } from '../../utils/displayFeatureLayer' import { requestGfi } from '../../utils/requestGfi' import sortFeatures from '../../utils/sortFeatures' -import { GfiGetters, GfiState } from '../../types' +import { FeaturesByLayerId, GfiGetters, GfiState } from '../../types' interface GetFeatureInfoParameters { coordinateOrExtent: [number, number] | [number, number, number, number] modifierPressed?: boolean } -type FeaturesByLayerId = Record - const filterAndMapFeaturesToLayerIds = ( layerKeys: string[], gfiConfiguration: GfiConfiguration, diff --git a/packages/plugins/Gfi/src/types.ts b/packages/plugins/Gfi/src/types.ts index 8e2918ee5..6cb976d86 100644 --- a/packages/plugins/Gfi/src/types.ts +++ b/packages/plugins/Gfi/src/types.ts @@ -34,6 +34,8 @@ export interface RequestGfiWmsParameters { layer: TileLayer } +export type FeaturesByLayerId = Record + /** GFI Vuex Module State */ export interface GfiState { /** default style for stroke and fill of the highlighted feature. */ From 54f8ebd5c5e1d8561c30b31b789d4ceb2dc30d9f Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 16:57:07 +0100 Subject: [PATCH 07/18] Move filterFeatures to a separate file to be a util function --- .../Gfi/src/store/actions/debouncedGfiRequest.ts | 12 +----------- packages/plugins/Gfi/src/utils/filterFeatures.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 packages/plugins/Gfi/src/utils/filterFeatures.ts diff --git a/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts b/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts index 9f3920d73..6182fdee8 100644 --- a/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts +++ b/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts @@ -11,6 +11,7 @@ import { Geometry } from 'ol/geom' import VectorLayer from 'ol/layer/Vector' import compare from 'just-compare' import { addFeature } from '../../utils/displayFeatureLayer' +import { filterFeatures } from '../../utils/filterFeatures' import { requestGfi } from '../../utils/requestGfi' import sortFeatures from '../../utils/sortFeatures' import { FeaturesByLayerId, GfiGetters, GfiState } from '../../types' @@ -91,17 +92,6 @@ const getPromisedFeatures = ( }) }) -const filterFeatures = ( - featuresByLayerId: FeaturesByLayerId -): Record => { - const entries = Object.entries(featuresByLayerId) - const filtered = entries.filter((keyValue) => Array.isArray(keyValue[1])) as [ - string, - GeoJsonFeature[] - ][] - return Object.fromEntries(filtered) -} - const createSelectionDiff = ( oldSelection: FeaturesByLayerId, newSelection: FeaturesByLayerId diff --git a/packages/plugins/Gfi/src/utils/filterFeatures.ts b/packages/plugins/Gfi/src/utils/filterFeatures.ts new file mode 100644 index 000000000..3e63bb1d3 --- /dev/null +++ b/packages/plugins/Gfi/src/utils/filterFeatures.ts @@ -0,0 +1,13 @@ +import { Feature as GeoJsonFeature } from 'geojson' +import { FeaturesByLayerId } from '../types' + +export function filterFeatures( + featuresByLayerId: FeaturesByLayerId +): Record { + const entries = Object.entries(featuresByLayerId) + const filtered = entries.filter((keyValue) => Array.isArray(keyValue[1])) as [ + string, + GeoJsonFeature[] + ][] + return Object.fromEntries(filtered) +} From 6f82b9b4336d1272e344893937e8fcc7fc7e5f52 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 21:15:06 +0100 Subject: [PATCH 08/18] Add information regarding isSelectable and the override --- packages/plugins/Gfi/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/plugins/Gfi/README.md b/packages/plugins/Gfi/README.md index 99b627844..8f67885d2 100644 --- a/packages/plugins/Gfi/README.md +++ b/packages/plugins/Gfi/README.md @@ -182,7 +182,10 @@ featureList: { #### setFeatureInformation -This method can be used to set the feature information in the store and trigger all relevant processes so that the information displayed to the user is as if he has selected the features himself. +This method can be used to set the feature information in the store and trigger all relevant processes so that the information displayed to the user is as if he has selected the features himself. +Note that calling this method completely overrides the previously set feature information. + +If a layer has a `isSelectable`-function configured, the features are filtered using that function. ```js map.$store.dispatch('plugin/gfi/setFeatureInformation', { From 16e90084c3f279899e495d6bc8ecefac0ab76837 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 21:19:39 +0100 Subject: [PATCH 09/18] Add information on how to reset the selection --- packages/plugins/Gfi/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/plugins/Gfi/README.md b/packages/plugins/Gfi/README.md index 8f67885d2..b8ae09cef 100644 --- a/packages/plugins/Gfi/README.md +++ b/packages/plugins/Gfi/README.md @@ -227,6 +227,8 @@ map.$store.dispatch('plugin/gfi/setFeatureInformation', { The payload object has to include all layers configured to be used with this plugin. The value assigned to those keys is an array of GeoJSON-Features. +The selected feature information can be reset by calling the method with an empty object. + ### State If a successful query has been sent and a response has been received, the result will be saved in the store and can be subscribed through the path `'plugin/gfi/featureInformation'`. If, however, a query for a layer fails, a `Symbol` containing the error will be saved in the store instead to indicate the error. From 4bf3c58e6ef9f3c162dcd791c49fb75825669b62 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 21:24:53 +0100 Subject: [PATCH 10/18] Move part of a function to be a new utility function renderFeatures --- .../src/store/actions/debouncedGfiRequest.ts | 14 ++++++------- .../plugins/Gfi/src/utils/renderFeatures.ts | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 packages/plugins/Gfi/src/utils/renderFeatures.ts diff --git a/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts b/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts index 6182fdee8..bbe6a8661 100644 --- a/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts +++ b/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts @@ -15,6 +15,7 @@ import { filterFeatures } from '../../utils/filterFeatures' import { requestGfi } from '../../utils/requestGfi' import sortFeatures from '../../utils/sortFeatures' import { FeaturesByLayerId, GfiGetters, GfiState } from '../../types' +import { renderFeatures } from '../../utils/renderFeatures' interface GetFeatureInfoParameters { coordinateOrExtent: [number, number] | [number, number, number, number] @@ -166,14 +167,11 @@ const gfiRequest = ) } commit('setFeatureInformation', featuresByLayerId) - // render feature geometries to help layer - getters.geometryLayerKeys - .filter((key) => Array.isArray(featuresByLayerId[key])) - .forEach((key) => - filterFeatures(featuresByLayerId)[key].forEach((feature) => - addFeature(feature, featureDisplayLayer) - ) - ) + renderFeatures( + featureDisplayLayer, + getters.geometryLayerKeys, + featuresByLayerId + ) } export const debouncedGfiRequest = ( diff --git a/packages/plugins/Gfi/src/utils/renderFeatures.ts b/packages/plugins/Gfi/src/utils/renderFeatures.ts new file mode 100644 index 000000000..00de2bf42 --- /dev/null +++ b/packages/plugins/Gfi/src/utils/renderFeatures.ts @@ -0,0 +1,20 @@ +import VectorLayer from 'ol/layer/Vector' +import { Feature } from 'ol' +import { Geometry } from 'ol/geom' +import { FeaturesByLayerId } from '../types' +import { addFeature } from './displayFeatureLayer' +import { filterFeatures } from './filterFeatures' + +export function renderFeatures( + featureDisplayLayer: VectorLayer>, + geometryLayerKeys: string[], + features: FeaturesByLayerId +) { + geometryLayerKeys + .filter((key) => Array.isArray(features[key])) + .forEach((key) => + filterFeatures(features)[key].forEach((feature) => + addFeature(feature, featureDisplayLayer) + ) + ) +} From 682d639730cf38b2025af26f35ae56597566d1c7 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 21:26:10 +0100 Subject: [PATCH 11/18] Do not filter the features anew in each iteration --- packages/plugins/Gfi/src/utils/renderFeatures.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/plugins/Gfi/src/utils/renderFeatures.ts b/packages/plugins/Gfi/src/utils/renderFeatures.ts index 00de2bf42..0f0f8ad49 100644 --- a/packages/plugins/Gfi/src/utils/renderFeatures.ts +++ b/packages/plugins/Gfi/src/utils/renderFeatures.ts @@ -10,10 +10,11 @@ export function renderFeatures( geometryLayerKeys: string[], features: FeaturesByLayerId ) { + const filteredFeatures = filterFeatures(features) geometryLayerKeys .filter((key) => Array.isArray(features[key])) .forEach((key) => - filterFeatures(features)[key].forEach((feature) => + filteredFeatures[key].forEach((feature) => addFeature(feature, featureDisplayLayer) ) ) From 30482291c76a5f14356d5057970c101cf57789f5 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 21:45:26 +0100 Subject: [PATCH 12/18] Remove unused import --- packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts b/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts index bbe6a8661..286e67970 100644 --- a/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts +++ b/packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts @@ -10,7 +10,6 @@ import { Map, Feature } from 'ol' import { Geometry } from 'ol/geom' import VectorLayer from 'ol/layer/Vector' import compare from 'just-compare' -import { addFeature } from '../../utils/displayFeatureLayer' import { filterFeatures } from '../../utils/filterFeatures' import { requestGfi } from '../../utils/requestGfi' import sortFeatures from '../../utils/sortFeatures' From 7d24515bc83ecea3ec41590e9813dfb2221995a6 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 21:48:18 +0100 Subject: [PATCH 13/18] Add setFeatureInformation to be able to programmatically add features --- .../plugins/Gfi/src/store/actions/index.ts | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/plugins/Gfi/src/store/actions/index.ts b/packages/plugins/Gfi/src/store/actions/index.ts index 4e01ce5a5..24b3245f5 100644 --- a/packages/plugins/Gfi/src/store/actions/index.ts +++ b/packages/plugins/Gfi/src/store/actions/index.ts @@ -1,11 +1,13 @@ import debounce from 'lodash.debounce' +import { Feature as GeoJsonFeature } from 'geojson' import { Style, Fill, Stroke } from 'ol/style' import { GeoJSON } from 'ol/format' import { Feature } from 'ol' -import { Feature as GeoJsonFeature } from 'geojson' import { PolarActionTree } from '@polar/lib-custom-types' import { getFeatureDisplayLayer, clear } from '../../utils/displayFeatureLayer' -import { GfiGetters, GfiState } from '../../types' +import { FeaturesByLayerId, GfiGetters, GfiState } from '../../types' +import { filterFeatures } from '../../utils/filterFeatures' +import { renderFeatures } from '../../utils/renderFeatures' import { debouncedGfiRequest } from './debouncedGfiRequest' import { setupCoreListener, @@ -162,6 +164,34 @@ export const makeActions = () => { dispatch('setCoreSelection', { feature, centerOnFeature }) } }, + setFeatureInformation( + { commit, getters }, + featuresByLayerId: FeaturesByLayerId + ) { + commit('clearFeatureInformation') + commit('setVisibleWindowFeatureIndex', 0) + clear(featureDisplayLayer) + + const filteredFeatures = Object.fromEntries( + Object.entries(filterFeatures(featuresByLayerId)).map( + ([layerId, features]) => { + const { isSelectable } = getters.gfiConfiguration.layers[layerId] + return [ + layerId, + typeof isSelectable === 'function' + ? features.filter((feature) => isSelectable(feature)) + : features, + ] + } + ) + ) + commit('setFeatureInformation', filteredFeatures) + renderFeatures( + featureDisplayLayer, + getters.geometryLayerKeys, + filteredFeatures + ) + }, hover({ commit, rootGetters }, feature: Feature) { if (rootGetters.configuration.extendedMasterportalapiMarkers) { commit('setHovered', feature, { root: true }) From 55e73bbdf5543d69cd899f51aaa18f47ceada40d Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Wed, 4 Dec 2024 21:49:25 +0100 Subject: [PATCH 14/18] Add an example for the new action setFeatureInformation to the snowbox --- .../snowbox/src/exampleFeatureInformation.ts | 539 ++++++++++++++++++ packages/clients/snowbox/src/index.html | 1 + packages/clients/snowbox/src/polar-client.ts | 9 + 3 files changed, 549 insertions(+) create mode 100644 packages/clients/snowbox/src/exampleFeatureInformation.ts diff --git a/packages/clients/snowbox/src/exampleFeatureInformation.ts b/packages/clients/snowbox/src/exampleFeatureInformation.ts new file mode 100644 index 000000000..7844fa243 --- /dev/null +++ b/packages/clients/snowbox/src/exampleFeatureInformation.ts @@ -0,0 +1,539 @@ +/* eslint-disable @typescript-eslint/naming-convention, max-lines */ + +import { FeaturesByLayerId } from '@polar/plugin-gfi/src/types' + +export const exampleFeatureInformation: FeaturesByLayerId = { + 1454: [ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567357.259, 5928923.532, 0], + [567428.316, 5929001.598, 0], + [567430.133, 5929002.656, 0], + [567432.915, 5929002.111, 0], + [567437.012, 5928999.346, 0], + [567438.918, 5928996.882, 0], + [567445.48, 5928991.466, 0], + [567452.541, 5928982.393, 0], + [567454.524, 5928976.202, 0], + [567452.703, 5928972.513, 0], + [567450.196, 5928969.906, 0], + [567440.903, 5928964.726, 0], + [567440.577, 5928966.139, 0], + [567402.309, 5928931.894, 0], + [567396.03, 5928928.249, 0], + [567388.572, 5928924.876, 0], + [567383.041, 5928924.019, 0], + [567381.222, 5928920.347, 0], + [567377.75, 5928913.341, 0], + [567357.259, 5928923.532, 0], + ], + ], + }, + properties: { + vorhaben: 'M-089 - BPlan Wilhelmsburg 72', + vorhaben_zulassung_am: '17.01.1994', + vorhaben_verfahrensart: 'BPlan', + kompensationsmassnahme: 'Fließgewässer und Gräben', + massnahmenstatus: 'Maßnahme § 9 (1) Nr. 20 BauGB', + flaechensicherung: 'k.A.', + flaeche: '3069.449', + kompensationsmassnahme_detail: + 'Entwicklung naturnaher Ufergehölze; Entwicklung von Uferrandstreifen', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443856', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [568075.605, 5929240.552, 0], + [568083.257, 5929233.41, 0], + [568086.317, 5929240.807, 0], + [568110.037, 5929231.624, 0], + [568124.32, 5929223.462, 0], + [568139.878, 5929210.709, 0], + [568155.181, 5929193.111, 0], + [568157.732, 5929189.285, 0], + [568154.671, 5929186.734, 0], + [568161.302, 5929177.807, 0], + [568165.128, 5929179.593, 0], + [568219.198, 5929109.962, 0], + [568215.627, 5929107.157, 0], + [568223.534, 5929097.464, 0], + [568227.105, 5929099.76, 0], + [568242.918, 5929082.671, 0], + [568258.986, 5929069.408, 0], + [568273.014, 5929059.461, 0], + [568276.843, 5929057.626, 0], + [568272.353, 5929039.184, 0], + [568256.407, 5928974.91, 0], + [568231.643, 5928872.379, 0], + [568190.591, 5928890.875, 0], + [568187.183, 5928883.557, 0], + [568160.632, 5928895.529, 0], + [568128.081, 5928822.376, 0], + [568107.128, 5928834.457, 0], + [568094.719, 5928842.197, 0], + [568108.683, 5928861.728, 0], + [568088.395, 5928874.902, 0], + [568074.657, 5928855.297, 0], + [568068.832, 5928859.384, 0], + [568055.556, 5928869.77, 0], + [568050.41, 5928874.163, 0], + [568044.248, 5928879.977, 0], + [568035.744, 5928888.363, 0], + [568021.109, 5928904.589, 0], + [568044.436, 5928919.135, 0], + [568059.399, 5928933.711, 0], + [568064.897, 5928953.563, 0], + [568019.338, 5928972.388, 0], + [567989.81, 5928947.615, 0], + [567987.35, 5928945.548, 0], + [567975.597, 5928960.372, 0], + [567941.206, 5929003.66, 0], + [567929.406, 5929018.945, 0], + [567903.589, 5929054.714, 0], + [567939.638, 5929077.036, 0], + [567928.205, 5929095.265, 0], + [567890.962, 5929071.875, 0], + [567889.548, 5929073.771, 0], + [567856.828, 5929124.897, 0], + [567856.211, 5929125.803, 0], + [567867.777, 5929134.715, 0], + [567873.657, 5929142.919, 0], + [567866.223, 5929156.417, 0], + [567861.382, 5929165.213, 0], + [567846.146, 5929189.574, 0], + [567836.127, 5929205.666, 0], + [567847.888, 5929209.488, 0], + [567842.359, 5929226.669, 0], + [567835.448, 5929247.617, 0], + [567824.664, 5929280.774, 0], + [567800.425, 5929355.307, 0], + [567913.44, 5929386.822, 0], + [567940.698, 5929394.368, 0], + [567937.879, 5929389.25, 0], + [567935.583, 5929379.048, 0], + [567937.623, 5929365.275, 0], + [567950.376, 5929342.575, 0], + [567961.343, 5929326.762, 0], + [567976.135, 5929306.867, 0], + [567990.418, 5929290.033, 0], + [568006.741, 5929277.535, 0], + [568025.87, 5929267.078, 0], + [568056.221, 5929253.815, 0], + [568076.881, 5929244.888, 0], + [568075.605, 5929240.552, 0], + ], + ], + }, + properties: { + vorhaben: 'U-061 - Sanierung der Deponie Georgswerder', + vorhaben_zulassung_am: '08.12.1989', + vorhaben_verfahrensart: 'Zustimmung', + kompensationsmassnahme: 'extensiv genutztes Grünland', + massnahmenstatus: 'festgesetzt', + flaechensicherung: 'k.A.', + flaeche: '131321.2321', + hektar: '13,2819', + kompensationsmassnahme_detail: + 'Teiche / Blänken anlegen; Wiederherstellung / Aufweitung von Gräben m.flachen Uferböschungen; Wiesen- oder Weidenutzung', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443789', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [568276.843, 5929057.626, 0], + [568273.014, 5929059.461, 0], + [568258.986, 5929069.408, 0], + [568242.918, 5929082.671, 0], + [568227.105, 5929099.76, 0], + [568223.534, 5929097.464, 0], + [568215.627, 5929107.157, 0], + [568219.198, 5929109.962, 0], + [568165.128, 5929179.593, 0], + [568161.302, 5929177.807, 0], + [568154.671, 5929186.734, 0], + [568157.732, 5929189.285, 0], + [568155.181, 5929193.111, 0], + [568139.878, 5929210.709, 0], + [568124.32, 5929223.462, 0], + [568110.037, 5929231.624, 0], + [568086.317, 5929240.807, 0], + [568083.257, 5929233.41, 0], + [568075.605, 5929240.552, 0], + [568076.881, 5929244.888, 0], + [568056.221, 5929253.815, 0], + [568025.87, 5929267.078, 0], + [568006.741, 5929277.535, 0], + [567990.418, 5929290.033, 0], + [567976.135, 5929306.867, 0], + [567961.343, 5929326.762, 0], + [567950.376, 5929342.575, 0], + [567937.623, 5929365.275, 0], + [567935.583, 5929379.048, 0], + [567937.879, 5929389.25, 0], + [567940.698, 5929394.368, 0], + [567941.33, 5929397.698, 0], + [567941.4, 5929398.016, 0], + [567944.266, 5929410.851, 0], + [567981.598, 5929566.414, 0], + [568000.569, 5929643.233, 0], + [568008.886, 5929677.539, 0], + [568043.052, 5929789.491, 0], + [568047.573, 5929818.123, 0], + [568055.108, 5929816.616, 0], + [568064.15, 5929822.644, 0], + [568122.168, 5929815.861, 0], + [568189.229, 5929803.05, 0], + [568260.057, 5929793.251, 0], + [568348.969, 5929791.737, 0], + [568386.646, 5929867.08, 0], + [568388.906, 5929881.396, 0], + [568425.074, 5929877.625, 0], + [568435.623, 5929874.61, 0], + [568459.392, 5929830.979, 0], + [568459.783, 5929830.261, 0], + [568502.296, 5929752.157, 0], + [568576.364, 5929614.283, 0], + [568591.287, 5929585.258, 0], + [568595.816, 5929576.092, 0], + [568606.483, 5929554.492, 0], + [568621.163, 5929522.709, 0], + [568677.129, 5929390.94, 0], + [568678.394, 5929385.75, 0], + [568678.097, 5929379.983, 0], + [568674.38, 5929369.219, 0], + [568673.56, 5929364.68, 0], + [568665.078, 5929342.991, 0], + [568653.293, 5929311.953, 0], + [568643.071, 5929295.232, 0], + [568629.263, 5929268.493, 0], + [568616.518, 5929240.724, 0], + [568605.232, 5929203.73, 0], + [568599.703, 5929180.679, 0], + [568596.204, 5929159.369, 0], + [568593.822, 5929138.526, 0], + [568592.056, 5929117.056, 0], + [568592.525, 5929087.488, 0], + [568594.016, 5929066.605, 0], + [568601.075, 5929024.447, 0], + [568587.168, 5929024.298, 0], + [568590.811, 5929001.791, 0], + [568583.395, 5928997.498, 0], + [568463.706, 5929014.413, 0], + [568450.436, 5929022.219, 0], + [568353.289, 5929035.578, 0], + [568340.093, 5929036.76, 0], + [568329.38, 5929039.311, 0], + [568312.802, 5929043.647, 0], + [568300.049, 5929047.473, 0], + [568285.256, 5929053.594, 0], + [568276.843, 5929057.626, 0], + ], + ], + }, + properties: { + vorhaben: 'U-061 - Sanierung der Deponie Georgswerder', + vorhaben_zulassung_am: '08.12.1989', + vorhaben_verfahrensart: 'Zustimmung', + kompensationsmassnahme: 'Grünfläche', + massnahmenstatus: 'festgesetzt', + flaechensicherung: 'k.A.', + flaeche: '437265.6768', + hektar: '44,0000', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_445371', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567357.787, 5928923.949, 0], + [567315.644, 5928961.167, 0], + [567294.262, 5928980.485, 0], + [567266.077, 5928943.721, 0], + [567264.453, 5928941.474, 0], + [567250.88, 5928951.822, 0], + [567244.539, 5928958.237, 0], + [567288.796, 5929008.833, 0], + [567276.848, 5929019.384, 0], + [567297.769, 5929042.745, 0], + [567317.301, 5929064.721, 0], + [567323.939, 5929072.335, 0], + [567335.083, 5929085.128, 0], + [567339.285, 5929084.829, 0], + [567344.103, 5929081.528, 0], + [567350.428, 5929074.203, 0], + [567352.927, 5929070.973, 0], + [567356.902, 5929067.697, 0], + [567367.062, 5929060.056, 0], + [567368.663, 5929053.734, 0], + [567371.115, 5929050.485, 0], + [567375.441, 5929046.828, 0], + [567387.577, 5929031.78, 0], + [567401.087, 5929020.575, 0], + [567428.271, 5929001.666, 0], + [567429.489, 5929002.375, 0], + [567357.787, 5928923.949, 0], + ], + ], + }, + properties: { + vorhaben: 'U-061 - Sanierung der Deponie Georgswerder', + vorhaben_zulassung_am: '08.12.1989', + vorhaben_verfahrensart: 'Zustimmung', + kompensationsmassnahme: 'extensiv genutztes Grünland', + massnahmenstatus: 'festgesetzt', + flaechensicherung: 'k.A.', + flaeche: '13912.17', + hektar: '1,3909', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_445859', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567127.09, 5929119.895, 0], + [567127.985, 5929120.911, 0], + [567130.358, 5929123.604, 0], + [567147.174, 5929140.753, 0], + [567160.241, 5929153.479, 0], + [567176.115, 5929169.519, 0], + [567187.449, 5929179.496, 0], + [567191.052, 5929182.756, 0], + [567195.175, 5929180.848, 0], + [567209.577, 5929169.185, 0], + [567223.784, 5929158.511, 0], + [567233.903, 5929152.536, 0], + [567237.231, 5929144.525, 0], + [567254.191, 5929139.763, 0], + [567257.055, 5929139.517, 0], + [567250.885, 5929130.647, 0], + [567233.685, 5929105.925, 0], + [567217.271, 5929084.086, 0], + [567214.738, 5929080.928, 0], + [567205.369, 5929069.245, 0], + [567197.853, 5929059.619, 0], + [567156.646, 5929095.768, 0], + [567127.09, 5929119.895, 0], + ], + ], + }, + properties: { + vorhaben: 'M-089 - BPlan Wilhelmsburg 72', + vorhaben_zulassung_am: '17.01.1994', + vorhaben_verfahrensart: 'BPlan', + kompensationsmassnahme: 'Fließgewässer und Gräben', + massnahmenstatus: 'Maßnahme § 9 (1) Nr. 20 BauGB', + flaechensicherung: 'k.A.', + flaeche: '8001.0111', + kompensationsmassnahme_detail: + 'Entwicklung naturnaher Ufergehölze; Entwicklung von Uferrandstreifen', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443869', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567231.266, 5929046.507, 0], + [567255.323, 5929073.702, 0], + [567283.414, 5929104.669, 0], + [567323.986, 5929072.271, 0], + [567317.352, 5929064.659, 0], + [567297.815, 5929042.682, 0], + [567276.892, 5929019.321, 0], + [567267.022, 5929008.2, 0], + [567249.716, 5929022.363, 0], + [567239.261, 5929029.874, 0], + [567225.324, 5929039.885, 0], + [567231.266, 5929046.507, 0], + ], + ], + }, + properties: { + vorhaben: 'M-089 - BPlan Wilhelmsburg 72', + vorhaben_zulassung_am: '17.01.1994', + vorhaben_verfahrensart: 'BPlan', + kompensationsmassnahme: 'Fließgewässer und Gräben', + massnahmenstatus: 'Maßnahme § 9 (1) Nr. 20 BauGB', + flaechensicherung: 'k.A.', + flaeche: '4486.4625', + kompensationsmassnahme_detail: + 'Entwicklung naturnaher Ufergehölze; Entwicklung von Uferrandstreifen', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443868', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567225.324, 5929039.885, 0], + [567220.366, 5929043.447, 0], + [567236.412, 5929063.977, 0], + [567226.756, 5929071.529, 0], + [567265.335, 5929119.107, 0], + [567283.414, 5929104.669, 0], + [567255.323, 5929073.702, 0], + [567231.266, 5929046.507, 0], + [567226.907, 5929041.58, 0], + [567225.324, 5929039.885, 0], + ], + ], + }, + properties: { + vorhaben: 'M-089 - BPlan Wilhelmsburg 72', + vorhaben_zulassung_am: '17.01.1994', + vorhaben_verfahrensart: 'BPlan', + kompensationsmassnahme: 'Fließgewässer und Gräben', + massnahmenstatus: 'Maßnahme § 9 (1) Nr. 20 BauGB', + flaechensicherung: 'k.A.', + flaeche: '1493.4451', + kompensationsmassnahme_detail: + 'Entwicklung naturnaher Ufergehölze; Entwicklung von Uferrandstreifen', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443867', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567214.738, 5929080.928, 0], + [567217.271, 5929084.086, 0], + [567233.685, 5929105.925, 0], + [567250.885, 5929130.647, 0], + [567265.335, 5929119.107, 0], + [567226.756, 5929071.529, 0], + [567214.738, 5929080.928, 0], + ], + ], + }, + properties: { + vorhaben: 'M-089 - BPlan Wilhelmsburg 72', + vorhaben_zulassung_am: '17.01.1994', + vorhaben_verfahrensart: 'BPlan', + kompensationsmassnahme: 'Fließgewässer und Gräben', + massnahmenstatus: 'Maßnahme § 9 (1) Nr. 20 BauGB', + flaechensicherung: 'k.A.', + flaeche: '1014.0151', + kompensationsmassnahme_detail: + 'Entwicklung naturnaher Ufergehölze; Entwicklung von Uferrandstreifen', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443870', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567283.414, 5929104.669, 0], + [567293.86, 5929116.185, 0], + [567296.655, 5929119.249, 0], + [567306.145, 5929111.82, 0], + [567310.907, 5929109.094, 0], + [567319.286, 5929104.094, 0], + [567324.432, 5929093.142, 0], + [567333.796, 5929085.163, 0], + [567335.136, 5929085.067, 0], + [567323.986, 5929072.271, 0], + [567283.414, 5929104.669, 0], + ], + ], + }, + properties: { + vorhaben: 'M-089 - BPlan Wilhelmsburg 72', + vorhaben_zulassung_am: '17.01.1994', + vorhaben_verfahrensart: 'BPlan', + kompensationsmassnahme: 'Fließgewässer und Gräben', + massnahmenstatus: 'Maßnahme § 9 (1) Nr. 20 BauGB', + flaechensicherung: 'k.A.', + flaeche: '987.9114', + kompensationsmassnahme_detail: + 'Entwicklung naturnaher Ufergehölze; Entwicklung von Uferrandstreifen', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443859', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567265.335, 5929119.107, 0], + [567278.897, 5929135.832, 0], + [567289.82, 5929125.988, 0], + [567296.655, 5929119.249, 0], + [567293.86, 5929116.185, 0], + [567283.414, 5929104.669, 0], + [567265.335, 5929119.107, 0], + ], + ], + }, + properties: { + vorhaben: 'M-089 - BPlan Wilhelmsburg 72', + vorhaben_zulassung_am: '17.01.1994', + vorhaben_verfahrensart: 'BPlan', + kompensationsmassnahme: 'Fließgewässer und Gräben', + massnahmenstatus: 'Maßnahme § 9 (1) Nr. 20 BauGB', + flaechensicherung: 'k.A.', + flaeche: '491.4061', + kompensationsmassnahme_detail: + 'Entwicklung naturnaher Ufergehölze; Entwicklung von Uferrandstreifen', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443861', + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [567257.055, 5929139.517, 0], + [567264.469, 5929138.876, 0], + [567274.527, 5929139.769, 0], + [567278.897, 5929135.832, 0], + [567265.335, 5929119.107, 0], + [567250.885, 5929130.647, 0], + [567257.055, 5929139.517, 0], + ], + ], + }, + properties: { + vorhaben: 'M-089 - BPlan Wilhelmsburg 72', + vorhaben_zulassung_am: '17.01.1994', + vorhaben_verfahrensart: 'BPlan', + kompensationsmassnahme: 'Fließgewässer und Gräben', + massnahmenstatus: 'Maßnahme § 9 (1) Nr. 20 BauGB', + flaechensicherung: 'k.A.', + flaeche: '335.7387', + kompensationsmassnahme_detail: + 'Entwicklung naturnaher Ufergehölze; Entwicklung von Uferrandstreifen', + }, + id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443871', + }, + ], + 6059: [], +} diff --git a/packages/clients/snowbox/src/index.html b/packages/clients/snowbox/src/index.html index 1e8617d69..091217213 100644 --- a/packages/clients/snowbox/src/index.html +++ b/packages/clients/snowbox/src/index.html @@ -96,6 +96,7 @@

🗺️ Map

Example for programmatic information binding

+

This illustrates which kind of data can be retrieved from the map client.

diff --git a/packages/clients/snowbox/src/polar-client.ts b/packages/clients/snowbox/src/polar-client.ts index 4a539ec87..fd9570347 100644 --- a/packages/clients/snowbox/src/polar-client.ts +++ b/packages/clients/snowbox/src/polar-client.ts @@ -5,6 +5,7 @@ import { changeLanguage } from 'i18next' import { enableClustering } from '../../meldemichel/src/utils/enableClustering' import { addPlugins } from './addPlugins' import { mapConfiguration, reports } from './mapConfiguration' +import { exampleFeatureInformation } from './exampleFeatureInformation' addPlugins(polarCore) @@ -20,6 +21,14 @@ const createMap = (layerConf) => { .then((map) => { // @ts-expect-error | adding it intentionally for e2e testing window.mapInstance = map + document + .getElementById('vuex-target-clicky')! + .addEventListener('click', () => { + map.$store.dispatch( + 'plugin/gfi/setFeatureInformation', + exampleFeatureInformation + ) + }) addStoreSubscriptions( ['plugin/zoom/zoomLevel', 'vuex-target-zoom'], [ From 125215f7ee4ebb4cf9401bcc5a1d51e581d5ab0b Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Thu, 5 Dec 2024 23:45:24 +0100 Subject: [PATCH 15/18] Add tests for util functions filterFeatures and sortFeatures --- .../plugins/Gfi/tests/filterFeatures.spec.ts | 67 +++++ .../plugins/Gfi/tests/sortFeatures.spec.ts | 235 ++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 packages/plugins/Gfi/tests/filterFeatures.spec.ts create mode 100644 packages/plugins/Gfi/tests/sortFeatures.spec.ts diff --git a/packages/plugins/Gfi/tests/filterFeatures.spec.ts b/packages/plugins/Gfi/tests/filterFeatures.spec.ts new file mode 100644 index 000000000..60cdfe5c8 --- /dev/null +++ b/packages/plugins/Gfi/tests/filterFeatures.spec.ts @@ -0,0 +1,67 @@ +import { filterFeatures } from '../src/utils/filterFeatures' + +describe('filterFeatures', () => { + beforeEach(() => { + jest.clearAllMocks() + jest.useFakeTimers() + }) + + it('should return an object with only GeoJsonFeature[] as its values if the values were already only GeoJsonFeature[]', () => { + expect( + Object.keys( + filterFeatures({ + idOne: [ + { + type: 'Feature', + geometry: { type: 'Point', coordinates: [0, 0] }, + properties: { goodFeature: true }, + }, + ], + idTwo: [ + { + type: 'Feature', + geometry: { type: 'Point', coordinates: [1, 1] }, + properties: { goodFeature: true }, + }, + { + type: 'Feature', + geometry: { type: 'Point', coordinates: [0, 1] }, + properties: { goodFeature: false }, + }, + ], + }) + ).length + ).toBe(2) + }) + it('should return an object with less keys if some values are symbols', () => { + expect( + Object.keys( + filterFeatures({ + idOne: Symbol('failing'), + idTwo: [ + { + type: 'Feature', + geometry: { type: 'Point', coordinates: [1, 1] }, + properties: { goodFeature: true }, + }, + { + type: 'Feature', + geometry: { type: 'Point', coordinates: [0, 1] }, + properties: { goodFeature: false }, + }, + ], + }) + ).length + ).toBe(1) + }) + it('should return an empty object if all values are symbols', () => { + expect( + Object.keys( + filterFeatures({ + idOne: Symbol('failing'), + idTwo: Symbol('another fail'), + }) + ).length + ).toBe(0) + }) +}) diff --git a/packages/plugins/Gfi/tests/sortFeatures.spec.ts b/packages/plugins/Gfi/tests/sortFeatures.spec.ts new file mode 100644 index 000000000..fd9d7a9e4 --- /dev/null +++ b/packages/plugins/Gfi/tests/sortFeatures.spec.ts @@ -0,0 +1,235 @@ +import { Feature as GeoJsonFeature, LineString, Point, Polygon } from 'geojson' +import sortFeatures from '../src/utils/sortFeatures' + +describe('sortFeatures', () => { + beforeEach(() => { + jest.clearAllMocks() + jest.useFakeTimers() + }) + + describe('sorting two features', () => { + it('should sort the features by area (biggest first) if both are Polygons', () => { + const featureA: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [10.5, 59], + [15, 59], + [15, 56], + [10, 56], + [10.5, 59], + ], + ], + }, + properties: {}, + } + const featureB: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-4.25, 62], + [27.5, 62], + [26.5, 45], + [-4.25, 50], + [-4.5, 62.25], + ], + ], + }, + properties: {}, + } + expect( + [featureA, featureB].sort((a, b) => sortFeatures(a, b, 'EPSG:4326')) + ).toEqual([featureB, featureA]) + }) + it('should put the Polygon feature first if the other feature is not a Polygon', () => { + const featureA: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [ + [1.5, 55], + [20, 55], + ], + }, + properties: {}, + } + const featureB: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [10.5, 59], + [15, 59], + [15, 56], + [10, 56], + [10.5, 59], + ], + ], + }, + properties: {}, + } + expect( + [featureA, featureB].sort((a, b) => sortFeatures(a, b, 'EPSG:4326')) + ).toEqual([featureB, featureA]) + }) + it('should should keep the original order if both features are LineStrings', () => { + const featureA: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [ + [1.5, 55], + [20, 55], + ], + }, + properties: {}, + } + const featureB: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [ + [1.5, 55], + [20, 55], + [10, 56], + ], + }, + properties: {}, + } + expect( + [featureA, featureB].sort((a, b) => sortFeatures(a, b, 'EPSG:4326')) + ).toEqual([featureA, featureB]) + }) + it('should put the LineString feature first if the other feature is a Point Feature', () => { + const featureA: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [1.5, 55], + }, + properties: {}, + } + const featureB: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [ + [1.5, 55], + [20, 55], + [10, 56], + ], + }, + properties: {}, + } + expect( + [featureA, featureB].sort((a, b) => sortFeatures(a, b, 'EPSG:4326')) + ).toEqual([featureB, featureA]) + }) + it('should should keep the original order if both features are Points', () => { + const featureA: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [1.5, 55], + }, + properties: {}, + } + const featureB: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [55, 1.5], + }, + properties: {}, + } + expect( + [featureA, featureB].sort((a, b) => sortFeatures(a, b, 'EPSG:4326')) + ).toEqual([featureA, featureB]) + }) + }) + describe('sorting more than two features', () => { + it('should order the features by Polygons first, then LineStrings and lastly Points', () => { + const featureA: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [ + [1.5, 55], + [20, 55], + ], + }, + properties: {}, + } + const featureB: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [ + [1.5, 55], + [20, 55], + [10, 56], + ], + }, + properties: {}, + } + const featureC: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [1.5, 55], + }, + properties: {}, + } + const featureD: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [10.5, 59], + [15, 59], + [15, 56], + [10, 56], + [10.5, 59], + ], + ], + }, + properties: {}, + } + const featureE: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [55, 1.5], + }, + properties: {}, + } + const featureF: GeoJsonFeature = { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-4.25, 62], + [27.5, 62], + [26.5, 45], + [-4.25, 50], + [-4.5, 62.25], + ], + ], + }, + properties: {}, + } + expect( + [featureA, featureB, featureC, featureD, featureE, featureF].sort( + (a, b) => sortFeatures(a, b, 'EPSG:4326') + ) + ).toEqual([featureF, featureD, featureA, featureB, featureC, featureE]) + }) + }) +}) From 2149091a6fc45c047cec48403c74e8d122135505 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Mon, 6 Jan 2025 12:40:29 +0100 Subject: [PATCH 16/18] Fix e2e tests breaking by moving the click handler outside createMap --- packages/clients/snowbox/src/polar-client.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/clients/snowbox/src/polar-client.ts b/packages/clients/snowbox/src/polar-client.ts index fd9570347..b9181d266 100644 --- a/packages/clients/snowbox/src/polar-client.ts +++ b/packages/clients/snowbox/src/polar-client.ts @@ -21,14 +21,6 @@ const createMap = (layerConf) => { .then((map) => { // @ts-expect-error | adding it intentionally for e2e testing window.mapInstance = map - document - .getElementById('vuex-target-clicky')! - .addEventListener('click', () => { - map.$store.dispatch( - 'plugin/gfi/setFeatureInformation', - exampleFeatureInformation - ) - }) addStoreSubscriptions( ['plugin/zoom/zoomLevel', 'vuex-target-zoom'], [ @@ -89,3 +81,11 @@ document target[1].innerHTML = value === 'en' ? 'German' : 'Deutsch' }) }) + +document.getElementById('vuex-target-clicky')!.addEventListener('click', () => + // @ts-expect-error | added for e2e testing + window.mapInstance.$store.dispatch( + 'plugin/gfi/setFeatureInformation', + exampleFeatureInformation + ) +) From 9afba0ca551f54772f3c9df69d107f58b7d0b33a Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Mon, 6 Jan 2025 17:16:39 +0100 Subject: [PATCH 17/18] Adjust documentation of setFeatureInformation --- packages/plugins/Gfi/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/plugins/Gfi/README.md b/packages/plugins/Gfi/README.md index e00e67b2c..fb562cc26 100644 --- a/packages/plugins/Gfi/README.md +++ b/packages/plugins/Gfi/README.md @@ -224,8 +224,7 @@ map.$store.dispatch('plugin/gfi/setFeatureInformation', { }) ``` -The payload object has to include all layers configured to be used with this plugin. -The value assigned to those keys is an array of GeoJSON-Features. +The payload object expects a layer id as a key and an array of GeoJSON-Features as its value. The selected feature information can be reset by calling the method with an empty object. From 397c57f4f66f33a016850ef5595b68fb3cca7813 Mon Sep 17 00:00:00 2001 From: Pascal Roehling Date: Mon, 6 Jan 2025 17:17:48 +0100 Subject: [PATCH 18/18] Remove unnecessary property --- packages/clients/snowbox/src/exampleFeatureInformation.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/clients/snowbox/src/exampleFeatureInformation.ts b/packages/clients/snowbox/src/exampleFeatureInformation.ts index 7844fa243..bae696072 100644 --- a/packages/clients/snowbox/src/exampleFeatureInformation.ts +++ b/packages/clients/snowbox/src/exampleFeatureInformation.ts @@ -535,5 +535,4 @@ export const exampleFeatureInformation: FeaturesByLayerId = { id: 'DE.HH.UP_AUSGLEICHSFLAECHEN_443871', }, ], - 6059: [], }