isard 0.0.0-stage → 0.2.1

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 (84) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/LICENSE +21 -0
  3. package/README.md +150 -2
  4. package/dist/basemap.worker.d.ts +76 -0
  5. package/dist/basemap.worker.js +4 -0
  6. package/dist/basemap.worker.js.map +13 -0
  7. package/dist/basemaps.d.ts +48 -0
  8. package/dist/basemaps.js +97 -0
  9. package/dist/basemaps.js.map +1 -0
  10. package/dist/color.d.ts +8 -0
  11. package/dist/color.js +98 -0
  12. package/dist/color.js.map +1 -0
  13. package/dist/controls.d.ts +43 -0
  14. package/dist/controls.js +115 -0
  15. package/dist/controls.js.map +1 -0
  16. package/dist/decoder.worker.js +6 -0
  17. package/dist/decoder.worker.js.map +12 -0
  18. package/dist/decoders.d.ts +27 -0
  19. package/dist/decoders.js +109 -0
  20. package/dist/decoders.js.map +1 -0
  21. package/dist/equirectangular.d.ts +35 -0
  22. package/dist/equirectangular.js +143 -0
  23. package/dist/equirectangular.js.map +1 -0
  24. package/dist/extrusions.d.ts +79 -0
  25. package/dist/extrusions.js +224 -0
  26. package/dist/extrusions.js.map +1 -0
  27. package/dist/features.d.ts +159 -0
  28. package/dist/features.js +387 -0
  29. package/dist/features.js.map +1 -0
  30. package/dist/geoplateforme.d.ts +35 -0
  31. package/dist/geoplateforme.js +56 -0
  32. package/dist/geoplateforme.js.map +1 -0
  33. package/dist/glyphs.d.ts +104 -0
  34. package/dist/glyphs.js +383 -0
  35. package/dist/glyphs.js.map +1 -0
  36. package/dist/index.d.ts +34 -0
  37. package/dist/index.js +25 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/interaction.d.ts +53 -0
  40. package/dist/interaction.js +259 -0
  41. package/dist/interaction.js.map +1 -0
  42. package/dist/isard_bg.wasm +0 -0
  43. package/dist/isard_core_bg.wasm +0 -0
  44. package/dist/layers.d.ts +103 -0
  45. package/dist/layers.js +290 -0
  46. package/dist/layers.js.map +1 -0
  47. package/dist/loader.d.ts +30 -0
  48. package/dist/loader.js +102 -0
  49. package/dist/loader.js.map +1 -0
  50. package/dist/map.d.ts +211 -0
  51. package/dist/map.js +1222 -0
  52. package/dist/map.js.map +1 -0
  53. package/dist/mvt.d.ts +19 -0
  54. package/dist/mvt.js +231 -0
  55. package/dist/mvt.js.map +1 -0
  56. package/dist/overlay.d.ts +118 -0
  57. package/dist/overlay.js +334 -0
  58. package/dist/overlay.js.map +1 -0
  59. package/dist/protocol.d.ts +30 -0
  60. package/dist/protocol.js +2 -0
  61. package/dist/protocol.js.map +1 -0
  62. package/dist/style.d.ts +7 -0
  63. package/dist/style.js +130 -0
  64. package/dist/style.js.map +1 -0
  65. package/dist/symbols.d.ts +61 -0
  66. package/dist/symbols.js +238 -0
  67. package/dist/symbols.js.map +1 -0
  68. package/dist/terrain.d.ts +14 -0
  69. package/dist/terrain.js +56 -0
  70. package/dist/terrain.js.map +1 -0
  71. package/dist/types.d.ts +312 -0
  72. package/dist/types.js +2 -0
  73. package/dist/types.js.map +1 -0
  74. package/dist/vectorstyle.d.ts +83 -0
  75. package/dist/vectorstyle.js +518 -0
  76. package/dist/vectorstyle.js.map +1 -0
  77. package/dist/vectortiles.d.ts +124 -0
  78. package/dist/vectortiles.js +523 -0
  79. package/dist/vectortiles.js.map +1 -0
  80. package/dist/wasm/isard.d.ts +612 -0
  81. package/dist/wasm/isard.js +2623 -0
  82. package/dist/wasm/isard_core.d.ts +469 -0
  83. package/dist/wasm/isard_core.js +2385 -0
  84. package/package.json +49 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,176 @@
