@defra/interactive-map 0.0.50-alpha → 0.0.51-apha
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/esm/im-core.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/docs/api.md +19 -0
- package/docs/architecture.md +14 -0
- package/docs/getting-started.md +10 -0
- package/docs/plugins/datasets.md +9 -3
- package/jest.setup.js +8 -0
- package/package.json +8 -3
- package/plugins/datasets/dist/esm/im-datasets-ml-adapter.js +1 -1
- package/plugins/datasets/dist/esm/im-datasets-ol-adapter.js +1 -0
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-ml-adapter.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-ol-adapter.js +2 -0
- package/plugins/datasets/dist/umd/im-datasets-ol-adapter.js.LICENSE.txt +1 -0
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/index.js +1 -1
- package/plugins/datasets/src/adapters/loadLayerAdapter.js +7 -3
- package/plugins/datasets/src/adapters/loadLayerAdapter.test.js +17 -0
- package/plugins/datasets/src/adapters/mapboxStyleLayerAdapter.js +74 -0
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +14 -79
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +4 -0
- package/plugins/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +3 -89
- package/plugins/datasets/src/adapters/openlayers/canvasPatternStyle.js +111 -0
- package/plugins/datasets/src/adapters/openlayers/canvasPatternStyle.test.js +198 -0
- package/plugins/datasets/src/adapters/openlayers/layerBuilders.js +205 -0
- package/plugins/datasets/src/adapters/openlayers/layerBuilders.test.js +233 -0
- package/plugins/datasets/src/adapters/openlayers/openlayersLayerAdapter.js +276 -0
- package/plugins/datasets/src/adapters/openlayers/openlayersLayerAdapter.test.js +431 -0
- package/plugins/datasets/src/adapters/openlayers/registry/openLayersDataset.js +172 -0
- package/plugins/datasets/src/adapters/openlayers/registry/openLayersDataset.test.js +262 -0
- package/plugins/datasets/src/fetch/createDynamicSource.js +12 -8
- package/plugins/datasets/src/fetch/createDynamicSource.test.js +61 -52
- package/plugins/datasets/src/initialise/DatasetsInit.jsx +13 -1
- package/plugins/datasets/src/initialise/DatasetsInit.test.jsx +20 -0
- package/plugins/datasets/src/initialise/initialiseDatasets.js +1 -1
- package/plugins/datasets/src/initialise/initialiseDatasets.test.js +1 -1
- package/plugins/datasets/src/registry/mapboxStyleDataset.js +108 -0
- package/plugins/datasets/src/registry/mapboxStyleDataset.test.js +240 -0
- package/plugins/datasets/src/utils/bbox.js +5 -7
- package/plugins/datasets/src/utils/bbox.test.js +3 -10
- package/plugins/draw/dist/css/index.css +1 -0
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/umd/im-draw-plugin.js +1 -1
- package/plugins/draw/src/index.js +2 -0
- package/plugins/draw/src/index.test.js +1 -0
- package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -1
- package/plugins/map-key/dist/umd/im-map-key-plugin.js +1 -1
- 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/dist/umd/index.js +1 -1
- package/providers/beta/openlayers/src/index.js +16 -0
- package/providers/beta/openlayers/src/openlayersProvider.js +160 -7
- package/providers/beta/openlayers/src/openlayersProvider.test.js +13 -0
- package/providers/beta/openlayers/src/utils/bngProjection.js +9 -0
- package/providers/beta/openlayers/src/utils/filterEvaluator.js +32 -0
- package/providers/beta/openlayers/src/utils/filterEvaluator.test.js +36 -0
- package/providers/beta/openlayers/src/utils/highlightFeatures.js +38 -23
- package/providers/beta/openlayers/src/utils/highlightFeatures.test.js +153 -8
- package/providers/beta/openlayers/src/utils/hoverCursor.js +5 -1
- package/providers/beta/openlayers/src/utils/hoverCursor.test.js +40 -0
- package/providers/beta/openlayers/src/utils/queryFeatures.js +72 -54
- package/providers/beta/openlayers/src/utils/queryFeatures.test.js +143 -1
- package/providers/beta/openlayers/src/utils/symbolImages.js +79 -5
- package/providers/beta/openlayers/src/utils/symbolImages.test.js +111 -1
- package/providers/beta/openlayers/src/utils/vtTileFragments.js +20 -8
- package/providers/beta/openlayers/src/utils/vtTileFragments.test.js +162 -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/dist/umd/index.js +1 -1
- package/providers/maplibre/src/utils/patternImages.js +4 -44
- package/rollup.esm.mjs +2 -0
- package/src/services/patternRegistry.js +38 -0
- package/src/services/patternRegistry.test.js +61 -0
- package/src/types.js +11 -0
- package/webpack.dev.mjs +1 -0
- /package/plugins/datasets/dist/esm/{esriLayerAdapter.js → im-datasets-esri-adapter.js} +0 -0
|
@@ -1,50 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getValueForStyle } from '../../../../src/utils/getValueForStyle.js'
|
|
3
|
-
import { rasteriseToImageData } from '../../../../src/utils/rasteriseToImageData.js'
|
|
4
|
-
|
|
5
|
-
// Module-level cache: imageId → ImageData. Avoids re-rasterising identical patterns.
|
|
6
|
-
const imageDataCache = new Map()
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Rasterises a dataset's pattern SVG to ImageData, using an in-memory cache
|
|
10
|
-
* to avoid re-rasterising identical patterns.
|
|
11
|
-
*
|
|
12
|
-
* @param {Object} dataset
|
|
13
|
-
* @param {string} mapStyleId
|
|
14
|
-
* @param {Object} patternRegistry
|
|
15
|
-
* @param {number} pixelRatio
|
|
16
|
-
* @returns {Promise<{ imageId: string, imageData: ImageData }|null>}
|
|
17
|
-
*/
|
|
18
|
-
const rasterisePattern = async (dataset, mapStyleId, patternRegistry, pixelRatio) => {
|
|
19
|
-
const innerContent = patternRegistry.getPatternInnerContent(dataset)
|
|
20
|
-
if (!innerContent) {
|
|
21
|
-
return null
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const imageId = patternRegistry.getPatternImageId(dataset, mapStyleId, pixelRatio)
|
|
25
|
-
if (!imageId) {
|
|
26
|
-
return null
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let imageData = imageDataCache.get(imageId)
|
|
30
|
-
if (!imageData) {
|
|
31
|
-
const fg = getValueForStyle(dataset.fillPatternForegroundColor, mapStyleId) || 'black'
|
|
32
|
-
const bg = getValueForStyle(dataset.fillPatternBackgroundColor, mapStyleId) || 'transparent'
|
|
33
|
-
const colored = injectColors(innerContent, fg, bg)
|
|
34
|
-
const bgRect = `<rect width="16" height="16" fill="${bg}"/>`
|
|
35
|
-
const effectiveRatio = getEffectivePixelRatio(pixelRatio)
|
|
36
|
-
const physicalSize = Math.round(8 * effectiveRatio)
|
|
37
|
-
const svgString = `<svg xmlns="http://www.w3.org/2000/svg" width="${physicalSize}" height="${physicalSize}" viewBox="0 0 16 16">${bgRect}${colored}</svg>`
|
|
38
|
-
imageData = await rasteriseToImageData(svgString, physicalSize, physicalSize)
|
|
39
|
-
imageDataCache.set(imageId, imageData)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return { imageId, imageData }
|
|
43
|
-
}
|
|
1
|
+
import { getEffectivePixelRatio } from '../../../../src/utils/patternUtils.js'
|
|
44
2
|
|
|
45
3
|
/**
|
|
46
4
|
* Register pattern images for the given pre-resolved pattern configs.
|
|
47
5
|
* Skips images that are already registered (safe to call on style change).
|
|
6
|
+
* Rasterisation itself lives in patternRegistry.rasterisePatternImage, shared with OpenLayers —
|
|
7
|
+
* this only owns the MapLibre-specific "last mile" of registering via map.addImage().
|
|
48
8
|
*
|
|
49
9
|
* @param {Object} map - MapLibre map instance
|
|
50
10
|
* @param {Object[]} styleArray - an array of pattern style configs
|
|
@@ -64,7 +24,7 @@ export const addPatternsToMap = async (map, styleArray, mapStyleId, patternRegis
|
|
|
64
24
|
const imageId = patternRegistry.getPatternImageId(style, mapStyleId, pixelRatio)
|
|
65
25
|
if (imageId && !imagesToAdd[imageId] && !map.hasImage(imageId)) {
|
|
66
26
|
imagesToAdd[imageId] = async () => {
|
|
67
|
-
const result = await
|
|
27
|
+
const result = await patternRegistry.rasterisePatternImage(style, mapStyleId, pixelRatio)
|
|
68
28
|
if (result && !map.hasImage(result.imageId)) {
|
|
69
29
|
map.addImage(result.imageId, result.imageData, { pixelRatio: effectiveRatio })
|
|
70
30
|
}
|
package/rollup.esm.mjs
CHANGED
|
@@ -265,6 +265,8 @@ const ALL_BUILDS = [
|
|
|
265
265
|
manualChunks: (id) => {
|
|
266
266
|
if (id.includes('/manifest')) { return 'im-datasets-plugin' }
|
|
267
267
|
if (id.includes('maplibreLayerAdapter')) { return 'im-datasets-ml-adapter' }
|
|
268
|
+
if (id.includes('openlayersLayerAdapter')) { return 'im-datasets-ol-adapter' }
|
|
269
|
+
if (id.includes('esriLayerAdapter')) { return 'im-datasets-esri-adapter' }
|
|
268
270
|
return undefined
|
|
269
271
|
}
|
|
270
272
|
},
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { BUILT_IN_PATTERNS } from '../config/patternConfig.js'
|
|
2
2
|
import { getValueForStyle } from '../utils/getValueForStyle.js'
|
|
3
3
|
import { KEY_BORDER_PATH, getEffectivePixelRatio, injectColors, hashString } from '../utils/patternUtils.js'
|
|
4
|
+
import { rasteriseToImageData } from '../utils/rasteriseToImageData.js'
|
|
4
5
|
const patterns = new Map()
|
|
6
|
+
// Module-level cache: imageId → ImageData. Avoids re-rasterising identical patterns.
|
|
7
|
+
const imageDataCache = new Map()
|
|
5
8
|
|
|
6
9
|
export const patternRegistry = {
|
|
7
10
|
/**
|
|
@@ -106,6 +109,41 @@ export const patternRegistry = {
|
|
|
106
109
|
const bg = getValueForStyle(dataset.fillPatternBackgroundColor, mapStyleId) || 'transparent'
|
|
107
110
|
const effectiveRatio = getEffectivePixelRatio(pixelRatio)
|
|
108
111
|
return `pattern-${hashString(innerContent + fg + bg)}-${effectiveRatio}x`
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Rasterise a pattern to ImageData, cached by imageId — shared by MapLibre's map.addImage()
|
|
116
|
+
* and OpenLayers' CanvasPattern (canvasPatternStyle.js). Note: the two currently render the
|
|
117
|
+
* pattern tile at different on-screen sizes (8px vs 16px CSS) — not yet confirmed if that
|
|
118
|
+
* needs aligning.
|
|
119
|
+
*
|
|
120
|
+
* @param {Object} style - Dataset or marker config with fillPattern* properties
|
|
121
|
+
* @param {string} mapStyleId - Current style/theme identifier
|
|
122
|
+
* @param {number} [pixelRatio=1] - Device pixel ratio × map size scale factor
|
|
123
|
+
* @returns {Promise<{imageId: string, imageData: ImageData}|null>}
|
|
124
|
+
*/
|
|
125
|
+
async rasterisePatternImage (style, mapStyleId, pixelRatio = 1) {
|
|
126
|
+
const innerContent = this.getPatternInnerContent(style)
|
|
127
|
+
if (!innerContent) {
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
const imageId = this.getPatternImageId(style, mapStyleId, pixelRatio)
|
|
131
|
+
if (!imageId) {
|
|
132
|
+
return null
|
|
133
|
+
}
|
|
134
|
+
let imageData = imageDataCache.get(imageId)
|
|
135
|
+
if (!imageData) {
|
|
136
|
+
const fg = getValueForStyle(style.fillPatternForegroundColor, mapStyleId) || 'black'
|
|
137
|
+
const bg = getValueForStyle(style.fillPatternBackgroundColor, mapStyleId) || 'transparent'
|
|
138
|
+
const colored = injectColors(innerContent, fg, bg)
|
|
139
|
+
const bgRect = `<rect width="16" height="16" fill="${bg}"/>`
|
|
140
|
+
const effectiveRatio = getEffectivePixelRatio(pixelRatio)
|
|
141
|
+
const physicalSize = Math.round(8 * effectiveRatio)
|
|
142
|
+
const svgString = `<svg xmlns="http://www.w3.org/2000/svg" width="${physicalSize}" height="${physicalSize}" viewBox="0 0 16 16">${bgRect}${colored}</svg>`
|
|
143
|
+
imageData = await rasteriseToImageData(svgString, physicalSize, physicalSize)
|
|
144
|
+
imageDataCache.set(imageId, imageData)
|
|
145
|
+
}
|
|
146
|
+
return { imageId, imageData }
|
|
109
147
|
}
|
|
110
148
|
}
|
|
111
149
|
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { patternRegistry } from './patternRegistry.js'
|
|
2
2
|
import { KEY_BORDER_PATH } from '../utils/patternUtils.js'
|
|
3
|
+
|
|
4
|
+
beforeAll(() => {
|
|
5
|
+
globalThis.Image = class {
|
|
6
|
+
constructor (w, h) {
|
|
7
|
+
this.width = w
|
|
8
|
+
this.height = h
|
|
9
|
+
this._src = ''
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get src () { return this._src }
|
|
13
|
+
set src (val) { this._src = val; this.onload?.() }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
HTMLCanvasElement.prototype.getContext = jest.fn(() => ({
|
|
17
|
+
drawImage: jest.fn(),
|
|
18
|
+
getImageData: jest.fn((_x, _y, w, h) => ({ width: w, height: h }))
|
|
19
|
+
}))
|
|
20
|
+
})
|
|
21
|
+
|
|
3
22
|
describe('patternRegistry', () => {
|
|
4
23
|
describe('built-in patterns', () => {
|
|
5
24
|
test.each([
|
|
@@ -156,4 +175,46 @@ describe('patternRegistry', () => {
|
|
|
156
175
|
expect(id).toBe(idExplicit)
|
|
157
176
|
})
|
|
158
177
|
})
|
|
178
|
+
|
|
179
|
+
describe('rasterisePatternImage', () => {
|
|
180
|
+
beforeEach(() => {
|
|
181
|
+
patternRegistry.register('rasterise-test', '<path d="M0 0 L8 8"/>')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
test('returns null when the pattern has no inner content', async () => {
|
|
185
|
+
const result = await patternRegistry.rasterisePatternImage({ fillPattern: 'unknown' }, 'style-a')
|
|
186
|
+
expect(result).toBeNull()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('returns null when getPatternImageId cannot resolve an id even though inner content exists', async () => {
|
|
190
|
+
// Defensive: getPatternImageId re-derives its own innerContent internally, so this
|
|
191
|
+
// shouldn't diverge from the check above in practice — but a consumer (see
|
|
192
|
+
// providers/maplibre/src/utils/patternImages.test.js's equivalent case) still relies on
|
|
193
|
+
// this guard rather than assuming the two calls can never disagree.
|
|
194
|
+
const getPatternImageId = jest.spyOn(patternRegistry, 'getPatternImageId').mockReturnValue(null)
|
|
195
|
+
const result = await patternRegistry.rasterisePatternImage({ fillPattern: 'rasterise-test' }, 'style-a')
|
|
196
|
+
expect(result).toBeNull()
|
|
197
|
+
getPatternImageId.mockRestore()
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('returns imageId and imageData for a valid pattern', async () => {
|
|
201
|
+
const result = await patternRegistry.rasterisePatternImage({ fillPattern: 'rasterise-test' }, 'style-a')
|
|
202
|
+
expect(result.imageId).toBe(patternRegistry.getPatternImageId({ fillPattern: 'rasterise-test' }, 'style-a'))
|
|
203
|
+
expect(result.imageData).toBeDefined()
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
test('rasterises at 8 * effectivePixelRatio physical pixels', async () => {
|
|
207
|
+
const result = await patternRegistry.rasterisePatternImage({ fillPattern: 'rasterise-test' }, 'style-a', 2)
|
|
208
|
+
// effectivePixelRatio(2) = max(2, 2*2) = 4 -> 8 * 4 = 32
|
|
209
|
+
expect(result.imageData.width).toBe(32)
|
|
210
|
+
expect(result.imageData.height).toBe(32)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('returns the same cached ImageData object on a second call with identical inputs', async () => {
|
|
214
|
+
const style = { fillPattern: 'rasterise-test', fillPatternForegroundColor: '#unique-cache-key' }
|
|
215
|
+
const first = await patternRegistry.rasterisePatternImage(style, 'style-a')
|
|
216
|
+
const second = await patternRegistry.rasterisePatternImage(style, 'style-a')
|
|
217
|
+
expect(second.imageData).toBe(first.imageData)
|
|
218
|
+
})
|
|
219
|
+
})
|
|
159
220
|
})
|
package/src/types.js
CHANGED
|
@@ -356,6 +356,17 @@
|
|
|
356
356
|
* inline blob, so your CSP only needs `worker-src 'self'`.
|
|
357
357
|
*/
|
|
358
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Configuration options for the OpenLayers provider.
|
|
361
|
+
*
|
|
362
|
+
* @typedef {Object} OpenLayersProviderConfig
|
|
363
|
+
*
|
|
364
|
+
* @property {'uk' | 'world'} [zoomAlignment='uk']
|
|
365
|
+
* Which zoom-level sequence the map's view resolutions follow. 'uk' uses the OS tile
|
|
366
|
+
* grid zoom levels (0–13, zoom 0 shows all of Great Britain). 'world' uses the ESRI LOD
|
|
367
|
+
* sequence, for zoom levels that match the ESRI SDK (full UK visible around zoom 7).
|
|
368
|
+
*/
|
|
369
|
+
|
|
359
370
|
/**
|
|
360
371
|
* Descriptor for lazy-loading a map provider.
|
|
361
372
|
*
|
package/webpack.dev.mjs
CHANGED
|
@@ -18,6 +18,7 @@ export default {
|
|
|
18
18
|
],
|
|
19
19
|
entry: {
|
|
20
20
|
index: path.join(__dirname, 'demo/js/index.js'),
|
|
21
|
+
'index-ol': path.join(__dirname, 'demo/js/index-ol.js'),
|
|
21
22
|
draw: path.join(__dirname, 'demo/js/draw.js'),
|
|
22
23
|
'draw-ol': path.join(__dirname, 'demo/js/draw-ol.js'),
|
|
23
24
|
farming: path.join(__dirname, 'demo/js/farming.js'),
|
|
File without changes
|