@defra/interactive-map 0.0.30-alpha → 0.0.31-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/context.md +4 -4
- package/docs/api.md +111 -37
- package/docs/assets/css/docusaurus.scss +20 -4
- package/docs/assets/images/basic-map.jpg +0 -0
- package/docs/assets/images/button-first.jpg +0 -0
- package/docs/assets/images/deuteranopia-ozs-map-thumb.jpg +0 -0
- package/docs/assets/images/draw.jpg +0 -0
- package/docs/assets/images/marker-panel.jpg +0 -0
- package/docs/assets/images/night-ozs-map-thumb.jpg +0 -0
- package/docs/assets/images/outdoor-ozs-map-thumb.jpg +0 -0
- package/docs/assets/images/search.jpg +0 -0
- package/docs/assets/images/select-feature.jpg +0 -0
- package/docs/assets/images/style-switcher.jpg +0 -0
- package/docs/assets/images/toggle-marker-label.jpg +0 -0
- package/docs/assets/images/tritanopia-ozs-map-thumb.jpg +0 -0
- package/docs/examples/add-marker-with-panel.mdx +2 -2
- package/docs/examples/basic-map.mdx +2 -2
- package/docs/examples/draw.mdx +271 -0
- package/docs/examples/index.mdx +58 -10
- package/docs/examples/search-control.mdx +71 -0
- package/docs/examples/select-feature.mdx +146 -0
- package/docs/examples/style-switcher.mdx +135 -0
- package/docs/examples/toggle-marker-label.mdx +3 -3
- package/docs/plugins/datasets.md +15 -28
- package/docusaurus.config.cjs +5 -1
- package/package.json +1 -1
- package/plugins/beta/datasets/dist/css/index.css +4 -0
- package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -0
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/esm/index.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +2 -0
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
- package/plugins/beta/datasets/dist/umd/index.js +2 -0
- package/plugins/beta/datasets/src/adapters/loadLayerAdapter.js +33 -0
- package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +44 -120
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +145 -245
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +516 -0
- package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +191 -0
- package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +441 -0
- package/plugins/beta/datasets/src/api/addDataset.js +2 -5
- package/plugins/beta/datasets/src/api/addDataset.test.js +16 -0
- package/plugins/beta/datasets/src/api/getOpacity.js +14 -14
- package/plugins/beta/datasets/src/api/getOpacity.test.js +72 -0
- package/plugins/beta/datasets/src/api/getStyle.js +8 -9
- package/plugins/beta/datasets/src/api/getStyle.test.js +42 -0
- package/plugins/beta/datasets/src/api/removeDataset.js +7 -7
- package/plugins/beta/datasets/src/api/removeDataset.test.js +45 -0
- package/plugins/beta/datasets/src/api/setData.js +9 -3
- package/plugins/beta/datasets/src/api/setData.test.js +62 -0
- package/plugins/beta/datasets/src/api/setDatasetVisibility.js +5 -30
- package/plugins/beta/datasets/src/api/setDatasetVisibility.test.js +31 -0
- package/plugins/beta/datasets/src/api/setFeatureVisibility.js +3 -21
- package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +32 -0
- package/plugins/beta/datasets/src/api/setGlobals.js +20 -0
- package/plugins/beta/datasets/src/api/setGlobals.test.js +63 -0
- package/plugins/beta/datasets/src/api/setOpacity.js +8 -27
- package/plugins/beta/datasets/src/api/setOpacity.test.js +31 -0
- package/plugins/beta/datasets/src/api/setStyle.js +5 -21
- package/plugins/beta/datasets/src/api/setStyle.test.js +53 -0
- package/plugins/beta/datasets/src/components/Key/Key.jsx +51 -0
- package/plugins/beta/datasets/src/components/Key/KeyGroupItem.jsx +18 -0
- package/plugins/beta/datasets/src/components/Key/KeyItem.jsx +20 -0
- package/plugins/beta/datasets/src/components/Key/KeySvg.jsx +22 -0
- package/plugins/beta/datasets/src/components/Key/KeySvg.test.jsx +87 -0
- package/plugins/beta/datasets/src/components/Key/KeySvgLine.jsx +19 -0
- package/plugins/beta/datasets/src/components/Key/KeySvgLine.test.jsx +71 -0
- package/plugins/beta/datasets/src/components/{KeySvgPattern.jsx → Key/KeySvgPattern.jsx} +3 -4
- package/plugins/beta/datasets/src/components/Key/KeySvgPattern.test.jsx +55 -0
- package/plugins/beta/datasets/src/components/Key/KeySvgRect.jsx +22 -0
- package/plugins/beta/datasets/src/components/Key/KeySvgRect.test.jsx +85 -0
- package/plugins/beta/datasets/src/components/{KeySvgSymbol.jsx → Key/KeySvgSymbol.jsx} +5 -5
- package/plugins/beta/datasets/src/components/Key/KeySvgSymbol.test.jsx +80 -0
- package/plugins/beta/datasets/src/components/Key/svgProperties.test.js +61 -0
- package/plugins/beta/datasets/src/{panels → components/LayersMenu}/Layers.module.scss +4 -0
- package/plugins/beta/datasets/src/components/LayersMenu/LayersMenu.jsx +33 -0
- package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuCheckbox.jsx +31 -0
- package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuGroupWrapper.jsx +19 -0
- package/plugins/beta/datasets/src/datasets.scss +2 -2
- package/plugins/beta/datasets/src/fetch/createDynamicSource.js +8 -8
- package/plugins/beta/datasets/src/{DatasetsInit.jsx → initialise/DatasetsInit.jsx} +19 -21
- package/plugins/beta/datasets/src/{defaults.js → initialise/defaults.js} +17 -2
- package/plugins/beta/datasets/src/initialise/defaults.test.js +127 -0
- package/plugins/beta/datasets/src/{datasets.js → initialise/initialiseDatasets.js} +30 -26
- package/plugins/beta/datasets/src/initialise/initialiseDatasets.test.js +273 -0
- package/plugins/beta/datasets/src/manifest.js +8 -6
- package/plugins/beta/datasets/src/reducers/__data__/demoDatasets.js +253 -0
- package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +78 -0
- package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +35 -0
- package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +34 -0
- package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.test.js +117 -0
- package/plugins/beta/datasets/src/reducers/pluginState.js +206 -0
- package/plugins/beta/datasets/src/reducers/pluginState.test.js +386 -0
- package/plugins/beta/datasets/src/registry/__mocks__/datasetRegistry.js +29 -0
- package/plugins/beta/datasets/src/registry/createDataset.js +5 -0
- package/plugins/beta/datasets/src/registry/dataset.js +172 -0
- package/plugins/beta/datasets/src/registry/dataset.test.js +483 -0
- package/plugins/beta/datasets/src/registry/datasetDefinitionCache.js +35 -0
- package/plugins/beta/datasets/src/registry/datasetRegistry.js +116 -0
- package/plugins/beta/datasets/src/registry/datasetRegistry.test.js +335 -0
- package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +17 -0
- package/plugins/beta/datasets/src/registry/globalDataset.js +36 -0
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/src/events.js +13 -1
- package/plugins/beta/draw-ml/src/index.js +1 -1
- package/plugins/beta/draw-ml/src/manifest.js +3 -0
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/src/InteractInit.jsx +2 -2
- package/plugins/interact/src/InteractInit.test.js +1 -1
- package/plugins/interact/src/events.js +2 -29
- package/plugins/interact/src/events.test.js +4 -102
- package/plugins/interact/src/hooks/useAttachEvents.js +3 -4
- package/plugins/interact/src/hooks/useAttachEvents.test.js +1 -3
- package/plugins/interact/src/hooks/useInteractionHandlers.js +10 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +23 -4
- package/plugins/interact/src/manifest.js +0 -35
- package/plugins/interact/src/manifest.test.js +3 -43
- package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
- package/providers/beta/esri/src/esriProvider.js +4 -0
- package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/src/openlayersProvider.js +4 -0
- package/providers/beta/openlayers/src/openlayersProvider.test.js +5 -0
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/src/maplibreProvider.js +6 -4
- package/providers/maplibre/src/maplibreProvider.test.js +5 -0
- package/providers/maplibre/src/utils/highlightFeatures.js +74 -25
- package/providers/maplibre/src/utils/highlightFeatures.test.js +132 -0
- package/providers/maplibre/src/utils/patternImages.js +2 -4
- package/providers/maplibre/src/utils/symbolImages.js +1 -4
- package/rollup.esm.mjs +2 -8
- package/src/App/components/MapButton/MapButton.jsx +16 -1
- package/src/App/components/MapButton/MapButton.module.scss +12 -1
- package/src/App/components/MapButton/MapButton.test.jsx +40 -1
- package/src/App/hooks/useContinueEnabledEvaluator.js +31 -0
- package/src/App/hooks/useContinueEnabledEvaluator.test.js +118 -0
- package/src/App/hooks/useEvaluateProp.js +15 -15
- package/src/App/hooks/useEvaluateProp.test.js +80 -76
- package/src/App/hooks/useLayoutMeasurements.js +1 -3
- package/src/App/hooks/useResizeObserver.js +12 -1
- package/src/App/hooks/useResizeObserver.test.js +1 -0
- package/src/App/renderer/PluginInits.jsx +2 -0
- package/src/App/renderer/mapButtons.js +21 -1
- package/src/App/renderer/mapButtons.test.js +46 -1
- package/src/App/store/appReducer.js +1 -1
- package/src/App/store/appReducer.test.js +37 -0
- package/src/InteractiveMap/InteractiveMap.js +10 -0
- package/src/InteractiveMap/InteractiveMap.test.js +8 -0
- package/src/config/appConfig.js +25 -0
- package/src/config/appConfig.test.js +50 -0
- package/src/config/defaults.js +1 -0
- package/src/scss/settings/_dimensions.scss +1 -0
- package/src/services/patternRegistry.js +8 -4
- package/src/types.js +7 -0
- package/src/utils/patternUtils.js +1 -2
- package/src/utils/patternUtils.test.js +9 -0
- package/webpack.dev.mjs +10 -0
- package/webpack.umd.mjs +1 -1
- package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +0 -1
- package/plugins/beta/datasets/dist/adapters/maplibre/esm/index.js +0 -1
- package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +0 -2
- package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +0 -2
- package/plugins/beta/datasets/dist/umd/maplibre/index.js +0 -2
- package/plugins/beta/datasets/src/adapters/maplibre/index.js +0 -18
- package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +0 -75
- package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +0 -27
- package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +0 -31
- package/plugins/beta/datasets/src/components/KeySvg.jsx +0 -24
- package/plugins/beta/datasets/src/components/KeySvgLine.jsx +0 -19
- package/plugins/beta/datasets/src/components/KeySvgRect.jsx +0 -22
- package/plugins/beta/datasets/src/index.maplibre.umd.js +0 -23
- package/plugins/beta/datasets/src/panels/Key.jsx +0 -62
- package/plugins/beta/datasets/src/panels/Layers.jsx +0 -141
- package/plugins/beta/datasets/src/reducer.js +0 -219
- package/plugins/beta/datasets/src/reducers/keyReducer.js +0 -34
- package/plugins/beta/datasets/src/utils/filters.js +0 -34
- package/plugins/beta/datasets/src/utils/mergeSublayer.js +0 -86
- package/plugins/beta/draw-ml/dist/css/index.css +0 -21
- /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt → im-datasets-ml-adapter.js.LICENSE.txt} +0 -0
- /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-plugin.js.LICENSE.txt → im-datasets-plugin.js.LICENSE.txt} +0 -0
- /package/plugins/beta/datasets/dist/umd/{maplibre/index.js.LICENSE.txt → index.js.LICENSE.txt} +0 -0
- /package/plugins/beta/datasets/src/components/{EmptyKey.jsx → Key/EmptyKey.jsx} +0 -0
- /package/plugins/beta/datasets/src/components/{EmptyKey.test.jsx → Key/EmptyKey.test.jsx} +0 -0
- /package/plugins/beta/datasets/src/{panels → components/Key}/Key.module.scss +0 -0
- /package/plugins/beta/datasets/src/components/{svgProperties.js → Key/svgProperties.js} +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { SVG_SIZE, SVG_CENTER, svgProps, svgSymbolProps } from './svgProperties'
|
|
2
|
+
|
|
3
|
+
describe('svgProperties constants', () => {
|
|
4
|
+
it('SVG_SIZE is 20', () => {
|
|
5
|
+
expect(SVG_SIZE).toBe(20)
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
it('SVG_CENTER is half of SVG_SIZE', () => {
|
|
9
|
+
expect(SVG_CENTER).toBe(SVG_SIZE / 2)
|
|
10
|
+
})
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('svgProps', () => {
|
|
14
|
+
it('has the correct xmlns', () => {
|
|
15
|
+
expect(svgProps.xmlns).toBe('http://www.w3.org/2000/svg')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('has width and height equal to SVG_SIZE', () => {
|
|
19
|
+
expect(svgProps.width).toBe(SVG_SIZE)
|
|
20
|
+
expect(svgProps.height).toBe(SVG_SIZE)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('has viewBox covering the full SVG_SIZE', () => {
|
|
24
|
+
expect(svgProps.viewBox).toBe(`0 0 ${SVG_SIZE} ${SVG_SIZE}`)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('has the correct className', () => {
|
|
28
|
+
expect(svgProps.className).toBe('am-c-datasets-key-symbol')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('is aria-hidden', () => {
|
|
32
|
+
expect(svgProps['aria-hidden']).toBe('true')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('is not focusable', () => {
|
|
36
|
+
expect(svgProps.focusable).toBe('false')
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('svgSymbolProps', () => {
|
|
41
|
+
it('inherits xmlns from svgProps', () => {
|
|
42
|
+
expect(svgSymbolProps.xmlns).toBe(svgProps.xmlns)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('has a larger width than svgProps', () => {
|
|
46
|
+
expect(svgSymbolProps.width).toBeGreaterThan(svgProps.width)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('has a larger height than svgProps', () => {
|
|
50
|
+
expect(svgSymbolProps.height).toBeGreaterThan(svgProps.height)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('width and height are equal', () => {
|
|
54
|
+
expect(svgSymbolProps.width).toBe(svgSymbolProps.height)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('className includes the base class and a modifier', () => {
|
|
58
|
+
expect(svgSymbolProps.className).toContain('am-c-datasets-key-symbol')
|
|
59
|
+
expect(svgSymbolProps.className).toContain('--point')
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { setDatasetVisibility } from '../../api/setDatasetVisibility.js'
|
|
3
|
+
import { LayersMenuCheckbox } from './LayersMenuCheckbox.jsx'
|
|
4
|
+
import { LayersMenuGroupWrapper } from './LayersMenuGroupWrapper.jsx'
|
|
5
|
+
|
|
6
|
+
export const LayersMenu = ({ pluginState }) => {
|
|
7
|
+
const { menu = [] } = pluginState
|
|
8
|
+
const handleDatasetChange = (e) => {
|
|
9
|
+
const { value, checked } = e.target
|
|
10
|
+
setDatasetVisibility({ pluginState }, checked, { datasetId: value })
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const hasGroups = menu.some(item => item.groupLabel)
|
|
14
|
+
const containerClass = `im-c-datasets-layers${hasGroups ? ' im-c-datasets-layers--has-groups' : ''}`
|
|
15
|
+
return (
|
|
16
|
+
<div className={containerClass}>
|
|
17
|
+
{// Each menuGroup
|
|
18
|
+
menu.map(menuGroup =>
|
|
19
|
+
<LayersMenuGroupWrapper key={menuGroup.id} menuGroup={menuGroup}>
|
|
20
|
+
{// Each menuGroupItem
|
|
21
|
+
menuGroup.items.map(menuGroupItem =>
|
|
22
|
+
<LayersMenuCheckbox
|
|
23
|
+
key={menuGroupItem.id}
|
|
24
|
+
menuGroupItem={menuGroupItem}
|
|
25
|
+
onChange={handleDatasetChange}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
</LayersMenuGroupWrapper>)
|
|
30
|
+
}
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { datasetRegistry } from '../../registry/datasetRegistry.js'
|
|
2
|
+
|
|
3
|
+
export const LayersMenuCheckbox = ({ menuGroupItem, onChange }) => {
|
|
4
|
+
const registryDataset = datasetRegistry.getDataset(menuGroupItem.id)
|
|
5
|
+
if (!registryDataset) {
|
|
6
|
+
return null
|
|
7
|
+
}
|
|
8
|
+
const datasetId = registryDataset.isSublayer ? registryDataset.parentId : registryDataset.id
|
|
9
|
+
const sublayerId = registryDataset.isSublayer ? registryDataset.id : undefined
|
|
10
|
+
const itemClass = `im-c-datasets-layers__item govuk-checkboxes govuk-checkboxes--small${registryDataset.visible ? '' : ' im-c-datasets-layers__item--checked'}`
|
|
11
|
+
return (
|
|
12
|
+
<div key={registryDataset.id} className={itemClass} data-module='govuk-checkboxes'>
|
|
13
|
+
<div className='govuk-checkboxes__item'>
|
|
14
|
+
<input
|
|
15
|
+
className='govuk-checkboxes__input'
|
|
16
|
+
id={registryDataset.id}
|
|
17
|
+
data-dataset-id={datasetId}
|
|
18
|
+
data-sublayer-id={sublayerId}
|
|
19
|
+
name='layers'
|
|
20
|
+
type='checkbox'
|
|
21
|
+
value={registryDataset.id}
|
|
22
|
+
checked={registryDataset.isLocallyVisible}
|
|
23
|
+
onChange={onChange}
|
|
24
|
+
/>
|
|
25
|
+
<label className='im-c-datasets-layers__item-label govuk-label govuk-checkboxes__label' htmlFor={registryDataset.id}>
|
|
26
|
+
{registryDataset.label}
|
|
27
|
+
</label>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const LayersMenuGroupWrapper = ({ menuGroup, children }) => {
|
|
2
|
+
if (!menuGroup.groupLabel) {
|
|
3
|
+
return <>{children}</>
|
|
4
|
+
}
|
|
5
|
+
// TODO - is there any need for im-c-datasets-layers-group--items-checked - it isn't used anywhere
|
|
6
|
+
// const anyItemsChecked = false
|
|
7
|
+
// const wrapperClass = `govuk-form-group im-c-datasets-layers-group${anyItemsChecked ? ' im-c-datasets-layers-group--items-checked' : ''}`
|
|
8
|
+
const wrapperClass = 'govuk-form-group im-c-datasets-layers-group'
|
|
9
|
+
return (
|
|
10
|
+
<div key={menuGroup.id} className={wrapperClass}>
|
|
11
|
+
<fieldset className='im-c-datasets-layers-group__fieldset'>
|
|
12
|
+
<legend className='im-c-datasets-layers-group__legend'>
|
|
13
|
+
{menuGroup.groupLabel}
|
|
14
|
+
</legend>
|
|
15
|
+
{children}
|
|
16
|
+
</fieldset>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -8,14 +8,14 @@ const EVICTION_THRESHOLD = 1.2 // Trigger eviction at 120% of maxFeatures
|
|
|
8
8
|
/**
|
|
9
9
|
* Create a dynamic GeoJSON source that fetches data based on viewport
|
|
10
10
|
* @param {Object} options
|
|
11
|
-
* @param {Object} options.
|
|
11
|
+
* @param {Object} options.registryDataset - registryDataset instance
|
|
12
12
|
* @param {Object} options.map - Map instance
|
|
13
13
|
* @param {string} options.sourceId - Source ID for the map
|
|
14
14
|
* @param {Function} options.onUpdate - Callback when source data should be updated
|
|
15
15
|
* @returns {Object} { destroy, clear, refresh }
|
|
16
16
|
*/
|
|
17
|
-
export const createDynamicSource = ({
|
|
18
|
-
const {
|
|
17
|
+
export const createDynamicSource = ({ dynamicGeoJSON, map, onUpdate }) => {
|
|
18
|
+
const { url: baseUrl, idProperty, transformRequest, maxFeatures, minZoom = 0 } = dynamicGeoJSON
|
|
19
19
|
|
|
20
20
|
// Feature cache: id → { feature, bbox, lastSeenAt }
|
|
21
21
|
const features = new Map()
|
|
@@ -112,7 +112,7 @@ export const createDynamicSource = ({ dataset, map, onUpdate }) => {
|
|
|
112
112
|
currentController = new AbortController()
|
|
113
113
|
|
|
114
114
|
try {
|
|
115
|
-
const context = { bbox: currentBbox, zoom
|
|
115
|
+
const context = { bbox: currentBbox, zoom }
|
|
116
116
|
const data = await fetchGeoJSON(baseUrl, context, transformRequest, currentController.signal)
|
|
117
117
|
|
|
118
118
|
const now = Date.now()
|
|
@@ -144,12 +144,12 @@ export const createDynamicSource = ({ dataset, map, onUpdate }) => {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// Update map source
|
|
147
|
-
onUpdate(
|
|
147
|
+
onUpdate(dynamicGeoJSON.id, toFeatureCollection())
|
|
148
148
|
} catch (error) {
|
|
149
149
|
if (error.name === 'AbortError') {
|
|
150
150
|
return
|
|
151
151
|
}
|
|
152
|
-
console.error(`Failed to fetch dynamic GeoJSON for ${
|
|
152
|
+
console.error(`Failed to fetch dynamic GeoJSON for ${dynamicGeoJSON.id}:`, error)
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -184,7 +184,7 @@ export const createDynamicSource = ({ dataset, map, onUpdate }) => {
|
|
|
184
184
|
clear () {
|
|
185
185
|
features.clear()
|
|
186
186
|
fetchedBbox = null
|
|
187
|
-
onUpdate(
|
|
187
|
+
onUpdate(dynamicGeoJSON.id, { type: 'FeatureCollection', features: [] })
|
|
188
188
|
},
|
|
189
189
|
|
|
190
190
|
/**
|
|
@@ -208,7 +208,7 @@ export const createDynamicSource = ({ dataset, map, onUpdate }) => {
|
|
|
208
208
|
*/
|
|
209
209
|
reapply () {
|
|
210
210
|
if (features.size > 0) {
|
|
211
|
-
onUpdate(
|
|
211
|
+
onUpdate(dynamicGeoJSON.id, toFeatureCollection())
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// src/plugins/datasets/datasetsInit.jsx
|
|
2
2
|
import { useEffect, useRef } from 'react'
|
|
3
|
-
import { EVENTS } from '
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { EVENTS } from '../../../../../src/config/events.js'
|
|
4
|
+
import { initialiseDatasets } from './initialiseDatasets.js'
|
|
5
|
+
import { datasetRegistry } from '../registry/datasetRegistry.js'
|
|
6
|
+
import { attachGlobalState } from '../registry/globalDataset.js'
|
|
7
|
+
import { loadLayerAdapter, layerAdapter } from '../adapters/loadLayerAdapter.js'
|
|
6
8
|
|
|
7
9
|
export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, mapProvider, services }) {
|
|
8
10
|
const { dispatch } = pluginState
|
|
9
11
|
const { eventBus, symbolRegistry, patternRegistry } = services
|
|
10
|
-
|
|
11
12
|
const isMapStyleReady = !!mapProvider.map?.getStyle()
|
|
12
13
|
|
|
13
14
|
// Keep a ref to the latest pluginState so event handlers can access current data
|
|
@@ -30,28 +31,17 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
|
|
|
30
31
|
return
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
// Only initialise state if not already set
|
|
34
|
-
if (!pluginState.datasets) {
|
|
35
|
-
dispatch({ type: 'SET_DATASETS', payload: { datasets: pluginConfig.datasets, datasetDefaults } })
|
|
36
|
-
}
|
|
37
|
-
|
|
38
34
|
const initDatasets = async () => {
|
|
39
|
-
|
|
40
|
-
throw new Error('datasets plugin: no layerAdapter provided. Import and pass maplibreLayerAdapter or a custom adapter.')
|
|
41
|
-
}
|
|
35
|
+
const adapter = await loadLayerAdapter(mapProvider, symbolRegistry, patternRegistry)
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
const adapter = new LayerAdapter(mapProvider, symbolRegistry, patternRegistry)
|
|
45
|
-
|
|
46
|
-
dispatch({ type: 'SET_LAYER_ADAPTER', payload: adapter })
|
|
47
|
-
|
|
48
|
-
datasetsInstanceRef.current = createDatasets({
|
|
37
|
+
datasetsInstanceRef.current = initialiseDatasets({
|
|
49
38
|
adapter,
|
|
50
39
|
pluginConfig,
|
|
51
40
|
pluginStateRef,
|
|
52
41
|
mapStyle: mapState.mapStyle,
|
|
53
42
|
mapProvider,
|
|
54
43
|
events: EVENTS,
|
|
44
|
+
dispatch,
|
|
55
45
|
eventBus
|
|
56
46
|
})
|
|
57
47
|
}
|
|
@@ -59,9 +49,18 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
|
|
|
59
49
|
initDatasets()
|
|
60
50
|
}, [isMapStyleReady, appState.mode])
|
|
61
51
|
|
|
52
|
+
useEffect(() => datasetRegistry.attach(pluginState.mappedDatasets, pluginState.orderedDatasets),
|
|
53
|
+
[pluginState.mappedDatasets, pluginState.orderedDatasets])
|
|
54
|
+
|
|
55
|
+
useEffect(() => attachGlobalState(pluginState.globals), [pluginState.globals])
|
|
56
|
+
|
|
57
|
+
// Call layerAdapter methods that are queued from state updates
|
|
62
58
|
useEffect(() => {
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
const { actionsArray } = pluginState
|
|
60
|
+
if (!actionsArray.length) { return }
|
|
61
|
+
actionsArray.forEach(({ method, parameters }) => layerAdapter[method](...parameters))
|
|
62
|
+
dispatch({ type: 'REMOVE_ADAPTER_ACTIONS', payload: actionsArray })
|
|
63
|
+
}, [pluginState.actionsArray])
|
|
65
64
|
|
|
66
65
|
// Cleanup only on unmount
|
|
67
66
|
useEffect(() => {
|
|
@@ -70,7 +69,6 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
|
|
|
70
69
|
datasetsInstanceRef.current.remove()
|
|
71
70
|
datasetsInstanceRef.current = null
|
|
72
71
|
}
|
|
73
|
-
dispatch({ type: 'SET_LAYER_ADAPTER', payload: null })
|
|
74
72
|
}
|
|
75
73
|
}, [])
|
|
76
74
|
|
|
@@ -3,7 +3,7 @@ const datasetDefaults = {
|
|
|
3
3
|
maxZoom: 24,
|
|
4
4
|
showInKey: false,
|
|
5
5
|
showInMenu: false,
|
|
6
|
-
|
|
6
|
+
visible: true,
|
|
7
7
|
style: {
|
|
8
8
|
stroke: '#d4351c',
|
|
9
9
|
strokeWidth: 2,
|
|
@@ -48,4 +48,19 @@ const applyDatasetDefaults = (dataset, defaults) => {
|
|
|
48
48
|
return { ...topLevelDefaults, ...topLevel, ...mergedStyle }
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
const applyDatasetDefaultsWithoutFlattening = (dataset) => {
|
|
52
|
+
// Allow for existing configs that use visibility instead of visible, but default to visible if neither is set
|
|
53
|
+
if (dataset.visible !== true && dataset.visible !== false) {
|
|
54
|
+
dataset.visible = dataset.visibility !== 'hidden'
|
|
55
|
+
}
|
|
56
|
+
const datasetWithDefaults = { ...datasetDefaults, ...dataset, style: { ...datasetDefaults.style, ...dataset.style } }
|
|
57
|
+
|
|
58
|
+
const style = dataset.style || {}
|
|
59
|
+
if (!('symbolDescription' in style) && hasCustomVisualStyle(style)) {
|
|
60
|
+
delete datasetWithDefaults.style.symbolDescription
|
|
61
|
+
}
|
|
62
|
+
STYLE_PROPS.forEach(prop => delete datasetWithDefaults[prop])
|
|
63
|
+
return datasetWithDefaults
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { datasetDefaults, hasCustomVisualStyle, applyDatasetDefaults, applyDatasetDefaultsWithoutFlattening }
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { datasetDefaults, hasCustomVisualStyle, applyDatasetDefaults } from './defaults'
|
|
2
|
+
|
|
3
|
+
describe('datasetDefaults', () => {
|
|
4
|
+
it('has expected top-level defaults', () => {
|
|
5
|
+
expect(datasetDefaults).toMatchObject({
|
|
6
|
+
minZoom: 6,
|
|
7
|
+
maxZoom: 24,
|
|
8
|
+
showInKey: false,
|
|
9
|
+
showInMenu: false,
|
|
10
|
+
visible: true
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('has expected style defaults', () => {
|
|
15
|
+
expect(datasetDefaults.style).toMatchObject({
|
|
16
|
+
stroke: '#d4351c',
|
|
17
|
+
strokeWidth: 2,
|
|
18
|
+
fill: 'transparent',
|
|
19
|
+
symbolDescription: 'red outline'
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('hasCustomVisualStyle', () => {
|
|
25
|
+
it('returns true when stroke is present', () => {
|
|
26
|
+
expect(hasCustomVisualStyle({ stroke: '#ff0000' })).toBe(true)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('returns true when fill is present', () => {
|
|
30
|
+
expect(hasCustomVisualStyle({ fill: 'blue' })).toBe(true)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('returns true when fillPattern is present', () => {
|
|
34
|
+
expect(hasCustomVisualStyle({ fillPattern: 'dots' })).toBe(true)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('returns true when fillPatternSvgContent is present', () => {
|
|
38
|
+
expect(hasCustomVisualStyle({ fillPatternSvgContent: '<svg/>' })).toBe(true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('returns true when symbol is present', () => {
|
|
42
|
+
expect(hasCustomVisualStyle({ symbol: 'marker' })).toBe(true)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('returns true when symbolSvgContent is present', () => {
|
|
46
|
+
expect(hasCustomVisualStyle({ symbolSvgContent: '<svg/>' })).toBe(true)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('returns false when no visual style props are present', () => {
|
|
50
|
+
expect(hasCustomVisualStyle({ strokeWidth: 2, opacity: 0.5 })).toBe(false)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('returns false for an empty style object', () => {
|
|
54
|
+
expect(hasCustomVisualStyle({})).toBe(false)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('applyDatasetDefaults', () => {
|
|
59
|
+
const defaults = {
|
|
60
|
+
minZoom: 6,
|
|
61
|
+
maxZoom: 24,
|
|
62
|
+
showInKey: false,
|
|
63
|
+
style: {
|
|
64
|
+
stroke: '#d4351c',
|
|
65
|
+
strokeWidth: 2,
|
|
66
|
+
symbolDescription: 'red outline'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
it('merges top-level dataset properties over defaults', () => {
|
|
71
|
+
const dataset = { id: 'test', minZoom: 10 }
|
|
72
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
73
|
+
expect(result.minZoom).toBe(10)
|
|
74
|
+
expect(result.maxZoom).toBe(24)
|
|
75
|
+
expect(result.showInKey).toBe(false)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('flattens style properties into the result', () => {
|
|
79
|
+
const dataset = { id: 'test', style: { fill: 'transparent' } }
|
|
80
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
81
|
+
expect(result.fill).toBe('transparent')
|
|
82
|
+
expect(result.stroke).toBe('#d4351c')
|
|
83
|
+
expect(result.style).toBeUndefined()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('dataset style properties override default style properties', () => {
|
|
87
|
+
const dataset = { id: 'test', style: { stroke: '#0000ff' } }
|
|
88
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
89
|
+
expect(result.stroke).toBe('#0000ff')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('drops symbolDescription from defaults when custom visual style is present and no explicit symbolDescription', () => {
|
|
93
|
+
const dataset = { id: 'test', style: { stroke: '#0000ff' } }
|
|
94
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
95
|
+
expect(result.symbolDescription).toBeUndefined()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('keeps symbolDescription when dataset provides its own, even with custom visual style', () => {
|
|
99
|
+
const dataset = { id: 'test', style: { stroke: '#0000ff', symbolDescription: 'blue outline' } }
|
|
100
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
101
|
+
expect(result.symbolDescription).toBe('blue outline')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('keeps symbolDescription from defaults when no custom visual style is present', () => {
|
|
105
|
+
const dataset = { id: 'test', style: { strokeWidth: 4 } }
|
|
106
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
107
|
+
expect(result.symbolDescription).toBe('red outline')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('keeps symbolDescription from defaults when dataset has no style', () => {
|
|
111
|
+
const dataset = { id: 'test' }
|
|
112
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
113
|
+
expect(result.symbolDescription).toBe('red outline')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('ignores style properties set at top level of dataset', () => {
|
|
117
|
+
const dataset = { id: 'test', stroke: '#ignored' }
|
|
118
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
119
|
+
expect(result.stroke).toBe('#d4351c')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('does not include style key in the result', () => {
|
|
123
|
+
const dataset = { id: 'test', style: { fill: 'blue' } }
|
|
124
|
+
const result = applyDatasetDefaults(dataset, defaults)
|
|
125
|
+
expect(result).not.toHaveProperty('style')
|
|
126
|
+
})
|
|
127
|
+
})
|
|
@@ -1,71 +1,75 @@
|
|
|
1
|
-
import { createDynamicSource } from '
|
|
1
|
+
import { createDynamicSource } from '../fetch/createDynamicSource.js'
|
|
2
2
|
// NOSONAR: applyDatasetDefaults and datasetDefaults are used in processedDatasets.map
|
|
3
3
|
import { applyDatasetDefaults, datasetDefaults } from './defaults.js'
|
|
4
|
+
import { mappedDatasetsReducer } from '../reducers/mappedDatasetsReducer.js'
|
|
5
|
+
import { datasetRegistry } from '../registry/datasetRegistry.js'
|
|
4
6
|
|
|
5
|
-
const
|
|
6
|
-
typeof dataset.geojson === 'string' &&
|
|
7
|
-
!!dataset.idProperty &&
|
|
8
|
-
typeof dataset.transformRequest === 'function'
|
|
9
|
-
|
|
10
|
-
export const createDatasets = ({
|
|
7
|
+
export const initialiseDatasets = ({
|
|
11
8
|
adapter,
|
|
12
9
|
pluginConfig,
|
|
13
10
|
pluginStateRef,
|
|
14
11
|
mapStyle,
|
|
15
12
|
mapProvider,
|
|
16
13
|
events,
|
|
14
|
+
dispatch,
|
|
17
15
|
eventBus
|
|
18
16
|
}) => {
|
|
19
17
|
const { datasets } = pluginConfig
|
|
20
|
-
|
|
21
18
|
const dynamicSources = new Map()
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
if (pluginConfig.globals) {
|
|
21
|
+
dispatch({ type: 'SET_GLOBAL_STATE', payload: pluginConfig.globals })
|
|
22
|
+
}
|
|
25
23
|
|
|
26
24
|
// Initialise all datasets via the adapter, then set up dynamic sources
|
|
27
25
|
const processedDatasets = datasets.map(d => applyDatasetDefaults(d, datasetDefaults))
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
const { mappedDatasets, orderedDatasets } = mappedDatasetsReducer({ datasets })
|
|
27
|
+
if (adapter.createDataset) {
|
|
28
|
+
datasetRegistry.attachCreateDataset(adapter.createDataset)
|
|
29
|
+
}
|
|
30
|
+
datasetRegistry.attach(mappedDatasets)
|
|
31
|
+
adapter.init(mapStyle).then(() => {
|
|
32
|
+
datasetRegistry.forEachDataset(registryDataset => {
|
|
33
|
+
if (!registryDataset.hasDynamicGeoJSON) {
|
|
31
34
|
return
|
|
32
35
|
}
|
|
33
|
-
|
|
36
|
+
const { dynamicGeoJSON } = registryDataset
|
|
34
37
|
const dynamicSource = createDynamicSource({
|
|
35
|
-
|
|
38
|
+
dynamicGeoJSON,
|
|
36
39
|
map: mapProvider.map,
|
|
37
40
|
onUpdate: (datasetId, geojson) => adapter.setData(datasetId, geojson)
|
|
38
41
|
})
|
|
39
|
-
dynamicSources.set(
|
|
42
|
+
dynamicSources.set(registryDataset.id, dynamicSource)
|
|
40
43
|
})
|
|
41
|
-
|
|
44
|
+
// TODO - apply dynamic source defaults here, and include in mappedDatasets
|
|
45
|
+
dispatch({ type: 'SET_DATASETS', payload: { datasets: processedDatasets, mappedDatasets, orderedDatasets } })
|
|
42
46
|
eventBus.emit('datasets:ready')
|
|
43
47
|
})
|
|
44
48
|
|
|
45
49
|
let currentMapStyle = mapStyle
|
|
46
50
|
|
|
47
51
|
// Handle basemap style changes — delegate entirely to the adapter
|
|
48
|
-
const
|
|
52
|
+
const onSetMapStyle = (newMapStyle) => {
|
|
49
53
|
currentMapStyle = newMapStyle
|
|
50
|
-
adapter.
|
|
54
|
+
adapter.onMapStyleChange(newMapStyle, dynamicSources)
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
const
|
|
54
|
-
adapter.
|
|
57
|
+
const onMapSizeChange = () => {
|
|
58
|
+
adapter.onMapSizeChange(currentMapStyle)
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
eventBus.on(events.MAP_SET_STYLE,
|
|
58
|
-
eventBus.on(events.MAP_SIZE_CHANGE,
|
|
61
|
+
eventBus.on(events.MAP_SET_STYLE, onSetMapStyle)
|
|
62
|
+
eventBus.on(events.MAP_SIZE_CHANGE, onMapSizeChange)
|
|
59
63
|
|
|
60
64
|
return {
|
|
61
65
|
remove () {
|
|
62
|
-
eventBus.off(events.MAP_SET_STYLE,
|
|
63
|
-
eventBus.off(events.MAP_SIZE_CHANGE,
|
|
66
|
+
eventBus.off(events.MAP_SET_STYLE, onSetMapStyle)
|
|
67
|
+
eventBus.off(events.MAP_SIZE_CHANGE, onMapSizeChange)
|
|
64
68
|
|
|
65
69
|
// Clean up dynamic sources
|
|
66
70
|
dynamicSources.forEach(source => source.destroy())
|
|
67
71
|
dynamicSources.clear()
|
|
68
|
-
adapter.destroy(
|
|
72
|
+
adapter.destroy()
|
|
69
73
|
},
|
|
70
74
|
|
|
71
75
|
/**
|