isard 1.1.0 → 1.2.0
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/CHANGELOG.md +33 -0
- package/dist/basemap/basemaps.d.ts +1 -1
- package/dist/basemap/basemaps.js +2 -2
- package/dist/basemap/basemaps.js.map +1 -1
- package/dist/basemap/grid.d.ts +5 -5
- package/dist/basemap/grid.js +59 -65
- package/dist/basemap/grid.js.map +1 -1
- package/dist/basemap/mvt.d.ts +1 -1
- package/dist/basemap/mvt.js +6 -6
- package/dist/basemap/mvt.js.map +1 -1
- package/dist/basemap/pmtiles.d.ts +10 -10
- package/dist/basemap/pmtiles.js +39 -31
- package/dist/basemap/pmtiles.js.map +1 -1
- package/dist/basemap/resample.d.ts +2 -2
- package/dist/basemap/resample.js +16 -14
- package/dist/basemap/resample.js.map +1 -1
- package/dist/basemap/terrain.d.ts +3 -3
- package/dist/basemap/terrain.js +6 -6
- package/dist/basemap/terrain.js.map +1 -1
- package/dist/basemap/vectorstyle.d.ts +12 -10
- package/dist/basemap/vectorstyle.js +128 -110
- package/dist/basemap/vectorstyle.js.map +1 -1
- package/dist/basemap/vectortiles.d.ts +11 -11
- package/dist/basemap/vectortiles.js +141 -103
- package/dist/basemap/vectortiles.js.map +1 -1
- package/dist/basemap/wmts.d.ts +3 -3
- package/dist/basemap/wmts.js +16 -11
- package/dist/basemap/wmts.js.map +1 -1
- package/dist/basemap.worker.js +2 -2
- package/dist/basemap.worker.js.map +14 -13
- package/dist/bind/layers.d.ts +13 -4
- package/dist/bind/layers.js +64 -40
- package/dist/bind/layers.js.map +1 -1
- package/dist/data/crs.d.ts +10 -10
- package/dist/data/crs.js +38 -34
- package/dist/data/crs.js.map +1 -1
- package/dist/data/gpx.d.ts +1 -1
- package/dist/data/gpx.js +20 -20
- package/dist/data/gpx.js.map +1 -1
- package/dist/data/kml.d.ts +1 -1
- package/dist/data/kml.js +42 -39
- package/dist/data/kml.js.map +1 -1
- package/dist/data/topojson.d.ts +1 -1
- package/dist/data/topojson.js +15 -10
- package/dist/data/topojson.js.map +1 -1
- package/dist/data/xml.d.ts +5 -5
- package/dist/data/xml.js +19 -18
- package/dist/data/xml.js.map +1 -1
- package/dist/decoder.worker.js +4 -4
- package/dist/decoder.worker.js.map +6 -6
- package/dist/engine/decoders.d.ts +2 -2
- package/dist/engine/decoders.js +28 -25
- package/dist/engine/decoders.js.map +1 -1
- package/dist/engine/loader.js +13 -14
- package/dist/engine/loader.js.map +1 -1
- package/dist/engine/requests.d.ts +1 -1
- package/dist/engine/requests.js +19 -16
- package/dist/engine/requests.js.map +1 -1
- package/dist/engine-loader.d.ts +8 -8
- package/dist/engine-loader.js +27 -27
- package/dist/engine-loader.js.map +1 -1
- package/dist/geoplateforme.d.ts +10 -10
- package/dist/geoplateforme.js +22 -20
- package/dist/geoplateforme.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js.map +1 -1
- package/dist/isard_bg.wasm +0 -0
- package/dist/isard_core_bg.wasm +0 -0
- package/dist/isard_points_bg.wasm +0 -0
- package/dist/layers/extrusions.d.ts +1 -1
- package/dist/layers/extrusions.js +10 -5
- package/dist/layers/extrusions.js.map +1 -1
- package/dist/layers/features.d.ts +10 -10
- package/dist/layers/features.js +41 -42
- package/dist/layers/features.js.map +1 -1
- package/dist/layers/glyphs.d.ts +3 -3
- package/dist/layers/glyphs.js +34 -40
- package/dist/layers/glyphs.js.map +1 -1
- package/dist/layers/icons.d.ts +8 -8
- package/dist/layers/icons.js +76 -68
- package/dist/layers/icons.js.map +1 -1
- package/dist/layers/layers.d.ts +1 -1
- package/dist/layers/layers.js +33 -29
- package/dist/layers/layers.js.map +1 -1
- package/dist/layers/panoramas.d.ts +2 -2
- package/dist/layers/panoramas.js +52 -41
- package/dist/layers/panoramas.js.map +1 -1
- package/dist/layers/pointclouds.d.ts +3 -3
- package/dist/layers/pointclouds.js +25 -33
- package/dist/layers/pointclouds.js.map +1 -1
- package/dist/layers/symbols.d.ts +1 -1
- package/dist/layers/symbols.js +68 -71
- package/dist/layers/symbols.js.map +1 -1
- package/dist/map/basemap-layers.d.ts +26 -0
- package/dist/map/basemap-layers.js +61 -0
- package/dist/map/basemap-layers.js.map +1 -0
- package/dist/map/canvas.d.ts +17 -0
- package/dist/map/canvas.js +56 -0
- package/dist/map/canvas.js.map +1 -0
- package/dist/map/checks.d.ts +11 -0
- package/dist/map/checks.js +39 -0
- package/dist/map/checks.js.map +1 -0
- package/dist/map/credits.d.ts +17 -0
- package/dist/map/credits.js +34 -0
- package/dist/map/credits.js.map +1 -0
- package/dist/map/engine-settings.d.ts +15 -0
- package/dist/map/engine-settings.js +45 -0
- package/dist/map/engine-settings.js.map +1 -0
- package/dist/map/engines.d.ts +41 -0
- package/dist/map/engines.js +77 -0
- package/dist/map/engines.js.map +1 -0
- package/dist/map/events.d.ts +14 -0
- package/dist/map/events.js +46 -0
- package/dist/map/events.js.map +1 -0
- package/dist/map/frame-loop.d.ts +61 -0
- package/dist/map/frame-loop.js +139 -0
- package/dist/map/frame-loop.js.map +1 -0
- package/dist/map/hover-pick.d.ts +30 -0
- package/dist/map/hover-pick.js +56 -0
- package/dist/map/hover-pick.js.map +1 -0
- package/dist/map/layer-spec.d.ts +42 -0
- package/dist/map/layer-spec.js +34 -0
- package/dist/map/layer-spec.js.map +1 -0
- package/dist/map/layer-stack.d.ts +36 -0
- package/dist/map/layer-stack.js +101 -0
- package/dist/map/layer-stack.js.map +1 -0
- package/dist/map/overlay-host.d.ts +36 -0
- package/dist/map/overlay-host.js +101 -0
- package/dist/map/overlay-host.js.map +1 -0
- package/dist/map/panorama-host.d.ts +5 -0
- package/dist/map/panorama-host.js +32 -0
- package/dist/map/panorama-host.js.map +1 -0
- package/dist/map/raster-source.d.ts +11 -0
- package/dist/map/raster-source.js +26 -0
- package/dist/map/raster-source.js.map +1 -0
- package/dist/map/symbol-atlases.d.ts +26 -0
- package/dist/map/symbol-atlases.js +59 -0
- package/dist/map/symbol-atlases.js.map +1 -0
- package/dist/map/viewshed.d.ts +5 -0
- package/dist/map/viewshed.js +28 -0
- package/dist/map/viewshed.js.map +1 -0
- package/dist/map/workers.d.ts +46 -0
- package/dist/map/workers.js +73 -0
- package/dist/map/workers.js.map +1 -0
- package/dist/map.d.ts +33 -120
- package/dist/map.js +164 -689
- package/dist/map.js.map +1 -1
- package/dist/react/Control.d.ts +11 -0
- package/dist/react/Control.js +18 -0
- package/dist/react/Control.js.map +1 -0
- package/dist/react/CustomLayer.d.ts +11 -0
- package/dist/react/CustomLayer.js +30 -0
- package/dist/react/CustomLayer.js.map +1 -0
- package/dist/react/IsardMap.d.ts +43 -0
- package/dist/react/IsardMap.js +146 -0
- package/dist/react/IsardMap.js.map +1 -0
- package/dist/react/Layer.d.ts +42 -0
- package/dist/react/Layer.js +74 -0
- package/dist/react/Layer.js.map +1 -0
- package/dist/react/Marker.d.ts +24 -0
- package/dist/react/Marker.js +53 -0
- package/dist/react/Marker.js.map +1 -0
- package/dist/react/Panel.d.ts +11 -0
- package/dist/react/Panel.js +37 -0
- package/dist/react/Panel.js.map +1 -0
- package/dist/react/Popup.d.ts +18 -0
- package/dist/react/Popup.js +60 -0
- package/dist/react/Popup.js.map +1 -0
- package/dist/react/Swipe.d.ts +9 -0
- package/dist/react/Swipe.js +41 -0
- package/dist/react/Swipe.js.map +1 -0
- package/dist/react/Viewshed.d.ts +5 -0
- package/dist/react/Viewshed.js +28 -0
- package/dist/react/Viewshed.js.map +1 -0
- package/dist/react/context.d.ts +32 -0
- package/dist/react/context.js +20 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/index.d.ts +15 -108
- package/dist/react/index.js +10 -397
- package/dist/react/index.js.map +1 -1
- package/dist/settings.d.ts +5 -9
- package/dist/settings.js +17 -15
- package/dist/settings.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.js.map +1 -1
- package/dist/ui/controls.js +6 -4
- package/dist/ui/controls.js.map +1 -1
- package/dist/ui/interaction.d.ts +2 -2
- package/dist/ui/interaction.js +8 -7
- package/dist/ui/interaction.js.map +1 -1
- package/dist/ui/overlay.js +18 -20
- package/dist/ui/overlay.js.map +1 -1
- package/dist/ui/style.d.ts +1 -1
- package/dist/ui/style.js +2 -2
- package/dist/ui/style.js.map +1 -1
- package/dist/util/animation.d.ts +3 -3
- package/dist/util/animation.js +6 -6
- package/dist/util/animation.js.map +1 -1
- package/dist/util/color.d.ts +1 -1
- package/dist/util/color.js +14 -16
- package/dist/util/color.js.map +1 -1
- package/dist/util/geo.d.ts +6 -6
- package/dist/util/geo.js +12 -12
- package/dist/util/geo.js.map +1 -1
- package/dist/util/guards.d.ts +7 -0
- package/dist/util/guards.js +8 -0
- package/dist/util/guards.js.map +1 -0
- package/dist/util/lru.js.map +1 -1
- package/dist/wasm/isard.d.ts +168 -0
- package/dist/wasm/isard.js +146 -0
- package/dist/wasm/isard_core.d.ts +144 -0
- package/dist/wasm/isard_core.js +122 -0
- package/dist/workers/basemap.worker.d.ts +3 -3
- package/dist/workers/basemap.worker.js +94 -83
- package/dist/workers/basemap.worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,11 +13,12 @@ import { assetUrl } from "../engine-loader.js";
|
|
|
13
13
|
import { IGN_ATTRIBUTION } from "../geoplateforme.js";
|
|
14
14
|
import { loadSprites, readPixels } from "../layers/icons.js";
|
|
15
15
|
import { mercatorX, mercatorY } from "../util/geo.js";
|
|
16
|
+
import { isRecord } from "../util/guards.js";
|
|
16
17
|
import { Lru } from "../util/lru.js";
|
|
17
18
|
import { OPENFREEMAP_ATTRIBUTION } from "./basemaps.js";
|
|
18
19
|
import { pmtiles, pmtilesUrl } from "./pmtiles.js";
|
|
19
20
|
import { SOURCE_CACHE } from "./resample.js";
|
|
20
|
-
import { labelFont, } from "./vectorstyle.js";
|
|
21
|
+
import { isStyle, labelFont, } from "./vectorstyle.js";
|
|
21
22
|
/** OpenFreeMap's styles: OpenStreetMap data in the OpenMapTiles schema, worldwide, free and without a key */
|
|
22
23
|
export const OPENFREEMAP_STYLES = {
|
|
23
24
|
positron: 'https://tiles.openfreemap.org/styles/positron',
|
|
@@ -38,43 +39,75 @@ export const VECTOR_BASEMAPS = {
|
|
|
38
39
|
'ign-plan-grey-vector': PLAN_STYLES.grey,
|
|
39
40
|
};
|
|
40
41
|
/** The credits of a known style: OpenFreeMap's, PLAN.IGN's */
|
|
41
|
-
export
|
|
42
|
+
export const styleAttribution = (style) => {
|
|
42
43
|
if (typeof style !== 'string')
|
|
43
44
|
return undefined;
|
|
44
|
-
if (Object.values(OPENFREEMAP_STYLES).
|
|
45
|
+
if (Object.values(OPENFREEMAP_STYLES).some((s) => s === style))
|
|
45
46
|
return OPENFREEMAP_ATTRIBUTION;
|
|
46
|
-
if (Object.values(PLAN_STYLES).
|
|
47
|
+
if (Object.values(PLAN_STYLES).some((s) => s === style))
|
|
47
48
|
return IGN_ATTRIBUTION;
|
|
48
49
|
return undefined;
|
|
49
|
-
}
|
|
50
|
+
};
|
|
50
51
|
/** Text or icon size of a style (a number, legacy stops, or the stops of a zoom expression made by the worker) as a
|
|
51
52
|
* size for the symbol layer */
|
|
52
|
-
export
|
|
53
|
+
export const size = (value, fallback = 16) => {
|
|
54
|
+
const stop = (s) => {
|
|
55
|
+
const [z, v] = Array.isArray(s) ? s : [];
|
|
56
|
+
return [Number(z), Number(v)];
|
|
57
|
+
};
|
|
53
58
|
if (typeof value === 'number')
|
|
54
59
|
return value;
|
|
55
|
-
if (Array.isArray(value) && value.length > 0 && value.every(Array.isArray))
|
|
56
|
-
return value.map(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return value.stops.map((s) => {
|
|
60
|
-
const [z, v] = s;
|
|
61
|
-
return [Number(z), Number(v)];
|
|
62
|
-
});
|
|
63
|
-
}
|
|
60
|
+
if (Array.isArray(value) && value.length > 0 && value.every(Array.isArray))
|
|
61
|
+
return value.map(stop);
|
|
62
|
+
if (isRecord(value) && Array.isArray(value.stops))
|
|
63
|
+
return value.stops.map(stop);
|
|
64
64
|
return fallback;
|
|
65
|
-
}
|
|
65
|
+
};
|
|
66
|
+
/** The anchors of the symbol layer */
|
|
67
|
+
const ANCHORS = new Set([
|
|
68
|
+
'center',
|
|
69
|
+
'top',
|
|
70
|
+
'bottom',
|
|
71
|
+
'left',
|
|
72
|
+
'right',
|
|
73
|
+
'top-left',
|
|
74
|
+
'top-right',
|
|
75
|
+
'bottom-left',
|
|
76
|
+
'bottom-right',
|
|
77
|
+
]);
|
|
78
|
+
const isAnchor = (value) => value !== undefined && ANCHORS.has(value);
|
|
79
|
+
/** A style's anchor (`text-anchor`, `icon-anchor`) for the symbol layer: `center` if it is none */
|
|
80
|
+
const anchor = (value) => (isAnchor(value) ? value : 'center');
|
|
66
81
|
/** Fetches a JSON document (a style, a TileJSON) */
|
|
67
|
-
async
|
|
82
|
+
const json = async (url) => {
|
|
68
83
|
const response = await fetch(url, withHeaders(url));
|
|
69
84
|
if (!response.ok)
|
|
70
85
|
throw new Error(`${url}: ${response.status}`);
|
|
71
|
-
return
|
|
72
|
-
}
|
|
86
|
+
return response.json();
|
|
87
|
+
};
|
|
88
|
+
/** A style fetched: a MapLibre style, or an error */
|
|
89
|
+
const fetchStyle = async (url) => {
|
|
90
|
+
const style = await json(url);
|
|
91
|
+
if (!isStyle(style))
|
|
92
|
+
throw new Error(`${url}: not a MapLibre style`);
|
|
93
|
+
return style;
|
|
94
|
+
};
|
|
95
|
+
const isTileJson = (value) => isRecord(value) &&
|
|
96
|
+
Array.isArray(value.tiles) &&
|
|
97
|
+
value.tiles.every((t) => typeof t === 'string') &&
|
|
98
|
+
(value.maxzoom === undefined || typeof value.maxzoom === 'number');
|
|
99
|
+
/** A TileJSON fetched, or an error */
|
|
100
|
+
const fetchTileJson = async (url) => {
|
|
101
|
+
const tilejson = await json(url);
|
|
102
|
+
if (!isTileJson(tilejson))
|
|
103
|
+
throw new Error(`${url}: not a TileJSON`);
|
|
104
|
+
return tilejson;
|
|
105
|
+
};
|
|
73
106
|
/**
|
|
74
107
|
* A style source's tiles and deepest level: a `pmtiles://` archive (its `url`, or a `tiles` template) read for its
|
|
75
108
|
* header, its tiles then named `pmtiles://<absolute archive URL>/{z}/{x}/{y}` for the workers; other sources as they are
|
|
76
109
|
*/
|
|
77
|
-
async
|
|
110
|
+
const resolveSource = async (source) => {
|
|
78
111
|
const given = source.url ?? source.tiles?.[0] ?? '';
|
|
79
112
|
const archive = pmtilesUrl(given.replace(/\/\{z\}\/\{x\}\/\{y\}$/, ''));
|
|
80
113
|
if (!archive)
|
|
@@ -84,20 +117,20 @@ async function resolveSource(source) {
|
|
|
84
117
|
};
|
|
85
118
|
const header = await pmtiles(archive).header();
|
|
86
119
|
return { tiles: [`pmtiles://${archive}/{z}/{x}/{y}`], maxzoom: source.maxzoom ?? header.maxZoom };
|
|
87
|
-
}
|
|
120
|
+
};
|
|
88
121
|
/** Pause before the labels of tiles that failed are asked for again, when no move brings an update (ms) */
|
|
89
122
|
const RETRY_LABELS_MS = 3000;
|
|
90
123
|
/** Default size of a basemap worker pool: cores - 1, 1 to 4 */
|
|
91
|
-
export
|
|
124
|
+
export const defaultWorkerCount = () => {
|
|
92
125
|
return Math.min(4, Math.max(1, (typeof navigator !== 'undefined' ? navigator.hardwareConcurrency || 2 : 2) - 1));
|
|
93
|
-
}
|
|
126
|
+
};
|
|
94
127
|
/** Default URL of the basemap worker: in the page's assets folder (`isard/vite`), else next to the module */
|
|
95
|
-
export
|
|
128
|
+
export const defaultWorkerUrl = (assetsUrl) => {
|
|
96
129
|
return assetUrl('basemap.worker.js', undefined, assetsUrl);
|
|
97
|
-
}
|
|
130
|
+
};
|
|
98
131
|
/** Fetch priority of a raster tile drawn by the workers: 'high' near the center of the view, at the current zoom or
|
|
99
132
|
* coarser; 'low' far from it or finer (prefetch); 'auto' between */
|
|
100
|
-
export
|
|
133
|
+
export const tilePriority = (z, x, y, camera) => {
|
|
101
134
|
// A raster tile of level z covers the map at zoom z - 1
|
|
102
135
|
if (z - 1 > camera.zoom + 1)
|
|
103
136
|
return 'low';
|
|
@@ -106,22 +139,22 @@ export function tilePriority(z, x, y, camera) {
|
|
|
106
139
|
const cx = mercatorX(lon) * n;
|
|
107
140
|
const cy = mercatorY(lat) * n;
|
|
108
141
|
// Across the antimeridian, the short way
|
|
109
|
-
|
|
110
|
-
dx = Math.min(
|
|
142
|
+
const along = Math.abs(x + 0.5 - cx) % n;
|
|
143
|
+
const dx = Math.min(along, n - along);
|
|
111
144
|
const d = Math.hypot(dx, y + 0.5 - cy);
|
|
112
145
|
if (d <= 1.5)
|
|
113
146
|
return 'high';
|
|
114
147
|
return d >= 4 ? 'low' : 'auto';
|
|
115
|
-
}
|
|
148
|
+
};
|
|
116
149
|
/** The worker of a vector tile among `count`: always the same one */
|
|
117
|
-
export
|
|
150
|
+
export const workerIndex = (vz, vx, vy, count) => {
|
|
118
151
|
// Unsigned: every worker gets its share
|
|
119
152
|
return ((Math.imul(vx, 73856093) ^ Math.imul(vy, 19349663) ^ vz) >>> 0) % Math.max(1, count);
|
|
120
|
-
}
|
|
153
|
+
};
|
|
121
154
|
/** The worker-side key of a renderer: unique across the `VectorTiles` sharing a pool */
|
|
122
|
-
export
|
|
155
|
+
export const rendererKey = (namespace, n) => {
|
|
123
156
|
return `${namespace}:r${n}`;
|
|
124
|
-
}
|
|
157
|
+
};
|
|
125
158
|
/**
|
|
126
159
|
* @internal Workers drawing vector tiles, shared by the `VectorTiles` of a map: each vector tile always goes to the
|
|
127
160
|
* same worker, which keeps it decoded for every style drawn from it. Requests are tracked one by one, with their
|
|
@@ -140,7 +173,7 @@ export class BasemapWorkers {
|
|
|
140
173
|
constructor(count, url, create = (u) => new Worker(u, { type: 'module' })) {
|
|
141
174
|
this.count = count;
|
|
142
175
|
this.url = url;
|
|
143
|
-
|
|
176
|
+
const start = () => {
|
|
144
177
|
const worker = create(url);
|
|
145
178
|
worker.onmessage = ({ data }) => {
|
|
146
179
|
const p = this.pending.get(data.id);
|
|
@@ -162,10 +195,12 @@ export class BasemapWorkers {
|
|
|
162
195
|
if (rules.length > 0)
|
|
163
196
|
worker.postMessage({ type: 'headers', rules });
|
|
164
197
|
return worker;
|
|
165
|
-
}
|
|
198
|
+
};
|
|
199
|
+
this.workers = [start(), ...Array.from({ length: Math.max(1, count) - 1 }, start)];
|
|
166
200
|
this.unfollow = onHeaderRules((rules) => {
|
|
167
|
-
|
|
201
|
+
this.workers.forEach((w) => {
|
|
168
202
|
w.postMessage({ type: 'headers', rules });
|
|
203
|
+
});
|
|
169
204
|
});
|
|
170
205
|
}
|
|
171
206
|
/** A prefix for the renderer keys of one `VectorTiles` */
|
|
@@ -179,12 +214,12 @@ export class BasemapWorkers {
|
|
|
179
214
|
/** Rejects the requests of a failed worker */
|
|
180
215
|
fail(worker, message) {
|
|
181
216
|
const error = new Error(`isard: ${message}. If a bundler moves the worker, give its URL in the \`basemapWorkerUrl\` option (see "With a bundler" in the guide)`);
|
|
182
|
-
|
|
217
|
+
this.pending.forEach((p, id) => {
|
|
183
218
|
if (p.worker !== worker)
|
|
184
|
-
|
|
219
|
+
return;
|
|
185
220
|
this.pending.delete(id);
|
|
186
221
|
p.reject(error);
|
|
187
|
-
}
|
|
222
|
+
});
|
|
188
223
|
}
|
|
189
224
|
/** The worker of a vector tile */
|
|
190
225
|
worker(vz, vx, vy) {
|
|
@@ -197,8 +232,9 @@ export class BasemapWorkers {
|
|
|
197
232
|
// A renderer tells the workers how many they are: they share the decoded tiles' budget
|
|
198
233
|
const sent = message.type === 'init' ? { ...message, workers: this.workers.length } : message;
|
|
199
234
|
// A sprite image is copied to each worker (structured clone)
|
|
200
|
-
|
|
235
|
+
this.workers.forEach((w) => {
|
|
201
236
|
w.postMessage(sent);
|
|
237
|
+
});
|
|
202
238
|
}
|
|
203
239
|
/** Asks a worker for a tile's image or labels, on behalf of `owner` */
|
|
204
240
|
request(worker, work, owner, signal) {
|
|
@@ -247,13 +283,13 @@ export class BasemapWorkers {
|
|
|
247
283
|
}
|
|
248
284
|
/** Cancels the requests of an owner (the workers stop them) */
|
|
249
285
|
cancel(owner) {
|
|
250
|
-
|
|
286
|
+
this.pending.forEach((p, id) => {
|
|
251
287
|
if (p.owner !== owner)
|
|
252
|
-
|
|
288
|
+
return;
|
|
253
289
|
this.pending.delete(id);
|
|
254
290
|
p.worker.postMessage({ type: 'abort', id });
|
|
255
291
|
p.reject(new DOMException('isard: vector tiles destroyed', 'AbortError'));
|
|
256
|
-
}
|
|
292
|
+
});
|
|
257
293
|
}
|
|
258
294
|
/** Stops the workers; their requests fail */
|
|
259
295
|
terminate() {
|
|
@@ -261,18 +297,20 @@ export class BasemapWorkers {
|
|
|
261
297
|
return;
|
|
262
298
|
this.terminated = true;
|
|
263
299
|
this.unfollow();
|
|
264
|
-
|
|
300
|
+
this.workers.forEach((w) => {
|
|
265
301
|
w.terminate();
|
|
302
|
+
});
|
|
266
303
|
const pending = [...this.pending.values()];
|
|
267
304
|
this.pending.clear();
|
|
268
|
-
|
|
305
|
+
pending.forEach((p) => {
|
|
269
306
|
p.reject(new DOMException('isard: vector tiles destroyed', 'AbortError'));
|
|
307
|
+
});
|
|
270
308
|
}
|
|
271
309
|
}
|
|
272
310
|
/** A tile's label candidates as features, by style layer */
|
|
273
|
-
export
|
|
311
|
+
export const tileLabels = (candidates) => {
|
|
274
312
|
const byLayer = new Map();
|
|
275
|
-
|
|
313
|
+
candidates.forEach((p) => {
|
|
276
314
|
const feature = {
|
|
277
315
|
type: 'Feature',
|
|
278
316
|
properties: p,
|
|
@@ -291,9 +329,9 @@ export function tileLabels(candidates) {
|
|
|
291
329
|
list.push(feature);
|
|
292
330
|
else
|
|
293
331
|
byLayer.set(p.layer, [feature]);
|
|
294
|
-
}
|
|
332
|
+
});
|
|
295
333
|
return [...byLayer].sort((a, b) => b[0] - a[0]);
|
|
296
|
-
}
|
|
334
|
+
};
|
|
297
335
|
export class VectorTiles {
|
|
298
336
|
map;
|
|
299
337
|
pool;
|
|
@@ -334,16 +372,14 @@ export class VectorTiles {
|
|
|
334
372
|
});
|
|
335
373
|
}
|
|
336
374
|
async load(input) {
|
|
337
|
-
const style = typeof input === 'string' ? await
|
|
375
|
+
const style = typeof input === 'string' ? await fetchStyle(input) : input;
|
|
338
376
|
const [name, source] = Object.entries(style.sources).find(([, s]) => s.type === 'vector') ?? [];
|
|
339
377
|
if (!name || !source)
|
|
340
378
|
throw new Error('the style has no vector source');
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
maxzoom ??= tilejson.maxzoom;
|
|
346
|
-
}
|
|
379
|
+
const resolved = await resolveSource(source);
|
|
380
|
+
const tilejson = !resolved.tiles && source.url ? await fetchTileJson(source.url) : undefined;
|
|
381
|
+
const tiles = tilejson === undefined ? resolved.tiles : tilejson.tiles;
|
|
382
|
+
const maxzoom = resolved.maxzoom ?? tilejson?.maxzoom;
|
|
347
383
|
const template = tiles?.[0];
|
|
348
384
|
if (!template)
|
|
349
385
|
throw new Error(`source ${name} has no tiles`);
|
|
@@ -367,11 +403,11 @@ export class VectorTiles {
|
|
|
367
403
|
/** The fonts of the style's labels, for its glyphs (the first of each `text-font`, as the workers take it) */
|
|
368
404
|
static fonts(style) {
|
|
369
405
|
const fonts = new Set();
|
|
370
|
-
|
|
406
|
+
style.layers.forEach((layer) => {
|
|
371
407
|
const font = layer.layout?.['text-font'];
|
|
372
408
|
if (layer.type === 'symbol' && Array.isArray(font) && typeof font[0] === 'string')
|
|
373
409
|
fonts.add(labelFont(font[0]));
|
|
374
|
-
}
|
|
410
|
+
});
|
|
375
411
|
return [...fonts];
|
|
376
412
|
}
|
|
377
413
|
/** The style's sprites (their JSON and their images), loaded once, for the fill patterns and the icons */
|
|
@@ -453,7 +489,7 @@ export class VectorTiles {
|
|
|
453
489
|
color: (p) => p.color,
|
|
454
490
|
haloColor: (p) => p.haloColor,
|
|
455
491
|
haloWidth: (p) => p.haloWidth,
|
|
456
|
-
anchor: (p) => p.anchor,
|
|
492
|
+
anchor: (p) => anchor(p.anchor),
|
|
457
493
|
offset: (p) => p.offset,
|
|
458
494
|
maxWidth: (p) => p.maxWidth,
|
|
459
495
|
lineHeight: (p) => p.lineHeight,
|
|
@@ -462,7 +498,7 @@ export class VectorTiles {
|
|
|
462
498
|
textOptional: (p) => p.textOptional === true,
|
|
463
499
|
icon: (p) => (p.icon ? this.iconPrefix + p.icon.image : ''),
|
|
464
500
|
iconSize: (p) => size(p.icon?.size ?? 1, 1),
|
|
465
|
-
iconAnchor: (p) => (p.icon?.anchor
|
|
501
|
+
iconAnchor: (p) => anchor(p.icon?.anchor),
|
|
466
502
|
iconOffset: (p) => p.icon?.offset ?? [0, 0],
|
|
467
503
|
iconRotate: (p) => p.icon?.rotate ?? 0,
|
|
468
504
|
iconRotationAlignment: (p) => p.icon?.alignment ?? 'viewport',
|
|
@@ -482,10 +518,10 @@ export class VectorTiles {
|
|
|
482
518
|
// Its icons from its sprites, before the first labels
|
|
483
519
|
const icons = style.layers.some((l) => l.type === 'symbol' && l.layout?.['icon-image'] !== undefined);
|
|
484
520
|
if (icons && style.sprite) {
|
|
485
|
-
|
|
521
|
+
(await this.loadSprites(style.sprite)).forEach((sprite) => {
|
|
486
522
|
if (!this.destroyed)
|
|
487
523
|
this.map.icons.addSprite(this.iconPrefix, sprite.icons, () => readPixels(sprite.image));
|
|
488
|
-
}
|
|
524
|
+
});
|
|
489
525
|
}
|
|
490
526
|
return this.renderer((l) => l.type === 'symbol' && layers(l), []);
|
|
491
527
|
});
|
|
@@ -510,18 +546,22 @@ export class VectorTiles {
|
|
|
510
546
|
if (ground.length > 0) {
|
|
511
547
|
// The ground's tiles, the nearest first: a tile so many levels coarser than the finest in view has its
|
|
512
548
|
// labels of a zoom as many under the view's, past the source's deepest level those of that level at the zoom
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
549
|
+
const groundTiles = Array.from({ length: Math.ceil(ground.length / 3) }, (_, k) => [
|
|
550
|
+
ground[3 * k] ?? 0,
|
|
551
|
+
ground[3 * k + 1] ?? 0,
|
|
552
|
+
ground[3 * k + 2] ?? 0,
|
|
553
|
+
]);
|
|
554
|
+
const top = groundTiles.reduce((t, [z]) => Math.max(t, z), 0);
|
|
555
|
+
groundTiles.forEach(([z, x, y]) => {
|
|
556
|
+
if (keys.length >= maxTiles)
|
|
557
|
+
return;
|
|
518
558
|
const at = Math.max(0, zoom - (top - z));
|
|
519
559
|
const tz = Math.min(at, maxzoom, z);
|
|
520
560
|
const shift = 2 ** (z - tz);
|
|
521
561
|
const key = `${tz}/${Math.floor(x / shift)}/${Math.floor(y / shift)}${at > tz ? `@${at}` : ''}`;
|
|
522
562
|
if (!keys.includes(key))
|
|
523
563
|
keys.push(key);
|
|
524
|
-
}
|
|
564
|
+
});
|
|
525
565
|
}
|
|
526
566
|
else {
|
|
527
567
|
const z = Math.min(zoom, maxzoom);
|
|
@@ -541,26 +581,22 @@ export class VectorTiles {
|
|
|
541
581
|
Math.max(0, ty(north), Math.floor(cy) - reach),
|
|
542
582
|
Math.min(n - 1, ty(south), Math.floor(cy) + reach),
|
|
543
583
|
];
|
|
544
|
-
const wanted = [];
|
|
545
|
-
for (let y = y0; y <= y1; y += 1)
|
|
546
|
-
for (let x = x0; x <= x1; x += 1)
|
|
547
|
-
wanted.push([x, y]);
|
|
584
|
+
const wanted = Array.from({ length: y1 - y0 + 1 }, (_, j) => y0 + j).flatMap((y) => Array.from({ length: x1 - x0 + 1 }, (_, i) => [x0 + i, y]));
|
|
548
585
|
// Nearest to the center first, at most `maxTiles` (tilted views reach far)
|
|
549
586
|
wanted.sort((a, b) => Math.hypot(a[0] + 0.5 - cx, a[1] + 0.5 - cy) - Math.hypot(b[0] + 0.5 - cx, b[1] + 0.5 - cy));
|
|
550
587
|
// Past the source's deepest level, its tiles' labels at the zoom of the view (the layers of the deeper zooms)
|
|
551
|
-
|
|
552
|
-
keys.push(`${z}/${x}/${y}${zoom > z ? `@${zoom}` : ''}`);
|
|
588
|
+
keys.push(...wanted.slice(0, maxTiles).map(([x, y]) => `${z}/${x}/${y}${zoom > z ? `@${zoom}` : ''}`));
|
|
553
589
|
}
|
|
554
590
|
// The same tiles as the labels shown: nothing to do
|
|
555
591
|
const set = keys.join(' ');
|
|
556
592
|
if (set === shown)
|
|
557
593
|
return;
|
|
558
594
|
const lists = await Promise.all(keys.map((k, index) => {
|
|
559
|
-
|
|
560
|
-
if (
|
|
561
|
-
return
|
|
595
|
+
const cached = tiles.get(k);
|
|
596
|
+
if (cached)
|
|
597
|
+
return cached;
|
|
562
598
|
const [tile, deeper] = k.split('@');
|
|
563
|
-
const [tz, x, y] = (tile ?? '').split('/').map(Number);
|
|
599
|
+
const [tz = 0, x = 0, y = 0] = (tile ?? '').split('/').map(Number);
|
|
564
600
|
// The nearest tiles first: the others wait for the raster tiles
|
|
565
601
|
const priority = index < 4 ? 'auto' : 'low';
|
|
566
602
|
const work = {
|
|
@@ -572,7 +608,7 @@ export class VectorTiles {
|
|
|
572
608
|
priority,
|
|
573
609
|
...(deeper === undefined ? {} : { zoom: Number(deeper) }),
|
|
574
610
|
};
|
|
575
|
-
p = this.request(tz, x, y, work).then((r) => tileLabels('labels' in r ? r.labels : []), () => {
|
|
611
|
+
const p = this.request(tz, x, y, work).then((r) => tileLabels('labels' in r ? r.labels : []), () => {
|
|
576
612
|
// A network error is not kept: the tile is asked for again on the next update
|
|
577
613
|
tiles.delete(k);
|
|
578
614
|
return [];
|
|
@@ -594,14 +630,7 @@ export class VectorTiles {
|
|
|
594
630
|
// The labels of the higher style layers first: they win the collisions; for each style layer, the tiles nearest
|
|
595
631
|
// to the center first
|
|
596
632
|
const layerIds = [...new Set(lists.flatMap((list) => list.map(([layer]) => layer)))].sort((a, b) => b - a);
|
|
597
|
-
const groups = [];
|
|
598
|
-
for (const layer of layerIds) {
|
|
599
|
-
for (const list of lists) {
|
|
600
|
-
const group = list.find(([l]) => l === layer)?.[1];
|
|
601
|
-
if (group)
|
|
602
|
-
groups.push(group);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
633
|
+
const groups = layerIds.flatMap((layer) => lists.map((list) => list.find(([l]) => l === layer)?.[1]).filter((group) => group !== undefined));
|
|
605
634
|
symbol.setGroups(groups);
|
|
606
635
|
};
|
|
607
636
|
// The style failed to load: reported by `ready`
|
|
@@ -629,19 +658,22 @@ export class VectorTiles {
|
|
|
629
658
|
if (this.destroyed)
|
|
630
659
|
return;
|
|
631
660
|
this.destroyed = true;
|
|
632
|
-
|
|
661
|
+
this.cleanup.forEach((off) => {
|
|
633
662
|
off();
|
|
663
|
+
});
|
|
634
664
|
this.cleanup.length = 0;
|
|
635
|
-
|
|
665
|
+
this.layers.forEach((layer) => {
|
|
636
666
|
if (this.map.hasLayer(layer))
|
|
637
667
|
layer.remove();
|
|
668
|
+
});
|
|
638
669
|
this.layers.length = 0;
|
|
639
670
|
this.map.dropVectorTiles(this);
|
|
640
671
|
// The page's copy of the sprites (the workers have theirs), and their icons
|
|
641
672
|
this.sprites
|
|
642
673
|
?.then((sprites) => {
|
|
643
|
-
|
|
674
|
+
sprites.forEach((sprite) => {
|
|
644
675
|
sprite.image.close();
|
|
676
|
+
});
|
|
645
677
|
this.map.icons.remove(this.iconPrefix, true);
|
|
646
678
|
})
|
|
647
679
|
.catch(() => undefined);
|
|
@@ -651,8 +683,9 @@ export class VectorTiles {
|
|
|
651
683
|
else {
|
|
652
684
|
// Shared workers: only its requests stop and its renderers go
|
|
653
685
|
this.pool.cancel(this);
|
|
654
|
-
|
|
686
|
+
this.renderers.forEach((key) => {
|
|
655
687
|
this.pool.broadcast({ type: 'drop', key });
|
|
688
|
+
});
|
|
656
689
|
}
|
|
657
690
|
this.renderers.length = 0;
|
|
658
691
|
}
|
|
@@ -665,10 +698,10 @@ const BASIC_COLORS = ['#2f6fd1', '#d1495b', '#2a9d8f', '#e9a03b', '#7b5ea7', '#5
|
|
|
665
698
|
* layer, the `name` of points as labels. `source` is the style's vector source (`url`: TileJSON or `pmtiles://…`, or
|
|
666
699
|
* `tiles`)
|
|
667
700
|
*/
|
|
668
|
-
export
|
|
701
|
+
export const basicStyle = (source, layers) => {
|
|
669
702
|
const out = [];
|
|
670
703
|
layers.forEach((name, i) => {
|
|
671
|
-
const color = BASIC_COLORS[i % BASIC_COLORS.length];
|
|
704
|
+
const color = BASIC_COLORS[i % BASIC_COLORS.length] ?? '#2f6fd1';
|
|
672
705
|
const base = { source: 'data', 'source-layer': name };
|
|
673
706
|
out.push({ id: `${name}-fill`, type: 'fill', ...base, paint: { 'fill-color': color, 'fill-opacity': 0.18 } }, { id: `${name}-line`, type: 'line', ...base, paint: { 'line-color': color, 'line-width': 1.2 } }, {
|
|
674
707
|
id: `${name}-circle`,
|
|
@@ -689,21 +722,24 @@ export function basicStyle(source, layers) {
|
|
|
689
722
|
});
|
|
690
723
|
});
|
|
691
724
|
return { version: 8, sources: { data: { type: 'vector', ...source } }, layers: out };
|
|
692
|
-
}
|
|
725
|
+
};
|
|
693
726
|
/** HTML of a file reduced to its text, escaped: credits from an archive's metadata cannot run in the page */
|
|
694
|
-
|
|
727
|
+
const plainText = (html) => {
|
|
695
728
|
return html
|
|
696
729
|
.replace(/<[^>]*>/g, '')
|
|
697
730
|
.replace(/&(?!(amp|lt|gt|quot|copy|#\d+);)/g, '&')
|
|
698
731
|
.replace(/</g, '<')
|
|
699
732
|
.replace(/>/g, '>');
|
|
700
|
-
}
|
|
733
|
+
};
|
|
701
734
|
/** @internal `GeoMap.addPmtiles`: a raster layer of the archive's images, or vector tiles with their raster layer and
|
|
702
735
|
* labels */
|
|
703
|
-
export async
|
|
704
|
-
const archiveUrl = pmtilesUrl(url.startsWith('pmtiles://') ? url : `pmtiles://${url}`);
|
|
736
|
+
export const pmtilesLayer = async (map, url, options) => {
|
|
737
|
+
const archiveUrl = pmtilesUrl(url.startsWith('pmtiles://') ? url : `pmtiles://${url}`) ?? url;
|
|
705
738
|
const archive = pmtiles(archiveUrl);
|
|
706
|
-
const [header, metadata] = await Promise.all([
|
|
739
|
+
const [header, metadata] = await Promise.all([
|
|
740
|
+
archive.header(),
|
|
741
|
+
archive.metadata().catch(() => ({})),
|
|
742
|
+
]);
|
|
707
743
|
const credit = metadata.attribution;
|
|
708
744
|
const attribution = options.attribution ?? (typeof credit === 'string' && credit ? plainText(credit) : undefined);
|
|
709
745
|
const kind = options.kind ?? (header.tileType === 'mvt' ? 'vector' : 'raster');
|
|
@@ -720,7 +756,9 @@ export async function pmtilesLayer(map, url, options) {
|
|
|
720
756
|
});
|
|
721
757
|
}
|
|
722
758
|
const layers = metadata.vector_layers;
|
|
723
|
-
const names = Array.isArray(layers)
|
|
759
|
+
const names = Array.isArray(layers)
|
|
760
|
+
? layers.map((l) => (isRecord(l) ? l.id : undefined)).filter((id) => typeof id === 'string')
|
|
761
|
+
: [];
|
|
724
762
|
const style = options.style ?? basicStyle({ url: `pmtiles://${archiveUrl}` }, names);
|
|
725
763
|
const tiles = map.vectorTiles({ style, ...credits });
|
|
726
764
|
tiles.raster({
|
|
@@ -729,10 +767,10 @@ export async function pmtilesLayer(map, url, options) {
|
|
|
729
767
|
});
|
|
730
768
|
tiles.labels();
|
|
731
769
|
return tiles;
|
|
732
|
-
}
|
|
770
|
+
};
|
|
733
771
|
/** A raster loader reading the tiles of a `pmtiles://` archive URL (images of any size, drawn at `size`); undefined
|
|
734
772
|
* for another URL */
|
|
735
|
-
export
|
|
773
|
+
export const pmtilesRasterLoader = (url, size) => {
|
|
736
774
|
const archiveUrl = pmtilesUrl(url);
|
|
737
775
|
if (!archiveUrl)
|
|
738
776
|
return undefined;
|
|
@@ -743,5 +781,5 @@ export function pmtilesRasterLoader(url, size) {
|
|
|
743
781
|
return null;
|
|
744
782
|
return createImageBitmap(new Blob([bytes]), { resizeWidth: size, resizeHeight: size });
|
|
745
783
|
};
|
|
746
|
-
}
|
|
784
|
+
};
|
|
747
785
|
//# sourceMappingURL=vectortiles.js.map
|