isard 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +11 -3
  3. package/dist/basemap.worker.d.ts +10 -3
  4. package/dist/basemap.worker.js +2 -2
  5. package/dist/basemap.worker.js.map +4 -4
  6. package/dist/decoder.worker.js +4 -4
  7. package/dist/decoder.worker.js.map +7 -6
  8. package/dist/decoders.d.ts +3 -2
  9. package/dist/decoders.js +28 -5
  10. package/dist/decoders.js.map +1 -1
  11. package/dist/engine-loader.d.ts +8 -4
  12. package/dist/engine-loader.js +10 -3
  13. package/dist/engine-loader.js.map +1 -1
  14. package/dist/formats.d.ts +11 -0
  15. package/dist/formats.js +12 -0
  16. package/dist/formats.js.map +1 -0
  17. package/dist/glyphs.d.ts +3 -0
  18. package/dist/glyphs.js +12 -2
  19. package/dist/glyphs.js.map +1 -1
  20. package/dist/icons.d.ts +146 -0
  21. package/dist/icons.js +333 -0
  22. package/dist/icons.js.map +1 -0
  23. package/dist/index.d.ts +8 -5
  24. package/dist/index.js +2 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/isard_bg.wasm +0 -0
  27. package/dist/isard_core_bg.wasm +0 -0
  28. package/dist/isard_points_bg.wasm +0 -0
  29. package/dist/layers.d.ts +88 -1
  30. package/dist/layers.js +165 -4
  31. package/dist/layers.js.map +1 -1
  32. package/dist/loader.d.ts +9 -3
  33. package/dist/loader.js +22 -3
  34. package/dist/loader.js.map +1 -1
  35. package/dist/map.d.ts +37 -3
  36. package/dist/map.js +76 -10
  37. package/dist/map.js.map +1 -1
  38. package/dist/pointclouds.d.ts +98 -0
  39. package/dist/pointclouds.js +232 -0
  40. package/dist/pointclouds.js.map +1 -0
  41. package/dist/projections.d.ts +13 -0
  42. package/dist/projections.js +13 -0
  43. package/dist/projections.js.map +1 -0
  44. package/dist/protocol.d.ts +17 -0
  45. package/dist/protocol.js.map +1 -1
  46. package/dist/symbols.d.ts +42 -8
  47. package/dist/symbols.js +160 -35
  48. package/dist/symbols.js.map +1 -1
  49. package/dist/types.d.ts +8 -3
  50. package/dist/types.js.map +1 -1
  51. package/dist/vectorstyle.d.ts +32 -3
  52. package/dist/vectorstyle.js.map +1 -1
  53. package/dist/vectortiles.d.ts +10 -5
  54. package/dist/vectortiles.js +54 -29
  55. package/dist/vectortiles.js.map +1 -1
  56. package/dist/wasm/isard.d.ts +168 -11
  57. package/dist/wasm/isard.js +234 -9
  58. package/dist/wasm/isard_core.d.ts +28 -3
  59. package/dist/wasm/isard_core.js +77 -4
  60. package/dist/wasm/isard_points.d.ts +43 -0
  61. package/dist/wasm/isard_points.js +212 -0
  62. package/package.json +17 -1
package/dist/map.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { type Control, type ControlPosition } from './controls.ts';
2
2
  import { ExtrusionLayer, type ExtrusionOptions } from './extrusions.ts';
3
3
  import { CircleLayer, type CircleOptions, FillLayer, type FillOptions, LineLayer, type LineOptions } from './features.ts';
4
- import { GroundLayer, type GroundOptions, type Layer, NetworkLayer, RasterLayer, Tiles3dLayer } from './layers.ts';
4
+ import { type ImageOptions, type Pixels } from './icons.ts';
5
+ import { CustomLayer, type CustomLayerInterface, GroundLayer, type GroundOptions, type Layer, ModelLayer, type ModelOptions, NetworkLayer, RasterLayer, Tiles3dLayer } from './layers.ts';
6
+ import { PointCloudLayer, type PointCloudOptions } from './pointclouds.ts';
5
7
  import { SymbolLayer, type SymbolOptions } from './symbols.ts';
6
8
  import type { AnimationOptions, AtmosphereOptions, Basemap, FeatureHit, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, NetworkOptions, Point, Projection, RasterOptions, TerrainModel, TerrainOptions, Tiles3dOptions } from './types.ts';
7
9
  import { VectorTiles, type VectorTilesOptions } from './vectortiles.ts';
