isard 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -1
- package/README.md +22 -7
- package/dist/basemap/vectortiles/context.d.ts +43 -0
- package/dist/basemap/vectortiles/context.js +6 -0
- package/dist/basemap/vectortiles/context.js.map +1 -0
- package/dist/basemap/vectortiles/extrusions.d.ts +11 -0
- package/dist/basemap/vectortiles/extrusions.js +46 -0
- package/dist/basemap/vectortiles/extrusions.js.map +1 -0
- package/dist/basemap/vectortiles/labels.d.ts +22 -0
- package/dist/basemap/vectortiles/labels.js +225 -0
- package/dist/basemap/vectortiles/labels.js.map +1 -0
- package/dist/basemap/vectortiles/requests.d.ts +24 -0
- package/dist/basemap/vectortiles/requests.js +61 -0
- package/dist/basemap/vectortiles/requests.js.map +1 -0
- package/dist/basemap/vectortiles/street-wind.d.ts +19 -0
- package/dist/basemap/vectortiles/street-wind.js +133 -0
- package/dist/basemap/vectortiles/street-wind.js.map +1 -0
- package/dist/basemap/vectortiles/style.d.ts +47 -0
- package/dist/basemap/vectortiles/style.js +141 -0
- package/dist/basemap/vectortiles/style.js.map +1 -0
- package/dist/basemap/vectortiles/tiles.d.ts +34 -0
- package/dist/basemap/vectortiles/tiles.js +62 -0
- package/dist/basemap/vectortiles/tiles.js.map +1 -0
- package/dist/basemap/vectortiles/trees.d.ts +19 -0
- package/dist/basemap/vectortiles/trees.js +93 -0
- package/dist/basemap/vectortiles/trees.js.map +1 -0
- package/dist/basemap/vectortiles/vectors.d.ts +20 -0
- package/dist/basemap/vectortiles/vectors.js +167 -0
- package/dist/basemap/vectortiles/vectors.js.map +1 -0
- package/dist/basemap/vectortiles.d.ts +23 -90
- package/dist/basemap/vectortiles.js +57 -765
- package/dist/basemap/vectortiles.js.map +1 -1
- package/dist/basemap/workerpool.js +25 -6
- package/dist/basemap/workerpool.js.map +1 -1
- package/dist/basemap.worker.js +2 -2
- package/dist/basemap.worker.js.map +4 -4
- package/dist/decoder.worker.js +5 -4
- package/dist/decoder.worker.js.map +8 -6
- package/dist/engine/decoders.d.ts +4 -8
- package/dist/engine/decoders.js +29 -2
- package/dist/engine/decoders.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/isard_bg.wasm +0 -0
- package/dist/isard_core_bg.wasm +0 -0
- package/dist/layers/flow.d.ts +9 -2
- package/dist/layers/flow.js +51 -10
- package/dist/layers/flow.js.map +1 -1
- package/dist/layers/panoramas.d.ts +2 -0
- package/dist/layers/panoramas.js +27 -6
- package/dist/layers/panoramas.js.map +1 -1
- package/dist/map/workers.js +3 -0
- package/dist/map/workers.js.map +1 -1
- package/dist/util/read.d.ts +4 -0
- package/dist/util/read.js +28 -0
- package/dist/util/read.js.map +1 -0
- package/dist/wasm/isard.d.ts +18 -5
- package/dist/wasm/isard.js +25 -4
- package/dist/wasm/isard_core.d.ts +18 -5
- package/dist/wasm/isard_core.js +25 -4
- package/dist/workers/basemap/street.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,60 @@
|
|
|
3
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
4
|
versions [Semantic Versioning](https://semver.org) (before 1.0.0, a minor version may change an API).
|
|
5
5
|
|
|
6
|
+
## [1.5.0] - 2026-10-24
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Two examples of analysis, in TypeScript and React: Rising water (the Seine rising through Paris over the IGN's
|
|
11
|
+
terrain model, read as a grid and drawn by a ground shader, the floods of 2016 and 1910, the area under water and the
|
|
12
|
+
depth under the cursor) and Elevation profile (the relief along a line from Chamonix to the Aiguille du Midi, its
|
|
13
|
+
ends dragged, its chart linked to the map).
|
|
14
|
+
- The option types of `VectorTiles`' layers exported (`VectorExtrusionOptions`, `StreetWindOptions`, `StreetWindLayer`,
|
|
15
|
+
`TreesOptions`, `VectorsOptions`, `LabelsOptions`, `LoadedStyle`). The wind examples say what the particles show;
|
|
16
|
+
the examples' pages of the documentation take the page's width.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Flows lighter: the map is drawn again only when the field carried particles (none in view: the map idles, where a
|
|
21
|
+
playing layer kept it drawing every frame); only the trails of the particles that move are drawn (about 6,000 of
|
|
22
|
+
the 16,384 on a laptop's screen, fewer on a phone); `setWind` of the streets' wind sends two numbers, the engine
|
|
23
|
+
mixing the flows of an east and a north wind it holds (no field rebuilt and copied, 2 MB, at each call); a model's
|
|
24
|
+
next hour, once reached, becomes the field on the engine, only the hour after it sent; the fields copied once into
|
|
25
|
+
the engine, not three times. Below street level, the streets' wind has no particles.
|
|
26
|
+
- glTF models and trees: each instance's own matrix computed once for the instances, not at every frame; the matrices
|
|
27
|
+
of a view kept for the next pass or frame of the same view (the camera's and the sun's: computed once a frame each
|
|
28
|
+
rather than once a pass, and not at all while neither moves). The example Trees: 3.3 → 2.5 ms of main thread a
|
|
29
|
+
frame.
|
|
30
|
+
- `bun run perf`: the streets' wind, the WebGL draw calls a frame, the main thread's time a frame, and the frames a map
|
|
31
|
+
left alone draws.
|
|
32
|
+
- The code split by concern for its readers: the engine's `engine.rs` (its camera, settings and frame loop in
|
|
33
|
+
`engine/`) and `render/gl.rs` (in `gl/`), the vector tiles' `vectortiles.ts` (a file per layer in `vectortiles/`,
|
|
34
|
+
their tile requests in one class, `TileRequests`).
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- Across the antimeridian (Fiji, Chukotka), `extrusions()`, `trees()` and `labels()` ask for the tiles of both sides
|
|
39
|
+
(the window round the center going on round the world), and the streets' wind lays the buildings of the other side
|
|
40
|
+
a turn away, rather than missing them or asking for a column outside the world.
|
|
41
|
+
- A COPC file behind a server that ignores range requests and tells no length is no longer read whole for each node:
|
|
42
|
+
its body is read no further than the range asked for, and the error says the server ignores ranges.
|
|
43
|
+
- Panoramas: the images of a panorama left before they arrived are no longer downloaded and decoded; a step of the
|
|
44
|
+
walk that fails (an image not found) is told in the console, no longer an unhandled rejection.
|
|
45
|
+
- The Draco decoder runs under a Content Security Policy without `'unsafe-eval'` (its script imported from a `blob:`
|
|
46
|
+
URL; evaluated only where `blob:` is refused). The Basis transcoder (KTX2) still needs `'unsafe-eval'`, its own
|
|
47
|
+
bindings building functions from strings.
|
|
48
|
+
- `lambert93Grid` exported by `isard` itself too, as the API reference says of the `isard/geoplateforme` helpers.
|
|
49
|
+
- A basemap worker that failed to load no longer leaves the requests sent to it hanging: its tiles go to the other
|
|
50
|
+
workers, and with none left each request fails at once (the relief, the labels, `readGrids`, the streets' wind).
|
|
51
|
+
- A raster source of a style that fails for a moment (a 503 on the hillshade) fails its tile, asked for again, rather
|
|
52
|
+
than leaving the tile drawn without it for good.
|
|
53
|
+
- A streets' wind square the view left is no longer solved in its worker.
|
|
54
|
+
- The style's sprites are asked for again when one failed, rather than the icons and patterns missing for good.
|
|
55
|
+
- `vectors()`: the tiles drawn out of view after a recut (`fill-outline-color`, `fill-antialias`) are dropped. A tile
|
|
56
|
+
the engine refuses (`extrusions()`, `trees()`, `vectors()`) is reported and asked for again, no longer an unhandled
|
|
57
|
+
rejection.
|
|
58
|
+
- `vectorTiles()` on a destroyed map throws, rather than starting workers nothing stops.
|
|
59
|
+
|
|
6
60
|
## [1.4.0] - 2026-10-24
|
|
7
61
|
|
|
8
62
|
### Added
|
|
@@ -20,7 +74,7 @@ versions [Semantic Versioning](https://semver.org) (before 1.0.0, a minor versio
|
|
|
20
74
|
- A model's files read in the workers: `map.readGrids(url | bytes)` reads GRIB2 (regular latitude/longitude grids,
|
|
21
75
|
packed simply, in groups or in groups of spatial differences, NOAA's GFS as it comes, with or without a bitmap) and
|
|
22
76
|
classic NetCDF (the CF conventions: scale, offset, fill values, times "since" a date) into `Grid`s, by a module of its
|
|
23
|
-
own (`isard_grid_bg.wasm`, the crate `isard-grid`,
|
|
77
|
+
own (`isard_grid_bg.wasm`, the crate `isard-grid`, 66 KB gzipped, loaded with the first file; `gridWasmUrl`);
|
|
24
78
|
`windFields` pairs their wind's components by time into a flow layer's `fields`. The example Wind reads the GFS's
|
|
25
79
|
GRIB2 file.
|
|
26
80
|
- The wind through a city's streets: `vectorTiles().streetWind({ wind, size, cell, height })`, particles at street
|
package/README.md
CHANGED
|
@@ -45,14 +45,15 @@ General-purpose map libraries spend their time on things these maps do not need.
|
|
|
45
45
|
its woods, the sun's shadows at any hour, the night with lit windows and glowing streets; indoor data floor by floor
|
|
46
46
|
(a shopping mall, its shops raised and named).
|
|
47
47
|
- 🌬️ **Wind and currents.** Particles carried by a field of velocities (a weather model's wind, an ocean's current),
|
|
48
|
-
their trails animated on the globe and the relief, colored by speed
|
|
48
|
+
their trails animated on the globe and the relief, colored by speed; the model's hours along a clock, its own GRIB2
|
|
49
|
+
or NetCDF files read in workers, and down to a city's streets, round its buildings.
|
|
49
50
|
- 🏙️ **Buildings and 3D Tiles.** Buildings colored by a value or by a network's travel times, photomeshes and city
|
|
50
51
|
models in 3D Tiles (Draco, meshopt, KTX2).
|
|
51
52
|
- ☁️ **Point clouds.** LiDAR in COPC read by range requests (the IGN's LiDAR HD, reprojected from Lambert-93 onto the
|
|
52
53
|
relief), 3D Tiles points; colored by RGB, class, altitude or intensity, Eye-Dome Lighting, a point budget, picked.
|
|
53
54
|
- 🧊 **Your own 3D.** glTF models at positions, instanced and picked; custom layers for your WebGL code or a three.js
|
|
54
55
|
scene, in the map's frame and depth, with camera-relative matrices exact at any zoom.
|
|
55
|
-
- 🦀 **Small and exact.**
|
|
56
|
+
- 🦀 **Small and exact.** 278 KB of WebAssembly gzipped (452 KB once a 3D Tiles layer, a model or a point cloud is added), f64 on the CPU and f32 relative to the camera on the GPU
|
|
56
57
|
(centimetres at any zoom), frames on demand only, strict TypeScript types end to end.
|
|
57
58
|
|
|
58
59
|
## Quick start
|
|
@@ -61,12 +62,20 @@ General-purpose map libraries spend their time on things these maps do not need.
|
|
|
61
62
|
bun add isard # or npm install isard
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
```html
|
|
66
|
+
<div id="map" style="height: 100vh"></div>
|
|
67
|
+
```
|
|
68
|
+
|
|
64
69
|
```ts
|
|
65
70
|
import { GeoMap } from 'isard';
|
|
66
71
|
|
|
67
|
-
const map = await GeoMap.create(
|
|
68
|
-
|
|
69
|
-
map.
|
|
72
|
+
const map = await GeoMap.create(document.getElementById('map')!, { center: [2.35, 48.85], zoom: 12 });
|
|
73
|
+
|
|
74
|
+
map.addFills({
|
|
75
|
+
data: { type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[[2.33, 48.84], [2.37, 48.84], [2.37, 48.87], [2.33, 48.87], [2.33, 48.84]]] } },
|
|
76
|
+
color: '#2f6fd1',
|
|
77
|
+
opacity: 0.4,
|
|
78
|
+
});
|
|
70
79
|
```
|
|
71
80
|
|
|
72
81
|
That is the whole integration: the map fills its container, follows its size, handles mouse and touch and loads the
|
|
@@ -93,6 +102,10 @@ Live in the [documentation](https://ideal-adventure-zqn6y6p.pages.github.io/exam
|
|
|
93
102
|
<td><a href="https://ideal-adventure-zqn6y6p.pages.github.io/examples/vector"><img src="docs/public/screenshots/vector.jpg" alt="" /></a><br /><b>Vector basemaps</b><br />OpenFreeMap's styles, a restyled subset, the water drawn as geometry</td>
|
|
94
103
|
<td><a href="https://ideal-adventure-zqn6y6p.pages.github.io/examples/tiles3d"><img src="docs/public/screenshots/tiles3d.jpg" alt="" /></a><br /><b>3D Tiles</b><br />the Lille photomesh</td>
|
|
95
104
|
</tr>
|
|
105
|
+
<tr>
|
|
106
|
+
<td><a href="https://ideal-adventure-zqn6y6p.pages.github.io/examples/vector-3d"><img src="docs/public/screenshots/vector-3d.jpg" alt="" /></a><br /><b>A 3D vector map</b><br />Paris from a MapLibre style all as geometry: streets, raised buildings, trees, the sun's shadows</td>
|
|
107
|
+
<td><a href="https://ideal-adventure-zqn6y6p.pages.github.io/examples/wind"><img src="docs/public/screenshots/wind.jpg" alt="" /></a><br /><b>Wind</b><br />NOAA's GFS read from its GRIB2 file, particles carried by its wind through a day</td>
|
|
108
|
+
</tr>
|
|
96
109
|
<tr>
|
|
97
110
|
<td><a href="https://ideal-adventure-zqn6y6p.pages.github.io/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>
|
|
98
111
|
<td><a href="https://ideal-adventure-zqn6y6p.pages.github.io/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>
|
|
@@ -112,6 +125,7 @@ Live in the [documentation](https://ideal-adventure-zqn6y6p.pages.github.io/exam
|
|
|
112
125
|
| [3D Tiles](https://ideal-adventure-zqn6y6p.pages.github.io/layers/tiles3d) | `addTiles3d` | b3dm, pnts, glb, external tilesets, compressed or not |
|
|
113
126
|
| [glTF models](https://ideal-adventure-zqn6y6p.pages.github.io/layers/models) | `addModels` | a glTF model at many positions, instanced, on the relief, picked |
|
|
114
127
|
| [Point clouds](https://ideal-adventure-zqn6y6p.pages.github.io/layers/pointclouds) | `addPointCloud` | COPC (LiDAR HD) by range requests, pnts and glTF points; by class, altitude, intensity, EDL |
|
|
128
|
+
| [Flows](https://ideal-adventure-zqn6y6p.pages.github.io/layers/flows) | `addFlow`, `readGrids` | particles carried by a wind or a current, a model's hours along a clock, its GRIB2 and NetCDF files read in workers, the wind through a city's streets (`streetWind`) |
|
|
115
129
|
| [Custom layers](https://ideal-adventure-zqn6y6p.pages.github.io/layers/custom) | `addCustomLayer` | your WebGL code or a three.js scene in the map's frame, sharing its depth |
|
|
116
130
|
|
|
117
131
|
Plus markers, popups, controls, picking within a radius, `easeTo`, `flyTo`, a sky and fog for tilted views. All in
|
|
@@ -152,6 +166,7 @@ crates/tiles3d/ 3D Tiles tree, traversal and decoding
|
|
|
152
166
|
crates/pointcloud/ COPC point clouds: header, octree hierarchy, traversal, LAZ chunks tested natively
|
|
153
167
|
crates/engine/ the WebAssembly core: the engine, the ground, the layers, picking
|
|
154
168
|
crates/tess/ the tessellation module: vector tiles made ready for the GPU in the basemap workers
|
|
169
|
+
crates/grid/ the grid module: GRIB2 and NetCDF files read into grids, the wind through the streets tested natively
|
|
155
170
|
packages/isard/ the npm package isard: GeoMap, layer handles, overlays, workers
|
|
156
171
|
apps/ the demo, the viewer (local files, a bridge for other apps) and the benchmark
|
|
157
172
|
examples/ one page per example
|
|
@@ -164,8 +179,8 @@ More in [Develop and release](https://ideal-adventure-zqn6y6p.pages.github.io/gu
|
|
|
164
179
|
|
|
165
180
|
## Roadmap
|
|
166
181
|
|
|
167
|
-
The whole MapLibre style drawn as vectors, a 3D vector map to walk through, fluids on the globe (
|
|
168
|
-
|
|
182
|
+
The whole MapLibre style drawn as vectors, a 3D vector map to walk through, fluids on the globe (wakes behind the
|
|
183
|
+
buildings, more of the models' files), several projections on the globe, and smaller items: see
|
|
169
184
|
[Roadmap](https://ideal-adventure-zqn6y6p.pages.github.io/guide/roadmap).
|
|
170
185
|
|
|
171
186
|
## Licence
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the layers of a `VectorTiles` (its extrusions, streets' wind, trees, vectors and labels) use of it: built by
|
|
3
|
+
* the instance for its layers only, its own fields staying private.
|
|
4
|
+
*/
|
|
5
|
+
import type { LoadedSprite } from '../../layers/icons.ts';
|
|
6
|
+
import type { Layer, RasterLayer } from '../../layers/layers.ts';
|
|
7
|
+
import type { GeoMap } from '../../map.ts';
|
|
8
|
+
import type { BasemapResponse, BasemapWork } from '../../workers/basemap.worker.ts';
|
|
9
|
+
import type { Style, StyleLayer } from '../vectorstyle.ts';
|
|
10
|
+
import type { BasemapWorkers } from '../workerpool.ts';
|
|
11
|
+
import type { TileRequests } from './requests.ts';
|
|
12
|
+
import type { LoadedStyle } from './style.ts';
|
|
13
|
+
/** A renderer sent to the workers: its key, the source's deepest level, its style layers */
|
|
14
|
+
export interface Renderer {
|
|
15
|
+
key: string;
|
|
16
|
+
maxzoom: number;
|
|
17
|
+
layers: StyleLayer[];
|
|
18
|
+
}
|
|
19
|
+
export interface VectorTilesContext {
|
|
20
|
+
readonly map: GeoMap;
|
|
21
|
+
/** The style once loaded, with its source resolved */
|
|
22
|
+
readonly ready: Promise<LoadedStyle>;
|
|
23
|
+
/** Its raster and label layers, removed with it: a layer made is added to them */
|
|
24
|
+
readonly layers: Layer[];
|
|
25
|
+
/** Called when it is destroyed: a layer adds what stops it (its listeners, its timers) */
|
|
26
|
+
readonly cleanup: (() => void)[];
|
|
27
|
+
/** Prefix of the names of its sprites' images among the map's icons */
|
|
28
|
+
readonly iconPrefix: string;
|
|
29
|
+
/** Whether it was destroyed */
|
|
30
|
+
destroyed(): boolean;
|
|
31
|
+
/** Its first raster layer: the ground its labels follow */
|
|
32
|
+
ground(): RasterLayer | undefined;
|
|
33
|
+
/** A message to every worker of its pool */
|
|
34
|
+
broadcast(message: Parameters<BasemapWorkers['broadcast']>[0]): void;
|
|
35
|
+
/** A work asked of the worker of the vector tile `vz/vx/vy` */
|
|
36
|
+
request(vz: number, vx: number, vy: number, work: BasemapWork, signal?: AbortSignal): Promise<BasemapResponse>;
|
|
37
|
+
/** Sends a renderer of these layers to every worker (again under `again`, restyled) */
|
|
38
|
+
renderer(select: (layer: StyleLayer) => boolean, extra: StyleLayer[], again?: string): Promise<Renderer>;
|
|
39
|
+
/** The style's sprites, kept once all of them came */
|
|
40
|
+
loadSprites(sprite: Style['sprite']): Promise<LoadedSprite[]>;
|
|
41
|
+
/** Makes a layer's tiles follow the view, its `requests` aborted when it is destroyed */
|
|
42
|
+
followView(update: () => Promise<unknown>, requests: TileRequests, moving: () => unknown): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/context.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * What the layers of a `VectorTiles` (its extrusions, streets' wind, trees, vectors and labels) use of it: built by\n * the instance for its layers only, its own fields staying private.\n */\n\nimport type { LoadedSprite } from '../../layers/icons.ts';\nimport type { Layer, RasterLayer } from '../../layers/layers.ts';\nimport type { GeoMap } from '../../map.ts';\nimport type { BasemapResponse, BasemapWork } from '../../workers/basemap.worker.ts';\nimport type { Style, StyleLayer } from '../vectorstyle.ts';\nimport type { BasemapWorkers } from '../workerpool.ts';\nimport type { TileRequests } from './requests.ts';\nimport type { LoadedStyle } from './style.ts';\n\n/** A renderer sent to the workers: its key, the source's deepest level, its style layers */\nexport interface Renderer {\n key: string;\n maxzoom: number;\n layers: StyleLayer[];\n}\n\nexport interface VectorTilesContext {\n readonly map: GeoMap;\n /** The style once loaded, with its source resolved */\n readonly ready: Promise<LoadedStyle>;\n /** Its raster and label layers, removed with it: a layer made is added to them */\n readonly layers: Layer[];\n /** Called when it is destroyed: a layer adds what stops it (its listeners, its timers) */\n readonly cleanup: (() => void)[];\n /** Prefix of the names of its sprites' images among the map's icons */\n readonly iconPrefix: string;\n /** Whether it was destroyed */\n destroyed(): boolean;\n /** Its first raster layer: the ground its labels follow */\n ground(): RasterLayer | undefined;\n /** A message to every worker of its pool */\n broadcast(message: Parameters<BasemapWorkers['broadcast']>[0]): void;\n /** A work asked of the worker of the vector tile `vz/vx/vy` */\n request(vz: number, vx: number, vy: number, work: BasemapWork, signal?: AbortSignal): Promise<BasemapResponse>;\n /** Sends a renderer of these layers to every worker (again under `again`, restyled) */\n renderer(select: (layer: StyleLayer) => boolean, extra: StyleLayer[], again?: string): Promise<Renderer>;\n /** The style's sprites, kept once all of them came */\n loadSprites(sprite: Style['sprite']): Promise<LoadedSprite[]>;\n /** Makes a layer's tiles follow the view, its `requests` aborted when it is destroyed */\n followView(update: () => Promise<unknown>, requests: TileRequests, moving: () => unknown): void;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The extrusions of a vector basemap (`VectorTiles.extrusions`): its buildings raised, tile by tile around the view */
|
|
2
|
+
import type { ExtrusionLayer, ExtrusionOptions } from '../../layers/extrusions.ts';
|
|
3
|
+
import type { StyleLayer } from '../vectorstyle.ts';
|
|
4
|
+
import type { VectorTilesContext } from './context.ts';
|
|
5
|
+
/** The options of `VectorTiles.extrusions` */
|
|
6
|
+
export type VectorExtrusionOptions<P> = Omit<ExtrusionOptions<P>, 'data'> & {
|
|
7
|
+
layers?: (layer: StyleLayer) => boolean;
|
|
8
|
+
minZoom?: number;
|
|
9
|
+
maxTiles?: number;
|
|
10
|
+
};
|
|
11
|
+
export declare const extrusions: <P = Record<string, unknown>>(context: VectorTilesContext, options?: VectorExtrusionOptions<P>) => ExtrusionLayer<P>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** The extrusions of a vector basemap (`VectorTiles.extrusions`): its buildings raised, tile by tile around the view */
|
|
2
|
+
import { TileRequests } from "./requests.js";
|
|
3
|
+
import { isBuildings, isPolygonLayer } from "./style.js";
|
|
4
|
+
import { MAX_TILES, nearPriority, STREET_ZOOM, tileKey, tilesAround } from "./tiles.js";
|
|
5
|
+
export const extrusions = (context, options = {}) => {
|
|
6
|
+
const { map } = context;
|
|
7
|
+
const { layers = isBuildings, minZoom = STREET_ZOOM, maxTiles = MAX_TILES, ...style } = options;
|
|
8
|
+
const layer = map.addExtrusions(style);
|
|
9
|
+
const renderer = context.renderer(isPolygonLayer(layers), []);
|
|
10
|
+
renderer.catch(() => undefined);
|
|
11
|
+
context.layers.push(layer);
|
|
12
|
+
/** The tiles held or asked for, by key */
|
|
13
|
+
const requests = new TileRequests();
|
|
14
|
+
const update = async () => {
|
|
15
|
+
if (context.destroyed())
|
|
16
|
+
return;
|
|
17
|
+
const { key, maxzoom } = await renderer;
|
|
18
|
+
const camera = map.getCamera();
|
|
19
|
+
const z = maxzoom;
|
|
20
|
+
const wanted = camera.zoom < minZoom ? [] : tilesAround(map, camera, z, maxTiles);
|
|
21
|
+
const keys = new Set(wanted.map(([x, y]) => tileKey(z, x, y)));
|
|
22
|
+
// Out of view: dropped, or no longer asked for
|
|
23
|
+
requests.keys().forEach((k) => {
|
|
24
|
+
if (keys.has(k))
|
|
25
|
+
return;
|
|
26
|
+
requests.drop(k);
|
|
27
|
+
layer.removeTile(k);
|
|
28
|
+
});
|
|
29
|
+
wanted.forEach(([x, y], index) => {
|
|
30
|
+
const k = tileKey(z, x, y);
|
|
31
|
+
if (requests.has(k))
|
|
32
|
+
return;
|
|
33
|
+
const work = { type: 'polygons', key, z, x, y, priority: nearPriority(index) };
|
|
34
|
+
requests.ask(k, (signal) => context.request(z, x, y, work, signal), (r) => {
|
|
35
|
+
if (!('polygons' in r))
|
|
36
|
+
return;
|
|
37
|
+
const tile = r.polygons;
|
|
38
|
+
// The features' properties as the style reads them
|
|
39
|
+
layer.setTile(k, { arrays: tile.arrays, properties: tile.properties });
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
context.followView(update, requests, update);
|
|
44
|
+
return layer;
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=extrusions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extrusions.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/extrusions.ts"],"names":[],"mappings":"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","sourcesContent":["/** The extrusions of a vector basemap (`VectorTiles.extrusions`): its buildings raised, tile by tile around the view */\n\nimport type { ExtrusionLayer, ExtrusionOptions } from '../../layers/extrusions.ts';\nimport type { BasemapWork, TilePolygons } from '../../workers/basemap.worker.ts';\nimport type { StyleLayer } from '../vectorstyle.ts';\nimport type { VectorTilesContext } from './context.ts';\nimport { TileRequests } from './requests.ts';\nimport { isBuildings, isPolygonLayer } from './style.ts';\nimport { MAX_TILES, nearPriority, STREET_ZOOM, tileKey, tilesAround } from './tiles.ts';\n\n/** The options of `VectorTiles.extrusions` */\nexport type VectorExtrusionOptions<P> = Omit<ExtrusionOptions<P>, 'data'> & {\n layers?: (layer: StyleLayer) => boolean;\n minZoom?: number;\n maxTiles?: number;\n};\n\nexport const extrusions = <P = Record<string, unknown>>(\n context: VectorTilesContext,\n options: VectorExtrusionOptions<P> = {},\n): ExtrusionLayer<P> => {\n const { map } = context;\n const { layers = isBuildings, minZoom = STREET_ZOOM, maxTiles = MAX_TILES, ...style } = options;\n const layer = map.addExtrusions<P>(style);\n const renderer = context.renderer(isPolygonLayer(layers), []);\n\n renderer.catch(() => undefined);\n context.layers.push(layer);\n\n /** The tiles held or asked for, by key */\n const requests = new TileRequests();\n const update = async () => {\n if (context.destroyed()) return;\n\n const { key, maxzoom } = await renderer;\n const camera = map.getCamera();\n const z = maxzoom;\n const wanted = camera.zoom < minZoom ? [] : tilesAround(map, camera, z, maxTiles);\n const keys = new Set(wanted.map(([x, y]) => tileKey(z, x, y)));\n\n // Out of view: dropped, or no longer asked for\n requests.keys().forEach((k) => {\n if (keys.has(k)) return;\n requests.drop(k);\n layer.removeTile(k);\n });\n wanted.forEach(([x, y], index) => {\n const k = tileKey(z, x, y);\n\n if (requests.has(k)) return;\n\n const work: BasemapWork = { type: 'polygons', key, z, x, y, priority: nearPriority(index) };\n\n requests.ask(\n k,\n (signal) => context.request(z, x, y, work, signal),\n (r) => {\n if (!('polygons' in r)) return;\n\n const tile: TilePolygons = r.polygons;\n\n // The features' properties as the style reads them\n layer.setTile(k, { arrays: tile.arrays, properties: tile.properties as P[] });\n },\n );\n });\n };\n context.followView(update, requests, update);\n\n return layer;\n};\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The labels of a vector basemap (`VectorTiles.labels`): the label candidates the workers list for each vector tile in
|
|
3
|
+
* view, shaped in the page once per tile, placed by the engine in a symbol layer of their own.
|
|
4
|
+
*/
|
|
5
|
+
import type { Feature, Size } from '../../layers/features.ts';
|
|
6
|
+
import type { SymbolLayer } from '../../layers/symbols.ts';
|
|
7
|
+
import type { Json, LabelCandidate, StyleLayer } from '../vectorstyle.ts';
|
|
8
|
+
import type { VectorTilesContext } from './context.ts';
|
|
9
|
+
/** Text or icon size of a style (a number, legacy stops, or the stops of a zoom expression made by the worker) as a
|
|
10
|
+
* size for the symbol layer */
|
|
11
|
+
export declare const size: (value: Json, fallback?: number) => Size;
|
|
12
|
+
/** The labels of a vector tile: features by style layer (its index in the style), the higher layers first */
|
|
13
|
+
type TileLabels = [layer: number, features: Feature<LabelCandidate>[]][];
|
|
14
|
+
/** A tile's label candidates as features, by style layer */
|
|
15
|
+
export declare const tileLabels: (candidates: readonly LabelCandidate[]) => TileLabels;
|
|
16
|
+
/** The options of `VectorTiles.labels` */
|
|
17
|
+
export interface LabelsOptions {
|
|
18
|
+
layers?: (layer: StyleLayer) => boolean;
|
|
19
|
+
maxTiles?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const labels: (context: VectorTilesContext, options?: LabelsOptions) => SymbolLayer<LabelCandidate>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The labels of a vector basemap (`VectorTiles.labels`): the label candidates the workers list for each vector tile in
|
|
3
|
+
* view, shaped in the page once per tile, placed by the engine in a symbol layer of their own.
|
|
4
|
+
*/
|
|
5
|
+
import { readPixels } from "../../layers/icons.js";
|
|
6
|
+
import { isRecord } from "../../util/guards.js";
|
|
7
|
+
import { Lru } from "../../util/lru.js";
|
|
8
|
+
import { styleFonts } from "./style.js";
|
|
9
|
+
import { MAX_TILES, nearPriority, parseTileKey, tileKey, tilesAround, tileTriples } from "./tiles.js";
|
|
10
|
+
/** Pause before the labels of tiles that failed are asked for again, when no move brings an update (ms) */
|
|
11
|
+
const RETRY_LABELS_MS = 3000;
|
|
12
|
+
/** Milliseconds between two updates of the labels while the camera moves */
|
|
13
|
+
const LABELS_MOVE_REFRESH_MS = 250;
|
|
14
|
+
/** Vector tiles' labels kept, shaped, the least recently used dropped past it */
|
|
15
|
+
const CACHED_TILES = 256;
|
|
16
|
+
/** Text or icon size of a style (a number, legacy stops, or the stops of a zoom expression made by the worker) as a
|
|
17
|
+
* size for the symbol layer */
|
|
18
|
+
export const size = (value, fallback = 16) => {
|
|
19
|
+
const stop = (s) => {
|
|
20
|
+
const [z, v] = Array.isArray(s) ? s : [];
|
|
21
|
+
return [Number(z), Number(v)];
|
|
22
|
+
};
|
|
23
|
+
if (typeof value === 'number')
|
|
24
|
+
return value;
|
|
25
|
+
if (Array.isArray(value) && value.length > 0 && value.every(Array.isArray))
|
|
26
|
+
return value.map(stop);
|
|
27
|
+
if (isRecord(value) && Array.isArray(value.stops))
|
|
28
|
+
return value.stops.map(stop);
|
|
29
|
+
return fallback;
|
|
30
|
+
};
|
|
31
|
+
/** The anchors of the symbol layer */
|
|
32
|
+
const ANCHORS = new Set([
|
|
33
|
+
'center',
|
|
34
|
+
'top',
|
|
35
|
+
'bottom',
|
|
36
|
+
'left',
|
|
37
|
+
'right',
|
|
38
|
+
'top-left',
|
|
39
|
+
'top-right',
|
|
40
|
+
'bottom-left',
|
|
41
|
+
'bottom-right',
|
|
42
|
+
]);
|
|
43
|
+
const isAnchor = (value) => value !== undefined && ANCHORS.has(value);
|
|
44
|
+
/** A style's anchor (`text-anchor`, `icon-anchor`) for the symbol layer: `center` if it is none */
|
|
45
|
+
const anchor = (value) => (isAnchor(value) ? value : 'center');
|
|
46
|
+
/** A tile's label candidates as features, by style layer */
|
|
47
|
+
export const tileLabels = (candidates) => {
|
|
48
|
+
const byLayer = new Map();
|
|
49
|
+
candidates.forEach((p) => {
|
|
50
|
+
const feature = {
|
|
51
|
+
type: 'Feature',
|
|
52
|
+
properties: p,
|
|
53
|
+
geometry: p.along
|
|
54
|
+
? {
|
|
55
|
+
type: 'LineString',
|
|
56
|
+
coordinates: [
|
|
57
|
+
[p.lon - p.along[0] / 2, p.lat - p.along[1] / 2],
|
|
58
|
+
[p.lon + p.along[0] / 2, p.lat + p.along[1] / 2],
|
|
59
|
+
],
|
|
60
|
+
}
|
|
61
|
+
: { type: 'Point', coordinates: [p.lon, p.lat] },
|
|
62
|
+
};
|
|
63
|
+
const list = byLayer.get(p.layer);
|
|
64
|
+
if (list)
|
|
65
|
+
list.push(feature);
|
|
66
|
+
else
|
|
67
|
+
byLayer.set(p.layer, [feature]);
|
|
68
|
+
});
|
|
69
|
+
return [...byLayer].sort((a, b) => b[0] - a[0]);
|
|
70
|
+
};
|
|
71
|
+
/** The key of a vector tile's labels: `z/x/y`, then `@zoom` when they are those of a deeper zoom than the tile's */
|
|
72
|
+
const labelsKey = (z, x, y, zoom) => `${tileKey(z, x, y)}${zoom > z ? `@${zoom}` : ''}`;
|
|
73
|
+
export const labels = (context, options = {}) => {
|
|
74
|
+
const { map } = context;
|
|
75
|
+
const { layers = () => true, maxTiles = MAX_TILES } = options;
|
|
76
|
+
const symbol = map.addSymbols({
|
|
77
|
+
data: [],
|
|
78
|
+
text: (p) => p.text,
|
|
79
|
+
font: (p) => p.font,
|
|
80
|
+
size: (p) => size(p.size),
|
|
81
|
+
color: (p) => p.color,
|
|
82
|
+
haloColor: (p) => p.haloColor,
|
|
83
|
+
haloWidth: (p) => p.haloWidth,
|
|
84
|
+
anchor: (p) => anchor(p.anchor),
|
|
85
|
+
offset: (p) => p.offset,
|
|
86
|
+
maxWidth: (p) => p.maxWidth,
|
|
87
|
+
lineHeight: (p) => p.lineHeight,
|
|
88
|
+
overlap: (p) => p.overlap,
|
|
89
|
+
placement: (p) => (p.textAlong ? 'line' : 'point'),
|
|
90
|
+
textOptional: (p) => p.textOptional === true,
|
|
91
|
+
icon: (p) => (p.icon ? context.iconPrefix + p.icon.image : ''),
|
|
92
|
+
iconSize: (p) => size(p.icon?.size ?? 1, 1),
|
|
93
|
+
iconAnchor: (p) => anchor(p.icon?.anchor),
|
|
94
|
+
iconOffset: (p) => p.icon?.offset ?? [0, 0],
|
|
95
|
+
iconRotate: (p) => p.icon?.rotate ?? 0,
|
|
96
|
+
iconRotationAlignment: (p) => p.icon?.alignment ?? 'viewport',
|
|
97
|
+
iconOverlap: (p) => p.icon?.overlap ?? false,
|
|
98
|
+
iconOptional: (p) => p.icon?.optional ?? false,
|
|
99
|
+
iconTextFit: (p) => p.icon?.fit ?? 'none',
|
|
100
|
+
iconTextFitPadding: (p) => p.icon?.padding ?? [0, 0, 0, 0],
|
|
101
|
+
iconColor: (p) => p.icon?.color ?? '#000',
|
|
102
|
+
iconOpacity: (p) => p.icon?.opacity ?? 1,
|
|
103
|
+
});
|
|
104
|
+
map.keepOnTop(symbol);
|
|
105
|
+
context.layers.push(symbol);
|
|
106
|
+
const renderer = context.ready.then(async ({ style }) => {
|
|
107
|
+
// The style's fonts from its own glyphs
|
|
108
|
+
if (style.glyphs)
|
|
109
|
+
map.glyphs.register(style.glyphs, styleFonts(style));
|
|
110
|
+
// Its icons from its sprites, before the first labels
|
|
111
|
+
const icons = style.layers.some((l) => l.type === 'symbol' && l.layout?.['icon-image'] !== undefined);
|
|
112
|
+
if (icons && style.sprite) {
|
|
113
|
+
(await context.loadSprites(style.sprite)).forEach((sprite) => {
|
|
114
|
+
if (!context.destroyed())
|
|
115
|
+
map.icons.addSprite(context.iconPrefix, sprite.icons, () => readPixels(sprite.image));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return context.renderer((l) => l.type === 'symbol' && layers(l), []);
|
|
119
|
+
});
|
|
120
|
+
renderer.catch(() => undefined);
|
|
121
|
+
/** The labels of the tiles, as features by style layer, the higher layers first: each list is given to the symbol
|
|
122
|
+
* layer as a group, shaped once while the tile is kept */
|
|
123
|
+
const tiles = new Lru(CACHED_TILES);
|
|
124
|
+
let generation = 0;
|
|
125
|
+
/** The tiles whose labels are shown */
|
|
126
|
+
let shown = '';
|
|
127
|
+
/** The update asked again after tiles failed */
|
|
128
|
+
let retry = 0;
|
|
129
|
+
const update = async () => {
|
|
130
|
+
if (context.destroyed())
|
|
131
|
+
return;
|
|
132
|
+
const { key, maxzoom } = await renderer;
|
|
133
|
+
const run = ++generation;
|
|
134
|
+
const camera = map.getCamera();
|
|
135
|
+
const zoom = Math.max(0, Math.min(Math.floor(camera.zoom), 24));
|
|
136
|
+
const ground = context.ground();
|
|
137
|
+
const groundTiles = ground && map.hasLayer(ground) ? tileTriples(map.engine.wantedTiles(ground.id)) : [];
|
|
138
|
+
const keys = [];
|
|
139
|
+
if (groundTiles.length > 0) {
|
|
140
|
+
// The ground's tiles, the nearest first: a tile so many levels coarser than the finest in view has its
|
|
141
|
+
// labels of a zoom as many under the view's, past the source's deepest level those of that level at the zoom
|
|
142
|
+
const top = groundTiles.reduce((t, [z]) => Math.max(t, z), 0);
|
|
143
|
+
groundTiles.forEach(([z, x, y]) => {
|
|
144
|
+
if (keys.length >= maxTiles)
|
|
145
|
+
return;
|
|
146
|
+
const at = Math.max(0, zoom - (top - z));
|
|
147
|
+
const tz = Math.min(at, maxzoom, z);
|
|
148
|
+
const shift = 2 ** (z - tz);
|
|
149
|
+
const key = labelsKey(tz, Math.floor(x / shift), Math.floor(y / shift), at);
|
|
150
|
+
if (!keys.includes(key))
|
|
151
|
+
keys.push(key);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const z = Math.min(zoom, maxzoom);
|
|
156
|
+
const wanted = tilesAround(map, camera, z, maxTiles);
|
|
157
|
+
// Past the source's deepest level, its tiles' labels at the zoom of the view (the layers of the deeper zooms)
|
|
158
|
+
keys.push(...wanted.map(([x, y]) => labelsKey(z, x, y, zoom)));
|
|
159
|
+
}
|
|
160
|
+
// The same tiles as the labels shown: nothing to do
|
|
161
|
+
const set = keys.join(' ');
|
|
162
|
+
if (set === shown)
|
|
163
|
+
return;
|
|
164
|
+
const lists = await Promise.all(keys.map((k, index) => {
|
|
165
|
+
const cached = tiles.get(k);
|
|
166
|
+
if (cached)
|
|
167
|
+
return cached;
|
|
168
|
+
const [tile = '', deeper] = k.split('@');
|
|
169
|
+
const [tz, x, y] = parseTileKey(tile);
|
|
170
|
+
// The nearest tiles first: the others wait for the raster tiles
|
|
171
|
+
const work = {
|
|
172
|
+
type: 'labels',
|
|
173
|
+
key,
|
|
174
|
+
z: tz,
|
|
175
|
+
x,
|
|
176
|
+
y,
|
|
177
|
+
priority: nearPriority(index),
|
|
178
|
+
...(deeper === undefined ? {} : { zoom: Number(deeper) }),
|
|
179
|
+
};
|
|
180
|
+
const p = context.request(tz, x, y, work).then((r) => tileLabels('labels' in r ? r.labels : []), () => {
|
|
181
|
+
// A network error is not kept: the tile is asked for again on the next update
|
|
182
|
+
tiles.delete(k);
|
|
183
|
+
return [];
|
|
184
|
+
});
|
|
185
|
+
// A bounded cache of tiles' labels, the least recently used dropped
|
|
186
|
+
tiles.set(k, p);
|
|
187
|
+
return p;
|
|
188
|
+
}));
|
|
189
|
+
if (run !== generation || context.destroyed())
|
|
190
|
+
return;
|
|
191
|
+
// A tile that failed is missing: the next update asks for it again, after a pause if no move brings one
|
|
192
|
+
shown = keys.every((k) => tiles.has(k)) ? set : '';
|
|
193
|
+
if (shown === '' && !retry) {
|
|
194
|
+
retry = window.setTimeout(() => {
|
|
195
|
+
retry = 0;
|
|
196
|
+
refresh();
|
|
197
|
+
}, RETRY_LABELS_MS);
|
|
198
|
+
}
|
|
199
|
+
// The labels of the higher style layers first: they win the collisions; for each style layer, the tiles nearest
|
|
200
|
+
// to the center first
|
|
201
|
+
const layerIds = [...new Set(lists.flatMap((list) => list.map(([layer]) => layer)))].sort((a, b) => b - a);
|
|
202
|
+
const groups = layerIds.flatMap((layer) => lists.map((list) => list.find(([l]) => l === layer)?.[1]).filter((group) => group !== undefined));
|
|
203
|
+
symbol.setGroups(groups);
|
|
204
|
+
};
|
|
205
|
+
// The style failed to load: reported by `ready`
|
|
206
|
+
const refresh = () => {
|
|
207
|
+
update().catch(() => undefined);
|
|
208
|
+
};
|
|
209
|
+
let timer = 0;
|
|
210
|
+
const schedule = () => {
|
|
211
|
+
if (timer)
|
|
212
|
+
return;
|
|
213
|
+
timer = window.setTimeout(() => {
|
|
214
|
+
timer = 0;
|
|
215
|
+
refresh();
|
|
216
|
+
}, LABELS_MOVE_REFRESH_MS);
|
|
217
|
+
};
|
|
218
|
+
context.cleanup.push(map.on('move', schedule), map.on('moveend', refresh), () => {
|
|
219
|
+
clearTimeout(timer);
|
|
220
|
+
clearTimeout(retry);
|
|
221
|
+
});
|
|
222
|
+
refresh();
|
|
223
|
+
return symbol;
|
|
224
|
+
};
|
|
225
|
+
//# sourceMappingURL=labels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/labels.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The labels of a vector basemap (`VectorTiles.labels`): the label candidates the workers list for each vector tile in\n * view, shaped in the page once per tile, placed by the engine in a symbol layer of their own.\n */\n\nimport type { Feature, Size } from '../../layers/features.ts';\nimport { readPixels } from '../../layers/icons.ts';\nimport type { SymbolLayer } from '../../layers/symbols.ts';\nimport type { Anchor } from '../../ui/overlay.ts';\nimport { isRecord } from '../../util/guards.ts';\nimport { Lru } from '../../util/lru.ts';\nimport type { BasemapWork } from '../../workers/basemap.worker.ts';\nimport type { Json, LabelCandidate, StyleLayer } from '../vectorstyle.ts';\nimport type { VectorTilesContext } from './context.ts';\nimport { styleFonts } from './style.ts';\nimport { MAX_TILES, nearPriority, parseTileKey, tileKey, tilesAround, tileTriples } from './tiles.ts';\n\n/** Pause before the labels of tiles that failed are asked for again, when no move brings an update (ms) */\nconst RETRY_LABELS_MS = 3000;\n\n/** Milliseconds between two updates of the labels while the camera moves */\nconst LABELS_MOVE_REFRESH_MS = 250;\n\n/** Vector tiles' labels kept, shaped, the least recently used dropped past it */\nconst CACHED_TILES = 256;\n\n/** Text or icon size of a style (a number, legacy stops, or the stops of a zoom expression made by the worker) as a\n * size for the symbol layer */\nexport const size = (value: Json, fallback = 16): Size => {\n const stop = (s: Json): [number, number] => {\n const [z, v] = Array.isArray(s) ? s : [];\n\n return [Number(z), Number(v)];\n };\n\n if (typeof value === 'number') return value;\n if (Array.isArray(value) && value.length > 0 && value.every(Array.isArray)) return value.map(stop);\n if (isRecord(value) && Array.isArray(value.stops)) return value.stops.map(stop);\n\n return fallback;\n};\n\n/** The anchors of the symbol layer */\nconst ANCHORS: ReadonlySet<string> = new Set<Anchor>([\n 'center',\n 'top',\n 'bottom',\n 'left',\n 'right',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n]);\n\nconst isAnchor = (value: string | undefined): value is Anchor => value !== undefined && ANCHORS.has(value);\n\n/** A style's anchor (`text-anchor`, `icon-anchor`) for the symbol layer: `center` if it is none */\nconst anchor = (value: string | undefined): Anchor => (isAnchor(value) ? value : 'center');\n\n/** The labels of a vector tile: features by style layer (its index in the style), the higher layers first */\ntype TileLabels = [layer: number, features: Feature<LabelCandidate>[]][];\n\n/** A tile's label candidates as features, by style layer */\nexport const tileLabels = (candidates: readonly LabelCandidate[]): TileLabels => {\n const byLayer = new Map<number, Feature<LabelCandidate>[]>();\n\n candidates.forEach((p) => {\n const feature: Feature<LabelCandidate> = {\n type: 'Feature',\n properties: p,\n geometry: p.along\n ? {\n type: 'LineString',\n coordinates: [\n [p.lon - p.along[0] / 2, p.lat - p.along[1] / 2],\n [p.lon + p.along[0] / 2, p.lat + p.along[1] / 2],\n ],\n }\n : { type: 'Point', coordinates: [p.lon, p.lat] },\n };\n const list = byLayer.get(p.layer);\n\n if (list) list.push(feature);\n else byLayer.set(p.layer, [feature]);\n });\n\n return [...byLayer].sort((a, b) => b[0] - a[0]);\n};\n\n/** The key of a vector tile's labels: `z/x/y`, then `@zoom` when they are those of a deeper zoom than the tile's */\nconst labelsKey = (z: number, x: number, y: number, zoom: number): string =>\n `${tileKey(z, x, y)}${zoom > z ? `@${zoom}` : ''}`;\n\n/** The options of `VectorTiles.labels` */\nexport interface LabelsOptions {\n layers?: (layer: StyleLayer) => boolean;\n maxTiles?: number;\n}\n\nexport const labels = (context: VectorTilesContext, options: LabelsOptions = {}): SymbolLayer<LabelCandidate> => {\n const { map } = context;\n const { layers = () => true, maxTiles = MAX_TILES } = options;\n const symbol = map.addSymbols<LabelCandidate>({\n data: [],\n text: (p) => p.text,\n font: (p) => p.font,\n size: (p) => size(p.size),\n color: (p) => p.color,\n haloColor: (p) => p.haloColor,\n haloWidth: (p) => p.haloWidth,\n anchor: (p) => anchor(p.anchor),\n offset: (p) => p.offset,\n maxWidth: (p) => p.maxWidth,\n lineHeight: (p) => p.lineHeight,\n overlap: (p) => p.overlap,\n placement: (p) => (p.textAlong ? 'line' : 'point'),\n textOptional: (p) => p.textOptional === true,\n icon: (p) => (p.icon ? context.iconPrefix + p.icon.image : ''),\n iconSize: (p) => size(p.icon?.size ?? 1, 1),\n iconAnchor: (p) => anchor(p.icon?.anchor),\n iconOffset: (p) => p.icon?.offset ?? [0, 0],\n iconRotate: (p) => p.icon?.rotate ?? 0,\n iconRotationAlignment: (p) => p.icon?.alignment ?? 'viewport',\n iconOverlap: (p) => p.icon?.overlap ?? false,\n iconOptional: (p) => p.icon?.optional ?? false,\n iconTextFit: (p) => p.icon?.fit ?? 'none',\n iconTextFitPadding: (p) => p.icon?.padding ?? [0, 0, 0, 0],\n iconColor: (p) => p.icon?.color ?? '#000',\n iconOpacity: (p) => p.icon?.opacity ?? 1,\n });\n\n map.keepOnTop(symbol);\n context.layers.push(symbol);\n\n const renderer = context.ready.then(async ({ style }) => {\n // The style's fonts from its own glyphs\n if (style.glyphs) map.glyphs.register(style.glyphs, styleFonts(style));\n\n // Its icons from its sprites, before the first labels\n const icons = style.layers.some((l) => l.type === 'symbol' && l.layout?.['icon-image'] !== undefined);\n\n if (icons && style.sprite) {\n (await context.loadSprites(style.sprite)).forEach((sprite) => {\n if (!context.destroyed()) map.icons.addSprite(context.iconPrefix, sprite.icons, () => readPixels(sprite.image));\n });\n }\n\n return context.renderer((l) => l.type === 'symbol' && layers(l), []);\n });\n\n renderer.catch(() => undefined);\n\n /** The labels of the tiles, as features by style layer, the higher layers first: each list is given to the symbol\n * layer as a group, shaped once while the tile is kept */\n const tiles = new Lru<string, Promise<TileLabels>>(CACHED_TILES);\n let generation = 0;\n /** The tiles whose labels are shown */\n let shown = '';\n /** The update asked again after tiles failed */\n let retry = 0;\n const update = async () => {\n if (context.destroyed()) return;\n\n const { key, maxzoom } = await renderer;\n const run = ++generation;\n const camera = map.getCamera();\n const zoom = Math.max(0, Math.min(Math.floor(camera.zoom), 24));\n const ground = context.ground();\n const groundTiles = ground && map.hasLayer(ground) ? tileTriples(map.engine.wantedTiles(ground.id)) : [];\n const keys: string[] = [];\n\n if (groundTiles.length > 0) {\n // The ground's tiles, the nearest first: a tile so many levels coarser than the finest in view has its\n // labels of a zoom as many under the view's, past the source's deepest level those of that level at the zoom\n const top = groundTiles.reduce((t, [z]) => Math.max(t, z), 0);\n\n groundTiles.forEach(([z, x, y]) => {\n if (keys.length >= maxTiles) return;\n\n const at = Math.max(0, zoom - (top - z));\n const tz = Math.min(at, maxzoom, z);\n const shift = 2 ** (z - tz);\n const key = labelsKey(tz, Math.floor(x / shift), Math.floor(y / shift), at);\n\n if (!keys.includes(key)) keys.push(key);\n });\n } else {\n const z = Math.min(zoom, maxzoom);\n const wanted = tilesAround(map, camera, z, maxTiles);\n\n // Past the source's deepest level, its tiles' labels at the zoom of the view (the layers of the deeper zooms)\n keys.push(...wanted.map(([x, y]) => labelsKey(z, x, y, zoom)));\n }\n\n // The same tiles as the labels shown: nothing to do\n const set = keys.join(' ');\n\n if (set === shown) return;\n\n const lists = await Promise.all(\n keys.map((k, index) => {\n const cached = tiles.get(k);\n\n if (cached) return cached;\n\n const [tile = '', deeper] = k.split('@');\n const [tz, x, y] = parseTileKey(tile);\n // The nearest tiles first: the others wait for the raster tiles\n const work: BasemapWork = {\n type: 'labels',\n key,\n z: tz,\n x,\n y,\n priority: nearPriority(index),\n ...(deeper === undefined ? {} : { zoom: Number(deeper) }),\n };\n const p = context.request(tz, x, y, work).then(\n (r) => tileLabels('labels' in r ? r.labels : []),\n () => {\n // A network error is not kept: the tile is asked for again on the next update\n tiles.delete(k);\n\n return [];\n },\n );\n\n // A bounded cache of tiles' labels, the least recently used dropped\n tiles.set(k, p);\n\n return p;\n }),\n );\n\n if (run !== generation || context.destroyed()) return;\n // A tile that failed is missing: the next update asks for it again, after a pause if no move brings one\n shown = keys.every((k) => tiles.has(k)) ? set : '';\n if (shown === '' && !retry) {\n retry = window.setTimeout(() => {\n retry = 0;\n refresh();\n }, RETRY_LABELS_MS);\n }\n\n // The labels of the higher style layers first: they win the collisions; for each style layer, the tiles nearest\n // to the center first\n const layerIds = [...new Set(lists.flatMap((list) => list.map(([layer]) => layer)))].sort((a, b) => b - a);\n const groups = layerIds.flatMap((layer) =>\n lists.map((list) => list.find(([l]) => l === layer)?.[1]).filter((group) => group !== undefined),\n );\n\n symbol.setGroups(groups);\n };\n // The style failed to load: reported by `ready`\n const refresh = () => {\n update().catch(() => undefined);\n };\n let timer = 0;\n const schedule = () => {\n if (timer) return;\n timer = window.setTimeout(() => {\n timer = 0;\n refresh();\n }, LABELS_MOVE_REFRESH_MS);\n };\n\n context.cleanup.push(map.on('move', schedule), map.on('moveend', refresh), () => {\n clearTimeout(timer);\n clearTimeout(retry);\n });\n refresh();\n\n return symbol;\n};\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The requests of a layer whose tiles follow the view (`VectorTiles`' extrusions, streets' wind, trees, vectors): the
|
|
3
|
+
* tiles held or asked for, by key, each with the controller that aborts its request.
|
|
4
|
+
*/
|
|
5
|
+
export declare class TileRequests {
|
|
6
|
+
private readonly held;
|
|
7
|
+
/** Whether the tile is held or asked for */
|
|
8
|
+
has(key: string): boolean;
|
|
9
|
+
/** The keys of the tiles held or asked for (a copy: the tiles can be dropped while it is gone through) */
|
|
10
|
+
keys(): string[];
|
|
11
|
+
/** Holds a new request under `key` (the one it held before is forgotten, not aborted); returns its signal */
|
|
12
|
+
start(key: string): AbortSignal;
|
|
13
|
+
/**
|
|
14
|
+
* Asks for a tile (`request`) and does with its answer what `use` does, unless the request was aborted meanwhile. On
|
|
15
|
+
* a failure of either, the tile is forgotten (the next update asks for it again); an error of `use` (the engine
|
|
16
|
+
* refusing the tile) is reported, never left unhandled
|
|
17
|
+
*/
|
|
18
|
+
ask<T>(key: string, request: (signal: AbortSignal) => Promise<T>, use: (answer: T) => unknown): Promise<void>;
|
|
19
|
+
/** Aborts the tile's request and forgets it */
|
|
20
|
+
drop(key: string): void;
|
|
21
|
+
/** Aborts every request and forgets every tile */
|
|
22
|
+
abortAll(): void;
|
|
23
|
+
private open;
|
|
24
|
+
}
|