Skip to content

Commit 2722da6

Browse files
committed
Merge branch 'main' into feature/add-measure-tool
2 parents 6099fe1 + 672dd30 commit 2722da6

File tree

14 files changed

+1152
-175
lines changed

14 files changed

+1152
-175
lines changed

packages/clients/snowbox/src/exampleFeatureInformation.ts

Lines changed: 538 additions & 0 deletions
Large diffs are not rendered by default.

packages/clients/snowbox/src/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ <h2>🗺️ Map</h2>
9696
<noscript>Please use a browser with active JavaScript to use the map client.</noscript>
9797
</div>
9898
<h2>Example for programmatic information binding</h2>
99+
<button id="vuex-target-clicky">Click here to programmatically select some features</button>
99100
<p>
100101
This illustrates which kind of data can be retrieved from the map client.
101102
</p>

packages/clients/snowbox/src/polar-client.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { changeLanguage } from 'i18next'
55
import { enableClustering } from '../../meldemichel/src/utils/enableClustering'
66
import { addPlugins } from './addPlugins'
77
import { mapConfiguration, reports } from './mapConfiguration'
8+
import { exampleFeatureInformation } from './exampleFeatureInformation'
89

910
addPlugins(polarCore)
1011

@@ -82,3 +83,11 @@ document
8283
target[1].innerHTML = value === 'en' ? 'German' : 'Deutsch'
8384
})
8485
})
86+
87+
document.getElementById('vuex-target-clicky')!.addEventListener('click', () =>
88+
// @ts-expect-error | added for e2e testing
89+
window.mapInstance.$store.dispatch(
90+
'plugin/gfi/setFeatureInformation',
91+
exampleFeatureInformation
92+
)
93+
)

packages/plugins/Gfi/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## unpublished
4+
5+
- 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.
6+
37
## 2.0.0
48

59
- Breaking: Upgrade `@masterportal/masterportalapi` from `2.8.0` to `2.40.0` and subsequently `ol` from `^7.1.0` to `^9.2.4`.

packages/plugins/Gfi/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,56 @@ featureList: {
178178

179179
## Store
180180

181+
### Actions
182+
183+
#### setFeatureInformation
184+
185+
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.
186+
Note that calling this method completely overrides the previously set feature information.
187+
188+
If a layer has a `isSelectable`-function configured, the features are filtered using that function.
189+
190+
```js
191+
map.$store.dispatch('plugin/gfi/setFeatureInformation', {
192+
"anotherLayer": [],
193+
"yetAnotherLayer": [],
194+
"relevantInformation": [
195+
{
196+
"type": "Feature",
197+
"geometry": {
198+
"type": "Point",
199+
"coordinates": [
200+
565669.6521397199,
201+
5930516.358614317
202+
]
203+
},
204+
"properties": {
205+
"propertyOne": "B0",
206+
"propertyTwo": "B1"
207+
}
208+
},
209+
{
210+
"type": "Feature",
211+
"geometry": {
212+
"type": "Point",
213+
"coordinates": [
214+
565594.9377660984,
215+
5930524.52634174
216+
]
217+
},
218+
"properties": {
219+
propertyOne: "A0",
220+
propertyTwo: "A1"
221+
}
222+
}
223+
]
224+
})
225+
```
226+
227+
The payload object expects a layer id as a key and an array of GeoJSON-Features as its value.
228+
229+
The selected feature information can be reset by calling the method with an empty object.
230+
181231
### State
182232

183233
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.

packages/plugins/Gfi/src/store/actions/debouncedGfiRequest.ts

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,17 @@ import { Map, Feature } from 'ol'
1010
import { Geometry } from 'ol/geom'
1111
import VectorLayer from 'ol/layer/Vector'
1212
import compare from 'just-compare'
13-
import { addFeature } from '../../utils/displayFeatureLayer'
13+
import { filterFeatures } from '../../utils/filterFeatures'
1414
import { requestGfi } from '../../utils/requestGfi'
1515
import sortFeatures from '../../utils/sortFeatures'
16-
import { GfiGetters, GfiState } from '../../types'
16+
import { FeaturesByLayerId, GfiGetters, GfiState } from '../../types'
17+
import { renderFeatures } from '../../utils/renderFeatures'
1718

1819
interface GetFeatureInfoParameters {
1920
coordinateOrExtent: [number, number] | [number, number, number, number]
2021
modifierPressed?: boolean
2122
}
2223

23-
type FeaturesByLayerId = Record<string, GeoJsonFeature[] | symbol>
24-
2524
const filterAndMapFeaturesToLayerIds = (
2625
layerKeys: string[],
2726
gfiConfiguration: GfiConfiguration,
@@ -93,17 +92,6 @@ const getPromisedFeatures = (
9392
})
9493
})
9594

