@defra/interactive-map 0.0.30-alpha → 0.0.32-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.
Files changed (247) hide show
  1. package/README.md +2 -2
  2. package/assets/images/bright-ofm-thumb.jpg +0 -0
  3. package/assets/images/dark-ofm-thumb.jpg +0 -0
  4. package/assets/images/road-ofm-thumb.jpg +0 -0
  5. package/assets/templates/add-polygons.njk +143 -0
  6. package/assets/templates/add-symbols.njk +119 -0
  7. package/assets/templates/{map.njk → basic-map.njk} +9 -17
  8. package/assets/templates/button-map.njk +56 -0
  9. package/assets/templates/draw-tools.njk +161 -0
  10. package/assets/templates/marker-panel.njk +91 -0
  11. package/assets/templates/place-marker.njk +104 -0
  12. package/assets/templates/search-control.njk +98 -0
  13. package/assets/templates/select-feature.njk +101 -0
  14. package/assets/templates/style-switcher.njk +87 -0
  15. package/assets/templates/toggle-marker-label.njk +74 -0
  16. package/dist/css/index.css +1 -1
  17. package/dist/esm/im-core.js +1 -1
  18. package/dist/esm/im-shell.js +1 -1
  19. package/dist/umd/im-core.js +1 -1
  20. package/dist/umd/index.js +1 -1
  21. package/docs/api/context.md +4 -4
  22. package/docs/api.md +166 -37
  23. package/docs/assets/css/docusaurus.scss +36 -4
  24. package/docs/assets/images/add-polygons.jpg +0 -0
  25. package/docs/assets/images/add-symbols.jpg +0 -0
  26. package/docs/assets/images/basic-map.jpg +0 -0
  27. package/docs/assets/images/button-first.jpg +0 -0
  28. package/docs/assets/images/deuteranopia-ozs-map-thumb.jpg +0 -0
  29. package/docs/assets/images/draw.jpg +0 -0
  30. package/docs/assets/images/marker-panel.jpg +0 -0
  31. package/docs/assets/images/night-ozs-map-thumb.jpg +0 -0
  32. package/docs/assets/images/outdoor-ozs-map-thumb.jpg +0 -0
  33. package/docs/assets/images/place-marker.jpg +0 -0
  34. package/docs/assets/images/screenshot.jpg +0 -0
  35. package/docs/assets/images/search.jpg +0 -0
  36. package/docs/assets/images/select-feature.jpg +0 -0
  37. package/docs/assets/images/style-switcher.jpg +0 -0
  38. package/docs/assets/images/toggle-marker-label.jpg +0 -0
  39. package/docs/assets/images/tritanopia-ozs-map-thumb.jpg +0 -0
  40. package/docs/examples/add-marker-with-panel.mdx +3 -3
  41. package/docs/examples/add-polygons.mdx +225 -0
  42. package/docs/examples/add-symbols.mdx +177 -0
  43. package/docs/examples/basic-map.mdx +2 -2
  44. package/docs/examples/draw-tools.mdx +271 -0
  45. package/docs/examples/index.mdx +95 -11
  46. package/docs/examples/place-marker.mdx +151 -0
  47. package/docs/examples/search-control.mdx +71 -0
  48. package/docs/examples/select-feature.mdx +146 -0
  49. package/docs/examples/style-switcher.mdx +135 -0
  50. package/docs/examples/toggle-marker-label.mdx +3 -3
  51. package/docs/plugins/datasets.md +157 -122
  52. package/docs/plugins/interact.md +1 -4
  53. package/docusaurus.config.cjs +9 -2
  54. package/govuk-prototype-kit.config.json +52 -2
  55. package/package.json +1 -1
  56. package/plugins/beta/datasets/dist/css/index.css +5 -2
  57. package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -0
  58. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  59. package/plugins/beta/datasets/dist/esm/index.js +1 -1
  60. package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +2 -0
  61. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
  62. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  63. package/plugins/beta/datasets/src/adapters/loadLayerAdapter.js +33 -0
  64. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +44 -120
  65. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +145 -245
  66. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +516 -0
  67. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +209 -0
  68. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +483 -0
  69. package/plugins/beta/datasets/src/api/addDataset.js +2 -5
  70. package/plugins/beta/datasets/src/api/addDataset.test.js +16 -0
  71. package/plugins/beta/datasets/src/api/getOpacity.js +14 -14
  72. package/plugins/beta/datasets/src/api/getOpacity.test.js +72 -0
  73. package/plugins/beta/datasets/src/api/getStyle.js +8 -9
  74. package/plugins/beta/datasets/src/api/getStyle.test.js +42 -0
  75. package/plugins/beta/datasets/src/api/removeDataset.js +7 -7
  76. package/plugins/beta/datasets/src/api/removeDataset.test.js +45 -0
  77. package/plugins/beta/datasets/src/api/setData.js +9 -3
  78. package/plugins/beta/datasets/src/api/setData.test.js +62 -0
  79. package/plugins/beta/datasets/src/api/setDatasetVisibility.js +5 -30
  80. package/plugins/beta/datasets/src/api/setDatasetVisibility.test.js +31 -0
  81. package/plugins/beta/datasets/src/api/setFeatureVisibility.js +9 -18
  82. package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +74 -0
  83. package/plugins/beta/datasets/src/api/setGlobals.js +20 -0
  84. package/plugins/beta/datasets/src/api/setGlobals.test.js +63 -0
  85. package/plugins/beta/datasets/src/api/setOpacity.js +8 -27
  86. package/plugins/beta/datasets/src/api/setOpacity.test.js +31 -0
  87. package/plugins/beta/datasets/src/api/setStyle.js +5 -21
  88. package/plugins/beta/datasets/src/api/setStyle.test.js +53 -0
  89. package/plugins/beta/datasets/src/components/Key/Key.jsx +51 -0
  90. package/plugins/beta/datasets/src/components/Key/KeyGroupItem.jsx +18 -0
  91. package/plugins/beta/datasets/src/components/Key/KeyItem.jsx +20 -0
  92. package/plugins/beta/datasets/src/components/Key/KeySvg.jsx +22 -0
  93. package/plugins/beta/datasets/src/components/Key/KeySvg.test.jsx +87 -0
  94. package/plugins/beta/datasets/src/components/Key/KeySvgLine.jsx +19 -0
  95. package/plugins/beta/datasets/src/components/Key/KeySvgLine.test.jsx +71 -0
  96. package/plugins/beta/datasets/src/components/{KeySvgPattern.jsx → Key/KeySvgPattern.jsx} +3 -4
  97. package/plugins/beta/datasets/src/components/Key/KeySvgPattern.test.jsx +55 -0
  98. package/plugins/beta/datasets/src/components/Key/KeySvgRect.jsx +22 -0
  99. package/plugins/beta/datasets/src/components/Key/KeySvgRect.test.jsx +85 -0
  100. package/plugins/beta/datasets/src/components/{KeySvgSymbol.jsx → Key/KeySvgSymbol.jsx} +5 -5
  101. package/plugins/beta/datasets/src/components/Key/KeySvgSymbol.test.jsx +80 -0
  102. package/plugins/beta/datasets/src/components/Key/svgProperties.test.js +61 -0
  103. package/plugins/beta/datasets/src/{panels → components/LayersMenu}/Layers.module.scss +15 -11
  104. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenu.jsx +33 -0
  105. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuCheckbox.jsx +31 -0
  106. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuGroupWrapper.jsx +19 -0
  107. package/plugins/beta/datasets/src/datasets.scss +2 -2
  108. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +8 -8
  109. package/plugins/beta/datasets/src/{DatasetsInit.jsx → initialise/DatasetsInit.jsx} +19 -21
  110. package/plugins/beta/datasets/src/{defaults.js → initialise/defaults.js} +17 -2
  111. package/plugins/beta/datasets/src/initialise/defaults.test.js +127 -0
  112. package/plugins/beta/datasets/src/{datasets.js → initialise/initialiseDatasets.js} +30 -26
  113. package/plugins/beta/datasets/src/initialise/initialiseDatasets.test.js +273 -0
  114. package/plugins/beta/datasets/src/manifest.js +8 -6
  115. package/plugins/beta/datasets/src/reducers/__data__/demoDatasets.js +253 -0
  116. package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +83 -0
  117. package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +84 -0
  118. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +41 -0
  119. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.test.js +117 -0
  120. package/plugins/beta/datasets/src/reducers/pluginState.js +206 -0
  121. package/plugins/beta/datasets/src/reducers/pluginState.test.js +386 -0
  122. package/plugins/beta/datasets/src/registry/__mocks__/datasetRegistry.js +29 -0
  123. package/plugins/beta/datasets/src/registry/createDataset.js +5 -0
  124. package/plugins/beta/datasets/src/registry/dataset.js +176 -0
  125. package/plugins/beta/datasets/src/registry/dataset.test.js +497 -0
  126. package/plugins/beta/datasets/src/registry/datasetDefinitionCache.js +35 -0
  127. package/plugins/beta/datasets/src/registry/datasetRegistry.js +116 -0
  128. package/plugins/beta/datasets/src/registry/datasetRegistry.test.js +335 -0
  129. package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +17 -0
  130. package/plugins/beta/datasets/src/registry/globalDataset.js +36 -0
  131. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  132. package/plugins/beta/draw-es/src/api/addFeature.js +12 -3
  133. package/plugins/beta/draw-es/src/events.js +8 -3
  134. package/plugins/beta/draw-es/src/events.test.js +2 -0
  135. package/plugins/beta/draw-es/src/graphic.js +26 -2
  136. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  137. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  138. package/plugins/beta/draw-ml/src/DrawInit.jsx +8 -0
  139. package/plugins/beta/draw-ml/src/api/newLine.js +1 -0
  140. package/plugins/beta/draw-ml/src/api/newPolygon.js +1 -0
  141. package/plugins/beta/draw-ml/src/events.js +13 -1
  142. package/plugins/beta/draw-ml/src/index.js +1 -1
  143. package/plugins/beta/draw-ml/src/manifest.js +3 -0
  144. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +21 -12
  145. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +4 -0
  146. package/plugins/beta/draw-ol/dist/esm/EditMode.js +1 -1
  147. package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
  148. package/plugins/beta/draw-ol/src/edit/keyboardHandler.js +4 -2
  149. package/plugins/beta/map-styles/src/mapStyles.scss +41 -41
  150. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  151. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  152. package/plugins/interact/dist/umd/index.js +1 -1
  153. package/plugins/interact/src/InteractInit.jsx +3 -5
  154. package/plugins/interact/src/InteractInit.test.js +1 -3
  155. package/plugins/interact/src/events.js +2 -29
  156. package/plugins/interact/src/events.test.js +4 -102
  157. package/plugins/interact/src/hooks/useAttachEvents.js +3 -4
  158. package/plugins/interact/src/hooks/useAttachEvents.test.js +1 -3
  159. package/plugins/interact/src/hooks/useCrossHairVisibility.js +4 -10
  160. package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +5 -10
  161. package/plugins/interact/src/hooks/useHighlightSync.js +1 -10
  162. package/plugins/interact/src/hooks/useHighlightSync.test.js +2 -21
  163. package/plugins/interact/src/hooks/useInteractionHandlers.js +15 -12
  164. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +28 -29
  165. package/plugins/interact/src/manifest.js +0 -35
  166. package/plugins/interact/src/manifest.test.js +3 -43
  167. package/plugins/interact/src/reducer.js +4 -22
  168. package/plugins/interact/src/reducer.test.js +11 -39
  169. package/plugins/search/dist/css/index.css +5 -2
  170. package/plugins/search/src/components/Form/Form.module.scss +5 -2
  171. package/plugins/search/src/search.scss +4 -4
  172. package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
  173. package/providers/beta/esri/src/esriProvider.js +4 -0
  174. package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
  175. package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
  176. package/providers/beta/openlayers/src/openlayersProvider.js +4 -0
  177. package/providers/beta/openlayers/src/openlayersProvider.test.js +5 -0
  178. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  179. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  180. package/providers/maplibre/src/maplibreProvider.js +6 -4
  181. package/providers/maplibre/src/maplibreProvider.test.js +5 -0
  182. package/providers/maplibre/src/utils/highlightFeatures.js +74 -25
  183. package/providers/maplibre/src/utils/highlightFeatures.test.js +132 -0
  184. package/providers/maplibre/src/utils/patternImages.js +2 -4
  185. package/providers/maplibre/src/utils/symbolImages.js +1 -4
  186. package/rollup.esm.mjs +2 -8
  187. package/src/App/components/CrossHair/CrossHair.module.scss +8 -8
  188. package/src/App/components/MapButton/MapButton.jsx +16 -1
  189. package/src/App/components/MapButton/MapButton.module.scss +12 -1
  190. package/src/App/components/MapButton/MapButton.test.jsx +40 -1
  191. package/src/App/hooks/useContinueEnabledEvaluator.js +31 -0
  192. package/src/App/hooks/useContinueEnabledEvaluator.test.js +118 -0
  193. package/src/App/hooks/useEvaluateProp.js +15 -15
  194. package/src/App/hooks/useEvaluateProp.test.js +80 -76
  195. package/src/App/hooks/useLayoutMeasurements.js +11 -5
  196. package/src/App/hooks/useResizeObserver.js +15 -2
  197. package/src/App/hooks/useResizeObserver.test.js +1 -0
  198. package/src/App/renderer/PluginInits.jsx +2 -0
  199. package/src/App/renderer/mapButtons.js +21 -1
  200. package/src/App/renderer/mapButtons.test.js +46 -1
  201. package/src/App/store/appReducer.js +1 -1
  202. package/src/App/store/appReducer.test.js +37 -0
  203. package/src/InteractiveMap/InteractiveMap.js +14 -4
  204. package/src/InteractiveMap/InteractiveMap.test.js +11 -3
  205. package/src/InteractiveMap/behaviourController.js +3 -1
  206. package/src/InteractiveMap/behaviourController.test.js +6 -3
  207. package/src/InteractiveMap/domStateManager.js +2 -0
  208. package/src/config/appConfig.js +25 -0
  209. package/src/config/appConfig.test.js +50 -0
  210. package/src/config/defaults.js +1 -0
  211. package/src/config/events.js +21 -3
  212. package/src/scss/settings/_dimensions.scss +1 -0
  213. package/src/services/patternRegistry.js +8 -4
  214. package/src/types.js +7 -0
  215. package/src/utils/patternUtils.js +1 -2
  216. package/src/utils/patternUtils.test.js +9 -0
  217. package/webpack.dev.mjs +10 -0
  218. package/webpack.umd.mjs +1 -1
  219. package/docs/assets/images/screens-blue.jpg +0 -0
  220. package/docs/assets/images/screens-white.jpg +0 -0
  221. package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +0 -1
  222. package/plugins/beta/datasets/dist/adapters/maplibre/esm/index.js +0 -1
  223. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +0 -2
  224. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +0 -2
  225. package/plugins/beta/datasets/dist/umd/maplibre/index.js +0 -2
  226. package/plugins/beta/datasets/src/adapters/maplibre/index.js +0 -18
  227. package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +0 -75
  228. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +0 -27
  229. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +0 -31
  230. package/plugins/beta/datasets/src/components/KeySvg.jsx +0 -24
  231. package/plugins/beta/datasets/src/components/KeySvgLine.jsx +0 -19
  232. package/plugins/beta/datasets/src/components/KeySvgRect.jsx +0 -22
  233. package/plugins/beta/datasets/src/index.maplibre.umd.js +0 -23
  234. package/plugins/beta/datasets/src/panels/Key.jsx +0 -62
  235. package/plugins/beta/datasets/src/panels/Layers.jsx +0 -141
  236. package/plugins/beta/datasets/src/reducer.js +0 -219
  237. package/plugins/beta/datasets/src/reducers/keyReducer.js +0 -34
  238. package/plugins/beta/datasets/src/utils/filters.js +0 -34
  239. package/plugins/beta/datasets/src/utils/mergeSublayer.js +0 -86
  240. package/plugins/beta/draw-ml/dist/css/index.css +0 -21
  241. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt → im-datasets-ml-adapter.js.LICENSE.txt} +0 -0
  242. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-plugin.js.LICENSE.txt → im-datasets-plugin.js.LICENSE.txt} +0 -0
  243. /package/plugins/beta/datasets/dist/umd/{maplibre/index.js.LICENSE.txt → index.js.LICENSE.txt} +0 -0
  244. /package/plugins/beta/datasets/src/components/{EmptyKey.jsx → Key/EmptyKey.jsx} +0 -0
  245. /package/plugins/beta/datasets/src/components/{EmptyKey.test.jsx → Key/EmptyKey.test.jsx} +0 -0
  246. /package/plugins/beta/datasets/src/{panels → components/Key}/Key.module.scss +0 -0
  247. /package/plugins/beta/datasets/src/components/{svgProperties.js → Key/svgProperties.js} +0 -0
