isard 1.1.0 → 1.3.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 (254) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +44 -39
  3. package/dist/basemap/basemaps.d.ts +1 -1
  4. package/dist/basemap/basemaps.js +2 -2
  5. package/dist/basemap/basemaps.js.map +1 -1
  6. package/dist/basemap/grid.d.ts +5 -5
  7. package/dist/basemap/grid.js +59 -65
  8. package/dist/basemap/grid.js.map +1 -1
  9. package/dist/basemap/mvt.d.ts +1 -1
  10. package/dist/basemap/mvt.js +6 -6
  11. package/dist/basemap/mvt.js.map +1 -1
  12. package/dist/basemap/pmtiles.d.ts +10 -10
  13. package/dist/basemap/pmtiles.js +39 -31
  14. package/dist/basemap/pmtiles.js.map +1 -1
  15. package/dist/basemap/pmtileslayer.d.ts +33 -0
  16. package/dist/basemap/pmtileslayer.js +97 -0
  17. package/dist/basemap/pmtileslayer.js.map +1 -0
  18. package/dist/basemap/resample.d.ts +2 -2
  19. package/dist/basemap/resample.js +16 -14
  20. package/dist/basemap/resample.js.map +1 -1
  21. package/dist/basemap/terrain.d.ts +3 -3
  22. package/dist/basemap/terrain.js +6 -6
  23. package/dist/basemap/terrain.js.map +1 -1
  24. package/dist/basemap/trees.d.ts +26 -0
  25. package/dist/basemap/trees.js +141 -0
  26. package/dist/basemap/trees.js.map +1 -0
  27. package/dist/basemap/vectorstyle.d.ts +22 -10
  28. package/dist/basemap/vectorstyle.js +163 -110
  29. package/dist/basemap/vectorstyle.js.map +1 -1
  30. package/dist/basemap/vectortiles.d.ts +70 -47
  31. package/dist/basemap/vectortiles.js +480 -358
  32. package/dist/basemap/vectortiles.js.map +1 -1
  33. package/dist/basemap/wmts.d.ts +3 -3
  34. package/dist/basemap/wmts.js +16 -11
  35. package/dist/basemap/wmts.js.map +1 -1
  36. package/dist/basemap/workerpool.d.ts +17 -0
  37. package/dist/basemap/workerpool.js +194 -0
  38. package/dist/basemap/workerpool.js.map +1 -0
  39. package/dist/basemap.worker.js +2 -2
  40. package/dist/basemap.worker.js.map +21 -13
  41. package/dist/bind/layers.d.ts +13 -4
  42. package/dist/bind/layers.js +64 -40
  43. package/dist/bind/layers.js.map +1 -1
  44. package/dist/data/crs.d.ts +10 -10
  45. package/dist/data/crs.js +38 -34
  46. package/dist/data/crs.js.map +1 -1
  47. package/dist/data/gpx.d.ts +1 -1
  48. package/dist/data/gpx.js +20 -20
  49. package/dist/data/gpx.js.map +1 -1
  50. package/dist/data/kml.d.ts +1 -1
  51. package/dist/data/kml.js +42 -39
  52. package/dist/data/kml.js.map +1 -1
  53. package/dist/data/topojson.d.ts +1 -1
  54. package/dist/data/topojson.js +15 -10
  55. package/dist/data/topojson.js.map +1 -1
  56. package/dist/data/xml.d.ts +5 -5
  57. package/dist/data/xml.js +19 -18
  58. package/dist/data/xml.js.map +1 -1
  59. package/dist/decoder.worker.js +4 -4
  60. package/dist/decoder.worker.js.map +6 -6
  61. package/dist/engine/decoders.d.ts +2 -2
  62. package/dist/engine/decoders.js +28 -25
  63. package/dist/engine/decoders.js.map +1 -1
  64. package/dist/engine/loader.js +13 -14
  65. package/dist/engine/loader.js.map +1 -1
  66. package/dist/engine/requests.d.ts +1 -1
  67. package/dist/engine/requests.js +19 -16
  68. package/dist/engine/requests.js.map +1 -1
  69. package/dist/engine-loader.d.ts +13 -9
  70. package/dist/engine-loader.js +38 -28
  71. package/dist/engine-loader.js.map +1 -1
  72. package/dist/geoplateforme.d.ts +26 -10
  73. package/dist/geoplateforme.js +45 -20
  74. package/dist/geoplateforme.js.map +1 -1
  75. package/dist/index.d.ts +12 -4
  76. package/dist/index.js +9 -2
  77. package/dist/index.js.map +1 -1
  78. package/dist/isard_bg.wasm +0 -0
  79. package/dist/isard_core_bg.wasm +0 -0
  80. package/dist/isard_points_bg.wasm +0 -0
  81. package/dist/isard_tess_bg.wasm +0 -0
  82. package/dist/layers/extrusions.d.ts +1 -1
  83. package/dist/layers/extrusions.js +10 -5
  84. package/dist/layers/extrusions.js.map +1 -1
  85. package/dist/layers/features.d.ts +10 -10
  86. package/dist/layers/features.js +41 -42
  87. package/dist/layers/features.js.map +1 -1
  88. package/dist/layers/glyphs.d.ts +3 -3
  89. package/dist/layers/glyphs.js +34 -40
  90. package/dist/layers/glyphs.js.map +1 -1
  91. package/dist/layers/icons.d.ts +8 -8
  92. package/dist/layers/icons.js +76 -68
  93. package/dist/layers/icons.js.map +1 -1
  94. package/dist/layers/layers.d.ts +1 -1
  95. package/dist/layers/layers.js +42 -29
  96. package/dist/layers/layers.js.map +1 -1
  97. package/dist/layers/panoramas.d.ts +2 -2
  98. package/dist/layers/panoramas.js +52 -41
  99. package/dist/layers/panoramas.js.map +1 -1
  100. package/dist/layers/pointclouds.d.ts +3 -3
  101. package/dist/layers/pointclouds.js +25 -33
  102. package/dist/layers/pointclouds.js.map +1 -1
  103. package/dist/layers/symbols.d.ts +1 -1
  104. package/dist/layers/symbols.js +68 -71
  105. package/dist/layers/symbols.js.map +1 -1
  106. package/dist/layers/vectors.d.ts +60 -0
  107. package/dist/layers/vectors.js +130 -0
  108. package/dist/layers/vectors.js.map +1 -0
  109. package/dist/map/basemap-layers.d.ts +26 -0
  110. package/dist/map/basemap-layers.js +61 -0
  111. package/dist/map/basemap-layers.js.map +1 -0
  112. package/dist/map/canvas.d.ts +17 -0
  113. package/dist/map/canvas.js +56 -0
  114. package/dist/map/canvas.js.map +1 -0
  115. package/dist/map/checks.d.ts +11 -0
  116. package/dist/map/checks.js +39 -0
  117. package/dist/map/checks.js.map +1 -0
  118. package/dist/map/credits.d.ts +17 -0
  119. package/dist/map/credits.js +34 -0
  120. package/dist/map/credits.js.map +1 -0
  121. package/dist/map/engine-settings.d.ts +15 -0
  122. package/dist/map/engine-settings.js +45 -0
  123. package/dist/map/engine-settings.js.map +1 -0
  124. package/dist/map/engines.d.ts +41 -0
  125. package/dist/map/engines.js +77 -0
  126. package/dist/map/engines.js.map +1 -0
  127. package/dist/map/events.d.ts +14 -0
  128. package/dist/map/events.js +46 -0
  129. package/dist/map/events.js.map +1 -0
  130. package/dist/map/frame-loop.d.ts +64 -0
  131. package/dist/map/frame-loop.js +145 -0
  132. package/dist/map/frame-loop.js.map +1 -0
  133. package/dist/map/hover-pick.d.ts +30 -0
  134. package/dist/map/hover-pick.js +56 -0
  135. package/dist/map/hover-pick.js.map +1 -0
  136. package/dist/map/layer-spec.d.ts +42 -0
  137. package/dist/map/layer-spec.js +34 -0
  138. package/dist/map/layer-spec.js.map +1 -0
  139. package/dist/map/layer-stack.d.ts +36 -0
  140. package/dist/map/layer-stack.js +101 -0
  141. package/dist/map/layer-stack.js.map +1 -0
  142. package/dist/map/overlay-host.d.ts +36 -0
  143. package/dist/map/overlay-host.js +101 -0
  144. package/dist/map/overlay-host.js.map +1 -0
  145. package/dist/map/panorama-host.d.ts +5 -0
  146. package/dist/map/panorama-host.js +32 -0
  147. package/dist/map/panorama-host.js.map +1 -0
  148. package/dist/map/raster-source.d.ts +11 -0
  149. package/dist/map/raster-source.js +26 -0
  150. package/dist/map/raster-source.js.map +1 -0
  151. package/dist/map/symbol-atlases.d.ts +26 -0
  152. package/dist/map/symbol-atlases.js +59 -0
  153. package/dist/map/symbol-atlases.js.map +1 -0
  154. package/dist/map/viewshed.d.ts +5 -0
  155. package/dist/map/viewshed.js +28 -0
  156. package/dist/map/viewshed.js.map +1 -0
  157. package/dist/map/workers.d.ts +46 -0
  158. package/dist/map/workers.js +73 -0
  159. package/dist/map/workers.js.map +1 -0
  160. package/dist/map.d.ts +54 -126
  161. package/dist/map.js +227 -698
  162. package/dist/map.js.map +1 -1
  163. package/dist/react/Control.d.ts +11 -0
  164. package/dist/react/Control.js +18 -0
  165. package/dist/react/Control.js.map +1 -0
  166. package/dist/react/CustomLayer.d.ts +11 -0
  167. package/dist/react/CustomLayer.js +30 -0
  168. package/dist/react/CustomLayer.js.map +1 -0
  169. package/dist/react/IsardMap.d.ts +43 -0
  170. package/dist/react/IsardMap.js +147 -0
  171. package/dist/react/IsardMap.js.map +1 -0
  172. package/dist/react/Layer.d.ts +42 -0
  173. package/dist/react/Layer.js +74 -0
  174. package/dist/react/Layer.js.map +1 -0
  175. package/dist/react/Marker.d.ts +24 -0
  176. package/dist/react/Marker.js +53 -0
  177. package/dist/react/Marker.js.map +1 -0
  178. package/dist/react/Panel.d.ts +11 -0
  179. package/dist/react/Panel.js +37 -0
  180. package/dist/react/Panel.js.map +1 -0
  181. package/dist/react/Popup.d.ts +18 -0
  182. package/dist/react/Popup.js +60 -0
  183. package/dist/react/Popup.js.map +1 -0
  184. package/dist/react/Swipe.d.ts +16 -0
  185. package/dist/react/Swipe.js +52 -0
  186. package/dist/react/Swipe.js.map +1 -0
  187. package/dist/react/VectorGeometry.d.ts +35 -0
  188. package/dist/react/VectorGeometry.js +82 -0
  189. package/dist/react/VectorGeometry.js.map +1 -0
  190. package/dist/react/Viewshed.d.ts +5 -0
  191. package/dist/react/Viewshed.js +28 -0
  192. package/dist/react/Viewshed.js.map +1 -0
  193. package/dist/react/context.d.ts +33 -0
  194. package/dist/react/context.js +20 -0
  195. package/dist/react/context.js.map +1 -0
  196. package/dist/react/index.d.ts +16 -108
  197. package/dist/react/index.js +11 -397
  198. package/dist/react/index.js.map +1 -1
  199. package/dist/settings.d.ts +5 -9
  200. package/dist/settings.js +17 -15
  201. package/dist/settings.js.map +1 -1
  202. package/dist/types.d.ts +27 -4
  203. package/dist/types.js.map +1 -1
  204. package/dist/ui/controls.js +6 -4
  205. package/dist/ui/controls.js.map +1 -1
  206. package/dist/ui/interaction.d.ts +2 -2
  207. package/dist/ui/interaction.js +8 -7
  208. package/dist/ui/interaction.js.map +1 -1
  209. package/dist/ui/minimap.d.ts +25 -0
  210. package/dist/ui/minimap.js +90 -0
  211. package/dist/ui/minimap.js.map +1 -0
  212. package/dist/ui/overlay.js +18 -20
  213. package/dist/ui/overlay.js.map +1 -1
  214. package/dist/ui/scale.d.ts +19 -0
  215. package/dist/ui/scale.js +44 -0
  216. package/dist/ui/scale.js.map +1 -0
  217. package/dist/ui/search.d.ts +25 -0
  218. package/dist/ui/search.js +119 -0
  219. package/dist/ui/search.js.map +1 -0
  220. package/dist/ui/style.d.ts +1 -1
  221. package/dist/ui/style.js +19 -2
  222. package/dist/ui/style.js.map +1 -1
  223. package/dist/util/animation.d.ts +3 -3
  224. package/dist/util/animation.js +6 -6
  225. package/dist/util/animation.js.map +1 -1
  226. package/dist/util/color.d.ts +1 -1
  227. package/dist/util/color.js +14 -16
  228. package/dist/util/color.js.map +1 -1
  229. package/dist/util/geo.d.ts +6 -6
  230. package/dist/util/geo.js +12 -12
  231. package/dist/util/geo.js.map +1 -1
  232. package/dist/util/guards.d.ts +7 -0
  233. package/dist/util/guards.js +8 -0
  234. package/dist/util/guards.js.map +1 -0
  235. package/dist/util/lru.js.map +1 -1
  236. package/dist/util/sun.d.ts +11 -0
  237. package/dist/util/sun.js +26 -0
  238. package/dist/util/sun.js.map +1 -0
  239. package/dist/wasm/isard.d.ts +250 -7
  240. package/dist/wasm/isard.js +375 -8
  241. package/dist/wasm/isard_core.d.ts +226 -7
  242. package/dist/wasm/isard_core.js +351 -8
  243. package/dist/wasm/isard_tess.d.ts +109 -0
  244. package/dist/wasm/isard_tess.js +398 -0
  245. package/dist/workers/basemap/geometry.d.ts +72 -0
  246. package/dist/workers/basemap/labels.d.ts +8 -0
  247. package/dist/workers/basemap/raster.d.ts +10 -0
  248. package/dist/workers/basemap/tiles.d.ts +65 -0
  249. package/dist/workers/basemap/trees.d.ts +19 -0
  250. package/dist/workers/basemap.worker.d.ts +126 -22
  251. package/dist/workers/basemap.worker.js +76 -544
  252. package/dist/workers/basemap.worker.js.map +1 -1
  253. package/package.json +5 -4
  254. package/vite.js +10 -2
