Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
809ed83
IM-258 added globals to dataset state
markfee May 6, 2026
e68abb4
IM-258 added a datasetRegistry
markfee May 7, 2026
1177f8b
IM-258 stopped flattening styles in mappedDatasets
markfee May 7, 2026
a703008
IM-258 fixed tests in datasets
markfee May 7, 2026
b8e73f0
IM-258 added a dataset class and tests
markfee May 12, 2026
7ba55d1
IM-258 added createDataset factory methods, and a mapLibreSpecific Da…
markfee May 12, 2026
5574e16
IM-258 added layerIds and tests to MapLibreDataset
markfee May 12, 2026
31a7c38
IM-258 mapLibreDataset.test.js avoids mocking
markfee May 12, 2026
d63e771
IM-258 refactored tests and ensured layerIds are not null
markfee May 12, 2026
a202c4d
IM-258 addLayers/subLayers refactored to use dataset registry
markfee May 13, 2026
269738d
IM-258 added source and sourceId to mapLibreDataset
markfee May 13, 2026
67f99a1
IM-258 setLayerAdapterActions can take a method name as param
markfee May 14, 2026
febccff
IM-258 ensured setLayerAdapterActions setStyle is only called once pe…
markfee May 14, 2026
7e6978b
IM-258 fixed _maintainSymbolOrdering, temp rennamed dataset as regist…
markfee May 14, 2026
12fb3e8
IM-258 further simplification
markfee May 14, 2026
1431299
IM-258 setSublayerStyle works from the api
markfee May 14, 2026
8edd4d3
IM-258 addSymbolLayer refactored
markfee May 15, 2026
bf8e84f
IM-258 simplified addSymbolLayer
markfee May 18, 2026
7db2717
IM-258 simplified addFillLayer
markfee May 18, 2026
6a8ad57
IM-258 simplified addStrokeLayer
markfee May 19, 2026
542226c
IM-258 fixed issue where addStrokeLayer called on a symbol
markfee May 19, 2026
7423f20
IM-258 removed unused params from addSublayerLayers
markfee May 19, 2026
c96a70b
IM-258 removed setSubLayerStyle altogether
markfee May 19, 2026
91ea9f5
IM-258 setSublayerStyle removed from maplibreLayerAdapter
markfee May 19, 2026
fb34bd1
IM-258 removed commented out addSource
markfee May 19, 2026
5aad8dd
IM-258 skipped tests while WIP
markfee May 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions demo/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,16 @@ interactiveMap.on('datasets:ready', function () {
// setTimeout(() => datasetsPlugin.setFeatureVisibility(false, [55], { datasetId: 'land-covers', idProperty: null }), 2000)
// setTimeout(() => datasetsPlugin.setFeatureVisibility(true, [55], { datasetId: 'land-covers', idProperty: null }), 4000)

setTimeout(() => datasetsPlugin.setStyle(
{
stroke: { outdoor: '#ff0000', dark: '#ffffff' },
fillPattern: 'horizontal-hatch',
fillPatternForegroundColor: { outdoor: '#ff0000', dark: '#ffffff' },
fillPatternBackgroundColor: 'transparent'
},
{ datasetId: 'land-covers', sublayerId: '130-131' }), 2000)
// setTimeout(() => datasetsPlugin.setStyle(
// {
// stroke: { outdoor: '#ff0000', dark: '#ffffff' },
// fillPattern: 'horizontal-hatch',
// fillPatternForegroundColor: { outdoor: '#ff0000', dark: '#ffffff' },
// fillPatternBackgroundColor: 'transparent'
// },
// { datasetId: 'land-covers', sublayerId: '130-131' }), 2000)
// setTimeout(() => datasetsPlugin.setDatasetVisibility(false), 1000)
// setTimeout(() => datasetsPlugin.setDatasetVisibility(true), 5000)
// setTimeout(() => datasetsPlugin.setDatasetVisibility(true, { datasetId: 'hedge-control' }), 500)
// setTimeout(() => datasetsPlugin.setStyle({ stroke: { outdoor: '#ffff00' }, }, { datasetId: 'hedge-control' }), 1000)
})
Expand Down
25 changes: 25 additions & 0 deletions plugins/beta/datasets/src/DatasetsInit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
import { useEffect, useRef } from 'react'
import { EVENTS } from '../../../../src/config/events.js'
import { createDatasets } from './datasets.js'
import { datasetRegistry } from './registry/datasetRegistry.js'

