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.
Files changed (216) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/basemap/basemaps.d.ts +1 -1
  3. package/dist/basemap/basemaps.js +2 -2
  4. package/dist/basemap/basemaps.js.map +1 -1
  5. package/dist/basemap/grid.d.ts +5 -5
  6. package/dist/basemap/grid.js +59 -65
  7. package/dist/basemap/grid.js.map +1 -1
  8. package/dist/basemap/mvt.d.ts +1 -1
  9. package/dist/basemap/mvt.js +6 -6
  10. package/dist/basemap/mvt.js.map +1 -1
  11. package/dist/basemap/pmtiles.d.ts +10 -10
  12. package/dist/basemap/pmtiles.js +39 -31
  13. package/dist/basemap/pmtiles.js.map +1 -1
  14. package/dist/basemap/resample.d.ts +2 -2
  15. package/dist/basemap/resample.js +16 -14
  16. package/dist/basemap/resample.js.map +1 -1
  17. package/dist/basemap/terrain.d.ts +3 -3
  18. package/dist/basemap/terrain.js +6 -6
  19. package/dist/basemap/terrain.js.map +1 -1
  20. package/dist/basemap/vectorstyle.d.ts +12 -10
  21. package/dist/basemap/vectorstyle.js +128 -110
  22. package/dist/basemap/vectorstyle.js.map +1 -1
  23. package/dist/basemap/vectortiles.d.ts +11 -11
  24. package/dist/basemap/vectortiles.js +141 -103
  25. package/dist/basemap/vectortiles.js.map +1 -1
  26. package/dist/basemap/wmts.d.ts +3 -3
  27. package/dist/basemap/wmts.js +16 -11
  28. package/dist/basemap/wmts.js.map +1 -1
  29. package/dist/basemap.worker.js +2 -2
  30. package/dist/basemap.worker.js.map +14 -13
  31. package/dist/bind/layers.d.ts +13 -4
  32. package/dist/bind/layers.js +64 -40
  33. package/dist/bind/layers.js.map +1 -1
  34. package/dist/data/crs.d.ts +10 -10
  35. package/dist/data/crs.js +38 -34
  36. package/dist/data/crs.js.map +1 -1
  37. package/dist/data/gpx.d.ts +1 -1
  38. package/dist/data/gpx.js +20 -20
  39. package/dist/data/gpx.js.map +1 -1
  40. package/dist/data/kml.d.ts +1 -1
  41. package/dist/data/kml.js +42 -39
  42. package/dist/data/kml.js.map +1 -1
  43. package/dist/data/topojson.d.ts +1 -1
  44. package/dist/data/topojson.js +15 -10
  45. package/dist/data/topojson.js.map +1 -1
  46. package/dist/data/xml.d.ts +5 -5
  47. package/dist/data/xml.js +19 -18
  48. package/dist/data/xml.js.map +1 -1
  49. package/dist/decoder.worker.js +4 -4
  50. package/dist/decoder.worker.js.map +6 -6
  51. package/dist/engine/decoders.d.ts +2 -2
  52. package/dist/engine/decoders.js +28 -25
  53. package/dist/engine/decoders.js.map +1 -1
  54. package/dist/engine/loader.js +13 -14
  55. package/dist/engine/loader.js.map +1 -1
  56. package/dist/engine/requests.d.ts +1 -1
  57. package/dist/engine/requests.js +19 -16
  58. package/dist/engine/requests.js.map +1 -1
  59. package/dist/engine-loader.d.ts +8 -8
  60. package/dist/engine-loader.js +27 -27
  61. package/dist/engine-loader.js.map +1 -1
  62. package/dist/geoplateforme.d.ts +10 -10
  63. package/dist/geoplateforme.js +22 -20
  64. package/dist/geoplateforme.js.map +1 -1
  65. package/dist/index.d.ts +1 -0
  66. package/dist/index.js.map +1 -1
  67. package/dist/isard_bg.wasm +0 -0
  68. package/dist/isard_core_bg.wasm +0 -0
  69. package/dist/isard_points_bg.wasm +0 -0
  70. package/dist/layers/extrusions.d.ts +1 -1
  71. package/dist/layers/extrusions.js +10 -5
  72. package/dist/layers/extrusions.js.map +1 -1
  73. package/dist/layers/features.d.ts +10 -10
  74. package/dist/layers/features.js +41 -42
  75. package/dist/layers/features.js.map +1 -1
  76. package/dist/layers/glyphs.d.ts +3 -3
  77. package/dist/layers/glyphs.js +34 -40
  78. package/dist/layers/glyphs.js.map +1 -1
  79. package/dist/layers/icons.d.ts +8 -8
  80. package/dist/layers/icons.js +76 -68
  81. package/dist/layers/icons.js.map +1 -1
  82. package/dist/layers/layers.d.ts +1 -1
  83. package/dist/layers/layers.js +33 -29
  84. package/dist/layers/layers.js.map +1 -1
  85. package/dist/layers/panoramas.d.ts +2 -2
  86. package/dist/layers/panoramas.js +52 -41
  87. package/dist/layers/panoramas.js.map +1 -1
  88. package/dist/layers/pointclouds.d.ts +3 -3
  89. package/dist/layers/pointclouds.js +25 -33
  90. package/dist/layers/pointclouds.js.map +1 -1
  91. package/dist/layers/symbols.d.ts +1 -1
  92. package/dist/layers/symbols.js +68 -71
  93. package/dist/layers/symbols.js.map +1 -1
  94. package/dist/map/basemap-layers.d.ts +26 -0
  95. package/dist/map/basemap-layers.js +61 -0
  96. package/dist/map/basemap-layers.js.map +1 -0
  97. package/dist/map/canvas.d.ts +17 -0
  98. package/dist/map/canvas.js +56 -0
  99. package/dist/map/canvas.js.map +1 -0
  100. package/dist/map/checks.d.ts +11 -0
  101. package/dist/map/checks.js +39 -0
  102. package/dist/map/checks.js.map +1 -0
  103. package/dist/map/credits.d.ts +17 -0
  104. package/dist/map/credits.js +34 -0
  105. package/dist/map/credits.js.map +1 -0
  106. package/dist/map/engine-settings.d.ts +15 -0
  107. package/dist/map/engine-settings.js +45 -0
  108. package/dist/map/engine-settings.js.map +1 -0
  109. package/dist/map/engines.d.ts +41 -0
  110. package/dist/map/engines.js +77 -0
  111. package/dist/map/engines.js.map +1 -0
  112. package/dist/map/events.d.ts +14 -0
  113. package/dist/map/events.js +46 -0
  114. package/dist/map/events.js.map +1 -0
  115. package/dist/map/frame-loop.d.ts +61 -0
  116. package/dist/map/frame-loop.js +139 -0
  117. package/dist/map/frame-loop.js.map +1 -0
  118. package/dist/map/hover-pick.d.ts +30 -0
  119. package/dist/map/hover-pick.js +56 -0
  120. package/dist/map/hover-pick.js.map +1 -0
  121. package/dist/map/layer-spec.d.ts +42 -0
  122. package/dist/map/layer-spec.js +34 -0
  123. package/dist/map/layer-spec.js.map +1 -0
  124. package/dist/map/layer-stack.d.ts +36 -0
  125. package/dist/map/layer-stack.js +101 -0
  126. package/dist/map/layer-stack.js.map +1 -0
  127. package/dist/map/overlay-host.d.ts +36 -0
  128. package/dist/map/overlay-host.js +101 -0
  129. package/dist/map/overlay-host.js.map +1 -0
  130. package/dist/map/panorama-host.d.ts +5 -0
  131. package/dist/map/panorama-host.js +32 -0
  132. package/dist/map/panorama-host.js.map +1 -0
  133. package/dist/map/raster-source.d.ts +11 -0
  134. package/dist/map/raster-source.js +26 -0
  135. package/dist/map/raster-source.js.map +1 -0
  136. package/dist/map/symbol-atlases.d.ts +26 -0
  137. package/dist/map/symbol-atlases.js +59 -0
  138. package/dist/map/symbol-atlases.js.map +1 -0
  139. package/dist/map/viewshed.d.ts +5 -0
  140. package/dist/map/viewshed.js +28 -0
  141. package/dist/map/viewshed.js.map +1 -0
  142. package/dist/map/workers.d.ts +46 -0
  143. package/dist/map/workers.js +73 -0
  144. package/dist/map/workers.js.map +1 -0
  145. package/dist/map.d.ts +33 -120
  146. package/dist/map.js +164 -689
  147. package/dist/map.js.map +1 -1
  148. package/dist/react/Control.d.ts +11 -0
  149. package/dist/react/Control.js +18 -0
  150. package/dist/react/Control.js.map +1 -0
  151. package/dist/react/CustomLayer.d.ts +11 -0
  152. package/dist/react/CustomLayer.js +30 -0
  153. package/dist/react/CustomLayer.js.map +1 -0
  154. package/dist/react/IsardMap.d.ts +43 -0
  155. package/dist/react/IsardMap.js +146 -0
  156. package/dist/react/IsardMap.js.map +1 -0
  157. package/dist/react/Layer.d.ts +42 -0
  158. package/dist/react/Layer.js +74 -0
  159. package/dist/react/Layer.js.map +1 -0
  160. package/dist/react/Marker.d.ts +24 -0
  161. package/dist/react/Marker.js +53 -0
  162. package/dist/react/Marker.js.map +1 -0
  163. package/dist/react/Panel.d.ts +11 -0
  164. package/dist/react/Panel.js +37 -0
  165. package/dist/react/Panel.js.map +1 -0
  166. package/dist/react/Popup.d.ts +18 -0
  167. package/dist/react/Popup.js +60 -0
  168. package/dist/react/Popup.js.map +1 -0
  169. package/dist/react/Swipe.d.ts +9 -0
  170. package/dist/react/Swipe.js +41 -0
  171. package/dist/react/Swipe.js.map +1 -0
  172. package/dist/react/Viewshed.d.ts +5 -0
  173. package/dist/react/Viewshed.js +28 -0
  174. package/dist/react/Viewshed.js.map +1 -0
  175. package/dist/react/context.d.ts +32 -0
  176. package/dist/react/context.js +20 -0
  177. package/dist/react/context.js.map +1 -0
  178. package/dist/react/index.d.ts +15 -108
  179. package/dist/react/index.js +10 -397
  180. package/dist/react/index.js.map +1 -1
  181. package/dist/settings.d.ts +5 -9
  182. package/dist/settings.js +17 -15
  183. package/dist/settings.js.map +1 -1
  184. package/dist/types.d.ts +3 -3
  185. package/dist/types.js.map +1 -1
  186. package/dist/ui/controls.js +6 -4
  187. package/dist/ui/controls.js.map +1 -1
  188. package/dist/ui/interaction.d.ts +2 -2
  189. package/dist/ui/interaction.js +8 -7
  190. package/dist/ui/interaction.js.map +1 -1
  191. package/dist/ui/overlay.js +18 -20
  192. package/dist/ui/overlay.js.map +1 -1
  193. package/dist/ui/style.d.ts +1 -1
  194. package/dist/ui/style.js +2 -2
  195. package/dist/ui/style.js.map +1 -1
  196. package/dist/util/animation.d.ts +3 -3
  197. package/dist/util/animation.js +6 -6
  198. package/dist/util/animation.js.map +1 -1
  199. package/dist/util/color.d.ts +1 -1
  200. package/dist/util/color.js +14 -16
  201. package/dist/util/color.js.map +1 -1
  202. package/dist/util/geo.d.ts +6 -6
  203. package/dist/util/geo.js +12 -12
  204. package/dist/util/geo.js.map +1 -1
  205. package/dist/util/guards.d.ts +7 -0
  206. package/dist/util/guards.js +8 -0
  207. package/dist/util/guards.js.map +1 -0
  208. package/dist/util/lru.js.map +1 -1
  209. package/dist/wasm/isard.d.ts +168 -0
  210. package/dist/wasm/isard.js +146 -0
  211. package/dist/wasm/isard_core.d.ts +144 -0
  212. package/dist/wasm/isard_core.js +122 -0
  213. package/dist/workers/basemap.worker.d.ts +3 -3
  214. package/dist/workers/basemap.worker.js +94 -83
  215. package/dist/workers/basemap.worker.js.map +1 -1
  216. 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 function styleAttribution(style) {
42
+ export const styleAttribution = (style) => {
42
43
  if (typeof style !== 'string')
43
44
  return undefined;
44
- if (Object.values(OPENFREEMAP_STYLES).includes(style))
45
+ if (Object.values(OPENFREEMAP_STYLES).some((s) => s === style))
45
46
  return OPENFREEMAP_ATTRIBUTION;
46
- if (Object.values(PLAN_STYLES).includes(style))
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 function size(value, fallback = 16) {
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((s) => [Number(s[0]), Number(s[1])]);
57
- }
58
- if (value && typeof value === 'object' && !Array.isArray(value) && Array.isArray(value.stops)) {
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 function json(url) {
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 (await response.json());
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 function resolveSource(source) {
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 function defaultWorkerCount() {
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 function defaultWorkerUrl(assetsUrl) {
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 function tilePriority(z, x, y, camera) {
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
- let dx = Math.abs(x + 0.5 - cx) % n;
110
- dx = Math.min(dx, n - dx);
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 function workerIndex(vz, vx, vy, count) {
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 function rendererKey(namespace, n) {
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
- this.workers = Array.from({ length: Math.max(1, count) }, () => {
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
- for (const w of this.workers)
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
- for (const [id, p] of this.pending) {
217
+ this.pending.forEach((p, id) => {
183
218
  if (p.worker !== worker)
184
- continue;
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
- for (const w of this.workers)
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
- for (const [id, p] of this.pending) {
286
+ this.pending.forEach((p, id) => {
251
287
  if (p.owner !== owner)
252
- continue;
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
- for (const w of this.workers)
300
+ this.workers.forEach((w) => {
265
301
  w.terminate();
302
+ });
266
303
  const pending = [...this.pending.values()];
267
304
  this.pending.clear();
268
- for (const p of pending)
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 function tileLabels(candidates) {
311
+ export const tileLabels = (candidates) => {
274
312
  const byLayer = new Map();
275
- for (const p of candidates) {
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 json(input) : input;
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
- let { tiles, maxzoom } = await resolveSource(source);
342
- if (!tiles && source.url) {
343
- const tilejson = await json(source.url);
344
- tiles = tilejson.tiles;
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
- for (const layer of style.layers) {
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 ?? 'center'),
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
- for (const sprite of await this.loadSprites(style.sprite)) {
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
- let top = 0;
514
- for (let i = 0; i < ground.length; i += 3)
515
- top = Math.max(top, ground[i]);
516
- for (let i = 0; i < ground.length && keys.length < maxTiles; i += 3) {
517
- const [z, x, y] = [ground[i], ground[i + 1], ground[i + 2]];
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
- for (const [x, y] of wanted.slice(0, maxTiles))
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
- let p = tiles.get(k);
560
- if (p)
561
- return p;
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
- for (const off of this.cleanup)
661
+ this.cleanup.forEach((off) => {
633
662
  off();
663
+ });
634
664
  this.cleanup.length = 0;
635
- for (const layer of this.layers)
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
- for (const sprite of sprites)
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
- for (const key of this.renderers)
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 function basicStyle(source, layers) {
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
- function plainText(html) {
727
+ const plainText = (html) => {
695
728
  return html
696
729
  .replace(/<[^>]*>/g, '')
697
730
  .replace(/&(?!(amp|lt|gt|quot|copy|#\d+);)/g, '&amp;')
698
731
  .replace(/</g, '&lt;')
699
732
  .replace(/>/g, '&gt;');
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 function pmtilesLayer(map, url, options) {
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([archive.header(), archive.metadata().catch(() => ({}))]);
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) ? layers.map((l) => l?.id).filter((id) => typeof id === 'string') : [];
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 function pmtilesRasterLoader(url, size) {
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