@defra/interactive-map 0.0.16-alpha → 0.0.18-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 (224) hide show
  1. package/assets/images/slot-map.svg +264 -0
  2. package/dist/css/index.css +1 -1
  3. package/dist/esm/im-core.js +1 -1
  4. package/dist/esm/im-shell.js +1 -1
  5. package/dist/umd/im-core.js +1 -1
  6. package/dist/umd/index.js +1 -1
  7. package/docs/api/context.md +53 -7
  8. package/docs/api/map-style-config.md +41 -2
  9. package/docs/api/marker-config.md +53 -11
  10. package/docs/api/slots.md +16 -15
  11. package/docs/api/symbol-config.md +160 -0
  12. package/docs/api/symbol-registry.md +115 -0
  13. package/docs/api.md +25 -22
  14. package/docs/getting-started.md +4 -1
  15. package/docs/plugins/datasets.md +657 -0
  16. package/docs/plugins/interact.md +68 -43
  17. package/docs/plugins/search.md +15 -3
  18. package/docs/plugins.md +1 -1
  19. package/package.json +2 -2
  20. package/plugins/beta/datasets/dist/css/index.css +103 -15
  21. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  22. package/plugins/beta/datasets/dist/esm/index.js +1 -1
  23. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
  24. package/plugins/beta/datasets/dist/umd/index.js +1 -1
  25. package/plugins/beta/datasets/src/DatasetsInit.jsx +29 -9
  26. package/plugins/beta/datasets/src/adapters/maplibre/index.js +18 -0
  27. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +159 -0
  28. package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +75 -0
  29. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +440 -0
  30. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +27 -0
  31. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +31 -0
  32. package/plugins/beta/datasets/src/api/addDataset.js +2 -8
  33. package/plugins/beta/datasets/src/api/getOpacity.js +17 -0
  34. package/plugins/beta/datasets/src/api/getStyle.js +13 -0
  35. package/plugins/beta/datasets/src/api/removeDataset.js +2 -44
  36. package/plugins/beta/datasets/src/api/setData.js +10 -0
  37. package/plugins/beta/datasets/src/api/setDatasetVisibility.js +37 -0
  38. package/plugins/beta/datasets/src/api/setFeatureVisibility.js +22 -0
  39. package/plugins/beta/datasets/src/api/setOpacity.js +29 -0
  40. package/plugins/beta/datasets/src/api/setStyle.js +22 -0
  41. package/plugins/beta/datasets/src/components/EmptyKey.jsx +7 -0
  42. package/plugins/beta/datasets/src/components/EmptyKey.test.jsx +21 -0
  43. package/plugins/beta/datasets/src/components/KeySvg.jsx +24 -0
  44. package/plugins/beta/datasets/src/components/KeySvgLine.jsx +19 -0
  45. package/plugins/beta/datasets/src/components/KeySvgPattern.jsx +15 -0
  46. package/plugins/beta/datasets/src/components/KeySvgRect.jsx +22 -0
  47. package/plugins/beta/datasets/src/components/KeySvgSymbol.jsx +16 -0
  48. package/plugins/beta/datasets/src/components/svgProperties.js +20 -0
  49. package/plugins/beta/datasets/src/datasets.js +39 -56
  50. package/plugins/beta/datasets/src/defaults.js +44 -8
  51. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +34 -25
  52. package/plugins/beta/datasets/src/fetch/fetchGeoJSON.js +2 -2
  53. package/plugins/beta/datasets/src/index.js +2 -1
  54. package/plugins/beta/datasets/src/manifest.js +25 -17
  55. package/plugins/beta/datasets/src/panels/Key.jsx +51 -51
  56. package/plugins/beta/datasets/src/panels/Key.module.scss +59 -9
  57. package/plugins/beta/datasets/src/panels/Layers.jsx +132 -29
  58. package/plugins/beta/datasets/src/panels/Layers.module.scss +56 -8
  59. package/plugins/beta/datasets/src/reducer.js +134 -9
  60. package/plugins/beta/datasets/src/reducers/keyReducer.js +34 -0
  61. package/plugins/beta/datasets/src/utils/bbox.js +7 -5
  62. package/plugins/beta/datasets/src/utils/filters.js +5 -2
  63. package/plugins/beta/datasets/src/utils/mergeSublayer.js +86 -0
  64. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  65. package/plugins/beta/draw-es/src/DrawInit.jsx +3 -2
  66. package/plugins/beta/draw-ml/dist/css/index.css +21 -1
  67. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  68. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  69. package/plugins/beta/draw-ml/dist/umd/index.js +1 -1
  70. package/plugins/beta/draw-ml/src/DrawInit.jsx +4 -3
  71. package/plugins/beta/draw-ml/src/draw.scss +0 -7
  72. package/plugins/beta/draw-ml/src/manifest.js +16 -16
  73. package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -1
  74. package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -1
  75. package/plugins/beta/frame/src/Frame.jsx +5 -5
  76. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
  77. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
  78. package/plugins/beta/map-styles/dist/umd/index.js +1 -1
  79. package/plugins/beta/map-styles/src/MapStyles.jsx +5 -4
  80. package/plugins/beta/map-styles/src/MapStylesInit.jsx +5 -4
  81. package/plugins/beta/map-styles/src/manifest.js +1 -1
  82. package/plugins/beta/scale-bar/dist/css/index.css +1 -1
  83. package/plugins/beta/scale-bar/dist/esm/im-scale-bar-plugin.js +1 -1
  84. package/plugins/beta/scale-bar/dist/umd/im-scale-bar-plugin.js +1 -1
  85. package/plugins/beta/scale-bar/src/index.test.js +3 -3
  86. package/plugins/beta/scale-bar/src/manifest.js +3 -3
  87. package/plugins/beta/scale-bar/src/scaleBar.scss +2 -1
  88. package/plugins/interact/dist/css/index.css +1 -1
  89. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  90. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  91. package/plugins/interact/dist/umd/index.js +1 -1
  92. package/plugins/interact/src/InteractInit.jsx +14 -5
  93. package/plugins/interact/src/InteractInit.test.js +26 -6
  94. package/plugins/interact/src/api/enable.test.js +7 -7
  95. package/plugins/interact/src/defaults.js +4 -6
  96. package/plugins/interact/src/events.js +9 -6
  97. package/plugins/interact/src/events.test.js +28 -4
  98. package/plugins/interact/src/hooks/useHighlightSync.js +3 -3
  99. package/plugins/interact/src/hooks/useHighlightSync.test.js +6 -6
  100. package/plugins/interact/src/hooks/useHoverCursor.js +10 -0
  101. package/plugins/interact/src/hooks/useHoverCursor.test.js +44 -0
  102. package/plugins/interact/src/hooks/useInteractionHandlers.js +111 -69
  103. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +147 -32
  104. package/plugins/interact/src/interact.scss +0 -7
  105. package/plugins/interact/src/manifest.js +14 -18
  106. package/plugins/interact/src/manifest.test.js +3 -1
  107. package/plugins/interact/src/reducer.js +23 -4
  108. package/plugins/interact/src/reducer.test.js +60 -11
  109. package/plugins/interact/src/utils/buildStylesMap.js +17 -4
  110. package/plugins/interact/src/utils/buildStylesMap.test.js +16 -2
  111. package/plugins/interact/src/utils/featureQueries.js +11 -6
  112. package/plugins/interact/src/utils/featureQueries.test.js +8 -1
  113. package/plugins/search/dist/css/index.css +1 -1
  114. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  115. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  116. package/plugins/search/src/Search.jsx +3 -1
  117. package/plugins/search/src/components/Form/Form.module.scss +2 -1
  118. package/plugins/search/src/events/fetchSuggestions.js +6 -4
  119. package/plugins/search/src/events/fetchSuggestions.test.js +26 -4
  120. package/plugins/search/src/events/formHandlers.js +3 -3
  121. package/plugins/search/src/events/formHandlers.test.js +1 -1
  122. package/plugins/search/src/events/suggestionHandlers.js +2 -2
  123. package/plugins/search/src/events/suggestionHandlers.test.js +1 -1
  124. package/plugins/search/src/utils/updateMap.js +3 -3
  125. package/plugins/search/src/utils/updateMap.test.js +3 -3
  126. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  127. package/providers/maplibre/dist/umd/im-maplibre-framework.js +1 -1
  128. package/providers/maplibre/dist/umd/im-maplibre-framework.js.LICENSE.txt +1 -1
  129. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  130. package/providers/maplibre/dist/umd/index.js +1 -1
  131. package/providers/maplibre/src/appEvents.js +7 -0
  132. package/providers/maplibre/src/appEvents.test.js +18 -4
  133. package/providers/maplibre/src/maplibreProvider.js +52 -0
  134. package/providers/maplibre/src/maplibreProvider.test.js +105 -1
  135. package/providers/maplibre/src/utils/highlightFeatures.js +37 -7
  136. package/providers/maplibre/src/utils/highlightFeatures.test.js +153 -95
  137. package/providers/maplibre/src/utils/hoverCursor.js +61 -0
  138. package/providers/maplibre/src/utils/hoverCursor.test.js +130 -0
  139. package/providers/maplibre/src/utils/patternImages.js +70 -0
  140. package/providers/maplibre/src/utils/patternImages.test.js +180 -0
  141. package/providers/maplibre/src/utils/queryFeatures.js +38 -16
  142. package/providers/maplibre/src/utils/queryFeatures.test.js +20 -3
  143. package/providers/maplibre/src/utils/rasteriseToImageData.js +30 -0
  144. package/providers/maplibre/src/utils/rasteriseToImageData.test.js +69 -0
  145. package/providers/maplibre/src/utils/symbolImages.js +147 -0
  146. package/providers/maplibre/src/utils/symbolImages.test.js +248 -0
  147. package/src/App/components/Actions/Actions.jsx +2 -2
  148. package/src/App/components/Actions/Actions.module.scss +0 -7
  149. package/src/App/components/Actions/Actions.test.jsx +1 -1
  150. package/src/App/components/Icon/Icon.jsx +3 -2
  151. package/src/App/components/Icon/Icon.module.scss +4 -0
  152. package/src/App/components/Icon/Icon.test.jsx +43 -4
  153. package/src/App/components/MapButton/MapButton.jsx +42 -17
  154. package/src/App/components/MapButton/MapButton.module.scss +4 -13
  155. package/src/App/components/MapButton/MapButton.test.jsx +27 -3
  156. package/src/App/components/Markers/Markers.jsx +122 -27
  157. package/src/App/components/Markers/Markers.module.scss +0 -10
  158. package/src/App/components/Markers/Markers.test.jsx +246 -0
  159. package/src/App/components/PopupMenu/PopupMenu.jsx +51 -274
  160. package/src/App/components/PopupMenu/PopupMenu.module.scss +14 -7
  161. package/src/App/components/PopupMenu/PopupMenu.test.jsx +70 -1
  162. package/src/App/components/PopupMenu/usePopupMenu.js +258 -0
  163. package/src/App/hooks/useButtonStateEvaluator.js +12 -2
  164. package/src/App/hooks/useButtonStateEvaluator.test.js +38 -4
  165. package/src/App/hooks/useInterfaceAPI.js +6 -0
  166. package/src/App/hooks/useInterfaceAPI.test.js +156 -0
  167. package/src/App/hooks/useLayoutMeasurements.js +84 -18
  168. package/src/App/hooks/useLayoutMeasurements.test.js +124 -17
  169. package/src/App/hooks/useMarkersAPI.js +2 -5
  170. package/src/App/hooks/useMarkersAPI.test.js +4 -4
  171. package/src/App/layout/Layout.jsx +14 -9
  172. package/src/App/layout/Layout.test.jsx +6 -4
  173. package/src/App/layout/layout.module.scss +67 -29
  174. package/src/App/registry/pluginRegistry.js +1 -1
  175. package/src/App/renderer/HtmlElementHost.jsx +2 -1
  176. package/src/App/renderer/HtmlElementHost.test.jsx +7 -7
  177. package/src/App/renderer/mapButtons.js +1 -1
  178. package/src/App/renderer/mapPanels.test.js +2 -2
  179. package/src/App/renderer/slotHelpers.js +2 -2
  180. package/src/App/renderer/slotHelpers.test.js +5 -5
  181. package/src/App/renderer/slots.js +9 -5
  182. package/src/App/store/AppProvider.jsx +3 -1
  183. package/src/App/store/AppProvider.test.jsx +1 -1
  184. package/src/App/store/ServiceProvider.jsx +8 -4
  185. package/src/App/store/appActionsMap.js +16 -0
  186. package/src/App/store/appActionsMap.test.js +27 -0
  187. package/src/App/store/appDispatchMiddleware.js +1 -1
  188. package/src/App/store/appDispatchMiddleware.test.js +2 -2
  189. package/src/App/store/appReducer.js +2 -0
  190. package/src/App/store/mapActionsMap.js +4 -6
  191. package/src/App/store/mapActionsMap.test.js +3 -2
  192. package/src/App/store/mapReducer.js +2 -1
  193. package/src/InteractiveMap/InteractiveMap.js +4 -0
  194. package/src/config/appConfig.js +5 -8
  195. package/src/config/appConfig.test.js +1 -2
  196. package/src/config/defaults.js +0 -2
  197. package/src/config/events.js +28 -0
  198. package/src/config/mapTheme.js +56 -0
  199. package/src/config/patternConfig.js +16 -0
  200. package/src/config/symbolConfig.js +80 -0
  201. package/src/scss/main.scss +1 -0
  202. package/src/scss/settings/_colors.scss +0 -9
  203. package/src/scss/settings/_dimensions.scss +0 -1
  204. package/src/services/patternRegistry.js +40 -0
  205. package/src/services/patternRegistry.test.js +48 -0
  206. package/src/services/symbolRegistry.js +113 -0
  207. package/src/services/symbolRegistry.test.js +262 -0
  208. package/src/types.js +93 -11
  209. package/src/utils/getSafeZoneInset.js +9 -7
  210. package/src/utils/getSafeZoneInset.test.js +10 -10
  211. package/src/utils/patternUtils.js +94 -0
  212. package/src/utils/patternUtils.test.js +160 -0
  213. package/src/utils/symbolUtils.js +85 -0
  214. package/src/utils/symbolUtils.test.js +156 -0
  215. package/webpack.dev.mjs +1 -1
  216. package/docs/api/slot-map.svg +0 -1
  217. package/plugins/beta/datasets/src/api/hideDataset.js +0 -14
  218. package/plugins/beta/datasets/src/api/hideFeatures.js +0 -41
  219. package/plugins/beta/datasets/src/api/showDataset.js +0 -14
  220. package/plugins/beta/datasets/src/api/showFeatures.js +0 -44
  221. package/plugins/beta/datasets/src/handleSetMapStyle.js +0 -54
  222. package/plugins/beta/datasets/src/mapLayers.js +0 -164
  223. /package/src/{utils → services}/logger.js +0 -0
  224. /package/src/{utils → services}/logger.test.js +0 -0