const useLayerAdapterActions = (methodName, dispatch, pluginState, dependencies) =>
useEffect(() => {
const methodParameters = pluginState.layerAdapterActions?.[methodName] || []
const method = pluginState.layerAdapter?.[methodName]
console.log('useEffect:', ...methodParameters.map((params) => `${params[0]},`))
if (method && methodParameters.length) {
methodParameters.forEach((parameters) => {
console.log(`calling ${methodName} with ${parameters[0]}`)
method.bind(pluginState.layerAdapter)(...parameters)
})
if (methodParameters.length) {
dispatch({ type: 'SET_LAYER_ADAPTER_ACTIONS', payload: { [methodName]: [] } })
}
}
}, [...dependencies])

export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, mapProvider, services }) {
const { dispatch } = pluginState
Expand Down Expand Up @@ -49,6 +66,9 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
dispatch,
eventBus
})
if (LayerAdapter.createDataset) {
datasetRegistry.attachCreateDataset(LayerAdapter.createDataset)
}
}

initDatasets()
Expand All @@ -58,6 +78,11 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
dispatch({ type: 'BUILD_KEY_GROUPS', payload: null })
}, [pluginState.datasets])

const datasetsRef = useRef(pluginState.mappedDatasets)
datasetsRef.current = pluginState.mappedDatasets
useEffect(() => datasetRegistry.attach(datasetsRef.current), [pluginState.mappedDatasets])
useLayerAdapterActions('setStyle', dispatch, pluginState, [pluginState.layerAdapterActions.setStyle])