@@ -1,9 +1,6 @@
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 { getPatternConfigs, hasPattern } from './patternImages.js'
5
- import { getSymbolConfigs } from './symbolImages.js'
6
- import { mergeSublayer } from '../../utils/mergeSublayer.js'
1
+ import { addDatasetLayers } from './layerBuilders.js'
2
+ import { MapLibreDataset } from './registry/mapLibreDataset.js'
3
+ import { datasetRegistry } from '../../registry/datasetRegistry.js'
7
4
 
8
5
  /**
9
6
  * MapLibre GL JS implementation of the LayerAdapter interface for the datasets plugin.
@@ -30,42 +27,54 @@ export default class MaplibreLayerAdapter {
30
27
  this._patternRegistry = patternRegistry
31
28
  // datasetId → sourceId, used by setData to update the correct source
32
29
  this._datasetSourceMap = new Map()
30
+ window._datasetSourceMap = this._datasetSourceMap // Expose for debugging
33
31
  // Tracks all active symbol-type layer IDs so non-symbol layers can be kept below them
34
32
  this._symbolLayerIds = new Set()
35
33
  }
36
34
 
35
+ createDataset (datasetDefinition) {
36
+ return new MapLibreDataset(datasetDefinition)
37
+ }
38
+
37
39
  // ─── Lifecycle ──────────────────────────────────────────────────────────────
38
40
 
39
41
  /**
40
42
  * Initialise all datasets: register patterns, add layers, then wait for idle.
41
- * @param {Object[]} datasets
42
43
  * @param {Object} mapStyle
43
44
  * @returns {Promise<void>} Resolves once the map has processed all layers.
44
45
  */
