isard 0.6.1 → 0.8.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 +100 -0
- package/dist/basemap.worker.d.ts +7 -1
- package/dist/basemap.worker.js +2 -2
- package/dist/basemap.worker.js.map +8 -7
- package/dist/decoder.worker.js +4 -4
- package/dist/decoder.worker.js.map +6 -5
- package/dist/engine-loader.d.ts +1 -1
- package/dist/engine-loader.js.map +1 -1
- package/dist/geoplateforme.d.ts +4 -3
- package/dist/geoplateforme.js +43 -19
- package/dist/geoplateforme.js.map +1 -1
- package/dist/glyphs.js +2 -1
- package/dist/glyphs.js.map +1 -1
- package/dist/icons.js +5 -1
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +2 -0
- package/dist/interaction.js +29 -2
- package/dist/interaction.js.map +1 -1
- package/dist/isard_bg.wasm +0 -0
- package/dist/isard_core_bg.wasm +0 -0
- package/dist/layers.d.ts +2 -0
- package/dist/layers.js +22 -4
- package/dist/layers.js.map +1 -1
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +10 -0
- package/dist/loader.js.map +1 -1
- package/dist/map.d.ts +52 -3
- package/dist/map.js +171 -6
- package/dist/map.js.map +1 -1
- package/dist/overlay.d.ts +55 -0
- package/dist/overlay.js +185 -7
- package/dist/overlay.js.map +1 -1
- package/dist/panoramas.d.ts +94 -0
- package/dist/panoramas.js +245 -0
- package/dist/panoramas.js.map +1 -0
- package/dist/pmtiles.js +3 -2
- package/dist/pmtiles.js.map +1 -1
- package/dist/pointclouds.js +1 -1
- package/dist/pointclouds.js.map +1 -1
- package/dist/protocol.d.ts +7 -1
- package/dist/protocol.js.map +1 -1
- package/dist/requests.d.ts +14 -0
- package/dist/requests.js +48 -0
- package/dist/requests.js.map +1 -0
- package/dist/resample.js +2 -1
- package/dist/resample.js.map +1 -1
- package/dist/style.js +3 -0
- package/dist/style.js.map +1 -1
- package/dist/terrain.d.ts +0 -5
- package/dist/terrain.js +2 -1
- package/dist/terrain.js.map +1 -1
- package/dist/types.d.ts +36 -4
- package/dist/types.js.map +1 -1
- package/dist/vectortiles.d.ts +6 -2
- package/dist/vectortiles.js +92 -33
- package/dist/vectortiles.js.map +1 -1
- package/dist/wasm/isard.d.ts +105 -11
- package/dist/wasm/isard.js +211 -13
- package/dist/wasm/isard_core.d.ts +64 -6
- package/dist/wasm/isard_core.js +149 -6
- package/dist/wmts.js +2 -1
- package/dist/wmts.js.map +1 -1
- package/package.json +1 -1
package/dist/map.js
CHANGED
|
@@ -15,7 +15,9 @@ import { equirectangularGrid, gridLoader, gridZooms, resolveGrid } from "./grid.
|
|
|
15
15
|
import { Icons, readPixels } from "./icons.js";
|
|
16
16
|
import { Interaction } from "./interaction.js";
|
|
17
17
|
import { CustomLayer, GroundLayer, ModelLayer, NetworkLayer, RasterLayer, Tiles3dLayer, } from "./layers.js";
|
|
18
|
+
import { loadPanoramas } from "./panoramas.js";
|
|
18
19
|
import { PointCloudLayer } from "./pointclouds.js";
|
|
20
|
+
import { headerRules, onHeaderRules } from "./requests.js";
|
|
19
21
|
import { atmosphereSettings, bodySettings, haloSettings, rgb, terrainSettings, } from "./settings.js";
|
|
20
22
|
import { injectStyle } from "./style.js";
|
|
21
23
|
import { SymbolLayer, symbolStyle } from "./symbols.js";
|
|
@@ -138,6 +140,8 @@ export class GeoMap {
|
|
|
138
140
|
basemapPool;
|
|
139
141
|
/** Stops watching the device pixel ratio */
|
|
140
142
|
unwatchRatio;
|
|
143
|
+
/** Stops following the page's request headers (`setRequestHeaders`) */
|
|
144
|
+
unwatchHeaders = () => undefined;
|
|
141
145
|
/** Stops listening to the canvas's context loss and restoration */
|
|
142
146
|
unwatchContext;
|
|
143
147
|
/** The loaders of the relief by encoding, URL and floor: the same loader for the same tiles (the engine keeps its
|
|
@@ -202,6 +206,8 @@ export class GeoMap {
|
|
|
202
206
|
globeColor: this.earth ? SEA['osm-positron'] : rgb(body.color),
|
|
203
207
|
};
|
|
204
208
|
this.#engine = this.createEngine();
|
|
209
|
+
// Once it has an engine: a map whose creation failed before is not left following them
|
|
210
|
+
this.unwatchHeaders = onHeaderRules((rules) => sendHeaders(this.#engine, rules));
|
|
205
211
|
// The browser may take the GPU away (driver reset, background tab on mobile): the map waits, then rebuilds
|
|
206
212
|
const lost = (event) => {
|
|
207
213
|
event.preventDefault();
|
|
@@ -256,6 +262,7 @@ export class GeoMap {
|
|
|
256
262
|
// The body first: the layers' shaders may take its size
|
|
257
263
|
const { radius, flattening, atmosphere: air, relief } = this.settings.body;
|
|
258
264
|
engine.setBody(radius, flattening, haloSettings(air), ...relief);
|
|
265
|
+
sendHeaders(engine, headerRules());
|
|
259
266
|
const { zoomRange, maxPitch, globe, sky, atmosphere, terrain, loading, background, globeColor } = this.settings;
|
|
260
267
|
engine.setZoomRange(...zoomRange);
|
|
261
268
|
engine.setMaxPitch(maxPitch);
|
|
@@ -349,8 +356,68 @@ export class GeoMap {
|
|
|
349
356
|
report(error);
|
|
350
357
|
}
|
|
351
358
|
}
|
|
359
|
+
this.applySwipe();
|
|
360
|
+
// A street-level eye stays one in the new engine
|
|
361
|
+
if (this.#firstPerson)
|
|
362
|
+
this.setFirstPerson(this.#firstPerson);
|
|
363
|
+
// What the old engine drew over the scene: the viewshed, the panoramas shown
|
|
364
|
+
if (this.viewshed)
|
|
365
|
+
this.setViewshed(this.viewshed);
|
|
366
|
+
for (const set of this.panoramaSets)
|
|
367
|
+
if (set.current)
|
|
368
|
+
void set.show(set.current.index).catch(report);
|
|
352
369
|
this.resize();
|
|
353
370
|
}
|
|
371
|
+
/** The street-level eye, while there is one (`setFirstPerson`) */
|
|
372
|
+
#firstPerson;
|
|
373
|
+
/** The street-level eye, if the camera is one */
|
|
374
|
+
get firstPerson() {
|
|
375
|
+
return this.#firstPerson;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Puts the camera at street level: an eye `height` metres above the ground, looking `bearing` and `pitch`, the
|
|
379
|
+
* drag turning it around (the wheel narrows or widens its field of view); undefined gives back the camera turning
|
|
380
|
+
* around a point, above where the eye stood, facing the same way
|
|
381
|
+
*/
|
|
382
|
+
setFirstPerson(view) {
|
|
383
|
+
if (!view) {
|
|
384
|
+
const was = this.#firstPerson;
|
|
385
|
+
this.#firstPerson = undefined;
|
|
386
|
+
if (was)
|
|
387
|
+
this.setCamera({ center: was.lonLat, zoom: 18, pitch: 60, bearing: was.bearing });
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const eye = {
|
|
391
|
+
height: 1.7,
|
|
392
|
+
pitch: 90,
|
|
393
|
+
fov: 75,
|
|
394
|
+
...view,
|
|
395
|
+
lonLat: [view.lonLat[0], view.lonLat[1]],
|
|
396
|
+
};
|
|
397
|
+
eye.pitch = Math.max(1, Math.min(179, eye.pitch));
|
|
398
|
+
eye.fov = Math.max(10, Math.min(140, eye.fov));
|
|
399
|
+
eye.bearing = ((eye.bearing % 360) + 360) % 360;
|
|
400
|
+
this.#firstPerson = eye;
|
|
401
|
+
this.engine.setFirstPerson(eye.lonLat[0], eye.lonLat[1], eye.height, eye.bearing, eye.pitch, eye.fov);
|
|
402
|
+
this.requestRender();
|
|
403
|
+
}
|
|
404
|
+
/** The swipe set (`setSwipe`), applied again with the layers' ids of a new engine */
|
|
405
|
+
swipe;
|
|
406
|
+
/**
|
|
407
|
+
* Compares two states of a place: `layers` shown on one side of a vertical line only, at `position` CSS pixels
|
|
408
|
+
* from the left of the map (left of it by default, `side: 'right'` for the other), the layers beneath them on the
|
|
409
|
+
* other side. Set again as the line moves (a slider, a drag); undefined stops
|
|
410
|
+
*/
|
|
411
|
+
setSwipe(options) {
|
|
412
|
+
this.swipe = options && options.layers.length > 0 ? { ...options, layers: [...options.layers] } : undefined;
|
|
413
|
+
this.applySwipe();
|
|
414
|
+
this.requestRender();
|
|
415
|
+
}
|
|
416
|
+
applySwipe() {
|
|
417
|
+
const s = this.swipe;
|
|
418
|
+
const ids = Uint32Array.from((s?.layers ?? []).filter((l) => l.id >= 0), (l) => l.id);
|
|
419
|
+
this.engine.setSwipe(s && ids.length > 0 ? s.position : Number.NaN, s?.side === 'right', ids);
|
|
420
|
+
}
|
|
354
421
|
/** @internal Forgets a removed layer */
|
|
355
422
|
detach(layer) {
|
|
356
423
|
this.unlist(layer);
|
|
@@ -515,7 +582,10 @@ export class GeoMap {
|
|
|
515
582
|
* (`direction`): the relief, extrusions, 3D Tiles and models hide what lies behind them (only what the map has
|
|
516
583
|
* loaded for its view). Several observers (16 at most): what any of them sees is seen, what is hidden from all those
|
|
517
584
|
* that reach it is hidden, in the colors and opacity of the first. Undefined or none removes it */
|
|
585
|
+
/** The viewshed set, set again in a new engine */
|
|
586
|
+
viewshed;
|
|
518
587
|
setViewshed(options) {
|
|
588
|
+
this.viewshed = options;
|
|
519
589
|
let observers = [];
|
|
520
590
|
if (options)
|
|
521
591
|
observers = 'position' in options ? [options] : options;
|
|
@@ -532,7 +602,7 @@ export class GeoMap {
|
|
|
532
602
|
...(o.direction === undefined
|
|
533
603
|
? [Number.NaN, 0, 0, 0]
|
|
534
604
|
: [o.direction, o.tilt ?? 0, o.fov ?? 60, o.verticalFov ?? 40]),
|
|
535
|
-
])), new Float32Array(rgb(first.visibleColor ?? '#2f9e44')), new Float32Array(rgb(first.hiddenColor ?? '#e03131')), first.opacity ?? 0.
|
|
605
|
+
])), new Float32Array(rgb(first.visibleColor ?? '#2f9e44')), new Float32Array(rgb(first.hiddenColor ?? '#e03131')), first.opacity ?? 0.7, new Float32Array(first.coneColor ? rgb(first.coneColor) : []));
|
|
536
606
|
this.requestRender();
|
|
537
607
|
}
|
|
538
608
|
/** Relief under the basemap: true for the body's terrain model (on the Earth the worldwide Terrain Tiles, NASA's
|
|
@@ -634,14 +704,99 @@ export class GeoMap {
|
|
|
634
704
|
cancelAnimationFrame(this.hoverPoll);
|
|
635
705
|
this.hoverPoll = 0;
|
|
636
706
|
}
|
|
637
|
-
/** Screen point (CSS pixels) of a position
|
|
638
|
-
|
|
639
|
-
|
|
707
|
+
/** Screen point (CSS pixels) of a position on the ground (the relief), or `height` metres above it, undefined
|
|
708
|
+
* behind the camera */
|
|
709
|
+
project(lonLat, height = 0) {
|
|
710
|
+
return this.engine.project(lonLat[0], lonLat[1], height);
|
|
640
711
|
}
|
|
641
712
|
/** Position under a screen point (CSS pixels), undefined above the horizon */
|
|
642
713
|
unproject(point) {
|
|
643
714
|
return this.engine.ground(point[0], point[1]);
|
|
644
715
|
}
|
|
716
|
+
/**
|
|
717
|
+
* Street-level panoramas (oriented images of a vehicle's cameras, the iTowns immersive format): their positions
|
|
718
|
+
* and calibrations loaded, none shown until `show`, which projects one onto the scene around it
|
|
719
|
+
*/
|
|
720
|
+
async addPanoramas(options) {
|
|
721
|
+
// Drawn by the engine with 3D Tiles: loaded first if needed
|
|
722
|
+
if (!this.tiles3dModule) {
|
|
723
|
+
this.upgrade();
|
|
724
|
+
await this.upgrading;
|
|
725
|
+
if (!this.tiles3dModule)
|
|
726
|
+
throw new Error('isard: panoramas need the engine with 3D Tiles, which failed to load');
|
|
727
|
+
}
|
|
728
|
+
const host = {
|
|
729
|
+
setPanorama: (...args) => {
|
|
730
|
+
this.engine.setPanorama(...args);
|
|
731
|
+
this.requestRender();
|
|
732
|
+
},
|
|
733
|
+
setPanoramaImage: (index, image) => this.engine.setPanoramaImage(index, image),
|
|
734
|
+
clearPanorama: () => this.engine.clearPanorama(),
|
|
735
|
+
geoidHeight: (lon, lat) => this.engineClass.geoidHeight(lon, lat),
|
|
736
|
+
firstPerson: () => this.firstPerson,
|
|
737
|
+
setFirstPerson: (view) => this.setFirstPerson(view),
|
|
738
|
+
elevationAt: (lonLat) => this.elevationAt(lonLat),
|
|
739
|
+
listen: (click, key, wheel) => {
|
|
740
|
+
const off = this.on('click', ({ lonLat }) => click(lonLat));
|
|
741
|
+
const container = this.getContainer();
|
|
742
|
+
// Before the map's own wheel (its zoom, the eye's field of view)
|
|
743
|
+
const onWheel = (e) => {
|
|
744
|
+
e.preventDefault();
|
|
745
|
+
e.stopImmediatePropagation();
|
|
746
|
+
wheel(e);
|
|
747
|
+
};
|
|
748
|
+
container.addEventListener('keydown', key);
|
|
749
|
+
container.addEventListener('wheel', onWheel, { capture: true, passive: false });
|
|
750
|
+
return () => {
|
|
751
|
+
off();
|
|
752
|
+
container.removeEventListener('keydown', key);
|
|
753
|
+
container.removeEventListener('wheel', onWheel, { capture: true });
|
|
754
|
+
};
|
|
755
|
+
},
|
|
756
|
+
};
|
|
757
|
+
const panoramas = await loadPanoramas(host, options);
|
|
758
|
+
this.panoramaSets.add(panoramas);
|
|
759
|
+
return panoramas;
|
|
760
|
+
}
|
|
761
|
+
/** The panorama sets, their panorama shown again in a new engine */
|
|
762
|
+
panoramaSets = new Set();
|
|
763
|
+
/** Altitude of the relief at a position, in metres (exaggeration included): from the copy of its tiles the map
|
|
764
|
+
* keeps, as fine as the tiles loaded there; undefined without relief or where none is loaded yet */
|
|
765
|
+
elevationAt(lonLat) {
|
|
766
|
+
return this.engine.elevationAt(lonLat[0], lonLat[1]);
|
|
767
|
+
}
|
|
768
|
+
/** Metres on the ground per CSS pixel at a screen point (default the center of the map), across the screen: for a
|
|
769
|
+
* scale bar; undefined above the horizon */
|
|
770
|
+
metersPerPixel(point) {
|
|
771
|
+
const [x, y] = point ?? [this.canvas.clientWidth / 2, this.canvas.clientHeight / 2];
|
|
772
|
+
const a = this.unproject([x - 0.5, y]);
|
|
773
|
+
const b = this.unproject([x + 0.5, y]);
|
|
774
|
+
if (!a || !b)
|
|
775
|
+
return undefined;
|
|
776
|
+
// Haversine, on the mean radius of the body
|
|
777
|
+
const rad = Math.PI / 180;
|
|
778
|
+
const [dLat, dLon] = [(b[1] - a[1]) * rad, (b[0] - a[0]) * rad];
|
|
779
|
+
const h = Math.sin(dLat / 2) ** 2 + Math.cos(a[1] * rad) * Math.cos(b[1] * rad) * Math.sin(dLon / 2) ** 2;
|
|
780
|
+
return 2 * this.settings.body.radius * Math.asin(Math.min(1, Math.sqrt(h)));
|
|
781
|
+
}
|
|
782
|
+
/** Whether a double click zooms in (Shift: out), on by default: off while a drawing takes double clicks */
|
|
783
|
+
setDoubleClickZoom(enabled) {
|
|
784
|
+
if (this.interaction)
|
|
785
|
+
this.interaction.doubleClickZoom = enabled;
|
|
786
|
+
}
|
|
787
|
+
/** What is drawn under a screen point (CSS pixels), on the relief or standing on it (a roof, a wall, a mesh): its
|
|
788
|
+
* position and its height above the relief, in metres; undefined where nothing was drawn. The last frame's scene is
|
|
789
|
+
* drawn again and read back, waiting for the GPU (a stall): for clicks and drags */
|
|
790
|
+
pickSurface(point) {
|
|
791
|
+
const hit = this.engine.surface(point[0], point[1]);
|
|
792
|
+
return hit && { lonLat: [hit[0], hit[1]], height: hit[2] };
|
|
793
|
+
}
|
|
794
|
+
/** The height above the relief, in metres, of the top of what stands at a position: a roof, a mesh, 0 on the bare
|
|
795
|
+
* ground; undefined where nothing of the last frame is there. The scene is drawn from above into a single pixel and
|
|
796
|
+
* read back: cheap, but it waits for the GPU */
|
|
797
|
+
surfaceAt(lonLat) {
|
|
798
|
+
return this.engine.surfaceAt(lonLat[0], lonLat[1]);
|
|
799
|
+
}
|
|
645
800
|
// ── Overlays and controls ──
|
|
646
801
|
/** Adds a control in a corner of the map; controls stack from the corner inward */
|
|
647
802
|
addControl(control, position = 'top-right') {
|
|
@@ -893,8 +1048,13 @@ export class GeoMap {
|
|
|
893
1048
|
}
|
|
894
1049
|
/** Textured meshes in 3D Tiles (photogrammetry, buildings), drawn with depth over the basemap */
|
|
895
1050
|
addTiles3d(options) {
|
|
896
|
-
const { url, maxScreenSpaceError = 16, opacity = 1, pointSize } = options;
|
|
897
|
-
return this.add(new Tiles3dLayer(this, url,
|
|
1051
|
+
const { url, maxScreenSpaceError = 16, opacity = 1, pointSize, color, geoid = false } = options;
|
|
1052
|
+
return this.add(new Tiles3dLayer(this, url, geoid, {
|
|
1053
|
+
maxScreenSpaceError,
|
|
1054
|
+
opacity,
|
|
1055
|
+
...(pointSize !== undefined ? { pointSize } : {}),
|
|
1056
|
+
...(color !== undefined ? { color } : {}),
|
|
1057
|
+
}));
|
|
898
1058
|
}
|
|
899
1059
|
/**
|
|
900
1060
|
* A point cloud (see `PointCloudOptions`): a COPC file read by range requests (IGN's LiDAR HD…), or 3D Tiles
|
|
@@ -1063,6 +1223,7 @@ export class GeoMap {
|
|
|
1063
1223
|
this.basemapPool = undefined;
|
|
1064
1224
|
this.unwatchRatio?.();
|
|
1065
1225
|
this.unwatchRatio = undefined;
|
|
1226
|
+
this.unwatchHeaders();
|
|
1066
1227
|
this.resizeObserver.disconnect();
|
|
1067
1228
|
this.interaction?.destroy();
|
|
1068
1229
|
this.loader?.destroy();
|
|
@@ -1089,4 +1250,8 @@ export class GeoMap {
|
|
|
1089
1250
|
listeners.clear();
|
|
1090
1251
|
}
|
|
1091
1252
|
}
|
|
1253
|
+
/** The page's request headers to an engine (its own requests: raster tiles, tilesets, point clouds) */
|
|
1254
|
+
function sendHeaders(engine, rules) {
|
|
1255
|
+
engine.setRequestHeaders(rules.map(([prefix]) => prefix), Uint32Array.from(rules, ([, headers]) => Object.keys(headers).length), rules.flatMap(([, headers]) => Object.entries(headers).flat()));
|
|
1256
|
+
}
|
|
1092
1257
|
//# sourceMappingURL=map.js.map
|