1
+ # Changelog
2
+
3
+ Every notable change of `isard` (published as `@ign-junn/isard` up to 0.1.1). The format follows [Keep a Changelog](https://keepachangelog.com), the
4
+ versions [Semantic Versioning](https://semver.org) (before 1.0.0, a minor version may change an API).
5
+
6
+ ## [0.2.1] - 2026-10-18
7
+
8
+ ### Changed
9
+
10
+ - A mouse moving over a map with no `hover` nor `hoverfeature` listener draws nothing (it drew a frame per move);
11
+ the position under the pointer is computed only for `hover` listeners.
12
+ - Vector basemap workers: each keeps its share of the pool's decoded tiles (96 in all, instead of 96 each); an opaque
13
+ tile is moved out of its canvas instead of being copied; their canvases are reused. Images no longer used (evicted
14
+ source tiles of equirectangular rasters and of a style's raster layers, a style's sprite) are freed at once.
15
+ - The pointer's position no longer reads the layout at every move; the Terrarium and Terrain-RGB relief decodes
16
+ faster.
17
+ - `RasterLayer.setStyle(opacity, saturation)` is deprecated: `setStyle({ opacity, saturation })`, either value alone
18
+ (the positional form still works, and still sets the saturation back to 1 when it is not given).
19
+
20
+ ### Added
21
+
22
+ - `tiles3d: 'preload'`: the engine with 3D Tiles downloaded and compiled once the map is idle, for its first 3D Tiles
23
+ layer; the map is rebuilt on it in a task of its own.
24
+ - `RasterLayer.setStyle({ opacity, saturation })` (`RasterStyle`), either value alone.
25
+ - NASA's relief of the Moon and Mars is credited by the attribution control (`terrain: true` there).
26
+
27
+ ### Fixed
28
+
29
+ - A WebGL context restored while the engine with 3D Tiles loads, or after it failed to, no longer throws: the 3D
30
+ Tiles layers wait for it, the others are created again; a layer that fails to be created again is reported and the
31
+ others are created. Buildings linked to a network above them keep the link after a context loss or the move onto
32
+ the engine with 3D Tiles.
33
+ - A destroyed map stays destroyed when its context comes back (no engine rebuilt, no tiles fetched), and glyphs
34
+ arriving after `destroy` no longer reach the freed engine.
35
+ - An equirectangular raster with a `minZoom` has no holes at its first levels.
36
+ - The hover picking stops polling when the engine cannot pick, and gives up an answer that never comes.
37
+ - `VectorTiles` no longer changes the style object it is given; `destroy()` removes its raster and label layers
38
+ from the map, and the map forgets it and a removed labels layer.
39
+ - An unknown basemap name throws `isard: unknown basemap '…'` (it failed on an undefined); a `'terrarium'` or
40
+ `'mapbox'` relief given an image service or a `{bbox}` URL throws instead of fetching the template, and every
41
+ `{z}`, `{x}`, `{y}` of its URL is replaced.
42
+ - A layer removed twice is removed once from the engine.
43
+ - Values that are not numbers or out of range no longer stop the engine: a minimum zoom above the maximum, a pitch
44
+ limit, camera, size or pick radius that is not a number (ignored); an infinite point in a circle, line or network
45
+ layer (left out); index arrays of lines, fills and labels pointing past their data.
46
+ - A 3D Tiles glb reusing one accessor in many primitives, or a tileset grafting itself, is an error instead of using
47
+ up the memory; a texture of the wrong size or format is left out instead of drawing black.
48
+ - More features than the GPU's textures hold throw an error instead of drawing black; a failed restyle or relabel
49
+ keeps the layer drawable.
50
+ - The relief's altitudes near the antimeridian are read on the right side of it; the RAF20 geoid is applied only when
51
+ asked (`geoid: true`) and only on the Earth.
52
+ - Tiles of a page's loader at another size are resized; lines and buildings of translucent colors no longer darken
53
+ where their pieces overlap.
54
+ - Browsing over a regional raster source no longer remembers every missing tile; 3D Tiles siblings are no longer
55
+ prefetched near the memory budget, only to be unloaded again.
56
+
57
+ ## [0.2.0] - 2026-10-18
58
+
59
+ ### Changed
60
+
61
+ - The package is renamed from `@ign-junn/isard` to `isard` (`npm install isard`, `import { GeoMap } from 'isard'`);
62
+ `@ign-junn/isard` is deprecated. The project is independent of the IGN; the Géoplateforme's data stays supported, as
63
+ one source among others.
64
+ - The default basemap is OpenFreeMap's Positron (`'osm-positron'`: OpenStreetMap data, worldwide, free, no key), with
65
+ `'osm-liberty'` and `'osm-bright'`. The Géoplateforme's basemaps are named `'ign-plan'`, `'ign-plan-grey'`,
66
+ `'ign-ortho'`, `'ign-plan-vector'` and `'ign-plan-grey-vector'`; their former names (`LegacyBasemap`) are deprecated,
67
+ still accepted with a warning. `vectorTiles()` takes Positron by default (`OPENFREEMAP_STYLES`).
68
+ - No IGN hillshade by default (`hillshade` defaults to 0).
69
+ - `terrain: true` on the Earth is the worldwide Terrain Tiles of AWS's open data registry (Terrarium PNGs decoded in
70
+ the page, the sea floor at sea level); the IGN's model is `terrain: 'ign'` (or `{ model: 'ign' }`), and only it is
71
+ raised by the RAF20 geoid by default (`geoid` defaults to false for the other models). The Earth's relief range is
72
+ its dry land, -450 to 8900 m (the prisms of draped fills reach above Everest); the Moon's relief starts at level 3.
73
+ - Labels use OpenFreeMap's fonts by default (`GLYPHS_URL`, `DEFAULT_FONT`: Noto Sans Regular, Bold, Italic); a vector
74
+ basemap's labels take the glyphs of its style.
75
+ - The attribution control credits the sources in use, following them: OpenFreeMap, OpenMapTiles and OpenStreetMap,
76
+ the IGN only for the Géoplateforme's data, the Terrain Tiles (`OPENFREEMAP_ATTRIBUTION`, `IGN_ATTRIBUTION`,
77
+ `TERRAIN_TILES_ATTRIBUTION`; `DEFAULT_ATTRIBUTION` deprecated).
78
+ - Licence: © 2026 Kevin Graff.
79
+ - Fewer GPU readbacks (zoom anchored on the relief from the CPU), labels placed only when something moved, layers
80
+ out of view not drawn, an LRU for raster tiles.
81
+ - The vector basemap and the vector tiles of a map share one pool of workers and their decoded tiles; raster, vector
82
+ and 3D Tiles near the center of the view are fetched first (fetch priority).
83
+ - Circles and fills drawn by the squares in view only.
84
+ - The package ships its modules as they are (an application's bundler keeps what it uses), the workers and the
85
+ engine next to them; releases are published from the Release workflow, with provenance.
86
+ - A map without 3D Tiles downloads a third less WebAssembly: maps load the engine without 3D Tiles
87
+ (`isard_core_bg.wasm`, 220 KB gzipped instead of 330 KB) and the full one for their first 3D Tiles layer only
88
+ (`tiles3d: true` to load it from the start, `coreWasmUrl` for bundlers). Given `wasmUrl` alone, a map loads the full
89
+ engine from the start, as before.
90
+
91
+ ### Added
92
+
93
+ - The Géoplateforme's data in one call: `geoplateformeWmts`, `geoplateformeWms` (raster options of any of its layers,
94
+ crediting the IGN) and `geoplateformeWfs` (its features as GeoJSON); a guide page, "Géoplateforme (IGN) data".
95
+ - `RasterOptions.attribution`: a raster's credits, shown while it is on the map; `VectorTilesOptions.attribution`.
96
+ - Relief tiles of PNG encodings: `terrain.encoding`, `'terrarium'` or `'mapbox'` (Terrain-RGB), decoded in the page.
97
+ - Vector basemaps: the raster layers of a style (its shaded relief), extrusions drawn flat, `line-gap-width`, text
98
+ sizes of zoom expressions; a text written over its icon (a road shield's number) is left out, icons not being drawn.
99
+ - 3D Tiles: implicit tiling (quadtree and octree, binary or JSON subtrees), several contents per tile, sparse and
100
+ zero-filled accessors, `texCoord` sets, content recognized by its bytes; horizon culling on the globe.
101
+ - Style expressions of vector basemaps: arithmetic, `format`, conversions, `coalesce`, `length`, `at`… (an unknown
102
+ operator is warned about once and evaluates to `null`).
103
+ - Every CSS color wherever a color is given (`#rgb`, `rgb()`, `hsl()`, named colors).
104
+ - Exported types used by the API: `AnimationOptions`, `FlyToOptions`, `AtmosphereOptions`, `DecoderUrls`,
105
+ `LabelCandidate`, `Json`, `TileKey`; an API reference in the documentation.
106
+ - `hoverfeature`: what is under the pointer, picked without stalling the GPU (`hoverPicking` for its radius and
107
+ layers); `PickResult`, `FeatureHit`, `MapHoverFeatureEvent` exported.
108
+ - Labels never run out of glyph space: the atlas grows into pages.
109
+ - Keyboard navigation (arrows, +/-, Shift to rotate and pitch, 0 to reset), `keyboard` and `ariaLabel` options, a
110
+ focusable map with a visible focus.
111
+ - Maps of other bodies than the Earth: the `body` option, `'moon'` (a sphere of 1 737 400 m, no atmosphere) or any
112
+ ellipsoid (`BodyOptions`: radius, flattening, atmosphere, color); the metres of the camera, relief, buildings and
113
+ 3D Tiles follow it. Off the Earth, none of the Géoplateforme defaults (basemap, hillshade, IGN relief, geoid, credits)
114
+ and a black background; `AttributionControl` without the IGN credits. A `moon` example (NASA's LRO mosaic).
115
+ - Mars: `body: 'mars'` (a sphere of its equatorial radius, 3 396 190 m, for the planetocentric latitudes of its data)
116
+ with a thin dusty halo. The halo is the body's: `BodyOptions.atmosphere` takes a CSS color (its alpha the strength)
117
+ besides true (the Earth's blue) and false. A `mars` example (the Viking colour mosaic of NASA's Mars Trek).
118
+ - Raster tiles in longitudes and latitudes (`grid: 'equirectangular'`, the grid of NASA's Trek and of WorldCRS84Quad),
119
+ reprojected in the page into Web Mercator tiles.
120
+ - Relief of the Moon and Mars: `terrain: true` there takes NASA's terrain models from the image services of its Trek
121
+ (LOLA, 237 m; MOLA, 463 m), asked for raw floats in the body's Mercator. `terrain.url` takes an ArcGIS image service
122
+ (`…/ImageServer`) on any body, and `{bbox}` is in the body's Mercator metres. `BodyOptions.relief` (its lowest and
123
+ highest ground) bounds what the relief may raise into view, the prisms of draped fills, the no-data and the horizon;
124
+ the relief starts at a coarser level on a body of a larger relief for its size (3 on the Moon and Mars). The
125
+ `moon` and `mars` examples have relief and pitched views (Tycho, Copernicus, Olympus Mons, Valles Marineris).
126
+
127
+ ### Fixed
128
+
129
+ - Layers whose first level is coarse for the view (the relief of the Moon and Mars) load under a pitched camera on the
130
+ globe: the tile under the view center no longer measures too small to refine to it.
131
+ - A listener that throws no longer stops the map from drawing; a failed engine download can be retried.
132
+ - Workers that fail to load reject their requests with an explanation instead of leaving them pending.
133
+ - 3D Tiles content and network arrays that are malformed are errors instead of a crash of the engine; a tileset whose
134
+ root fails no longer prevents `idle`, and is retried.
135
+ - Negative integer properties of vector tiles; vector tiles spread evenly over the workers.
136
+ - Destroying a map stops its downloads and workers and frees its GPU memory; the device pixel ratio is followed.
137
+ - Lines, circles, labels and networks no longer jitter at high zoom over large layers.
138
+ - Circles and labels past the horizon of the globe are hidden instead of showing through it.
139
+
140
+ ### Migrating from @ign-junn/isard 0.1.1
141
+
142
+ - The package: `npm install isard`, `import … from 'isard'` (instead of `@ign-junn/isard`).
143
+ - The basemaps: the default is `'osm-positron'` (OpenFreeMap), no longer PLAN.IGN; the Géoplateforme's are
144
+ `'ign-plan'`, `'ign-plan-grey'`, `'ign-ortho'`, `'ign-plan-vector'`, `'ign-plan-grey-vector'` (the former names
145
+ still work, with a warning). `basemap: 'ign-plan-grey'` brings back the former default (`'plan-grey'`).
146
+ - The hillshade: `setBasemap(basemap)` and the `hillshade` option default to 0 (0.4 before): pass `0.4` to keep the
147
+ IGN hillshade.
148
+ - The relief: `terrain: { exaggeration }` without `url` (and `terrain: true`) is now the worldwide Terrain Tiles;
149
+ `terrain: 'ign'` or `{ model: 'ign' }` for the IGN's model. A custom `terrain.url` no longer gets the RAF20 geoid by
150
+ default: add `geoid: true` to keep it.
151
+ - The credits: `AttributionControl` shows those of the sources in use (OpenFreeMap and OpenStreetMap, the IGN only
152
+ for its data, the Terrain Tiles, NASA); `DEFAULT_ATTRIBUTION` is deprecated (`IGN_ATTRIBUTION`); credit your own
153
+ rasters with `RasterOptions.attribution`, other data with `customAttribution`.
154
+
155
+ ## [0.1.1] - 2026-10-16
156
+
157
+ ### Fixed
158
+
159
+ - Vector basemaps: the style's sprite is downloaded once, and only for the layers drawing fill patterns (it was by
160
+ every worker of every renderer); a vector tile always goes to the same worker, which keeps it decoded for all the
161
+ raster tiles drawn from it.
162
+
163
+ ## [0.1.0] - 2026-10-16
164
+
165
+ First published version.
166
+
167
+ ### Added
168
+
169
+ - `GeoMap`: Géoplateforme basemaps (PLAN.IGN raster or vector, ortho), hillshade and relief (RGE ALTI, exaggeration,
170
+ geoid optional), globe and Web Mercator, camera of MapLibre's conventions (`easeTo`, `flyTo`, pitch up to 85 with a
171
+ sky and ground fog), events (`move`, `moveend`, `hover` once per frame, `click`, `idle`…), picking within a radius.
172
+ - Layers: rasters (fetched or drawn by the page), networks colored by a value per node, GeoJSON lines, circles, fills
173
+ (on the relief at any size) and labels (Géoplateforme fonts, shared collisions), buildings (values, or a network's
174
+ times), ground shaders (the application's GLSL over the ground), 3D Tiles (b3dm, glb, Draco, meshopt, KTX2).
175
+ - Vector basemaps from MapLibre styles, drawn in workers into the raster pool, their labels above the other layers.
176
+ - Markers, popups, navigation and attribution controls.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kevin Graff
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,151 @@
1
- # Temporary Holding Version
1
+ <div align="center">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <img src="docs/public/logo.svg" width="96" alt="" />
4
+
5
+ # Isard
6
+
7
+ **Maps that recolor at every mouse move.**
8
+
9
+ A WebGL2 map engine for large, interactive data layers, labels and 3D Tiles.<br />
10
+ Rust core in WebAssembly, a small TypeScript API.
11
+
12
+ [![npm](https://img.shields.io/npm/v/isard?color=2f6fd1)](https://www.npmjs.com/package/isard)
13
+ [![Documentation](https://img.shields.io/badge/docs-graffkevin.github.io%2Fisard-2f6fd1)](https://graffkevin.github.io/isard/)
14
+ [![Checks](https://github.com/graffkevin/isard/actions/workflows/checks.yml/badge.svg)](https://github.com/graffkevin/isard/actions/workflows/checks.yml)
15
+ [![Licence: MIT](https://img.shields.io/badge/licence-MIT-blue)](LICENSE)
16
+
17
+ **[Documentation](https://graffkevin.github.io/isard/)** ·
18
+ **[Examples](https://graffkevin.github.io/isard/examples/)** ·
19
+ **[API](https://graffkevin.github.io/isard/api)** ·
20
+ **[Changelog](CHANGELOG.md)**
21
+
22
+ <img src="docs/public/screenshots/hero.jpg" alt="Isard: rings drawn by a shader over the relief of the Mont Blanc, buildings colored by height, communes colored by population, the globe" />
23
+
24
+ </div>
25
+
26
+ ## Why Isard
27
+
28
+ General-purpose map libraries spend their time on things these maps do not need. Isard is built for maps where
29
+ **millions of features change at every mouse move**: accessibility, flooding, flows.
30
+
31
+ - ⚡ **One upload, millions of features.** A network of 2 million street pieces recolors from a single texture upload
32
+ of 4 bytes per node, drawn in the very frame of the mouse move; only the squares in view are drawn.
33
+ - 🗺️ **Basemaps and relief, worldwide.** OpenFreeMap's vector basemaps by default (OpenStreetMap data, no key), any
34
+ MapLibre style drawn in workers with its labels above your data, raster tiles, a worldwide relief, on a globe that
35
+ turns into Web Mercator. The Géoplateforme's data for France in one option or call: PLAN.IGN, orthophotos,
36
+ hillshade, the IGN relief, any WMTS, WMS or WFS layer ([Géoplateforme (IGN) data](https://graffkevin.github.io/isard/guide/geoplateforme)).
37
+ - ⛰️ **Everything on the relief.** Lines, circles, labels and buildings stand on the ground; fills of any size are
38
+ exact to the pixel over hills and valleys; your own GLSL is drawn over the ground like the basemap.
39
+ - 🪐 **The Moon, Mars, any body.** `body: 'moon'`, `'mars'` or an ellipsoid of your own: the globe at its radius
40
+ with its own atmosphere halo (or none), its metres in the camera, the relief and 3D Tiles, NASA's planetary mosaics
41
+ reprojected in the page from longitudes and latitudes, and their relief: `terrain: true` raises Olympus Mons and
42
+ digs Valles Marineris and the lunar craters from NASA's LOLA and MOLA models.
43
+ - 🏙️ **Buildings and 3D Tiles.** Buildings colored by a value or by a network's travel times, photomeshes and city
44
+ models in 3D Tiles (Draco, meshopt, KTX2).
45
+ - 🦀 **Small and exact.** 220 KB of WebAssembly gzipped (330 KB once a 3D Tiles layer is added), f64 on the CPU and f32 relative to the camera on the GPU
46
+ (centimetres at any zoom), frames on demand only, strict TypeScript types end to end.
47
+
48
+ ## Quick start
49
+
50
+ ```sh
51
+ bun add isard # or npm install isard
52
+ ```
53
+
54
+ ```ts
55
+ import { GeoMap } from 'isard';
56
+
57
+ const map = await GeoMap.create(container, { center: [2.4246, 48.8457], zoom: 14 });
58
+ const network = map.addNetwork({ nodes, edges, max: 1800 }); // lon/lat pairs, node index pairs
59
+ map.on('hover', ({ lonLat }) => lonLat && network.setValues(travelTimes(lonLat))); // one value per node
60
+ ```
61
+
62
+ That is the whole integration: the map fills its container, follows its size, handles mouse and touch and loads the
63
+ basemap. With a bundler, see [Getting started](https://graffkevin.github.io/isard/guide/getting-started#with-a-bundler).
64
+
65
+ ## Examples
66
+
67
+ Live in the [documentation](https://graffkevin.github.io/isard/examples/), sources in [`examples/`](examples/).
68
+
69
+ <table>
70
+ <tr>
71
+ <td width="50%"><a href="https://graffkevin.github.io/isard/examples/globe"><img src="docs/public/screenshots/globe.jpg" alt="" /></a><br /><b>The globe</b><br />turning in its atmosphere, a flight to a click</td>
72
+ <td width="50%"><a href="https://graffkevin.github.io/isard/examples/basic"><img src="docs/public/screenshots/basic.jpg" alt="" /></a><br /><b>A map</b><br />the vector basemap, a marker and its popup</td>
73
+ </tr>
74
+ <tr>
75
+ <td><a href="https://graffkevin.github.io/isard/examples/geojson"><img src="docs/public/screenshots/geojson.jpg" alt="" /></a><br /><b>GeoJSON layers</b><br />fills by value, outlines, labels, picking</td>
76
+ <td><a href="https://graffkevin.github.io/isard/examples/buildings"><img src="docs/public/screenshots/buildings.jpg" alt="" /></a><br /><b>Buildings</b><br />raised to their height, colored by it</td>
77
+ </tr>
78
+ <tr>
79
+ <td><a href="https://graffkevin.github.io/isard/examples/relief"><img src="docs/public/screenshots/relief.jpg" alt="" /></a><br /><b>Relief and a ground shader</b><br />your GLSL over the mountains</td>
80
+ <td><a href="https://graffkevin.github.io/isard/examples/network"><img src="docs/public/screenshots/network.jpg" alt="" /></a><br /><b>Travel times on hover</b><br />a network recolored at every move</td>
81
+ </tr>
82
+ <tr>
83
+ <td><a href="https://graffkevin.github.io/isard/examples/vector"><img src="docs/public/screenshots/vector.jpg" alt="" /></a><br /><b>Vector basemaps</b><br />OpenFreeMap's styles, a restyled subset</td>
84
+ <td><a href="https://graffkevin.github.io/isard/examples/tiles3d"><img src="docs/public/screenshots/tiles3d.jpg" alt="" /></a><br /><b>3D Tiles</b><br />the Lille photomesh</td>
85
+ </tr>
86
+ <tr>
87
+ <td><a href="https://graffkevin.github.io/isard/examples/moon"><img src="docs/public/screenshots/moon.jpg" alt="" /></a><br /><b>The Moon</b><br />another body, the LRO mosaic over LOLA's relief, the Apollo sites</td>
88
+ <td><a href="https://graffkevin.github.io/isard/examples/mars"><img src="docs/public/screenshots/mars.jpg" alt="" /></a><br /><b>Mars</b><br />its thin dusty halo, the Viking mosaic over MOLA's relief, famous sites</td>
89
+ </tr>
90
+ </table>
91
+
92
+ ## What it draws
93
+
94
+ | Layer | | |
95
+ |---|---|---|
96
+ | [Networks](https://graffkevin.github.io/isard/layers/networks) | `addNetwork` | millions of edges colored by a value per node, worked out per pixel along the edges |
97
+ | [Lines, circles, fills](https://graffkevin.github.io/isard/layers/features) | `addLines`, `addCircles`, `addFills` | GeoJSON styled per feature: zoom stops, functions, values through a ramp, filters |
98
+ | [Labels](https://graffkevin.github.io/isard/layers/labels) | `addSymbols` | SDF fonts (Noto Sans by default, any glyphs), halos, along lines, collisions shared by all layers |
99
+ | [Buildings](https://graffkevin.github.io/isard/layers/buildings) | `addExtrusions` | tiles or one set, colored by values or by a network's times |
100
+ | [Ground shaders](https://graffkevin.github.io/isard/layers/ground) | `addGround` | your fragment shader over the ground: water depth, heat maps |
101
+ | [Rasters](https://graffkevin.github.io/isard/layers/rasters) | `addRaster` | XYZ, WMTS, WMS, equirectangular tiles reprojected, or tiles drawn by your code |
102
+ | [3D Tiles](https://graffkevin.github.io/isard/layers/tiles3d) | `addTiles3d` | b3dm, glb, external tilesets, compressed or not |
103
+
104
+ Plus markers, popups, controls, picking within a radius, `easeTo`, `flyTo`, a sky and fog for tilted views. All in
105
+ the [API reference](https://graffkevin.github.io/isard/api).
106
+
107
+ ## Fast by design
108
+
109
+ Geometry uploaded once and only the view drawn, values in textures, the ground composited in one pass, frames on
110
+ demand, decoding and vector tiles in workers. The details and the measures:
111
+ [Performance](https://graffkevin.github.io/isard/guide/performance).
112
+
113
+ ## Develop
114
+
115
+ Rust (with `wasm32-unknown-unknown` and `wasm-bindgen-cli` 0.2.129) and Bun:
116
+
117
+ ```sh
118
+ bun install
119
+ bun run dev # the demo on http://127.0.0.1:5180, the examples on /examples/, the benchmark on /bench/
120
+ bun run test # Rust tests
121
+ bun run lint # Biome, clippy pedantic (native and wasm)
122
+ bun run typecheck # strict TypeScript
123
+ bun run docs:dev # the documentation, with the examples live
124
+ ```
125
+
126
+ The root holds two workspaces side by side: Rust (`Cargo.toml`) and TypeScript (`package.json`), and `bun run build`
127
+ drives both.
128
+
129
+ ```
130
+ crates/geo/ camera, projections, geodesy and geoid, tile selection, label placement, polygons tested natively
131
+ crates/tiles3d/ 3D Tiles tree, traversal and decoding tested natively
132
+ crates/engine/ the WebAssembly core: the engine, the ground, the layers, picking
133
+ packages/isard/ the npm package isard: GeoMap, layer handles, overlays, workers
134
+ apps/ the demo and the benchmark (Isard, iTowns, 3DTilesRendererJS)
135
+ examples/ one page per example
136
+ docs/ the documentation site (VitePress)
137
+ scripts/ build, dev server, docs, release
138
+ ```
139
+
140
+ More in [Develop and release](https://graffkevin.github.io/isard/guide/develop).
141
+
142
+ ## Roadmap
143
+
144
+ - Flood maps over the relief (water depth as a ground shader) as a full example.
145
+ - Icons of the basemaps' symbol layers (sprites), text along curved lines.
146
+ - Labels fading in and out instead of popping.
147
+
148
+ ## Licence
149
+
150
+ MIT, © 2026 Kevin Graff. Basemap data: © OpenStreetMap contributors (OpenFreeMap, OpenMapTiles); IGN data: Licence
151
+ Ouverte (Géoplateforme).
@@ -0,0 +1,76 @@
1
+ import { type LabelCandidate, type RasterSource, type StyleLayer } from './vectorstyle.ts';
2
+ export type { LabelCandidate, RasterSource };
3
+ /** Fetch priority of a request (`fetch`'s `priority`, a hint to the browser) */
4
+ export type Priority = 'high' | 'low' | 'auto';
5
+ /** A tile's image or labels, asked of a worker */
6
+ export type BasemapWork = {
7
+ type: 'render';
8
+ key: string;
9
+ z: number;
10
+ x: number;
11
+ y: number;
12
+ size: number;
13
+ priority?: Priority;
14
+ } | {
15
+ type: 'labels';
16
+ key: string;
17
+ z: number;
18
+ x: number;
19
+ y: number;
20
+ priority?: Priority;
21
+ };
22
+ export type BasemapRequest = {
23
+ type: 'init';
24
+ /** Renderers by key: each draws its own layers of the same source */
25
+ key: string;
26
+ tiles: string;
27
+ maxzoom: number;
28
+ layers: StyleLayer[];
29
+ /** The style's raster sources, by name, for its raster layers */
30
+ rasters?: Record<string, RasterSource>;
31
+ /** The style's sprite, for the fill patterns (loaded once by the page) */
32
+ sprite?: Sprite;
33
+ /** Workers of the pool: each keeps its share of the decoded tiles */
34
+ workers?: number;
35
+ } | (BasemapWork & {
36
+ id: number;
37
+ }) | {
38
+ type: 'abort';
39
+ id: number;
40
+ }
41
+ /** A renderer no longer used: forgotten */
42
+ | {
43
+ type: 'drop';
44
+ key: string;
45
+ };
46
+ export type BasemapResponse = {
47
+ id: number;
48
+ image: ImageBitmap;
49
+ } | {
50
+ id: number;
51
+ missing: true;
52
+ } | {
53
+ id: number;
54
+ error: string;
55
+ } | {
56
+ id: number;
57
+ labels: LabelCandidate[];
58
+ };
59
+ export interface Sprite {
60
+ image: ImageBitmap;
61
+ icons: Record<string, {
62
+ x: number;
63
+ y: number;
64
+ width: number;
65
+ height: number;
66
+ pixelRatio?: number;
67
+ }>;
68
+ }
69
+ /** The decoded tiles one worker of `workers` keeps */
70
+ export declare function cacheShare(workers: number): number;
71
+ /**
72
+ * The image of a drawn canvas, its alpha straight (the engine blends straight alpha). An opaque tile (a background
73
+ * of full opacity, as a basemap's) is moved out of the canvas as it is, premultiplied and straight being the same
74
+ * there; another one is read back unpremultiplied, copied
75
+ */
76
+ export declare function tileImage(canvas: OffscreenCanvas, g: OffscreenCanvasRenderingContext2D, opaque: boolean): Promise<ImageBitmap>;
@@ -0,0 +1,4 @@
1
+ class Q{buf;end;pos;constructor(e,n=0,r=e.length){this.buf=e;this.end=r;this.pos=n}varint(){let e=0;for(let n=0;n<70;n+=7){let r=this.buf[this.pos++]??0;if(e+=(r&127)*2**n,r<128)break}return e}int64(){let e=0,n=0;for(let r=0;r<10;r+=1){let i=this.buf[this.pos++]??0,t=i&127;if(r<4)e|=t<<7*r;else if(r===4)e|=(t&15)<<28,n|=t>>4;else n|=t<<7*r-32;if(i<128)break}return(n|0)*4294967296+(e>>>0)}sint(){let e=this.varint();return e%2===1?-(e+1)/2:e/2}sub(){let e=this.varint(),n=Math.min(this.pos+e,this.end),r=new Q(this.buf,this.pos,n);return this.pos=n,r}string(){let e=this.varint(),n=Math.min(this.pos+e,this.end),r=Ae.decode(this.buf.subarray(this.pos,n));return this.pos=n,r}float(){let e=this.pos+4<=this.end?this.view().getFloat32(this.pos,!0):0;return this.pos+=4,e}double(){let e=this.pos+8<=this.end?this.view().getFloat64(this.pos,!0):0;return this.pos+=8,e}view(){return new DataView(this.buf.buffer,this.buf.byteOffset,this.buf.byteLength)}skip(e){if(e===0)this.varint();else if(e===1)this.pos+=8;else if(e===2)this.pos+=this.varint();else if(e===5)this.pos+=4}get more(){return this.pos<this.end}}var Ae=new TextDecoder;function Ce(e){let n="";while(e.more){let r=e.varint();switch(r>>3){case 1:n=e.string();break;case 2:n=e.float();break;case 3:n=e.double();break;case 4:n=e.int64();break;case 5:n=e.varint();break;case 6:n=e.sint();break;case 7:n=e.varint()!==0;break;default:e.skip(r&7)}}return n}function Ne(e){let n=[],r,[i,t]=[0,0];while(e.more){let s=e.varint(),u=s&7,o=s>>3;if(u===7){if(r&&r.length>=2)r.push(r[0]??0,r[1]??0);continue}for(let a=0;a<o&&e.more;a+=1){if(i+=e.sint(),t+=e.sint(),u===1)r=[],n.push(r);r?.push(i,t)}}return n}function ie(e,n){let r=new Map,i=new Q(e);while(i.more){let t=i.varint();if(t>>3!==3){i.skip(t&7);continue}let s=i.sub(),u="",o=4096,a=[],c=[],x=[];while(s.more){let g=s.varint();switch(g>>3){case 1:u=s.string();break;case 2:x.push(s.sub());break;case 3:a.push(s.string());break;case 4:c.push(Ce(s.sub()));break;case 5:o=s.varint();break;default:s.skip(g&7)}}if(n&&!n.has(u))continue;let f=[];for(let g of x){let h=1,v={},L=[];while(g.more){let d=g.varint();switch(d>>3){case 2:{let S=g.sub();while(S.more){let N=a[S.varint()],R=c[S.varint()];if(N!==void 0&&R!==void 0)v[N]=R}break}case 3:h=Math.min(3,Math.max(1,g.varint()));break;case 4:L=Ne(g.sub());break;default:g.skip(d&7)}}f.push({type:h,properties:v,geometry:L})}r.set(u,{extent:o,features:f})}return r}var Re="aliceblue f0f8ff antiquewhite faebd7 aqua 00ffff aquamarine 7fffd4 azure f0ffff beige f5f5dc bisque ffe4c4 black 000000 blanchedalmond ffebcd blue 0000ff blueviolet 8a2be2 brown a52a2a burlywood deb887 cadetblue 5f9ea0 chartreuse 7fff00 chocolate d2691e coral ff7f50 cornflowerblue 6495ed cornsilk fff8dc crimson dc143c cyan 00ffff darkblue 00008b darkcyan 008b8b darkgoldenrod b8860b darkgray a9a9a9 darkgreen 006400 darkgrey a9a9a9 darkkhaki bdb76b darkmagenta 8b008b darkolivegreen 556b2f darkorange ff8c00 darkorchid 9932cc darkred 8b0000 darksalmon e9967a darkseagreen 8fbc8f darkslateblue 483d8b darkslategray 2f4f4f darkslategrey 2f4f4f darkturquoise 00ced1 darkviolet 9400d3 deeppink ff1493 deepskyblue 00bfff dimgray 696969 dimgrey 696969 dodgerblue 1e90ff firebrick b22222 floralwhite fffaf0 forestgreen 228b22 fuchsia ff00ff gainsboro dcdcdc ghostwhite f8f8ff gold ffd700 goldenrod daa520 gray 808080 green 008000 greenyellow adff2f grey 808080 honeydew f0fff0 hotpink ff69b4 indianred cd5c5c indigo 4b0082 ivory fffff0 khaki f0e68c lavender e6e6fa lavenderblush fff0f5 lawngreen 7cfc00 lemonchiffon fffacd lightblue add8e6 lightcoral f08080 lightcyan e0ffff lightgoldenrodyellow fafad2 lightgray d3d3d3 lightgreen 90ee90 lightgrey d3d3d3 lightpink ffb6c1 lightsalmon ffa07a lightseagreen 20b2aa lightskyblue 87cefa lightslategray 778899 lightslategrey 778899 lightsteelblue b0c4de lightyellow ffffe0 lime 00ff00 limegreen 32cd32 linen faf0e6 magenta ff00ff maroon 800000 mediumaquamarine 66cdaa mediumblue 0000cd mediumorchid ba55d3 mediumpurple 9370db mediumseagreen 3cb371 mediumslateblue 7b68ee mediumspringgreen 00fa9a mediumturquoise 48d1cc mediumvioletred c71585 midnightblue 191970 mintcream f5fffa mistyrose ffe4e1 moccasin ffe4b5 navajowhite ffdead navy 000080 oldlace fdf5e6 olive 808000 olivedrab 6b8e23 orange ffa500 orangered ff4500 orchid da70d6 palegoldenrod eee8aa palegreen 98fb98 paleturquoise afeeee palevioletred db7093 papayawhip ffefd5 peachpuff ffdab9 peru cd853f pink ffc0cb plum dda0dd powderblue b0e0e6 purple 800080 rebeccapurple 663399 red ff0000 rosybrown bc8f8f royalblue 4169e1 saddlebrown 8b4513 salmon fa8072 sandybrown f4a460 seagreen 2e8b57 seashell fff5ee sienna a0522d silver c0c0c0 skyblue 87ceeb slateblue 6a5acd slategray 708090 slategrey 708090 snow fffafa springgreen 00ff7f steelblue 4682b4 tan d2b48c teal 008080 thistle d8bfd8 tomato ff6347 turquoise 40e0d0 violet ee82ee wheat f5deb3 white ffffff whitesmoke f5f5f5 yellow ffff00 yellowgreen 9acd32",E;function Le(e){if(!E){E=new Map;let n=Re.split(" ");for(let r=0;r+1<n.length;r+=2)E.set(n[r],n[r+1])}return E.get(e)}var W=(e)=>Number.isFinite(e)?Math.max(0,Math.min(1,e)):0;function oe(e){if(!/^[0-9a-f]+$/.test(e)||![3,4,6,8].includes(e.length))return;let n=e.length<=4?[...e].map((i)=>i+i).join(""):e,r=(i)=>Number.parseInt(n.slice(i,i+2),16)/255;return[r(0),r(2),r(4),n.length===8?r(6):1]}function Je(e){if(e===void 0)return 1;return W(e.endsWith("%")?Number.parseFloat(e)/100:Number.parseFloat(e))}function Pe(e,n,r){let i=r<0.5?r*(1+n):r+n-r*n,t=2*r-i,s=(u)=>{let o=(u%1+1)%1;if(o<0.16666666666666666)return t+(i-t)*6*o;if(o<0.5)return i;if(o<0.6666666666666666)return t+(i-t)*(0.6666666666666666-o)*6;return t};return[s(e+0.3333333333333333),s(e),s(e-0.3333333333333333)]}function se(e){if(typeof e!=="string")return;let n=e.trim().toLowerCase();if(n.startsWith("#"))return oe(n.slice(1));if(n==="transparent")return[0,0,0,0];let r=Le(n);if(r)return oe(r);let i=/^(rgba?|hsla?)\(([^)]*)\)$/.exec(n);if(!i?.[1]||i[2]===void 0)return;let t=i[2].split(/[\s,/]+/).filter(Boolean);if(t.length<3||t.slice(0,3).some((x)=>Number.isNaN(Number.parseFloat(x))))return;let s=Je(t[3]);if(i[1].startsWith("rgb")){let x=(f)=>W(f.endsWith("%")?Number.parseFloat(f)/100:Number.parseFloat(f)/255);return[x(t[0]),x(t[1]),x(t[2]),s]}let u=Number.parseFloat(t[0])/360,[o,a,c]=Pe(u,W(Number.parseFloat(t[1])/100),W(Number.parseFloat(t[2])/100));return[o,a,c,s]}var Fe=["Unknown","Point","LineString","Polygon"];function X(e,n){return{zoom:n,properties:e.properties,type:Fe[e.type]??"Unknown"}}function B(e){return se(e)}function j([e,n,r,i]){return`rgba(${Math.round(e*255)},${Math.round(n*255)},${Math.round(r*255)},${i})`}function le(e,n,r,i=1){let t=r-n;if(t===0)return 0;let s=e-n;return i===1?s/t:(i**s-1)/(i**t-1)}function Z(e,n,r){if(typeof e==="number"&&typeof n==="number")return e+(n-e)*r;let i=B(e),t=B(n);if(i&&t)return j([0,1,2,3].map((s)=>(i[s]??0)+((t[s]??0)-(i[s]??0))*r));if(Array.isArray(e)&&Array.isArray(n))return e.map((s,u)=>Z(s,n[u],r));return r<0.5?e:n}function ce(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)&&Array.isArray(e.stops)}function ze(e,n){let r=e.property===void 0?n.zoom:n.properties[e.property],i=e.stops,t=e.type??(e.property===void 0||typeof i[0]?.[0]==="number"?"exponential":"categorical");if(t==="identity")return r??e.default;if(t==="categorical")return i.find(([o])=>o===r)?.[1]??e.default;if(typeof r!=="number"||!i.length)return e.default??i[0]?.[1];let s=(o)=>typeof o[0]==="object"&&o[0]!==null?Number(o[0].zoom):Number(o[0]),u=i[0];if(!u)return e.default;if(r<=s(u))return u[1];for(let o=1;o<i.length;o+=1){let[a,c]=[i[o-1],i[o]];if(!a||!c)continue;if(r<s(c)){if(t==="interval")return a[1];return Z(a[1],c[1],le(r,s(a),s(c),e.base??1))}}return i.at(-1)?.[1]}function V(e,n){if(n==="$type")return e.type;return typeof n==="string"?e.properties[n]:void 0}var Te=new Set(["literal","zoom","get","has","!has","properties","id","geometry-type","concat","format","image","number-format","to-number","to-string","to-boolean","to-color","number","string","boolean","typeof","length","at","upcase","downcase","coalesce","!","all","any","==","!=","<","<=",">",">=","in","case","match","step","interpolate","interpolate-hcl","interpolate-lab","rgb","rgba","+","-","*","/","%","^","min","max","abs","round","floor","ceil","sqrt","ln","log10","log2","pi","e"]);function ee(e){return Array.isArray(e)&&typeof e[0]==="string"&&Te.has(e[0])}var ae=new Set,O=(e)=>e===void 0||e===null?"":String(e);function J(e,n){if(ce(e))return ze(e,n);if(!Array.isArray(e))return e;let[r,...i]=e;if(typeof r!=="string")return e;let t=(o)=>J(i[o]??null,n),s=(o)=>Number(t(o)),u=()=>i.map((o,a)=>s(a));switch(r){case"literal":return i[0];case"zoom":return n.zoom;case"get":return V(n,t(0));case"has":return V(n,t(0))!==void 0;case"!has":return V(n,t(0))===void 0;case"properties":return n.properties;case"id":return null;case"geometry-type":return n.type;case"concat":return i.map((o,a)=>O(t(a))).join("");case"format":return i.map((o,a)=>o!==null&&typeof o==="object"&&!Array.isArray(o)?"":O(t(a))).join("");case"image":return t(0);case"number-format":{let o=i[1]??{},a=s(0);if(!Number.isFinite(a))return"";return a.toLocaleString(o.locale,{minimumFractionDigits:o["min-fraction-digits"],maximumFractionDigits:o["max-fraction-digits"]})}case"to-number":{for(let o=0;o<i.length;o+=1){let a=t(o),c=typeof a==="string"&&a.trim()===""?Number.NaN:Number(a);if(a!==null&&a!==void 0&&!Number.isNaN(c))return c}return 0}case"to-string":return O(t(0));case"to-boolean":{let o=t(0);return!(o===""||o===0||o===!1||o===null||o===void 0||Number.isNaN(o))}case"to-color":case"number":case"string":case"boolean":{let o={"to-color":(a)=>B(a)!==void 0,number:(a)=>typeof a==="number",string:(a)=>typeof a==="string",boolean:(a)=>typeof a==="boolean"};for(let a=0;a<i.length;a+=1){let c=t(a);if(o[r]?.(c))return c}return i.length?t(i.length-1):null}case"typeof":{let o=t(0);if(o===null||o===void 0)return"null";return Array.isArray(o)?"array":typeof o}case"length":{let o=t(0);return typeof o==="string"||Array.isArray(o)?o.length:0}case"at":{let o=t(1);return Array.isArray(o)?o[s(0)]??null:null}case"upcase":return O(t(0)).toUpperCase();case"downcase":return O(t(0)).toLowerCase();case"coalesce":for(let o=0;o<i.length;o+=1){let a=t(o);if(a!==void 0&&a!==null)return a}return null;case"!":return!t(0);case"all":return i.every((o,a)=>Boolean(t(a)));case"any":return i.some((o,a)=>Boolean(t(a)));case"==":return t(0)===t(1);case"!=":return t(0)!==t(1);case"<":return t(0)<t(1);case"<=":return t(0)<=t(1);case">":return t(0)>t(1);case">=":return t(0)>=t(1);case"in":{let o=t(1),a=t(0);if(typeof o==="string")return o.includes(String(a));return Array.isArray(o)&&o.includes(a)}case"case":{for(let o=0;o+1<i.length;o+=2)if(t(o))return t(o+1);return t(i.length-1)}case"match":{let o=t(0);for(let a=1;a+1<i.length;a+=2){let c=i[a];if(Array.isArray(c)?c.includes(o):c===o)return t(a+1)}return t(i.length-1)}case"step":{let o=s(0),a=t(1);for(let c=2;c+1<i.length;c+=2)if(o>=Number(i[c]))a=t(c+1);else break;return a}case"interpolate":case"interpolate-hcl":case"interpolate-lab":{let o=i[0],a=s(1),c=Array.isArray(o)&&o[0]==="exponential"?Number(o[1]):1,x=[];for(let g=2;g+1<i.length;g+=2)x.push([Number(i[g]),i[g+1]??null]);let f=x[0];if(!f)return null;if(a<=f[0])return J(f[1],n);for(let g=1;g<x.length;g+=1){let[h,v]=[x[g-1],x[g]];if(h&&v&&a<v[0])return Z(J(h[1],n),J(v[1],n),le(a,h[0],v[0],c))}return J(x.at(-1)?.[1]??null,n)}case"rgb":case"rgba":return j([s(0)/255,s(1)/255,s(2)/255,i.length>3?s(3):1]);case"+":return u().reduce((o,a)=>o+a,0);case"*":return u().reduce((o,a)=>o*a,1);case"-":return i.length===1?-s(0):s(0)-s(1);case"/":return s(0)/s(1);case"%":return s(0)%s(1);case"^":return s(0)**s(1);case"min":return Math.min(...u());case"max":return Math.max(...u());case"abs":return Math.abs(s(0));case"round":return Math.sign(s(0))*Math.round(Math.abs(s(0)));case"floor":return Math.floor(s(0));case"ceil":return Math.ceil(s(0));case"sqrt":return Math.sqrt(s(0));case"ln":return Math.log(s(0));case"log10":return Math.log10(s(0));case"log2":return Math.log2(s(0));case"pi":return Math.PI;case"e":return Math.E;default:if(!ae.has(r))ae.add(r),console.warn(`isard: the style expression "${r}" is not supported`);return null}}var Ie=new Set(["==","!=","<","<=",">",">="]);function Be(e){let[n,r,...i]=e;if(typeof n!=="string"||typeof r!=="string"||i.some(Array.isArray))return!1;if(Ie.has(n))return e.length===3;if(n==="in")return e.length!==3||!Array.isArray(e[2]);return n==="!in"||n==="has"||n==="!has"}function Oe(e,n){let[r,i,...t]=e,s=V(n,i);switch(r){case"==":return s===t[0];case"!=":return s!==t[0];case"<":return s<t[0];case"<=":return s<=t[0];case">":return s>t[0];case">=":return s>=t[0];case"in":return t.includes(s);case"!in":return!t.includes(s);case"has":return s!==void 0;case"!has":return s===void 0;default:return!0}}var ue=new WeakMap;function fe(e){if(!Array.isArray(e))return()=>Boolean(e);let n=ue.get(e);if(n)return n;let r,i=e[0];if(i==="all"||i==="any"||i==="none"){let t=e.slice(1).map(fe);if(i==="all")r=(s)=>t.every((u)=>u(s));else if(i==="any")r=(s)=>t.some((u)=>u(s));else r=(s)=>!t.some((u)=>u(s))}else if(Be(e))r=(t)=>Oe(e,t);else r=(t)=>Boolean(J(e,t));return ue.set(e,r),r}function te(e,n){if(e===void 0||e===null)return!0;return fe(e)(n)}function U(e){if(e!==void 0&&ce(e))return e.property!==void 0;if(!Array.isArray(e))return!1;if(["get","has","!has","geometry-type","properties","id"].includes(e[0]))return!0;return e.some((n)=>U(n))}function me(e,n){if(e===void 0||e===null)return"";if(typeof e==="string")return e.replace(/\{([^}]+)\}/g,(i,t)=>{let s=n.properties[t];return s===void 0?"":String(s)});let r=J(e,n);if(Array.isArray(r))return r.filter((i)=>typeof i==="string").join("");return r===void 0||r===null?"":String(r)}function de(e){return/Regular|Bold|Italic|Semibold|Light|Medium|Black/.test(e)?e:`${e} Regular`}function pe(e){let n=new Set,r=(u)=>{if(!Array.isArray(u))return;let o=(a)=>Array.isArray(a)&&a[0]==="zoom";if((u[0]==="interpolate"||u[0]==="interpolate-hcl"||u[0]==="interpolate-lab")&&o(u[2]))for(let a=3;a<u.length;a+=2)n.add(Number(u[a]));else if(u[0]==="step"&&o(u[1]))for(let a=3;a<u.length;a+=2){let c=Number(u[a]);n.add(c),n.add(c-0.01)}for(let a of u)r(a)};r(e);let i=[...n].filter(Number.isFinite);if(i.length===0)i.push(0);let[t,s]=[Math.min(...i),Math.max(...i)];for(let u=Math.ceil(t);u<=Math.floor(s);u+=1)i.push(u);return[...new Set(i)].sort((u,o)=>u-o).map((u)=>[u,Number(J(e,{zoom:u,properties:{},type:"Unknown"}))])}var _=new Map,P=new Map,be=96,ge=be;function qe(e){return Math.max(16,Math.ceil(be/Math.max(1,e)))}var ne=new Set,q=new Set,F=new Map,De=64;function Ee(e,n="auto"){let r=F.get(e);if(r)return F.delete(e),F.set(e,r),r;let i=(async()=>{let s=await fetch(e,{priority:n});if(s.status===404||s.status===204)return;if(!s.ok)throw Error(`${e}: ${s.status}`);return createImageBitmap(await s.blob())})();i.catch(()=>F.delete(e)),F.set(e,i);while(F.size>De){let[t,s]=F.entries().next().value??[];if(t===void 0||s===void 0)break;F.delete(t),s.then((u)=>setTimeout(()=>u?.close(),0),()=>{return})}return i}function he(e,n="auto"){let r=P.get(e);if(r)return P.delete(e),P.set(e,r),r;let i=(async()=>{for(let t=0;;t+=1){let u=await fetch(e,{priority:n});if(u.status===404||u.status===204)return;if(u.ok)return ie(new Uint8Array(await u.arrayBuffer()));if(t>=2)throw Error(`${e}: ${u.status}`);await new Promise((o)=>setTimeout(o,400*(t+1)))}})();i.catch(()=>P.delete(e)),P.set(e,i);while(P.size>ge){let t=P.keys().next().value;if(t===void 0)break;P.delete(t)}return i}function We(e,n,r,i){let t=Math.max(0,Math.min(n-1,e.maxzoom)),s=n-t,u=2**Math.max(0,s),[o,a]=s>=0?[Math.floor(r/u),Math.floor(i/u)]:[r,i],c=e.tiles.replace("{z}",String(t)).replace("{x}",String(o)).replace("{y}",String(a)),x=1/u;return{url:c,vz:t,vx:o,vy:a,part:x,ox:r%u*x,oy:i%u*x}}var A=(e,n,r,i,t)=>{let s=e[n]?.[r];if(s===void 0)return t;let u=Array.isArray(s)&&!ee(s)?s:J(s,i);return u===void 0||u===null?t:u};function z(e,n){let r=B(e)??[0,0,0,1];return j([r[0],r[1],r[2],r[3]*n])}function ye(e,n){if(e.layout?.visibility==="none")return!1;return n>=(e.minzoom??0)&&n<(e.maxzoom??24)}function $e(e,n){if(e.patterns.has(n))return e.patterns.get(n);let r=e.sprite?.icons[n],i;if(e.sprite&&r&&r.width>0&&r.height>0)i=new OffscreenCanvas(r.width,r.height),i.getContext("2d")?.drawImage(e.sprite.image,r.x,r.y,r.width,r.height,0,0,r.width,r.height);return e.patterns.set(n,i),i}var re=new Map;function Ve(e){let n=re.get(e)?.pop()??new OffscreenCanvas(e,e),r=n.getContext("2d",{willReadFrequently:!0});if(!r)throw Error("no 2D context");return r.setTransform(1,0,0,1,0,0),r.globalAlpha=1,r.setLineDash([]),r.clearRect(0,0,e,e),{canvas:n,g:r}}function je(e){let n=re.get(e.width)??[];if(re.set(e.width,n),n.length<4)n.push(e)}function Ue(e,n,r){if(r)return Promise.resolve(e.transferToImageBitmap());return createImageBitmap(n.getImageData(0,0,e.width,e.height),{premultiplyAlpha:"none"})}async function _e(e,n,r,i,t,s,u){let o=We(e,n,r,i),a=await he(o.url,u);if(q.has(s))return;let{canvas:c,g:x}=Ve(t);try{return await He(e,o,a,c,x,n,r,i,t,s,u)}finally{je(c)}}async function He(e,n,r,i,t,s,u,o,a,c,x){let f=e.sprite,g=Math.max(0,s-1),h=a/256,v=!1,L=!1;for(let d of e.layers){if(!ye(d,g))continue;let S={zoom:g,properties:{},type:"Unknown"};if(d.type==="background"){let b=A(d,"paint","background-color",S,"#000"),l=Number(A(d,"paint","background-opacity",S,1));if(t.fillStyle=z(b,l),t.fillRect(0,0,a,a),(B(b)??[0,0,0,1])[3]*l>=1)L=!0;v=!0;continue}if(d.type==="raster"){let b=d.source===void 0?void 0:e.rasters[d.source];if(!b)continue;let l=Math.max(0,Math.min(s-(b.tileSize>=512?1:0),b.maxzoom)),m=2**(s-l),p=b.tiles.replace("{z}",String(l)).replace("{x}",String(Math.floor(u/m))).replace("{y}",String(Math.floor(o/m))),w=await Ee(p,x).catch(()=>{return});if(q.has(c))return;if(!w)continue;let[k,M]=[w.width/m,w.height/m];t.globalAlpha=Number(A(d,"paint","raster-opacity",S,1)),t.drawImage(w,u%m*k,o%m*M,k,M,0,0,a,a),t.globalAlpha=1,v=!0;continue}let N=r?.get(d["source-layer"]??"");if(!N)continue;let R=a/(N.extent*n.part),[D,T]=[n.ox*N.extent,n.oy*N.extent],I=(b,l)=>{t.beginPath();for(let m of b.geometry){for(let p=0;p+1<m.length;p+=2){let[w,k]=[((m[p]??0)-D)*R,((m[p+1]??0)-T)*R];if(p===0)t.moveTo(w,k);else t.lineTo(w,k)}if(l)t.closePath()}},C=new Map,y=(b,l,m,p)=>{let w=C.get(l);if(w===void 0)w=U(d[b]?.[l]),C.set(l,w);return A(d,b,l,w?m:S,p)};for(let b of N.features){let l=X(b,g);if(!te(d.filter,l))continue;if(d.type==="fill-extrusion"&&b.type===3)I(b,!0),t.fillStyle=z(y("paint","fill-extrusion-color",l,"#000"),Number(y("paint","fill-extrusion-opacity",l,1))),t.fill("evenodd"),v=!0;else if(d.type==="fill"&&b.type===3){let m=Number(y("paint","fill-opacity",l,1)),p=y("paint","fill-pattern",l,void 0);I(b,!0);let w=typeof p==="string"?f?.icons[p]:void 0,k=w&&$e(e,p);if(w&&k){let M=t.createPattern(k,"repeat");if(M)M.setTransform(new DOMMatrix().scale(h/(w.pixelRatio??1))),t.globalAlpha=m,t.fillStyle=M,t.fill("evenodd"),t.globalAlpha=1}else{t.fillStyle=z(y("paint","fill-color",l,"#000"),m),t.fill("evenodd");let M=y("paint","fill-outline-color",l,void 0);if(M!==void 0)t.strokeStyle=z(M,m),t.lineWidth=h,t.setLineDash([]),t.stroke()}v=!0}else if(d.type==="line"&&b.type!==1){let m=Number(y("paint","line-width",l,1));if(m<=0)continue;let p=Number(y("paint","line-gap-width",l,0));t.strokeStyle=z(y("paint","line-color",l,"#000"),Number(y("paint","line-opacity",l,1))),t.lineWidth=(p>0?p+2*m:m)*h,t.lineCap=String(y("layout","line-cap",l,"butt")),t.lineJoin=String(y("layout","line-join",l,"miter"));let w=y("paint","line-dasharray",l,void 0);t.setLineDash(Array.isArray(w)?w.map((k)=>Number(k)*m*h):[]),I(b,b.type===3),t.stroke(),v=!0}else if(d.type==="circle"&&b.type===1){let m=Number(y("paint","circle-radius",l,5))*h;t.fillStyle=z(y("paint","circle-color",l,"#000"),Number(y("paint","circle-opacity",l,1)));let p=Number(y("paint","circle-stroke-width",l,0))*h;t.strokeStyle=z(y("paint","circle-stroke-color",l,"#000"),Number(y("paint","circle-stroke-opacity",l,1))),t.lineWidth=p;for(let w of b.geometry)for(let k=0;k+1<w.length;k+=2)if(t.beginPath(),t.arc(((w[k]??0)-D)*R,((w[k+1]??0)-T)*R,m,0,2*Math.PI),t.fill(),p>0)t.stroke();v=!0}}}if(!v&&!r)return null;return Ue(i,t,L)}function Ge(e){let{layout:n={},paint:r={}}=e;return n["icon-image"]!==void 0&&r["text-halo-width"]===void 0&&r["text-halo-color"]===void 0&&n["text-offset"]===void 0&&(n["text-anchor"]??"center")==="center"}function H(e,n,r,i,t,s){let u=2**e,o=(n+t/i)/u,a=(r+s/i)/u;return[o*360-180,Math.atan(Math.sinh(Math.PI*(1-2*a)))*180/Math.PI]}async function Ye(e,n,r,i,t,s){let u=e.tiles.replace("{z}",String(n)).replace("{x}",String(r)).replace("{y}",String(i)),o=await he(u,s);if(!o||q.has(t))return[];let a=[],c=new Map,x=(f)=>{let g=c.get(f);if(g===void 0){let h=f.layout?.["text-size"]??16;g=ee(h)?pe(h):h,c.set(f,g)}return g};return e.layers.forEach((f,g)=>{if(f.type!=="symbol"||!ye(f,n)||f.layout?.["text-field"]===void 0)return;if(Ge(f))return;let h=o.get(f["source-layer"]??"");if(!h)return;let v=f.layout?.["symbol-placement"]==="line"?"line":"point";for(let L of h.features){let d=X(L,n);if(!te(f.filter,d))continue;let S=me(f.layout?.["text-field"],d).trim();if(!S)continue;let N=A(f,"layout","text-transform",d,"none");if(N==="uppercase")S=S.toUpperCase();else if(N==="lowercase")S=S.toLowerCase();let R=A(f,"layout","text-font",d,["Noto Sans Regular"]),D=de(Array.isArray(R)&&typeof R[0]==="string"?R[0]:"Noto Sans Regular"),T=A(f,"layout","text-offset",d,[0,0]),I={layer:g,text:S,font:D,size:U(f.layout?.["text-size"])?Number(A(f,"layout","text-size",d,16)):x(f),color:String(A(f,"paint","text-color",d,"#000")),haloColor:String(A(f,"paint","text-halo-color",d,"rgba(0,0,0,0)")),haloWidth:Number(A(f,"paint","text-halo-width",d,0)),anchor:String(A(f,"layout","text-anchor",d,"center")),offset:Array.isArray(T)?[Number(T[0]??0),Number(T[1]??0)]:[0,0],maxWidth:Number(A(f,"layout","text-max-width",d,10)),lineHeight:Number(A(f,"layout","text-line-height",d,1.2)),overlap:Boolean(A(f,"layout","text-allow-overlap",d,!1))};if(L.type===1)for(let C of L.geometry)for(let y=0;y+1<C.length;y+=2){let[b,l]=[C[y]??0,C[y+1]??0];if(b<0||l<0||b>=h.extent||l>=h.extent)continue;let[m,p]=H(n,r,i,h.extent,b,l);a.push({...I,lon:m,lat:p})}else{let C,y=0;for(let l of L.geometry){let m=0;for(let p=2;p+1<l.length;p+=2)m+=Math.hypot((l[p]??0)-(l[p-2]??0),(l[p+1]??0)-(l[p-1]??0));if(m>y)y=m,C=l}if(!C||y<h.extent/32)continue;let b=y/2;for(let l=2;l+1<C.length;l+=2){let[m,p,w,k]=[C[l-2]??0,C[l-1]??0,C[l]??0,C[l+1]??0],M=Math.hypot(w-m,k-p);if(M>=b&&M>0){let[Y,K]=[m+(w-m)*b/M,p+(k-p)*b/M];if(Y<0||K<0||Y>=h.extent||K>=h.extent)break;let[we,xe]=H(n,r,i,h.extent,Y,K),[ke,ve]=H(n,r,i,h.extent,w,k),[Se,Me]=H(n,r,i,h.extent,m,p);a.push({...I,lon:we,lat:xe,...v==="line"?{along:[ke-Se,ve-Me]}:{}});break}b-=M}}}}),a}var G=(e,n=[])=>self.postMessage(e,n);self.onmessage=async({data:e})=>{if(e.type==="init"){if(e.workers!==void 0)ge=qe(e.workers);_.set(e.key,{tiles:e.tiles,maxzoom:e.maxzoom,layers:e.layers,rasters:e.rasters??{},sprite:e.sprite,patterns:new Map});return}if(e.type==="drop"){_.get(e.key)?.sprite?.image.close(),_.delete(e.key);return}if(e.type==="abort"){if(ne.has(e.id))q.add(e.id);return}ne.add(e.id);let n=_.get(e.key);try{if(!n)throw Error(`no renderer ${e.key}`);if(e.type==="render"){let r=await _e(n,e.z,e.x,e.y,e.size,e.id,e.priority);if(r)G({id:e.id,image:r},[r]);else G({id:e.id,missing:!0})}else G({id:e.id,labels:await Ye(n,e.z,e.x,e.y,e.id,e.priority)})}catch(r){G({id:e.id,error:String(r)})}finally{ne.delete(e.id),q.delete(e.id)}};export{qe as cacheShare,Ue as tileImage};
2
+
3
+ //# debugId=01D6BC7A49684B7264756E2164756E21
4
+ //# sourceMappingURL=basemap.worker.js.map