isard 1.4.0 → 1.6.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 +220 -2
- package/README.md +23 -7
- package/dist/basemap/furniture.d.ts +8 -0
- package/dist/basemap/furniture.js +26 -0
- package/dist/basemap/furniture.js.map +1 -0
- package/dist/basemap/trees.d.ts +35 -8
- package/dist/basemap/trees.js +105 -52
- package/dist/basemap/trees.js.map +1 -1
- package/dist/basemap/vectorstyle.d.ts +9 -3
- package/dist/basemap/vectorstyle.js +5 -6
- package/dist/basemap/vectorstyle.js.map +1 -1
- package/dist/basemap/vectortiles/context.d.ts +46 -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 +15 -0
- package/dist/basemap/vectortiles/extrusions.js +61 -0
- package/dist/basemap/vectortiles/extrusions.js.map +1 -0
- package/dist/basemap/vectortiles/labels.d.ts +25 -0
- package/dist/basemap/vectortiles/labels.js +250 -0
- package/dist/basemap/vectortiles/labels.js.map +1 -0
- package/dist/basemap/vectortiles/lamps.d.ts +23 -0
- package/dist/basemap/vectortiles/lamps.js +90 -0
- package/dist/basemap/vectortiles/lamps.js.map +1 -0
- package/dist/basemap/vectortiles/requests.d.ts +28 -0
- package/dist/basemap/vectortiles/requests.js +74 -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 +28 -0
- package/dist/basemap/vectortiles/trees.js +178 -0
- package/dist/basemap/vectortiles/trees.js.map +1 -0
- package/dist/basemap/vectortiles/vectors.d.ts +37 -0
- package/dist/basemap/vectortiles/vectors.js +231 -0
- package/dist/basemap/vectortiles/vectors.js.map +1 -0
- package/dist/basemap/vectortiles.d.ts +49 -96
- package/dist/basemap/vectortiles.js +99 -774
- 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 +14 -12
- package/dist/bind/layers.js +1 -0
- package/dist/bind/layers.js.map +1 -1
- package/dist/data/collada.d.ts +19 -0
- package/dist/data/collada.js +319 -0
- package/dist/data/collada.js.map +1 -0
- package/dist/data/ifc.d.ts +100 -0
- package/dist/data/ifc.js +372 -0
- package/dist/data/ifc.js.map +1 -0
- 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/formats.d.ts +7 -2
- package/dist/formats.js +7 -2
- package/dist/formats.js.map +1 -1
- package/dist/index.d.ts +15 -2
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/isard_bg.wasm +0 -0
- package/dist/isard_core_bg.wasm +0 -0
- package/dist/isard_grid_bg.wasm +0 -0
- package/dist/isard_tess_bg.wasm +0 -0
- package/dist/layers/extrusions.d.ts +10 -0
- package/dist/layers/extrusions.js +28 -2
- package/dist/layers/extrusions.js.map +1 -1
- 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/layers.d.ts +12 -0
- package/dist/layers/layers.js +44 -1
- package/dist/layers/layers.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/layers/photos.d.ts +48 -0
- package/dist/layers/photos.js +140 -0
- package/dist/layers/photos.js.map +1 -0
- package/dist/layers/symbols.d.ts +5 -1
- package/dist/layers/symbols.js +111 -32
- package/dist/layers/symbols.js.map +1 -1
- package/dist/layers/trees.d.ts +28 -0
- package/dist/layers/trees.js +51 -0
- package/dist/layers/trees.js.map +1 -0
- package/dist/layers/vectors.d.ts +28 -2
- package/dist/layers/vectors.js +81 -7
- package/dist/layers/vectors.js.map +1 -1
- package/dist/map/layer-spec.d.ts +3 -0
- package/dist/map/layer-spec.js +2 -0
- package/dist/map/layer-spec.js.map +1 -1
- package/dist/map/workers.js +3 -0
- package/dist/map/workers.js.map +1 -1
- package/dist/map.d.ts +8 -0
- package/dist/map.js +15 -2
- package/dist/map.js.map +1 -1
- package/dist/panoramax.d.ts +49 -0
- package/dist/panoramax.js +71 -0
- package/dist/panoramax.js.map +1 -0
- package/dist/react/Layer.d.ts +1 -0
- package/dist/react/Layer.js.map +1 -1
- package/dist/react/VectorGeometry.d.ts +5 -0
- package/dist/react/VectorGeometry.js +12 -4
- package/dist/react/VectorGeometry.js.map +1 -1
- package/dist/util/gltf.d.ts +9 -0
- package/dist/util/gltf.js +27 -0
- package/dist/util/gltf.js.map +1 -0
- 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/util/roofs.d.ts +10 -0
- package/dist/util/roofs.js +30 -0
- package/dist/util/roofs.js.map +1 -0
- package/dist/wasm/isard.d.ts +74 -22
- package/dist/wasm/isard.js +153 -22
- package/dist/wasm/isard_core.d.ts +62 -22
- package/dist/wasm/isard_core.js +129 -22
- package/dist/wasm/isard_tess.d.ts +6 -3
- package/dist/wasm/isard_tess.js +27 -4
- package/dist/workers/basemap/geometry.d.ts +38 -22
- package/dist/workers/basemap/labels.d.ts +14 -1
- package/dist/workers/basemap/lamps.d.ts +10 -0
- package/dist/workers/basemap/street.d.ts +1 -1
- package/dist/workers/basemap/trees.d.ts +3 -1
- package/dist/workers/basemap.worker.d.ts +43 -3
- package/dist/workers/basemap.worker.js +14 -2
- package/dist/workers/basemap.worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
/** The requests not answered yet, by key */
|
|
8
|
+
private readonly pending;
|
|
9
|
+
/** Whether the tile is held or asked for */
|
|
10
|
+
has(key: string): boolean;
|
|
11
|
+
/** The keys of the tiles held or asked for (a copy: the tiles can be dropped while it is gone through) */
|
|
12
|
+
keys(): string[];
|
|
13
|
+
/** Holds a new request under `key` (the one it held before is forgotten, not aborted); returns its signal */
|
|
14
|
+
start(key: string): AbortSignal;
|
|
15
|
+
/**
|
|
16
|
+
* Asks for a tile (`request`) and does with its answer what `use` does, unless the request was aborted meanwhile. On
|
|
17
|
+
* a failure of either, the tile is forgotten (the next update asks for it again); an error of `use` (the engine
|
|
18
|
+
* refusing the tile) is reported, never left unhandled
|
|
19
|
+
*/
|
|
20
|
+
ask<T>(key: string, request: (signal: AbortSignal) => Promise<T>, use: (answer: T) => unknown): Promise<void>;
|
|
21
|
+
/** Resolves once the tiles of `keys` asked for, by this update or an earlier one, are answered (or dropped) */
|
|
22
|
+
answered(keys: Iterable<string>): Promise<void>;
|
|
23
|
+
/** Aborts the tile's request and forgets it */
|
|
24
|
+
drop(key: string): void;
|
|
25
|
+
/** Aborts every request and forgets every tile */
|
|
26
|
+
abortAll(): void;
|
|
27
|
+
private open;
|
|
28
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
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 class TileRequests {
|
|
6
|
+
held = new Map();
|
|
7
|
+
/** The requests not answered yet, by key */
|
|
8
|
+
pending = new Map();
|
|
9
|
+
/** Whether the tile is held or asked for */
|
|
10
|
+
has(key) {
|
|
11
|
+
return this.held.has(key);
|
|
12
|
+
}
|
|
13
|
+
/** The keys of the tiles held or asked for (a copy: the tiles can be dropped while it is gone through) */
|
|
14
|
+
keys() {
|
|
15
|
+
return [...this.held.keys()];
|
|
16
|
+
}
|
|
17
|
+
/** Holds a new request under `key` (the one it held before is forgotten, not aborted); returns its signal */
|
|
18
|
+
start(key) {
|
|
19
|
+
return this.open(key).signal;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Asks for a tile (`request`) and does with its answer what `use` does, unless the request was aborted meanwhile. On
|
|
23
|
+
* a failure of either, the tile is forgotten (the next update asks for it again); an error of `use` (the engine
|
|
24
|
+
* refusing the tile) is reported, never left unhandled
|
|
25
|
+
*/
|
|
26
|
+
ask(key, request, use) {
|
|
27
|
+
const controller = this.open(key);
|
|
28
|
+
// A later request under the same key is not forgotten by this one's failure
|
|
29
|
+
const forget = () => {
|
|
30
|
+
if (this.held.get(key) === controller)
|
|
31
|
+
this.held.delete(key);
|
|
32
|
+
};
|
|
33
|
+
const answered = request(controller.signal)
|
|
34
|
+
.then(async (answer) => {
|
|
35
|
+
if (controller.signal.aborted)
|
|
36
|
+
return;
|
|
37
|
+
try {
|
|
38
|
+
await use(answer);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.warn('isard: a vector tile not drawn', error);
|
|
42
|
+
forget();
|
|
43
|
+
}
|
|
44
|
+
}, forget)
|
|
45
|
+
.finally(() => {
|
|
46
|
+
if (this.pending.get(key) === answered)
|
|
47
|
+
this.pending.delete(key);
|
|
48
|
+
});
|
|
49
|
+
this.pending.set(key, answered);
|
|
50
|
+
return answered;
|
|
51
|
+
}
|
|
52
|
+
/** Resolves once the tiles of `keys` asked for, by this update or an earlier one, are answered (or dropped) */
|
|
53
|
+
answered(keys) {
|
|
54
|
+
return Promise.all([...keys].map((k) => this.pending.get(k))).then(() => undefined);
|
|
55
|
+
}
|
|
56
|
+
/** Aborts the tile's request and forgets it */
|
|
57
|
+
drop(key) {
|
|
58
|
+
this.held.get(key)?.abort();
|
|
59
|
+
this.held.delete(key);
|
|
60
|
+
}
|
|
61
|
+
/** Aborts every request and forgets every tile */
|
|
62
|
+
abortAll() {
|
|
63
|
+
this.held.forEach((controller) => {
|
|
64
|
+
controller.abort();
|
|
65
|
+
});
|
|
66
|
+
this.held.clear();
|
|
67
|
+
}
|
|
68
|
+
open(key) {
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
this.held.set(key, controller);
|
|
71
|
+
return controller;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=requests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requests.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/requests.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The requests of a layer whose tiles follow the view (`VectorTiles`' extrusions, streets' wind, trees, vectors): the\n * tiles held or asked for, by key, each with the controller that aborts its request.\n */\n\nexport class TileRequests {\n private readonly held = new Map<string, AbortController>();\n /** The requests not answered yet, by key */\n private readonly pending = new Map<string, Promise<void>>();\n\n /** Whether the tile is held or asked for */\n has(key: string): boolean {\n return this.held.has(key);\n }\n\n /** The keys of the tiles held or asked for (a copy: the tiles can be dropped while it is gone through) */\n keys(): string[] {\n return [...this.held.keys()];\n }\n\n /** Holds a new request under `key` (the one it held before is forgotten, not aborted); returns its signal */\n start(key: string): AbortSignal {\n return this.open(key).signal;\n }\n\n /**\n * Asks for a tile (`request`) and does with its answer what `use` does, unless the request was aborted meanwhile. On\n * a failure of either, the tile is forgotten (the next update asks for it again); an error of `use` (the engine\n * refusing the tile) is reported, never left unhandled\n */\n ask<T>(key: string, request: (signal: AbortSignal) => Promise<T>, use: (answer: T) => unknown): Promise<void> {\n const controller = this.open(key);\n // A later request under the same key is not forgotten by this one's failure\n const forget = () => {\n if (this.held.get(key) === controller) this.held.delete(key);\n };\n\n const answered = request(controller.signal)\n .then(async (answer) => {\n if (controller.signal.aborted) return;\n\n try {\n await use(answer);\n } catch (error) {\n console.warn('isard: a vector tile not drawn', error);\n forget();\n }\n }, forget)\n .finally(() => {\n if (this.pending.get(key) === answered) this.pending.delete(key);\n });\n\n this.pending.set(key, answered);\n\n return answered;\n }\n\n /** Resolves once the tiles of `keys` asked for, by this update or an earlier one, are answered (or dropped) */\n answered(keys: Iterable<string>): Promise<void> {\n return Promise.all([...keys].map((k) => this.pending.get(k))).then(() => undefined);\n }\n\n /** Aborts the tile's request and forgets it */\n drop(key: string): void {\n this.held.get(key)?.abort();\n this.held.delete(key);\n }\n\n /** Aborts every request and forgets every tile */\n abortAll(): void {\n this.held.forEach((controller) => {\n controller.abort();\n });\n this.held.clear();\n }\n\n private open(key: string): AbortController {\n const controller = new AbortController();\n\n this.held.set(key, controller);\n\n return controller;\n }\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** The wind through the streets of a vector basemap (`VectorTiles.streetWind`): a flow round its buildings */
|
|
2
|
+
import type { FlowLayer, FlowOptions } from '../../layers/flow.ts';
|
|
3
|
+
import type { StyleLayer } from '../vectorstyle.ts';
|
|
4
|
+
import type { VectorTilesContext } from './context.ts';
|
|
5
|
+
/** The options of `VectorTiles.streetWind` */
|
|
6
|
+
export type StreetWindOptions = Omit<FlowOptions, 'field' | 'fields' | 'time'> & {
|
|
7
|
+
wind?: [u: number, v: number];
|
|
8
|
+
size?: number;
|
|
9
|
+
cell?: number;
|
|
10
|
+
iterations?: number;
|
|
11
|
+
layers?: (layer: StyleLayer) => boolean;
|
|
12
|
+
minZoom?: number;
|
|
13
|
+
};
|
|
14
|
+
/** The layer of `VectorTiles.streetWind` */
|
|
15
|
+
export type StreetWindLayer = FlowLayer & {
|
|
16
|
+
setWind(wind: [u: number, v: number]): void;
|
|
17
|
+
computed: Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
export declare const streetWind: (context: VectorTilesContext, options?: StreetWindOptions) => StreetWindLayer;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/** The wind through the streets of a vector basemap (`VectorTiles.streetWind`): a flow round its buildings */
|
|
2
|
+
import { latitude, longitude, mercatorX, mercatorY } from "../../util/geo.js";
|
|
3
|
+
import { TileRequests } from "./requests.js";
|
|
4
|
+
import { isBuildings, isPolygonLayer } from "./style.js";
|
|
5
|
+
import { STREET_ZOOM } from "./tiles.js";
|
|
6
|
+
/** Earth's circumference at the equator (metres): the size of the Mercator square, 1 wide, at latitude 0 */
|
|
7
|
+
const EARTH_CIRCUMFERENCE_METRES = 40_075_016.686;
|
|
8
|
+
/** Pause before the first square that failed is asked for again (ms) */
|
|
9
|
+
const RETRY_FIRST_MS = 1000;
|
|
10
|
+
/** Longest pause before a square that failed again and again is asked for again (ms) */
|
|
11
|
+
const RETRY_MAX_MS = 30_000;
|
|
12
|
+
export const streetWind = (context, options = {}) => {
|
|
13
|
+
const { map } = context;
|
|
14
|
+
const { size = 1200, cell = 4, iterations = 300, layers = isBuildings, minZoom = STREET_ZOOM, ...flow } = options;
|
|
15
|
+
const renderer = context.renderer(isPolygonLayer(layers), []);
|
|
16
|
+
/** No square: a field of no value at the view's center, where no particle is born nor moves (the map idles) */
|
|
17
|
+
const empty = () => {
|
|
18
|
+
const [lon, lat] = map.getCamera().center;
|
|
19
|
+
return {
|
|
20
|
+
width: 2,
|
|
21
|
+
height: 2,
|
|
22
|
+
u: [NaN, NaN, NaN, NaN],
|
|
23
|
+
v: [NaN, NaN, NaN, NaN],
|
|
24
|
+
bounds: [lon, lat, lon + 1e-4, lat + 1e-4],
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
const layer = map.addFlow({ height: 3, ...flow, field: empty() });
|
|
28
|
+
const state = { wind: options.wind ?? [3, 2], asked: 0, wait: RETRY_FIRST_MS };
|
|
29
|
+
/** The flow of the wind far away: `u` times the flow towards the east plus `v` times the one towards the north,
|
|
30
|
+
* mixed by the engine (the two flows sent once a square, a new wind two numbers) */
|
|
31
|
+
const send = () => {
|
|
32
|
+
const { flows, grid, wind } = state;
|
|
33
|
+
if (!flows || !grid)
|
|
34
|
+
return;
|
|
35
|
+
layer.setBasis({ ...grid, u: flows.eastU, v: flows.eastV }, { ...grid, u: flows.northU, v: flows.northV }, wind);
|
|
36
|
+
};
|
|
37
|
+
/** The square asked for, its only request (under `square`) */
|
|
38
|
+
const requests = new TileRequests();
|
|
39
|
+
const first = {};
|
|
40
|
+
const computed = new Promise((resolve) => {
|
|
41
|
+
first.resolve = resolve;
|
|
42
|
+
});
|
|
43
|
+
const update = async () => {
|
|
44
|
+
if (context.destroyed())
|
|
45
|
+
return;
|
|
46
|
+
const camera = map.getCamera();
|
|
47
|
+
const [x, y] = [mercatorX(camera.center[0]), mercatorY(camera.center[1])];
|
|
48
|
+
// Half the square in Mercator, at its latitude
|
|
49
|
+
const half = size / 2 / (EARTH_CIRCUMFERENCE_METRES * Math.cos((camera.center[1] * Math.PI) / 180));
|
|
50
|
+
const square = state.square;
|
|
51
|
+
// Off street level: no square (its particles gone, asked for again back at street level)
|
|
52
|
+
if (camera.zoom < minZoom) {
|
|
53
|
+
if (state.square) {
|
|
54
|
+
state.asked++;
|
|
55
|
+
state.square = undefined;
|
|
56
|
+
state.flows = undefined;
|
|
57
|
+
requests.abortAll();
|
|
58
|
+
layer.setField(empty());
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Still in the square's middle: nothing to compute
|
|
63
|
+
if (square &&
|
|
64
|
+
Math.abs(x - (square[0] + square[2]) / 2) < half / 3 &&
|
|
65
|
+
Math.abs(y - (square[1] + square[3]) / 2) < half / 3) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const { key, maxzoom } = await renderer;
|
|
69
|
+
const bounds = [x - half, y - half, x + half, y + half];
|
|
70
|
+
const n = 2 ** maxzoom;
|
|
71
|
+
const tiles = [];
|
|
72
|
+
for (let ty = Math.floor(bounds[1] * n); ty <= Math.floor(bounds[3] * n); ty++) {
|
|
73
|
+
for (let tx = Math.floor(bounds[0] * n); tx <= Math.floor(bounds[2] * n); tx++)
|
|
74
|
+
tiles.push(tx, ty);
|
|
75
|
+
}
|
|
76
|
+
const sides = Math.max(8, Math.round(size / cell));
|
|
77
|
+
const work = {
|
|
78
|
+
type: 'street',
|
|
79
|
+
key,
|
|
80
|
+
module: map.gridUrl,
|
|
81
|
+
z: maxzoom,
|
|
82
|
+
tiles,
|
|
83
|
+
bounds,
|
|
84
|
+
width: sides,
|
|
85
|
+
height: sides,
|
|
86
|
+
iterations,
|
|
87
|
+
};
|
|
88
|
+
const asked = ++state.asked;
|
|
89
|
+
requests.abortAll();
|
|
90
|
+
const signal = requests.start('square');
|
|
91
|
+
state.square = bounds;
|
|
92
|
+
const answer = await context
|
|
93
|
+
.request(maxzoom, Math.floor(x * n), Math.floor(y * n), work, signal)
|
|
94
|
+
.catch((error) => ({ error }));
|
|
95
|
+
if (asked !== state.asked || signal.aborted)
|
|
96
|
+
return;
|
|
97
|
+
// Failed (a tile or the grid module not fetched, a worker of an older build): the square asked for again later,
|
|
98
|
+
// a while after each failure
|
|
99
|
+
if (!('street' in answer)) {
|
|
100
|
+
console.warn("isard: the streets' wind not computed, tried again", 'error' in answer ? answer.error : answer);
|
|
101
|
+
state.square = undefined;
|
|
102
|
+
clearTimeout(state.retry);
|
|
103
|
+
state.retry = window.setTimeout(() => {
|
|
104
|
+
update().catch(() => undefined);
|
|
105
|
+
}, state.wait);
|
|
106
|
+
state.wait = Math.min(state.wait * 2, RETRY_MAX_MS);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
state.wait = RETRY_FIRST_MS;
|
|
110
|
+
state.flows = answer.street;
|
|
111
|
+
state.grid = {
|
|
112
|
+
width: sides,
|
|
113
|
+
height: sides,
|
|
114
|
+
bounds: [longitude(bounds[0]), latitude(bounds[3]), longitude(bounds[2]), latitude(bounds[1])],
|
|
115
|
+
};
|
|
116
|
+
send();
|
|
117
|
+
first.resolve?.();
|
|
118
|
+
};
|
|
119
|
+
renderer.catch(() => undefined);
|
|
120
|
+
context.layers.push(layer);
|
|
121
|
+
context.followView(update, requests, () => undefined);
|
|
122
|
+
context.cleanup.push(() => {
|
|
123
|
+
clearTimeout(state.retry);
|
|
124
|
+
});
|
|
125
|
+
return Object.assign(layer, {
|
|
126
|
+
computed,
|
|
127
|
+
setWind: (wind) => {
|
|
128
|
+
state.wind = wind;
|
|
129
|
+
layer.setWeights(wind);
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=street-wind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"street-wind.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/street-wind.ts"],"names":[],"mappings":"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","sourcesContent":["/** The wind through the streets of a vector basemap (`VectorTiles.streetWind`): a flow round its buildings */\n\nimport type { StreetFlows } from '../../grid-module.ts';\nimport type { FlowField, FlowLayer, FlowOptions } from '../../layers/flow.ts';\nimport { latitude, longitude, mercatorX, mercatorY } from '../../util/geo.ts';\nimport type { BasemapWork } 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 { STREET_ZOOM } from './tiles.ts';\n\n/** Earth's circumference at the equator (metres): the size of the Mercator square, 1 wide, at latitude 0 */\nconst EARTH_CIRCUMFERENCE_METRES = 40_075_016.686;\n\n/** Pause before the first square that failed is asked for again (ms) */\nconst RETRY_FIRST_MS = 1000;\n\n/** Longest pause before a square that failed again and again is asked for again (ms) */\nconst RETRY_MAX_MS = 30_000;\n\n/** The options of `VectorTiles.streetWind` */\nexport type StreetWindOptions = Omit<FlowOptions, 'field' | 'fields' | 'time'> & {\n wind?: [u: number, v: number];\n size?: number;\n cell?: number;\n iterations?: number;\n layers?: (layer: StyleLayer) => boolean;\n minZoom?: number;\n};\n\n/** The layer of `VectorTiles.streetWind` */\nexport type StreetWindLayer = FlowLayer & { setWind(wind: [u: number, v: number]): void; computed: Promise<void> };\n\nexport const streetWind = (context: VectorTilesContext, options: StreetWindOptions = {}): StreetWindLayer => {\n const { map } = context;\n const { size = 1200, cell = 4, iterations = 300, layers = isBuildings, minZoom = STREET_ZOOM, ...flow } = options;\n const renderer = context.renderer(isPolygonLayer(layers), []);\n /** No square: a field of no value at the view's center, where no particle is born nor moves (the map idles) */\n const empty = (): FlowField => {\n const [lon, lat] = map.getCamera().center;\n\n return {\n width: 2,\n height: 2,\n u: [NaN, NaN, NaN, NaN],\n v: [NaN, NaN, NaN, NaN],\n bounds: [lon, lat, lon + 1e-4, lat + 1e-4],\n };\n };\n const layer = map.addFlow({ height: 3, ...flow, field: empty() });\n const state: {\n wind: [number, number];\n /** The square solved (Mercator: west, north, east, south), its grid, the flows of its basis */\n square?: [number, number, number, number] | undefined;\n grid?: { width: number; height: number; bounds: [number, number, number, number] };\n flows?: StreetFlows | undefined;\n /** Bumped by each square asked for: only the last one's answer is taken */\n asked: number;\n /** After a failure, the square asked for again in `wait` ms (doubled each time) */\n retry?: number;\n wait: number;\n } = { wind: options.wind ?? [3, 2], asked: 0, wait: RETRY_FIRST_MS };\n /** The flow of the wind far away: `u` times the flow towards the east plus `v` times the one towards the north,\n * mixed by the engine (the two flows sent once a square, a new wind two numbers) */\n const send = () => {\n const { flows, grid, wind } = state;\n\n if (!flows || !grid) return;\n layer.setBasis({ ...grid, u: flows.eastU, v: flows.eastV }, { ...grid, u: flows.northU, v: flows.northV }, wind);\n };\n /** The square asked for, its only request (under `square`) */\n const requests = new TileRequests();\n const first: { resolve?: () => void } = {};\n const computed = new Promise<void>((resolve) => {\n first.resolve = resolve;\n });\n const update = async () => {\n if (context.destroyed()) return;\n\n const camera = map.getCamera();\n const [x, y] = [mercatorX(camera.center[0]), mercatorY(camera.center[1])];\n // Half the square in Mercator, at its latitude\n const half = size / 2 / (EARTH_CIRCUMFERENCE_METRES * Math.cos((camera.center[1] * Math.PI) / 180));\n const square = state.square;\n\n // Off street level: no square (its particles gone, asked for again back at street level)\n if (camera.zoom < minZoom) {\n if (state.square) {\n state.asked++;\n state.square = undefined;\n state.flows = undefined;\n requests.abortAll();\n layer.setField(empty());\n }\n\n return;\n }\n // Still in the square's middle: nothing to compute\n if (\n square &&\n Math.abs(x - (square[0] + square[2]) / 2) < half / 3 &&\n Math.abs(y - (square[1] + square[3]) / 2) < half / 3\n ) {\n return;\n }\n\n const { key, maxzoom } = await renderer;\n const bounds: [number, number, number, number] = [x - half, y - half, x + half, y + half];\n const n = 2 ** maxzoom;\n const tiles: number[] = [];\n\n for (let ty = Math.floor(bounds[1] * n); ty <= Math.floor(bounds[3] * n); ty++) {\n for (let tx = Math.floor(bounds[0] * n); tx <= Math.floor(bounds[2] * n); tx++) tiles.push(tx, ty);\n }\n\n const sides = Math.max(8, Math.round(size / cell));\n const work: BasemapWork = {\n type: 'street',\n key,\n module: map.gridUrl,\n z: maxzoom,\n tiles,\n bounds,\n width: sides,\n height: sides,\n iterations,\n };\n const asked = ++state.asked;\n\n requests.abortAll();\n\n const signal = requests.start('square');\n\n state.square = bounds;\n\n const answer = await context\n .request(maxzoom, Math.floor(x * n), Math.floor(y * n), work, signal)\n .catch((error: unknown) => ({ error }));\n\n if (asked !== state.asked || signal.aborted) return;\n // Failed (a tile or the grid module not fetched, a worker of an older build): the square asked for again later,\n // a while after each failure\n if (!('street' in answer)) {\n console.warn(\"isard: the streets' wind not computed, tried again\", 'error' in answer ? answer.error : answer);\n state.square = undefined;\n clearTimeout(state.retry);\n state.retry = window.setTimeout(() => {\n update().catch(() => undefined);\n }, state.wait);\n state.wait = Math.min(state.wait * 2, RETRY_MAX_MS);\n\n return;\n }\n state.wait = RETRY_FIRST_MS;\n state.flows = answer.street;\n state.grid = {\n width: sides,\n height: sides,\n bounds: [longitude(bounds[0]), latitude(bounds[3]), longitude(bounds[2]), latitude(bounds[1])],\n };\n send();\n first.resolve?.();\n };\n\n renderer.catch(() => undefined);\n context.layers.push(layer);\n context.followView(update, requests, () => undefined);\n context.cleanup.push(() => {\n clearTimeout(state.retry);\n });\n\n return Object.assign(layer, {\n computed,\n setWind: (wind: [u: number, v: number]) => {\n state.wind = wind;\n layer.setWeights(wind);\n },\n });\n};\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The styles of the vector basemaps (`VectorTiles`): the known ones and their credits, a style loaded with its vector
|
|
3
|
+
* source resolved (a TileJSON, a PMTiles archive) and its raster sources, the fonts of its labels, the style layers
|
|
4
|
+
* of buildings.
|
|
5
|
+
*/
|
|
6
|
+
import { type RasterSource, type Style, type StyleLayer } from '../vectorstyle.ts';
|
|
7
|
+
/** OpenFreeMap's styles: OpenStreetMap data in the OpenMapTiles schema, worldwide, free and without a key */
|
|
8
|
+
export declare const OPENFREEMAP_STYLES: {
|
|
9
|
+
readonly positron: "https://tiles.openfreemap.org/styles/positron";
|
|
10
|
+
readonly liberty: "https://tiles.openfreemap.org/styles/liberty";
|
|
11
|
+
readonly bright: "https://tiles.openfreemap.org/styles/bright";
|
|
12
|
+
readonly dark: "https://tiles.openfreemap.org/styles/dark";
|
|
13
|
+
readonly fiord: "https://tiles.openfreemap.org/styles/fiord";
|
|
14
|
+
};
|
|
15
|
+
/** PLAN.IGN vector styles of the Géoplateforme (France) */
|
|
16
|
+
export declare const PLAN_STYLES: {
|
|
17
|
+
readonly standard: "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json";
|
|
18
|
+
readonly grey: "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/gris.json";
|
|
19
|
+
};
|
|
20
|
+
/** Vector basemaps by name (see `BasemapName`) */
|
|
21
|
+
export declare const VECTOR_BASEMAPS: {
|
|
22
|
+
readonly 'osm-positron': "https://tiles.openfreemap.org/styles/positron";
|
|
23
|
+
readonly 'osm-liberty': "https://tiles.openfreemap.org/styles/liberty";
|
|
24
|
+
readonly 'osm-bright': "https://tiles.openfreemap.org/styles/bright";
|
|
25
|
+
readonly 'osm-dark': "https://tiles.openfreemap.org/styles/dark";
|
|
26
|
+
readonly 'ign-plan-vector': "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json";
|
|
27
|
+
readonly 'ign-plan-grey-vector': "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/gris.json";
|
|
28
|
+
};
|
|
29
|
+
/** The credits of a known style: OpenFreeMap's, PLAN.IGN's */
|
|
30
|
+
export declare const styleAttribution: (style: string | Style) => string | undefined;
|
|
31
|
+
/** A style loaded: its layers of its vector source (and of its raster sources), the source's tiles and deepest level */
|
|
32
|
+
export interface LoadedStyle {
|
|
33
|
+
style: Style;
|
|
34
|
+
tiles: string;
|
|
35
|
+
maxzoom: number;
|
|
36
|
+
rasters: Record<string, RasterSource>;
|
|
37
|
+
}
|
|
38
|
+
/** A style (its URL, or the style itself) loaded, with its first vector source resolved and its raster sources */
|
|
39
|
+
export declare const loadStyle: (input: string | Style) => Promise<LoadedStyle>;
|
|
40
|
+
/** The fonts of the style's labels, for its glyphs (the first of each `text-font`, as the workers take it) */
|
|
41
|
+
export declare const styleFonts: (style: Style) => string[];
|
|
42
|
+
/** The style layers of buildings, by default: PLAN IGN's `bati_surf` (not its built-up areas nor its parks), the
|
|
43
|
+
* OpenMapTiles' `building` */
|
|
44
|
+
export declare const isBuildings: (layer: StyleLayer) => boolean;
|
|
45
|
+
/** Whether a style layer is a fill (or fill-extrusion) layer that `layers` keeps: the polygons extruded or flowed
|
|
46
|
+
* round */
|
|
47
|
+
export declare const isPolygonLayer: (layers: (layer: StyleLayer) => boolean) => (layer: StyleLayer) => boolean;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The styles of the vector basemaps (`VectorTiles`): the known ones and their credits, a style loaded with its vector
|
|
3
|
+
* source resolved (a TileJSON, a PMTiles archive) and its raster sources, the fonts of its labels, the style layers
|
|
4
|
+
* of buildings.
|
|
5
|
+
*/
|
|
6
|
+
import { withHeaders } from "../../engine/requests.js";
|
|
7
|
+
import { IGN_ATTRIBUTION } from "../../geoplateforme.js";
|
|
8
|
+
import { isRecord } from "../../util/guards.js";
|
|
9
|
+
import { OPENFREEMAP_ATTRIBUTION } from "../basemaps.js";
|
|
10
|
+
import { pmtiles, pmtilesUrl } from "../pmtiles.js";
|
|
11
|
+
import { isStyle, labelFont } from "../vectorstyle.js";
|
|
12
|
+
/** OpenFreeMap's styles: OpenStreetMap data in the OpenMapTiles schema, worldwide, free and without a key */
|
|
13
|
+
export const OPENFREEMAP_STYLES = {
|
|
14
|
+
positron: 'https://tiles.openfreemap.org/styles/positron',
|
|
15
|
+
liberty: 'https://tiles.openfreemap.org/styles/liberty',
|
|
16
|
+
bright: 'https://tiles.openfreemap.org/styles/bright',
|
|
17
|
+
dark: 'https://tiles.openfreemap.org/styles/dark',
|
|
18
|
+
fiord: 'https://tiles.openfreemap.org/styles/fiord',
|
|
19
|
+
};
|
|
20
|
+
/** PLAN.IGN vector styles of the Géoplateforme (France) */
|
|
21
|
+
export const PLAN_STYLES = {
|
|
22
|
+
standard: 'https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json',
|
|
23
|
+
grey: 'https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/gris.json',
|
|
24
|
+
};
|
|
25
|
+
/** Vector basemaps by name (see `BasemapName`) */
|
|
26
|
+
export const VECTOR_BASEMAPS = {
|
|
27
|
+
'osm-positron': OPENFREEMAP_STYLES.positron,
|
|
28
|
+
'osm-liberty': OPENFREEMAP_STYLES.liberty,
|
|
29
|
+
'osm-bright': OPENFREEMAP_STYLES.bright,
|
|
30
|
+
'osm-dark': OPENFREEMAP_STYLES.dark,
|
|
31
|
+
'ign-plan-vector': PLAN_STYLES.standard,
|
|
32
|
+
'ign-plan-grey-vector': PLAN_STYLES.grey,
|
|
33
|
+
};
|
|
34
|
+
/** The credits of a known style: OpenFreeMap's, PLAN.IGN's */
|
|
35
|
+
export const styleAttribution = (style) => {
|
|
36
|
+
if (typeof style !== 'string')
|
|
37
|
+
return undefined;
|
|
38
|
+
if (Object.values(OPENFREEMAP_STYLES).some((s) => s === style))
|
|
39
|
+
return OPENFREEMAP_ATTRIBUTION;
|
|
40
|
+
if (Object.values(PLAN_STYLES).some((s) => s === style))
|
|
41
|
+
return IGN_ATTRIBUTION;
|
|
42
|
+
return undefined;
|
|
43
|
+
};
|
|
44
|
+
/** Deepest level of a vector source that does not give its own */
|
|
45
|
+
const DEFAULT_MAXZOOM = 14;
|
|
46
|
+
/** Deepest level of a raster source that does not give its own */
|
|
47
|
+
const DEFAULT_RASTER_MAXZOOM = 22;
|
|
48
|
+
/** Tile size of a raster source that does not give its own (pixels) */
|
|
49
|
+
const DEFAULT_RASTER_TILE_SIZE = 512;
|
|
50
|
+
/** Fetches a JSON document (a style, a TileJSON) */
|
|
51
|
+
const json = async (url) => {
|
|
52
|
+
const response = await fetch(url, withHeaders(url));
|
|
53
|
+
if (!response.ok)
|
|
54
|
+
throw new Error(`${url}: ${response.status}`);
|
|
55
|
+
return response.json();
|
|
56
|
+
};
|
|
57
|
+
/** A style fetched: a MapLibre style, or an error */
|
|
58
|
+
const fetchStyle = async (url) => {
|
|
59
|
+
const style = await json(url);
|
|
60
|
+
if (!isStyle(style))
|
|
61
|
+
throw new Error(`${url}: not a MapLibre style`);
|
|
62
|
+
return style;
|
|
63
|
+
};
|
|
64
|
+
const isTileJson = (value) => isRecord(value) &&
|
|
65
|
+
Array.isArray(value.tiles) &&
|
|
66
|
+
value.tiles.every((t) => typeof t === 'string') &&
|
|
67
|
+
(value.maxzoom === undefined || typeof value.maxzoom === 'number');
|
|
68
|
+
/** A TileJSON fetched, or an error */
|
|
69
|
+
const fetchTileJson = async (url) => {
|
|
70
|
+
const tilejson = await json(url);
|
|
71
|
+
if (!isTileJson(tilejson))
|
|
72
|
+
throw new Error(`${url}: not a TileJSON`);
|
|
73
|
+
return tilejson;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* A style source's tiles and deepest level: a `pmtiles://` archive (its `url`, or a `tiles` template) read for its
|
|
77
|
+
* header, its tiles then named `pmtiles://<absolute archive URL>/{z}/{x}/{y}` for the workers; other sources as they are
|
|
78
|
+
*/
|
|
79
|
+
const resolveSource = async (source) => {
|
|
80
|
+
const given = source.url ?? source.tiles?.[0] ?? '';
|
|
81
|
+
const archive = pmtilesUrl(given.replace(/\/\{z\}\/\{x\}\/\{y\}$/, ''));
|
|
82
|
+
if (!archive)
|
|
83
|
+
return {
|
|
84
|
+
...(source.tiles ? { tiles: source.tiles } : {}),
|
|
85
|
+
...(source.maxzoom === undefined ? {} : { maxzoom: source.maxzoom }),
|
|
86
|
+
};
|
|
87
|
+
const header = await pmtiles(archive).header();
|
|
88
|
+
return { tiles: [`pmtiles://${archive}/{z}/{x}/{y}`], maxzoom: source.maxzoom ?? header.maxZoom };
|
|
89
|
+
};
|
|
90
|
+
/** A style (its URL, or the style itself) loaded, with its first vector source resolved and its raster sources */
|
|
91
|
+
export const loadStyle = async (input) => {
|
|
92
|
+
const style = typeof input === 'string' ? await fetchStyle(input) : input;
|
|
93
|
+
const [name, source] = Object.entries(style.sources).find(([, s]) => s.type === 'vector') ?? [];
|
|
94
|
+
if (!name || !source)
|
|
95
|
+
throw new Error('the style has no vector source');
|
|
96
|
+
const resolved = await resolveSource(source);
|
|
97
|
+
const tilejson = !resolved.tiles && source.url ? await fetchTileJson(source.url) : undefined;
|
|
98
|
+
const tiles = tilejson === undefined ? resolved.tiles : tilejson.tiles;
|
|
99
|
+
const maxzoom = resolved.maxzoom ?? tilejson?.maxzoom;
|
|
100
|
+
const template = tiles?.[0];
|
|
101
|
+
if (!template)
|
|
102
|
+
throw new Error(`source ${name} has no tiles`);
|
|
103
|
+
// Its raster sources of tiles, drawn into the same images (the shaded relief of a style at low zoom)
|
|
104
|
+
const rasters = {};
|
|
105
|
+
for (const [key, s] of Object.entries(style.sources)) {
|
|
106
|
+
if (s.type !== 'raster')
|
|
107
|
+
continue;
|
|
108
|
+
const r = await resolveSource(s);
|
|
109
|
+
const url = r.tiles?.[0];
|
|
110
|
+
if (url) {
|
|
111
|
+
rasters[key] = {
|
|
112
|
+
tiles: url,
|
|
113
|
+
maxzoom: r.maxzoom ?? DEFAULT_RASTER_MAXZOOM,
|
|
114
|
+
tileSize: s.tileSize ?? DEFAULT_RASTER_TILE_SIZE,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Only the layers of those sources (and the background); other types (hillshade, heatmap…) are left out. The
|
|
119
|
+
// application's style object stays as it is
|
|
120
|
+
const layers = style.layers.filter((l) => l.type === 'background' ||
|
|
121
|
+
(l.source === name && l.type !== 'raster') ||
|
|
122
|
+
(l.type === 'raster' && l.source !== undefined && rasters[l.source] !== undefined));
|
|
123
|
+
return { style: { ...style, layers }, tiles: template, maxzoom: maxzoom ?? DEFAULT_MAXZOOM, rasters };
|
|
124
|
+
};
|
|
125
|
+
/** The fonts of the style's labels, for its glyphs (the first of each `text-font`, as the workers take it) */
|
|
126
|
+
export const styleFonts = (style) => {
|
|
127
|
+
const fonts = new Set();
|
|
128
|
+
style.layers.forEach((layer) => {
|
|
129
|
+
const font = layer.layout?.['text-font'];
|
|
130
|
+
if (layer.type === 'symbol' && Array.isArray(font) && typeof font[0] === 'string')
|
|
131
|
+
fonts.add(labelFont(font[0]));
|
|
132
|
+
});
|
|
133
|
+
return [...fonts];
|
|
134
|
+
};
|
|
135
|
+
/** The style layers of buildings, by default: PLAN IGN's `bati_surf` (not its built-up areas nor its parks), the
|
|
136
|
+
* OpenMapTiles' `building` */
|
|
137
|
+
export const isBuildings = (layer) => ['bati_surf', 'building'].includes(layer['source-layer'] ?? '');
|
|
138
|
+
/** Whether a style layer is a fill (or fill-extrusion) layer that `layers` keeps: the polygons extruded or flowed
|
|
139
|
+
* round */
|
|
140
|
+
export const isPolygonLayer = (layers) => (layer) => (layer.type === 'fill' || layer.type === 'fill-extrusion') && layers(layer);
|
|
141
|
+
//# sourceMappingURL=style.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.js","sourceRoot":"","sources":["../../../src/basemap/vectortiles/style.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The styles of the vector basemaps (`VectorTiles`): the known ones and their credits, a style loaded with its vector\n * source resolved (a TileJSON, a PMTiles archive) and its raster sources, the fonts of its labels, the style layers\n * of buildings.\n */\n\nimport { withHeaders } from '../../engine/requests.ts';\nimport { IGN_ATTRIBUTION } from '../../geoplateforme.ts';\nimport { isRecord } from '../../util/guards.ts';\nimport { OPENFREEMAP_ATTRIBUTION } from '../basemaps.ts';\nimport { pmtiles, pmtilesUrl } from '../pmtiles.ts';\nimport { isStyle, labelFont, type RasterSource, type Style, type StyleLayer } from '../vectorstyle.ts';\n\n/** OpenFreeMap's styles: OpenStreetMap data in the OpenMapTiles schema, worldwide, free and without a key */\nexport const OPENFREEMAP_STYLES = {\n positron: 'https://tiles.openfreemap.org/styles/positron',\n liberty: 'https://tiles.openfreemap.org/styles/liberty',\n bright: 'https://tiles.openfreemap.org/styles/bright',\n dark: 'https://tiles.openfreemap.org/styles/dark',\n fiord: 'https://tiles.openfreemap.org/styles/fiord',\n} as const;\n\n/** PLAN.IGN vector styles of the Géoplateforme (France) */\nexport const PLAN_STYLES = {\n standard: 'https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json',\n grey: 'https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/gris.json',\n} as const;\n\n/** Vector basemaps by name (see `BasemapName`) */\nexport const VECTOR_BASEMAPS = {\n 'osm-positron': OPENFREEMAP_STYLES.positron,\n 'osm-liberty': OPENFREEMAP_STYLES.liberty,\n 'osm-bright': OPENFREEMAP_STYLES.bright,\n 'osm-dark': OPENFREEMAP_STYLES.dark,\n 'ign-plan-vector': PLAN_STYLES.standard,\n 'ign-plan-grey-vector': PLAN_STYLES.grey,\n} as const;\n\n/** The credits of a known style: OpenFreeMap's, PLAN.IGN's */\nexport const styleAttribution = (style: string | Style): string | undefined => {\n if (typeof style !== 'string') return undefined;\n if (Object.values(OPENFREEMAP_STYLES).some((s) => s === style)) return OPENFREEMAP_ATTRIBUTION;\n if (Object.values(PLAN_STYLES).some((s) => s === style)) return IGN_ATTRIBUTION;\n\n return undefined;\n};\n\n/** Deepest level of a vector source that does not give its own */\nconst DEFAULT_MAXZOOM = 14;\n\n/** Deepest level of a raster source that does not give its own */\nconst DEFAULT_RASTER_MAXZOOM = 22;\n\n/** Tile size of a raster source that does not give its own (pixels) */\nconst DEFAULT_RASTER_TILE_SIZE = 512;\n\n/** A style loaded: its layers of its vector source (and of its raster sources), the source's tiles and deepest level */\nexport interface LoadedStyle {\n style: Style;\n tiles: string;\n maxzoom: number;\n rasters: Record<string, RasterSource>;\n}\n\n/** Fetches a JSON document (a style, a TileJSON) */\nconst json = async (url: string): Promise<unknown> => {\n const response = await fetch(url, withHeaders(url));\n\n if (!response.ok) throw new Error(`${url}: ${response.status}`);\n\n return response.json();\n};\n\n/** A style fetched: a MapLibre style, or an error */\nconst fetchStyle = async (url: string): Promise<Style> => {\n const style = await json(url);\n\n if (!isStyle(style)) throw new Error(`${url}: not a MapLibre style`);\n\n return style;\n};\n\n/** What a basemap reads of a TileJSON */\ninterface TileJson {\n tiles: string[];\n maxzoom?: number;\n}\n\nconst isTileJson = (value: unknown): value is TileJson =>\n isRecord(value) &&\n Array.isArray(value.tiles) &&\n value.tiles.every((t) => typeof t === 'string') &&\n (value.maxzoom === undefined || typeof value.maxzoom === 'number');\n\n/** A TileJSON fetched, or an error */\nconst fetchTileJson = async (url: string): Promise<TileJson> => {\n const tilejson = await json(url);\n\n if (!isTileJson(tilejson)) throw new Error(`${url}: not a TileJSON`);\n\n return tilejson;\n};\n\n/**\n * A style source's tiles and deepest level: a `pmtiles://` archive (its `url`, or a `tiles` template) read for its\n * header, its tiles then named `pmtiles://<absolute archive URL>/{z}/{x}/{y}` for the workers; other sources as they are\n */\nconst resolveSource = async (source: Style['sources'][string]): Promise<{ tiles?: string[]; maxzoom?: number }> => {\n const given = source.url ?? source.tiles?.[0] ?? '';\n const archive = pmtilesUrl(given.replace(/\\/\\{z\\}\\/\\{x\\}\\/\\{y\\}$/, ''));\n\n if (!archive)\n return {\n ...(source.tiles ? { tiles: source.tiles } : {}),\n ...(source.maxzoom === undefined ? {} : { maxzoom: source.maxzoom }),\n };\n\n const header = await pmtiles(archive).header();\n\n return { tiles: [`pmtiles://${archive}/{z}/{x}/{y}`], maxzoom: source.maxzoom ?? header.maxZoom };\n};\n\n/** A style (its URL, or the style itself) loaded, with its first vector source resolved and its raster sources */\nexport const loadStyle = async (input: string | Style): Promise<LoadedStyle> => {\n const style = typeof input === 'string' ? await fetchStyle(input) : input;\n const [name, source] = Object.entries(style.sources).find(([, s]) => s.type === 'vector') ?? [];\n\n if (!name || !source) throw new Error('the style has no vector source');\n\n const resolved = await resolveSource(source);\n const tilejson = !resolved.tiles && source.url ? await fetchTileJson(source.url) : undefined;\n const tiles = tilejson === undefined ? resolved.tiles : tilejson.tiles;\n const maxzoom = resolved.maxzoom ?? tilejson?.maxzoom;\n const template = tiles?.[0];\n\n if (!template) throw new Error(`source ${name} has no tiles`);\n\n // Its raster sources of tiles, drawn into the same images (the shaded relief of a style at low zoom)\n const rasters: Record<string, RasterSource> = {};\n\n for (const [key, s] of Object.entries(style.sources)) {\n if (s.type !== 'raster') continue;\n\n const r = await resolveSource(s);\n const url = r.tiles?.[0];\n\n if (url) {\n rasters[key] = {\n tiles: url,\n maxzoom: r.maxzoom ?? DEFAULT_RASTER_MAXZOOM,\n tileSize: s.tileSize ?? DEFAULT_RASTER_TILE_SIZE,\n };\n }\n }\n\n // Only the layers of those sources (and the background); other types (hillshade, heatmap…) are left out. The\n // application's style object stays as it is\n const layers = style.layers.filter(\n (l) =>\n l.type === 'background' ||\n (l.source === name && l.type !== 'raster') ||\n (l.type === 'raster' && l.source !== undefined && rasters[l.source] !== undefined),\n );\n\n return { style: { ...style, layers }, tiles: template, maxzoom: maxzoom ?? DEFAULT_MAXZOOM, rasters };\n};\n\n/** The fonts of the style's labels, for its glyphs (the first of each `text-font`, as the workers take it) */\nexport const styleFonts = (style: Style): string[] => {\n const fonts = new Set<string>();\n\n style.layers.forEach((layer) => {\n const font = layer.layout?.['text-font'];\n\n if (layer.type === 'symbol' && Array.isArray(font) && typeof font[0] === 'string') fonts.add(labelFont(font[0]));\n });\n\n return [...fonts];\n};\n\n/** The style layers of buildings, by default: PLAN IGN's `bati_surf` (not its built-up areas nor its parks), the\n * OpenMapTiles' `building` */\nexport const isBuildings = (layer: StyleLayer): boolean =>\n ['bati_surf', 'building'].includes(layer['source-layer'] ?? '');\n\n/** Whether a style layer is a fill (or fill-extrusion) layer that `layers` keeps: the polygons extruded or flowed\n * round */\nexport const isPolygonLayer =\n (layers: (layer: StyleLayer) => boolean) =>\n (layer: StyleLayer): boolean =>\n (layer.type === 'fill' || layer.type === 'fill-extrusion') && layers(layer);\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tiles of the vector layers (`VectorTiles`): the tiles around the view, their keys, the order they are asked
|
|
3
|
+
* for in, and the defaults the layers share.
|
|
4
|
+
*/
|
|
5
|
+
import type { GeoMap } from '../../map.ts';
|
|
6
|
+
import type { Camera } from '../../wasm/isard.js';
|
|
7
|
+
import type { Priority } from '../../workers/basemap.worker.ts';
|
|
8
|
+
/** A tile: its level and its column and row at that level */
|
|
9
|
+
export type TileTriple = [z: number, x: number, y: number];
|
|
10
|
+
/** Street level: the zoom the extrusions, the streets' wind and the trees start at by default */
|
|
11
|
+
export declare const STREET_ZOOM = 15;
|
|
12
|
+
/** Tiles of a layer that follows the view, by default (the extrusions', the labels') */
|
|
13
|
+
export declare const MAX_TILES = 36;
|
|
14
|
+
/** Tiles nearest to the center asked for first: the others wait (for the raster tiles, for instance) */
|
|
15
|
+
export declare const NEAR_TILES = 4;
|
|
16
|
+
/** Milliseconds between two updates of the tiles of a layer while the camera moves */
|
|
17
|
+
export declare const VECTOR_MOVE_REFRESH_MS = 250;
|
|
18
|
+
/** A tile's key, `z/x/y` */
|
|
19
|
+
export declare const tileKey: (z: number, x: number, y: number) => string;
|
|
20
|
+
/** The tile of a key `z/x/y` (zeros for the numbers it lacks) */
|
|
21
|
+
export declare const parseTileKey: (key: string) => TileTriple;
|
|
22
|
+
/** The tiles of the engine's flat list of z, x, y triples */
|
|
23
|
+
export declare const tileTriples: (list: Uint32Array) => TileTriple[];
|
|
24
|
+
/** The priority of the `index`th tile asked for, the nearest first: `near` for the first `NEAR_TILES`, `far` after */
|
|
25
|
+
export declare const nearPriority: (index: number, near?: Priority, far?: Priority) => Priority;
|
|
26
|
+
/** A tile's column at level `z` brought back into the world (the columns past the antimeridian are those of its other
|
|
27
|
+
* side) */
|
|
28
|
+
export declare const wrapColumn: (x: number, z: number) => number;
|
|
29
|
+
/**
|
|
30
|
+
* The tiles of level `z` in view, within a window around the center (a tilted view's bounds reach far), the
|
|
31
|
+
* `maxTiles` nearest to the center first. Across the antimeridian, the window's columns go on round the world (the
|
|
32
|
+
* view's bounds, which stop at ±180°, no longer limit them)
|
|
33
|
+
*/
|
|
34
|
+
export declare const tilesAround: (map: GeoMap, camera: Camera, z: number, maxTiles: number) => [x: number, y: number][];
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tiles of the vector layers (`VectorTiles`): the tiles around the view, their keys, the order they are asked
|
|
3
|
+
* for in, and the defaults the layers share.
|
|
4
|
+
*/
|
|
5
|
+
import { mercatorX, mercatorY } from "../../util/geo.js";
|
|
6
|
+
/** Street level: the zoom the extrusions, the streets' wind and the trees start at by default */
|
|
7
|
+
export const STREET_ZOOM = 15;
|
|
8
|
+
/** Tiles of a layer that follows the view, by default (the extrusions', the labels') */
|
|
9
|
+
export const MAX_TILES = 36;
|
|
10
|
+
/** Tiles nearest to the center asked for first: the others wait (for the raster tiles, for instance) */
|
|
11
|
+
export const NEAR_TILES = 4;
|
|
12
|
+
/** Milliseconds between two updates of the tiles of a layer while the camera moves */
|
|
13
|
+
export const VECTOR_MOVE_REFRESH_MS = 250;
|
|
14
|
+
/** A tile's key, `z/x/y` */
|
|
15
|
+
export const tileKey = (z, x, y) => `${z}/${x}/${y}`;
|
|
16
|
+
/** The tile of a key `z/x/y` (zeros for the numbers it lacks) */
|
|
17
|
+
export const parseTileKey = (key) => {
|
|
18
|
+
const [z = 0, x = 0, y = 0] = key.split('/').map(Number);
|
|
19
|
+
return [z, x, y];
|
|
20
|
+
};
|
|
21
|
+
/** The tiles of the engine's flat list of z, x, y triples */
|
|
22
|
+
export const tileTriples = (list) => Array.from({ length: Math.floor(list.length / 3) }, (_, i) => [list[i * 3] ?? 0, list[i * 3 + 1] ?? 0, list[i * 3 + 2] ?? 0]);
|
|
23
|
+
/** The priority of the `index`th tile asked for, the nearest first: `near` for the first `NEAR_TILES`, `far` after */
|
|
24
|
+
export const nearPriority = (index, near = 'auto', far = 'low') => index < NEAR_TILES ? near : far;
|
|
25
|
+
/** A tile's column at level `z` brought back into the world (the columns past the antimeridian are those of its other
|
|
26
|
+
* side) */
|
|
27
|
+
export const wrapColumn = (x, z) => {
|
|
28
|
+
const n = 2 ** z;
|
|
29
|
+
return ((x % n) + n) % n;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The tiles of level `z` in view, within a window around the center (a tilted view's bounds reach far), the
|
|
33
|
+
* `maxTiles` nearest to the center first. Across the antimeridian, the window's columns go on round the world (the
|
|
34
|
+
* view's bounds, which stop at ±180°, no longer limit them)
|
|
35
|
+
*/
|
|
36
|
+
export const tilesAround = (map, camera, z, maxTiles) => {
|
|
37
|
+
const n = 2 ** z;
|
|
38
|
+
const [west, south, east, north] = map.getBounds();
|
|
39
|
+
const tx = (lon) => Math.floor(mercatorX(lon) * n);
|
|
40
|
+
const ty = (lat) => Math.floor(mercatorY(lat) * n);
|
|
41
|
+
const [cx, cy] = [mercatorX(camera.center[0]) * n, mercatorY(camera.center[1]) * n];
|
|
42
|
+
const reach = Math.ceil(Math.sqrt(maxTiles)) + 2;
|
|
43
|
+
const [left, right] = [Math.floor(cx) - reach, Math.floor(cx) + reach];
|
|
44
|
+
const across = left < 0 || right > n - 1;
|
|
45
|
+
const [x0, x1] = across ? [left, right] : [Math.max(left, tx(west)), Math.min(right, tx(east))];
|
|
46
|
+
const [y0, y1] = [Math.max(0, ty(north), Math.floor(cy) - reach), Math.min(n - 1, ty(south), Math.floor(cy) + reach)];
|
|
47
|
+
const wanted = Array.from({ length: Math.max(0, y1 - y0 + 1) }, (_, j) => y0 + j).flatMap((y) => Array.from({ length: Math.max(0, x1 - x0 + 1) }, (_, i) => [x0 + i, y]));
|
|
48
|
+
wanted.sort((a, b) => Math.hypot(a[0] + 0.5 - cx, a[1] + 0.5 - cy) - Math.hypot(b[0] + 0.5 - cx, b[1] + 0.5 - cy));
|
|
49
|
+
// Each tile once (at a low level, the window can go round the whole world)
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
return wanted
|
|
52
|
+
.map(([x, y]) => [wrapColumn(x, z), y])
|
|
53
|
+
.filter(([x, y]) => {
|
|
54
|
+
const k = `${x}/${y}`;
|
|
55
|
+
if (seen.has(k))
|
|
56
|
+
return false;
|
|
57
|
+
seen.add(k);
|
|
58
|
+
return true;
|
|
59
|
+
})
|
|
60
|
+
.slice(0, maxTiles);
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=tiles.js.map
|