isard 1.15.0 → 2.0.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 +70 -0
- package/README.md +4 -4
- package/dist/basemap/vectortiles/placed.js +7 -4
- package/dist/basemap/vectortiles/placed.js.map +1 -1
- package/dist/basemap/vectortiles/tiles.d.ts +1 -1
- package/dist/basemap/vectortiles/tiles.js.map +1 -1
- package/dist/basemap/workerpool.d.ts +1 -1
- package/dist/basemap/workerpool.js.map +1 -1
- package/dist/bind/layers.js +3 -1
- package/dist/bind/layers.js.map +1 -1
- package/dist/decoder.worker.js +4 -5
- package/dist/decoder.worker.js.map +6 -6
- package/dist/decoding/decoders.d.ts +1 -1
- package/dist/decoding/decoders.js.map +1 -1
- package/dist/decoding/loader.d.ts +6 -3
- package/dist/decoding/loader.js +7 -5
- package/dist/decoding/loader.js.map +1 -1
- package/dist/decoding/protocol.d.ts +3 -2
- package/dist/decoding/protocol.js.map +1 -1
- package/dist/engine-loader.d.ts +20 -18
- package/dist/engine-loader.js +45 -42
- package/dist/engine-loader.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js.map +1 -1
- package/dist/isard_tess_bg.wasm +0 -0
- package/dist/isard_tiles3d_bg.wasm +0 -0
- package/dist/isard_webgl2_bg.wasm +0 -0
- package/dist/isard_webgpu_bg.wasm +0 -0
- package/dist/layers/custom.d.ts +35 -29
- package/dist/layers/custom.js +60 -46
- package/dist/layers/custom.js.map +1 -1
- package/dist/layers/extrusions.js.map +1 -1
- package/dist/layers/features.js.map +1 -1
- package/dist/layers/flow.js.map +1 -1
- package/dist/layers/glyphs.d.ts +1 -1
- package/dist/layers/glyphs.js.map +1 -1
- package/dist/layers/ground.d.ts +20 -13
- package/dist/layers/ground.js +29 -8
- package/dist/layers/ground.js.map +1 -1
- package/dist/layers/icons.d.ts +1 -1
- package/dist/layers/icons.js.map +1 -1
- package/dist/layers/layers.d.ts +1 -2
- package/dist/layers/layers.js +2 -11
- package/dist/layers/layers.js.map +1 -1
- package/dist/layers/models.js +0 -4
- package/dist/layers/models.js.map +1 -1
- package/dist/layers/network.d.ts +1 -1
- package/dist/layers/network.js.map +1 -1
- package/dist/layers/panoramas.d.ts +1 -0
- package/dist/layers/panoramas.js +3 -2
- package/dist/layers/panoramas.js.map +1 -1
- package/dist/layers/pointclouds.d.ts +1 -1
- package/dist/layers/pointclouds.js +0 -4
- package/dist/layers/pointclouds.js.map +1 -1
- package/dist/layers/raster.js +2 -2
- package/dist/layers/raster.js.map +1 -1
- package/dist/layers/symbols.js.map +1 -1
- package/dist/layers/tiles3d.d.ts +6 -2
- package/dist/layers/tiles3d.js +23 -5
- package/dist/layers/tiles3d.js.map +1 -1
- package/dist/layers/vectors.d.ts +4 -5
- package/dist/layers/vectors.js +7 -15
- package/dist/layers/vectors.js.map +1 -1
- package/dist/map/engine-settings.d.ts +1 -1
- package/dist/map/engine-settings.js.map +1 -1
- package/dist/map/engines.d.ts +11 -26
- package/dist/map/engines.js +10 -48
- package/dist/map/engines.js.map +1 -1
- package/dist/map/frame-loop.d.ts +1 -3
- package/dist/map/frame-loop.js +3 -3
- package/dist/map/frame-loop.js.map +1 -1
- package/dist/map/hover-pick.d.ts +9 -3
- package/dist/map/hover-pick.js +29 -6
- package/dist/map/hover-pick.js.map +1 -1
- package/dist/map/layer-stack.d.ts +2 -2
- package/dist/map/layer-stack.js +1 -3
- package/dist/map/layer-stack.js.map +1 -1
- package/dist/map/symbol-atlases.d.ts +5 -4
- package/dist/map/symbol-atlases.js +8 -13
- package/dist/map/symbol-atlases.js.map +1 -1
- package/dist/map/viewshed.d.ts +1 -1
- package/dist/map/viewshed.js.map +1 -1
- package/dist/map.d.ts +26 -20
- package/dist/map.js +97 -82
- package/dist/map.js.map +1 -1
- package/dist/react/IsardMap.d.ts +1 -1
- package/dist/react/IsardMap.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/types.d.ts +49 -18
- package/dist/types.js.map +1 -1
- package/dist/ui/interaction.d.ts +1 -1
- package/dist/ui/interaction.js.map +1 -1
- package/dist/util/animation.d.ts +1 -1
- package/dist/util/animation.js.map +1 -1
- package/dist/wasm/isard_tiles3d.d.ts +181 -0
- package/dist/wasm/isard_tiles3d.js +387 -0
- package/dist/wasm/{isard_core.d.ts → isard_webgl2.d.ts} +375 -184
- package/dist/wasm/{isard_core.js → isard_webgl2.js} +2167 -415
- package/dist/wasm/{isard.d.ts → isard_webgpu.d.ts} +183 -355
- package/dist/wasm/{isard.js → isard_webgpu.js} +1374 -1097
- package/package.json +4 -3
- package/vite.js +5 -4
- package/dist/isard_bg.wasm +0 -0
- package/dist/isard_core_bg.wasm +0 -0
package/dist/map/viewshed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewshed.js","sourceRoot":"","sources":["../../src/map/viewshed.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAE5E,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAIrC,gEAAgE;AAChE,MAAM,WAAW,GAAG,CAAC,OAAiE,EAA8B,EAAE;IACpH,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,OAAO,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AACrD,CAAC,CAAC;AAEF,4GAA4G;AAC5G,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,MAAc,EACd,OAAiE,EACxD,EAAE;IACX,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAE1B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,CAAC,aAAa,EAAE,CAAC;QAEvB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,CAAC,WAAW,CAChB,IAAI,YAAY,CACd,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACvB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACb,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACb,CAAC,CAAC,MAAM,IAAI,GAAG;QACf,CAAC,CAAC,KAAK,IAAI,IAAI;QACf,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS;YAC3B,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACvB,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;KAClE,CAAC,CACH,EACD,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,SAAS,CAAC,CAAC,EACtD,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,SAAS,CAAC,CAAC,EACrD,KAAK,CAAC,OAAO,IAAI,GAAG,EACpB,IAAI,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAC9D,CAAC;IAEF,OAAO,IAAI,CAAC;AACd,CAAC,CAAC","sourcesContent":["/** The viewshed's observers given to the engine, in the arrays it reads. */\n\nimport { rgb } from '../settings.ts';\nimport type { ViewshedOptions } from '../types.ts';\nimport type { Engine } from '../wasm/
|
|
1
|
+
{"version":3,"file":"viewshed.js","sourceRoot":"","sources":["../../src/map/viewshed.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAE5E,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAIrC,gEAAgE;AAChE,MAAM,WAAW,GAAG,CAAC,OAAiE,EAA8B,EAAE;IACpH,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,OAAO,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AACrD,CAAC,CAAC;AAEF,4GAA4G;AAC5G,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,MAAc,EACd,OAAiE,EACxD,EAAE;IACX,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAE1B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,CAAC,aAAa,EAAE,CAAC;QAEvB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,CAAC,WAAW,CAChB,IAAI,YAAY,CACd,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACvB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACb,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACb,CAAC,CAAC,MAAM,IAAI,GAAG;QACf,CAAC,CAAC,KAAK,IAAI,IAAI;QACf,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS;YAC3B,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACvB,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;KAClE,CAAC,CACH,EACD,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,SAAS,CAAC,CAAC,EACtD,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,SAAS,CAAC,CAAC,EACrD,KAAK,CAAC,OAAO,IAAI,GAAG,EACpB,IAAI,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAC9D,CAAC;IAEF,OAAO,IAAI,CAAC;AACd,CAAC,CAAC","sourcesContent":["/** The viewshed's observers given to the engine, in the arrays it reads. */\n\nimport { rgb } from '../settings.ts';\nimport type { ViewshedOptions } from '../types.ts';\nimport type { Engine } from '../wasm/isard_webgpu.js';\n\n/** The observers of a viewshed option: none, one, or several */\nconst observersOf = (options: ViewshedOptions | readonly ViewshedOptions[] | undefined): readonly ViewshedOptions[] => {\n if (!options) return [];\n\n return 'position' in options ? [options] : options;\n};\n\n/** Sets the viewshed of the engine, or clears it without observers; true if one is set (a frame to draw) */\nexport const applyViewshed = (\n engine: Engine,\n options: ViewshedOptions | readonly ViewshedOptions[] | undefined,\n): boolean => {\n const observers = observersOf(options);\n const [first] = observers;\n\n if (!first) {\n engine.clearViewshed();\n\n return false;\n }\n engine.setViewshed(\n new Float64Array(\n observers.flatMap((o) => [\n o.position[0],\n o.position[1],\n o.height ?? 1.7,\n o.range ?? 1000,\n ...(o.direction === undefined\n ? [Number.NaN, 0, 0, 0]\n : [o.direction, o.tilt ?? 0, o.fov ?? 60, o.verticalFov ?? 40]),\n ]),\n ),\n new Float32Array(rgb(first.visibleColor ?? '#2f9e44')),\n new Float32Array(rgb(first.hiddenColor ?? '#e03131')),\n first.opacity ?? 0.7,\n new Float32Array(first.coneColor ? rgb(first.coneColor) : []),\n );\n\n return true;\n};\n"]}
|
package/dist/map.d.ts
CHANGED
|
@@ -26,14 +26,15 @@ import { Tiles3dLayer } from './layers/tiles3d.ts';
|
|
|
26
26
|
import { type TreeOptions } from './layers/trees.ts';
|
|
27
27
|
import { type HoverPicking } from './map/hover-pick.ts';
|
|
28
28
|
import { type LayerSpec } from './map/layer-spec.ts';
|
|
29
|
-
import type { AnimationOptions, AtmosphereOptions, Basemap, FeatureHit, FirstPersonView, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, NetworkOptions, PickOptions, Point, Projection, Quality, RasterOptions, SunOptions, SwipeOptions, TerrainModel, TerrainOptions, Tiles3dOptions, ViewshedOptions } from './types.ts';
|
|
29
|
+
import type { AnimationOptions, AtmosphereOptions, Basemap, FeatureHit, FirstPersonView, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, MapGpuTimings, NetworkOptions, PickOptions, Point, Projection, Quality, RasterOptions, SunOptions, SwipeOptions, TerrainModel, TerrainOptions, Tiles3dOptions, ViewshedOptions } from './types.ts';
|
|
30
30
|
import { type Control, type ControlPosition } from './ui/controls.ts';
|
|
31
|
-
import type { Camera, PickHit, RasterLayerStats } from './wasm/
|
|
31
|
+
import type { Camera, PickHit, RasterLayerStats } from './wasm/isard_webgpu.js';
|
|
32
32
|
export type { LayerSpec } from './map/layer-spec.ts';
|
|
33
|
-
/** A map (see `GeoMap.create`); layers take it as their host */
|
|
34
33
|
export declare class GeoMap {
|
|
35
34
|
#private;
|
|
36
35
|
readonly canvas: HTMLCanvasElement;
|
|
36
|
+
/** The frames' parts timed on the GPU (`setGpuTiming`), again on a new engine */
|
|
37
|
+
private gpuTiming;
|
|
37
38
|
/** The engine class (core, or with 3D Tiles) and the engine with 3D Tiles loaded when needed */
|
|
38
39
|
private readonly engines;
|
|
39
40
|
/** The layers in drawing order and those kept on top */
|
|
@@ -84,13 +85,6 @@ export declare class GeoMap {
|
|
|
84
85
|
private get layers();
|
|
85
86
|
/** Layers new layers go under */
|
|
86
87
|
private get onTop();
|
|
87
|
-
private createEngine;
|
|
88
|
-
/** A 3D Tiles layer or a model on the core engine: loads the engine with 3D Tiles, then rebuilds the map on it
|
|
89
|
-
* (camera, settings and layers kept, as after a context loss); the layer is created then */
|
|
90
|
-
private upgrade;
|
|
91
|
-
/** Downloads and compiles the engine with 3D Tiles once the map is idle (`tiles3d: 'preload'`), without running
|
|
92
|
-
* it: the first 3D Tiles layer then only instantiates it */
|
|
93
|
-
private preloadTiles3d;
|
|
94
88
|
/**
|
|
95
89
|
* The context is lost: its engine freed at once, the map's calls going to one that does nothing until `restore`
|
|
96
90
|
* builds the next. The old engine's GPU objects went with the context: deleted now, it is silent (on the context
|
|
@@ -98,8 +92,12 @@ export declare class GeoMap {
|
|
|
98
92
|
* would fail on the lost context, whose limits read 0
|
|
99
93
|
*/
|
|
100
94
|
private dropLostEngine;
|
|
101
|
-
/** After a context loss: a new engine with the same camera, settings and layers (same handles, same order)
|
|
95
|
+
/** After a context loss: a new engine with the same camera, settings and layers (same handles, same order); the
|
|
96
|
+
* engine made asynchronously, adopted once there */
|
|
102
97
|
private restore;
|
|
98
|
+
/** A new engine in place of the map's (a placeholder, or a lost one): the same camera, settings and layers (same
|
|
99
|
+
* handles, same order) */
|
|
100
|
+
private adopt;
|
|
103
101
|
/** The street-level eye, if the camera is one */
|
|
104
102
|
get firstPerson(): Readonly<Required<FirstPersonView>> | undefined;
|
|
105
103
|
/**
|
|
@@ -182,18 +180,25 @@ export declare class GeoMap {
|
|
|
182
180
|
setBackground(color: string): void;
|
|
183
181
|
/** Tile counters of the raster layers, for diagnostics */
|
|
184
182
|
stats(): RasterLayerStats[];
|
|
183
|
+
/** Times the parts of each frame on the GPU, for profiling (`gpuTimings`): timer queries, read a few frames later.
|
|
184
|
+
* An engine built to profile only (`PROFILE=1 bun run build`); false without one, or if the browser cannot
|
|
185
|
+
* (`EXT_disjoint_timer_query_webgl2`) */
|
|
186
|
+
setGpuTiming(on: boolean): boolean;
|
|
187
|
+
/** The GPU time of the last frame measured (`setGpuTiming`), in all and by part: the backdrop, the ground (the raster
|
|
188
|
+
* layers over the relief), each layer, the shadows, the panorama, the viewshed (ms) */
|
|
189
|
+
gpuTimings(): MapGpuTimings | undefined;
|
|
185
190
|
/**
|
|
186
|
-
* The feature under a screen point (CSS pixels), undefined if none: its layer id and index. Reads
|
|
187
|
-
* offscreen picking pass, whatever the number of features,
|
|
188
|
-
*
|
|
191
|
+
* The feature under a screen point (CSS pixels), undefined if none: its layer id and index. Reads the pixels around
|
|
192
|
+
* the point of an offscreen picking pass, whatever the number of features, once the GPU is done (a frame or two
|
|
193
|
+
* later: the GPU is not waited for). On hover, listen to 'hoverfeature' instead.
|
|
189
194
|
*/
|
|
190
|
-
pick(point: Point, { radius, layers }?: PickOptions): PickHit | undefined
|
|
195
|
+
pick(point: Point, { radius, layers }?: PickOptions): Promise<PickHit | undefined>;
|
|
191
196
|
/**
|
|
192
197
|
* The feature under a screen point (CSS pixels), or the nearest one within `radius` pixels: its layer handle, its
|
|
193
198
|
* index there and, for line, circle and fill layers, the feature itself; undefined if none. `layers` restricts the
|
|
194
199
|
* search to some layers (the others hide nothing).
|
|
195
200
|
*/
|
|
196
|
-
pickFeature(point: Point, options?: PickOptions): FeatureHit | undefined
|
|
201
|
+
pickFeature(point: Point, options?: PickOptions): Promise<FeatureHit | undefined>;
|
|
197
202
|
/** A hit with its layer handle and feature; undefined if the layer is gone */
|
|
198
203
|
private featureHit;
|
|
199
204
|
/** Screen point (CSS pixels) of a position on the ground (the relief), or `height` metres above it, undefined
|
|
@@ -220,15 +225,16 @@ export declare class GeoMap {
|
|
|
220
225
|
* where it starts, a roof or a mesh as the relief (on by default), or around the screen center */
|
|
221
226
|
setRotateAroundPointer(enabled: boolean): void;
|
|
222
227
|
/** What is drawn under a screen point (CSS pixels), on the relief or standing on it (a roof, a wall, a mesh): its
|
|
223
|
-
* position and its height above the relief, in metres; undefined where nothing was drawn.
|
|
224
|
-
*
|
|
228
|
+
* position and its height above the relief, in metres; undefined where nothing was drawn. Read from the last copy of
|
|
229
|
+
* the scene's depth (a frame or two old, a pixel every 4 CSS pixels): at once, without waiting for the GPU */
|
|
225
230
|
pickSurface(point: Point): {
|
|
226
231
|
lonLat: LonLat;
|
|
227
232
|
height: number;
|
|
228
233
|
} | undefined;
|
|
229
234
|
/** The height above the relief, in metres, of the top of what stands at a position: a roof, a mesh, 0 on the bare
|
|
230
|
-
* ground; undefined where nothing of the last frame is there
|
|
231
|
-
* read back
|
|
235
|
+
* ground; undefined where nothing of the last frame is there, or until measured: the scene is drawn looking down at
|
|
236
|
+
* it after a frame and read back a frame or two later, the answer kept and measured again every 250 ms at most (ask
|
|
237
|
+
* again on the next `render` event) */
|
|
232
238
|
surfaceAt(lonLat: LonLat): number | undefined;
|
|
233
239
|
/** Adds a control in a corner of the map; controls stack from the corner inward */
|
|
234
240
|
addControl(control: Control, position?: ControlPosition): this;
|
package/dist/map.js
CHANGED
|
@@ -9,7 +9,7 @@ import { SEA } from "./basemap/basemaps.js";
|
|
|
9
9
|
import { pmtilesLayer } from "./basemap/pmtileslayer.js";
|
|
10
10
|
import { VectorTiles } from "./basemap/vectortiles.js";
|
|
11
11
|
import { defaultWorkerUrl } from "./basemap/workerpool.js";
|
|
12
|
-
import { destroyedEngine, engineUrl, gridUrl, loadEngine, tessUrl } from "./engine-loader.js";
|
|
12
|
+
import { destroyedEngine, engineUrl, gridUrl, loadEngine, rendererKind, tessUrl, } from "./engine-loader.js";
|
|
13
13
|
import { IGN_PRESETS } from "./geoplateforme.js";
|
|
14
14
|
import { CustomLayer } from "./layers/custom.js";
|
|
15
15
|
import { ExtrusionLayer, extrusionStyle } from "./layers/extrusions.js";
|
|
@@ -34,7 +34,7 @@ import { mapSettings, sendHeaders } from "./map/engine-settings.js";
|
|
|
34
34
|
import { MapEngines } from "./map/engines.js";
|
|
35
35
|
import { MapListeners } from "./map/events.js";
|
|
36
36
|
import { FrameLoop } from "./map/frame-loop.js";
|
|
37
|
-
import { HoverPick } from "./map/hover-pick.js";
|
|
37
|
+
import { HoverPick, pickOnce } from "./map/hover-pick.js";
|
|
38
38
|
import { addSpec } from "./map/layer-spec.js";
|
|
39
39
|
import { LayerStack } from "./map/layer-stack.js";
|
|
40
40
|
import { OverlayHost } from "./map/overlay-host.js";
|
|
@@ -67,9 +67,19 @@ const NETWORK_HALO_OPACITY = 0.11;
|
|
|
67
67
|
/** A layer whose picked features come back with their properties (lines, circles, fills, extrusions, 3D Tiles…) */
|
|
68
68
|
const hasFeatures = (layer) => 'featureAt' in layer && typeof layer.featureAt === 'function';
|
|
69
69
|
/** A map (see `GeoMap.create`); layers take it as their host */
|
|
70
|
+
/** The parts of a frame by their code in the engine's GPU timings (`Engine.gpuTimings`) */
|
|
71
|
+
const GPU_PARTS = ['backdrop', 'ground', 'layer', 'shadows', 'panorama', 'viewshed'];
|
|
72
|
+
/** Turns the GPU timing of an engine on or off: whether it times, false for an engine built without it (only a build to
|
|
73
|
+
* profile has `setGpuTiming`) */
|
|
74
|
+
const engineGpuTiming = (engine, on) => {
|
|
75
|
+
const set = Reflect.get(engine, 'setGpuTiming');
|
|
76
|
+
return typeof set === 'function' && Reflect.apply(set, engine, [on]) === true;
|
|
77
|
+
};
|
|
70
78
|
export class GeoMap {
|
|
71
79
|
canvas;
|
|
72
80
|
#engine;
|
|
81
|
+
/** The frames' parts timed on the GPU (`setGpuTiming`), again on a new engine */
|
|
82
|
+
gpuTiming = false;
|
|
73
83
|
/** The work the map waits on before it is idle (`track`) */
|
|
74
84
|
#working = 0;
|
|
75
85
|
/** The engine class (core, or with 3D Tiles) and the engine with 3D Tiles loaded when needed */
|
|
@@ -124,20 +134,25 @@ export class GeoMap {
|
|
|
124
134
|
static async create(container, given = {}) {
|
|
125
135
|
// A preset under the options given
|
|
126
136
|
const options = given.preset ? { ...IGN_PRESETS[given.preset], ...given } : given;
|
|
127
|
-
// With 3D Tiles from the start when asked, or when only that engine's URL is given (as before the core engine)
|
|
128
|
-
const full = options.tiles3d === true || (options.wasmUrl !== undefined && options.coreWasmUrl === undefined);
|
|
129
137
|
// Before anything is created
|
|
130
138
|
if (options.basemap !== undefined)
|
|
131
139
|
checkBasemap(options.basemap);
|
|
132
|
-
|
|
140
|
+
// The engine on wgpu (#146): WebGPU, else wgpu's WebGL2 fallback
|
|
141
|
+
const kind = await rendererKind(options.renderer);
|
|
133
142
|
const bindings = await loadEngine(kind, engineUrl(kind, options));
|
|
134
|
-
const map = new GeoMap(container, options, bindings.Engine
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
const map = new GeoMap(container, options, bindings.Engine);
|
|
144
|
+
// Its device asked for: the map started on a placeholder meanwhile, its layers created on the engine once it is
|
|
145
|
+
// there (as after a context loss)
|
|
146
|
+
map.adopt(await map.engines.create(map.canvas, () => map.requestRender(), map.settings));
|
|
147
|
+
// A profiling script's page (`window.__isardProfile` set before isard loads): its maps, to time
|
|
148
|
+
if (Reflect.get(globalThis, '__isardProfile')) {
|
|
149
|
+
const maps = Reflect.get(globalThis, '__isardMaps');
|
|
150
|
+
Reflect.set(globalThis, '__isardMaps', [...(Array.isArray(maps) ? maps : []), map]);
|
|
151
|
+
}
|
|
137
152
|
return map;
|
|
138
153
|
}
|
|
139
|
-
constructor(container, options, engineClass
|
|
140
|
-
this.engines = new MapEngines(engineClass,
|
|
154
|
+
constructor(container, options, engineClass) {
|
|
155
|
+
this.engines = new MapEngines(engineClass, options);
|
|
141
156
|
const workerUrl = options.basemapWorkerUrl ?? defaultWorkerUrl(options.assetsUrl);
|
|
142
157
|
this.tessUrl = tessUrl(options);
|
|
143
158
|
this.gridUrl = gridUrl(options);
|
|
@@ -145,7 +160,8 @@ export class GeoMap {
|
|
|
145
160
|
this.basemaps = new BasemapLayers(this, this.stack, workerUrl, options.basemapTileSize);
|
|
146
161
|
this.container = container;
|
|
147
162
|
this.canvas = createCanvas();
|
|
148
|
-
|
|
163
|
+
// The engine takes the canvas for itself: it says how large a texture its device takes
|
|
164
|
+
this.atlases = new SymbolAtlases(() => this.#engine.maxTextureSize(), options.glyphs, () => this.#engine);
|
|
149
165
|
this.overlays = new OverlayHost(container, this.canvas, this);
|
|
150
166
|
this.earth = (options.body ?? 'earth') === 'earth';
|
|
151
167
|
this.body = options.body;
|
|
@@ -156,7 +172,6 @@ export class GeoMap {
|
|
|
156
172
|
lost: () => this.lost,
|
|
157
173
|
interacting: () => this.interaction?.active === true,
|
|
158
174
|
customLayers: () => this.layers.some((layer) => layer instanceof CustomLayer),
|
|
159
|
-
upgrading: () => this.engines.upgrading !== undefined,
|
|
160
175
|
working: () => this.#working > 0,
|
|
161
176
|
drawn: (hovered) => {
|
|
162
177
|
this.qualityWatch?.frame(performance.now(), this.movingNow);
|
|
@@ -167,7 +182,8 @@ export class GeoMap {
|
|
|
167
182
|
this.overlays.positionAll();
|
|
168
183
|
},
|
|
169
184
|
}, this.listeners);
|
|
170
|
-
|
|
185
|
+
// The engine is made asynchronously (`GeoMap.create` adopts it): a placeholder meanwhile
|
|
186
|
+
this.#engine = destroyedEngine(EARTH_START);
|
|
171
187
|
// Once it has an engine: a map whose creation failed before is not left following them
|
|
172
188
|
this.unwatchHeaders = onHeaderRules((rules) => sendHeaders(this.#engine, rules));
|
|
173
189
|
// The browser may take the GPU away (driver reset, background tab on mobile): the map waits, then rebuilds
|
|
@@ -224,34 +240,6 @@ export class GeoMap {
|
|
|
224
240
|
get onTop() {
|
|
225
241
|
return this.stack.onTop;
|
|
226
242
|
}
|
|
227
|
-
createEngine() {
|
|
228
|
-
return this.engines.create(this.canvas, () => this.requestRender(), this.settings);
|
|
229
|
-
}
|
|
230
|
-
/** A 3D Tiles layer or a model on the core engine: loads the engine with 3D Tiles, then rebuilds the map on it
|
|
231
|
-
* (camera, settings and layers kept, as after a context loss); the layer is created then */
|
|
232
|
-
upgrade() {
|
|
233
|
-
this.engines.upgrade(() => {
|
|
234
|
-
// A lost context: the engine is rebuilt on it once restored
|
|
235
|
-
if (!this.lost)
|
|
236
|
-
this.restore();
|
|
237
|
-
}, (error) => {
|
|
238
|
-
// The 3D Tiles layers stay empty; the next one added tries again
|
|
239
|
-
report(error);
|
|
240
|
-
this.requestRender();
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
/** Downloads and compiles the engine with 3D Tiles once the map is idle (`tiles3d: 'preload'`), without running
|
|
244
|
-
* it: the first 3D Tiles layer then only instantiates it */
|
|
245
|
-
preloadTiles3d() {
|
|
246
|
-
if (this.frames.settled)
|
|
247
|
-
this.engines.preload();
|
|
248
|
-
else {
|
|
249
|
-
const off = this.on('idle', () => {
|
|
250
|
-
off();
|
|
251
|
-
this.engines.preload();
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
243
|
/**
|
|
256
244
|
* The context is lost: its engine freed at once, the map's calls going to one that does nothing until `restore`
|
|
257
245
|
* builds the next. The old engine's GPU objects went with the context: deleted now, it is silent (on the context
|
|
@@ -266,19 +254,34 @@ export class GeoMap {
|
|
|
266
254
|
// Glyph ranges and icons arriving meanwhile upload to it, not to the freed engine
|
|
267
255
|
this.atlases.attach(this.#engine);
|
|
268
256
|
}
|
|
269
|
-
/** After a context loss: a new engine with the same camera, settings and layers (same handles, same order)
|
|
257
|
+
/** After a context loss: a new engine with the same camera, settings and layers (same handles, same order); the
|
|
258
|
+
* engine made asynchronously, adopted once there */
|
|
270
259
|
restore() {
|
|
271
260
|
if (this.gone)
|
|
272
261
|
return;
|
|
262
|
+
this.engines
|
|
263
|
+
.create(this.canvas, () => this.requestRender(), this.settings)
|
|
264
|
+
.then((engine) => this.adopt(engine))
|
|
265
|
+
.catch(report);
|
|
266
|
+
}
|
|
267
|
+
/** A new engine in place of the map's (a placeholder, or a lost one): the same camera, settings and layers (same
|
|
268
|
+
* handles, same order) */
|
|
269
|
+
adopt(engine) {
|
|
270
|
+
if (this.gone) {
|
|
271
|
+
engine.free();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
273
274
|
this.hoverPick.cancel();
|
|
274
275
|
const camera = this.getCamera();
|
|
275
276
|
this.#engine.free();
|
|
276
|
-
this.#engine =
|
|
277
|
+
this.#engine = engine;
|
|
277
278
|
this.lost = false;
|
|
279
|
+
// Still timed (profiling)
|
|
280
|
+
if (this.gpuTiming)
|
|
281
|
+
engineGpuTiming(engine, true);
|
|
278
282
|
this.setCamera(camera);
|
|
279
283
|
this.atlases.attach(this.#engine);
|
|
280
|
-
|
|
281
|
-
this.stack.reattach(this.#engine, (layer) => layer.needsFullEngine && !this.engines.tiles3d);
|
|
284
|
+
this.stack.reattach(this.#engine);
|
|
282
285
|
this.applySwipe();
|
|
283
286
|
// A street-level eye stays one in the new engine
|
|
284
287
|
if (this.#firstPerson)
|
|
@@ -293,6 +296,7 @@ export class GeoMap {
|
|
|
293
296
|
void set.show(set.current.index).catch(report);
|
|
294
297
|
});
|
|
295
298
|
this.resize();
|
|
299
|
+
this.requestRender();
|
|
296
300
|
}
|
|
297
301
|
/** The street-level eye, while there is one (`setFirstPerson`) */
|
|
298
302
|
#firstPerson;
|
|
@@ -354,20 +358,14 @@ export class GeoMap {
|
|
|
354
358
|
return !this.gone && this.stack.has(layer);
|
|
355
359
|
}
|
|
356
360
|
addLayer(layer) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
catch (error) {
|
|
366
|
-
// A context lost before the browser says so (its event comes later): nothing could be made on it, the layer is
|
|
367
|
-
// made with the others on the next engine (`restore`). Any other failure is the caller's
|
|
368
|
-
if (!this.contextLost())
|
|
369
|
-
throw error;
|
|
370
|
-
}
|
|
361
|
+
try {
|
|
362
|
+
layer.attach(this.engine);
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
// A context lost before the browser says so (its event comes later): nothing could be made on it, the layer is
|
|
366
|
+
// made with the others on the next engine (`restore`). Any other failure is the caller's
|
|
367
|
+
if (!this.contextLost())
|
|
368
|
+
throw error;
|
|
371
369
|
}
|
|
372
370
|
// Under the layers kept on top (the basemap's labels)
|
|
373
371
|
this.stack.add(layer);
|
|
@@ -376,6 +374,7 @@ export class GeoMap {
|
|
|
376
374
|
}
|
|
377
375
|
/** Whether the canvas's WebGL context is lost, its event come or not */
|
|
378
376
|
contextLost() {
|
|
377
|
+
// On a WebGPU canvas, no WebGL context: null, nothing taken
|
|
379
378
|
return this.lost || this.canvas.getContext('webgl2')?.isContextLost() === true;
|
|
380
379
|
}
|
|
381
380
|
/** @internal Keeps a layer above the layers added after it (they go under it), as the labels of a basemap */
|
|
@@ -577,22 +576,44 @@ export class GeoMap {
|
|
|
577
576
|
stats() {
|
|
578
577
|
return this.engine.stats();
|
|
579
578
|
}
|
|
579
|
+
/** Times the parts of each frame on the GPU, for profiling (`gpuTimings`): timer queries, read a few frames later.
|
|
580
|
+
* An engine built to profile only (`PROFILE=1 bun run build`); false without one, or if the browser cannot
|
|
581
|
+
* (`EXT_disjoint_timer_query_webgl2`) */
|
|
582
|
+
setGpuTiming(on) {
|
|
583
|
+
this.gpuTiming = engineGpuTiming(this.engine, on);
|
|
584
|
+
return this.gpuTiming;
|
|
585
|
+
}
|
|
586
|
+
/** The GPU time of the last frame measured (`setGpuTiming`), in all and by part: the backdrop, the ground (the raster
|
|
587
|
+
* layers over the relief), each layer, the shadows, the panorama, the viewshed (ms) */
|
|
588
|
+
gpuTimings() {
|
|
589
|
+
// The frame's number, then each part's code, layer id (-1 for none) and milliseconds
|
|
590
|
+
const read = Reflect.get(this.engine, 'gpuTimings');
|
|
591
|
+
const flat = typeof read === 'function' ? Reflect.apply(read, this.engine, []) : undefined;
|
|
592
|
+
if (!(flat instanceof Float64Array) || flat.length === 0)
|
|
593
|
+
return undefined;
|
|
594
|
+
const parts = Array.from({ length: (flat.length - 1) / 3 }, (_, k) => {
|
|
595
|
+
const [code = 0, id = -1, ms = 0] = flat.subarray(1 + k * 3, 4 + k * 3);
|
|
596
|
+
const layer = id >= 0 ? this.layers.find((l) => l.id === id) : undefined;
|
|
597
|
+
return { part: GPU_PARTS[code] ?? 'layer', ms, ...(layer && { layer }) };
|
|
598
|
+
});
|
|
599
|
+
return { frame: flat[0] ?? 0, ms: parts.reduce((sum, p) => sum + p.ms, 0), parts };
|
|
600
|
+
}
|
|
580
601
|
/**
|
|
581
|
-
* The feature under a screen point (CSS pixels), undefined if none: its layer id and index. Reads
|
|
582
|
-
* offscreen picking pass, whatever the number of features,
|
|
583
|
-
*
|
|
602
|
+
* The feature under a screen point (CSS pixels), undefined if none: its layer id and index. Reads the pixels around
|
|
603
|
+
* the point of an offscreen picking pass, whatever the number of features, once the GPU is done (a frame or two
|
|
604
|
+
* later: the GPU is not waited for). On hover, listen to 'hoverfeature' instead.
|
|
584
605
|
*/
|
|
585
|
-
pick(point, { radius = 0, layers } = {}) {
|
|
606
|
+
async pick(point, { radius = 0, layers } = {}) {
|
|
586
607
|
const only = layers && Uint32Array.from(layers.map((l) => l.id));
|
|
587
|
-
return this.engine
|
|
608
|
+
return (await pickOnce(() => this.engine, point, radius, only))?.hit;
|
|
588
609
|
}
|
|
589
610
|
/**
|
|
590
611
|
* The feature under a screen point (CSS pixels), or the nearest one within `radius` pixels: its layer handle, its
|
|
591
612
|
* index there and, for line, circle and fill layers, the feature itself; undefined if none. `layers` restricts the
|
|
592
613
|
* search to some layers (the others hide nothing).
|
|
593
614
|
*/
|
|
594
|
-
pickFeature(point, options = {}) {
|
|
595
|
-
const hit = this.pick(point, options);
|
|
615
|
+
async pickFeature(point, options = {}) {
|
|
616
|
+
const hit = await this.pick(point, options);
|
|
596
617
|
return hit && this.featureHit(hit);
|
|
597
618
|
}
|
|
598
619
|
/** A hit with its layer handle and feature; undefined if the layer is gone */
|
|
@@ -617,13 +638,6 @@ export class GeoMap {
|
|
|
617
638
|
* and calibrations loaded, none shown until `show`, which projects one onto the scene around it
|
|
618
639
|
*/
|
|
619
640
|
async addPanoramas(options) {
|
|
620
|
-
// Drawn by the engine with 3D Tiles: loaded first if needed
|
|
621
|
-
if (!this.engines.tiles3d) {
|
|
622
|
-
this.upgrade();
|
|
623
|
-
await this.engines.upgrading;
|
|
624
|
-
if (!this.engines.tiles3d)
|
|
625
|
-
throw new Error('isard: panoramas need the engine with 3D Tiles, which failed to load');
|
|
626
|
-
}
|
|
627
641
|
const host = panoramaHost(this, (lon, lat) => this.engines.geoidHeight(lon, lat));
|
|
628
642
|
const panoramas = await loadPanoramas(host, options);
|
|
629
643
|
this.panoramaSets.add(panoramas);
|
|
@@ -664,15 +678,16 @@ export class GeoMap {
|
|
|
664
678
|
this.interaction.rotateAroundPointer = enabled;
|
|
665
679
|
}
|
|
666
680
|
/** What is drawn under a screen point (CSS pixels), on the relief or standing on it (a roof, a wall, a mesh): its
|
|
667
|
-
* position and its height above the relief, in metres; undefined where nothing was drawn.
|
|
668
|
-
*
|
|
681
|
+
* position and its height above the relief, in metres; undefined where nothing was drawn. Read from the last copy of
|
|
682
|
+
* the scene's depth (a frame or two old, a pixel every 4 CSS pixels): at once, without waiting for the GPU */
|
|
669
683
|
pickSurface(point) {
|
|
670
684
|
const hit = this.engine.surface(point[0], point[1]);
|
|
671
685
|
return hit && { lonLat: [hit[0], hit[1]], height: hit[2] };
|
|
672
686
|
}
|
|
673
687
|
/** The height above the relief, in metres, of the top of what stands at a position: a roof, a mesh, 0 on the bare
|
|
674
|
-
* ground; undefined where nothing of the last frame is there
|
|
675
|
-
* read back
|
|
688
|
+
* ground; undefined where nothing of the last frame is there, or until measured: the scene is drawn looking down at
|
|
689
|
+
* it after a frame and read back a frame or two later, the answer kept and measured again every 250 ms at most (ask
|
|
690
|
+
* again on the next `render` event) */
|
|
676
691
|
surfaceAt(lonLat) {
|
|
677
692
|
return this.engine.surfaceAt(lonLat[0], lonLat[1]);
|
|
678
693
|
}
|
|
@@ -714,9 +729,9 @@ export class GeoMap {
|
|
|
714
729
|
/** Raster tiles ({z}, {x}, {y} URL; Web Mercator, or reprojected from another `grid`: longitudes and latitudes,
|
|
715
730
|
* Lambert-93, UTM…), over the layers already added */
|
|
716
731
|
addRaster(options) {
|
|
717
|
-
const { url, opacity = 1, saturation = 1, size, before } = options;
|
|
732
|
+
const { url, opacity = 1, saturation = 1, size, before, ground = true } = options;
|
|
718
733
|
const { loader, minZoom, maxZoom } = rasterSource(options, this.workers.remoteReprojection);
|
|
719
|
-
const layer = new RasterLayer(this, { url, minZoom, maxZoom, opacity, saturation, size, loader });
|
|
734
|
+
const layer = new RasterLayer(this, { url, minZoom, maxZoom, opacity, saturation, size, loader, ground });
|
|
720
735
|
this.place(layer, before);
|
|
721
736
|
if (options.attribution)
|
|
722
737
|
this.credits.set(layer, options.attribution);
|
|
@@ -869,13 +884,13 @@ export class GeoMap {
|
|
|
869
884
|
}
|
|
870
885
|
/** Textured meshes in 3D Tiles (photogrammetry, buildings), drawn with depth over the basemap */
|
|
871
886
|
addTiles3d(options) {
|
|
872
|
-
const { url, maxScreenSpaceError = 16, opacity = 1, pointSize, color, geoid = false, facades = false } = options;
|
|
887
|
+
const { url, maxScreenSpaceError = 16, opacity = 1, pointSize, color, geoid = false, facades = false, drape, } = options;
|
|
873
888
|
return this.place(new Tiles3dLayer(this, url, geoid, {
|
|
874
889
|
maxScreenSpaceError,
|
|
875
890
|
opacity,
|
|
876
891
|
...(pointSize !== undefined ? { pointSize } : {}),
|
|
877
892
|
...(color !== undefined ? { color } : {}),
|
|
878
|
-
}, facades), options.before);
|
|
893
|
+
}, facades, drape), options.before);
|
|
879
894
|
}
|
|
880
895
|
/**
|
|
881
896
|
* A point cloud (see `PointCloudOptions`): a COPC file read by range requests (IGN's LiDAR HD…), or 3D Tiles
|
|
@@ -917,7 +932,7 @@ export class GeoMap {
|
|
|
917
932
|
* WebGL, three.js… with the matrices of the frame (`frame.matrixAt`); under `before` if given
|
|
918
933
|
*/
|
|
919
934
|
addCustomLayer(layer, options = {}) {
|
|
920
|
-
const custom = new CustomLayer(this, layer, this
|
|
935
|
+
const custom = new CustomLayer(this, layer, this, (lon, lat, altitude) => this.engines.customMatrix(lon, lat, altitude), () => this.getCamera());
|
|
921
936
|
return this.place(custom, options.before);
|
|
922
937
|
}
|
|
923
938
|
// ── Frames ──
|