@@ -2,565 +2,43 @@
2
2
  * Worker of the vector basemaps: fetches and decodes vector tiles (kept for the tiles that share them), draws their
3
3
  * fills, lines and circles with a MapLibre style into images (Canvas 2D, off the main thread) for the raster pool,
4
4
  * with the style's raster layers under them, and lists the label candidates of the style's symbol layers (their texts
5
- * and icons) for the page to shape and place. Extrusions are drawn flat, as fills. It also decodes the altitudes of PNG relief tiles and
6
- * draws the rasters reprojected from other grids (planned by the page), for the map's other layers.
5
+ * and icons) for the page to shape and place. Extrusions are drawn flat, as fills. Or it cuts the fills, lines and
6
+ * buildings of a tile into geometry for the engine to draw as such (`vectors`), with their style per feature (a
7
+ * building's color, height and base height). It also decodes the
8
+ * altitudes of PNG relief tiles and draws the rasters reprojected from other grids (planned by the page), for the
9
+ * map's other layers.
7
10
  *
8
11
  * A raster tile of level z covers 256 CSS pixels at map zoom z - 1 (MapLibre's 512 px convention): it is drawn from
9
12
  * the vector tile of level z - 1 (or the deepest one the source has), with the style at that zoom.
13
+ *
14
+ * This module answers the page's messages; the jobs are in `./basemap/`: the state they share (`tiles.ts`), the
15
+ * images (`raster.ts`), the labels (`labels.ts`), the geometry (`geometry.ts`), the trees (`trees.ts`).
10
16
  */
11
- import { decode } from "../basemap/mvt.js";
12
- import { pmtilesTile } from "../basemap/pmtiles.js";
13
17
  import { resampler } from "../basemap/resample.js";
14
18
  import { fetchHeights } from "../basemap/terrain.js";
15
- import { color, context, css, evaluate, filter, isExpression, isFeatureDependent, labelFont, text, zoomStops, } from "../basemap/vectorstyle.js";
16
- import { setHeaderRules, withHeaders } from "../engine/requests.js";
17
- import { latitude, longitude } from "../util/geo.js";
18
- import { Lru } from "../util/lru.js";
19
- /** An image of the sprites and its sheet */
20
- function spriteIcon(r, name) {
21
- for (const sprite of r.sprites) {
22
- const icon = sprite.icons[name];
23
- if (icon)
24
- return { icon, image: sprite.image };
25
- }
26
- return undefined;
27
- }
28
- const renderers = new Map();
29
- /** Decoded tiles kept by the pool, shared among its workers (a vector tile always goes to the same worker): some
30
- * 2 MB each */
31
- const CACHE = 96;
32
- /** Decoded tiles by URL; this worker's share of them, set by the renderers */
33
- const cache = new Lru(CACHE);
34
- /** The decoded tiles one worker of `workers` keeps */
35
- export function cacheShare(workers) {
36
- return Math.max(16, Math.ceil(CACHE / Math.max(1, workers)));
37
- }
38
- /** Requests in progress, and those of them aborted (both forgotten once answered) */
39
- const active = new Set();
40
- const aborted = new Set();
19
+ import { vectorKind } from "../basemap/vectorstyle.js";
20
+ import { setHeaderRules } from "../engine/requests.js";
21
+ import { polygons, tess, tessellate, tessellator, vectors } from "./basemap/geometry.js";
22
+ import { labels } from "./basemap/labels.js";
23
+ import { render } from "./basemap/raster.js";
24
+ import { aborted, active, cache, cacheShare, renderers } from "./basemap/tiles.js";
25
+ import { trees } from "./basemap/trees.js";
41
26
  /** The signals of the relief and reprojection requests in progress, aborted with them */
42
27
  const controllers = new Map();
43
28
  /** Where this worker reads the colors of the relief tiles back */
44
29
  const heightsSurface = {};
45
30
  /** The reprojected rasters' source images this worker keeps, shared by every source */
46
31
  const reprojection = resampler();
47
- /** Images of the raster sources by URL. An image dropped is freed once settled, after the renders that awaited it have
48
- * drawn it (they draw without awaiting again) */
49
- const images = new Lru(64, (evicted) => evicted.then((bitmap) => setTimeout(() => bitmap?.close(), 0), () => undefined));
50
- /** An image of a raster source (shared by every renderer); undefined where the source has none */
51
- function image(url, priority = 'auto') {
52
- const hit = images.get(url);
53
- if (hit)
54
- return hit;
55
- const pending = (async () => {
56
- // A tile of a PMTiles archive (`pmtiles://…/{z}/{x}/{y}`)
57
- const archived = pmtilesTile(url);
58
- if (archived) {
59
- const bytes = await archived;
60
- return bytes && createImageBitmap(new Blob([bytes]));
61
- }
62
- const init = { priority };
63
- const response = await fetch(url, withHeaders(url, init));
64
- if (response.status === 404 || response.status === 204)
65
- return undefined;
66
- if (!response.ok)
67
- throw new Error(`${url}: ${response.status}`);
68
- return createImageBitmap(await response.blob());
69
- })();
70
- pending.catch(() => images.delete(url));
71
- images.set(url, pending);
72
- return pending;
73
- }
74
- /** A vector tile, decoded (shared by every renderer of its URL); the first request's priority fetches it */
75
- function tile(url, priority = 'auto') {
76
- const hit = cache.get(url);
77
- if (hit)
78
- return hit;
79
- const pending = (async () => {
80
- // A tile of a PMTiles archive, ungzipped by the reader
81
- const archived = pmtilesTile(url);
82
- if (archived) {
83
- const bytes = await archived;
84
- return bytes && decode(bytes);
85
- }
86
- for (let attempt = 0;; attempt += 1) {
87
- const init = { priority };
88
- const response = await fetch(url, withHeaders(url, init));
89
- if (response.status === 404 || response.status === 204)
90
- return undefined;
91
- if (response.ok) {
92
- // Every layer: renderers of other subsets share the tile
93
- return decode(new Uint8Array(await response.arrayBuffer()));
94
- }
95
- if (attempt >= 2)
96
- throw new Error(`${url}: ${response.status}`);
97
- await new Promise((r) => setTimeout(r, 400 * (attempt + 1)));
98
- }
99
- })();
100
- pending.catch(() => cache.delete(url));
101
- cache.set(url, pending);
102
- return pending;
103
- }
104
- /** The vector tile to draw a raster tile from, and where the raster tile lies in it (tile units) */
105
- function source(r, z, x, y) {
106
- const vz = Math.max(0, Math.min(z - 1, r.maxzoom));
107
- const d = z - vz;
108
- const n = 2 ** Math.max(0, d);
109
- const [vx, vy] = d >= 0 ? [Math.floor(x / n), Math.floor(y / n)] : [x, y];
110
- const url = r.tiles.replace('{z}', String(vz)).replace('{x}', String(vx)).replace('{y}', String(vy));
111
- // Fraction of the vector tile covered, and the offset in it
112
- const part = 1 / n;
113
- return { url, vz, vx, vy, part, ox: (x % n) * part, oy: (y % n) * part };
114
- }
115
- const value = (layer, group, key, ctx, fallback) => {
116
- const v = layer[group]?.[key];
117
- if (v === undefined)
118
- return fallback;
119
- // An array that is not an expression is a literal value (`text-font`, `text-offset`, `line-dasharray`)
120
- const out = Array.isArray(v) && !isExpression(v) ? v : evaluate(v, ctx);
121
- return out === undefined || out === null ? fallback : out;
122
- };
123
- /** A color with an opacity, for the canvas */
124
- function paint(c, opacity) {
125
- const rgba = color(c) ?? [0, 0, 0, 1];
126
- return css([rgba[0], rgba[1], rgba[2], rgba[3] * opacity]);
127
- }
128
- function shown(layer, zoom) {
129
- if (layer.layout?.visibility === 'none')
130
- return false;
131
- return zoom >= (layer.minzoom ?? 0) && zoom < (layer.maxzoom ?? 24);
132
- }
133
- /** The image of a fill pattern, cut from the sprite on first use */
134
- function patternImage(r, name) {
135
- if (r.patterns.has(name))
136
- return r.patterns.get(name);
137
- const found = spriteIcon(r, name);
138
- let image;
139
- if (found && found.icon.width > 0 && found.icon.height > 0) {
140
- const { icon } = found;
141
- image = new OffscreenCanvas(icon.width, icon.height);
142
- image
143
- .getContext('2d')
144
- ?.drawImage(found.image, icon.x, icon.y, icon.width, icon.height, 0, 0, icon.width, icon.height);
145
- }
146
- r.patterns.set(name, image);
147
- return image;
148
- }
149
- /** Canvases free for the next tiles, by size: one per tile being drawn at most */
150
- const canvases = new Map();
151
- /** A blank canvas of `size` pixels and its context, from the free ones if any */
152
- function takeCanvas(size) {
153
- const canvas = canvases.get(size)?.pop() ?? new OffscreenCanvas(size, size);
154
- // A canvas on the CPU: its pixels are read back here, in the worker, not on the main thread at the upload
155
- const g = canvas.getContext('2d', { willReadFrequently: true });
156
- if (!g)
157
- throw new Error('no 2D context');
158
- // The state a former tile left
159
- g.setTransform(1, 0, 0, 1, 0, 0);
160
- g.globalAlpha = 1;
161
- g.setLineDash([]);
162
- g.clearRect(0, 0, size, size);
163
- return { canvas, g };
164
- }
165
- function giveCanvas(canvas) {
166
- const free = canvases.get(canvas.width) ?? [];
167
- canvases.set(canvas.width, free);
168
- if (free.length < 4)
169
- free.push(canvas);
170
- }
171
- /**
172
- * The image of a drawn canvas, its alpha straight (the engine blends straight alpha). An opaque tile (a background
173
- * of full opacity, as a basemap's) is moved out of the canvas as it is, premultiplied and straight being the same
174
- * there; another one is read back unpremultiplied, copied
175
- */
176
- export function tileImage(canvas, g, opaque) {
177
- if (opaque)
178
- return Promise.resolve(canvas.transferToImageBitmap());
179
- return createImageBitmap(g.getImageData(0, 0, canvas.width, canvas.height), { premultiplyAlpha: 'none' });
180
- }
181
- async function render(r, z, x, y, size, id, priority) {
182
- const s = source(r, z, x, y);
183
- const layers = await tile(s.url, priority);
184
- if (aborted.has(id))
185
- return undefined;
186
- const { canvas, g } = takeCanvas(size);
187
- try {
188
- return await draw(r, s, layers, canvas, g, z, x, y, size, id, priority);
189
- }
190
- finally {
191
- giveCanvas(canvas);
192
- }
193
- }
194
- async function draw(r, s, layers, canvas, g, z, x, y, size, id, priority) {
195
- const zoom = Math.max(0, z - 1);
196
- // CSS pixels to canvas pixels: the tile is 256 CSS pixels across at its zoom
197
- const px = size / 256;
198
- let drawn = false;
199
- /** A background of full opacity covers the tile: every pixel stays opaque */
200
- let opaque = false;
201
- for (const layer of r.layers) {
202
- if (!shown(layer, zoom))
203
- continue;
204
- const zctx = { zoom, properties: {}, type: 'Unknown' };
205
- if (layer.type === 'background') {
206
- const fill = value(layer, 'paint', 'background-color', zctx, '#000');
207
- const opacity = Number(value(layer, 'paint', 'background-opacity', zctx, 1));
208
- g.fillStyle = paint(fill, opacity);
209
- g.fillRect(0, 0, size, size);
210
- if ((color(fill) ?? [0, 0, 0, 1])[3] * opacity >= 1)
211
- opaque = true;
212
- drawn = true;
213
- continue;
214
- }
215
- if (layer.type === 'raster') {
216
- const source = layer.source === undefined ? undefined : r.rasters[layer.source];
217
- if (!source)
218
- continue;
219
- // The canvas covers the Web Mercator tile (z, x, y): a source tile of 512 px covers it from a level coarser
220
- const rz = Math.max(0, Math.min(z - (source.tileSize >= 512 ? 1 : 0), source.maxzoom));
221
- const n = 2 ** (z - rz);
222
- const url = source.tiles
223
- .replace('{z}', String(rz))
224
- .replace('{x}', String(Math.floor(x / n)))
225
- .replace('{y}', String(Math.floor(y / n)));
226
- const picture = await image(url, priority).catch(() => undefined);
227
- if (aborted.has(id))
228
- return undefined;
229
- if (!picture)
230
- continue;
231
- const [w, h] = [picture.width / n, picture.height / n];
232
- g.globalAlpha = Number(value(layer, 'paint', 'raster-opacity', zctx, 1));
233
- g.drawImage(picture, (x % n) * w, (y % n) * h, w, h, 0, 0, size, size);
234
- g.globalAlpha = 1;
235
- drawn = true;
236
- continue;
237
- }
238
- const data = layers?.get(layer['source-layer'] ?? '');
239
- if (!data)
240
- continue;
241
- // Tile units to canvas pixels
242
- const k = size / (data.extent * s.part);
243
- const [ox, oy] = [s.ox * data.extent, s.oy * data.extent];
244
- const path = (f, close) => {
245
- g.beginPath();
246
- for (const line of f.geometry) {
247
- for (let i = 0; i + 1 < line.length; i += 2) {
248
- const [lx, ly] = [((line[i] ?? 0) - ox) * k, ((line[i + 1] ?? 0) - oy) * k];
249
- if (i === 0)
250
- g.moveTo(lx, ly);
251
- else
252
- g.lineTo(lx, ly);
253
- }
254
- if (close)
255
- g.closePath();
256
- }
257
- };
258
- // Values that only depend on the zoom are evaluated once for the layer
259
- const dependent = new Map();
260
- const get = (group, key, ctx, fallback) => {
261
- let d = dependent.get(key);
262
- if (d === undefined) {
263
- d = isFeatureDependent(layer[group]?.[key]);
264
- dependent.set(key, d);
265
- }
266
- return value(layer, group, key, d ? ctx : zctx, fallback);
267
- };
268
- for (const f of data.features) {
269
- const ctx = context(f, zoom);
270
- if (!filter(layer.filter, ctx))
271
- continue;
272
- if (layer.type === 'fill-extrusion' && f.type === 3) {
273
- // Flat, seen from above: its color
274
- path(f, true);
275
- g.fillStyle = paint(get('paint', 'fill-extrusion-color', ctx, '#000'), Number(get('paint', 'fill-extrusion-opacity', ctx, 1)));
276
- g.fill('evenodd');
277
- drawn = true;
278
- }
279
- else if (layer.type === 'fill' && f.type === 3) {
280
- const opacity = Number(get('paint', 'fill-opacity', ctx, 1));
281
- const pattern = get('paint', 'fill-pattern', ctx, undefined);
282
- path(f, true);
283
- const icon = typeof pattern === 'string' ? spriteIcon(r, pattern)?.icon : undefined;
284
- const image = icon && patternImage(r, pattern);
285
- if (icon && image) {
286
- const p = g.createPattern(image, 'repeat');
287
- if (p) {
288
- p.setTransform(new DOMMatrix().scale(px / (icon.pixelRatio ?? 1)));
289
- g.globalAlpha = opacity;
290
- g.fillStyle = p;
291
- g.fill('evenodd');
292
- g.globalAlpha = 1;
293
- }
294
- }
295
- else {
296
- g.fillStyle = paint(get('paint', 'fill-color', ctx, '#000'), opacity);
297
- g.fill('evenodd');
298
- const outline = get('paint', 'fill-outline-color', ctx, undefined);
299
- if (outline !== undefined) {
300
- g.strokeStyle = paint(outline, opacity);
301
- g.lineWidth = px;
302
- g.setLineDash([]);
303
- g.stroke();
304
- }
305
- }
306
- drawn = true;
307
- }
308
- else if (layer.type === 'line' && f.type !== 1) {
309
- const width = Number(get('paint', 'line-width', ctx, 1));
310
- if (width <= 0)
311
- continue;
312
- // A gap: two lines on either side of it, drawn as one wide line (the casing of a road, its inside drawn over)
313
- const gap = Number(get('paint', 'line-gap-width', ctx, 0));
314
- g.strokeStyle = paint(get('paint', 'line-color', ctx, '#000'), Number(get('paint', 'line-opacity', ctx, 1)));
315
- g.lineWidth = (gap > 0 ? gap + 2 * width : width) * px;
316
- g.lineCap = String(get('layout', 'line-cap', ctx, 'butt'));
317
- g.lineJoin = String(get('layout', 'line-join', ctx, 'miter'));
318
- const dash = get('paint', 'line-dasharray', ctx, undefined);
319
- g.setLineDash(Array.isArray(dash) ? dash.map((d) => Number(d) * width * px) : []);
320
- path(f, f.type === 3);
321
- g.stroke();
322
- drawn = true;
323
- }
324
- else if (layer.type === 'circle' && f.type === 1) {
325
- const radius = Number(get('paint', 'circle-radius', ctx, 5)) * px;
326
- g.fillStyle = paint(get('paint', 'circle-color', ctx, '#000'), Number(get('paint', 'circle-opacity', ctx, 1)));
327
- const stroke = Number(get('paint', 'circle-stroke-width', ctx, 0)) * px;
328
- g.strokeStyle = paint(get('paint', 'circle-stroke-color', ctx, '#000'), Number(get('paint', 'circle-stroke-opacity', ctx, 1)));
329
- g.lineWidth = stroke;
330
- for (const point of f.geometry) {
331
- for (let i = 0; i + 1 < point.length; i += 2) {
332
- g.beginPath();
333
- g.arc(((point[i] ?? 0) - ox) * k, ((point[i + 1] ?? 0) - oy) * k, radius, 0, 2 * Math.PI);
334
- g.fill();
335
- if (stroke > 0)
336
- g.stroke();
337
- }
338
- }
339
- drawn = true;
340
- }
341
- }
342
- }
343
- if (!drawn && !layers)
344
- return null;
345
- return tileImage(canvas, g, opaque);
346
- }
347
- /** How a layer's text or icon (`prefix`) meets the others: allowed to overlap, ignored by the placement */
348
- function overlapOf(layer, prefix, ctx) {
349
- const allow = Boolean(value(layer, 'layout', `${prefix}-allow-overlap`, ctx, false));
350
- const ignore = Boolean(value(layer, 'layout', `${prefix}-ignore-placement`, ctx, false));
351
- if (allow && ignore)
352
- return true;
353
- if (allow)
354
- return 'allow';
355
- return ignore ? 'ignore' : false;
356
- }
357
- /** A pair of numbers of a style value, `fallback` if it is not one */
358
- function pair(v, fallback) {
359
- return Array.isArray(v) ? [Number(v[0] ?? fallback[0]), Number(v[1] ?? fallback[1])] : fallback;
360
- }
361
- /** Top, right, bottom, left of one to four numbers, as CSS's sides */
362
- function sides(v) {
363
- const n = (Array.isArray(v) ? v : [v]).map((x) => Number(x) || 0);
364
- const [top = 0, right = top, bottom = top, left = right] = n;
365
- return [top, right, bottom, left];
366
- }
367
- /** Points every `spacing` along a line (tile units, x and y interleaved), the first half a spacing in (the middle of a
368
- * shorter line): each point and the segment it lies on (x, y, x0, y0, x1, y1) */
369
- export function lineAnchors(line, spacing) {
370
- let length = 0;
371
- for (let i = 2; i + 1 < line.length; i += 2) {
372
- length += Math.hypot((line[i] ?? 0) - (line[i - 2] ?? 0), (line[i + 1] ?? 0) - (line[i - 1] ?? 0));
373
- }
374
- const out = [];
375
- if (!(length > 0) || !(spacing > 0))
376
- return out;
377
- let next = Math.min(spacing / 2, length / 2);
378
- let walked = 0;
379
- for (let i = 2; i + 1 < line.length; i += 2) {
380
- const [ax, ay, bx, by] = [line[i - 2] ?? 0, line[i - 1] ?? 0, line[i] ?? 0, line[i + 1] ?? 0];
381
- const d = Math.hypot(bx - ax, by - ay);
382
- while (d > 0 && next <= walked + d) {
383
- const t = (next - walked) / d;
384
- out.push([ax + (bx - ax) * t, ay + (by - ay) * t, ax, ay, bx, by]);
385
- next += spacing;
386
- }
387
- walked += d;
388
- }
389
- return out;
390
- }
391
- /** Longitude and latitude of a point of tile (z, x, y) in tile units */
392
- function lonLat(z, x, y, extent, px, py) {
393
- const n = 2 ** z;
394
- return [longitude((x + px / extent) / n), latitude((y + py / extent) / n)];
395
- }
396
- /** The icon of a label: its layer's icon properties for the feature */
397
- function iconOf(layer, image, ctx, placement, sizeOf) {
398
- const alignment = value(layer, 'layout', 'icon-rotation-alignment', ctx, 'auto');
399
- const fit = String(value(layer, 'layout', 'icon-text-fit', ctx, 'none'));
400
- const padding = value(layer, 'layout', 'icon-text-fit-padding', ctx, [0, 0, 0, 0]);
401
- return {
402
- image,
403
- size: isFeatureDependent(layer.layout?.['icon-size'])
404
- ? Number(value(layer, 'layout', 'icon-size', ctx, 1))
405
- : sizeOf(layer),
406
- anchor: String(value(layer, 'layout', 'icon-anchor', ctx, 'center')),
407
- offset: pair(value(layer, 'layout', 'icon-offset', ctx, [0, 0]), [0, 0]),
408
- rotate: Number(value(layer, 'layout', 'icon-rotate', ctx, 0)) || 0,
409
- alignment: alignment === 'map' || (alignment === 'auto' && placement === 'line') ? 'map' : 'viewport',
410
- overlap: overlapOf(layer, 'icon', ctx),
411
- optional: Boolean(value(layer, 'layout', 'icon-optional', ctx, false)),
412
- fit: fit === 'width' || fit === 'height' || fit === 'both' ? fit : 'none',
413
- padding: sides(padding),
414
- color: String(value(layer, 'paint', 'icon-color', ctx, '#000')),
415
- opacity: Number(value(layer, 'paint', 'icon-opacity', ctx, 1)),
416
- };
417
- }
418
- async function labels(r, z, x, y, id, priority, zoom = z) {
419
- const url = r.tiles.replace('{z}', String(z)).replace('{x}', String(x)).replace('{y}', String(y));
420
- const layers = await tile(url, priority);
421
- if (!layers || aborted.has(id))
422
- return [];
423
- const out = [];
424
- // The text size of a layer when it depends on the zoom only: legacy stops as they are, an expression as stops
425
- const sizes = new Map();
426
- const sizeOf = (layer) => {
427
- let s = sizes.get(layer);
428
- if (s === undefined) {
429
- const v = layer.layout?.['text-size'] ?? 16;
430
- s = isExpression(v) ? zoomStops(v) : v;
431
- sizes.set(layer, s);
432
- }
433
- return s;
434
- };
435
- // The icon size of a layer when it depends on the zoom only, as the text size
436
- const iconSizes = new Map();
437
- const iconSizeOf = (layer) => {
438
- let s = iconSizes.get(layer);
439
- if (s === undefined) {
440
- const v = layer.layout?.['icon-size'] ?? 1;
441
- s = isExpression(v) ? zoomStops(v) : v;
442
- iconSizes.set(layer, s);
443
- }
444
- return s;
445
- };
446
- r.layers.forEach((layer, index) => {
447
- const layout = layer.layout ?? {};
448
- if (layer.type !== 'symbol' || !shown(layer, zoom))
449
- return;
450
- if (layout['text-field'] === undefined && layout['icon-image'] === undefined)
451
- return;
452
- const data = layers.get(layer['source-layer'] ?? '');
453
- if (!data)
454
- return;
455
- const zctx = { zoom, properties: {}, type: 'Unknown' };
456
- const placement = value(layer, 'layout', 'symbol-placement', zctx, 'point') === 'line' ? 'line' : 'point';
457
- const textAlong = placement === 'line' && value(layer, 'layout', 'text-rotation-alignment', zctx, 'auto') !== 'viewport';
458
- for (const f of data.features) {
459
- const ctx = context(f, zoom);
460
- if (!filter(layer.filter, ctx))
461
- continue;
462
- let t = text(layout['text-field'], ctx).trim();
463
- const image = text(layout['icon-image'], ctx).trim();
464
- if (!t && !image)
465
- continue;
466
- const transform = value(layer, 'layout', 'text-transform', ctx, 'none');
467
- if (transform === 'uppercase')
468
- t = t.toUpperCase();
469
- else if (transform === 'lowercase')
470
- t = t.toLowerCase();
471
- const fonts = value(layer, 'layout', 'text-font', ctx, ['Noto Sans Regular']);
472
- const font = labelFont(Array.isArray(fonts) && typeof fonts[0] === 'string' ? fonts[0] : 'Noto Sans Regular');
473
- const offset = value(layer, 'layout', 'text-offset', ctx, [0, 0]);
474
- const base = {
475
- layer: index,
476
- text: t,
477
- font,
478
- // Zoom stops are kept for the engine; a value of the feature evaluated here
479
- size: isFeatureDependent(layer.layout?.['text-size'])
480
- ? Number(value(layer, 'layout', 'text-size', ctx, 16))
481
- : sizeOf(layer),
482
- color: String(value(layer, 'paint', 'text-color', ctx, '#000')),
483
- haloColor: String(value(layer, 'paint', 'text-halo-color', ctx, 'rgba(0,0,0,0)')),
484
- haloWidth: Number(value(layer, 'paint', 'text-halo-width', ctx, 0)),
485
- anchor: String(value(layer, 'layout', 'text-anchor', ctx, 'center')),
486
- offset: (Array.isArray(offset) ? [Number(offset[0] ?? 0), Number(offset[1] ?? 0)] : [0, 0]),
487
- maxWidth: Number(value(layer, 'layout', 'text-max-width', ctx, 10)),
488
- lineHeight: Number(value(layer, 'layout', 'text-line-height', ctx, 1.2)),
489
- overlap: overlapOf(layer, 'text', ctx),
490
- ...(textAlong ? { textAlong } : {}),
491
- ...(value(layer, 'layout', 'text-optional', ctx, false) ? { textOptional: true } : {}),
492
- ...(image ? { icon: iconOf(layer, image, ctx, placement, iconSizeOf) } : {}),
493
- };
494
- if (f.type === 1) {
495
- for (const point of f.geometry) {
496
- for (let i = 0; i + 1 < point.length; i += 2) {
497
- const [px, py] = [point[i] ?? 0, point[i + 1] ?? 0];
498
- // Points in the buffer belong to the neighbouring tile
499
- if (px < 0 || py < 0 || px >= data.extent || py >= data.extent)
500
- continue;
501
- const [lon, lat] = lonLat(z, x, y, data.extent, px, py);
502
- out.push({ ...base, lon, lat });
503
- }
504
- }
505
- }
506
- else if (!t && placement === 'line') {
507
- // Icons alone along lines (one-way arrows): every `symbol-spacing` CSS pixels (a tile is 512 across at its
508
- // level, twice as many at each zoom past it)
509
- const spacing = (Number(value(layer, 'layout', 'symbol-spacing', ctx, 250)) * data.extent) / (512 * 2 ** (zoom - z));
510
- for (const line of f.geometry) {
511
- for (const [px = 0, py = 0, ax = 0, ay = 0, bx = 0, by = 0] of lineAnchors(line, Math.max(spacing, 1))) {
512
- if (px < 0 || py < 0 || px >= data.extent || py >= data.extent)
513
- continue;
514
- const [lon, lat] = lonLat(z, x, y, data.extent, px, py);
515
- const [lon1, lat1] = lonLat(z, x, y, data.extent, bx, by);
516
- const [lon0, lat0] = lonLat(z, x, y, data.extent, ax, ay);
517
- out.push({ ...base, lon, lat, along: [lon1 - lon0, lat1 - lat0] });
518
- }
519
- }
520
- }
521
- else {
522
- // The middle of the longest line inside the tile, along it
523
- let best;
524
- let bestLength = 0;
525
- for (const line of f.geometry) {
526
- let length = 0;
527
- for (let i = 2; i + 1 < line.length; i += 2) {
528
- length += Math.hypot((line[i] ?? 0) - (line[i - 2] ?? 0), (line[i + 1] ?? 0) - (line[i - 1] ?? 0));
529
- }
530
- if (length > bestLength) {
531
- bestLength = length;
532
- best = line;
533
- }
534
- }
535
- if (!best || bestLength < data.extent / 32)
536
- continue;
537
- let rest = bestLength / 2;
538
- for (let i = 2; i + 1 < best.length; i += 2) {
539
- const [ax, ay, bx, by] = [best[i - 2] ?? 0, best[i - 1] ?? 0, best[i] ?? 0, best[i + 1] ?? 0];
540
- const d = Math.hypot(bx - ax, by - ay);
541
- if (d >= rest && d > 0) {
542
- const [px, py] = [ax + ((bx - ax) * rest) / d, ay + ((by - ay) * rest) / d];
543
- if (px < 0 || py < 0 || px >= data.extent || py >= data.extent)
544
- break;
545
- const [lon, lat] = lonLat(z, x, y, data.extent, px, py);
546
- const [lon1, lat1] = lonLat(z, x, y, data.extent, bx, by);
547
- const [lon0, lat0] = lonLat(z, x, y, data.extent, ax, ay);
548
- out.push({ ...base, lon, lat, ...(placement === 'line' ? { along: [lon1 - lon0, lat1 - lat0] } : {}) });
549
- break;
550
- }
551
- rest -= d;
552
- }
553
- }
554
- }
555
- });
556
- return out;
557
- }
558
32
  const post = (message, transfer = []) => self.postMessage(message, transfer);
559
33
  self.onmessage = async ({ data }) => {
560
34
  if (data.type === 'headers') {
561
35
  setHeaderRules(data.rules);
562
36
  return;
563
37
  }
38
+ if (data.type === 'tess') {
39
+ tess.url = data.url;
40
+ return;
41
+ }
564
42
  if (data.type === 'init') {
565
43
  if (data.workers !== undefined)
566
44
  cache.capacity = cacheShare(data.workers);
@@ -576,8 +54,9 @@ self.onmessage = async ({ data }) => {
576
54
  }
577
55
  if (data.type === 'drop') {
578
56
  // Its copy of the sprites
579
- for (const sprite of renderers.get(data.key)?.sprites ?? [])
57
+ renderers.get(data.key)?.sprites.forEach((sprite) => {
580
58
  sprite.image.close();
59
+ });
581
60
  renderers.delete(data.key);
582
61
  return;
583
62
  }
@@ -604,6 +83,59 @@ self.onmessage = async ({ data }) => {
604
83
  else
605
84
  post({ id: data.id, missing: true });
606
85
  }
86
+ else if (data.type === 'trees') {
87
+ const out = await trees(r, data.z, data.x, data.y, data.id, data.spacing, data.maxTrees, data.priority);
88
+ if (!out)
89
+ post({ id: data.id, missing: true });
90
+ else
91
+ post({ id: data.id, trees: out }, [out.buffer]);
92
+ }
93
+ else if (data.type === 'polygons') {
94
+ const tilePolygons = await polygons(r, data.z, data.x, data.y, data.id, data.priority);
95
+ if (!tilePolygons)
96
+ post({ id: data.id, missing: true });
97
+ else {
98
+ const { points, ringStart, polygonStart, feature } = tilePolygons.arrays;
99
+ post({ id: data.id, polygons: tilePolygons }, [
100
+ points.buffer,
101
+ ringStart.buffer,
102
+ polygonStart.buffer,
103
+ feature.buffer,
104
+ ]);
105
+ }
106
+ }
107
+ else if (data.type === 'vectors') {
108
+ const out = await vectors(r, data, data.id, data.styles === true, data.priority);
109
+ if (!out)
110
+ post({ id: data.id, missing: true });
111
+ else if (out instanceof Float32Array)
112
+ post({ id: data.id, styles: out }, [out.buffer]);
113
+ else {
114
+ const raised = Uint8Array.from(r.layers, (l) => (vectorKind(l) === 'extrusion' ? 1 : 0));
115
+ const mesh = (await tessellator()) ? tessellate(out, raised, data.prism === true) : undefined;
116
+ const { fills, lines } = out;
117
+ post({ id: data.id, vectors: mesh ? { ...out, mesh } : out }, [
118
+ ...[fills.points, fills.ringStart, fills.polygonStart, fills.feature, fills.fillStart].map((a) => a.buffer),
119
+ ...[lines.points, lines.start, lines.feature, lines.lineStart].map((a) => a.buffer),
120
+ out.styles.buffer,
121
+ out.sublayer.buffer,
122
+ ...(mesh
123
+ ? [
124
+ mesh.vertices,
125
+ mesh.indices,
126
+ mesh.fillRanges,
127
+ mesh.pieces,
128
+ mesh.lineTable,
129
+ mesh.lineRanges,
130
+ mesh.blocks,
131
+ mesh.blockIndices,
132
+ mesh.blockRanges,
133
+ mesh.bounds,
134
+ ]
135
+ : []).map((a) => a.buffer),
136
+ ]);
137
+ }
138
+ }
607
139
  else {
608
140
  post({ id: data.id, labels: await labels(r, data.z, data.x, data.y, data.id, data.priority, data.zoom) });
609
141
  }
@@ -617,7 +149,7 @@ self.onmessage = async ({ data }) => {
617
149
  }
618
150
  };
619
151
  /** A tile of another layer: the altitudes of a relief tile, or a reprojected raster tile */
620
- async function decodeTile(data) {
152
+ const decodeTile = async (data) => {
621
153
  const controller = new AbortController();
622
154
  controllers.set(data.id, controller);
623
155
  try {
@@ -643,5 +175,5 @@ async function decodeTile(data) {
643
175
  finally {
644
176
  controllers.delete(data.id);
645
177
  }
646
- }
178
+ };
647
179
  //# sourceMappingURL=basemap.worker.js.map