Skip to content

Commit 6661f7d

Browse files
authored
Merge branch 'main' into dish_intern
2 parents 76b9468 + 672dd30 commit 6661f7d

File tree

13 files changed

+1140
-156
lines changed

13 files changed

+1140
-156
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

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

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: 42 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import debounce from 'lodash.debounce'
2+
import { Feature as GeoJsonFeature } from 'geojson'
23
import { Style, Fill, Stroke } from 'ol/style'
3-
import Overlay from 'ol/Overlay'
44
import { GeoJSON } from 'ol/format'
55
import { Feature } from 'ol'
6-
import { Feature as GeoJsonFeature, GeoJsonProperties } from 'geojson'
76
import { PolarActionTree } from '@polar/lib-custom-types'
8-
import getCluster from '@polar/lib-get-cluster'
9-
import { getTooltip, Tooltip } from '@polar/lib-tooltip'
10-
import { DragBox } from 'ol/interaction'
11-
import { platformModifierKeyOnly } from 'ol/events/condition'
127
import { getFeatureDisplayLayer, clear } from '../../utils/displayFeatureLayer'
13-
import { GfiGetters, GfiState } from '../../types'
14-
import { getOriginalFeature } from '../../utils/getOriginalFeature'
8+
import { FeaturesByLayerId, GfiGetters, GfiState } from '../../types'
9+
import { filterFeatures } from '../../utils/filterFeatures'
10+
import { renderFeatures } from '../../utils/renderFeatures'
1511
import { debouncedGfiRequest } from './debouncedGfiRequest'
12+
import {
13+
setupCoreListener,
14+
setupMultiSelection,
15+
setupTooltip,
16+
setupZoomListeners,
17+
} from './setup'
1618

1719
// OK for module action set creation
1820
// eslint-disable-next-line max-lines-per-function
@@ -67,131 +69,10 @@ export const makeActions = () => {
6769
dispatch('setupZoomListeners')
6870
dispatch('setupMultiSelection')
6971
},
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-
dispatch('getFeatureInfo', {
97-
coordinateOrExtent: coordinate,
98-
modifierPressed:
99-
navigator.userAgent.indexOf('Mac') !== -1
100-
? originalEvent.metaKey
101-
: originalEvent.ctrlKey,
102-
})
103-
)
104-
}
105-
},
106-
setupZoomListeners({ dispatch, getters, rootGetters }) {
107-
if (getters.gfiConfiguration.featureList) {
108-
this.watch(
109-
() => rootGetters.zoomLevel,
110-
() => {
111-
const {
112-
featureInformation,
113-
listableLayerSources,
114-
visibleWindowFeatureIndex,
115-
windowFeatures,
116-
} = getters
117-
118-
if (windowFeatures.length) {
119-
const layerId: string =
120-
// @ts-expect-error | if windowFeatures has features, visibleWindowFeatureIndex is in the range of possible features
121-
windowFeatures[visibleWindowFeatureIndex].polarInternalLayerKey
122-
const selectedFeatureProperties: GeoJsonProperties = {
123-
// eslint-disable-next-line @typescript-eslint/naming-convention
124-
_gfiLayerId: layerId,
125-
...featureInformation[layerId][visibleWindowFeatureIndex]
126-
.properties,
127-
}
128-
const originalFeature = getOriginalFeature(
129-
listableLayerSources,
130-
selectedFeatureProperties
131-
)
132-
if (originalFeature) {
133-
dispatch('setOlFeatureInformation', {
134-
feature: getCluster(
135-
rootGetters.map,
136-
originalFeature,
137-
'_gfiLayerId'
138-
),
139-
})
140-
}
141-
}
142-
}
143-
)
144-
}
145-
},
146-
setupTooltip({ getters: { gfiConfiguration }, rootGetters: { map } }) {
147-
const tooltipLayerIds = Object.keys(gfiConfiguration.layers).filter(
148-
(key) => gfiConfiguration.layers[key].showTooltip
149-
)
150-
if (!tooltipLayerIds.length) {
151-
return
152-
}
153-
154-
let element: Tooltip['element'], unregister: Tooltip['unregister']
155-
const overlay = new Overlay({
156-
positioning: 'bottom-center',
157-
offset: [0, -5],
158-
})
159-
map.addOverlay(overlay)
160-
map.on('pointermove', ({ pixel, dragging, originalEvent }) => {
161-
if (dragging || ['touch', 'pen'].includes(originalEvent.pointerType)) {
162-
return
163-
}
164-
let hasFeatureAtPixel = false
165-
// stops on return `true`, thus only using the uppermost feature
166-
map.forEachFeatureAtPixel(
167-
pixel,
168-
(feature, layer) => {
169-
if (!(feature instanceof Feature)) {
170-
return false
171-
}
172-
hasFeatureAtPixel = true
173-
overlay.setPosition(map.getCoordinateFromPixel(pixel))
174-
if (unregister) {
175-
unregister()
176-
}
177-
;({ element, unregister } = getTooltip({
178-
localeKeys:
179-
// @ts-expect-error | it exists by virtue of layerFilter below
180-
gfiConfiguration.layers[layer.get('id')].showTooltip(
181-
feature,
182-
map
183-
),
184-
}))
185-
overlay.setElement(element)
186-
return true
187-
},
188-
{ layerFilter: (layer) => tooltipLayerIds.includes(layer.get('id')) }
189-
)
190-
if (!hasFeatureAtPixel) {
191-
overlay.setPosition(undefined)
192-
}
193-
})
194-
},
72+
setupCoreListener,
73+
setupMultiSelection,
74+
setupTooltip,
75+
setupZoomListeners,
19576
setupFeatureVisibilityUpdates({ commit, state, getters, rootGetters }) {
19677
// debounce to prevent update spam
19778
debouncedVisibilityChangeIndicator = debounce(
@@ -283,6 +164,34 @@ export const makeActions = () => {
283164
dispatch('setCoreSelection', { feature, centerOnFeature })
284165
}
285166
},
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+
},
286195
hover({ commit, rootGetters }, feature: Feature) {
287196
if (rootGetters.configuration.extendedMasterportalapiMarkers) {
288197
commit('setHovered', feature, { root: true })

0 commit comments

Comments
 (0)