// Cleanup only on unmount
useEffect(() => {
return () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Dataset } from '../../../registry/dataset.js'
import { MAX_TILE_ZOOM, hashString } from '../layerIds.js'
import { anchorToMaplibre } from '../symbolImages.js'

export class MapLibreDataset extends Dataset {
get isDynamicSource () {
return typeof this.geojson === 'string' && !!this.idProperty && typeof this.transformRequest === 'function'
}

get fillLayerId () {
if (this.hasSublayers) {
return null
}
if (this.hasSymbol) {
return null
}
if (this.hasFill) {
return this.id
}
return null
}

get strokeLayerId () {
if (this.hasSublayers) {
return null
}
if (this.hasSymbol) {
return null
}
if (this.hasStroke) {
return this.hasFill ? `${this.id}-stroke` : this.id
}
return null
}

get symbolLayerId () {
if (this.hasSublayers) {
return null
}
if (this.hasSymbol) {
return this.id
}
return null
}

get layerIds () {
if (this.hasSublayers) {
return this.sublayers.flatMap(sublayer => sublayer.layerIds).filter(Boolean)
}
return [this.symbolLayerId, this.fillLayerId, this.strokeLayerId].filter(Boolean)
}

get sourceId () {
if (this.isSublayer) { return this.parent.sourceId }
if (this.tiles) {
const tilesKey = Array.isArray(this.tiles) ? this.tiles.join(',') : this.tiles
return `tiles-${hashString(tilesKey)}`
}
if (this.geojson) {
if (this.isDynamicSource) { return `geojson-dynamic-${this.id}` }
if (typeof this.geojson === 'string') { return `geojson-${hashString(this.geojson)}` }
return `geojson-${this.id}`
}
return `source-${this.id}`
}

get source () {
if (this.tiles) {
return {
type: 'vector',
tiles: this.tiles,
minzoom: this.minZoom || 0,
maxzoom: this.maxZoom || MAX_TILE_ZOOM
}
}
if (this.geojson) {
const data = this.isDynamicSource ? { type: 'FeatureCollection', features: [] } : this.geojson
return { type: 'geojson', data, generateId: true }
}
return null
}

getSymbolSource (imageId, anchor, symbolDef) {
return {
id: this.symbolLayerId,
type: 'symbol',
source: this.sourceId,
'source-layer': this.sourceLayer,
minzoom: this.minZoom,
maxzoom: this.maxZoom,
layout: {
visibility: this.visibility === 'hidden' ? 'none' : 'visible',
'icon-image': imageId,
'icon-anchor': anchorToMaplibre(anchor || symbolDef?.anchor || [0.5, 0.5]),
'icon-allow-overlap': true
},
...(this.filter ? { filter: this.filter } : {})
}
}

getFillSource (paint) {
return {
id: this.fillLayerId,
type: 'fill',
source: this.sourceId,
'source-layer': this.sourceLayer,
minzoom: this.minZoom,
maxzoom: this.maxZoom,
layout: { visibility: this.visibility === 'hidden' ? 'none' : 'visible' },
paint,
...(this.filter ? { filter: this.filter } : {})
}
}

getStrokeSource (paint) {
return {
id: this.strokeLayerId,
type: 'line',
source: this.sourceId,
'source-layer': this.sourceLayer,
minzoom: this.minZoom,
maxzoom: this.maxZoom,
layout: { visibility: this.visibility === 'hidden' ? 'none' : 'visible' },
paint,
...(this.filter ? { filter: this.filter } : {})
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { MapLibreDataset } from './mapLibreDataset.js'
import { datasetRegistry } from '../../../registry/datasetRegistry.js'
import { datasets } from '../../../reducers/__data__/demoDatasets.js'
import { mappedDatasetsReducer } from '../../../reducers/mappedDatasetsReducer.js'

describe('MapLibreDataset', () => {
beforeAll(() => {
const { mappedDatasets } = mappedDatasetsReducer({ datasets })
datasetRegistry.attach({
...mappedDatasets,
'ds-fill-only': { id: 'ds-fill-only', style: { fill: 'blue' } },
'ds-pattern-only': { id: 'ds-pattern-only', style: { fillPattern: 'dots' } },
'ds-transparent-fill': { id: 'ds-transparent-fill', style: { fill: 'transparent' } },
'ds-no-style': { id: 'ds-no-style', style: {} }
})
datasetRegistry.attachCreateDataset(def => new MapLibreDataset(def))
})

describe('layerIds', () => {
it('returns [id] when dataset has a symbol (historic-monuments-prehistoric inherits symbol from parent)', () => {
const dataset = datasetRegistry.getDataset('historic-monuments-prehistoric')
expect(dataset.layerIds).toEqual(['historic-monuments-prehistoric'])
})

it('returns [id, id-stroke] when dataset has both fill and stroke (existing-fields)', () => {
const dataset = datasetRegistry.getDataset('existing-fields')
expect(dataset.layerIds).toEqual(['existing-fields', 'existing-fields-stroke'])
})

it('returns [id] when dataset has only fill', () => {
const dataset = datasetRegistry.getDataset('ds-fill-only')
expect(dataset.layerIds).toEqual(['ds-fill-only'])
})

it('returns [id] when dataset has only stroke (hedge-control)', () => {
const dataset = datasetRegistry.getDataset('hedge-control')
expect(dataset.layerIds).toEqual(['hedge-control'])
})

it('returns [id] when dataset has a fillPattern but no stroke', () => {
const dataset = datasetRegistry.getDataset('ds-pattern-only')
expect(dataset.layerIds).toEqual(['ds-pattern-only'])
})

it('returns [id, id-stroke] when dataset has fillPattern and stroke (land-covers-130-131)', () => {
const dataset = datasetRegistry.getDataset('land-covers-130-131')
expect(dataset.layerIds).toEqual(['land-covers-130-131', 'land-covers-130-131-stroke'])
})

it('returns null when fill is transparent and there is no stroke, symbol, or pattern', () => {
const dataset = datasetRegistry.getDataset('ds-transparent-fill')
expect(dataset.layerIds).toEqual([])
})

it('returns null when dataset has no fill, stroke, symbol, or pattern', () => {
const dataset = datasetRegistry.getDataset('ds-no-style')
expect(dataset.layerIds).toEqual([])
})

it('returns the combined layerIds from all sublayers (historic-monuments)', () => {
const dataset = datasetRegistry.getDataset('historic-monuments')
expect(dataset.layerIds).toEqual([
'historic-monuments-prehistoric',
'historic-monuments-roman',
'historic-monuments-medieval'
])
})

it('returns the combined layerIds from all sublayers (land-covers)', () => {
const dataset = datasetRegistry.getDataset('land-covers')
expect(dataset.layerIds).toEqual([
'land-covers-130-131', 'land-covers-130-131-stroke',
'land-covers-332', 'land-covers-332-stroke',
'land-covers-110', 'land-covers-110-stroke',
'land-covers-379', 'land-covers-379-stroke',
'land-covers-other', 'land-covers-other-stroke'
])
})
})
})
Loading
Loading