96-
const filterFeatures = (
97-
featuresByLayerId: FeaturesByLayerId
98-
): Record<string, GeoJsonFeature[]> => {
99-
const entries = Object.entries(featuresByLayerId)
100-
const filtered = entries.filter((keyValue) => Array.isArray(keyValue[1])) as [
101-
string,
102-
GeoJsonFeature[]
103-
][]
104-
return Object.fromEntries(filtered)
105-
}
106-
10795
const createSelectionDiff = (
10896
oldSelection: FeaturesByLayerId,
10997
newSelection: FeaturesByLayerId
@@ -178,14 +166,11 @@ const gfiRequest =
178166
)
179167
}
180168
commit('setFeatureInformation', featuresByLayerId)
181-
// render feature geometries to help layer
182-
getters.geometryLayerKeys
183-
.filter((key) => Array.isArray(featuresByLayerId[key]))
184-
.forEach((key) =>
185-
filterFeatures(featuresByLayerId)[key].forEach((feature) =>
186-
addFeature(feature, featureDisplayLayer)
187-
)
188-
)
169+
renderFeatures(
170+
featureDisplayLayer,
171+
getters.geometryLayerKeys,
172+
featuresByLayerId
173+
)
189174
}
190175

191176
export const debouncedGfiRequest = (

packages/plugins/Gfi/src/store/actions/index.ts

Lines changed: 41 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import debounce from 'lodash.debounce'
2+
import { Feature as GeoJsonFeature } from 'geojson'
23
import { Style, Fill, Stroke } from 'ol/style'
34
import { GeoJSON } from 'ol/format'
45
import { Feature } from 'ol'
5-
import { Feature as GeoJsonFeature, GeoJsonProperties } from 'geojson'
66
import { PolarActionTree } from '@polar/lib-custom-types'
7-
import getCluster from '@polar/lib-get-cluster'
8-
import { DragBox, Draw, Modify } from 'ol/interaction'
9-
import { platformModifierKeyOnly } from 'ol/events/condition'
107
import { getFeatureDisplayLayer, clear } from '../../utils/displayFeatureLayer'
11-
import { GfiGetters, GfiState } from '../../types'
12-
import { getOriginalFeature } from '../../utils/getOriginalFeature'
13-
import { setupTooltip } from './setupTooltip'
8+
import { FeaturesByLayerId, GfiGetters, GfiState } from '../../types'
9+
import { filterFeatures } from '../../utils/filterFeatures'
10+
import { renderFeatures } from '../../utils/renderFeatures'
1411
import { debouncedGfiRequest } from './debouncedGfiRequest'
12+
import {
13+
setupCoreListener,
14+
setupMultiSelection,
15+
setupTooltip,
16+
setupZoomListeners,
17+
} from './setup'
1518

1619
// OK for module action set creation
1720
// eslint-disable-next-line max-lines-per-function
@@ -66,97 +69,10 @@ export const makeActions = () => {
6669
dispatch('setupZoomListeners')
6770
dispatch('setupMultiSelection')
6871
},
72+
setupCoreListener,
73+
setupMultiSelection,
6974
setupTooltip,
70-
setupCoreListener({
71-
getters: { gfiConfiguration },
72-
rootGetters,
73-
dispatch,
74-
}) {
75-
if (gfiConfiguration.featureList?.bindWithCoreHoverSelect) {
76-
this.watch(
77-
() => rootGetters.selected,
78-
(feature) => dispatch('setOlFeatureInformation', { feature }),
79-
{ deep: true }
80-
)
81-
}
82-
},
83-
setupMultiSelection({ dispatch, getters, rootGetters }) {
84-
if (getters.gfiConfiguration.boxSelect) {
85-
const dragBox = new DragBox({ condition: platformModifierKeyOnly })
86-
dragBox.on('boxend', () =>
87-
dispatch('getFeatureInfo', {
88-
coordinateOrExtent: dragBox.getGeometry().getExtent(),
89-
modifierPressed: true,
90-
})
91-
)
92-
rootGetters.map.addInteraction(dragBox)
93-
}
94-
if (getters.gfiConfiguration.directSelect) {
95-
rootGetters.map.on('click', ({ coordinate, originalEvent }) => {
96-
const isDrawing = rootGetters.map
97-
.getInteractions()
98-
.getArray()
99-
.some(
100-
(interaction) =>
101-
// these indicate other interactions are expected now
102-
interaction instanceof Draw ||
103-
interaction instanceof Modify ||
104-
// @ts-expect-error | internal hack to detect it from @polar/plugin-draw
105-
interaction._isDeleteSelect ||
106-
// @ts-expect-error | internal hack to detect it from @polar/plugin-measure
107-
interaction._isMeasureSelect
108-
)
109-
if (!isDrawing) {
110-
dispatch('getFeatureInfo', {
111-
coordinateOrExtent: coordinate,
112-
modifierPressed: navigator.userAgent.includes('Mac')
113-
? originalEvent.metaKey
114-
: originalEvent.ctrlKey,
115-
})
116-
}
117-
})
118-
}
119-
},
120-
setupZoomListeners({ dispatch, getters, rootGetters }) {
121-
if (getters.gfiConfiguration.featureList) {
122-
this.watch(
123-
() => rootGetters.zoomLevel,
124-
() => {
125-
const {
126-
featureInformation,
127-
listableLayerSources,
128-
visibleWindowFeatureIndex,
129-
windowFeatures,
130-
} = getters
131-
132-
if (windowFeatures.length) {
133-
const layerId: string =
134-
// @ts-expect-error | if windowFeatures has features, visibleWindowFeatureIndex is in the range of possible features
135-
windowFeatures[visibleWindowFeatureIndex].polarInternalLayerKey
136-
const selectedFeatureProperties: GeoJsonProperties = {
137-
// eslint-disable-next-line @typescript-eslint/naming-convention
138-
_gfiLayerId: layerId,
139-
...featureInformation[layerId][visibleWindowFeatureIndex]
140-
.properties,
141-
}
142-
const originalFeature = getOriginalFeature(
143-
listableLayerSources,
144-
selectedFeatureProperties
145-
)
146-
if (originalFeature) {
147-
dispatch('setOlFeatureInformation', {
148-
feature: getCluster(
149-
rootGetters.map,
150-
originalFeature,
151-
'_gfiLayerId'
152-
),
153-
})
154-
}
155-
}
156-
}
157-
)
158-
}
159-
},
75+
setupZoomListeners,
16076
setupFeatureVisibilityUpdates({ commit, state, getters, rootGetters }) {
16177
// debounce to prevent update spam
16278
debouncedVisibilityChangeIndicator = debounce(
@@ -248,6 +164,34 @@ export const makeActions = () => {
248164
dispatch('setCoreSelection', { feature, centerOnFeature })
249165
}
250166
},
167+
setFeatureInformation(
168+
{ commit, getters },
169+
featuresByLayerId: FeaturesByLayerId
170+
) {
171+
commit('clearFeatureInformation')
172+
commit('setVisibleWindowFeatureIndex', 0)
173+
clear(featureDisplayLayer)
174+
175+
const filteredFeatures = Object.fromEntries(
176+
Object.entries(filterFeatures(featuresByLayerId)).map(
177+
([layerId, features]) => {
178+
const { isSelectable } = getters.gfiConfiguration.layers[layerId]
179+
return [
180+
layerId,
181+
typeof isSelectable === 'function'
182+
? features.filter((feature) => isSelectable(feature))
183+
: features,
184+
]
185+
}
186+
)
187+
)
188+
commit('setFeatureInformation', filteredFeatures)
189+
renderFeatures(
190+
featureDisplayLayer,
191+
getters.geometryLayerKeys,
192+
filteredFeatures
193+
)
194+
},
251195
hover({ commit, rootGetters }, feature: Feature) {
252196
if (rootGetters.configuration.extendedMasterportalapiMarkers) {
253197
commit('setHovered', feature, { root: true })

0 commit comments

Comments
 (0)