@@ -72,6 +74,8 @@ export declare class GeoMap {
72
74
  /** The last frame found everything loaded ('idle' sent) */
73
75
  private settled;
74
76
  private inFrame;
77
+ /** The camera of the frame being drawn: what `getCamera` gives the custom layers while the engine is busy */
78
+ private frameCamera;
75
79
  /** Vector tiles made by `vectorTiles()`, destroyed with the map */
76
80
  private readonly vectorSets;
77
81
  /** The workers drawing vector tiles, shared by every `VectorTiles` of the map; created on first use */
@@ -92,8 +96,8 @@ export declare class GeoMap {
92
96
  private createEngine;
93
97
  /** The decoding workers of 3D Tiles, with the engine that has them (they run its module) and unless turned off */
94
98
  private startLoader;
95
- /** A 3D Tiles layer on the core engine: loads the engine with 3D Tiles, then rebuilds the map on it (camera,
96
- * settings and layers kept, as after a context loss); the layer is created then */
99
+ /** A 3D Tiles layer or a model on the core engine: loads the engine with 3D Tiles, then rebuilds the map on it
100
+ * (camera, settings and layers kept, as after a context loss); the layer is created then */
97
101
  private upgrade;
98
102
  /** Downloads and compiles the engine with 3D Tiles once the map is idle (`tiles3d: 'preload'`), without running
99
103
  * it: the first 3D Tiles layer then only instantiates it */
@@ -193,6 +197,18 @@ export declare class GeoMap {
193
197
  addExtrusions<P = Record<string, unknown>>(options?: ExtrusionOptions<P>): ExtrusionLayer<P>;
194
198
  /** Text labels from GeoJSON (points, the middle of lines, the center of polygons), placed without overlaps */
195
199
  addSymbols<P = Record<string, unknown>>(options: SymbolOptions<P>): SymbolLayer<P>;
200
+ /**
201
+ * An image for the symbol layers' icons (`icon: name`), as MapLibre's `addImage`: its pixels (an image, a canvas,
202
+ * `ImageData`, or RGBA bytes with their size), `pixelRatio` 2 for an image drawn at twice its CSS size; `sdf` for a
203
+ * signed distance field drawn in the `iconColor`; `stretchX`, `stretchY` and `content` for an icon stretched around its
204
+ * text. An image of the same name is replaced
205
+ */
206
+ addImage(name: string, image: Pixels | (CanvasImageSource & {
207
+ width: number;
208
+ height: number;
209
+ }), options?: ImageOptions): void;
210
+ hasImage(name: string): boolean;
211
+ removeImage(name: string): void;
196
212
  /**
197
213
  * Vector tiles with a MapLibre style (OpenFreeMap's Positron by default): `tiles.raster(…)` draws its fills and lines
198
214
  * (or a subset) as a raster layer, `tiles.labels(…)` its labels. `setBasemap('osm-positron')` does both for the
@@ -212,6 +228,24 @@ export declare class GeoMap {
212
228
  private place;
213
229
  /** Textured meshes in 3D Tiles (photogrammetry, buildings), drawn with depth over the basemap */
214
230
  addTiles3d(options: Tiles3dOptions): Tiles3dLayer;
231
+ /**
232
+ * A point cloud (see `PointCloudOptions`): a COPC file read by range requests (IGN's LiDAR HD…), or 3D Tiles
233
+ * points; with depth, on the engine with 3D Tiles; under `before` if given
234
+ */
235
+ addPointCloud(options: PointCloudOptions & {
236
+ before?: Layer;
237
+ }): PointCloudLayer;
238
+ /** A glTF model drawn at positions (see `ModelOptions`), with depth; on the engine with 3D Tiles, as they are */
239
+ addModels(options: ModelOptions & {
240
+ before?: Layer;
241
+ }): ModelLayer;
242
+ /**
243
+ * The page's own WebGL drawing at its place among the layers, in every frame (see `CustomLayerInterface`): raw
244
+ * WebGL, three.js… with the matrices of the frame (`frame.matrixAt`); under `before` if given
245
+ */
246
+ addCustomLayer(layer: CustomLayerInterface, options?: {
247
+ before?: Layer;
248
+ }): CustomLayer;
215
249
  /** Asks for a frame; several calls before the next frame draw it once */
216
250
  requestRender(): void;
217
251
  private frame;
package/dist/map.js CHANGED
@@ -12,8 +12,10 @@ import { ExtrusionLayer, extrusionStyle } from "./extrusions.js";
12
12
  import { CircleLayer, circleStyle, FillLayer, fillStyle, LineLayer, lineStyle, } from "./features.js";
13
13
  import { Glyphs } from "./glyphs.js";
14
14
  import { equirectangularGrid, gridLoader, gridZooms, resolveGrid } from "./grid.js";
15
+ import { Icons, readPixels } from "./icons.js";
15
16
  import { Interaction } from "./interaction.js";
16
- import { GroundLayer, NetworkLayer, RasterLayer, Tiles3dLayer } from "./layers.js";
17
+ import { CustomLayer, GroundLayer, ModelLayer, NetworkLayer, RasterLayer, Tiles3dLayer, } from "./layers.js";
18
+ import { PointCloudLayer } from "./pointclouds.js";
17
19
  import { atmosphereSettings, bodySettings, haloSettings, rgb, terrainSettings, } from "./settings.js";
18
20
  import { injectStyle } from "./style.js";
19
21
  import { SymbolLayer, symbolStyle } from "./symbols.js";
@@ -56,6 +58,7 @@ function checkBasemap(basemap) {
56
58
  export class GeoMap {
57
59
  canvas;
58
60
  #glyphs;
61
+ #icons;
59
62
  /** Layers new layers go under */
60
63
  onTop = new Set();
61
64
  /** The vector basemap, if any: its tiles' workers and its labels */
@@ -127,6 +130,8 @@ export class GeoMap {
127
130
  /** The last frame found everything loaded ('idle' sent) */
128
131
  settled = false;
129
132
  inFrame = false;
133
+ /** The camera of the frame being drawn: what `getCamera` gives the custom layers while the engine is busy */
134
+ frameCamera;
130
135
  /** Vector tiles made by `vectorTiles()`, destroyed with the map */
131
136
  vectorSets = new Set();
132
137
  /** The workers drawing vector tiles, shared by every `VectorTiles` of the map; created on first use */
@@ -270,8 +275,8 @@ export class GeoMap {
270
275
  startLoader(engine) {
271
276
  return this.tiles3dModule && startTileLoader(this.tiles3dModule, this.tiles3dOptions, engine);
272
277
  }
273
- /** A 3D Tiles layer on the core engine: loads the engine with 3D Tiles, then rebuilds the map on it (camera,
274
- * settings and layers kept, as after a context loss); the layer is created then */
278
+ /** A 3D Tiles layer or a model on the core engine: loads the engine with 3D Tiles, then rebuilds the map on it
279
+ * (camera, settings and layers kept, as after a context loss); the layer is created then */
275
280
  upgrade() {
276
281
  this.upgrading ??= loadFullEngineLater(engineUrl('full', this.tiles3dOptions)).then(({ module, Engine }) => {
277
282
  this.upgrading = undefined;
@@ -316,13 +321,14 @@ export class GeoMap {
316
321
  this.lost = false;
317
322
  this.setCamera(camera);
318
323
  this.#glyphs?.attach(this.#engine);
324
+ this.#icons?.attach(this.#engine);
319
325
  const engine = this.#engine;
320
326
  // No layer refers to an id of the old engine while the others are created
321
327
  for (const layer of this.layers)
322
328
  layer.forgetEngine();
323
329
  for (const layer of this.layers) {
324
330
  // Still waiting for the engine with 3D Tiles (loading, or failed): created with it
325
- if (layer instanceof Tiles3dLayer && !this.tiles3dModule)
331
+ if (layer.needsFullEngine && !this.tiles3dModule)
326
332
  continue;
327
333
  // A layer that fails is reported and left out; the others are created
328
334
  try {
@@ -363,8 +369,9 @@ export class GeoMap {
363
369
  this.layers.splice(index, 1);
364
370
  }
365
371
  add(layer) {
366
- // On the core engine, a 3D Tiles layer waits for the engine with 3D Tiles: it is created with the others on it
367
- if (layer instanceof Tiles3dLayer && !this.tiles3dModule)
372
+ // On the core engine, a 3D Tiles layer or a model waits for the engine with 3D Tiles: it is created with the
373
+ // others on it
374
+ if (layer.needsFullEngine && !this.tiles3dModule)
368
375
  this.upgrade();
369
376
  else
370
377
  layer.attach(this.engine);
@@ -433,7 +440,7 @@ export class GeoMap {
433
440
  }
434
441
  // ── Camera ──
435
442
  getCamera() {
436
- return this.engine.camera();
443
+ return this.frameCamera ?? this.engine.camera();
437
444
  }
438
445
  setCamera(camera) {
439
446
  const c = { ...this.getCamera(), ...camera };
@@ -758,6 +765,34 @@ export class GeoMap {
758
765
  }
759
766
  return this.#glyphs;
760
767
  }
768
+ /** @internal The icons of the symbol layers: the application's images and the vector basemaps' sprites */
769
+ get icons() {
770
+ if (!this.#icons) {
771
+ const gl = this.canvas.getContext('webgl2');
772
+ const max = gl?.getParameter(gl.MAX_TEXTURE_SIZE);
773
+ this.#icons = new Icons(typeof max === 'number' ? max : undefined);
774
+ this.#icons.attach(this.engine);
775
+ }
776
+ return this.#icons;
777
+ }
778
+ /**
779
+ * An image for the symbol layers' icons (`icon: name`), as MapLibre's `addImage`: its pixels (an image, a canvas,
780
+ * `ImageData`, or RGBA bytes with their size), `pixelRatio` 2 for an image drawn at twice its CSS size; `sdf` for a
781
+ * signed distance field drawn in the `iconColor`; `stretchX`, `stretchY` and `content` for an icon stretched around its
782
+ * text. An image of the same name is replaced
783
+ */
784
+ addImage(name, image, options = {}) {
785
+ const pixels = 'data' in image ? image : readPixels(image);
786
+ if (!pixels)
787
+ throw new Error(`isard: the image ${name} has no pixels`);
788
+ this.icons.add(name, pixels, options);
789
+ }
790
+ hasImage(name) {
791
+ return this.#icons?.has(name) ?? false;
792
+ }
793
+ removeImage(name) {
794
+ this.#icons?.remove(name);
795
+ }
761
796
  /**
762
797
  * Vector tiles with a MapLibre style (OpenFreeMap's Positron by default): `tiles.raster(…)` draws its fills and lines
763
798
  * (or a subset) as a raster layer, `tiles.labels(…)` its labels. `setBasemap('osm-positron')` does both for the
@@ -824,8 +859,27 @@ export class GeoMap {
824
859
  }
825
860
  /** Textured meshes in 3D Tiles (photogrammetry, buildings), drawn with depth over the basemap */
826
861
  addTiles3d(options) {
827
- const { url, maxScreenSpaceError = 16, opacity = 1 } = options;
828
- return this.add(new Tiles3dLayer(this, url, { maxScreenSpaceError, opacity }));
862
+ const { url, maxScreenSpaceError = 16, opacity = 1, pointSize } = options;
863
+ return this.add(new Tiles3dLayer(this, url, { maxScreenSpaceError, opacity, ...(pointSize !== undefined ? { pointSize } : {}) }));
864
+ }
865
+ /**
866
+ * A point cloud (see `PointCloudOptions`): a COPC file read by range requests (IGN's LiDAR HD…), or 3D Tiles
867
+ * points; with depth, on the engine with 3D Tiles; under `before` if given
868
+ */
869
+ addPointCloud(options) {
870
+ return this.place(new PointCloudLayer(this, options), options.before);
871
+ }
872
+ /** A glTF model drawn at positions (see `ModelOptions`), with depth; on the engine with 3D Tiles, as they are */
873
+ addModels(options) {
874
+ return this.place(new ModelLayer(this, options), options.before);
875
+ }
876
+ /**
877
+ * The page's own WebGL drawing at its place among the layers, in every frame (see `CustomLayerInterface`): raw
878
+ * WebGL, three.js… with the matrices of the frame (`frame.matrixAt`); under `before` if given
879
+ */
880
+ addCustomLayer(layer, options = {}) {
881
+ const custom = new CustomLayer(this, layer, this.canvas, this, (lon, lat, altitude) => this.engineClass.customMatrix(lon, lat, altitude), () => this.getCamera());
882
+ return this.place(custom, options.before);
829
883
  }
830
884
  // ── Frames ──
831
885
  /** Asks for a frame; several calls before the next frame draw it once */
@@ -899,7 +953,15 @@ export class GeoMap {
899
953
  const t0 = performance.now();
900
954
  // The engine asks for the next frame while it draws (uploads past the frame budget, fades): not ignored
901
955
  this.inFrame = false;
902
- const loading = this.engine.render();
956
+ // As the engine has it now (a listener may have moved it), for the custom layers drawn in the frame
957
+ this.frameCamera = this.layers.some((layer) => layer instanceof CustomLayer) ? this.engine.camera() : undefined;
958
+ let loading;
959
+ try {
960
+ loading = this.engine.render();
961
+ }
962
+ finally {
963
+ this.frameCamera = undefined;
964
+ }
903
965
  this.inFrame = true;
904
966
  // The picking pass sees the frame just drawn; its answer comes on a later frame
905
967
  if (hovered && this.listeners.hoverfeature.size > 0)
@@ -970,12 +1032,16 @@ export class GeoMap {
970
1032
  this.resizeObserver.disconnect();
971
1033
  this.interaction?.destroy();
972
1034
  this.loader?.destroy();
1035
+ // Custom layers free their WebGL objects, models their data, while the context is there
1036
+ for (const layer of this.layers)
1037
+ layer.dispose();
973
1038
  this.engine.free();
974
1039
  // Work still under way (labels being shaped, tiles arriving, timers) may call the engine after this: it finds one
975
1040
  // that does nothing, and nothing is drawn again
976
1041
  this.#engine = destroyedEngine(camera);
977
1042
  // Glyph ranges still arriving upload to it, not to the freed engine
978
1043
  this.#glyphs?.attach(this.#engine);
1044
+ this.#icons?.attach(this.#engine);
979
1045
  this.lost = true;
980
1046
  for (const control of [...this.controls.keys()])
981
1047
  this.removeControl(control);