@@ -0,0 +1,657 @@
1
+ # Datasets Plugin
2
+
3
+ The datasets plugin renders GeoJSON and vector tile datasets on the map, with support for polygon, line, and symbol (point) layer types, sublayer style rules, layer visibility toggling, a key panel, and runtime style and data updates.
4
+
5
+ ## Usage
6
+
7
+ ```js
8
+ import createDatasetsPlugin from '@defra/interactive-map/plugins/beta/datasets'
9
+ import { maplibreLayerAdapter } from '@defra/interactive-map/plugins/beta/datasets/adapters/maplibre'
10
+
11
+ const datasetsPlugin = createDatasetsPlugin({
12
+ layerAdapter: maplibreLayerAdapter,
13
+ datasets: [
14
+ {
15
+ id: 'my-parcels',
16
+ label: 'My parcels',
17
+ geojson: 'https://example.com/api/parcels',
18
+ minZoom: 10,
19
+ maxZoom: 24,
20
+ showInKey: true,
21
+ toggleVisibility: true,
22
+ style: {
23
+ stroke: '#d4351c',
24
+ strokeWidth: 2,
25
+ fill: 'transparent'
26
+ }
27
+ }
28
+ ]
29
+ })
30
+
31
+ const interactiveMap = new InteractiveMap({
32
+ plugins: [datasetsPlugin]
33
+ })
34
+ ```
35
+
36
+ ## Options
37
+
38
+ Options are passed to the factory function when creating the plugin.
39
+
40
+ ---
41
+
42
+ ### `layerAdapter`
43
+
44
+ **Type:** `LayerAdapter`
45
+ **Required**
46
+
47
+ The map provider adapter responsible for rendering datasets. Import `maplibreLayerAdapter` for MapLibre GL JS, or supply a custom adapter.
48
+
49
+ ```js
50
+ import { maplibreLayerAdapter } from '@defra/interactive-map/plugins/beta/datasets/adapters/maplibre'
51
+ ```
52
+
53
+ ---
54
+
55
+ ### `datasets`
56
+
57
+ **Type:** `Dataset[]`
58
+ **Required**
59
+
60
+ Array of dataset configurations to render on the map. See [Dataset configuration](#dataset-configuration) below.
61
+
62
+ ---
63
+
64
+ ### `includeModes`
65
+
66
+ **Type:** `string[]`
67
+
68
+ When set, the plugin only initialises when the app is in one of the specified modes.
69
+
70
+ ---
71
+
72
+ ### `excludeModes`
73
+
74
+ **Type:** `string[]`
75
+
76
+ When set, the plugin does not initialise when the app is in one of the specified modes.
77
+
78
+ ---
79
+
80
+ ## Dataset configuration
81
+
82
+ Each entry in the `datasets` array describes one data source and how it should be rendered.
83
+
84
+ ---
85
+
86
+ ### `id`
87
+
88
+ **Type:** `string`
89
+ **Required**
90
+
91
+ Unique identifier for the dataset. Used in all API method calls.
92
+
93
+ ---
94
+
95
+ ### `label`
96
+
97
+ **Type:** `string`
98
+
99
+ Human-readable name shown in the Layers panel and Key panel.
100
+
101
+ ---
102
+
103
+ ### `geojson`
104
+
105
+ **Type:** `string | GeoJSON.FeatureCollection`
106
+
107
+ GeoJSON source. Provide a URL string for remote data, or a GeoJSON object for inline data. Use alongside `transformRequest` to add authentication or append bbox parameters to the request.
108
+
109
+ ---
110
+
111
+ ### `tiles`
112
+
113
+ **Type:** `string[]`
114
+
115
+ Array of vector tile URL templates (e.g. `https://example.com/tiles/{z}/{x}/{y}`). When set, the dataset uses a vector tile source instead of GeoJSON.
116
+
117
+ ---
118
+
119
+ ### `sourceLayer`
120
+
121
+ **Type:** `string`
122
+
123
+ The layer name within the vector tile source to render. Required when using `tiles`.
124
+
125
+ ---
126
+
127
+ ### `transformRequest`
128
+
129
+ **Type:** `Function`
130
+
131
+ A function called before each fetch to transform the request. Its primary purpose is to attach authentication credentials — API keys, OAuth tokens, or other headers. It also receives the current viewport context so you can append bbox or zoom parameters to the URL if your API supports spatial filtering.
132
+
133
+ The plugin handles all dynamic fetching concerns (viewport tracking, debouncing, deduplication, caching, request cancellation) — `transformRequest` only needs to return the final URL and any headers.
134
+
135
+ **Signature:** `transformRequest(url, { bbox, zoom, dataset })`
136
+
137
+ | Argument | Type | Description |
138
+ |----------|------|-------------|
139
+ | `url` | `string` | The base URL from `geojson` |
140
+ | `bbox` | `number[]` | Current viewport bounds as `[west, south, east, north]` |
141
+ | `zoom` | `number` | Current map zoom level |
142
+ | `dataset` | `Object` | The full dataset configuration |
143
+
144
+ Return either a plain URL string or an object `{ url, headers }`. The object form is needed when attaching auth headers.
145
+
146
+ ```js
147
+ // Auth headers only (no bbox filtering)
148
+ transformRequest: (url) => ({
149
+ url,
150
+ headers: { Authorization: `Bearer ${getToken()}` }
151
+ })
152
+
153
+ // Append bbox to URL for server-side spatial filtering
154
+ transformRequest: (url, { bbox }) => {
155
+ const separator = url.includes('?') ? '&' : '?'
156
+ return { url: `${url}${separator}bbox=${bbox.join(',')}` }
157
+ }
158
+
159
+ // Both — auth + bbox
160
+ transformRequest: (url, { bbox }) => {
161
+ const separator = url.includes('?') ? '&' : '?'
162
+ return {
163
+ url: `${url}${separator}bbox=${bbox.join(',')}`,
164
+ headers: { Authorization: `Bearer ${getToken()}` }
165
+ }
166
+ }
167
+ ```
168
+
169
+ ---
170
+
171
+ ### `idProperty`
172
+
173
+ **Type:** `string`
174
+
175
+ Property name used to uniquely identify features. Required alongside `transformRequest` to enable dynamic bbox-based fetching — the plugin uses it internally to deduplicate features across successive viewport fetches.
176
+
177
+ ---
178
+
179
+ ### `filter`
180
+
181
+ **Type:** `FilterExpression`
182
+
183
+ A MapLibre filter expression applied to the dataset's map layers. Features not matching the filter are not rendered.
184
+
185
+ ```js
186
+ filter: ['==', ['get', 'status'], 'active']
187
+ ```
188
+
189
+ ---
190
+
191
+ ### `minZoom`
192
+
193
+ **Type:** `number`
194
+ **Default:** `6`
195
+
196
+ Minimum zoom level at which the dataset is visible.
197
+
198
+ ---
199
+
200
+ ### `maxZoom`
201
+
202
+ **Type:** `number`
203
+ **Default:** `24`
204
+
205
+ Maximum zoom level at which the dataset is visible.
206
+
207
+ ---
208
+
209
+ ### `maxFeatures`
210
+
211
+ **Type:** `number`
212
+
213
+ Only applies to dynamic sources (those using `transformRequest`). Caps the number of features held in memory across all viewport fetches — older out-of-viewport features are evicted when the limit is exceeded. Omit for small or bounded datasets; set it when users are likely to pan extensively over a large dataset.
214
+
215
+ ---
216
+
217
+ ### `visibility`
218
+
219
+ **Type:** `'visible' | 'hidden'`
220
+ **Default:** `'visible'`
221
+
222
+ Initial visibility of the dataset.
223
+
224
+ ---
225
+
226
+ ### `showInKey`
227
+
228
+ **Type:** `boolean`
229
+ **Default:** `false`
230
+
231
+ When `true`, the dataset appears in the Key panel with its style symbol and label.
232
+
233
+ ---
234
+
235
+ ### `toggleVisibility`
236
+
237
+ **Type:** `boolean`
238
+ **Default:** `false`
239
+
240
+ When `true`, the dataset appears in the Layers panel and can be toggled on and off by the user.
241
+
242
+ ---
243
+
244
+ ### `groupLabel`
245
+
246
+ **Type:** `string`
247
+
248
+ Groups this dataset with others sharing the same `groupLabel` in the Layers panel, rendering them as a single collapsible group.
249
+
250
+ ---
251
+
252
+ ### `keySymbolShape`
253
+
254
+ **Type:** `'polygon' | 'line'`
255
+
256
+ Overrides the shape used to render the key symbol for this dataset. Defaults to a polygon shape.
257
+
258
+ ---
259
+
260
+ ### `style`
261
+
262
+ **Type:** `Object`
263
+
264
+ Visual style for the dataset. All style properties must be nested within this object.
265
+
266
+ **Common properties:**
267
+
268
+ | Property | Type | Description |
269
+ |----------|------|-------------|
270
+ | `opacity` | `number` | Layer opacity from `0` to `1` |
271
+ | `symbolDescription` | `string \| Record<string, string>` | Accessible description of the symbol shown in the key |
272
+
273
+ **Polygon/line properties:**
274
+
275
+ | Property | Type | Description |
276
+ |----------|------|-------------|
277
+ | `stroke` | `string \| Record<string, string>` | Stroke (outline) colour. Accepts a plain colour string or a map-style-keyed object e.g. `{ outdoor: '#ff0000', dark: '#ffffff' }` |
278
+ | `strokeWidth` | `number` | Stroke width in pixels. **Default:** `2` |
279
+ | `strokeDashArray` | `number[]` | Dash pattern for the stroke e.g. `[4, 2]` |
280
+ | `fill` | `string \| Record<string, string>` | Fill colour. Use `'transparent'` for no fill |
281
+ | `fillPattern` | `string` | Named fill pattern e.g. `'diagonal-cross-hatch'`, `'horizontal-hatch'`, `'dot'`, `'vertical-hatch'` |
282
+ | `fillPatternSvgContent` | `string` | Raw SVG content for a custom fill pattern |
283
+ | `fillPatternForegroundColor` | `string \| Record<string, string>` | Foreground colour for the fill pattern |
284
+ | `fillPatternBackgroundColor` | `string \| Record<string, string>` | Background colour for the fill pattern |
285
+ | `keySymbolShape` | `'polygon' \| 'line'` | Shape used for the key symbol |
286
+
287
+ **Symbol (point) properties:**
288
+
289
+ Setting `symbol` or `symbolSvgContent` renders the dataset as a point layer instead of a polygon/line layer.
290
+
291
+ | Property | Type | Description |
292
+ |----------|------|-------------|
293
+ | `symbol` | `string` | Registered symbol ID e.g. `'pin'`, `'circle'`, `'square'` |
294
+ | `symbolSvgContent` | `string` | Inline SVG content for a fully custom symbol (no `<svg>` wrapper). Takes precedence over `symbol` |
295
+ | `symbolViewBox` | `string` | SVG viewBox for the symbol e.g. `'0 0 38 38'`. Defaults to the registered symbol's viewBox |
296
+ | `symbolAnchor` | `[number, number]` | Anchor point as a normalised `[x, y]` pair. Defaults to the registered symbol's anchor |
297
+ | `symbolBackgroundColor` | `string \| Record<string, string>` | Background fill colour of the symbol |
298
+ | `symbolForegroundColor` | `string \| Record<string, string>` | Foreground fill colour of the symbol (e.g. the inner dot) |
299
+ | `symbolHaloWidth` | `string` | Stroke width of the halo in SVG units |
300
+ | `symbolGraphic` | `string` | SVG `d` attribute for the foreground graphic path. Use named values (`'dot'`, `'cross'`, `'diamond'`, `'triangle'`, `'square'`) or supply your own path data |
301
+
302
+ Symbol colour properties use the `symbol` prefix to distinguish them from polygon/line properties in the same style object. They follow the same resolution order and support style-keyed colour objects in the same way as markers — see [Symbol Config](../api/symbol-config.md) for details.
303
+
304
+ `haloColor` and `selectedColor` are not settable here — they are basemap-level properties set on [`MapStyleConfig`](../api/map-style-config.md).
305
+
306
+ ```js
307
+ // Polygon/line dataset
308
+ style: {
309
+ stroke: { outdoor: '#d4351c', dark: '#ffffff' },
310
+ strokeWidth: 2,
311
+ fill: 'rgba(212,53,28,0.1)',
312
+ symbolDescription: { outdoor: 'Red outline' }
313
+ }
314
+
315
+ // Point dataset — registered symbol with colour overrides
316
+ style: {
317
+ symbol: 'pin',
318
+ symbolBackgroundColor: '#1d70b8',
319
+ symbolForegroundColor: '#ffffff'
320
+ }
321
+
322
+ // Point dataset — style-keyed colours for multi-basemap support
323
+ style: {
324
+ symbol: 'pin',
325
+ symbolBackgroundColor: { outdoor: '#1d70b8', dark: '#5694ca' }
326
+ }
327
+
328
+ // Point dataset — custom inline SVG
329
+ style: {
330
+ symbolSvgContent: '<circle cx="19" cy="19" r="12" fill="{{backgroundColor}}"/>',
331
+ symbolViewBox: '0 0 38 38',
332
+ symbolAnchor: [0.5, 0.5],
333
+ symbolBackgroundColor: '#1d70b8'
334
+ }
335
+ ```
336
+
337
+ ---
338
+
339
+ ### `sublayers`
340
+
341
+ **Type:** `Sublayer[]`
342
+
343
+ Array of sublayer rules that partition the dataset into visually distinct groups based on feature filters. Each sublayer is rendered as a separate map layer.
344
+
345
+ Sublayers inherit the parent dataset's style and only override what they specify in their own `style` object. For polygon/line datasets, fill precedence is (highest to lowest): sublayer `fillPattern` → sublayer `fill` → parent `fillPattern` → parent `fill`. For symbol datasets, each symbol property is inherited individually from the parent unless the sublayer sets it explicitly.
346
+
347
+ #### `Sublayer` properties
348
+
349
+ | Property | Type | Description |
350
+ |----------|------|-------------|
351
+ | `id` | `string` | **Required.** Unique identifier within the dataset |
352
+ | `label` | `string` | Human-readable name shown in the Layers and Key panels |
353
+ | `filter` | `FilterExpression` | MapLibre filter expression to match features for this sublayer |
354
+ | `style` | `Object` | Style overrides. Accepts the same properties as the dataset `style` object |
355
+ | `showInKey` | `boolean` | Shows this sublayer in the Key panel. Inherits from dataset if not set |
356
+ | `toggleVisibility` | `boolean` | Shows this sublayer in the Layers panel. **Default:** `false` |
357
+
358
+ **Polygon/line example:**
359
+
360
+ ```js
361
+ sublayers: [
362
+ {
363
+ id: 'active',
364
+ label: 'Active parcels',
365
+ filter: ['==', ['get', 'status'], 'active'],
366
+ toggleVisibility: true,
367
+ style: {
368
+ stroke: '#00703c',
369
+ fill: 'rgba(0,112,60,0.1)',
370
+ symbolDescription: 'Green outline'
371
+ }
372
+ },
373
+ {
374
+ id: 'inactive',
375
+ label: 'Inactive parcels',
376
+ filter: ['==', ['get', 'status'], 'inactive'],
377
+ toggleVisibility: true,
378
+ style: {
379
+ stroke: '#d4351c',
380
+ fillPattern: 'diagonal-cross-hatch',
381
+ fillPatternForegroundColor: '#d4351c'
382
+ }
383
+ }
384
+ ]
385
+ ```
386
+
387
+ **Symbol (point) example — scheduled monuments by type:**
388
+
389
+ When the parent dataset has `symbol` set, each sublayer can override individual symbol properties to represent different categories. Properties not set on the sublayer are inherited from the parent.
390
+
391
+ ```js
392
+ {
393
+ id: 'scheduled-monuments',
394
+ geojson: scheduledMonumentsData,
395
+ style: { symbol: 'square' },
396
+ sublayers: [
397
+ {
398
+ id: 'prehistoric',
399
+ label: 'Prehistoric sites',
400
+ filter: ['==', ['get', 'type'], 'prehistoric'],
401
+ showInKey: true,
402
+ toggleVisibility: true,
403
+ style: { symbolBackgroundColor: '#0f7a52' }
404
+ },
405
+ {
406
+ id: 'roman',
407
+ label: 'Roman sites',
408
+ filter: ['==', ['get', 'type'], 'roman'],
409
+ showInKey: true,
410
+ toggleVisibility: true,
411
+ style: { symbolBackgroundColor: '#54319f' }
412
+ },
413
+ {
414
+ id: 'medieval',
415
+ label: 'Medieval sites',
416
+ filter: ['==', ['get', 'type'], 'medieval'],
417
+ showInKey: true,
418
+ toggleVisibility: true,
419
+ style: { symbolBackgroundColor: '#ca357c' }
420
+ }
421
+ ]
422
+ }
423
+ ```
424
+
425
+ ---
426
+
427
+ ## Methods
428
+
429
+ Methods are called on the plugin instance after the `datasets:ready` event.
430
+
431
+ The API follows a consistent pattern: the primary value is the first argument, with an optional scope object as the second argument. Omitting the scope applies the operation globally where supported.
432
+
433
+ ---
434
+
435
+ ### `addDataset(dataset)`
436
+
437
+ Add a new dataset to the map at runtime.
438
+
439
+ | Argument | Type | Description |
440
+ |----------|------|-------------|
441
+ | `dataset` | `Dataset` | Dataset configuration object. Accepts the same properties as `datasets` array entries |
442
+
443
+ ```js
444
+ interactiveMap.on('datasets:ready', () => {
445
+ datasetsPlugin.addDataset({
446
+ id: 'new-layer',
447
+ geojson: 'https://example.com/api/features',
448
+ minZoom: 10,
449
+ style: { stroke: '#0000ff' }
450
+ })
451
+ })
452
+ ```
453
+
454
+ ---
455
+
456
+ ### `removeDataset(datasetId)`
457
+
458
+ Remove a dataset from the map.
459
+
460
+ | Argument | Type | Description |
461
+ |----------|------|-------------|
462
+ | `datasetId` | `string` | ID of the dataset to remove |
463
+
464
+ ```js
465
+ datasetsPlugin.removeDataset('my-parcels')
466
+ ```
467
+
468
+ ---
469
+
470
+ ### `setDatasetVisibility(visible, scope?)`
471
+
472
+ Set the visibility of datasets or sublayers. Omit `scope` to apply to all datasets globally.
473
+
474
+ When showing a dataset that has sublayers, any sublayers that were individually hidden before the dataset was hidden will remain hidden — their individual visibility state is preserved.
475
+
476
+ | Argument | Type | Description |
477
+ |----------|------|-------------|
478
+ | `visible` | `boolean` | `true` to show, `false` to hide |
479
+ | `scope.datasetId` | `string` | Optional. When omitted, applies to all datasets |
480
+ | `scope.sublayerId` | `string` | Optional. When provided alongside `datasetId`, targets a single sublayer |
481
+
482
+ ```js
483
+ // Global — all datasets
484
+ datasetsPlugin.setDatasetVisibility(false)
485
+ datasetsPlugin.setDatasetVisibility(true)
486
+
487
+ // Single dataset
488
+ datasetsPlugin.setDatasetVisibility(false, { datasetId: 'my-parcels' })
489
+
490
+ // Single sublayer
491
+ datasetsPlugin.setDatasetVisibility(false, { datasetId: 'my-parcels', sublayerId: 'active' })
492
+ ```
493
+
494
+ ---
495
+
496
+ ### `setFeatureVisibility(visible, featureIds, scope)`
497
+
498
+ Show or hide specific features within a dataset without removing them from the source.
499
+
500
+ | Argument | Type | Description |
501
+ |----------|------|-------------|
502
+ | `visible` | `boolean` | `true` to show, `false` to hide |
503
+ | `featureIds` | `(string \| number)[]` | IDs of the features to target |
504
+ | `scope.datasetId` | `string` | ID of the dataset |
505
+ | `scope.idProperty` | `string \| null` | Property name to match features on. Pass `null` to match against the top-level `feature.id` |
506
+
507
+ ```js
508
+ // Hide by a feature property
509
+ datasetsPlugin.setFeatureVisibility(false, [123, 456], {
510
+ datasetId: 'my-parcels',
511
+ idProperty: 'parcel_id'
512
+ })
513
+
514
+ // Show using feature.id
515
+ datasetsPlugin.setFeatureVisibility(true, [123, 456], {
516
+ datasetId: 'my-parcels',
517
+ idProperty: null
518
+ })
519
+ ```
520
+
521
+ ---
522
+
523
+ ### `setStyle(style, scope)`
524
+
525
+ Update the visual style of a dataset or sublayer at runtime. When targeting a sublayer, only the properties specified are overridden — the sublayer inherits all other styles from the parent dataset.
526
+
527
+ For symbol datasets, pass `symbol` as the style property to change the symbol config.
528
+
529
+ | Argument | Type | Description |
530
+ |----------|------|-------------|
531
+ | `style` | `Object` | Style properties to apply. Accepts the same properties as `dataset.style`, plus `symbol` |
532
+ | `scope.datasetId` | `string` | ID of the dataset |
533
+ | `scope.sublayerId` | `string` | Optional. When provided, targets a single sublayer |
534
+
535
+ ```js
536
+ // Polygon/line dataset
537
+ datasetsPlugin.setStyle(
538
+ { stroke: '#0000ff', strokeWidth: 3 },
539
+ { datasetId: 'my-parcels' }
540
+ )
541
+
542
+ // Sublayer — polygon
543
+ datasetsPlugin.setStyle(
544
+ { stroke: '#00703c', fillPattern: 'diagonal-cross-hatch', fillPatternForegroundColor: '#00703c' },
545
+ { datasetId: 'my-parcels', sublayerId: 'active' }
546
+ )
547
+
548
+ // Sublayer — symbol colour override
549
+ datasetsPlugin.setStyle(
550
+ { symbolBackgroundColor: '#912b88' },
551
+ { datasetId: 'flood-warnings', sublayerId: 'severe' }
552
+ )
553
+ ```
554
+
555
+ ---
556
+
557
+ ### `getStyle(scope)`
558
+
559
+ Returns the current style object for a dataset or sublayer, or `null` if not found.
560
+
561
+ | Argument | Type | Description |
562
+ |----------|------|-------------|
563
+ | `scope.datasetId` | `string` | ID of the dataset |
564
+ | `scope.sublayerId` | `string` | Optional. When provided, returns the sublayer's style |
565
+
566
+ ```js
567
+ // Dataset style
568
+ const style = datasetsPlugin.getStyle({ datasetId: 'my-parcels' })
569
+
570
+ // Sublayer style
571
+ const style = datasetsPlugin.getStyle({ datasetId: 'my-parcels', sublayerId: 'active' })
572
+ ```
573
+
574
+ ---
575
+
576
+ ### `setOpacity(opacity, scope?)`
577
+
578
+ Set the opacity of datasets or a sublayer. Safe to call on every tick from a slider — uses `setPaintProperty` internally rather than removing and re-adding layers. Omit `scope` to apply globally.
579
+
580
+ | Argument | Type | Description |
581
+ |----------|------|-------------|
582
+ | `opacity` | `number` | Opacity from `0` (transparent) to `1` (fully opaque) |
583
+ | `scope.datasetId` | `string` | Optional. When omitted, applies to all datasets |
584
+ | `scope.sublayerId` | `string` | Optional. When provided alongside `datasetId`, targets a single sublayer |
585
+
586
+ ```js
587
+ // Global — all datasets
588
+ datasetsPlugin.setOpacity(0.5)
589
+
590
+ // Single dataset
591
+ datasetsPlugin.setOpacity(0.5, { datasetId: 'my-parcels' })
592
+
593
+ // Single sublayer
594
+ datasetsPlugin.setOpacity(0.5, { datasetId: 'my-parcels', sublayerId: 'active' })
595
+ ```
596
+
597
+ ---
598
+
599
+ ### `getOpacity(scope?)`
600
+
601
+ Returns the current opacity for a dataset or sublayer. When called without arguments, returns the first dataset's opacity — useful for initialising a global slider. Returns `null` if not found.
602
+
603
+ | Argument | Type | Description |
604
+ |----------|------|-------------|
605
+ | `scope.datasetId` | `string` | Optional. When omitted, returns the first dataset's opacity |
606
+ | `scope.sublayerId` | `string` | Optional. When provided alongside `datasetId`, returns the sublayer's opacity |
607
+
608
+ ```js
609
+ // Global — read back after setOpacity() for slider initialisation
610
+ const opacity = datasetsPlugin.getOpacity()
611
+
612
+ // Single dataset
613
+ const opacity = datasetsPlugin.getOpacity({ datasetId: 'my-parcels' })
614
+
615
+ // Single sublayer
616
+ const opacity = datasetsPlugin.getOpacity({ datasetId: 'my-parcels', sublayerId: 'active' })
617
+ ```
618
+
619
+ ---
620
+
621
+ ### `setData(geojson, scope)`
622
+
623
+ Replace the GeoJSON data for a dataset source. Has no effect on vector tile datasets.
624
+
625
+ | Argument | Type | Description |
626
+ |----------|------|-------------|
627
+ | `geojson` | `GeoJSON.FeatureCollection` | New GeoJSON data |
628
+ | `scope.datasetId` | `string` | ID of the dataset |
629
+
630
+ ```js
631
+ datasetsPlugin.setData(
632
+ { type: 'FeatureCollection', features: [...] },
633
+ { datasetId: 'my-parcels' }
634
+ )
635
+ ```
636
+
637
+ ---
638
+
639
+ ## Events
640
+
641
+ Subscribe to events using `interactiveMap.on()`.
642
+
643
+ ---
644
+
645
+ ### `datasets:ready`
646
+
647
+ Emitted once all datasets have been initialised and rendered on the map.
648
+
649
+ **Payload:** None
650
+
651
+ ```js
652
+ interactiveMap.on('datasets:ready', () => {
653
+ console.log('Datasets are ready')
654
+ // Safe to call API methods from here
655
+ const style = datasetsPlugin.getStyle({ datasetId: 'my-parcels' }) // unchanged — scope object
656
+ })
657
+ ```