Datasets Plugin
The datasets plugin renders GeoJSON and vector tile datasets on the map, with support for sublayer style rules, layer visibility toggling, a key panel, and runtime style and data updates.
Usage
import createDatasetsPlugin from '@defra/interactive-map/plugins/beta/datasets'import { maplibreLayerAdapter } from '@defra/interactive-map/plugins/beta/datasets/adapters/maplibre'
const datasetsPlugin = createDatasetsPlugin({ layerAdapter: maplibreLayerAdapter, datasets: [ { id: 'my-parcels', label: 'My parcels', geojson: 'https://example.com/api/parcels', minZoom: 10, maxZoom: 24, showInKey: true, toggleVisibility: true, style: { stroke: '#d4351c', strokeWidth: 2, fill: 'transparent' } } ]})
const interactiveMap = new InteractiveMap({ plugins: [datasetsPlugin]})Options
Options are passed to the factory function when creating the plugin.
layerAdapter
Type: LayerAdapter
Required
The map provider adapter responsible for rendering datasets. Import maplibreLayerAdapter for MapLibre GL JS, or supply a custom adapter.
import { maplibreLayerAdapter } from '@defra/interactive-map/plugins/beta/datasets/adapters/maplibre'datasets
Type: Dataset[]
Required
Array of dataset configurations to render on the map. See Dataset configuration below.
includeModes
Type: string[]
When set, the plugin only initialises when the app is in one of the specified modes.
excludeModes
Type: string[]
When set, the plugin does not initialise when the app is in one of the specified modes.
Dataset configuration
Each entry in the datasets array describes one data source and how it should be rendered.
id
Type: string
Required
Unique identifier for the dataset. Used in all API method calls.
label
Type: string
Human-readable name shown in the Layers panel and Key panel.
geojson
Type: string | GeoJSON.FeatureCollection
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.
tiles
Type: string[]
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.
sourceLayer
Type: string
The layer name within the vector tile source to render. Required when using tiles.
transformRequest
Type: Function
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.
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.
Signature: transformRequest(url, { bbox, zoom, dataset })
| Argument | Type | Description |
|---|---|---|
url | string | The base URL from geojson |
bbox | number[] | Current viewport bounds as [west, south, east, north] |
zoom | number | Current map zoom level |
dataset | Object | The full dataset configuration |
Return either a plain URL string or an object { url, headers }. The object form is needed when attaching auth headers.
// Auth headers only (no bbox filtering)transformRequest: (url) => ({ url, headers: { Authorization: `Bearer ${getToken()}` }})
// Append bbox to URL for server-side spatial filteringtransformRequest: (url, { bbox }) => { const separator = url.includes('?') ? '&' : '?' return { url: `${url}${separator}bbox=${bbox.join(',')}` }}
// Both — auth + bboxtransformRequest: (url, { bbox }) => { const separator = url.includes('?') ? '&' : '?' return { url: `${url}${separator}bbox=${bbox.join(',')}`, headers: { Authorization: `Bearer ${getToken()}` } }}idProperty
Type: string
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.
filter
Type: FilterExpression
A MapLibre filter expression applied to the dataset's map layers. Features not matching the filter are not rendered.
filter: ['==', ['get', 'status'], 'active']minZoom
Type: number
Default: 6
Minimum zoom level at which the dataset is visible.
maxZoom
Type: number
Default: 24
Maximum zoom level at which the dataset is visible.
maxFeatures
Type: number
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.
visibility
Type: 'visible' | 'hidden'
Default: 'visible'
Initial visibility of the dataset.
showInKey
Type: boolean
Default: false
When true, the dataset appears in the Key panel with its style symbol and label.
toggleVisibility
Type: boolean
Default: false
When true, the dataset appears in the Layers panel and can be toggled on and off by the user.
groupLabel
Type: string
Groups this dataset with others sharing the same groupLabel in the Layers panel, rendering them as a single collapsible group.
keySymbolShape
Type: 'polygon' | 'line'
Overrides the shape used to render the key symbol for this dataset. Defaults to a polygon shape.
style
Type: Object
Visual style for the dataset. All style properties must be nested within this object.
| Property | Type | Description |
|---|---|---|
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' } |
strokeWidth | number | Stroke width in pixels. Default: 2 |
strokeDashArray | number[] | Dash pattern for the stroke e.g. [4, 2] |
fill | string | Record<string, string> | Fill colour. Use 'transparent' for no fill |
fillPattern | string | Named fill pattern e.g. 'diagonal-cross-hatch', 'horizontal-hatch', 'dot', 'vertical-hatch' |
fillPatternSvgContent | string | Raw SVG content for a custom fill pattern |
fillPatternForegroundColor | string | Record<string, string> | Foreground colour for the fill pattern |
fillPatternBackgroundColor | string | Record<string, string> | Background colour for the fill pattern |
opacity | number | Layer opacity from 0 to 1 |
symbolDescription | string | Record<string, string> | Accessible description of the symbol shown in the key |
keySymbolShape | 'polygon' | 'line' | Shape used for the key symbol |
style: { stroke: { outdoor: '#d4351c', dark: '#ffffff' }, strokeWidth: 2, fill: 'rgba(212,53,28,0.1)', symbolDescription: { outdoor: 'Red outline' }}sublayers
Type: Sublayer[]
Array of sublayer rules that partition the dataset into visually distinct groups based on feature filters. Each sublayer is rendered as a separate pair of map layers.
Sublayers inherit the parent dataset's style and only override what they specify. Fill precedence (highest to lowest): sublayer's own fillPattern → sublayer's own fill → parent's fillPattern → parent's fill.
Sublayer properties
| Property | Type | Description |
|---|---|---|
id | string | Required. Unique identifier within the dataset |
label | string | Human-readable name shown in the Layers and Key panels |
filter | FilterExpression | MapLibre filter expression to match features for this sublayer |
style | Object | Style overrides for this sublayer. Accepts the same properties as the dataset style object |
showInKey | boolean | Shows this sublayer in the Key panel. Inherits from dataset if not set |
toggleVisibility | boolean | Shows this sublayer in the Layers panel. Default: false |
sublayers: [ { id: 'active', label: 'Active parcels', filter: ['==', ['get', 'status'], 'active'], toggleVisibility: true, style: { stroke: '#00703c', fill: 'rgba(0,112,60,0.1)', symbolDescription: 'Green outline' } }, { id: 'inactive', label: 'Inactive parcels', filter: ['==', ['get', 'status'], 'inactive'], toggleVisibility: true, style: { stroke: '#d4351c', fillPattern: 'diagonal-cross-hatch', fillPatternForegroundColor: '#d4351c' } }]Methods
Methods are called on the plugin instance after the datasets:ready event.
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.
addDataset(dataset)
Add a new dataset to the map at runtime.
| Argument | Type | Description |
|---|---|---|
dataset | Dataset | Dataset configuration object. Accepts the same properties as datasets array entries |
interactiveMap.on('datasets:ready', () => { datasetsPlugin.addDataset({ id: 'new-layer', geojson: 'https://example.com/api/features', minZoom: 10, style: { stroke: '#0000ff' } })})removeDataset(datasetId)
Remove a dataset from the map.
| Argument | Type | Description |
|---|---|---|
datasetId | string | ID of the dataset to remove |
datasetsPlugin.removeDataset('my-parcels')setDatasetVisibility(visible, scope?)
Set the visibility of datasets or sublayers. Omit scope to apply to all datasets globally.
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.
| Argument | Type | Description |
|---|---|---|
visible | boolean | true to show, false to hide |
scope.datasetId | string | Optional. When omitted, applies to all datasets |
scope.sublayerId | string | Optional. When provided alongside datasetId, targets a single sublayer |
// Global — all datasetsdatasetsPlugin.setDatasetVisibility(false)datasetsPlugin.setDatasetVisibility(true)
// Single datasetdatasetsPlugin.setDatasetVisibility(false, { datasetId: 'my-parcels' })
// Single sublayerdatasetsPlugin.setDatasetVisibility(false, { datasetId: 'my-parcels', sublayerId: 'active' })setFeatureVisibility(visible, featureIds, scope)
Show or hide specific features within a dataset without removing them from the source.
| Argument | Type | Description |
|---|---|---|
visible | boolean | true to show, false to hide |
featureIds | (string | number)[] | IDs of the features to target |
scope.datasetId | string | ID of the dataset |
scope.idProperty | string | null | Property name to match features on. Pass null to match against the top-level feature.id |
// Hide by a feature propertydatasetsPlugin.setFeatureVisibility(false, [123, 456], { datasetId: 'my-parcels', idProperty: 'parcel_id'})
// Show using feature.iddatasetsPlugin.setFeatureVisibility(true, [123, 456], { datasetId: 'my-parcels', idProperty: null})setStyle(style, scope)
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.
| Argument | Type | Description |
|---|---|---|
style | Object | Style properties to apply. Accepts the same properties as dataset.style |
scope.datasetId | string | ID of the dataset |
scope.sublayerId | string | Optional. When provided, targets a single sublayer |
// Dataset leveldatasetsPlugin.setStyle( { stroke: '#0000ff', strokeWidth: 3 }, { datasetId: 'my-parcels' })
// Sublayer leveldatasetsPlugin.setStyle( { stroke: '#00703c', fillPattern: 'diagonal-cross-hatch', fillPatternForegroundColor: '#00703c' }, { datasetId: 'my-parcels', sublayerId: 'active' })getStyle(scope)
Returns the current style object for a dataset or sublayer, or null if not found.
| Argument | Type | Description |
|---|---|---|
scope.datasetId | string | ID of the dataset |
scope.sublayerId | string | Optional. When provided, returns the sublayer's style |
// Dataset styleconst style = datasetsPlugin.getStyle({ datasetId: 'my-parcels' })
// Sublayer styleconst style = datasetsPlugin.getStyle({ datasetId: 'my-parcels', sublayerId: 'active' })setOpacity(opacity, scope?)
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.
| Argument | Type | Description |
|---|---|---|
opacity | number | Opacity from 0 (transparent) to 1 (fully opaque) |
scope.datasetId | string | Optional. When omitted, applies to all datasets |
scope.sublayerId | string | Optional. When provided alongside datasetId, targets a single sublayer |
// Global — all datasetsdatasetsPlugin.setOpacity(0.5)
// Single datasetdatasetsPlugin.setOpacity(0.5, { datasetId: 'my-parcels' })
// Single sublayerdatasetsPlugin.setOpacity(0.5, { datasetId: 'my-parcels', sublayerId: 'active' })getOpacity(scope?)
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.
| Argument | Type | Description |
|---|---|---|
scope.datasetId | string | Optional. When omitted, returns the first dataset's opacity |
scope.sublayerId | string | Optional. When provided alongside datasetId, returns the sublayer's opacity |
// Global — read back after setOpacity() for slider initialisationconst opacity = datasetsPlugin.getOpacity()
// Single datasetconst opacity = datasetsPlugin.getOpacity({ datasetId: 'my-parcels' })
// Single sublayerconst opacity = datasetsPlugin.getOpacity({ datasetId: 'my-parcels', sublayerId: 'active' })setData(geojson, scope)
Replace the GeoJSON data for a dataset source. Has no effect on vector tile datasets.
| Argument | Type | Description |
|---|---|---|
geojson | GeoJSON.FeatureCollection | New GeoJSON data |
scope.datasetId | string | ID of the dataset |
datasetsPlugin.setData( { type: 'FeatureCollection', features: [...] }, { datasetId: 'my-parcels' })Events
Subscribe to events using interactiveMap.on().
datasets:ready
Emitted once all datasets have been initialised and rendered on the map.
Payload: None
interactiveMap.on('datasets:ready', () => { console.log('Datasets are ready') // Safe to call API methods from here const style = datasetsPlugin.getStyle({ datasetId: 'my-parcels' }) // unchanged — scope object})