@defra/interactive-map 0.0.15-alpha → 0.0.17-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/css/docusaurus.css +104 -0
- package/assets/images/favicon.svg +1 -0
- package/assets/images/hero.png +0 -0
- package/assets/images/slot-map.svg +264 -0
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/slots.md +90 -6
- package/docs/api.md +4 -4
- package/docs/architecture.md +3 -1
- package/docs/{demo.mdx → examples.mdx} +1 -1
- package/docs/getting-started.md +5 -4
- package/docs/index.mdx +42 -0
- package/docs/plugins/datasets.md +561 -0
- package/docs/plugins/interact.md +176 -55
- package/docs/plugins/map-styles.md +64 -7
- package/docs/plugins/search.md +207 -63
- package/docs/plugins.md +8 -16
- package/docusaurus.config.cjs +34 -34
- package/jest.setup.js +1 -1
- package/package.json +6 -5
- package/plugins/beta/datasets/dist/css/index.css +85 -15
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/index.js +1 -1
- package/plugins/beta/datasets/src/DatasetsInit.jsx +24 -9
- package/plugins/beta/datasets/src/adapters/maplibre/index.js +18 -0
- package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +113 -0
- package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +69 -0
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +338 -0
- package/plugins/beta/datasets/src/adapters/maplibre/patternRegistry.js +48 -0
- package/plugins/beta/datasets/src/api/addDataset.js +3 -9
- package/plugins/beta/datasets/src/api/getOpacity.js +17 -0
- package/plugins/beta/datasets/src/api/getStyle.js +13 -0
- package/plugins/beta/datasets/src/api/removeDataset.js +3 -45
- package/plugins/beta/datasets/src/api/setData.js +8 -0
- package/plugins/beta/datasets/src/api/setDatasetVisibility.js +37 -0
- package/plugins/beta/datasets/src/api/setFeatureVisibility.js +22 -0
- package/plugins/beta/datasets/src/api/setOpacity.js +29 -0
- package/plugins/beta/datasets/src/api/setStyle.js +22 -0
- package/plugins/beta/datasets/src/datasets.js +33 -59
- package/plugins/beta/datasets/src/defaults.js +43 -9
- package/plugins/beta/datasets/src/fetch/createDynamicSource.js +39 -30
- package/plugins/beta/datasets/src/fetch/fetchGeoJSON.js +2 -2
- package/plugins/beta/datasets/src/manifest.js +27 -19
- package/plugins/beta/datasets/src/panels/Key.jsx +129 -49
- package/plugins/beta/datasets/src/panels/Key.module.scss +48 -9
- package/plugins/beta/datasets/src/panels/Layers.jsx +131 -29
- package/plugins/beta/datasets/src/panels/Layers.module.scss +50 -8
- package/plugins/beta/datasets/src/reducer.js +128 -9
- package/plugins/beta/datasets/src/styles/patterns.js +157 -0
- package/plugins/beta/datasets/src/utils/bbox.js +8 -6
- package/plugins/beta/datasets/src/utils/filters.js +5 -2
- package/plugins/beta/datasets/src/utils/mergeSublayer.js +78 -0
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-es/src/DrawInit.jsx +16 -16
- package/plugins/beta/draw-es/src/api/addFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/deleteFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/editFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/newPolygon.js +3 -3
- package/plugins/beta/draw-es/src/events.js +52 -20
- package/plugins/beta/draw-es/src/events.test.js +301 -0
- package/plugins/beta/draw-es/src/graphic.js +1 -1
- package/plugins/beta/draw-es/src/manifest.js +4 -4
- package/plugins/beta/draw-es/src/reducer.js +1 -1
- package/plugins/beta/draw-es/src/sketchViewModel.js +1 -1
- package/plugins/beta/draw-ml/dist/css/index.css +1 -1
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/src/DrawInit.jsx +49 -52
- package/plugins/beta/draw-ml/src/api/deleteFeature.js +1 -1
- package/plugins/beta/draw-ml/src/api/editFeature.js +8 -5
- package/plugins/beta/draw-ml/src/api/newLine.js +0 -1
- package/plugins/beta/draw-ml/src/api/newPolygon.js +0 -1
- package/plugins/beta/draw-ml/src/api/split.js +4 -4
- package/plugins/beta/draw-ml/src/defaults.js +1 -1
- package/plugins/beta/draw-ml/src/draw.scss +0 -7
- package/plugins/beta/draw-ml/src/events.js +8 -6
- package/plugins/beta/draw-ml/src/manifest.js +29 -29
- package/plugins/beta/draw-ml/src/mapboxDraw.js +1 -1
- package/plugins/beta/draw-ml/src/mapboxSnap.js +17 -18
- package/plugins/beta/draw-ml/src/modes/createDrawMode.js +31 -31
- package/plugins/beta/draw-ml/src/modes/disabledMode.js +1 -1
- package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +11 -11
- package/plugins/beta/draw-ml/src/modes/editVertex/undoHandlers.js +7 -7
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +8 -8
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexQueries.js +7 -7
- package/plugins/beta/draw-ml/src/modes/editVertexMode.js +32 -24
- package/plugins/beta/draw-ml/src/reducer.js +1 -1
- package/plugins/beta/draw-ml/src/undoStack.js +4 -4
- package/plugins/beta/draw-ml/src/utils/snapHelpers.js +12 -12
- package/plugins/beta/draw-ml/src/utils/spatial.js +11 -11
- package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -1
- package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -1
- package/plugins/beta/frame/src/Frame.jsx +9 -9
- package/plugins/beta/frame/src/FrameInit.jsx +4 -4
- package/plugins/beta/frame/src/api/addFrame.js +1 -1
- package/plugins/beta/frame/src/api/editFeature.js +1 -1
- package/plugins/beta/frame/src/config.js +1 -1
- package/plugins/beta/frame/src/manifest.js +3 -3
- package/plugins/beta/frame/src/reducer.js +1 -1
- package/plugins/beta/frame/src/utils.js +1 -1
- package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/src/MapStyles.jsx +18 -18
- package/plugins/beta/map-styles/src/manifest.js +1 -1
- package/plugins/beta/scale-bar/dist/css/index.css +1 -1
- package/plugins/beta/scale-bar/dist/esm/im-scale-bar-plugin.js +1 -1
- package/plugins/beta/scale-bar/dist/umd/im-scale-bar-plugin.js +1 -1
- package/plugins/beta/scale-bar/src/ScaleBar.jsx +5 -5
- package/plugins/beta/scale-bar/src/index.test.js +3 -3
- package/plugins/beta/scale-bar/src/manifest.js +3 -3
- package/plugins/beta/scale-bar/src/scaleBar.scss +2 -1
- package/plugins/beta/use-location/src/UseLocation.jsx +1 -1
- package/plugins/beta/use-location/src/defaults.js +1 -1
- package/plugins/beta/use-location/src/events.js +3 -3
- package/plugins/interact/dist/css/index.css +1 -1
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/src/InteractInit.jsx +1 -2
- package/plugins/interact/src/api/enable.js +8 -5
- package/plugins/interact/src/api/enable.test.js +2 -2
- package/plugins/interact/src/api/selectFeature.js +4 -4
- package/plugins/interact/src/api/unselectFeature.js +5 -5
- package/plugins/interact/src/defaults.js +0 -1
- package/plugins/interact/src/events.test.js +15 -15
- package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.js +2 -2
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +5 -5
- package/plugins/interact/src/interact.scss +0 -7
- package/plugins/interact/src/manifest.js +15 -19
- package/plugins/interact/src/manifest.test.js +6 -5
- package/plugins/interact/src/reducer.js +3 -3
- package/plugins/interact/src/reducer.test.js +0 -1
- package/plugins/interact/src/utils/spatial.js +10 -10
- package/plugins/interact/src/utils/spatial.test.js +14 -14
- package/plugins/search/dist/css/index.css +1 -1
- package/plugins/search/dist/esm/im-search-plugin.js +1 -1
- package/plugins/search/dist/esm/index.js +1 -1
- package/plugins/search/dist/umd/im-search-plugin.js +1 -1
- package/plugins/search/dist/umd/index.js +1 -1
- package/plugins/search/src/Search.jsx +7 -6
- package/plugins/search/src/Search.test.jsx +23 -23
- package/plugins/search/src/components/CloseButton/CloseButton.jsx +15 -15
- package/plugins/search/src/components/CloseButton/CloseButton.test.jsx +2 -2
- package/plugins/search/src/components/Form/Form.jsx +14 -14
- package/plugins/search/src/components/Form/Form.module.scss +2 -1
- package/plugins/search/src/components/Form/Form.test.jsx +11 -11
- package/plugins/search/src/components/OpenButton/OpenButton.jsx +16 -15
- package/plugins/search/src/components/OpenButton/OpenButton.test.jsx +6 -2
- package/plugins/search/src/components/SubmitButton/SubmitButton.jsx +15 -15
- package/plugins/search/src/components/Suggestions/Suggestions.jsx +6 -6
- package/plugins/search/src/components/Suggestions/Suggestions.test.jsx +4 -4
- package/plugins/search/src/datasets.js +12 -13
- package/plugins/search/src/datasets.test.js +1 -1
- package/plugins/search/src/defaults.js +1 -1
- package/plugins/search/src/events/fetchSuggestions.js +3 -3
- package/plugins/search/src/events/fetchSuggestions.test.js +1 -1
- package/plugins/search/src/events/formHandlers.js +3 -3
- package/plugins/search/src/events/formHandlers.test.js +1 -1
- package/plugins/search/src/events/index.js +2 -2
- package/plugins/search/src/events/index.test.js +2 -2
- package/plugins/search/src/events/inputHandlers.js +4 -4
- package/plugins/search/src/events/inputHandlers.test.js +1 -1
- package/plugins/search/src/events/suggestionHandlers.js +2 -2
- package/plugins/search/src/events/suggestionHandlers.test.js +1 -1
- package/plugins/search/src/index.js +2 -1
- package/plugins/search/src/index.test.js +3 -3
- package/plugins/search/src/manifest.js +6 -4
- package/plugins/search/src/reducer.js +1 -2
- package/plugins/search/src/reducer.test.js +2 -2
- package/plugins/search/src/search.scss +10 -3
- package/plugins/search/src/utils/parseOsNamesResults.js +1 -2
- package/plugins/search/src/utils/parseOsNamesResults.test.js +2 -2
- package/plugins/search/src/utils/updateMap.js +1 -1
- package/plugins/search/src/utils/updateMap.test.js +5 -5
- package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
- package/providers/beta/esri/src/esriProvider.js +5 -5
- package/providers/beta/esri/src/utils/coords.js +1 -1
- package/providers/beta/esri/src/utils/esriFixes.js +1 -1
- package/providers/beta/esri/src/utils/query.js +4 -4
- package/providers/beta/esri/src/utils/spatial.js +1 -2
- package/providers/beta/esri/src/utils/spatial.test.js +4 -1
- package/providers/beta/open-names/src/utils/mapToLocationModel.test.js +1 -1
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-framework.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-framework.js.LICENSE.txt +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/src/appEvents.test.js +1 -1
- package/providers/maplibre/src/index.js +1 -1
- package/providers/maplibre/src/index.test.js +3 -5
- package/providers/maplibre/src/mapEvents.test.js +15 -5
- package/providers/maplibre/src/maplibreProvider.test.js +6 -2
- package/providers/maplibre/src/utils/calculateLinearTextSize.js +4 -4
- package/providers/maplibre/src/utils/calculateLinearTextSize.test.js +3 -3
- package/providers/maplibre/src/utils/detectWebgl.test.js +1 -1
- package/providers/maplibre/src/utils/highlightFeatures.js +3 -2
- package/providers/maplibre/src/utils/highlightFeatures.test.js +13 -6
- package/providers/maplibre/src/utils/labels.js +19 -20
- package/providers/maplibre/src/utils/labels.test.js +15 -13
- package/providers/maplibre/src/utils/maplibreFixes.test.js +1 -1
- package/providers/maplibre/src/utils/queryFeatures.js +6 -6
- package/providers/maplibre/src/utils/queryFeatures.test.js +13 -13
- package/providers/maplibre/src/utils/spatial.js +0 -1
- package/providers/maplibre/src/utils/spatial.test.js +26 -27
- package/src/App/components/Actions/Actions.jsx +2 -2
- package/src/App/components/Actions/Actions.module.scss +0 -7
- package/src/App/components/Actions/Actions.test.jsx +1 -1
- package/src/App/components/Icon/Icon.jsx +3 -2
- package/src/App/components/Icon/Icon.module.scss +4 -0
- package/src/App/components/Icon/Icon.test.jsx +43 -4
- package/src/App/components/MapButton/MapButton.jsx +42 -17
- package/src/App/components/MapButton/MapButton.module.scss +4 -13
- package/src/App/components/MapButton/MapButton.test.jsx +27 -3
- package/src/App/components/PopupMenu/PopupMenu.jsx +51 -274
- package/src/App/components/PopupMenu/PopupMenu.module.scss +14 -7
- package/src/App/components/PopupMenu/PopupMenu.test.jsx +70 -1
- package/src/App/components/PopupMenu/usePopupMenu.js +258 -0
- package/src/App/hooks/useButtonStateEvaluator.js +12 -2
- package/src/App/hooks/useButtonStateEvaluator.test.js +38 -4
- package/src/App/hooks/useInterfaceAPI.js +6 -0
- package/src/App/hooks/useLayoutMeasurements.js +84 -18
- package/src/App/hooks/useLayoutMeasurements.test.js +124 -17
- package/src/App/layout/Layout.jsx +12 -7
- package/src/App/layout/Layout.test.jsx +2 -2
- package/src/App/layout/layout.module.scss +67 -29
- package/src/App/registry/pluginRegistry.js +17 -0
- package/src/App/registry/pluginRegistry.test.js +33 -0
- package/src/App/renderer/HtmlElementHost.jsx +2 -1
- package/src/App/renderer/HtmlElementHost.test.jsx +7 -7
- package/src/App/renderer/mapButtons.js +3 -2
- package/src/App/renderer/mapPanels.test.js +2 -2
- package/src/App/renderer/slotHelpers.js +2 -2
- package/src/App/renderer/slotHelpers.test.js +5 -5
- package/src/App/renderer/slots.js +9 -5
- package/src/App/store/AppProvider.jsx +3 -1
- package/src/App/store/AppProvider.test.jsx +1 -1
- package/src/App/store/ServiceProvider.jsx +3 -1
- package/src/App/store/appActionsMap.js +16 -0
- package/src/App/store/appActionsMap.test.js +27 -0
- package/src/App/store/appDispatchMiddleware.js +33 -1
- package/src/App/store/appDispatchMiddleware.test.js +250 -222
- package/src/App/store/appReducer.js +2 -0
- package/src/InteractiveMap/InteractiveMap.js +4 -0
- package/src/config/appConfig.js +7 -4
- package/src/config/events.js +28 -0
- package/src/scss/main.scss +1 -0
- package/src/scss/settings/_dimensions.scss +0 -1
- package/src/services/logger.js +6 -0
- package/src/services/logger.test.js +32 -0
- package/src/utils/getSafeZoneInset.js +9 -7
- package/src/utils/getSafeZoneInset.test.js +10 -10
- package/webpack.dev.mjs +23 -19
- package/docs/govuk-prototype.md +0 -23
- package/docs/index.md +0 -19
- package/plugins/beta/datasets/src/api/hideDataset.js +0 -14
- package/plugins/beta/datasets/src/api/hideFeatures.js +0 -41
- package/plugins/beta/datasets/src/api/showDataset.js +0 -14
- package/plugins/beta/datasets/src/api/showFeatures.js +0 -44
- package/plugins/beta/datasets/src/handleSetMapStyle.js +0 -54
- package/plugins/beta/datasets/src/mapLayers.js +0 -165
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { getValueForStyle } from '../../../../../../src/utils/getValueForStyle.js'
|
|
2
|
+
import { hasPattern, getPatternImageId } from '../../styles/patterns.js'
|
|
3
|
+
import { mergeSublayer } from '../../utils/mergeSublayer.js'
|
|
4
|
+
import { getSourceId, getLayerIds, getSublayerLayerIds, isDynamicSource, MAX_TILE_ZOOM } from './layerIds.js'
|
|
5
|
+
|
|
6
|
+
// ─── Source ───────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export const addSource = (map, dataset, sourceId) => {
|
|
9
|
+
if (map.getSource(sourceId)) {
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
if (dataset.tiles) {
|
|
13
|
+
map.addSource(sourceId, {
|
|
14
|
+
type: 'vector',
|
|
15
|
+
tiles: dataset.tiles,
|
|
16
|
+
minzoom: dataset.minZoom || 0,
|
|
17
|
+
maxzoom: dataset.maxZoom || MAX_TILE_ZOOM
|
|
18
|
+
})
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
if (dataset.geojson) {
|
|
22
|
+
const initialData = isDynamicSource(dataset)
|
|
23
|
+
? { type: 'FeatureCollection', features: [] }
|
|
24
|
+
: dataset.geojson
|
|
25
|
+
map.addSource(sourceId, { type: 'geojson', data: initialData, generateId: true })
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── Fill layer ───────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
export const addFillLayer = (map, config, layerId, sourceId, sourceLayer, visibility, mapStyleId) => {
|
|
32
|
+
if (!layerId || map.getLayer(layerId)) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
if (!config.fill && !hasPattern(config)) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
const patternImageId = hasPattern(config) ? getPatternImageId(config, mapStyleId) : null
|
|
39
|
+
const paint = patternImageId
|
|
40
|
+
? { 'fill-pattern': patternImageId, 'fill-opacity': config.opacity || 1 }
|
|
41
|
+
: { 'fill-color': getValueForStyle(config.fill, mapStyleId), 'fill-opacity': config.opacity || 1 }
|
|
42
|
+
map.addLayer({
|
|
43
|
+
id: layerId,
|
|
44
|
+
type: 'fill',
|
|
45
|
+
source: sourceId,
|
|
46
|
+
'source-layer': sourceLayer,
|
|
47
|
+
layout: { visibility },
|
|
48
|
+
paint,
|
|
49
|
+
...(config.filter ? { filter: config.filter } : {})
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ─── Stroke layer ─────────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
export const addStrokeLayer = (map, config, layerId, sourceId, sourceLayer, visibility, mapStyleId) => {
|
|
56
|
+
if (!layerId || !config.stroke || map.getLayer(layerId)) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
map.addLayer({
|
|
60
|
+
id: layerId,
|
|
61
|
+
type: 'line',
|
|
62
|
+
source: sourceId,
|
|
63
|
+
'source-layer': sourceLayer,
|
|
64
|
+
layout: { visibility },
|
|
65
|
+
paint: {
|
|
66
|
+
'line-color': getValueForStyle(config.stroke, mapStyleId),
|
|
67
|
+
'line-width': config.strokeWidth || 1,
|
|
68
|
+
'line-opacity': config.opacity || 1,
|
|
69
|
+
...(config.strokeDashArray ? { 'line-dasharray': config.strokeDashArray } : {})
|
|
70
|
+
},
|
|
71
|
+
...(config.filter ? { filter: config.filter } : {})
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── Dataset layers ───────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
export const addSublayerLayers = (map, dataset, sublayer, sourceId, sourceLayer, mapStyleId) => {
|
|
78
|
+
const merged = mergeSublayer(dataset, sublayer)
|
|
79
|
+
const { fillLayerId, strokeLayerId } = getSublayerLayerIds(dataset.id, sublayer.id)
|
|
80
|
+
const parentHidden = dataset.visibility === 'hidden'
|
|
81
|
+
const sublayerHidden = dataset.sublayerVisibility?.[sublayer.id] === 'hidden'
|
|
82
|
+
const visibility = (parentHidden || sublayerHidden) ? 'none' : 'visible'
|
|
83
|
+
addFillLayer(map, merged, fillLayerId, sourceId, sourceLayer, visibility, mapStyleId)
|
|
84
|
+
addStrokeLayer(map, merged, strokeLayerId, sourceId, sourceLayer, visibility, mapStyleId)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Add all layers (and source if needed) for a dataset.
|
|
89
|
+
* Returns the sourceId so the caller can track the datasetId → sourceId mapping.
|
|
90
|
+
* @param {Object} map - MapLibre map instance
|
|
91
|
+
* @param {Object} dataset
|
|
92
|
+
* @param {string} mapStyleId
|
|
93
|
+
* @returns {string} sourceId
|
|
94
|
+
*/
|
|
95
|
+
export const addDatasetLayers = (map, dataset, mapStyleId) => {
|
|
96
|
+
const sourceId = getSourceId(dataset)
|
|
97
|
+
addSource(map, dataset, sourceId)
|
|
98
|
+
|
|
99
|
+
const sourceLayer = dataset.tiles?.length ? dataset.sourceLayer : undefined
|
|
100
|
+
|
|
101
|
+
if (dataset.sublayers?.length) {
|
|
102
|
+
dataset.sublayers.forEach(sublayer => {
|
|
103
|
+
addSublayerLayers(map, dataset, sublayer, sourceId, sourceLayer, mapStyleId)
|
|
104
|
+
})
|
|
105
|
+
return sourceId
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const { fillLayerId, strokeLayerId } = getLayerIds(dataset)
|
|
109
|
+
const visibility = dataset.visibility === 'hidden' ? 'none' : 'visible'
|
|
110
|
+
addFillLayer(map, dataset, fillLayerId, sourceId, sourceLayer, visibility, mapStyleId)
|
|
111
|
+
addStrokeLayer(map, dataset, strokeLayerId, sourceId, sourceLayer, visibility, mapStyleId)
|
|
112
|
+
return sourceId
|
|
113
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { hasPattern } from '../../styles/patterns.js'
|
|
2
|
+
|
|
3
|
+
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
export const isDynamicSource = (dataset) =>
|
|
6
|
+
typeof dataset.geojson === 'string' &&
|
|
7
|
+
!!dataset.idProperty &&
|
|
8
|
+
typeof dataset.transformRequest === 'function'
|
|
9
|
+
|
|
10
|
+
const HASH_BASE = 36
|
|
11
|
+
const MAX_TILE_ZOOM = 22
|
|
12
|
+
|
|
13
|
+
export { MAX_TILE_ZOOM }
|
|
14
|
+
|
|
15
|
+
export const hashString = (str) => {
|
|
16
|
+
let hash = 0
|
|
17
|
+
for (const ch of str) {
|
|
18
|
+
hash = Math.trunc(((hash << 5) - hash) + ch.codePointAt(0))
|
|
19
|
+
}
|
|
20
|
+
return Math.abs(hash).toString(HASH_BASE)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ─── Source ID ────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export const getSourceId = (dataset) => {
|
|
26
|
+
if (dataset.tiles) {
|
|
27
|
+
const tilesKey = Array.isArray(dataset.tiles) ? dataset.tiles.join(',') : dataset.tiles
|
|
28
|
+
return `tiles-${hashString(tilesKey)}`
|
|
29
|
+
}
|
|
30
|
+
if (dataset.geojson) {
|
|
31
|
+
if (isDynamicSource(dataset)) {
|
|
32
|
+
return `geojson-dynamic-${dataset.id}`
|
|
33
|
+
}
|
|
34
|
+
if (typeof dataset.geojson === 'string') {
|
|
35
|
+
return `geojson-${hashString(dataset.geojson)}`
|
|
36
|
+
}
|
|
37
|
+
return `geojson-${dataset.id}`
|
|
38
|
+
}
|
|
39
|
+
return `source-${dataset.id}`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─── Layer IDs ────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export const getLayerIds = (dataset) => {
|
|
45
|
+
const hasFill = !!dataset.fill || hasPattern(dataset)
|
|
46
|
+
const hasStroke = !!dataset.stroke
|
|
47
|
+
const fillLayerId = hasFill ? dataset.id : null
|
|
48
|
+
let strokeLayerId = null
|
|
49
|
+
if (hasStroke) {
|
|
50
|
+
strokeLayerId = hasFill ? `${dataset.id}-stroke` : dataset.id
|
|
51
|
+
}
|
|
52
|
+
return { fillLayerId, strokeLayerId }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const getSublayerLayerIds = (datasetId, sublayerId) => ({
|
|
56
|
+
fillLayerId: `${datasetId}-${sublayerId}`,
|
|
57
|
+
strokeLayerId: `${datasetId}-${sublayerId}-stroke`
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
export const getAllLayerIds = (dataset) => {
|
|
61
|
+
if (dataset.sublayers?.length) {
|
|
62
|
+
return dataset.sublayers.flatMap(sublayer => {
|
|
63
|
+
const { fillLayerId, strokeLayerId } = getSublayerLayerIds(dataset.id, sublayer.id)
|
|
64
|
+
return [strokeLayerId, fillLayerId]
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
const { fillLayerId, strokeLayerId } = getLayerIds(dataset)
|
|
68
|
+
return [strokeLayerId, fillLayerId].filter(Boolean)
|
|
69
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { applyExclusionFilter } from '../../utils/filters.js'
|
|
2
|
+
import { getSourceId, getLayerIds, getSublayerLayerIds, getAllLayerIds } from './layerIds.js'
|
|
3
|
+
import { addDatasetLayers, addSublayerLayers } from './layerBuilders.js'
|
|
4
|
+
import { registerPatterns } from './patternRegistry.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MapLibre GL JS implementation of the LayerAdapter interface for the datasets plugin.
|
|
8
|
+
*
|
|
9
|
+
* Owns all map-framework-specific concerns:
|
|
10
|
+
* - Source and layer creation (delegated to layerBuilders)
|
|
11
|
+
* - Pattern image registration (delegated to patternRegistry)
|
|
12
|
+
* - Visibility toggling, feature filtering, style changes
|
|
13
|
+
* - Style-change recovery (re-adding layers after basemap swap)
|
|
14
|
+
*/
|
|
15
|
+
export default class MaplibreLayerAdapter {
|
|
16
|
+
constructor (map) {
|
|
17
|
+
this._map = map
|
|
18
|
+
// datasetId → sourceId, used by setData to update the correct source
|
|
19
|
+
this._datasetSourceMap = new Map()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ─── Lifecycle ──────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initialise all datasets: register patterns, add layers, then wait for idle.
|
|
26
|
+
* @param {Object[]} datasets
|
|
27
|
+
* @param {string} mapStyleId
|
|
28
|
+
* @returns {Promise<void>} Resolves once the map has processed all layers.
|
|
29
|
+
*/
|
|
30
|
+
async init (datasets, mapStyleId) {
|
|
31
|
+
await registerPatterns(this._map, datasets, mapStyleId)
|
|
32
|
+
datasets.forEach(dataset => this._addLayers(dataset, mapStyleId))
|
|
33
|
+
await new Promise(resolve => this._map.once('idle', resolve))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Remove all layers and sources for the given datasets.
|
|
38
|
+
* @param {Object[]} datasets
|
|
39
|
+
*/
|
|
40
|
+
destroy (datasets) {
|
|
41
|
+
const removedSourceIds = new Set()
|
|
42
|
+
datasets.forEach(dataset => {
|
|
43
|
+
const sourceId = getSourceId(dataset)
|
|
44
|
+
this._getLayersUsingSource(sourceId).forEach(layerId => {
|
|
45
|
+
if (this._map.getLayer(layerId)) {
|
|
46
|
+
this._map.removeLayer(layerId)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
if (!removedSourceIds.has(sourceId) && this._map.getSource(sourceId)) {
|
|
50
|
+
this._map.removeSource(sourceId)
|
|
51
|
+
removedSourceIds.add(sourceId)
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
this._datasetSourceMap.clear()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Re-register patterns and re-add all layers after a basemap style change,
|
|
59
|
+
* then reapply cached dynamic source data and hidden-feature filters.
|
|
60
|
+
* @param {Object[]} datasets
|
|
61
|
+
* @param {string} newStyleId
|
|
62
|
+
* @param {Object} hiddenFeatures - pluginState.hiddenFeatures
|
|
63
|
+
* @param {Map} dynamicSources - datasetId → dynamic source instance
|
|
64
|
+
* @returns {Promise<void>}
|
|
65
|
+
*/
|
|
66
|
+
async onStyleChange (datasets, newStyleId, hiddenFeatures, dynamicSources) {
|
|
67
|
+
// MapLibre wipes all sources/layers on style change — must wait for idle first
|
|
68
|
+
await new Promise(resolve => this._map.once('idle', resolve))
|
|
69
|
+
|
|
70
|
+
await registerPatterns(this._map, datasets, newStyleId)
|
|
71
|
+
datasets.forEach(dataset => this._addLayers(dataset, newStyleId))
|
|
72
|
+
|
|
73
|
+
// Re-push cached data for dynamic sources
|
|
74
|
+
dynamicSources.forEach(source => source.reapply())
|
|
75
|
+
|
|
76
|
+
// Reapply hidden feature filters
|
|
77
|
+
Object.entries(hiddenFeatures).forEach(([datasetId, { idProperty, ids }]) => {
|
|
78
|
+
const dataset = datasets.find(d => d.id === datasetId)
|
|
79
|
+
if (!dataset) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
this._applyFeatureFilter(dataset, idProperty, ids)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ─── Dataset operations ─────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Add a single dataset's source and layers to the map.
|
|
90
|
+
* @param {Object} dataset
|
|
91
|
+
* @param {string} mapStyleId
|
|
92
|
+
*/
|
|
93
|
+
addDataset (dataset, mapStyleId) {
|
|
94
|
+
this._addLayers(dataset, mapStyleId)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Remove a dataset's layers and source from the map.
|
|
99
|
+
* Shared sources (same tiles URL or geojson URL used by multiple datasets) are
|
|
100
|
+
* only removed when no other remaining dataset references them.
|
|
101
|
+
* @param {Object} dataset
|
|
102
|
+
* @param {Object[]} allDatasets - Full current dataset list, for shared-source check.
|
|
103
|
+
*/
|
|
104
|
+
removeDataset (dataset, allDatasets) {
|
|
105
|
+
const sourceId = getSourceId(dataset)
|
|
106
|
+
|
|
107
|
+
getAllLayerIds(dataset).forEach(layerId => {
|
|
108
|
+
if (this._map.getLayer(layerId)) {
|
|
109
|
+
this._map.removeLayer(layerId)
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const sourceIsShared = allDatasets.some(d => d.id !== dataset.id && getSourceId(d) === sourceId)
|
|
114
|
+
|
|
115
|
+
if (!sourceIsShared && this._map.getSource(sourceId)) {
|
|
116
|
+
this._map.removeSource(sourceId)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this._datasetSourceMap.delete(dataset.id)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Make a dataset's layers visible.
|
|
124
|
+
* @param {string} datasetId
|
|
125
|
+
*/
|
|
126
|
+
showDataset (datasetId) {
|
|
127
|
+
this._setDatasetVisibility(datasetId, 'visible')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Hide a dataset's layers.
|
|
132
|
+
* @param {string} datasetId
|
|
133
|
+
*/
|
|
134
|
+
hideDataset (datasetId) {
|
|
135
|
+
this._setDatasetVisibility(datasetId, 'none')
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Make a single sublayer's layers visible.
|
|
140
|
+
* @param {string} datasetId
|
|
141
|
+
* @param {string} sublayerId
|
|
142
|
+
*/
|
|
143
|
+
showSublayer (datasetId, sublayerId) {
|
|
144
|
+
const { fillLayerId, strokeLayerId } = getSublayerLayerIds(datasetId, sublayerId)
|
|
145
|
+
if (this._map.getLayer(fillLayerId)) {
|
|
146
|
+
this._map.setLayoutProperty(fillLayerId, 'visibility', 'visible')
|
|
147
|
+
}
|
|
148
|
+
if (this._map.getLayer(strokeLayerId)) {
|
|
149
|
+
this._map.setLayoutProperty(strokeLayerId, 'visibility', 'visible')
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Hide a single sublayer's layers.
|
|
155
|
+
* @param {string} datasetId
|
|
156
|
+
* @param {string} sublayerId
|
|
157
|
+
*/
|
|
158
|
+
hideSublayer (datasetId, sublayerId) {
|
|
159
|
+
const { fillLayerId, strokeLayerId } = getSublayerLayerIds(datasetId, sublayerId)
|
|
160
|
+
if (this._map.getLayer(fillLayerId)) {
|
|
161
|
+
this._map.setLayoutProperty(fillLayerId, 'visibility', 'none')
|
|
162
|
+
}
|
|
163
|
+
if (this._map.getLayer(strokeLayerId)) {
|
|
164
|
+
this._map.setLayoutProperty(strokeLayerId, 'visibility', 'none')
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ─── Feature operations ─────────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Show previously hidden features by updating the layer exclusion filter.
|
|
172
|
+
* @param {Object} dataset
|
|
173
|
+
* @param {string} idProperty
|
|
174
|
+
* @param {Array} remainingHiddenIds - IDs that should remain hidden after this call.
|
|
175
|
+
*/
|
|
176
|
+
showFeatures (dataset, idProperty, remainingHiddenIds) {
|
|
177
|
+
this._applyFeatureFilter(dataset, idProperty, remainingHiddenIds)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Hide features by updating the layer exclusion filter.
|
|
182
|
+
* @param {Object} dataset
|
|
183
|
+
* @param {string} idProperty
|
|
184
|
+
* @param {Array} allHiddenIds - Full set of IDs to hide (existing + new).
|
|
185
|
+
*/
|
|
186
|
+
hideFeatures (dataset, idProperty, allHiddenIds) {
|
|
187
|
+
this._applyFeatureFilter(dataset, idProperty, allHiddenIds)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Update a dataset's style and re-render all its layers.
|
|
192
|
+
* @param {Object} dataset - Updated dataset (style changes already merged in)
|
|
193
|
+
* @param {string} mapStyleId
|
|
194
|
+
* @returns {Promise<void>}
|
|
195
|
+
*/
|
|
196
|
+
async setStyle (dataset, mapStyleId) {
|
|
197
|
+
getAllLayerIds(dataset).forEach(layerId => {
|
|
198
|
+
if (this._map.getLayer(layerId)) {
|
|
199
|
+
this._map.removeLayer(layerId)
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
await registerPatterns(this._map, [dataset], mapStyleId)
|
|
203
|
+
this._addLayers(dataset, mapStyleId)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Update a single sublayer's style and re-render its layers.
|
|
208
|
+
* @param {Object} dataset - Updated dataset (sublayer style changes already merged in)
|
|
209
|
+
* @param {string} sublayerId
|
|
210
|
+
* @param {string} mapStyleId
|
|
211
|
+
* @returns {Promise<void>}
|
|
212
|
+
*/
|
|
213
|
+
async setSublayerStyle (dataset, sublayerId, mapStyleId) {
|
|
214
|
+
const { fillLayerId, strokeLayerId } = getSublayerLayerIds(dataset.id, sublayerId)
|
|
215
|
+
if (this._map.getLayer(fillLayerId)) {
|
|
216
|
+
this._map.removeLayer(fillLayerId)
|
|
217
|
+
}
|
|
218
|
+
if (this._map.getLayer(strokeLayerId)) {
|
|
219
|
+
this._map.removeLayer(strokeLayerId)
|
|
220
|
+
}
|
|
221
|
+
const sublayer = dataset.sublayers?.find(s => s.id === sublayerId)
|
|
222
|
+
if (!sublayer) {
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
await registerPatterns(this._map, [dataset], mapStyleId)
|
|
226
|
+
const sourceId = this._datasetSourceMap.get(dataset.id)
|
|
227
|
+
const sourceLayer = dataset.tiles?.length ? dataset.sourceLayer : undefined
|
|
228
|
+
addSublayerLayers(this._map, dataset, sublayer, sourceId, sourceLayer, mapStyleId)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Set opacity for all layers belonging to a dataset.
|
|
233
|
+
* Uses setPaintProperty directly — safe to call on every slider tick.
|
|
234
|
+
* @param {string} datasetId
|
|
235
|
+
* @param {number} opacity
|
|
236
|
+
*/
|
|
237
|
+
setOpacity (datasetId, opacity) {
|
|
238
|
+
const style = this._map.getStyle()
|
|
239
|
+
if (!style?.layers) {
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
style.layers
|
|
243
|
+
.filter(layer => layer.id === datasetId || layer.id.startsWith(`${datasetId}-`))
|
|
244
|
+
.forEach(layer => this._setPaintOpacity(layer.id, opacity))
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Set opacity for a single sublayer's fill and stroke layers.
|
|
249
|
+
* Uses setPaintProperty directly — safe to call on every slider tick.
|
|
250
|
+
* @param {string} datasetId
|
|
251
|
+
* @param {string} sublayerId
|
|
252
|
+
* @param {number} opacity
|
|
253
|
+
*/
|
|
254
|
+
setSublayerOpacity (datasetId, sublayerId, opacity) {
|
|
255
|
+
const { fillLayerId, strokeLayerId } = getSublayerLayerIds(datasetId, sublayerId)
|
|
256
|
+
this._setPaintOpacity(fillLayerId, opacity)
|
|
257
|
+
this._setPaintOpacity(strokeLayerId, opacity)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Update the GeoJSON data for a dataset's source.
|
|
262
|
+
* @param {string} datasetId
|
|
263
|
+
* @param {Object} geojson - GeoJSON FeatureCollection
|
|
264
|
+
*/
|
|
265
|
+
setData (datasetId, geojson) {
|
|
266
|
+
const sourceId = this._datasetSourceMap.get(datasetId)
|
|
267
|
+
if (!sourceId) {
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
const source = this._map.getSource(sourceId)
|
|
271
|
+
if (source && typeof source.setData === 'function') {
|
|
272
|
+
source.setData(geojson)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ─── Private ─────────────────────────────────────────────────────────────────
|
|
277
|
+
|
|
278
|
+
_addLayers (dataset, mapStyleId) {
|
|
279
|
+
const sourceId = addDatasetLayers(this._map, dataset, mapStyleId)
|
|
280
|
+
this._datasetSourceMap.set(dataset.id, sourceId)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
_setDatasetVisibility (datasetId, visibility) {
|
|
284
|
+
const style = this._map.getStyle()
|
|
285
|
+
if (!style?.layers) {
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
// Covers base fill layer (datasetId) and all suffixed layers
|
|
289
|
+
// (-stroke, -${sublayerId}, -${sublayerId}-stroke) without needing the dataset object.
|
|
290
|
+
style.layers
|
|
291
|
+
.filter(layer =>
|
|
292
|
+
layer.id === datasetId ||
|
|
293
|
+
layer.id.startsWith(`${datasetId}-`)
|
|
294
|
+
)
|
|
295
|
+
.forEach(layer => this._map.setLayoutProperty(layer.id, 'visibility', visibility))
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
_applyFeatureFilter (dataset, idProperty, excludeIds) {
|
|
299
|
+
if (dataset.sublayers?.length) {
|
|
300
|
+
dataset.sublayers.forEach(sublayer => {
|
|
301
|
+
const { fillLayerId: subFillId, strokeLayerId: subStrokeId } = getSublayerLayerIds(dataset.id, sublayer.id)
|
|
302
|
+
const sublayerFilter = dataset.filter && sublayer.filter
|
|
303
|
+
? ['all', dataset.filter, sublayer.filter]
|
|
304
|
+
: (sublayer.filter || dataset.filter || null)
|
|
305
|
+
applyExclusionFilter(this._map, subFillId, sublayerFilter, idProperty, excludeIds)
|
|
306
|
+
applyExclusionFilter(this._map, subStrokeId, sublayerFilter, idProperty, excludeIds)
|
|
307
|
+
})
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
const { fillLayerId, strokeLayerId } = getLayerIds(dataset)
|
|
311
|
+
const originalFilter = dataset.filter || null
|
|
312
|
+
if (fillLayerId) {
|
|
313
|
+
applyExclusionFilter(this._map, fillLayerId, originalFilter, idProperty, excludeIds)
|
|
314
|
+
}
|
|
315
|
+
if (strokeLayerId) {
|
|
316
|
+
applyExclusionFilter(this._map, strokeLayerId, originalFilter, idProperty, excludeIds)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
_setPaintOpacity (layerId, opacity) {
|
|
321
|
+
const layer = this._map.getLayer(layerId)
|
|
322
|
+
if (!layer) {
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
const prop = layer.type === 'line' ? 'line-opacity' : 'fill-opacity'
|
|
326
|
+
this._map.setPaintProperty(layerId, prop, opacity)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
_getLayersUsingSource (sourceId) {
|
|
330
|
+
const style = this._map.getStyle()
|
|
331
|
+
if (!style?.layers) {
|
|
332
|
+
return []
|
|
333
|
+
}
|
|
334
|
+
return style.layers
|
|
335
|
+
.filter(layer => layer.source === sourceId)
|
|
336
|
+
.map(layer => layer.id)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { hasPattern, getPatternImageId, rasterisePattern } from '../../styles/patterns.js'
|
|
2
|
+
import { mergeSublayer } from '../../utils/mergeSublayer.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Collect all style configs that require a pattern image: top-level datasets
|
|
6
|
+
* and any sublayers whose merged style has a pattern.
|
|
7
|
+
* @param {Object[]} datasets
|
|
8
|
+
* @returns {Object[]}
|
|
9
|
+
*/
|
|
10
|
+
const getPatternConfigs = (datasets) =>
|
|
11
|
+
datasets.flatMap(dataset => {
|
|
12
|
+
const configs = hasPattern(dataset) ? [dataset] : []
|
|
13
|
+
if (dataset.sublayers?.length) {
|
|
14
|
+
dataset.sublayers.forEach(sublayer => {
|
|
15
|
+
const merged = mergeSublayer(dataset, sublayer)
|
|
16
|
+
if (hasPattern(merged)) {
|
|
17
|
+
configs.push(merged)
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
return configs
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Register all required pattern images with the map.
|
|
26
|
+
* Skips images that are already registered (safe to call on style change).
|
|
27
|
+
* @param {Object} map - MapLibre map instance
|
|
28
|
+
* @param {Object[]} datasets
|
|
29
|
+
* @param {string} mapStyleId
|
|
30
|
+
* @returns {Promise<void>}
|
|
31
|
+
*/
|
|
32
|
+
export const registerPatterns = async (map, datasets, mapStyleId) => {
|
|
33
|
+
const patternConfigs = getPatternConfigs(datasets)
|
|
34
|
+
if (!patternConfigs.length) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await Promise.all(patternConfigs.map(async (config) => {
|
|
39
|
+
const imageId = getPatternImageId(config, mapStyleId)
|
|
40
|
+
if (!imageId || map.hasImage(imageId)) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
const result = await rasterisePattern(config, mapStyleId)
|
|
44
|
+
if (result) {
|
|
45
|
+
map.addImage(result.imageId, result.imageData, { pixelRatio: 2 })
|
|
46
|
+
}
|
|
47
|
+
}))
|
|
48
|
+
}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { datasetDefaults } from '../defaults.js'
|
|
2
|
-
import { addMapLayers } from '../mapLayers.js'
|
|
3
2
|
|
|
4
|
-
export const addDataset = ({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Add source and layers to the map
|
|
8
|
-
addMapLayers(map, mapState.mapStyle.id, { ...datasetDefaults, ...dataset })
|
|
9
|
-
|
|
10
|
-
// Update state
|
|
3
|
+
export const addDataset = ({ pluginState, mapState }, dataset) => {
|
|
4
|
+
pluginState.layerAdapter?.addDataset({ ...datasetDefaults, ...dataset }, mapState.mapStyle.id)
|
|
11
5
|
pluginState.dispatch({ type: 'ADD_DATASET', payload: { dataset, datasetDefaults } })
|
|
12
|
-
}
|
|
6
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const getOpacity = ({ pluginState }, options) => {
|
|
2
|
+
const { datasetId, sublayerId } = options || {}
|
|
3
|
+
|
|
4
|
+
if (sublayerId) {
|
|
5
|
+
const dataset = pluginState.datasets?.find(d => d.id === datasetId)
|
|
6
|
+
const sublayer = dataset?.sublayers?.find(s => s.id === sublayerId)
|
|
7
|
+
return sublayer?.style?.opacity ?? null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (datasetId) {
|
|
11
|
+
const dataset = pluginState.datasets?.find(d => d.id === datasetId)
|
|
12
|
+
return dataset?.opacity ?? null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Global — return first dataset's opacity
|
|
16
|
+
return pluginState.datasets?.[0]?.opacity ?? null
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const getStyle = ({ pluginState }, { datasetId, sublayerId } = {}) => {
|
|
2
|
+
const dataset = pluginState.datasets?.find(d => d.id === datasetId)
|
|
3
|
+
if (!dataset) {
|
|
4
|
+
return null
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
if (sublayerId) {
|
|
8
|
+
const sublayer = dataset.sublayers?.find(s => s.id === sublayerId)
|
|
9
|
+
return sublayer?.style ?? null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return dataset.style ?? null
|
|
13
|
+
}
|
|
@@ -1,51 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const getLayerIds = (dataset) => {
|
|
4
|
-
const hasFill = !!dataset.fill
|
|
5
|
-
const hasStroke = !!dataset.stroke
|
|
6
|
-
|
|
7
|
-
const fillLayerId = hasFill ? dataset.id : null
|
|
8
|
-
let strokeLayerId = null
|
|
9
|
-
if (hasStroke) {
|
|
10
|
-
if (hasFill) {
|
|
11
|
-
strokeLayerId = `${dataset.id}-stroke`
|
|
12
|
-
} else {
|
|
13
|
-
strokeLayerId = dataset.id
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return { fillLayerId, strokeLayerId }
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const removeDataset = ({ mapProvider, pluginState }, datasetId) => {
|
|
21
|
-
const map = mapProvider.map
|
|
22
|
-
|
|
23
|
-
// Find the dataset
|
|
1
|
+
export const removeDataset = ({ pluginState }, datasetId) => {
|
|
24
2
|
const dataset = pluginState.datasets?.find(d => d.id === datasetId)
|
|
25
3
|
if (!dataset) {
|
|
26
4
|
return
|
|
27
5
|
}
|
|
28
6
|
|
|
29
|
-
|
|
30
|
-
const { fillLayerId, strokeLayerId } = getLayerIds(dataset)
|
|
31
|
-
const sourceId = getSourceId(dataset)
|
|
32
|
-
|
|
33
|
-
// Remove layers first
|
|
34
|
-
const layerIdsToRemove = [strokeLayerId, fillLayerId]
|
|
35
|
-
layerIdsToRemove.forEach(layerId => {
|
|
36
|
-
if (layerId && map.getLayer(layerId)) {
|
|
37
|
-
map.removeLayer(layerId)
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
// Remove source if no other datasets use it
|
|
42
|
-
const otherDatasetsUseSource = pluginState.datasets?.some(
|
|
43
|
-
d => d.id !== datasetId && getSourceId(d) === sourceId
|
|
44
|
-
)
|
|
45
|
-
if (!otherDatasetsUseSource && map.getSource(sourceId)) {
|
|
46
|
-
map.removeSource(sourceId)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Update plugin state
|
|
7
|
+
pluginState.layerAdapter?.removeDataset(dataset, pluginState.datasets)
|
|
50
8
|
pluginState.dispatch({ type: 'REMOVE_DATASET', payload: { id: datasetId } })
|
|
51
|
-
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const setData = ({ pluginState, services }, geojson, { datasetId }) => {
|
|
2
|
+
const dataset = pluginState.datasets?.find(d => d.id === datasetId)
|
|
3
|
+
if (dataset?.tiles) {
|
|
4
|
+
services.logger.warn(`setData called on vector tile dataset "${datasetId}" — has no effect`)
|
|
5
|
+
return
|
|
6
|
+
}
|
|
7
|
+
pluginState.layerAdapter?.setData(datasetId, geojson)
|
|
8
|
+
}
|