@defra/interactive-map 0.0.20-alpha → 0.0.21-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/docs/examples/index.mdx +2 -2
- package/docs/plugins/datasets.md +11 -11
- package/package.json +6 -2
- package/plugins/beta/datasets/adapters/maplibre/dist/esm/im-datasets-ml-adapter.js +1 -0
- package/plugins/beta/datasets/adapters/maplibre/dist/esm/index.js +1 -0
- package/plugins/beta/datasets/adapters/maplibre/dist/umd/im-datasets-ml-adapter.js +2 -0
- package/plugins/beta/datasets/adapters/maplibre/dist/umd/im-datasets-ml-adapter.js.LICENSE.txt +1 -0
- package/plugins/beta/datasets/adapters/maplibre/dist/umd/index.js +1 -0
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +4 -4
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +26 -7
- package/plugins/beta/datasets/src/defaults.js +1 -1
- package/plugins/beta/datasets/src/manifest.js +1 -1
- package/plugins/beta/datasets/src/panels/Layers.jsx +4 -4
- package/plugins/beta/datasets/src/utils/mergeSublayer.js +1 -1
- 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 +2 -1
- package/providers/maplibre/src/maplibreProvider.test.js +22 -2
- package/providers/maplibre/src/utils/patternImages.js +16 -10
- package/providers/maplibre/src/utils/patternImages.test.js +50 -19
- package/rollup.esm.mjs +9 -0
- package/src/utils/patternUtils.js +8 -3
- package/src/utils/patternUtils.test.js +17 -0
- package/webpack.umd.mjs +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getPatternInnerContent, getPatternImageId, injectColors } from '../../../../src/utils/patternUtils.js'
|
|
1
|
+
import { getPatternInnerContent, getPatternImageId, injectColors, PATTERN_MIN_PIXEL_RATIO } from '../../../../src/utils/patternUtils.js'
|
|
2
2
|
import { getValueForStyle } from '../../../../src/utils/getValueForStyle.js'
|
|
3
3
|
import { rasteriseToImageData } from './rasteriseToImageData.js'
|
|
4
4
|
|
|
@@ -12,15 +12,16 @@ const imageDataCache = new Map()
|
|
|
12
12
|
* @param {Object} dataset
|
|
13
13
|
* @param {string} mapStyleId
|
|
14
14
|
* @param {Object} patternRegistry
|
|
15
|
+
* @param {number} pixelRatio
|
|
15
16
|
* @returns {Promise<{ imageId: string, imageData: ImageData }|null>}
|
|
16
17
|
*/
|
|
17
|
-
const rasterisePattern = async (dataset, mapStyleId, patternRegistry) => {
|
|
18
|
+
const rasterisePattern = async (dataset, mapStyleId, patternRegistry, pixelRatio) => {
|
|
18
19
|
const innerContent = getPatternInnerContent(dataset, patternRegistry)
|
|
19
20
|
if (!innerContent) {
|
|
20
21
|
return null
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
const imageId = getPatternImageId(dataset, mapStyleId, patternRegistry)
|
|
24
|
+
const imageId = getPatternImageId(dataset, mapStyleId, patternRegistry, pixelRatio)
|
|
24
25
|
if (!imageId) {
|
|
25
26
|
return null
|
|
26
27
|
}
|
|
@@ -31,9 +32,12 @@ const rasterisePattern = async (dataset, mapStyleId, patternRegistry) => {
|
|
|
31
32
|
const bg = getValueForStyle(dataset.fillPatternBackgroundColor, mapStyleId) || 'transparent'
|
|
32
33
|
const colored = injectColors(innerContent, fg, bg)
|
|
33
34
|
const bgRect = `<rect width="16" height="16" fill="${bg}"/>`
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// effectiveRatio floored at PATTERN_MIN_PIXEL_RATIO keeps the canvas at ≥16px physical so
|
|
36
|
+
// SVG detail renders crisply. Logical tile size = physicalSize / effectiveRatio = 8px always.
|
|
37
|
+
const effectiveRatio = Math.max(PATTERN_MIN_PIXEL_RATIO, pixelRatio)
|
|
38
|
+
const physicalSize = Math.round(8 * effectiveRatio)
|
|
39
|
+
const svgString = `<svg xmlns="http://www.w3.org/2000/svg" width="${physicalSize}" height="${physicalSize}" viewBox="0 0 16 16">${bgRect}${colored}</svg>`
|
|
40
|
+
imageData = await rasteriseToImageData(svgString, physicalSize, physicalSize)
|
|
37
41
|
imageDataCache.set(imageId, imageData)
|
|
38
42
|
}
|
|
39
43
|
|
|
@@ -50,21 +54,23 @@ const rasterisePattern = async (dataset, mapStyleId, patternRegistry) => {
|
|
|
50
54
|
* @param {Object[]} patternConfigs - Flat list of datasets/merged-sublayers with a pattern config
|
|
51
55
|
* @param {string} mapStyleId
|
|
52
56
|
* @param {Object} patternRegistry
|
|
57
|
+
* @param {number} pixelRatio
|
|
53
58
|
* @returns {Promise<void>}
|
|
54
59
|
*/
|
|
55
|
-
export const registerPatterns = async (map, patternConfigs, mapStyleId, patternRegistry) => {
|
|
60
|
+
export const registerPatterns = async (map, patternConfigs, mapStyleId, patternRegistry, pixelRatio) => {
|
|
56
61
|
if (!patternConfigs.length) {
|
|
57
62
|
return
|
|
58
63
|
}
|
|
59
64
|
|
|
65
|
+
const effectiveRatio = Math.max(PATTERN_MIN_PIXEL_RATIO, pixelRatio)
|
|
60
66
|
await Promise.all(patternConfigs.map(async (config) => {
|
|
61
|
-
const imageId = getPatternImageId(config, mapStyleId, patternRegistry)
|
|
67
|
+
const imageId = getPatternImageId(config, mapStyleId, patternRegistry, pixelRatio)
|
|
62
68
|
if (!imageId || map.hasImage(imageId)) {
|
|
63
69
|
return
|
|
64
70
|
}
|
|
65
|
-
const result = await rasterisePattern(config, mapStyleId, patternRegistry)
|
|
71
|
+
const result = await rasterisePattern(config, mapStyleId, patternRegistry, pixelRatio)
|
|
66
72
|
if (result) {
|
|
67
|
-
map.addImage(result.imageId, result.imageData, { pixelRatio:
|
|
73
|
+
map.addImage(result.imageId, result.imageData, { pixelRatio: effectiveRatio })
|
|
68
74
|
}
|
|
69
75
|
}))
|
|
70
76
|
}
|
|
@@ -45,19 +45,6 @@ describe('registerPatterns — registration', () => {
|
|
|
45
45
|
expect(map.addImage).not.toHaveBeenCalled()
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
it('calls addImage with pixelRatio 2 for a named pattern', async () => {
|
|
49
|
-
const map = makeMap()
|
|
50
|
-
const registry = makePatternRegistry()
|
|
51
|
-
const config = { fillPattern: 'stripes' }
|
|
52
|
-
await registerPatterns(map, [config], OUTDOOR, registry)
|
|
53
|
-
expect(map.addImage).toHaveBeenCalledTimes(1)
|
|
54
|
-
expect(map.addImage).toHaveBeenCalledWith(
|
|
55
|
-
expect.stringMatching(/^pattern-[a-z0-9]+$/),
|
|
56
|
-
expect.any(Object),
|
|
57
|
-
{ pixelRatio: 2 }
|
|
58
|
-
)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
48
|
it('calls addImage for an inline fillPatternSvgContent config', async () => {
|
|
62
49
|
const map = makeMap()
|
|
63
50
|
const registry = makePatternRegistry()
|
|
@@ -69,10 +56,11 @@ describe('registerPatterns — registration', () => {
|
|
|
69
56
|
it('skips addImage when image is already registered', async () => {
|
|
70
57
|
const registry = makePatternRegistry()
|
|
71
58
|
const config = { fillPattern: 'stripes' }
|
|
59
|
+
const pixelRatio = 2
|
|
72
60
|
const { getPatternImageId } = await import('../../../../src/utils/patternUtils.js')
|
|
73
|
-
const existingId = getPatternImageId(config, OUTDOOR, registry)
|
|
61
|
+
const existingId = getPatternImageId(config, OUTDOOR, registry, pixelRatio)
|
|
74
62
|
const map = makeMap([existingId])
|
|
75
|
-
await registerPatterns(map, [config], OUTDOOR, registry)
|
|
63
|
+
await registerPatterns(map, [config], OUTDOOR, registry, pixelRatio)
|
|
76
64
|
expect(map.addImage).not.toHaveBeenCalled()
|
|
77
65
|
})
|
|
78
66
|
|
|
@@ -104,6 +92,48 @@ describe('registerPatterns — registration', () => {
|
|
|
104
92
|
})
|
|
105
93
|
})
|
|
106
94
|
|
|
95
|
+
describe('registerPatterns — pixel ratio', () => {
|
|
96
|
+
it('encodes effectiveRatio in the image ID and passes it to addImage', async () => {
|
|
97
|
+
const map = makeMap()
|
|
98
|
+
const registry = makePatternRegistry()
|
|
99
|
+
const config = { fillPattern: 'stripes' }
|
|
100
|
+
await registerPatterns(map, [config], OUTDOOR, registry, 2)
|
|
101
|
+
expect(map.addImage).toHaveBeenCalledTimes(1)
|
|
102
|
+
expect(map.addImage).toHaveBeenCalledWith(
|
|
103
|
+
expect.stringMatching(/^pattern-[a-z0-9]+-2x$/),
|
|
104
|
+
expect.any(Object),
|
|
105
|
+
{ pixelRatio: 2 }
|
|
106
|
+
)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('floors effectiveRatio at 2 so low-DPI patterns stay crisp', async () => {
|
|
110
|
+
const map = makeMap()
|
|
111
|
+
const registry = makePatternRegistry()
|
|
112
|
+
const config = { fillPattern: 'stripes' }
|
|
113
|
+
await registerPatterns(map, [config], OUTDOOR, registry, 1)
|
|
114
|
+
expect(map.addImage).toHaveBeenCalledWith(
|
|
115
|
+
expect.stringMatching(/-2x$/),
|
|
116
|
+
expect.any(Object),
|
|
117
|
+
{ pixelRatio: 2 }
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('produces different image IDs for ratios above the floor', async () => {
|
|
122
|
+
const map1 = makeMap()
|
|
123
|
+
const map2 = makeMap()
|
|
124
|
+
const registry = makePatternRegistry()
|
|
125
|
+
const config = { fillPattern: 'stripes' }
|
|
126
|
+
const hiDpi = 3
|
|
127
|
+
await registerPatterns(map1, [config], OUTDOOR, registry, 2)
|
|
128
|
+
await registerPatterns(map2, [config], OUTDOOR, registry, hiDpi)
|
|
129
|
+
const [id2x] = map1.addImage.mock.calls[0]
|
|
130
|
+
const [id3x] = map2.addImage.mock.calls[0]
|
|
131
|
+
expect(id2x).not.toBe(id3x)
|
|
132
|
+
expect(id2x).toMatch(/-2x$/)
|
|
133
|
+
expect(id3x).toMatch(/-3x$/)
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
107
137
|
describe('registerPatterns — color resolution and caching', () => {
|
|
108
138
|
it('applies foreground and background colors when resolving the SVG', async () => {
|
|
109
139
|
const map = makeMap()
|
|
@@ -137,12 +167,13 @@ describe('registerPatterns — color resolution and caching', () => {
|
|
|
137
167
|
it('uses cached ImageData on second call with identical config', async () => {
|
|
138
168
|
const map = makeMap()
|
|
139
169
|
const registry = makePatternRegistry()
|
|
140
|
-
const config = { fillPattern: 'stripes', fillPatternForegroundColor: '#
|
|
141
|
-
|
|
170
|
+
const config = { fillPattern: 'stripes', fillPatternForegroundColor: '#unique2' }
|
|
171
|
+
const pixelRatio = 2
|
|
172
|
+
await registerPatterns(map, [config], OUTDOOR, registry, pixelRatio)
|
|
142
173
|
const { getPatternImageId } = await import('../../../../src/utils/patternUtils.js')
|
|
143
|
-
const imageId = getPatternImageId(config, OUTDOOR, registry)
|
|
174
|
+
const imageId = getPatternImageId(config, OUTDOOR, registry, pixelRatio)
|
|
144
175
|
const map2 = makeMap([imageId])
|
|
145
|
-
await registerPatterns(map2, [config], OUTDOOR, registry)
|
|
176
|
+
await registerPatterns(map2, [config], OUTDOOR, registry, pixelRatio)
|
|
146
177
|
expect(map2.addImage).not.toHaveBeenCalled()
|
|
147
178
|
})
|
|
148
179
|
})
|
package/rollup.esm.mjs
CHANGED
|
@@ -255,6 +255,15 @@ const ALL_BUILDS = [
|
|
|
255
255
|
outDir: 'plugins/beta/datasets/dist/esm',
|
|
256
256
|
manualChunks: (id) => { if (id.includes('/manifest')) return 'im-datasets-plugin' }
|
|
257
257
|
},
|
|
258
|
+
{
|
|
259
|
+
entryPath: './plugins/beta/datasets/src/adapters/maplibre/index.js',
|
|
260
|
+
outDir: 'plugins/beta/datasets/adapters/maplibre/dist/esm',
|
|
261
|
+
manualChunks: (id) => {
|
|
262
|
+
if (id.includes('maplibreLayerAdapter')) {
|
|
263
|
+
return 'im-datasets-ml-adapter'
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
258
267
|
{
|
|
259
268
|
entryPath: './plugins/beta/map-styles/src/index.js',
|
|
260
269
|
outDir: 'plugins/beta/map-styles/dist/esm',
|
|
@@ -52,21 +52,26 @@ export const getPatternInnerContent = (dataset, patternRegistry) => {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
* Returns a deterministic image ID for a pattern + resolved colour combination.
|
|
55
|
+
* Returns a deterministic image ID for a pattern + resolved colour + pixel ratio combination.
|
|
56
56
|
*
|
|
57
57
|
* @param {Object} dataset
|
|
58
58
|
* @param {string} mapStyleId
|
|
59
59
|
* @param {Object} patternRegistry
|
|
60
|
+
* @param {number} [pixelRatio=1]
|
|
60
61
|
* @returns {string|null}
|
|
61
62
|
*/
|
|
62
|
-
|
|
63
|
+
// Minimum oversampling — keeps 16×16 physical pixels as the floor so patterns remain crisp.
|
|
64
|
+
export const PATTERN_MIN_PIXEL_RATIO = 2
|
|
65
|
+
|
|
66
|
+
export const getPatternImageId = (dataset, mapStyleId, patternRegistry, pixelRatio = 1) => {
|
|
63
67
|
const innerContent = getPatternInnerContent(dataset, patternRegistry)
|
|
64
68
|
if (!innerContent) {
|
|
65
69
|
return null
|
|
66
70
|
}
|
|
67
71
|
const fg = getValueForStyle(dataset.fillPatternForegroundColor, mapStyleId) || 'black'
|
|
68
72
|
const bg = getValueForStyle(dataset.fillPatternBackgroundColor, mapStyleId) || 'transparent'
|
|
69
|
-
|
|
73
|
+
const effectiveRatio = Math.max(PATTERN_MIN_PIXEL_RATIO, pixelRatio)
|
|
74
|
+
return `pattern-${hashString(innerContent + fg + bg)}-${effectiveRatio}x`
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
/**
|
|
@@ -103,6 +103,23 @@ describe('getPatternImageId', () => {
|
|
|
103
103
|
expect(idA).not.toBe(idB)
|
|
104
104
|
})
|
|
105
105
|
|
|
106
|
+
test('floors effective ratio at PATTERN_MIN_PIXEL_RATIO so low-DPI ids match 2x', () => {
|
|
107
|
+
const dataset = { fillPattern: 'dot' }
|
|
108
|
+
const id1x = getPatternImageId(dataset, 'style-a', mockRegistry, 1)
|
|
109
|
+
const id2x = getPatternImageId(dataset, 'style-a', mockRegistry, 2)
|
|
110
|
+
expect(id1x).toBe(id2x)
|
|
111
|
+
expect(id1x).toMatch(/-2x$/)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('produces different ids for pixelRatios above the floor', () => {
|
|
115
|
+
const dataset = { fillPattern: 'dot' }
|
|
116
|
+
const id2x = getPatternImageId(dataset, 'style-a', mockRegistry, 2)
|
|
117
|
+
const id3x = getPatternImageId(dataset, 'style-a', mockRegistry, 3)
|
|
118
|
+
expect(id2x).not.toBe(id3x)
|
|
119
|
+
expect(id2x).toMatch(/-2x$/)
|
|
120
|
+
expect(id3x).toMatch(/-3x$/)
|
|
121
|
+
})
|
|
122
|
+
|
|
106
123
|
test('falls back to "black" foreground and "transparent" background when colours are absent', () => {
|
|
107
124
|
const id = getPatternImageId({ fillPattern: 'dot' }, 'style-a', mockRegistry)
|
|
108
125
|
const idExplicit = getPatternImageId(
|
package/webpack.umd.mjs
CHANGED
|
@@ -140,6 +140,7 @@ const ALL_BUILDS = [
|
|
|
140
140
|
{ entryPath: './plugins/search/src/index.js', libraryPath: 'searchPlugin', outDir: 'plugins/search/dist/umd' },
|
|
141
141
|
{ entryPath: './plugins/interact/src/index.js', libraryPath: 'interactPlugin', outDir: 'plugins/interact/dist/umd' },
|
|
142
142
|
{ entryPath: './plugins/beta/datasets/src/index.js', libraryPath: 'datasetsPlugin', outDir: 'plugins/beta/datasets/dist/umd' },
|
|
143
|
+
{ entryPath: './plugins/beta/datasets/src/adapters/maplibre/index.js', libraryPath: 'datasetsMaplibreAdapter', outDir: 'plugins/beta/datasets/adapters/maplibre/dist/umd' },
|
|
143
144
|
{ entryPath: './plugins/beta/map-styles/src/index.js', libraryPath: 'mapStylesPlugin', outDir: 'plugins/beta/map-styles/dist/umd' },
|
|
144
145
|
{ entryPath: './plugins/beta/draw-ml/src/index.js', libraryPath: 'drawMLPlugin', outDir: 'plugins/beta/draw-ml/dist/umd' },
|
|
145
146
|
{ entryPath: './plugins/beta/frame/src/index.js', libraryPath: 'framePlugin', outDir: 'plugins/beta/frame/dist/umd' }
|