isard 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/{basemaps.d.ts → basemap/basemaps.d.ts} +3 -3
- package/dist/{basemaps.js → basemap/basemaps.js} +3 -3
- package/dist/basemap/basemaps.js.map +1 -0
- package/dist/{grid.d.ts → basemap/grid.d.ts} +6 -6
- package/dist/{grid.js → basemap/grid.js} +61 -67
- package/dist/basemap/grid.js.map +1 -0
- package/dist/{mvt.d.ts → basemap/mvt.d.ts} +1 -1
- package/dist/{mvt.js → basemap/mvt.js} +6 -6
- package/dist/basemap/mvt.js.map +1 -0
- package/dist/{pmtiles.d.ts → basemap/pmtiles.d.ts} +16 -10
- package/dist/{pmtiles.js → basemap/pmtiles.js} +41 -33
- package/dist/basemap/pmtiles.js.map +1 -0
- package/dist/{resample.d.ts → basemap/resample.d.ts} +11 -2
- package/dist/{resample.js → basemap/resample.js} +18 -16
- package/dist/basemap/resample.js.map +1 -0
- package/dist/{terrain.d.ts → basemap/terrain.d.ts} +3 -3
- package/dist/{terrain.js → basemap/terrain.js} +7 -7
- package/dist/basemap/terrain.js.map +1 -0
- package/dist/{vectorstyle.d.ts → basemap/vectorstyle.d.ts} +12 -10
- package/dist/{vectorstyle.js → basemap/vectorstyle.js} +129 -111
- package/dist/basemap/vectorstyle.js.map +1 -0
- package/dist/{vectortiles.d.ts → basemap/vectortiles.d.ts} +18 -18
- package/dist/{vectortiles.js → basemap/vectortiles.js} +157 -109
- package/dist/basemap/vectortiles.js.map +1 -0
- package/dist/{wmts.d.ts → basemap/wmts.d.ts} +3 -3
- package/dist/{wmts.js → basemap/wmts.js} +19 -14
- package/dist/basemap/wmts.js.map +1 -0
- package/dist/basemap.worker.js +2 -2
- package/dist/basemap.worker.js.map +14 -13
- package/dist/bind/layers.d.ts +46 -0
- package/dist/bind/layers.js +154 -0
- package/dist/bind/layers.js.map +1 -0
- package/dist/{crs.d.ts → data/crs.d.ts} +11 -11
- package/dist/{crs.js → data/crs.js} +38 -34
- package/dist/data/crs.js.map +1 -0
- package/dist/{gpx.d.ts → data/gpx.d.ts} +2 -2
- package/dist/{gpx.js → data/gpx.js} +20 -20
- package/dist/data/gpx.js.map +1 -0
- package/dist/{kml.d.ts → data/kml.d.ts} +2 -2
- package/dist/{kml.js → data/kml.js} +42 -39
- package/dist/data/kml.js.map +1 -0
- package/dist/{topojson.d.ts → data/topojson.d.ts} +2 -2
- package/dist/{topojson.js → data/topojson.js} +15 -10
- package/dist/data/topojson.js.map +1 -0
- package/dist/{xml.d.ts → data/xml.d.ts} +5 -5
- package/dist/{xml.js → data/xml.js} +19 -18
- package/dist/data/xml.js.map +1 -0
- package/dist/decoder.worker.js +4 -4
- package/dist/decoder.worker.js.map +7 -7
- package/dist/{decoders.d.ts → engine/decoders.d.ts} +3 -3
- package/dist/{decoders.js → engine/decoders.js} +28 -25
- package/dist/engine/decoders.js.map +1 -0
- package/dist/{loader.d.ts → engine/loader.d.ts} +1 -1
- package/dist/{loader.js → engine/loader.js} +17 -14
- package/dist/engine/loader.js.map +1 -0
- package/dist/{protocol.d.ts → engine/protocol.d.ts} +1 -1
- package/dist/engine/protocol.js +3 -0
- package/dist/engine/protocol.js.map +1 -0
- package/dist/{requests.d.ts → engine/requests.d.ts} +1 -1
- package/dist/{requests.js → engine/requests.js} +19 -16
- package/dist/engine/requests.js.map +1 -0
- package/dist/engine-loader.d.ts +13 -9
- package/dist/engine-loader.js +30 -30
- package/dist/engine-loader.js.map +1 -1
- package/dist/formats.d.ts +3 -3
- package/dist/formats.js +3 -3
- package/dist/formats.js.map +1 -1
- package/dist/geoplateforme.d.ts +13 -13
- package/dist/geoplateforme.js +23 -21
- package/dist/geoplateforme.js.map +1 -1
- package/dist/index.d.ts +30 -29
- package/dist/index.js +18 -18
- 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_points_bg.wasm +0 -0
- package/dist/{extrusions.d.ts → layers/extrusions.d.ts} +1 -1
- package/dist/{extrusions.js → layers/extrusions.js} +10 -5
- package/dist/layers/extrusions.js.map +1 -0
- package/dist/{features.d.ts → layers/features.d.ts} +10 -10
- package/dist/{features.js → layers/features.js} +44 -45
- package/dist/layers/features.js.map +1 -0
- package/dist/{glyphs.d.ts → layers/glyphs.d.ts} +6 -6
- package/dist/{glyphs.js → layers/glyphs.js} +45 -41
- package/dist/layers/glyphs.js.map +1 -0
- package/dist/{icons.d.ts → layers/icons.d.ts} +10 -10
- package/dist/{icons.js → layers/icons.js} +84 -69
- package/dist/layers/icons.js.map +1 -0
- package/dist/{layers.d.ts → layers/layers.d.ts} +9 -4
- package/dist/{layers.js → layers/layers.js} +35 -31
- package/dist/layers/layers.js.map +1 -0
- package/dist/{panoramas.d.ts → layers/panoramas.d.ts} +3 -3
- package/dist/{panoramas.js → layers/panoramas.js} +54 -43
- package/dist/layers/panoramas.js.map +1 -0
- package/dist/{pointclouds.d.ts → layers/pointclouds.d.ts} +5 -5
- package/dist/{pointclouds.js → layers/pointclouds.js} +26 -34
- package/dist/layers/pointclouds.js.map +1 -0
- package/dist/{symbols.d.ts → layers/symbols.d.ts} +3 -3
- package/dist/{symbols.js → layers/symbols.js} +69 -72
- package/dist/layers/symbols.js.map +1 -0
- package/dist/map/basemap-layers.d.ts +26 -0
- package/dist/map/basemap-layers.js +61 -0
- package/dist/map/basemap-layers.js.map +1 -0
- package/dist/map/canvas.d.ts +17 -0
- package/dist/map/canvas.js +56 -0
- package/dist/map/canvas.js.map +1 -0
- package/dist/map/checks.d.ts +11 -0
- package/dist/map/checks.js +39 -0
- package/dist/map/checks.js.map +1 -0
- package/dist/map/credits.d.ts +17 -0
- package/dist/map/credits.js +34 -0
- package/dist/map/credits.js.map +1 -0
- package/dist/map/engine-settings.d.ts +15 -0
- package/dist/map/engine-settings.js +45 -0
- package/dist/map/engine-settings.js.map +1 -0
- package/dist/map/engines.d.ts +41 -0
- package/dist/map/engines.js +77 -0
- package/dist/map/engines.js.map +1 -0
- package/dist/map/events.d.ts +14 -0
- package/dist/map/events.js +46 -0
- package/dist/map/events.js.map +1 -0
- package/dist/map/frame-loop.d.ts +61 -0
- package/dist/map/frame-loop.js +139 -0
- package/dist/map/frame-loop.js.map +1 -0
- package/dist/map/hover-pick.d.ts +30 -0
- package/dist/map/hover-pick.js +56 -0
- package/dist/map/hover-pick.js.map +1 -0
- package/dist/map/layer-spec.d.ts +42 -0
- package/dist/map/layer-spec.js +34 -0
- package/dist/map/layer-spec.js.map +1 -0
- package/dist/map/layer-stack.d.ts +36 -0
- package/dist/map/layer-stack.js +101 -0
- package/dist/map/layer-stack.js.map +1 -0
- package/dist/map/overlay-host.d.ts +36 -0
- package/dist/map/overlay-host.js +101 -0
- package/dist/map/overlay-host.js.map +1 -0
- package/dist/map/panorama-host.d.ts +5 -0
- package/dist/map/panorama-host.js +32 -0
- package/dist/map/panorama-host.js.map +1 -0
- package/dist/map/raster-source.d.ts +11 -0
- package/dist/map/raster-source.js +26 -0
- package/dist/map/raster-source.js.map +1 -0
- package/dist/map/symbol-atlases.d.ts +26 -0
- package/dist/map/symbol-atlases.js +59 -0
- package/dist/map/symbol-atlases.js.map +1 -0
- package/dist/map/viewshed.d.ts +5 -0
- package/dist/map/viewshed.js +28 -0
- package/dist/map/viewshed.js.map +1 -0
- package/dist/map/workers.d.ts +46 -0
- package/dist/map/workers.js +73 -0
- package/dist/map/workers.js.map +1 -0
- package/dist/map.d.ts +46 -127
- package/dist/map.js +183 -698
- package/dist/map.js.map +1 -1
- package/dist/projections.d.ts +3 -3
- package/dist/projections.js +2 -2
- package/dist/projections.js.map +1 -1
- package/dist/react/Control.d.ts +11 -0
- package/dist/react/Control.js +18 -0
- package/dist/react/Control.js.map +1 -0
- package/dist/react/CustomLayer.d.ts +11 -0
- package/dist/react/CustomLayer.js +30 -0
- package/dist/react/CustomLayer.js.map +1 -0
- package/dist/react/IsardMap.d.ts +43 -0
- package/dist/react/IsardMap.js +146 -0
- package/dist/react/IsardMap.js.map +1 -0
- package/dist/react/Layer.d.ts +42 -0
- package/dist/react/Layer.js +74 -0
- package/dist/react/Layer.js.map +1 -0
- package/dist/react/Marker.d.ts +24 -0
- package/dist/react/Marker.js +53 -0
- package/dist/react/Marker.js.map +1 -0
- package/dist/react/Panel.d.ts +11 -0
- package/dist/react/Panel.js +37 -0
- package/dist/react/Panel.js.map +1 -0
- package/dist/react/Popup.d.ts +18 -0
- package/dist/react/Popup.js +60 -0
- package/dist/react/Popup.js.map +1 -0
- package/dist/react/Swipe.d.ts +9 -0
- package/dist/react/Swipe.js +41 -0
- package/dist/react/Swipe.js.map +1 -0
- package/dist/react/Viewshed.d.ts +5 -0
- package/dist/react/Viewshed.js +28 -0
- package/dist/react/Viewshed.js.map +1 -0
- package/dist/react/context.d.ts +32 -0
- package/dist/react/context.js +20 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/index.d.ts +26 -0
- package/dist/react/index.js +22 -0
- package/dist/react/index.js.map +1 -0
- package/dist/settings.d.ts +6 -10
- package/dist/settings.js +19 -17
- package/dist/settings.js.map +1 -1
- package/dist/types.d.ts +8 -8
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/{controls.d.ts → ui/controls.d.ts} +1 -1
- package/dist/{controls.js → ui/controls.js} +6 -4
- package/dist/ui/controls.js.map +1 -0
- package/dist/{interaction.d.ts → ui/interaction.d.ts} +9 -5
- package/dist/{interaction.js → ui/interaction.js} +9 -8
- package/dist/ui/interaction.js.map +1 -0
- package/dist/{overlay.d.ts → ui/overlay.d.ts} +2 -2
- package/dist/{overlay.js → ui/overlay.js} +18 -20
- package/dist/ui/overlay.js.map +1 -0
- package/dist/{style.d.ts → ui/style.d.ts} +1 -1
- package/dist/{style.js → ui/style.js} +2 -2
- package/dist/ui/style.js.map +1 -0
- package/dist/{animation.d.ts → util/animation.d.ts} +9 -5
- package/dist/{animation.js → util/animation.js} +6 -6
- package/dist/util/animation.js.map +1 -0
- package/dist/{color.d.ts → util/color.d.ts} +1 -1
- package/dist/{color.js → util/color.js} +14 -16
- package/dist/util/color.js.map +1 -0
- package/dist/{geo.d.ts → util/geo.d.ts} +6 -6
- package/dist/{geo.js → util/geo.js} +12 -12
- package/dist/util/geo.js.map +1 -0
- package/dist/util/guards.d.ts +7 -0
- package/dist/util/guards.js +8 -0
- package/dist/util/guards.js.map +1 -0
- package/dist/util/lru.js.map +1 -0
- package/dist/wasm/isard.d.ts +168 -0
- package/dist/wasm/isard.js +146 -0
- package/dist/wasm/isard_core.d.ts +144 -0
- package/dist/wasm/isard_core.js +122 -0
- package/dist/{basemap.worker.d.ts → workers/basemap.worker.d.ts} +17 -7
- package/dist/workers/basemap.worker.js +658 -0
- package/dist/workers/basemap.worker.js.map +1 -0
- package/package.json +24 -1
- package/vite.js +1 -1
- package/dist/animation.js.map +0 -1
- package/dist/basemaps.js.map +0 -1
- package/dist/color.js.map +0 -1
- package/dist/controls.js.map +0 -1
- package/dist/crs.js.map +0 -1
- package/dist/decoders.js.map +0 -1
- package/dist/extrusions.js.map +0 -1
- package/dist/features.js.map +0 -1
- package/dist/geo.js.map +0 -1
- package/dist/glyphs.js.map +0 -1
- package/dist/gpx.js.map +0 -1
- package/dist/grid.js.map +0 -1
- package/dist/icons.js.map +0 -1
- package/dist/interaction.js.map +0 -1
- package/dist/kml.js.map +0 -1
- package/dist/layers.js.map +0 -1
- package/dist/loader.js.map +0 -1
- package/dist/lru.js.map +0 -1
- package/dist/mvt.js.map +0 -1
- package/dist/overlay.js.map +0 -1
- package/dist/panoramas.js.map +0 -1
- package/dist/pmtiles.js.map +0 -1
- package/dist/pointclouds.js.map +0 -1
- package/dist/protocol.js +0 -2
- package/dist/protocol.js.map +0 -1
- package/dist/requests.js.map +0 -1
- package/dist/resample.js.map +0 -1
- package/dist/style.js.map +0 -1
- package/dist/symbols.js.map +0 -1
- package/dist/terrain.js.map +0 -1
- package/dist/topojson.js.map +0 -1
- package/dist/vectorstyle.js.map +0 -1
- package/dist/vectortiles.js.map +0 -1
- package/dist/wmts.js.map +0 -1
- package/dist/xml.js.map +0 -1
- /package/dist/{lru.d.ts → util/lru.d.ts} +0 -0
- /package/dist/{lru.js → util/lru.js} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Cesium use): Draco geometry (`KHR_draco_mesh_compression`) and KTX2 / Basis Universal textures (`KHR_texture_basisu`),
|
|
4
4
|
* transcoded to the best compressed format the GPU supports.
|
|
5
5
|
*/
|
|
6
|
-
import type { CompressedTexture, DracoGeometry, DracoRequest, TextureFormats } from '
|
|
6
|
+
import type { CompressedTexture, DracoGeometry, DracoRequest, TextureFormats } from '../wasm/isard.js';
|
|
7
7
|
/**
|
|
8
8
|
* Base URLs of the decoders (each ending with `/`).
|
|
9
9
|
*
|
|
@@ -23,6 +23,6 @@ export interface DecoderUrls {
|
|
|
23
23
|
}
|
|
24
24
|
export declare const DEFAULT_DECODERS: DecoderUrls;
|
|
25
25
|
/** Decodes the Draco primitives of a tile (their points alone for point clouds) */
|
|
26
|
-
export declare
|
|
26
|
+
export declare const decodeDraco: (urls: DecoderUrls, requests: DracoRequest[]) => Promise<DracoGeometry[]>;
|
|
27
27
|
/** Transcodes a KTX2 image for the GPU */
|
|
28
|
-
export declare
|
|
28
|
+
export declare const transcodeKtx2: (urls: DecoderUrls, formats: TextureFormats, bytes: Uint8Array) => Promise<CompressedTexture>;
|
|
@@ -4,23 +4,23 @@ export const DEFAULT_DECODERS = {
|
|
|
4
4
|
};
|
|
5
5
|
/** Loads an Emscripten factory published as a classic script (module workers have no importScripts); needs
|
|
6
6
|
* `'unsafe-eval'` under a Content Security Policy (see `DecoderUrls`) */
|
|
7
|
-
async
|
|
7
|
+
const factory = async (url, name) => {
|
|
8
8
|
const source = await (await fetch(url)).text();
|
|
9
9
|
return new Function(`${source}\nreturn ${name};`)();
|
|
10
|
-
}
|
|
11
|
-
async
|
|
10
|
+
};
|
|
11
|
+
const instantiate = async (base, script, wasm, name) => {
|
|
12
12
|
const [create, binary] = await Promise.all([
|
|
13
13
|
factory(`${base}${script}`, name),
|
|
14
14
|
fetch(`${base}${wasm}`).then((r) => r.arrayBuffer()),
|
|
15
15
|
]);
|
|
16
16
|
return create({ wasmBinary: binary });
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
let draco;
|
|
19
19
|
/** The full Draco decoder: the glTF build decodes meshes only, not the point clouds */
|
|
20
20
|
let dracoPoints;
|
|
21
21
|
let basis;
|
|
22
22
|
/** Decodes the Draco primitives of a tile (their points alone for point clouds) */
|
|
23
|
-
export async
|
|
23
|
+
export const decodeDraco = async (urls, requests) => {
|
|
24
24
|
const meshes = requests.some((r) => !r.points);
|
|
25
25
|
if (meshes) {
|
|
26
26
|
draco ??= instantiate(urls.draco, 'draco_wasm_wrapper_gltf.js', 'draco_decoder_gltf.wasm', 'DracoDecoderModule');
|
|
@@ -30,7 +30,9 @@ export async function decodeDraco(urls, requests) {
|
|
|
30
30
|
}
|
|
31
31
|
const [forMeshes, forPoints] = await Promise.all([meshes ? draco : undefined, dracoPoints]);
|
|
32
32
|
return requests.map((request) => {
|
|
33
|
-
const module =
|
|
33
|
+
const module = request.points ? forPoints : forMeshes;
|
|
34
|
+
if (!module)
|
|
35
|
+
throw new Error('isard: the Draco decoder is not loaded');
|
|
34
36
|
const decoder = new module.Decoder();
|
|
35
37
|
const mesh = request.points ? new module.PointCloud() : new module.Mesh();
|
|
36
38
|
try {
|
|
@@ -64,16 +66,18 @@ export async function decodeDraco(urls, requests) {
|
|
|
64
66
|
geometry.normals = floats(request.normal);
|
|
65
67
|
return geometry;
|
|
66
68
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
const triangles = () => {
|
|
70
|
+
const count = mesh.num_faces() * 3;
|
|
71
|
+
const pointer = module._malloc(count * 4);
|
|
72
|
+
try {
|
|
73
|
+
decoder.GetTrianglesUInt32Array(mesh, count * 4, pointer);
|
|
74
|
+
return module.HEAPU32.slice(pointer / 4, pointer / 4 + count);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
module._free(pointer);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const indices = triangles();
|
|
77
81
|
const geometry = {
|
|
78
82
|
mesh: request.mesh,
|
|
79
83
|
primitive: request.primitive,
|
|
@@ -89,9 +93,9 @@ export async function decodeDraco(urls, requests) {
|
|
|
89
93
|
module.destroy(decoder);
|
|
90
94
|
}
|
|
91
95
|
});
|
|
92
|
-
}
|
|
96
|
+
};
|
|
93
97
|
/** Basis target format and WebGL internal format, best first */
|
|
94
|
-
|
|
98
|
+
const target = (formats, alpha) => {
|
|
95
99
|
if (formats.astc)
|
|
96
100
|
return { basis: 10, gl: 0x93b0 }; // ASTC 4×4
|
|
97
101
|
if (formats.bptc)
|
|
@@ -101,9 +105,9 @@ function target(formats, alpha) {
|
|
|
101
105
|
if (formats.s3tc)
|
|
102
106
|
return alpha ? { basis: 3, gl: 0x83f3 } : { basis: 2, gl: 0x83f0 }; // BC3 / BC1
|
|
103
107
|
return { basis: 13, gl: 0x8058 }; // RGBA8, uncompressed
|
|
104
|
-
}
|
|
108
|
+
};
|
|
105
109
|
/** Transcodes a KTX2 image for the GPU */
|
|
106
|
-
export async
|
|
110
|
+
export const transcodeKtx2 = async (urls, formats, bytes) => {
|
|
107
111
|
basis ??= instantiate(urls.basis, 'basis_transcoder.js', 'basis_transcoder.wasm', 'BASIS').then((m) => {
|
|
108
112
|
m.initializeBasis();
|
|
109
113
|
return m;
|
|
@@ -114,19 +118,18 @@ export async function transcodeKtx2(urls, formats, bytes) {
|
|
|
114
118
|
if (!file.isValid() || !file.startTranscoding())
|
|
115
119
|
throw new Error('KTX2: invalid file');
|
|
116
120
|
const { basis: format, gl } = target(formats, file.getHasAlpha());
|
|
117
|
-
const levels =
|
|
118
|
-
for (let level = 0; level < file.getLevels(); level++) {
|
|
121
|
+
const levels = Array.from({ length: file.getLevels() }, (_, level) => {
|
|
119
122
|
const { origWidth: width, origHeight: height } = file.getImageLevelInfo(level, 0, 0);
|
|
120
123
|
const data = new Uint8Array(file.getImageTranscodedSizeInBytes(level, 0, 0, format));
|
|
121
124
|
if (!file.transcodeImage(data, level, 0, 0, format, 0, -1, -1))
|
|
122
125
|
throw new Error('KTX2: transcoding failed');
|
|
123
|
-
|
|
124
|
-
}
|
|
126
|
+
return { width, height, data };
|
|
127
|
+
});
|
|
125
128
|
return { format: gl, levels };
|
|
126
129
|
}
|
|
127
130
|
finally {
|
|
128
131
|
file.close();
|
|
129
132
|
file.delete();
|
|
130
133
|
}
|
|
131
|
-
}
|
|
134
|
+
};
|
|
132
135
|
//# sourceMappingURL=decoders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decoders.js","sourceRoot":"","sources":["../../src/engine/decoders.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Optional decoders of the worker, loaded on first use from configurable URLs (the official builds three.js and\n * Cesium use): Draco geometry (`KHR_draco_mesh_compression`) and KTX2 / Basis Universal textures (`KHR_texture_basisu`),\n * transcoded to the best compressed format the GPU supports.\n */\nimport type { CompressedTexture, DracoGeometry, DracoRequest, TextureFormats } from '../wasm/isard.js';\n\n/**\n * Base URLs of the decoders (each ending with `/`).\n *\n * The decoders are Emscripten builds published as classic scripts: the worker fetches the script and runs it with\n * `new Function`, so a Content Security Policy must allow `'unsafe-eval'` in `script-src` (or `worker-src`) for 3D\n * Tiles that use Draco or KTX2. They are fetched from the default CDNs; to self-host them (offline, locked-down\n * networks, no third-party requests), copy the four files (`draco_wasm_wrapper_gltf.js`, `draco_decoder_gltf.wasm`\n * from the Draco release, `basis_transcoder.js`, `basis_transcoder.wasm` from three.js's `examples/jsm/libs/basis/`)\n * to your server and give their folders in `GeoMapOptions.decoders`.\n */\nexport interface DecoderUrls {\n /** Folder of `draco_wasm_wrapper_gltf.js` and `draco_decoder_gltf.wasm` (and, for Draco point clouds, of the full\n * decoder `draco_wasm_wrapper.js` and `draco_decoder.wasm`) */\n draco: string;\n /** Folder of `basis_transcoder.js` and `basis_transcoder.wasm` */\n basis: string;\n}\n\nexport const DEFAULT_DECODERS: DecoderUrls = {\n draco: 'https://www.gstatic.com/draco/versioned/decoders/1.5.7/',\n basis: 'https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/libs/basis/',\n};\n\n// ── Draco (the subset of its Emscripten API used here) ──\n\ninterface DracoStatus {\n ok(): boolean;\n error_msg(): string;\n}\ninterface DracoMesh {\n num_points(): number;\n num_faces(): number;\n}\ninterface DracoAttribute {\n num_components(): number;\n}\ninterface DracoDecoder {\n DecodeArrayToMesh(data: Int8Array, size: number, mesh: DracoMesh): DracoStatus;\n DecodeArrayToPointCloud(data: Int8Array, size: number, cloud: DracoMesh): DracoStatus;\n GetAttributeByUniqueId(mesh: DracoMesh, id: number): DracoAttribute;\n GetAttributeDataArrayForAllPoints(\n mesh: DracoMesh,\n attribute: DracoAttribute,\n type: number,\n bytes: number,\n pointer: number,\n ): boolean;\n GetTrianglesUInt32Array(mesh: DracoMesh, bytes: number, pointer: number): boolean;\n}\ninterface DracoModule {\n Decoder: new () => DracoDecoder;\n Mesh: new () => DracoMesh;\n PointCloud: new () => DracoMesh;\n DT_FLOAT32: number;\n HEAPF32: Float32Array;\n HEAPU32: Uint32Array;\n _malloc(bytes: number): number;\n _free(pointer: number): void;\n destroy(object: object): void;\n}\n\n// ── Basis Universal transcoder ──\n\ninterface KTX2File {\n isValid(): boolean;\n getLevels(): number;\n getHasAlpha(): boolean;\n startTranscoding(): boolean;\n getImageLevelInfo(level: number, layer: number, face: number): { origWidth: number; origHeight: number };\n getImageTranscodedSizeInBytes(level: number, layer: number, face: number, format: number): number;\n transcodeImage(\n target: Uint8Array,\n level: number,\n layer: number,\n face: number,\n format: number,\n alphaForOpaque: number,\n channel0: number,\n channel1: number,\n ): boolean;\n close(): void;\n delete(): void;\n}\ninterface BasisModule {\n initializeBasis(): void;\n KTX2File: new (data: Uint8Array) => KTX2File;\n}\n\ntype Factory<T> = (options: { wasmBinary: ArrayBuffer }) => Promise<T>;\n\n/** Loads an Emscripten factory published as a classic script (module workers have no importScripts); needs\n * `'unsafe-eval'` under a Content Security Policy (see `DecoderUrls`) */\nconst factory = async <T>(url: string, name: string): Promise<Factory<T>> => {\n const source = await (await fetch(url)).text();\n\n return new Function(`${source}\\nreturn ${name};`)() as Factory<T>;\n};\n\nconst instantiate = async <T>(base: string, script: string, wasm: string, name: string): Promise<T> => {\n const [create, binary] = await Promise.all([\n factory<T>(`${base}${script}`, name),\n fetch(`${base}${wasm}`).then((r) => r.arrayBuffer()),\n ]);\n\n return create({ wasmBinary: binary });\n};\n\nlet draco: Promise<DracoModule> | undefined;\n/** The full Draco decoder: the glTF build decodes meshes only, not the point clouds */\nlet dracoPoints: Promise<DracoModule> | undefined;\nlet basis: Promise<BasisModule> | undefined;\n\n/** Decodes the Draco primitives of a tile (their points alone for point clouds) */\nexport const decodeDraco = async (urls: DecoderUrls, requests: DracoRequest[]): Promise<DracoGeometry[]> => {\n const meshes = requests.some((r) => !r.points);\n\n if (meshes) {\n draco ??= instantiate<DracoModule>(\n urls.draco,\n 'draco_wasm_wrapper_gltf.js',\n 'draco_decoder_gltf.wasm',\n 'DracoDecoderModule',\n );\n }\n if (requests.some((r) => r.points)) {\n dracoPoints ??= instantiate<DracoModule>(\n urls.draco,\n 'draco_wasm_wrapper.js',\n 'draco_decoder.wasm',\n 'DracoDecoderModule',\n );\n }\n\n const [forMeshes, forPoints] = await Promise.all([meshes ? draco : undefined, dracoPoints]);\n\n return requests.map((request) => {\n const module = request.points ? forPoints : forMeshes;\n\n if (!module) throw new Error('isard: the Draco decoder is not loaded');\n\n const decoder = new module.Decoder();\n const mesh = request.points ? new module.PointCloud() : new module.Mesh();\n\n try {\n const data = new Int8Array(request.data.buffer, request.data.byteOffset, request.data.byteLength);\n const status = request.points\n ? decoder.DecodeArrayToPointCloud(data, data.byteLength, mesh)\n : decoder.DecodeArrayToMesh(data, data.byteLength, mesh);\n\n if (!status.ok()) throw new Error(`Draco: ${status.error_msg()}`);\n\n const floats = (id: number): Float32Array => {\n const attribute = decoder.GetAttributeByUniqueId(mesh, id);\n const count = mesh.num_points() * attribute.num_components();\n const pointer = module._malloc(count * 4);\n\n try {\n decoder.GetAttributeDataArrayForAllPoints(mesh, attribute, module.DT_FLOAT32, count * 4, pointer);\n\n return module.HEAPF32.slice(pointer / 4, pointer / 4 + count);\n } finally {\n module._free(pointer);\n }\n };\n\n if (request.points) {\n const geometry: DracoGeometry = {\n mesh: request.mesh,\n primitive: request.primitive,\n positions: floats(request.position),\n };\n\n if (request.color !== undefined) geometry.colors = floats(request.color);\n if (request.normal !== undefined) geometry.normals = floats(request.normal);\n\n return geometry;\n }\n\n const triangles = (): Uint32Array => {\n const count = mesh.num_faces() * 3;\n const pointer = module._malloc(count * 4);\n\n try {\n decoder.GetTrianglesUInt32Array(mesh, count * 4, pointer);\n\n return module.HEAPU32.slice(pointer / 4, pointer / 4 + count);\n } finally {\n module._free(pointer);\n }\n };\n const indices = triangles();\n const geometry: DracoGeometry = {\n mesh: request.mesh,\n primitive: request.primitive,\n positions: floats(request.position),\n indices,\n };\n\n if (request.uv !== undefined) geometry.uvs = floats(request.uv);\n\n return geometry;\n } finally {\n module.destroy(mesh);\n module.destroy(decoder);\n }\n });\n};\n\n/** Basis target format and WebGL internal format, best first */\nconst target = (formats: TextureFormats, alpha: boolean): { basis: number; gl: number } => {\n if (formats.astc) return { basis: 10, gl: 0x93b0 }; // ASTC 4×4\n if (formats.bptc) return { basis: 6, gl: 0x8e8c }; // BC7\n if (formats.etc) return alpha ? { basis: 1, gl: 0x9278 } : { basis: 0, gl: 0x9274 }; // ETC2 RGBA / ETC1 as ETC2 RGB\n if (formats.s3tc) return alpha ? { basis: 3, gl: 0x83f3 } : { basis: 2, gl: 0x83f0 }; // BC3 / BC1\n\n return { basis: 13, gl: 0x8058 }; // RGBA8, uncompressed\n};\n\n/** Transcodes a KTX2 image for the GPU */\nexport const transcodeKtx2 = async (\n urls: DecoderUrls,\n formats: TextureFormats,\n bytes: Uint8Array,\n): Promise<CompressedTexture> => {\n basis ??= instantiate<BasisModule>(urls.basis, 'basis_transcoder.js', 'basis_transcoder.wasm', 'BASIS').then((m) => {\n m.initializeBasis();\n\n return m;\n });\n\n const module = await basis;\n const file = new module.KTX2File(bytes);\n\n try {\n if (!file.isValid() || !file.startTranscoding()) throw new Error('KTX2: invalid file');\n\n const { basis: format, gl } = target(formats, file.getHasAlpha());\n const levels: CompressedTexture['levels'] = Array.from({ length: file.getLevels() }, (_, level) => {\n const { origWidth: width, origHeight: height } = file.getImageLevelInfo(level, 0, 0);\n const data = new Uint8Array(file.getImageTranscodedSizeInBytes(level, 0, 0, format));\n\n if (!file.transcodeImage(data, level, 0, 0, format, 0, -1, -1)) throw new Error('KTX2: transcoding failed');\n\n return { width, height, data };\n });\n\n return { format: gl, levels };\n } finally {\n file.close();\n file.delete();\n }\n};\n"]}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Pool of decoding workers for 3D Tiles content and point clouds. The engine calls `load`, `loadPoints` and `abort`
|
|
3
3
|
* (`TileLoaderLike`); the main thread then only uploads the decoded arrays to the GPU.
|
|
4
4
|
*/
|
|
5
|
+
import type { DecodedTile, TextureFormats, TileLoaderLike } from '../wasm/isard.js';
|
|
5
6
|
import type { DecoderUrls } from './decoders.ts';
|
|
6
|
-
import type { DecodedTile, TextureFormats, TileLoaderLike } from './wasm/isard.js';
|
|
7
7
|
export declare class TileLoader implements TileLoaderLike {
|
|
8
8
|
private readonly module;
|
|
9
9
|
private readonly workerUrl;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pool of decoding workers for 3D Tiles content and point clouds. The engine calls `load`, `loadPoints` and `abort`
|
|
3
|
+
* (`TileLoaderLike`); the main thread then only uploads the decoded arrays to the GPU.
|
|
4
|
+
*/
|
|
1
5
|
import { headerRules, onHeaderRules } from "./requests.js";
|
|
2
6
|
export class TileLoader {
|
|
3
7
|
module;
|
|
@@ -14,8 +18,9 @@ export class TileLoader {
|
|
|
14
18
|
failed;
|
|
15
19
|
/** The page's request headers, sent to the workers as they change */
|
|
16
20
|
unfollow = onHeaderRules((rules) => {
|
|
17
|
-
|
|
21
|
+
this.workers.forEach((worker) => {
|
|
18
22
|
this.send(worker, { type: 'headers', rules });
|
|
23
|
+
});
|
|
19
24
|
});
|
|
20
25
|
constructor(module, workerUrl, size, decoders, formats,
|
|
21
26
|
/** URL of the point cloud module, for the workers */
|
|
@@ -57,18 +62,20 @@ export class TileLoader {
|
|
|
57
62
|
}
|
|
58
63
|
destroy() {
|
|
59
64
|
this.unfollow();
|
|
60
|
-
|
|
65
|
+
this.workers.forEach((worker) => {
|
|
61
66
|
worker.terminate();
|
|
67
|
+
});
|
|
62
68
|
this.workers = [];
|
|
63
69
|
this.busy.clear();
|
|
64
|
-
|
|
70
|
+
this.pending.forEach(({ reject }) => {
|
|
65
71
|
reject(new Error('loader destroyed'));
|
|
72
|
+
});
|
|
66
73
|
this.pending.clear();
|
|
67
74
|
}
|
|
68
75
|
/** Workers start with the first tile: a map without 3D Tiles costs nothing */
|
|
69
76
|
leastBusy() {
|
|
70
77
|
if (this.workers.length === 0) {
|
|
71
|
-
|
|
78
|
+
Array.from({ length: this.size }).forEach(() => {
|
|
72
79
|
const worker = new Worker(this.workerUrl, { type: 'module' });
|
|
73
80
|
worker.onmessage = (event) => this.receive(worker, event.data);
|
|
74
81
|
// A worker that fails to load (a bundler serving it from elsewhere) or to read a message fails its requests
|
|
@@ -84,14 +91,10 @@ export class TileLoader {
|
|
|
84
91
|
this.send(worker, { type: 'headers', rules });
|
|
85
92
|
this.workers.push(worker);
|
|
86
93
|
this.busy.set(worker, 0);
|
|
87
|
-
}
|
|
94
|
+
});
|
|
88
95
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if ((this.busy.get(worker) ?? 0) < (this.busy.get(best) ?? 0))
|
|
92
|
-
best = worker;
|
|
93
|
-
}
|
|
94
|
-
return best;
|
|
96
|
+
const load = (worker) => this.busy.get(worker) ?? 0;
|
|
97
|
+
return this.workers.reduce((best, worker) => (load(worker) < load(best) ? worker : best));
|
|
95
98
|
}
|
|
96
99
|
send(worker, message) {
|
|
97
100
|
worker.postMessage(message);
|
|
@@ -114,12 +117,12 @@ export class TileLoader {
|
|
|
114
117
|
/** Rejects the requests of a failed worker */
|
|
115
118
|
fail(worker, message) {
|
|
116
119
|
const error = new Error(`isard: ${message}. If a bundler moves the worker, give its URL in the \`workerUrl\` option (see "With a bundler" in the guide)`);
|
|
117
|
-
|
|
120
|
+
this.pending.forEach((pending, id) => {
|
|
118
121
|
if (pending.worker !== worker)
|
|
119
|
-
|
|
122
|
+
return;
|
|
120
123
|
this.pending.delete(id);
|
|
121
124
|
pending.reject(error);
|
|
122
|
-
}
|
|
125
|
+
});
|
|
123
126
|
// Out of the pool: when none is left, the next requests fail at once
|
|
124
127
|
worker.terminate();
|
|
125
128
|
this.workers = this.workers.filter((w) => w !== worker);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/engine/loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pool of decoding workers for 3D Tiles content and point clouds. The engine calls `load`, `loadPoints` and `abort`\n * (`TileLoaderLike`); the main thread then only uploads the decoded arrays to the GPU.\n */\n\nimport type { DecodedTile, TextureFormats, TileLoaderLike } from '../wasm/isard.js';\nimport type { DecoderUrls } from './decoders.ts';\nimport type { WorkerReply, WorkerRequest } from './protocol.ts';\nimport { headerRules, onHeaderRules } from './requests.ts';\n\ninterface Pending {\n resolve: (result: DecodedTile | Uint8Array<ArrayBuffer>) => void;\n reject: (error: Error) => void;\n worker: Worker;\n}\n\nexport class TileLoader implements TileLoaderLike {\n private workers: Worker[] = [];\n /** Requests in flight per worker */\n private readonly busy = new Map<Worker, number>();\n private readonly pending = new Map<number, Pending>();\n /** Every worker failed: why */\n private failed: Error | undefined;\n /** The page's request headers, sent to the workers as they change */\n private readonly unfollow = onHeaderRules((rules) => {\n this.workers.forEach((worker) => {\n this.send(worker, { type: 'headers', rules });\n });\n });\n\n constructor(\n private readonly module: WebAssembly.Module,\n private readonly workerUrl: string | URL,\n private readonly size: number,\n private readonly decoders: DecoderUrls,\n private readonly formats: TextureFormats,\n /** URL of the point cloud module, for the workers */\n private readonly points: string = '',\n ) {}\n\n /** Called by the engine: fetch and decode off the main thread, on the least busy worker */\n load(\n id: number,\n url: string,\n transform: Float64Array,\n yUp: boolean,\n reload: boolean,\n priority?: 'high' | 'low',\n ): Promise<DecodedTile> {\n if (this.failed) return Promise.reject(this.failed);\n\n const worker = this.leastBusy();\n\n this.busy.set(worker, (this.busy.get(worker) ?? 0) + 1);\n\n return new Promise((resolve, reject) => {\n this.pending.set(id, { resolve: resolve as Pending['resolve'], reject, worker });\n this.send(worker, { type: 'load', id, url, transform, yUp, reload, ...(priority ? { priority } : {}) });\n });\n }\n\n /** Called by the engine: the points of a COPC node, fetched (a range request) and decoded off the main thread */\n loadPoints(\n id: number,\n url: string,\n offset: number,\n size: number,\n laz: Uint8Array,\n layout: Float64Array,\n grid: Float64Array,\n count: number,\n priority?: 'high' | 'low',\n ): Promise<Uint8Array<ArrayBuffer>> {\n if (this.failed) return Promise.reject(this.failed);\n\n const worker = this.leastBusy();\n\n this.busy.set(worker, (this.busy.get(worker) ?? 0) + 1);\n\n return new Promise((resolve, reject) => {\n this.pending.set(id, { resolve: resolve as Pending['resolve'], reject, worker });\n\n const message = { type: 'points', id, url, offset, size, laz, layout, grid, count } as const;\n\n this.send(worker, { ...message, ...(priority ? { priority } : {}) });\n });\n }\n\n abort(id: number): void {\n const pending = this.pending.get(id);\n\n if (pending) this.send(pending.worker, { type: 'abort', id });\n }\n\n destroy(): void {\n this.unfollow();\n this.workers.forEach((worker) => {\n worker.terminate();\n });\n this.workers = [];\n this.busy.clear();\n this.pending.forEach(({ reject }) => {\n reject(new Error('loader destroyed'));\n });\n this.pending.clear();\n }\n\n /** Workers start with the first tile: a map without 3D Tiles costs nothing */\n private leastBusy(): Worker {\n if (this.workers.length === 0) {\n Array.from({ length: this.size }).forEach(() => {\n const worker = new Worker(this.workerUrl, { type: 'module' });\n\n worker.onmessage = (event: MessageEvent<WorkerReply>) => this.receive(worker, event.data);\n // A worker that fails to load (a bundler serving it from elsewhere) or to read a message fails its requests\n worker.onerror = (event) => {\n event.preventDefault();\n this.fail(worker, `the decoding worker ${this.workerUrl} failed${event.message ? `: ${event.message}` : ''}`);\n };\n worker.onmessageerror = () =>\n this.fail(worker, `the decoding worker ${this.workerUrl} sent an unreadable message`);\n\n const { module, decoders, formats, points } = this;\n\n this.send(worker, { type: 'init', module, decoders, formats, points });\n\n const rules = headerRules();\n\n if (rules.length > 0) this.send(worker, { type: 'headers', rules });\n this.workers.push(worker);\n this.busy.set(worker, 0);\n });\n }\n\n const load = (worker: Worker) => this.busy.get(worker) ?? 0;\n\n return this.workers.reduce((best, worker) => (load(worker) < load(best) ? worker : best));\n }\n\n private send(worker: Worker, message: WorkerRequest): void {\n worker.postMessage(message);\n }\n\n private receive(worker: Worker, reply: WorkerReply): void {\n this.busy.set(worker, Math.max(0, (this.busy.get(worker) ?? 1) - 1));\n\n const pending = this.pending.get(reply.id);\n\n if (!pending) return;\n this.pending.delete(reply.id);\n if (reply.type === 'tile') pending.resolve(reply.tile);\n else if (reply.type === 'points') pending.resolve(reply.vertices);\n else if (reply.aborted) pending.reject(new DOMException(`tile ${reply.id} aborted`, 'AbortError'));\n else pending.reject(new Error(reply.error));\n }\n\n /** Rejects the requests of a failed worker */\n private fail(worker: Worker, message: string): void {\n const error = new Error(\n `isard: ${message}. If a bundler moves the worker, give its URL in the \\`workerUrl\\` option (see \"With a bundler\" in the guide)`,\n );\n\n this.pending.forEach((pending, id) => {\n if (pending.worker !== worker) return;\n this.pending.delete(id);\n pending.reject(error);\n });\n // Out of the pool: when none is left, the next requests fail at once\n worker.terminate();\n this.workers = this.workers.filter((w) => w !== worker);\n this.busy.delete(worker);\n if (this.workers.length === 0) this.failed = error;\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Messages between the main thread and the decoding workers */
|
|
2
|
+
import type { DecodedTile, TextureFormats } from '../wasm/isard.js';
|
|
2
3
|
import type { DecoderUrls } from './decoders.ts';
|
|
3
4
|
import type { HeaderRules } from './requests.ts';
|
|
4
|
-
import type { DecodedTile, TextureFormats } from './wasm/isard.js';
|
|
5
5
|
export type WorkerRequest =
|
|
6
6
|
/** The page's request headers (`setRequestHeaders`) */
|
|
7
7
|
{
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/engine/protocol.ts"],"names":[],"mappings":"AAAA,gEAAgE","sourcesContent":["/** Messages between the main thread and the decoding workers */\n\nimport type { DecodedTile, TextureFormats } from '../wasm/isard.js';\nimport type { DecoderUrls } from './decoders.ts';\nimport type { HeaderRules } from './requests.ts';\n\nexport type WorkerRequest =\n /** The page's request headers (`setRequestHeaders`) */\n | { type: 'headers'; rules: HeaderRules }\n | {\n type: 'init';\n module: WebAssembly.Module;\n decoders: DecoderUrls;\n formats: TextureFormats;\n /** URL of the point cloud module (`isard_points_bg.wasm`), loaded with the first COPC chunk */\n points: string;\n }\n | {\n type: 'load';\n id: number;\n url: string;\n transform: Float64Array;\n yUp: boolean;\n reload: boolean;\n priority?: 'high' | 'low';\n }\n | {\n type: 'points';\n id: number;\n url: string;\n offset: number;\n size: number;\n laz: Uint8Array;\n layout: Float64Array;\n grid: Float64Array;\n count: number;\n priority?: 'high' | 'low';\n }\n | { type: 'abort'; id: number };\n\nexport type WorkerReply =\n | { type: 'tile'; id: number; tile: DecodedTile }\n | { type: 'points'; id: number; vertices: Uint8Array<ArrayBuffer> }\n | { type: 'error'; id: number; error: string; aborted: boolean };\n"]}
|
|
@@ -11,4 +11,4 @@ export type HeaderRules = [prefix: string, headers: Record<string, string>][];
|
|
|
11
11
|
* no headers, stops: `setRequestHeaders('https://data.geopf.fr/private/', { Authorization: 'Bearer …' })`. The
|
|
12
12
|
* server must allow these headers (CORS). For the whole page: every map, the ones to come too
|
|
13
13
|
*/
|
|
14
|
-
export declare
|
|
14
|
+
export declare const setRequestHeaders: (prefix: string, headers?: Record<string, string>) => void;
|
|
@@ -11,38 +11,41 @@ const listeners = new Set();
|
|
|
11
11
|
* no headers, stops: `setRequestHeaders('https://data.geopf.fr/private/', { Authorization: 'Bearer …' })`. The
|
|
12
12
|
* server must allow these headers (CORS). For the whole page: every map, the ones to come too
|
|
13
13
|
*/
|
|
14
|
-
export
|
|
14
|
+
export const setRequestHeaders = (prefix, headers) => {
|
|
15
15
|
const kept = rules.filter(([p]) => p !== prefix);
|
|
16
16
|
if (headers && Object.keys(headers).length > 0)
|
|
17
17
|
kept.push([prefix, { ...headers }]);
|
|
18
18
|
kept.sort(([p], [q]) => p.length - q.length);
|
|
19
19
|
setHeaderRules(kept);
|
|
20
|
-
|
|
20
|
+
listeners.forEach((listener) => {
|
|
21
21
|
listener(rules);
|
|
22
|
-
}
|
|
22
|
+
});
|
|
23
|
+
};
|
|
23
24
|
/** @internal The rules now */
|
|
24
|
-
export
|
|
25
|
+
export const headerRules = () => {
|
|
25
26
|
return rules;
|
|
26
|
-
}
|
|
27
|
+
};
|
|
27
28
|
/** @internal The rules, from the page (a worker's copy) */
|
|
28
|
-
export
|
|
29
|
+
export const setHeaderRules = (next) => {
|
|
29
30
|
rules = next;
|
|
30
|
-
}
|
|
31
|
+
};
|
|
31
32
|
/** @internal Follows the rules (the workers and engines of a map); returns the function that stops */
|
|
32
|
-
export
|
|
33
|
+
export const onHeaderRules = (listener) => {
|
|
33
34
|
listeners.add(listener);
|
|
34
35
|
return () => listeners.delete(listener);
|
|
35
|
-
}
|
|
36
|
+
};
|
|
36
37
|
/** @internal `init` with the headers of the rules matching `url` (unchanged when none does) */
|
|
37
|
-
export
|
|
38
|
+
export const withHeaders = (url, init = {}) => {
|
|
38
39
|
const href = String(url);
|
|
39
40
|
const matching = rules.filter(([prefix]) => href.startsWith(prefix));
|
|
40
41
|
if (matching.length === 0)
|
|
41
|
-
return init
|
|
42
|
-
const headers = new Headers(init
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
return init;
|
|
43
|
+
const headers = new Headers(init.headers);
|
|
44
|
+
matching.forEach(([, set]) => {
|
|
45
|
+
Object.entries(set).forEach(([name, value]) => {
|
|
45
46
|
headers.set(name, value);
|
|
46
|
-
|
|
47
|
-
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
return { ...init, headers };
|
|
50
|
+
};
|
|
48
51
|
//# sourceMappingURL=requests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requests.js","sourceRoot":"","sources":["../../src/engine/requests.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,IAAI,KAAK,GAAgB,EAAE,CAAC;AAC5B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAc,EAAE,OAAgC,EAAQ,EAAE;IAC1F,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC;IAEjD,IAAI,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;IACpF,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAC7C,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QAC7B,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,8BAA8B;AAC9B,MAAM,CAAC,MAAM,WAAW,GAAG,GAAgB,EAAE;IAC3C,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAiB,EAAQ,EAAE;IACxD,KAAK,GAAG,IAAI,CAAC;AACf,CAAC,CAAC;AAEF,sGAAsG;AACtG,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAsC,EAAgB,EAAE;IACpF,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAExB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC,CAAC;AAEF,+FAA+F;AAC/F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAiB,EAAE,OAAoB,EAAE,EAAe,EAAE;IACpF,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAErE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE1C,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE;QAC3B,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE;YAC5C,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC,CAAC","sourcesContent":["/**\n * Headers added to the requests of the page by URL prefix: an access token for private sources (a Géoplateforme\n * private stream, a server behind single sign-on), on every request isard makes, from the page, its workers (vector\n * tiles, 3D Tiles and point clouds) and its engine (raster tiles, tilesets): `setRequestHeaders(prefix, headers)`,\n * again with a new token when it is renewed. The rules of a URL are merged, the longest prefix last (it wins).\n */\n\n/** Rules: a URL prefix and its headers, the shortest prefix first */\nexport type HeaderRules = [prefix: string, headers: Record<string, string>][];\n\nlet rules: HeaderRules = [];\nconst listeners = new Set<(rules: HeaderRules) => void>();\n\n/**\n * Adds `headers` to every request isard makes to a URL starting with `prefix` (in the page and its workers), or with\n * no headers, stops: `setRequestHeaders('https://data.geopf.fr/private/', { Authorization: 'Bearer …' })`. The\n * server must allow these headers (CORS). For the whole page: every map, the ones to come too\n */\nexport const setRequestHeaders = (prefix: string, headers?: Record<string, string>): void => {\n const kept = rules.filter(([p]) => p !== prefix);\n\n if (headers && Object.keys(headers).length > 0) kept.push([prefix, { ...headers }]);\n kept.sort(([p], [q]) => p.length - q.length);\n setHeaderRules(kept);\n listeners.forEach((listener) => {\n listener(rules);\n });\n};\n\n/** @internal The rules now */\nexport const headerRules = (): HeaderRules => {\n return rules;\n};\n\n/** @internal The rules, from the page (a worker's copy) */\nexport const setHeaderRules = (next: HeaderRules): void => {\n rules = next;\n};\n\n/** @internal Follows the rules (the workers and engines of a map); returns the function that stops */\nexport const onHeaderRules = (listener: (rules: HeaderRules) => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n};\n\n/** @internal `init` with the headers of the rules matching `url` (unchanged when none does) */\nexport const withHeaders = (url: string | URL, init: RequestInit = {}): RequestInit => {\n const href = String(url);\n const matching = rules.filter(([prefix]) => href.startsWith(prefix));\n\n if (matching.length === 0) return init;\n\n const headers = new Headers(init.headers);\n\n matching.forEach(([, set]) => {\n Object.entries(set).forEach(([name, value]) => {\n headers.set(name, value);\n });\n });\n\n return { ...init, headers };\n};\n"]}
|
package/dist/engine-loader.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The two builds of the engine, downloaded, compiled and instantiated once per page; the decoding workers of 3D Tiles
|
|
3
|
+
* that run the full one; the engine of a destroyed map.
|
|
4
|
+
*/
|
|
5
|
+
import { TileLoader } from './engine/loader.ts';
|
|
2
6
|
import type { GeoMapOptions } from './types.ts';
|
|
3
7
|
import type { Camera, Engine } from './wasm/isard.js';
|
|
4
8
|
/** The class of an engine build: its constructor and its static `customMatrix` (custom layers) */
|
|
@@ -16,23 +20,23 @@ export type AssetFile = 'isard_bg.wasm' | 'isard_core_bg.wasm' | 'isard_points_b
|
|
|
16
20
|
* Where a file of isard is loaded from: its own option, else in the folder `assetsUrl` (the option, or the page's
|
|
17
21
|
* as `isard/vite` sets it), else next to the module (`import.meta.url`)
|
|
18
22
|
*/
|
|
19
|
-
export declare
|
|
23
|
+
export declare const assetUrl: (file: AssetFile, explicit: string | URL | undefined, assetsUrl?: string | URL) => string | URL;
|
|
20
24
|
/** Where a build of the engine is loaded from: the option, the assets' folder, or next to the module */
|
|
21
|
-
export declare
|
|
25
|
+
export declare const engineUrl: (kind: EngineKind, options: Pick<GeoMapOptions, "wasmUrl" | "coreWasmUrl" | "assetsUrl">) => string | URL;
|
|
22
26
|
/** A build compiled and instantiated with its bindings (once per page, unless it failed) */
|
|
23
|
-
export declare
|
|
27
|
+
export declare const loadEngine: (kind: EngineKind, url: string | URL) => Promise<Bindings>;
|
|
24
28
|
/** The full engine for a map on the core one, resolved in a task of its own, not in the one that instantiated it:
|
|
25
29
|
* the map rebuilt on it there, two shorter tasks */
|
|
26
|
-
export declare
|
|
30
|
+
export declare const loadFullEngineLater: (url: string | URL) => Promise<Bindings>;
|
|
27
31
|
/** Downloads and compiles the full engine when the page is idle, without running it; a failure is left to the first
|
|
28
32
|
* 3D Tiles layer, which tries again */
|
|
29
|
-
export declare
|
|
33
|
+
export declare const preloadFullEngine: (url: string | URL, wanted: () => boolean) => void;
|
|
30
34
|
/** Where the workers load the point cloud module from (`isard_points_bg.wasm`, the LAZ decoder): the option, or next
|
|
31
35
|
* to the module; absolute, the workers being elsewhere */
|
|
32
|
-
export declare
|
|
36
|
+
export declare const pointsUrl: (options: Pick<GeoMapOptions, "pointsWasmUrl" | "assetsUrl">) => string;
|
|
33
37
|
/** The decoding workers of 3D Tiles and point clouds, running the full engine's module, unless turned off (`workers:
|
|
34
38
|
* false` or 0, or no workers in this environment) */
|
|
35
|
-
export declare
|
|
39
|
+
export declare const startTileLoader: (module: WebAssembly.Module, options: Pick<GeoMapOptions, "workers" | "workerUrl" | "decoders" | "pointsWasmUrl" | "assetsUrl">, engine: Engine) => TileLoader | undefined;
|
|
36
40
|
/** The engine of a destroyed map: every method a no-op returning undefined, but the camera (the last one) and the
|
|
37
41
|
* bounds, which late callers read */
|
|
38
|
-
export declare
|
|
42
|
+
export declare const destroyedEngine: (camera: Camera) => Engine;
|
package/dist/engine-loader.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* The two builds of the engine, downloaded, compiled and instantiated once per page; the decoding workers of 3D Tiles
|
|
3
3
|
* that run the full one; the engine of a destroyed map.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { defaultWorkerCount } from "./basemap/vectortiles.js";
|
|
6
|
+
import { DEFAULT_DECODERS } from "./engine/decoders.js";
|
|
7
|
+
import { TileLoader } from "./engine/loader.js";
|
|
8
8
|
/** The two builds of the engine, each compiled once per page: `core` without 3D Tiles (a third smaller), loaded by
|
|
9
9
|
* every map; `full` with them, loaded for the first 3D Tiles layer, its module shared with the decoding workers. The
|
|
10
10
|
* core engine has the same API but the 3D Tiles methods: its class is typed as the full one. */
|
|
@@ -13,22 +13,22 @@ const engines = {};
|
|
|
13
13
|
* compiles the full engine ahead, its first 3D Tiles layer then only instantiates it */
|
|
14
14
|
const modules = {};
|
|
15
15
|
/** @internal Forgets the engines loaded (tests) */
|
|
16
|
-
export
|
|
17
|
-
|
|
16
|
+
export const forgetEngines = () => {
|
|
17
|
+
['core', 'full'].forEach((kind) => {
|
|
18
18
|
delete engines[kind];
|
|
19
19
|
delete modules[kind];
|
|
20
|
-
}
|
|
21
|
-
}
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
22
|
/** The folder the assets are served from for the whole page, as `isard/vite` sets it; undefined: next to the module */
|
|
23
|
-
|
|
24
|
-
const url = globalThis
|
|
23
|
+
const pageAssets = () => {
|
|
24
|
+
const url = Reflect.get(globalThis, 'isardAssetsUrl');
|
|
25
25
|
return typeof url === 'string' ? url : undefined;
|
|
26
|
-
}
|
|
26
|
+
};
|
|
27
27
|
/**
|
|
28
28
|
* Where a file of isard is loaded from: its own option, else in the folder `assetsUrl` (the option, or the page's
|
|
29
29
|
* as `isard/vite` sets it), else next to the module (`import.meta.url`)
|
|
30
30
|
*/
|
|
31
|
-
export
|
|
31
|
+
export const assetUrl = (file, explicit, assetsUrl) => {
|
|
32
32
|
if (explicit)
|
|
33
33
|
return explicit;
|
|
34
34
|
const folder = assetsUrl ?? pageAssets();
|
|
@@ -49,15 +49,15 @@ export function assetUrl(file, explicit, assetsUrl) {
|
|
|
49
49
|
default:
|
|
50
50
|
return new URL('./basemap.worker.js', import.meta.url);
|
|
51
51
|
}
|
|
52
|
-
}
|
|
52
|
+
};
|
|
53
53
|
/** Where a build of the engine is loaded from: the option, the assets' folder, or next to the module */
|
|
54
|
-
export
|
|
54
|
+
export const engineUrl = (kind, options) => {
|
|
55
55
|
return kind === 'full'
|
|
56
56
|
? assetUrl('isard_bg.wasm', options.wasmUrl, options.assetsUrl)
|
|
57
57
|
: assetUrl('isard_core_bg.wasm', options.coreWasmUrl, options.assetsUrl);
|
|
58
|
-
}
|
|
58
|
+
};
|
|
59
59
|
/** Downloads and compiles a build (once per page, unless it failed: a later call tries again) */
|
|
60
|
-
|
|
60
|
+
const compileEngine = (kind, url) => {
|
|
61
61
|
const compiled = modules[kind];
|
|
62
62
|
if (compiled)
|
|
63
63
|
return compiled;
|
|
@@ -68,9 +68,9 @@ function compileEngine(kind, url) {
|
|
|
68
68
|
});
|
|
69
69
|
modules[kind] = compiling;
|
|
70
70
|
return compiling;
|
|
71
|
-
}
|
|
71
|
+
};
|
|
72
72
|
/** A build compiled and instantiated with its bindings (once per page, unless it failed) */
|
|
73
|
-
export
|
|
73
|
+
export const loadEngine = (kind, url) => {
|
|
74
74
|
const loaded = engines[kind];
|
|
75
75
|
if (loaded)
|
|
76
76
|
return loaded;
|
|
@@ -88,15 +88,15 @@ export function loadEngine(kind, url) {
|
|
|
88
88
|
});
|
|
89
89
|
engines[kind] = loading;
|
|
90
90
|
return loading;
|
|
91
|
-
}
|
|
91
|
+
};
|
|
92
92
|
/** The full engine for a map on the core one, resolved in a task of its own, not in the one that instantiated it:
|
|
93
93
|
* the map rebuilt on it there, two shorter tasks */
|
|
94
|
-
export
|
|
94
|
+
export const loadFullEngineLater = (url) => {
|
|
95
95
|
return loadEngine('full', url).then((bindings) => new Promise((resolve) => setTimeout(() => resolve(bindings), 0)));
|
|
96
|
-
}
|
|
96
|
+
};
|
|
97
97
|
/** Downloads and compiles the full engine when the page is idle, without running it; a failure is left to the first
|
|
98
98
|
* 3D Tiles layer, which tries again */
|
|
99
|
-
export
|
|
99
|
+
export const preloadFullEngine = (url, wanted) => {
|
|
100
100
|
const start = () => {
|
|
101
101
|
if (wanted())
|
|
102
102
|
compileEngine('full', url).catch(() => undefined);
|
|
@@ -105,8 +105,8 @@ export function preloadFullEngine(url, wanted) {
|
|
|
105
105
|
requestIdleCallback(start, { timeout: 2000 });
|
|
106
106
|
else
|
|
107
107
|
setTimeout(start, 0);
|
|
108
|
-
}
|
|
109
|
-
async
|
|
108
|
+
};
|
|
109
|
+
const compile = async (url) => {
|
|
110
110
|
const response = await fetch(url);
|
|
111
111
|
if (!response.ok)
|
|
112
112
|
throw new Error(`isard: failed to load the engine from ${url}: ${response.status}`);
|
|
@@ -117,26 +117,26 @@ async function compile(url) {
|
|
|
117
117
|
// Servers that do not send application/wasm
|
|
118
118
|
return WebAssembly.compile(await response.arrayBuffer());
|
|
119
119
|
}
|
|
120
|
-
}
|
|
120
|
+
};
|
|
121
121
|
/** Where the workers load the point cloud module from (`isard_points_bg.wasm`, the LAZ decoder): the option, or next
|
|
122
122
|
* to the module; absolute, the workers being elsewhere */
|
|
123
|
-
export
|
|
123
|
+
export const pointsUrl = (options) => {
|
|
124
124
|
const url = assetUrl('isard_points_bg.wasm', options.pointsWasmUrl, options.assetsUrl);
|
|
125
125
|
const base = typeof document === 'undefined' ? undefined : document.baseURI;
|
|
126
126
|
return base ? new URL(url, base).href : String(url);
|
|
127
|
-
}
|
|
127
|
+
};
|
|
128
128
|
/** The decoding workers of 3D Tiles and point clouds, running the full engine's module, unless turned off (`workers:
|
|
129
129
|
* false` or 0, or no workers in this environment) */
|
|
130
|
-
export
|
|
130
|
+
export const startTileLoader = (module, options, engine) => {
|
|
131
131
|
const { workers: count, workerUrl, decoders } = options;
|
|
132
132
|
const workers = count ?? defaultWorkerCount();
|
|
133
133
|
if (workers === false || workers <= 0 || typeof Worker === 'undefined')
|
|
134
134
|
return undefined;
|
|
135
135
|
return new TileLoader(module, assetUrl('decoder.worker.js', workerUrl, options.assetsUrl), workers, { ...DEFAULT_DECODERS, ...decoders }, engine.textureFormats(), pointsUrl(options));
|
|
136
|
-
}
|
|
136
|
+
};
|
|
137
137
|
/** The engine of a destroyed map: every method a no-op returning undefined, but the camera (the last one) and the
|
|
138
138
|
* bounds, which late callers read */
|
|
139
|
-
export
|
|
139
|
+
export const destroyedEngine = (camera) => {
|
|
140
140
|
return new Proxy({}, {
|
|
141
141
|
get: (_, name) => {
|
|
142
142
|
if (name === 'camera')
|
|
@@ -146,5 +146,5 @@ export function destroyedEngine(camera) {
|
|
|
146
146
|
return () => undefined;
|
|
147
147
|
},
|
|
148
148
|
});
|
|
149
|
-
}
|
|
149
|
+
};
|
|
150
150
|
//# sourceMappingURL=engine-loader.js.map
|