45
- async init (datasets, mapStyle) {
46
- const mapStyleId = mapStyle.id
47
- await Promise.all([
48
- this._mapProvider.addPatternsToMap(getPatternConfigs(datasets, this._patternRegistry), mapStyleId, this._patternRegistry),
49
- this._mapProvider.addSymbolsToMap(getSymbolConfigs(datasets), mapStyle, this._symbolRegistry)
50
- ])
46
+ async init (mapStyle) {
47
+ const { patternConfigs, symbolConfigs } = datasetRegistry.getPatternAndSymbolConfigs()
48
+ await this.addPatternsAndSymbolsToMap(patternConfigs, symbolConfigs, mapStyle)
49
+
51
50
  this._symbolLayerIds.clear()
52
- datasets.forEach(dataset => this._addLayers(dataset, mapStyle))
51
+ datasetRegistry.forEachDataset(registryDataset => this._addLayers(registryDataset, mapStyle))
53
52
  await new Promise(resolve => this._map.once('idle', resolve))
54
53
  }
55
54
 
55
+ removeLayer (layerId) {
56
+ if (this._map.getLayer(layerId)) {
57
+ this._map.removeLayer(layerId)
58
+ }
59
+ this._symbolLayerIds.delete(layerId)
60
+ }
61
+
62
+ async addPatternsAndSymbolsToMap (patterns, symbols, mapStyle) {
63
+ const mapStyleId = mapStyle.id
64
+ return Promise.all([
65
+ this._mapProvider.addPatternsToMap(patterns, mapStyleId, this._patternRegistry),
66
+ this._mapProvider.addSymbolsToMap(symbols, mapStyle, this._symbolRegistry)
67
+ ])
68
+ }
69
+
56
70
  /**
57
71
  * Remove all layers and sources for the given datasets.
58
- * @param {Object[]} datasets
59
72
  */
60
- destroy (datasets) {
73
+ destroy () {
61
74
  const removedSourceIds = new Set()
62
- datasets.forEach(dataset => {
63
- const sourceId = getSourceId(dataset)
64
- this._getLayersUsingSource(sourceId).forEach(layerId => {
65
- if (this._map.getLayer(layerId)) {
66
- this._map.removeLayer(layerId)
67
- }
68
- })
75
+ datasetRegistry.forEachDataset(registryDataset => {
76
+ const sourceId = registryDataset.sourceId
77
+ this._getLayersUsingSource(sourceId).forEach(layerId => this.removeLayer(layerId))
69
78
  if (!removedSourceIds.has(sourceId) && this._map.getSource(sourceId)) {
70
79
  this._map.removeSource(sourceId)
71
80
  removedSourceIds.add(sourceId)
@@ -77,82 +86,51 @@ export default class MaplibreLayerAdapter {
77
86
  /**
78
87
  * Re-register patterns and re-add all layers after a basemap style change,
79
88
  * then reapply cached dynamic source data and hidden-feature filters.
80
- * @param {Object[]} datasets
81
89
  * @param {Object} newMapStyle
82
- * @param {Object} hiddenFeatures - pluginState.hiddenFeatures
83
90
  * @param {Map} dynamicSources - datasetId → dynamic source instance
84
91
  * @returns {Promise<void>}
85
92
  */
86
- async onStyleChange (datasets, newMapStyle, hiddenFeatures, dynamicSources) {
93
+ async onMapStyleChange (newMapStyle, dynamicSources) {
87
94
  // MapLibre wipes all sources/layers on style change — must wait for idle first
88
95
  await new Promise(resolve => this._map.once('idle', resolve))
89
96
 
90
- const newStyleId = newMapStyle.id
91
- await Promise.all([
92
- this._mapProvider.addPatternsToMap(getPatternConfigs(datasets, this._patternRegistry), newStyleId, this._patternRegistry),
93
- this._mapProvider.addSymbolsToMap(getSymbolConfigs(datasets), newMapStyle, this._symbolRegistry)
94
- ])
97
+ const { patternConfigs, symbolConfigs } = datasetRegistry.getPatternAndSymbolConfigs()
98
+ await this.addPatternsAndSymbolsToMap(patternConfigs, symbolConfigs, newMapStyle)
95
99
  this._symbolLayerIds.clear()
96
- datasets.forEach(dataset => this._addLayers(dataset, newMapStyle))
97
100
 
101
+ datasetRegistry.forEachDataset(registryDataset => {
102
+ this._addLayers(registryDataset, newMapStyle)
103
+ this._applyFeatureFilter(registryDataset)
104
+ })
105
+
106
+ // TODO: check dynamicSources still work
98
107
  // Re-push cached data for dynamic sources
99
108
  dynamicSources.forEach(source => source.reapply())
100
-
101
- // Reapply hidden feature filters
102
- Object.entries(hiddenFeatures).forEach(([datasetId, { idProperty, ids }]) => {
103
- const dataset = datasets.find(d => d.id === datasetId)
104
- if (!dataset) {
105
- return
106
- }
107
- this._applyFeatureFilter(dataset, idProperty, ids)
108
- })
109
109
  }
110
110
 
111
111
  /**
112
112
  * Re-register symbols at the new pixel ratio and update icon-image on all symbol layers.
113
113
  * Called when the map size changes so symbols are rasterised at the correct resolution.
114
- * @param {Object[]} datasets
115
114
  * @param {Object} mapStyle
116
115
  * @returns {Promise<void>}
117
116
  */
118
- async onSizeChange (datasets, mapStyle) {
119
- await Promise.all([
120
- this._mapProvider.addSymbolsToMap(getSymbolConfigs(datasets), mapStyle, this._symbolRegistry),
121
- this._mapProvider.addPatternsToMap(getPatternConfigs(datasets, this._patternRegistry), mapStyle.id, this._patternRegistry)
122
- ])
123
- datasets.forEach(dataset => {
124
- getAllLayerIds(dataset).forEach(layerId => {
125
- if (!this._symbolLayerIds.has(layerId) || !this._map.getLayer(layerId)) { return }
126
- const imageId = this._symbolRegistry.getSymbolImageId(dataset, mapStyle, false, this._pixelRatio)
117
+ async onMapSizeChange (mapStyle) {
118
+ const { patternConfigs, symbolConfigs } = datasetRegistry.getPatternAndSymbolConfigs()
119
+ await this.addPatternsAndSymbolsToMap(patternConfigs, symbolConfigs, mapStyle)
120
+
121
+ datasetRegistry.forEach(registryDataset => {
122
+ const { fillLayerId, symbolLayerId } = registryDataset
123
+ if (symbolLayerId && this._symbolLayerIds.has(symbolLayerId) && this._map.getLayer(symbolLayerId)) {
124
+ const imageId = this._symbolRegistry.getSymbolImageId(registryDataset.style, mapStyle, false, this._pixelRatio)
127
125
  if (imageId) {
128
- this._map.setLayoutProperty(layerId, 'icon-image', imageId)
126
+ this._map.setLayoutProperty(symbolLayerId, 'icon-image', imageId)
129
127
  }
130
- })
131
- if (hasPattern(dataset)) {
132
- const { fillLayerId } = getLayerIds(dataset)
133
- if (this._map.getLayer(fillLayerId)) {
134
- const imageId = this._patternRegistry.getPatternImageId(dataset, mapStyle.id, this._pixelRatio)
135
- if (imageId) {
136
- this._map.setPaintProperty(fillLayerId, 'fill-pattern', imageId)
137
- }
128
+ } else if (fillLayerId && this._map.getLayer(fillLayerId)) {
129
+ const imageId = this._patternRegistry.getPatternImageId(registryDataset.style, mapStyle.id, this._pixelRatio)
130
+ if (imageId) {
131
+ this._map.setPaintProperty(fillLayerId, 'fill-pattern', imageId)
138
132
  }
139
133
  }
140
- dataset.sublayers?.forEach(sublayer => {
141
- const merged = mergeSublayer(dataset, sublayer)
142
- const { symbolLayerId, fillLayerId } = getSublayerLayerIds(dataset.id, sublayer.id)
143
- if (this._map.getLayer(symbolLayerId)) {
144
- const imageId = this._symbolRegistry.getSymbolImageId(merged, mapStyle, false, this._pixelRatio)
145
- if (imageId) {
146
- this._map.setLayoutProperty(symbolLayerId, 'icon-image', imageId)
147
- }
148
- }
149
- if (hasPattern(merged) && this._map.getLayer(fillLayerId)) {
150
- const imageId = this._patternRegistry.getPatternImageId(merged, mapStyle.id, this._pixelRatio)
151
- if (imageId) {
152
- this._map.setPaintProperty(fillLayerId, 'fill-pattern', imageId)
153
- }
154
- }
155
- })
156
134
  })
157
135
  }
158
136
 
@@ -160,184 +138,97 @@ export default class MaplibreLayerAdapter {
160
138
 
161
139
  /**
162
140
  * Add a single dataset's source and layers to the map.
163
- * @param {Object} dataset
141
+ * @param {string} datasetId
164
142
  * @param {Object} mapStyle
165
143
  */
166
- addDataset (dataset, mapStyle) {
167
- this._addLayers(dataset, mapStyle)
144
+ async addDataset (datasetId, mapStyle) {
145
+ const registryDataset = datasetRegistry.getDataset(datasetId)
146
+ await this.addPatternsAndSymbolsToMap(registryDataset.patternConfigs, registryDataset.symbolConfigs, mapStyle)
147
+ this._addLayers(registryDataset, mapStyle)
168
148
  }
169
149
 
170
150
  /**
171
151
  * Remove a dataset's layers and source from the map.
172
152
  * Shared sources (same tiles URL or geojson URL used by multiple datasets) are
173
153
  * only removed when no other remaining dataset references them.
174
- * @param {Object} dataset
175
- * @param {Object[]} allDatasets - Full current dataset list, for shared-source check.
154
+ * @param {string} datasetId
176
155
  */
177
- removeDataset (dataset, allDatasets) {
178
- const sourceId = getSourceId(dataset)
179
-
180
- getAllLayerIds(dataset).forEach(layerId => {
181
- if (this._map.getLayer(layerId)) {
182
- this._map.removeLayer(layerId)
183
- }
184
- this._symbolLayerIds.delete(layerId)
185
- })
156
+ removeDataset (datasetId) {
157
+ const registryDataset = datasetRegistry.getDataset(datasetId)
158
+ if (!registryDataset) {
159
+ return
160
+ }
161
+ const { sourceId, layerIds } = registryDataset
162
+ // Remove all layers for this dataset (including sublayers, which share the same source)
163
+ layerIds.forEach(layerId => this.removeLayer(layerId))
186
164
 
187
- const sourceIsShared = allDatasets.some(d => d.id !== dataset.id && getSourceId(d) === sourceId)
165
+ // Remove source if no other dataset is using it
166
+ const sourceIsShared = datasetRegistry.topLevelDatasets()
167
+ .filter(registryDataset => registryDataset.id !== datasetId && registryDataset.sourceId === sourceId)
168
+ .length > 0
188
169
 
189
170
  if (!sourceIsShared && this._map.getSource(sourceId)) {
190
171
  this._map.removeSource(sourceId)
191
172
  }
192
173
 
193
- this._datasetSourceMap.delete(dataset.id)
194
- }
195
-
196
- /**
197
- * Make a dataset's layers visible.
198
- * @param {string} datasetId
199
- */
200
- showDataset (datasetId) {
201
- this._setDatasetVisibility(datasetId, 'visible')
202
- }
203
-
204
- /**
205
- * Hide a dataset's layers.
206
- * @param {string} datasetId
207
- */
208
- hideDataset (datasetId) {
209
- this._setDatasetVisibility(datasetId, 'none')
210
- }
211
-
212
- /**
213
- * Make a single sublayer's layers visible.
214
- * @param {string} datasetId
215
- * @param {string} sublayerId
216
- */
217
- showSublayer (datasetId, sublayerId) {
218
- const { fillLayerId, strokeLayerId, symbolLayerId } = getSublayerLayerIds(datasetId, sublayerId)
219
- ;[fillLayerId, strokeLayerId, symbolLayerId].forEach(layerId => {
220
- if (this._map.getLayer(layerId)) {
221
- this._map.setLayoutProperty(layerId, 'visibility', 'visible')
222
- }
223
- })
224
- }
225
-
226
- /**
227
- * Hide a single sublayer's layers.
228
- * @param {string} datasetId
229
- * @param {string} sublayerId
230
- */
231
- hideSublayer (datasetId, sublayerId) {
232
- const { fillLayerId, strokeLayerId, symbolLayerId } = getSublayerLayerIds(datasetId, sublayerId)
233
- ;[fillLayerId, strokeLayerId, symbolLayerId].forEach(layerId => {
234
- if (this._map.getLayer(layerId)) {
235
- this._map.setLayoutProperty(layerId, 'visibility', 'none')
236
- }
237
- })
174
+ this._datasetSourceMap.delete(datasetId)
238
175
  }
239
176
 
240
177
  // ─── Feature operations ─────────────────────────────────────────────────────
241
178
 
242
179
  /**
243
- * Show previously hidden features by updating the layer exclusion filter.
244
- * @param {Object} dataset
245
- * @param {string} idProperty
246
- * @param {Array} remainingHiddenIds - IDs that should remain hidden after this call.
180
+ * Show/Hide features by updating the layer exclusion filter.
181
+ * @param {string} datasetId
247
182
  */
248
- showFeatures (dataset, idProperty, remainingHiddenIds) {
249
- this._applyFeatureFilter(dataset, idProperty, remainingHiddenIds)
250
- }
183
+ applyFeatureFilter (datasetId) {
184
+ const registryDataset = datasetRegistry.getDataset(datasetId)
185
+ if (!registryDataset) {
186
+ return
187
+ }
251
188
 
252
- /**
253
- * Hide features by updating the layer exclusion filter.
254
- * @param {Object} dataset
255
- * @param {string} idProperty
256
- * @param {Array} allHiddenIds - Full set of IDs to hide (existing + new).
257
- */
258
- hideFeatures (dataset, idProperty, allHiddenIds) {
259
- this._applyFeatureFilter(dataset, idProperty, allHiddenIds)
189
+ this._applyFeatureFilter(registryDataset)
260
190
  }
261
191
 
262
192
  /**
263
193
  * Update a dataset's style and re-render all its layers.
264
- * @param {Object} dataset - Updated dataset (style changes already merged in)
194
+ * @param {string} datasetId - Updated dataset (style changes already merged in)
265
195
  * @param {Object} mapStyle
266
196
  * @returns {Promise<void>}
267
197
  */
268
- async setStyle (dataset, mapStyle) {
269
- const mapStyleId = mapStyle.id
270
- getAllLayerIds(dataset).forEach(layerId => {
271
- if (this._map.getLayer(layerId)) {
272
- this._map.removeLayer(layerId)
273
- }
274
- this._symbolLayerIds.delete(layerId)
275
- })
276
- await Promise.all([
277
- this._mapProvider.addPatternsToMap(getPatternConfigs([dataset], this._patternRegistry), mapStyleId, this._patternRegistry),
278
- this._mapProvider.addSymbolsToMap(getSymbolConfigs([dataset]), mapStyle, this._symbolRegistry)
279
- ])
280
- this._addLayers(dataset, mapStyle)
198
+ async applyStyle (datasetId, mapStyle) {
199
+ const registryDataset = datasetRegistry.getDataset(datasetId)
200
+ registryDataset.layerIds.forEach(layerId => this.removeLayer(layerId))
201
+ await this.addPatternsAndSymbolsToMap(registryDataset.patternConfigs, registryDataset.symbolConfigs, mapStyle)
202
+ this._addLayers(registryDataset, mapStyle)
281
203
  }
282
204
 
283
205
  /**
284
- * Update a single sublayer's style and re-render its layers.
285
- * @param {Object} dataset - Updated dataset (sublayer style changes already merged in)
286
- * @param {string} sublayerId
287
- * @param {Object} mapStyle
288
- * @returns {Promise<void>}
206
+ * Apply opacity for all layers with datasetId
207
+ * @param {string} datasetId
289
208
  */
290
- async setSublayerStyle (dataset, sublayerId, mapStyle) {
291
- const mapStyleId = mapStyle.id
292
- const pixelRatio = this._pixelRatio
293
- const { fillLayerId, strokeLayerId, symbolLayerId } = getSublayerLayerIds(dataset.id, sublayerId)
294
- ;[fillLayerId, strokeLayerId, symbolLayerId].forEach(layerId => {
295
- if (this._map.getLayer(layerId)) {
296
- this._map.removeLayer(layerId)
297
- }
298
- this._symbolLayerIds.delete(layerId)
299
- })
300
- const sublayer = dataset.sublayers?.find(s => s.id === sublayerId)
301
- if (!sublayer) {
302
- return
209
+ applyDatasetOpacity (datasetId) {
210
+ const registryDataset = datasetRegistry.getDataset(datasetId)
211
+ if (registryDataset) {
212
+ this._applyRegistryDatasetOpacity(registryDataset)
303
213
  }
304
- await Promise.all([
305
- this._mapProvider.addPatternsToMap(getPatternConfigs([dataset], this._patternRegistry), mapStyleId, this._patternRegistry),
306
- this._mapProvider.addSymbolsToMap(getSymbolConfigs([dataset]), mapStyle, this._symbolRegistry)
307
- ])
308
- const sourceId = this._datasetSourceMap.get(dataset.id)
309
- const sourceLayer = dataset.tiles?.length ? dataset.sourceLayer : undefined
310
- addSublayerLayers(this._map, dataset, sublayer, sourceId, sourceLayer, { mapStyle, symbolRegistry: this._symbolRegistry, patternRegistry: this._patternRegistry, pixelRatio })
311
- this._maintainSymbolOrdering(dataset)
312
214
  }
313
215
 
314
216
  /**
315
- * Set opacity for all layers belonging to a dataset.
217
+ * Apply opacity for all layers belonging to a registryDataset.
316
218
  * Uses setPaintProperty directly — safe to call on every slider tick.
317
- * @param {string} datasetId
318
- * @param {number} opacity
219
+ * @param {Object} registryDataset
319
220
  */
320
- setOpacity (datasetId, opacity) {
321
- const style = this._map.getStyle()
322
- if (!style?.layers) {
323
- return
324
- }
325
- style.layers
326
- .filter(layer => layer.id === datasetId || layer.id.startsWith(`${datasetId}-`))
327
- .forEach(layer => this._setPaintOpacity(layer.id, opacity))
221
+ _applyRegistryDatasetOpacity (registryDataset) {
222
+ registryDataset.getLayersWithOpacity().forEach(({ layerIds, opacity }) => {
223
+ layerIds.forEach(layerId => this._setPaintOpacity(layerId, opacity))
224
+ })
328
225
  }
329
226
 
330
227
  /**
331
- * Set opacity for a single sublayer's fill and stroke layers.
332
- * Uses setPaintProperty directly — safe to call on every slider tick.
333
- * @param {string} datasetId
334
- * @param {string} sublayerId
335
- * @param {number} opacity
228
+ * Apply opacity for all layers
336
229
  */
337
- setSublayerOpacity (datasetId, sublayerId, opacity) {
338
- const { fillLayerId, strokeLayerId } = getSublayerLayerIds(datasetId, sublayerId)
339
- this._setPaintOpacity(fillLayerId, opacity)
340
- this._setPaintOpacity(strokeLayerId, opacity)
230
+ applyGlobalOpacity () {
231
+ datasetRegistry.forEachDataset(registryDataset => this._applyRegistryDatasetOpacity(registryDataset))
341
232
  }
342
233
 
343
234
  /**
@@ -362,10 +253,10 @@ export default class MaplibreLayerAdapter {
362
253
  return this._mapProvider.map.getPixelRatio()
363
254
  }
364
255
 
365
- _addLayers (dataset, mapStyle) {
366
- const sourceId = addDatasetLayers(this._map, dataset, mapStyle, this._symbolRegistry, this._patternRegistry, this._pixelRatio)
367
- this._datasetSourceMap.set(dataset.id, sourceId)
368
- this._maintainSymbolOrdering(dataset)
256
+ _addLayers (registryDataset, mapStyle) {
257
+ const sourceId = addDatasetLayers(this._map, registryDataset, mapStyle, this._symbolRegistry, this._patternRegistry, this._pixelRatio)
258
+ this._datasetSourceMap.set(registryDataset.id, sourceId)
259
+ this._maintainSymbolOrdering(registryDataset)
369
260
  }
370
261
 
371
262
  _getFirstSymbolLayerId () {
@@ -377,8 +268,9 @@ export default class MaplibreLayerAdapter {
377
268
  return layer?.id ?? null
378
269
  }
379
270
 
380
- _maintainSymbolOrdering (dataset) {
381
- const layerIds = getAllLayerIds(dataset).filter(id => id && this._map.getLayer(id))
271
+ _maintainSymbolOrdering (registryDataset) {
272
+ registryDataset = registryDataset.isSublayer ? registryDataset.parent : registryDataset
273
+ const layerIds = registryDataset.layerIds.filter(id => id && this._map.getLayer(id))
382
274
  layerIds.forEach(id => {
383
275
  if (this._map.getLayer(id)?.type === 'symbol') {
384
276
  this._symbolLayerIds.add(id)
@@ -397,38 +289,46 @@ export default class MaplibreLayerAdapter {
397
289
  })
398
290
  }
399
291
 
400
- _setDatasetVisibility (datasetId, visibility) {
401
- const style = this._map.getStyle()
402
- if (!style?.layers) {
403
- return
292
+ /**
293
+ * Apply visibility for all layers with datasetId
294
+ * @param {string} datasetId
295
+ */
296
+ applyDatasetVisibility (datasetId) {
297
+ const registryDataset = datasetRegistry.getDataset(datasetId)
298
+ if (registryDataset) {
299
+ this._applyRegistryDatasetVisibility(registryDataset)
404
300
  }
405
- // Covers base fill layer (datasetId) and all suffixed layers
406
- // (-stroke, -${sublayerId}, -${sublayerId}-stroke) without needing the dataset object.
407
- style.layers
408
- .filter(layer =>
409
- layer.id === datasetId ||
410
- layer.id.startsWith(`${datasetId}-`)
411
- )
412
- .forEach(layer => this._map.setLayoutProperty(layer.id, 'visibility', visibility))
413
301
  }
414
302
 
415
- _applyFeatureFilter (dataset, idProperty, excludeIds) {
416
- if (dataset.sublayers?.length) {
417
- dataset.sublayers.forEach(sublayer => {
418
- const { fillLayerId: subFillId, strokeLayerId: subStrokeId } = getSublayerLayerIds(dataset.id, sublayer.id)
419
- const sublayerFilter = dataset.filter && sublayer.filter
420
- ? ['all', dataset.filter, sublayer.filter]
421
- : (sublayer.filter || dataset.filter || null)
422
- applyExclusionFilter(this._map, subFillId, sublayerFilter, idProperty, excludeIds)
423
- applyExclusionFilter(this._map, subStrokeId, sublayerFilter, idProperty, excludeIds)
303
+ /**
304
+ * Apply visibility for all layers belonging to a registryDataset.
305
+ * @param {Object} registryDataset
306
+ */
307
+ _applyRegistryDatasetVisibility (registryDataset) {
308
+ registryDataset.getLayersWithVisibility().forEach(({ layerIds, visibility }) => {
309
+ layerIds.forEach(layerId => {
310
+ if (this._map.getLayer(layerId)) {
311
+ this._map.setLayoutProperty(layerId, 'visibility', visibility)
312
+ }
313
+ })
314
+ })
315
+ }
316
+
317
+ /**
318
+ * Apply visibility for all layers
319
+ */
320
+ applyGlobalVisibility () {
321
+ datasetRegistry.forEachDataset(registryDataset => this._applyRegistryDatasetVisibility(registryDataset))
322
+ }
323
+
324
+ _applyFeatureFilter (registryDataset) {
325
+ const layersWithFilters = registryDataset.getLayersWithFilters()
326
+ layersWithFilters.forEach(({ layerIds, filter }) => {
327
+ layerIds.forEach(layerId => {
328
+ if (this._map.getLayer(layerId)) {
329
+ this._map.setFilter(layerId, filter)
330
+ }
424
331
  })
425
- return
426
- }
427
- const { fillLayerId, strokeLayerId, symbolLayerId } = getLayerIds(dataset)
428
- const originalFilter = dataset.filter || null
429
- const layerIds = [fillLayerId, strokeLayerId, symbolLayerId].filter(Boolean)
430
- layerIds.forEach(layerId => {
431
- applyExclusionFilter(this._map, layerId, originalFilter, idProperty, excludeIds)
432
332
  })
433
333
  }
434
334