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
@@ -6,7 +6,7 @@
6
6
  import { withHeaders } from "../engine/requests.js";
7
7
  const SIZE = 256;
8
8
  /** Altitudes (metres, rows from the north) of RGBA pixels in an encoding; below `floor` raised to it */
9
- export function decodeHeights(rgba, encoding, floor = Number.NEGATIVE_INFINITY) {
9
+ export const decodeHeights = (rgba, encoding, floor = Number.NEGATIVE_INFINITY) => {
10
10
  const n = rgba.length >> 2;
11
11
  const out = new Float32Array(n);
12
12
  // A loop per encoding, without allocations: 65 536 pixels a tile
@@ -23,12 +23,12 @@ export function decodeHeights(rgba, encoding, floor = Number.NEGATIVE_INFINITY)
23
23
  }
24
24
  }
25
25
  return out;
26
- }
26
+ };
27
27
  /**
28
28
  * The altitudes of the PNG tile at `url`: 256 × 256, null where the server has no tile; a network or server error
29
29
  * rejects (the engine retries). Where it runs: in a worker of the map, or in the page without workers
30
30
  */
31
- export async function fetchHeights(url, encoding, floor, signal, surface) {
31
+ export const fetchHeights = async (url, encoding, floor, signal, surface) => {
32
32
  const response = await fetch(url, withHeaders(url, { signal }));
33
33
  if (response.status === 404 || response.status === 204 || response.status === 410)
34
34
  return null;
@@ -53,17 +53,17 @@ export async function fetchHeights(url, encoding, floor, signal, surface) {
53
53
  finally {
54
54
  bitmap.close();
55
55
  }
56
- }
56
+ };
57
57
  /**
58
58
  * A loader of the engine's relief for PNG tiles of `url` ({z}, {x}, {y}) in an encoding: 256 × 256 altitudes, null
59
59
  * where the server has no tile; a network or server error rejects (retried). `floor`: the lowest altitude kept (the
60
60
  * sea floor of a worldwide model flattened at sea level). `remote`: decodes in a worker when it can
61
61
  */
62
- export function encodedTerrainLoader(url, encoding, floor, remote) {
62
+ export const encodedTerrainLoader = (url, encoding, floor, remote) => {
63
63
  const surface = {};
64
64
  return (z, x, y, signal) => {
65
65
  const tile = url.replaceAll('{z}', String(z)).replaceAll('{x}', String(x)).replaceAll('{y}', String(y));
66
66
  return remote?.(tile, encoding, floor, [z, x, y], signal) ?? fetchHeights(tile, encoding, floor, signal, surface);
67
67
  };
68
- }
68
+ };
69
69
  //# sourceMappingURL=terrain.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"terrain.js","sourceRoot":"","sources":["../../src/basemap/terrain.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Altitudes encoded in the colors of PNG tiles, for the relief: Terrarium (Mapzen, the Terrain Tiles of AWS) and\n * Terrain-RGB (Mapbox, MapTiler). The engine reads raw floats only; a loader gives it the altitudes of each tile,\n * decoded in a worker of the map's pool (OffscreenCanvas), or in the page when the map has no workers.\n */\nimport { withHeaders } from '../engine/requests.ts';\n\n/** The encodings of altitudes in the colors of PNG tiles */\nexport type PngEncoding = 'terrarium' | 'mapbox';\n\nconst SIZE = 256;\n\n/** Altitudes (metres, rows from the north) of RGBA pixels in an encoding; below `floor` raised to it */\nexport function decodeHeights(\n rgba: Uint8ClampedArray | Uint8Array,\n encoding: PngEncoding,\n floor = Number.NEGATIVE_INFINITY,\n): Float32Array {\n const n = rgba.length >> 2;\n const out = new Float32Array(n);\n // A loop per encoding, without allocations: 65 536 pixels a tile\n if (encoding === 'terrarium') {\n for (let i = 0, j = 0; i < n; i += 1, j += 4) {\n const h = (rgba[j] as number) * 256 + (rgba[j + 1] as number) + (rgba[j + 2] as number) / 256 - 32768;\n out[i] = h > floor ? h : floor;\n }\n } else {\n for (let i = 0, j = 0; i < n; i += 1, j += 4) {\n const h = -10000 + ((rgba[j] as number) * 65536 + (rgba[j + 1] as number) * 256 + (rgba[j + 2] as number)) * 0.1;\n out[i] = h > floor ? h : floor;\n }\n }\n return out;\n}\n\n/** Where the tiles are drawn and read back, made on first use and kept (each tile drawn and read at once) */\nexport interface Surface {\n canvas?: OffscreenCanvas;\n}\n\n/**\n * The altitudes of the PNG tile at `url`: 256 × 256, null where the server has no tile; a network or server error\n * rejects (the engine retries). Where it runs: in a worker of the map, or in the page without workers\n */\nexport async function fetchHeights(\n url: string,\n encoding: PngEncoding,\n floor: number | undefined,\n signal: AbortSignal,\n surface: Surface,\n): Promise<Float32Array | null> {\n const response = await fetch(url, withHeaders(url, { signal }));\n if (response.status === 404 || response.status === 204 || response.status === 410) return null;\n if (!response.ok) throw new Error(`${response.url}: ${response.status}`);\n // The colors as stored: no color management, no premultiplied alpha\n const bitmap = await createImageBitmap(await response.blob(), {\n premultiplyAlpha: 'none',\n colorSpaceConversion: 'none',\n });\n try {\n surface.canvas ??= new OffscreenCanvas(SIZE, SIZE);\n const g = surface.canvas.getContext('2d', { willReadFrequently: true });\n if (!g) throw new Error('no 2D context');\n // Another size is resampled to the nearest pixel: an interpolated color is no altitude\n g.imageSmoothingEnabled = false;\n g.clearRect(0, 0, SIZE, SIZE);\n g.drawImage(bitmap, 0, 0, SIZE, SIZE);\n return decodeHeights(g.getImageData(0, 0, SIZE, SIZE).data, encoding, floor);\n } finally {\n bitmap.close();\n }\n}\n\n/** `fetchHeights` run in a worker for tile `z`/`x`/`y`; undefined when the map has no workers (run in the page) */\nexport type RemoteHeights = (\n url: string,\n encoding: PngEncoding,\n floor: number | undefined,\n tile: [z: number, x: number, y: number],\n signal: AbortSignal,\n) => Promise<Float32Array | null> | undefined;\n\n/**\n * A loader of the engine's relief for PNG tiles of `url` ({z}, {x}, {y}) in an encoding: 256 × 256 altitudes, null\n * where the server has no tile; a network or server error rejects (retried). `floor`: the lowest altitude kept (the\n * sea floor of a worldwide model flattened at sea level). `remote`: decodes in a worker when it can\n */\nexport function encodedTerrainLoader(\n url: string,\n encoding: PngEncoding,\n floor?: number,\n remote?: RemoteHeights,\n): (z: number, x: number, y: number, signal: AbortSignal) => Promise<Float32Array | null> {\n const surface: Surface = {};\n return (z, x, y, signal) => {\n const tile = url.replaceAll('{z}', String(z)).replaceAll('{x}', String(x)).replaceAll('{y}', String(y));\n return remote?.(tile, encoding, floor, [z, x, y], signal) ?? fetchHeights(tile, encoding, floor, signal, surface);\n };\n}\n"]}
1
+ {"version":3,"file":"terrain.js","sourceRoot":"","sources":["../../src/basemap/terrain.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKpD,MAAM,IAAI,GAAG,GAAG,CAAC;AAEjB,wGAAwG;AACxG,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,IAAoC,EACpC,QAAqB,EACrB,KAAK,GAAG,MAAM,CAAC,iBAAiB,EAClB,EAAE;IAChB,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IAEhC,iEAAiE;IACjE,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,CAAC,GAAI,IAAI,CAAC,CAAC,CAAY,GAAG,GAAG,GAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAY,GAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAY,GAAG,GAAG,GAAG,KAAK,CAAC;YAEtG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACjC,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,CAAE,IAAI,CAAC,CAAC,CAAY,GAAG,KAAK,GAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAY,GAAG,GAAG,GAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAY,CAAC,GAAG,GAAG,CAAC;YAEjH,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACjC,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAOF;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,GAAW,EACX,QAAqB,EACrB,KAAyB,EACzB,MAAmB,EACnB,OAAgB,EACc,EAAE;IAChC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAEhE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC/F,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAC,GAAG,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAEzE,oEAAoE;IACpE,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE;QAC5D,gBAAgB,EAAE,MAAM;QACxB,oBAAoB,EAAE,MAAM;KAC7B,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,KAAK,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAEnD,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QAExE,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACzC,uFAAuF;QACvF,CAAC,CAAC,qBAAqB,GAAG,KAAK,CAAC;QAChC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9B,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAEtC,OAAO,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC/E,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;AACH,CAAC,CAAC;AAWF;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,GAAW,EACX,QAAqB,EACrB,KAAc,EACd,MAAsB,EACoE,EAAE;IAC5F,MAAM,OAAO,GAAY,EAAE,CAAC;IAE5B,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE;QACzB,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAExG,OAAO,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACpH,CAAC,CAAC;AACJ,CAAC,CAAC","sourcesContent":["/**\n * Altitudes encoded in the colors of PNG tiles, for the relief: Terrarium (Mapzen, the Terrain Tiles of AWS) and\n * Terrain-RGB (Mapbox, MapTiler). The engine reads raw floats only; a loader gives it the altitudes of each tile,\n * decoded in a worker of the map's pool (OffscreenCanvas), or in the page when the map has no workers.\n */\nimport { withHeaders } from '../engine/requests.ts';\n\n/** The encodings of altitudes in the colors of PNG tiles */\nexport type PngEncoding = 'terrarium' | 'mapbox';\n\nconst SIZE = 256;\n\n/** Altitudes (metres, rows from the north) of RGBA pixels in an encoding; below `floor` raised to it */\nexport const decodeHeights = (\n rgba: Uint8ClampedArray | Uint8Array,\n encoding: PngEncoding,\n floor = Number.NEGATIVE_INFINITY,\n): Float32Array => {\n const n = rgba.length >> 2;\n const out = new Float32Array(n);\n\n // A loop per encoding, without allocations: 65 536 pixels a tile\n if (encoding === 'terrarium') {\n for (let i = 0, j = 0; i < n; i += 1, j += 4) {\n const h = (rgba[j] as number) * 256 + (rgba[j + 1] as number) + (rgba[j + 2] as number) / 256 - 32768;\n\n out[i] = h > floor ? h : floor;\n }\n } else {\n for (let i = 0, j = 0; i < n; i += 1, j += 4) {\n const h = -10000 + ((rgba[j] as number) * 65536 + (rgba[j + 1] as number) * 256 + (rgba[j + 2] as number)) * 0.1;\n\n out[i] = h > floor ? h : floor;\n }\n }\n\n return out;\n};\n\n/** Where the tiles are drawn and read back, made on first use and kept (each tile drawn and read at once) */\nexport interface Surface {\n canvas?: OffscreenCanvas;\n}\n\n/**\n * The altitudes of the PNG tile at `url`: 256 × 256, null where the server has no tile; a network or server error\n * rejects (the engine retries). Where it runs: in a worker of the map, or in the page without workers\n */\nexport const fetchHeights = async (\n url: string,\n encoding: PngEncoding,\n floor: number | undefined,\n signal: AbortSignal,\n surface: Surface,\n): Promise<Float32Array | null> => {\n const response = await fetch(url, withHeaders(url, { signal }));\n\n if (response.status === 404 || response.status === 204 || response.status === 410) return null;\n if (!response.ok) throw new Error(`${response.url}: ${response.status}`);\n\n // The colors as stored: no color management, no premultiplied alpha\n const bitmap = await createImageBitmap(await response.blob(), {\n premultiplyAlpha: 'none',\n colorSpaceConversion: 'none',\n });\n\n try {\n surface.canvas ??= new OffscreenCanvas(SIZE, SIZE);\n\n const g = surface.canvas.getContext('2d', { willReadFrequently: true });\n\n if (!g) throw new Error('no 2D context');\n // Another size is resampled to the nearest pixel: an interpolated color is no altitude\n g.imageSmoothingEnabled = false;\n g.clearRect(0, 0, SIZE, SIZE);\n g.drawImage(bitmap, 0, 0, SIZE, SIZE);\n\n return decodeHeights(g.getImageData(0, 0, SIZE, SIZE).data, encoding, floor);\n } finally {\n bitmap.close();\n }\n};\n\n/** `fetchHeights` run in a worker for tile `z`/`x`/`y`; undefined when the map has no workers (run in the page) */\nexport type RemoteHeights = (\n url: string,\n encoding: PngEncoding,\n floor: number | undefined,\n tile: [z: number, x: number, y: number],\n signal: AbortSignal,\n) => Promise<Float32Array | null> | undefined;\n\n/**\n * A loader of the engine's relief for PNG tiles of `url` ({z}, {x}, {y}) in an encoding: 256 × 256 altitudes, null\n * where the server has no tile; a network or server error rejects (retried). `floor`: the lowest altitude kept (the\n * sea floor of a worldwide model flattened at sea level). `remote`: decodes in a worker when it can\n */\nexport const encodedTerrainLoader = (\n url: string,\n encoding: PngEncoding,\n floor?: number,\n remote?: RemoteHeights,\n): ((z: number, x: number, y: number, signal: AbortSignal) => Promise<Float32Array | null>) => {\n const surface: Surface = {};\n\n return (z, x, y, signal) => {\n const tile = url.replaceAll('{z}', String(z)).replaceAll('{x}', String(x)).replaceAll('{y}', String(y));\n\n return remote?.(tile, encoding, floor, [z, x, y], signal) ?? fetchHeights(tile, encoding, floor, signal, surface);\n };\n};\n"]}
@@ -31,6 +31,8 @@ export interface Style {
31
31
  url: string;
32
32
  }[];
33
33
  }
34
+ /** A MapLibre style, as far as the basemap reads it: its sources and its layers (an id and a type each) */
35
+ export declare const isStyle: (value: unknown) => value is Style;
34
36
  /** A raster source of a style: its tile URL, its deepest level and the CSS pixels of its tiles (MapLibre's
35
37
  * `tileSize`: 512 by default) */
36
38
  export interface RasterSource {
@@ -90,23 +92,23 @@ export interface Context {
90
92
  /** 'Point', 'LineString' or 'Polygon' */
91
93
  type: string;
92
94
  }
93
- export declare function context(feature: TileFeature, zoom: number): Context;
95
+ export declare const context: (feature: TileFeature, zoom: number) => Context;
94
96
  /** A color to RGBA 0-1, undefined if it is not one */
95
- export declare function color(value: unknown): [number, number, number, number] | undefined;
96
- export declare function css([r, g, b, a]: [number, number, number, number]): string;
97
+ export declare const color: (value: unknown) => [number, number, number, number] | undefined;
98
+ export declare const css: ([r, g, b, a]: [number, number, number, number]) => string;
97
99
  /** Whether a value is an expression (an array starting with a known operator), not a literal array */
98
- export declare function isExpression(e: unknown): e is Json[];
99
- export declare function evaluate(e: Json, ctx: Context): unknown;
100
- export declare function filter(f: Json | undefined, ctx: Context): boolean;
100
+ export declare const isExpression: (e: unknown) => e is Json[];
101
+ export declare const evaluate: (e: Json, ctx: Context) => unknown;
102
+ export declare const filter: (f: Json | undefined, ctx: Context) => boolean;
101
103
  /** Whether a value depends on the feature (else only on the zoom, evaluated once per tile) */
102
- export declare function isFeatureDependent(e: Json | undefined): boolean;
104
+ export declare const isFeatureDependent: (e: Json | undefined) => boolean;
103
105
  /** A `{token}` text field (or an expression) to the text of a feature */
104
- export declare function text(field: Json | undefined, ctx: Context): string;
106
+ export declare const text: (field: Json | undefined, ctx: Context) => string;
105
107
  /** The font of a label from the first of its `text-font`: a family without its weight takes the regular one */
106
- export declare function labelFont(font: string): string;
108
+ export declare const labelFont: (font: string) => string;
107
109
  /**
108
110
  * A value of the zoom only (an expression: `interpolate`, `step` of `["zoom"]`) as stops `[[zoom, value], …]` for the
109
111
  * engine, which interpolates them linearly: evaluated at its own stops and at the whole zooms between (an exponential
110
112
  * curve followed closely), a step kept sharp just before its threshold
111
113
  */
112
- export declare function zoomStops(e: Json): [number, number][];
114
+ export declare const zoomStops: (e: Json) => [number, number][];
@@ -7,42 +7,51 @@
7
7
  * interpolated in RGBA, `{token}` text fields.
8
8
  */
9
9
  import { parseCssColor } from "../util/color.js";
10
+ import { isRecord } from "../util/guards.js";
11
+ /** A MapLibre style, as far as the basemap reads it: its sources and its layers (an id and a type each) */
12
+ export const isStyle = (value) => isRecord(value) &&
13
+ isRecord(value.sources) &&
14
+ Object.values(value.sources).every((s) => isRecord(s) && typeof s.type === 'string') &&
15
+ Array.isArray(value.layers) &&
16
+ value.layers.every((l) => isRecord(l) && typeof l.id === 'string' && typeof l.type === 'string');
10
17
  const TYPES = ['Unknown', 'Point', 'LineString', 'Polygon'];
11
- export function context(feature, zoom) {
18
+ export const context = (feature, zoom) => {
12
19
  return { zoom, properties: feature.properties, type: TYPES[feature.type] ?? 'Unknown' };
13
- }
20
+ };
14
21
  // ── Colors ──
15
22
  /** A color to RGBA 0-1, undefined if it is not one */
16
- export function color(value) {
23
+ export const color = (value) => {
17
24
  return parseCssColor(value);
18
- }
19
- export function css([r, g, b, a]) {
25
+ };
26
+ export const css = ([r, g, b, a]) => {
20
27
  return `rgba(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)},${a})`;
21
- }
28
+ };
22
29
  // ── Interpolation ──
23
30
  /** Factor between two stops: linear, or exponential of `base` */
24
- function factor(x, x0, x1, base = 1) {
31
+ const factor = (x, x0, x1, base = 1) => {
25
32
  const d = x1 - x0;
26
33
  if (d === 0)
27
34
  return 0;
28
35
  const t = x - x0;
29
36
  return base === 1 ? t / d : (base ** t - 1) / (base ** d - 1);
30
- }
31
- function mix(a, b, t) {
37
+ };
38
+ const mix = (a, b, t) => {
32
39
  if (typeof a === 'number' && typeof b === 'number')
33
40
  return a + (b - a) * t;
34
41
  const ca = color(a);
35
42
  const cb = color(b);
36
- if (ca && cb)
37
- return css([0, 1, 2, 3].map((i) => (ca[i] ?? 0) + ((cb[i] ?? 0) - (ca[i] ?? 0)) * t));
43
+ if (ca && cb) {
44
+ const lerp = (i) => ca[i] + (cb[i] - ca[i]) * t;
45
+ return css([lerp(0), lerp(1), lerp(2), lerp(3)]);
46
+ }
38
47
  if (Array.isArray(a) && Array.isArray(b))
39
48
  return a.map((v, i) => mix(v, b[i], t));
40
49
  return t < 0.5 ? a : b;
41
- }
42
- function isFunction(v) {
50
+ };
51
+ const isFunction = (v) => {
43
52
  return typeof v === 'object' && v !== null && !Array.isArray(v) && Array.isArray(v.stops);
44
- }
45
- function legacy(f, ctx) {
53
+ };
54
+ const legacy = (f, ctx) => {
46
55
  const input = f.property === undefined ? ctx.zoom : ctx.properties[f.property];
47
56
  const stops = f.stops;
48
57
  const type = f.type ?? (f.property === undefined || typeof stops[0]?.[0] === 'number' ? 'exponential' : 'categorical');
@@ -53,30 +62,45 @@ function legacy(f, ctx) {
53
62
  if (typeof input !== 'number' || !stops.length)
54
63
  return f.default ?? stops[0]?.[1];
55
64
  // Zoom-and-property stops ({ zoom, value } keys) are taken at their zoom only
56
- const key = (s) => typeof s[0] === 'object' && s[0] !== null ? Number(s[0].zoom) : Number(s[0]);
65
+ const key = ([k]) => {
66
+ if (Array.isArray(k))
67
+ return Number.NaN;
68
+ return isRecord(k) ? Number(k.zoom) : Number(k);
69
+ };
57
70
  const first = stops[0];
58
71
  if (!first)
59
72
  return f.default;
60
73
  if (input <= key(first))
61
74
  return first[1];
62
- for (let i = 1; i < stops.length; i += 1) {
63
- const [s0, s1] = [stops[i - 1], stops[i]];
64
- if (!s0 || !s1)
65
- continue;
66
- if (input < key(s1)) {
67
- if (type === 'interval')
68
- return s0[1];
69
- return mix(s0[1], s1[1], factor(input, key(s0), key(s1), f.base ?? 1));
70
- }
71
- }
72
- return stops.at(-1)?.[1];
73
- }
75
+ const at = stops.findIndex((s1, i) => i > 0 && Boolean(stops[i - 1]) && Boolean(s1) && input < key(s1));
76
+ const [s0, s1] = [stops[at - 1], stops[at]];
77
+ if (at < 0 || !s0 || !s1)
78
+ return stops.at(-1)?.[1];
79
+ if (type === 'interval')
80
+ return s0[1];
81
+ return mix(s0[1], s1[1], factor(input, key(s0), key(s1), f.base ?? 1));
82
+ };
74
83
  // ── Expressions ──
75
- function get(ctx, key) {
84
+ /** A list of values to look a value up in (any value: `includes` compares with `===`) */
85
+ const labels = (values) => values;
86
+ const ORDERS = {
87
+ '<': (a, b) => a < b,
88
+ '<=': (a, b) => a <= b,
89
+ '>': (a, b) => a > b,
90
+ '>=': (a, b) => a >= b,
91
+ };
92
+ /** A comparison of any two values the way JavaScript's operators make it: two strings (lists and objects as their
93
+ * text) in text order, anything else as numbers */
94
+ const compare = (op, a, b) => {
95
+ const text = (v) => (typeof v === 'object' && v !== null ? String(v) : v);
96
+ const [x, y] = [text(a), text(b)];
97
+ return typeof x === 'string' && typeof y === 'string' ? ORDERS[op](x, y) : ORDERS[op](Number(x), Number(y));
98
+ };
99
+ const get = (ctx, key) => {
76
100
  if (key === '$type')
77
101
  return ctx.type;
78
102
  return typeof key === 'string' ? ctx.properties[key] : undefined;
79
- }
103
+ };
80
104
  /** The operators `evaluate` knows */
81
105
  const OPERATORS = new Set([
82
106
  'literal',
@@ -142,13 +166,13 @@ const OPERATORS = new Set([
142
166
  'e',
143
167
  ]);
144
168
  /** Whether a value is an expression (an array starting with a known operator), not a literal array */
145
- export function isExpression(e) {
169
+ export const isExpression = (e) => {
146
170
  return Array.isArray(e) && typeof e[0] === 'string' && OPERATORS.has(e[0]);
147
- }
171
+ };
148
172
  /** Operators already warned about */
149
173
  const warned = new Set();
150
174
  const str = (v) => (v === undefined || v === null ? '' : String(v));
151
- export function evaluate(e, ctx) {
175
+ export const evaluate = (e, ctx) => {
152
176
  if (isFunction(e))
153
177
  return legacy(e, ctx);
154
178
  if (!Array.isArray(e))
@@ -184,13 +208,17 @@ export function evaluate(e, ctx) {
184
208
  case 'image':
185
209
  return ev(0);
186
210
  case 'number-format': {
187
- const options = (args[1] ?? {});
211
+ const options = isRecord(args[1]) ? args[1] : {};
212
+ const option = (key) => {
213
+ const v = options[key];
214
+ return typeof v === 'number' ? v : undefined;
215
+ };
188
216
  const n = num(0);
189
217
  if (!Number.isFinite(n))
190
218
  return '';
191
- return n.toLocaleString(options.locale, {
192
- minimumFractionDigits: options['min-fraction-digits'],
193
- maximumFractionDigits: options['max-fraction-digits'],
219
+ return n.toLocaleString(typeof options.locale === 'string' ? options.locale : undefined, {
220
+ minimumFractionDigits: option('min-fraction-digits'),
221
+ maximumFractionDigits: option('max-fraction-digits'),
194
222
  });
195
223
  }
196
224
  case 'to-number': {
@@ -262,13 +290,10 @@ export function evaluate(e, ctx) {
262
290
  case '!=':
263
291
  return ev(0) !== ev(1);
264
292
  case '<':
265
- return ev(0) < ev(1);
266
293
  case '<=':
267
- return ev(0) <= ev(1);
268
294
  case '>':
269
- return ev(0) > ev(1);
270
295
  case '>=':
271
- return ev(0) >= ev(1);
296
+ return compare(op, ev(0), ev(1));
272
297
  case 'in': {
273
298
  const haystack = ev(1);
274
299
  const needle = ev(0);
@@ -277,19 +302,15 @@ export function evaluate(e, ctx) {
277
302
  return Array.isArray(haystack) && haystack.includes(needle);
278
303
  }
279
304
  case 'case': {
280
- for (let i = 0; i + 1 < args.length; i += 2)
281
- if (ev(i))
282
- return ev(i + 1);
283
- return ev(args.length - 1);
305
+ const hit = args.findIndex((_, i) => i % 2 === 0 && i + 1 < args.length && Boolean(ev(i)));
306
+ return hit < 0 ? ev(args.length - 1) : ev(hit + 1);
284
307
  }
285
308
  case 'match': {
286
309
  const input = ev(0);
287
- for (let i = 1; i + 1 < args.length; i += 2) {
288
- const label = args[i];
289
- if (Array.isArray(label) ? label.includes(input) : label === input)
290
- return ev(i + 1);
291
- }
292
- return ev(args.length - 1);
310
+ const hit = args.findIndex((label, i) => i % 2 === 1 &&
311
+ i + 1 < args.length &&
312
+ (Array.isArray(label) ? labels(label).includes(input) : label === input));
313
+ return hit < 0 ? ev(args.length - 1) : ev(hit + 1);
293
314
  }
294
315
  case 'step': {
295
316
  const input = num(0);
@@ -308,20 +329,20 @@ export function evaluate(e, ctx) {
308
329
  const kind = args[0];
309
330
  const input = num(1);
310
331
  const base = Array.isArray(kind) && kind[0] === 'exponential' ? Number(kind[1]) : 1;
311
- const stops = [];
312
- for (let i = 2; i + 1 < args.length; i += 2)
313
- stops.push([Number(args[i]), args[i + 1] ?? null]);
332
+ const stops = Array.from({ length: Math.floor((args.length - 2) / 2) }, (_, k) => [
333
+ Number(args[2 + 2 * k]),
334
+ args[3 + 2 * k] ?? null,
335
+ ]);
314
336
  const first = stops[0];
315
337
  if (!first)
316
338
  return null;
317
339
  if (input <= first[0])
318
340
  return evaluate(first[1], ctx);
319
- for (let i = 1; i < stops.length; i += 1) {
320
- const [s0, s1] = [stops[i - 1], stops[i]];
321
- if (s0 && s1 && input < s1[0])
322
- return mix(evaluate(s0[1], ctx), evaluate(s1[1], ctx), factor(input, s0[0], s1[0], base));
323
- }
324
- return evaluate(stops.at(-1)?.[1] ?? null, ctx);
341
+ const at = stops.findIndex((s1, i) => i > 0 && input < s1[0]);
342
+ const [s0, s1] = [stops[at - 1], stops[at]];
343
+ if (at < 0 || !s0 || !s1)
344
+ return evaluate(stops.at(-1)?.[1] ?? null, ctx);
345
+ return mix(evaluate(s0[1], ctx), evaluate(s1[1], ctx), factor(input, s0[0], s1[0], base));
325
346
  }
326
347
  case 'rgb':
327
348
  case 'rgba':
@@ -371,12 +392,12 @@ export function evaluate(e, ctx) {
371
392
  }
372
393
  return null;
373
394
  }
374
- }
395
+ };
375
396
  // ── Filters ──
376
397
  const LEGACY_COMPARISONS = new Set(['==', '!=', '<', '<=', '>', '>=']);
377
398
  /** Whether a filter (not `all`, `any`, `none`) is in the legacy form: `["==", "key", value]`, `["in", "key", a, b]`,
378
399
  * `["has", "key"]`; a key and literal values, as MapLibre tells them from expressions */
379
- function isLegacyLeaf(f) {
400
+ const isLegacyLeaf = (f) => {
380
401
  const [op, a, ...rest] = f;
381
402
  if (typeof op !== 'string' || typeof a !== 'string' || rest.some(Array.isArray))
382
403
  return false;
@@ -385,8 +406,8 @@ function isLegacyLeaf(f) {
385
406
  if (op === 'in')
386
407
  return f.length !== 3 || !Array.isArray(f[2]);
387
408
  return op === '!in' || op === 'has' || op === '!has';
388
- }
389
- function legacyFilter(f, ctx) {
409
+ };
410
+ const legacyFilter = (f, ctx) => {
390
411
  const [op, key, ...rest] = f;
391
412
  const v = get(ctx, key);
392
413
  switch (op) {
@@ -395,17 +416,14 @@ function legacyFilter(f, ctx) {
395
416
  case '!=':
396
417
  return v !== rest[0];
397
418
  case '<':
398
- return v < rest[0];
399
419
  case '<=':
400
- return v <= rest[0];
401
420
  case '>':
402
- return v > rest[0];
403
421
  case '>=':
404
- return v >= rest[0];
422
+ return compare(op, v, rest[0]);
405
423
  case 'in':
406
- return rest.includes(v);
424
+ return labels(rest).includes(v);
407
425
  case '!in':
408
- return !rest.includes(v);
426
+ return !labels(rest).includes(v);
409
427
  case 'has':
410
428
  return v !== undefined;
411
429
  case '!has':
@@ -413,53 +431,53 @@ function legacyFilter(f, ctx) {
413
431
  default:
414
432
  return true;
415
433
  }
416
- }
434
+ };
417
435
  /** Compiled filters, by filter object */
418
436
  const compiled = new WeakMap();
419
437
  /** A filter as a function: `all`, `any`, `none` of their parts, each part legacy or an expression (they may mix) */
420
- function compile(f) {
438
+ const compile = (f) => {
421
439
  if (!Array.isArray(f))
422
440
  return () => Boolean(f);
423
441
  const hit = compiled.get(f);
424
442
  if (hit)
425
443
  return hit;
426
- let test;
427
- const op = f[0];
428
- if (op === 'all' || op === 'any' || op === 'none') {
429
- const parts = f.slice(1).map(compile);
430
- if (op === 'all')
431
- test = (ctx) => parts.every((p) => p(ctx));
432
- else if (op === 'any')
433
- test = (ctx) => parts.some((p) => p(ctx));
434
- else
435
- test = (ctx) => !parts.some((p) => p(ctx));
436
- }
437
- else if (isLegacyLeaf(f)) {
438
- test = (ctx) => legacyFilter(f, ctx);
439
- }
440
- else {
441
- test = (ctx) => Boolean(evaluate(f, ctx));
442
- }
444
+ const make = () => {
445
+ const op = f[0];
446
+ if (op === 'all' || op === 'any' || op === 'none') {
447
+ const parts = f.slice(1).map(compile);
448
+ if (op === 'all')
449
+ return (ctx) => parts.every((p) => p(ctx));
450
+ if (op === 'any')
451
+ return (ctx) => parts.some((p) => p(ctx));
452
+ return (ctx) => !parts.some((p) => p(ctx));
453
+ }
454
+ if (isLegacyLeaf(f))
455
+ return (ctx) => legacyFilter(f, ctx);
456
+ return (ctx) => Boolean(evaluate(f, ctx));
457
+ };
458
+ const test = make();
443
459
  compiled.set(f, test);
444
460
  return test;
445
- }
446
- export function filter(f, ctx) {
461
+ };
462
+ export const filter = (f, ctx) => {
447
463
  if (f === undefined || f === null)
448
464
  return true;
449
465
  return compile(f)(ctx);
450
- }
466
+ };
467
+ /** The operators that read the feature */
468
+ const FEATURE_OPERATORS = new Set(['get', 'has', '!has', 'geometry-type', 'properties', 'id']);
451
469
  /** Whether a value depends on the feature (else only on the zoom, evaluated once per tile) */
452
- export function isFeatureDependent(e) {
470
+ export const isFeatureDependent = (e) => {
453
471
  if (e !== undefined && isFunction(e))
454
472
  return e.property !== undefined;
455
473
  if (!Array.isArray(e))
456
474
  return false;
457
- if (['get', 'has', '!has', 'geometry-type', 'properties', 'id'].includes(e[0]))
475
+ if (FEATURE_OPERATORS.has(e[0]))
458
476
  return true;
459
477
  return e.some((x) => isFeatureDependent(x));
460
- }
478
+ };
461
479
  /** A `{token}` text field (or an expression) to the text of a feature */
462
- export function text(field, ctx) {
480
+ export const text = (field, ctx) => {
463
481
  if (field === undefined || field === null)
464
482
  return '';
465
483
  if (typeof field === 'string') {
@@ -474,45 +492,45 @@ export function text(field, ctx) {
474
492
  return v.filter((x) => typeof x === 'string').join('');
475
493
  }
476
494
  return v === undefined || v === null ? '' : String(v);
477
- }
495
+ };
478
496
  /** The font of a label from the first of its `text-font`: a family without its weight takes the regular one */
479
- export function labelFont(font) {
497
+ export const labelFont = (font) => {
480
498
  return /Regular|Bold|Italic|Semibold|Light|Medium|Black/.test(font) ? font : `${font} Regular`;
481
- }
499
+ };
482
500
  /**
483
501
  * A value of the zoom only (an expression: `interpolate`, `step` of `["zoom"]`) as stops `[[zoom, value], …]` for the
484
502
  * engine, which interpolates them linearly: evaluated at its own stops and at the whole zooms between (an exponential
485
503
  * curve followed closely), a step kept sharp just before its threshold
486
504
  */
487
- export function zoomStops(e) {
505
+ export const zoomStops = (e) => {
488
506
  const zooms = new Set();
507
+ // The inputs of the stops of an `interpolate` or a `step` (from its fourth argument, one in two)
508
+ const stopInputs = (x) => x.filter((_, i) => i >= 3 && i % 2 === 1).map(Number);
489
509
  const walk = (x) => {
490
510
  if (!Array.isArray(x))
491
511
  return;
492
512
  const zoomInput = (input) => Array.isArray(input) && input[0] === 'zoom';
493
513
  if ((x[0] === 'interpolate' || x[0] === 'interpolate-hcl' || x[0] === 'interpolate-lab') && zoomInput(x[2])) {
494
- for (let i = 3; i < x.length; i += 2)
495
- zooms.add(Number(x[i]));
514
+ stopInputs(x).forEach((t) => {
515
+ zooms.add(t);
516
+ });
496
517
  }
497
518
  else if (x[0] === 'step' && zoomInput(x[1])) {
498
- for (let i = 3; i < x.length; i += 2) {
499
- const t = Number(x[i]);
519
+ stopInputs(x).forEach((t) => {
500
520
  zooms.add(t);
501
521
  zooms.add(t - 0.01);
502
- }
522
+ });
503
523
  }
504
- for (const y of x)
505
- walk(y);
524
+ x.forEach(walk);
506
525
  };
507
526
  walk(e);
508
527
  const finite = [...zooms].filter(Number.isFinite);
509
528
  if (finite.length === 0)
510
529
  finite.push(0);
511
530
  const [lo, hi] = [Math.min(...finite), Math.max(...finite)];
512
- for (let z = Math.ceil(lo); z <= Math.floor(hi); z += 1)
513
- finite.push(z);
531
+ finite.push(...Array.from({ length: Math.floor(hi) - Math.ceil(lo) + 1 }, (_, i) => Math.ceil(lo) + i));
514
532
  return [...new Set(finite)]
515
533
  .sort((a, b) => a - b)
516
534
  .map((zoom) => [zoom, Number(evaluate(e, { zoom, properties: {}, type: 'Unknown' }))]);
517
- }
535
+ };
518
536
  //# sourceMappingURL=vectorstyle.js.map