itowns 2.44.3-next.4 → 2.44.3-next.40

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 (109) hide show
  1. package/CODING.md +1 -1
  2. package/CONTRIBUTORS.md +1 -0
  3. package/dist/debug.js +1 -1
  4. package/dist/debug.js.map +1 -1
  5. package/dist/itowns.js +1 -1
  6. package/dist/itowns.js.LICENSE.txt +0 -2
  7. package/dist/itowns.js.map +1 -1
  8. package/dist/itowns_widgets.js +1 -1
  9. package/dist/itowns_widgets.js.map +1 -1
  10. package/examples/3dtiles_loader.html +123 -48
  11. package/examples/config.json +3 -10
  12. package/examples/copc_simple_loader.html +15 -5
  13. package/examples/effects_stereo.html +2 -2
  14. package/examples/entwine_3d_loader.html +3 -1
  15. package/examples/entwine_simple_loader.html +1 -1
  16. package/examples/images/itowns_logo.svg +123 -0
  17. package/examples/js/plugins/COGParser.js +1 -1
  18. package/examples/jsm/OGC3DTilesHelper.js +6 -1
  19. package/examples/layers/JSONLayers/GeoidMNT.json +3 -1
  20. package/examples/misc_collada.html +2 -2
  21. package/examples/source_file_geojson_3d.html +0 -1
  22. package/examples/source_file_kml_raster_usgs.html +0 -1
  23. package/examples/source_stream_wfs_raster.html +0 -7
  24. package/examples/vector_tile_mapbox_raster.html +91 -0
  25. package/examples/view_3d_map_webxr.html +3 -1
  26. package/examples/view_multi_25d.html +2 -2
  27. package/lib/Controls/GlobeControls.js +45 -28
  28. package/lib/Controls/StateControl.js +5 -2
  29. package/lib/Converter/Feature2Mesh.js +10 -4
  30. package/lib/Converter/Feature2Texture.js +6 -1
  31. package/lib/Converter/convertToTile.js +3 -8
  32. package/lib/Converter/textureConverter.js +4 -5
  33. package/lib/Core/Deprecated/Undeprecator.js +0 -1
  34. package/lib/Core/Feature.js +3 -4
  35. package/lib/Core/Geographic/Coordinates.js +143 -132
  36. package/lib/Core/Geographic/Crs.js +140 -145
  37. package/lib/Core/Geographic/Extent.js +221 -397
  38. package/lib/Core/Geographic/GeoidGrid.js +1 -1
  39. package/lib/Core/MainLoop.js +1 -3
  40. package/lib/Core/Math/Ellipsoid.js +62 -21
  41. package/lib/Core/Prefab/Globe/Atmosphere.js +4 -8
  42. package/lib/Core/Prefab/Globe/GlobeLayer.js +22 -15
  43. package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +111 -0
  44. package/lib/Core/Prefab/GlobeView.js +2 -7
  45. package/lib/Core/Prefab/Planar/PlanarLayer.js +17 -11
  46. package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +43 -43
  47. package/lib/Core/Prefab/TileBuilder.js +42 -40
  48. package/lib/Core/Prefab/computeBufferTileGeometry.js +195 -130
  49. package/lib/Core/Scheduler/Cache.js +1 -240
  50. package/lib/Core/Style.js +34 -495
  51. package/lib/Core/StyleOptions.js +486 -0
  52. package/lib/Core/Tile/Tile.js +207 -0
  53. package/lib/Core/Tile/TileGrid.js +49 -0
  54. package/lib/Core/TileGeometry.js +112 -28
  55. package/lib/Core/TileMesh.js +3 -3
  56. package/lib/Core/View.js +15 -8
  57. package/lib/Layer/C3DTilesLayer.js +20 -16
  58. package/lib/Layer/ColorLayer.js +35 -9
  59. package/lib/Layer/CopcLayer.js +7 -2
  60. package/lib/Layer/ElevationLayer.js +39 -7
  61. package/lib/Layer/EntwinePointTileLayer.js +14 -7
  62. package/lib/Layer/FeatureGeometryLayer.js +20 -6
  63. package/lib/Layer/GeometryLayer.js +42 -11
  64. package/lib/Layer/LabelLayer.js +45 -27
  65. package/lib/Layer/Layer.js +92 -61
  66. package/lib/Layer/OGC3DTilesLayer.js +212 -56
  67. package/lib/Layer/OrientedImageLayer.js +11 -5
  68. package/lib/Layer/PointCloudLayer.js +76 -30
  69. package/lib/Layer/Potree2Layer.js +9 -2
  70. package/lib/Layer/PotreeLayer.js +10 -3
  71. package/lib/Layer/RasterLayer.js +12 -2
  72. package/lib/Layer/TiledGeometryLayer.js +69 -13
  73. package/lib/Main.js +2 -2
  74. package/lib/Parser/GeoJsonParser.js +1 -1
  75. package/lib/Parser/VectorTileParser.js +42 -29
  76. package/lib/Parser/XbilParser.js +14 -2
  77. package/lib/Provider/Fetcher.js +5 -1
  78. package/lib/Provider/URLBuilder.js +22 -11
  79. package/lib/Renderer/Camera.js +1 -1
  80. package/lib/Renderer/Label2DRenderer.js +9 -7
  81. package/lib/Renderer/OBB.js +11 -13
  82. package/lib/Renderer/PointsMaterial.js +5 -5
  83. package/lib/Renderer/RasterTile.js +1 -2
  84. package/lib/Renderer/SphereHelper.js +0 -6
  85. package/lib/Source/CopcSource.js +13 -2
  86. package/lib/Source/EntwinePointTileSource.js +14 -4
  87. package/lib/Source/FileSource.js +9 -10
  88. package/lib/Source/OrientedImageSource.js +2 -2
  89. package/lib/Source/Source.js +26 -46
  90. package/lib/Source/TMSSource.js +10 -9
  91. package/lib/Source/VectorTilesSource.js +38 -34
  92. package/lib/Source/WFSSource.js +18 -13
  93. package/lib/Source/WMSSource.js +56 -18
  94. package/lib/Source/WMTSSource.js +13 -7
  95. package/lib/ThreeExtended/libs/ktx-parse.module.js +310 -274
  96. package/lib/ThreeExtended/loaders/DRACOLoader.js +3 -2
  97. package/lib/ThreeExtended/loaders/GLTFLoader.js +6 -3
  98. package/lib/ThreeExtended/loaders/KTX2Loader.js +144 -60
  99. package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
  100. package/lib/Utils/CameraUtils.js +1 -1
  101. package/lib/Utils/gui/C3DTilesStyle.js +2 -3
  102. package/lib/Utils/placeObjectOnGround.js +0 -1
  103. package/package.json +10 -8
  104. package/examples/3dtiles_25d.html +0 -120
  105. package/examples/3dtiles_basic.html +0 -94
  106. package/examples/3dtiles_batch_table.html +0 -86
  107. package/examples/3dtiles_ion.html +0 -126
  108. package/examples/3dtiles_pointcloud.html +0 -95
  109. package/lib/Core/Prefab/Globe/BuilderEllipsoidTile.js +0 -110
@@ -1,24 +1,28 @@
1
1
  import * as THREE from 'three';
2
- import { TilesRenderer, GLTFStructuralMetadataExtension, GLTFMeshFeaturesExtension, GLTFCesiumRTCExtension, CesiumIonAuthPlugin, GoogleCloudAuthPlugin, ImplicitTilingPlugin } from '3d-tiles-renderer';
2
+ import { TilesRenderer } from '3d-tiles-renderer';
3
+ import { GLTFStructuralMetadataExtension, GLTFMeshFeaturesExtension, GLTFCesiumRTCExtension, CesiumIonAuthPlugin, GoogleCloudAuthPlugin, ImplicitTilingPlugin
4
+ // eslint-disable-next-line import/no-unresolved
5
+ } from '3d-tiles-renderer/plugins';
3
6
  import GeometryLayer from "./GeometryLayer.js";
4
7
  import iGLTFLoader from "../Parser/iGLTFLoader.js";
5
8
  import { DRACOLoader } from "../ThreeExtended/loaders/DRACOLoader.js";
6
9
  import { KTX2Loader } from "../ThreeExtended/loaders/KTX2Loader.js";
7
10
  import ReferLayerProperties from "./ReferencingLayerProperties.js";
8
11
  import PointsMaterial, { PNTS_MODE, PNTS_SHAPE, PNTS_SIZE_MODE, ClassificationScheme } from "../Renderer/PointsMaterial.js";
12
+ import { VIEW_EVENTS } from "../Core/View.js";
9
13
  const _raycaster = new THREE.Raycaster();
10
14
 
15
+ // Stores lruCache, downloadQueue and parseQueue for each id of view {@link View}
16
+ // every time a tileset has been added
17
+ // https://github.com/iTowns/itowns/issues/2426
18
+ const viewers = {};
19
+
11
20
  // Internal instance of GLTFLoader, passed to 3d-tiles-renderer-js to support GLTF 1.0 and 2.0
12
21
  // Temporary exported to be used in deprecated B3dmParser
13
22
  export const itownsGLTFLoader = new iGLTFLoader();
14
23
  itownsGLTFLoader.register(() => new GLTFMeshFeaturesExtension());
15
24
  itownsGLTFLoader.register(() => new GLTFStructuralMetadataExtension());
16
25
  itownsGLTFLoader.register(() => new GLTFCesiumRTCExtension());
17
-
18
- // Instantiated by the first tileset. Used to share cache and download and parse queues between tilesets
19
- let lruCache = null;
20
- let downloadQueue = null;
21
- let parseQueue = null;
22
26
  export const OGC3DTILES_LAYER_EVENTS = {
23
27
  /**
24
28
  * Fired when a new root or child tile set is loaded
@@ -52,7 +56,19 @@ export const OGC3DTILES_LAYER_EVENTS = {
52
56
  * @property {Object} tile - the tile metadata from the tileset
53
57
  * @property {boolean} visible - the tile visible state
54
58
  */
55
- TILE_VISIBILITY_CHANGE: 'tile-visibility-change'
59
+ TILE_VISIBILITY_CHANGE: 'tile-visibility-change',
60
+ /**
61
+ * Fired when a new batch of tiles start loading (can be fired multiple times, e.g. when the camera moves and new tiles
62
+ * start loading)
63
+ * @event OGC3DTilesLayer#tiles-load-start
64
+ */
65
+ TILES_LOAD_START: 'tiles-load-start',
66
+ /**
67
+ * Fired when all visible tiles are loaded (can be fired multiple times, e.g. when the camera moves and new tiles
68
+ * are loaded)
69
+ * @event OGC3DTilesLayer#tiles-load-end
70
+ */
71
+ TILES_LOAD_END: 'tiles-load-end'
56
72
  };
57
73
 
58
74
  /**
@@ -92,32 +108,151 @@ export function enableKtx2Loader(path, renderer) {
92
108
  ktx2Loader.detectSupport(renderer);
93
109
  itownsGLTFLoader.setKTX2Loader(ktx2Loader);
94
110
  }
111
+
112
+ /**
113
+ * Enable loading 3D Tiles and GLTF with
114
+ * [meshopt](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_meshopt_compression/README.md) compression extension.
115
+ *
116
+ * @param {MeshOptDecoder.constructor} MeshOptDecoder - The Meshopt decoder
117
+ * module.
118
+ *
119
+ * @example
120
+ * import * as itowns from 'itowns';
121
+ * import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';
122
+ *
123
+ * // Enable support of EXT_meshopt_compression
124
+ * itowns.enableMeshoptDecoder(MeshoptDecoder);
125
+ */
126
+ export function enableMeshoptDecoder(MeshOptDecoder) {
127
+ if (!MeshOptDecoder) {
128
+ throw new Error('MeshOptDecoder module is mandatory');
129
+ }
130
+ itownsGLTFLoader.setMeshoptDecoder(MeshOptDecoder);
131
+ }
132
+ async function getMeshFeatures(meshFeatures, options) {
133
+ const {
134
+ faceIndex,
135
+ barycoord
136
+ } = options;
137
+ const features = await meshFeatures.getFeaturesAsync(faceIndex, barycoord);
138
+ return {
139
+ features,
140
+ featureIds: meshFeatures.getFeatureInfo()
141
+ };
142
+ }
143
+ function getStructuralMetadata(structuralMetadata, options) {
144
+ const {
145
+ index,
146
+ faceIndex,
147
+ barycoord,
148
+ tableIndices,
149
+ features
150
+ } = options;
151
+ const tableData = [];
152
+ if (tableIndices !== undefined && features !== undefined) {
153
+ structuralMetadata.getPropertyTableData(tableIndices, features, tableData);
154
+ }
155
+ const attributeData = [];
156
+ if (index !== undefined) {
157
+ structuralMetadata.getPropertyAttributeData(index, attributeData);
158
+ }
159
+ const textureData = [];
160
+ if (faceIndex !== undefined) {
161
+ structuralMetadata.getPropertyTextureData(faceIndex, barycoord, textureData);
162
+ }
163
+ const metadata = [...tableData, ...textureData, ...attributeData];
164
+ return metadata;
165
+ }
166
+ async function getMetadataFromIntersection(intersection) {
167
+ const {
168
+ point,
169
+ object,
170
+ face,
171
+ faceIndex
172
+ } = intersection;
173
+ const {
174
+ meshFeatures,
175
+ structuralMetadata
176
+ } = object.userData;
177
+ const barycoord = new THREE.Vector3();
178
+ if (face) {
179
+ const position = object.geometry.getAttribute('position');
180
+ const triangle = new THREE.Triangle().setFromAttributeAndIndices(position, face.a, face.b, face.c);
181
+ triangle.a.applyMatrix4(object.matrixWorld);
182
+ triangle.b.applyMatrix4(object.matrixWorld);
183
+ triangle.c.applyMatrix4(object.matrixWorld);
184
+ triangle.getBarycoord(point, barycoord);
185
+ } else {
186
+ barycoord.set(0, 0, 0);
187
+ }
188
+
189
+ // EXT_mesh_features
190
+ const {
191
+ features,
192
+ featureIds
193
+ } = meshFeatures ? await getMeshFeatures(meshFeatures, {
194
+ faceIndex,
195
+ barycoord
196
+ }) : {};
197
+ const tableIndices = featureIds?.map(p => p.propertyTable);
198
+
199
+ // EXT_structural_metadata
200
+ const metadata = structuralMetadata ? getStructuralMetadata(structuralMetadata, {
201
+ ...intersection,
202
+ barycoord,
203
+ tableIndices,
204
+ features
205
+ }) : [];
206
+ return metadata;
207
+ }
95
208
  class OGC3DTilesLayer extends GeometryLayer {
96
209
  /**
97
210
  * Layer for [3D Tiles](https://www.ogc.org/standard/3dtiles/) datasets.
211
+ *
212
+ * Advanced configuration note: 3D Tiles rendering is delegated to 3DTilesRendererJS that exposes several
213
+ * configuration options accessible through the tilesRenderer property of this class. see the
214
+ * [3DTilesRendererJS doc](https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/README.md). Also note that
215
+ * the cache is shared amongst 3D tiles layers and can be configured through tilesRenderer.lruCache (see the
216
+ * [following documentation](https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/README.md#lrucache-1).
217
+ *
98
218
  * @extends Layer
99
219
  *
100
220
  * @param {String} id - unique layer id.
101
221
  * @param {Object} config - layer specific configuration
102
222
  * @param {OGC3DTilesSource} config.source - data source configuration
103
- * @param {String} [config.pntsMode= PNTS_MODE.COLOR] Point cloud coloring mode (passed to {@link PointsMaterial}).
223
+ * @param {String} [config.pntsMode = PNTS_MODE.COLOR] Point cloud coloring mode (passed to {@link PointsMaterial}).
104
224
  * Only 'COLOR' or 'CLASSIFICATION' are possible. COLOR uses RGB colors of the points,
105
225
  * CLASSIFICATION uses a classification property of the batch table to color points.
106
- * @param {ClassificationScheme} [config.classificationScheme] {@link PointsMaterial} classification scheme
107
- * @param {String} [config.pntsShape= PNTS_SHAPE.CIRCLE] Point cloud point shape. Only 'CIRCLE' or 'SQUARE' are possible.
226
+ * @param {ClassificationScheme} [config.classificationScheme = ClassificationScheme.DEFAULT] {@link PointsMaterial} classification scheme
227
+ * @param {String} [config.pntsShape = PNTS_SHAPE.CIRCLE] Point cloud point shape. Only 'CIRCLE' or 'SQUARE' are possible.
108
228
  * (passed to {@link PointsMaterial}).
109
- * @param {String} [config.pntsSizeMode= PNTS_SIZE_MODE.VALUE] {@link PointsMaterial} Point cloud size mode (passed to {@link PointsMaterial}).
229
+ * @param {String} [config.pntsSizeMode = PNTS_SIZE_MODE.VALUE] {@link PointsMaterial} Point cloud size mode (passed to {@link PointsMaterial}).
110
230
  * Only 'VALUE' or 'ATTENUATED' are possible. VALUE use constant size, ATTENUATED compute size depending on distance
111
231
  * from point to camera.
112
- * @param {Number} [config.pntsMinAttenuatedSize=1] Minimum scale used by 'ATTENUATED' size mode.
113
- * @param {Number} [config.pntsMaxAttenuatedSize=7] Maximum scale used by 'ATTENUATED' size mode.
232
+ * @param {Number} [config.pntsMinAttenuatedSize = 3] Minimum scale used by 'ATTENUATED' size mode.
233
+ * @param {Number} [config.pntsMaxAttenuatedSize = 10] Maximum scale used by 'ATTENUATED' size mode.
114
234
  */
115
235
  constructor(id, config) {
116
- super(id, new THREE.Group(), {
117
- source: config.source
118
- });
236
+ const {
237
+ pntsMode = PNTS_MODE.COLOR,
238
+ pntsShape = PNTS_SHAPE.CIRCLE,
239
+ classification = ClassificationScheme.DEFAULT,
240
+ pntsSizeMode = PNTS_SIZE_MODE.VALUE,
241
+ pntsMinAttenuatedSize = 3,
242
+ pntsMaxAttenuatedSize = 10,
243
+ ...geometryLayerConfig
244
+ } = config;
245
+ super(id, new THREE.Group(), geometryLayerConfig);
119
246
  this.isOGC3DTilesLayer = true;
120
- this._handlePointsMaterialConfig(config);
247
+ // Store points material config so they can be used later to substitute points tiles material
248
+ // by our own PointsMaterial. These properties should eventually be managed through the Style API
249
+ // (see https://github.com/iTowns/itowns/issues/2336)
250
+ this.pntsMode = pntsMode;
251
+ this.pntsShape = pntsShape;
252
+ this.classification = classification;
253
+ this.pntsSizeMode = pntsSizeMode;
254
+ this.pntsMinAttenuatedSize = pntsMinAttenuatedSize;
255
+ this.pntsMaxAttenuatedSize = pntsMaxAttenuatedSize;
121
256
  this.tilesRenderer = new TilesRenderer(this.source.url);
122
257
  if (config.source.isOGC3DTilesIonSource) {
123
258
  this.tilesRenderer.registerPlugin(new CesiumIonAuthPlugin({
@@ -133,8 +268,6 @@ class OGC3DTilesLayer extends GeometryLayer {
133
268
  }
134
269
  this.tilesRenderer.registerPlugin(new ImplicitTilingPlugin());
135
270
  this.tilesRenderer.manager.addHandler(/\.gltf$/, itownsGLTFLoader);
136
- this._setupCacheAndQueues();
137
- this._setupEvents();
138
271
  this.object3d.add(this.tilesRenderer.group);
139
272
 
140
273
  // Add an initialization step that is resolved when the root tileset is loaded (see this._setup below), meaning
@@ -159,39 +292,26 @@ class OGC3DTilesLayer extends GeometryLayer {
159
292
  }
160
293
 
161
294
  /**
162
- * Store points material config so they can be used later to substitute points tiles material by our own PointsMaterial
163
- * These properties should eventually be managed through the Style API (see https://github.com/iTowns/itowns/issues/2336)
164
- * @param {Object} config - points material configuration as passed to the layer constructor.
165
- * @private
166
- */
167
- _handlePointsMaterialConfig(config) {
168
- this.pntsMode = config.pntsMode ?? PNTS_MODE.COLOR;
169
- this.pntsShape = config.pntsShape ?? PNTS_SHAPE.CIRCLE;
170
- this.classification = config.classification ?? ClassificationScheme.DEFAULT;
171
- this.pntsSizeMode = config.pntsSizeMode ?? PNTS_SIZE_MODE.VALUE;
172
- this.pntsMinAttenuatedSize = config.pntsMinAttenuatedSize || 1;
173
- this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize || 7;
174
- }
175
-
176
- /**
177
- * Sets the lruCache and download and parse queues so they are shared amongst all tilesets.
295
+ * Sets the lruCache and download and parse queues so they are shared amongst
296
+ * all tilesets from a same {@link View} view.
297
+ * @param {View} view - view associated to this layer.
178
298
  * @private
179
299
  */
180
- _setupCacheAndQueues() {
181
- if (lruCache === null) {
182
- lruCache = this.tilesRenderer.lruCache;
300
+ _setupCacheAndQueues(view) {
301
+ const id = view.id;
302
+ if (viewers[id]) {
303
+ this.tilesRenderer.lruCache = viewers[id].lruCache;
304
+ this.tilesRenderer.downloadQueue = viewers[id].downloadQueue;
305
+ this.tilesRenderer.parseQueue = viewers[id].parseQueue;
183
306
  } else {
184
- this.tilesRenderer.lruCache = lruCache;
185
- }
186
- if (downloadQueue === null) {
187
- downloadQueue = this.tilesRenderer.downloadQueue;
188
- } else {
189
- this.tilesRenderer.downloadQueue = downloadQueue;
190
- }
191
- if (parseQueue === null) {
192
- parseQueue = this.tilesRenderer.parseQueue;
193
- } else {
194
- this.tilesRenderer.parseQueue = parseQueue;
307
+ viewers[id] = {
308
+ lruCache: this.tilesRenderer.lruCache,
309
+ downloadQueue: this.tilesRenderer.downloadQueue,
310
+ parseQueue: this.tilesRenderer.parseQueue
311
+ };
312
+ view.addEventListener(VIEW_EVENTS.DISPOSED, evt => {
313
+ delete viewers[evt.target.id];
314
+ });
195
315
  }
196
316
  }
197
317
 
@@ -225,16 +345,19 @@ class OGC3DTilesLayer extends GeometryLayer {
225
345
  this._res();
226
346
  }
227
347
  });
228
- this.tilesRenderer.addEventListener('load-model', _ref => {
229
- let {
348
+ this.tilesRenderer.addEventListener('load-model', e => {
349
+ const {
230
350
  scene
231
- } = _ref;
351
+ } = e;
232
352
  scene.traverse(obj => {
233
353
  this._assignFinalMaterial(obj);
234
354
  this._assignFinalAttributes(obj);
235
355
  });
236
356
  view.notifyChange(this);
237
357
  });
358
+ this._setupCacheAndQueues(view);
359
+ this._setupEvents();
360
+
238
361
  // Start loading tileset and tiles
239
362
  this.tilesRenderer.update();
240
363
  }
@@ -298,10 +421,36 @@ class OGC3DTilesLayer extends GeometryLayer {
298
421
  this.tilesRenderer.dispose();
299
422
  }
300
423
 
424
+ /**
425
+ * Get the [metadata](https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata)
426
+ * of the closest intersected object from a list of intersections.
427
+ *
428
+ * This method retrieves structured metadata stored in GLTF 2.0 assets using
429
+ * the [`EXT_structural_metadata`](https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata)
430
+ * extension.
431
+ *
432
+ * Internally, it uses the closest intersected point to index metadata
433
+ * stored in property attributes and textures.
434
+ *
435
+ * If present in GLTF 2.0 assets, this method leverages the
436
+ * [`EXT_mesh_features`](`https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features)
437
+ * extension and the returned featured to index metadata stored in property tables.
438
+ *
439
+ * @param {Array<THREE.Intersection>} intersections
440
+ * @returns {Promise<Object | null>} - the intersected object's metadata
441
+ */
442
+ async getMetadataFromIntersections(intersections) {
443
+ if (!intersections.length) {
444
+ return null;
445
+ }
446
+ const metadata = await getMetadataFromIntersection(intersections[0]);
447
+ return metadata;
448
+ }
449
+
301
450
  /**
302
451
  * Get the attributes for the closest intersection from a list of
303
452
  * intersects.
304
- * @param {Array} intersects - An array containing all
453
+ * @param {Array<THREE.Intersection>} intersects - An array containing all
305
454
  * objects picked under mouse coordinates computed with view.pickObjectsAt(..).
306
455
  * @returns {Object | null} - An object containing
307
456
  */
@@ -312,7 +461,8 @@ class OGC3DTilesLayer extends GeometryLayer {
312
461
  const {
313
462
  face,
314
463
  index,
315
- object
464
+ object,
465
+ instanceId
316
466
  } = intersects[0];
317
467
 
318
468
  /** @type{number|null} */
@@ -320,7 +470,7 @@ class OGC3DTilesLayer extends GeometryLayer {
320
470
  if (object.isPoints && index) {
321
471
  batchId = object.geometry.getAttribute('_BATCHID')?.getX(index) ?? index;
322
472
  } else if (object.isMesh && face) {
323
- batchId = object.geometry.getAttribute('_BATCHID')?.getX(face.a);
473
+ batchId = object.geometry.getAttribute('_BATCHID')?.getX(face.a) ?? instanceId;
324
474
  }
325
475
  if (batchId === undefined) {
326
476
  return null;
@@ -350,7 +500,13 @@ class OGC3DTilesLayer extends GeometryLayer {
350
500
  _raycaster.near = camera.near;
351
501
  _raycaster.far = camera.far;
352
502
  _raycaster.firstHitOnly = true;
353
- _raycaster.intersectObject(this.tilesRenderer.group, true, target);
503
+ const picked = _raycaster.intersectObject(this.tilesRenderer.group, true);
504
+ // Store the layer of the picked object to conform to the interface of what's returned by Picking.js (used for
505
+ // other GeometryLayers
506
+ picked.forEach(p => {
507
+ p.layer = this;
508
+ });
509
+ target.push(...picked);
354
510
  return target;
355
511
  }
356
512
 
@@ -96,14 +96,20 @@ class OrientedImageLayer extends GeometryLayer {
96
96
  */
97
97
  constructor(id) {
98
98
  let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
99
- /* istanbul ignore next */
99
+ const {
100
+ backgroundDistance,
101
+ background = createBackground(backgroundDistance),
102
+ onPanoChanged = () => {},
103
+ getCamerasNameFromFeature = () => {},
104
+ ...geometryOptions
105
+ } = config;
100
106
  if (config.projection) {
101
107
  console.warn('OrientedImageLayer projection parameter is deprecated, use crs instead.');
102
108
  config.crs = config.crs || config.projection;
103
109
  }
104
- super(id, new THREE.Group(), config);
110
+ super(id, new THREE.Group(), geometryOptions);
105
111
  this.isOrientedImageLayer = true;
106
- this.background = config.background || createBackground(config.backgroundDistance);
112
+ this.background = background;
107
113
  if (this.background) {
108
114
  // Add layer id to easily identify the objects later on (e.g. to delete the geometries when deleting the layer)
109
115
  this.background.layer = this.background.layer ?? {};
@@ -115,10 +121,10 @@ class OrientedImageLayer extends GeometryLayer {
115
121
  this.currentPano = undefined;
116
122
 
117
123
  // store a callback to fire event when current panoramic change
118
- this.onPanoChanged = config.onPanoChanged || (() => {});
124
+ this.onPanoChanged = onPanoChanged;
119
125
 
120
126
  // function to get cameras name from panoramic feature
121
- this.getCamerasNameFromFeature = config.getCamerasNameFromFeature || (() => {});
127
+ this.getCamerasNameFromFeature = getCamerasNameFromFeature;
122
128
  const resolve = this.addInitializationStep();
123
129
  this.mergeFeatures = false;
124
130
  this.filteringExtent = false;
@@ -113,6 +113,8 @@ function changeAngleRange(layer) {
113
113
  * @property {number} [maxIntensityRange=1] - The maximal intensity of the
114
114
  * layer. Changing this value will affect the material, if it has the
115
115
  * corresponding uniform. The value is normalized between 0 and 1.
116
+ *
117
+ * @extends GeometryLayer
116
118
  */
117
119
  class PointCloudLayer extends GeometryLayer {
118
120
  /**
@@ -120,8 +122,6 @@ class PointCloudLayer extends GeometryLayer {
120
122
  * Constructs a new instance of a Point Cloud Layer. This should not be used
121
123
  * directly, but rather implemented using `extends`.
122
124
  *
123
- * @extends GeometryLayer
124
- *
125
125
  * @param {string} id - The id of the layer, that should be unique. It is
126
126
  * not mandatory, but an error will be emitted if this layer is added a
127
127
  * {@link View} that already has a layer going by that id.
@@ -130,40 +130,88 @@ class PointCloudLayer extends GeometryLayer {
130
130
  * contains three elements `name, protocol, extent`, these elements will be
131
131
  * available using `layer.name` or something else depending on the property
132
132
  * name. See the list of properties to know which one can be specified.
133
+ * @param {Source} config.source - Description and options of the source See @Layer.
134
+ * @param {number} [options.minElevationRange] - Min value for the elevation range (default value will be taken from the source.metadata).
135
+ * @param {number} [options.maxElevationRange] - Max value for the elevation range (default value will be taken from the source.metadata).
133
136
  */
134
137
  constructor(id) {
135
138
  let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
136
- super(id, config.object3d || new THREE.Group(), config);
139
+ const {
140
+ object3d = new THREE.Group(),
141
+ group = new THREE.Group(),
142
+ bboxes = new THREE.Group(),
143
+ octreeDepthLimit = -1,
144
+ pointBudget = 2000000,
145
+ pointSize = 2,
146
+ sseThreshold = 2,
147
+ minIntensityRange = 1,
148
+ maxIntensityRange = 65536,
149
+ minElevationRange,
150
+ maxElevationRange,
151
+ minAngleRange = -90,
152
+ maxAngleRange = 90,
153
+ material = {},
154
+ mode = PNTS_MODE.COLOR,
155
+ ...geometryLayerConfig
156
+ } = config;
157
+ super(id, object3d, geometryLayerConfig);
158
+
159
+ /**
160
+ * @type {boolean}
161
+ * @readonly
162
+ */
137
163
  this.isPointCloudLayer = true;
138
164
  this.protocol = 'pointcloud';
139
- this.group = config.group || new THREE.Group();
165
+ this.group = group;
140
166
  this.object3d.add(this.group);
141
- this.bboxes = config.bboxes || new THREE.Group();
167
+ this.bboxes = bboxes || new THREE.Group();
142
168
  this.bboxes.visible = false;
143
169
  this.object3d.add(this.bboxes);
144
170
  this.group.updateMatrixWorld();
145
171
 
146
172
  // default config
147
- this.octreeDepthLimit = config.octreeDepthLimit || -1;
148
- this.pointBudget = config.pointBudget || 2000000;
149
- this.pointSize = config.pointSize === 0 || !isNaN(config.pointSize) ? config.pointSize : 4;
150
- this.sseThreshold = config.sseThreshold || 2;
151
- this.defineLayerProperty('minIntensityRange', config.minIntensityRange || 1, changeIntensityRange);
152
- this.defineLayerProperty('maxIntensityRange', config.maxIntensityRange || 65536, changeIntensityRange);
153
- this.defineLayerProperty('minElevationRange', config.minElevationRange || 0, changeElevationRange);
154
- this.defineLayerProperty('maxElevationRange', config.maxElevationRange || 1000, changeElevationRange);
155
- this.defineLayerProperty('minAngleRange', config.minAngleRange || -90, changeAngleRange);
156
- this.defineLayerProperty('maxAngleRange', config.maxAngleRange || 90, changeAngleRange);
157
- this.material = config.material || {};
173
+ /**
174
+ * @type {number}
175
+ */
176
+ this.octreeDepthLimit = octreeDepthLimit;
177
+
178
+ /**
179
+ * @type {number}
180
+ */
181
+ this.pointBudget = pointBudget;
182
+
183
+ /**
184
+ * @type {number}
185
+ */
186
+ this.pointSize = pointSize;
187
+
188
+ /**
189
+ * @type {number}
190
+ */
191
+ this.sseThreshold = sseThreshold;
192
+ this.defineLayerProperty('minIntensityRange', minIntensityRange, changeIntensityRange);
193
+ this.defineLayerProperty('maxIntensityRange', maxIntensityRange, changeIntensityRange);
194
+ this.defineLayerProperty('minElevationRange', minElevationRange, changeElevationRange);
195
+ this.defineLayerProperty('maxElevationRange', maxElevationRange, changeElevationRange);
196
+ this.defineLayerProperty('minAngleRange', minAngleRange, changeAngleRange);
197
+ this.defineLayerProperty('maxAngleRange', maxAngleRange, changeAngleRange);
198
+
199
+ /**
200
+ * @type {THREE.Material}
201
+ */
202
+ this.material = material;
158
203
  if (!this.material.isMaterial) {
159
- config.material = config.material || {};
160
- config.material.intensityRange = new THREE.Vector2(this.minIntensityRange, this.maxIntensityRange);
161
- config.material.elevationRange = new THREE.Vector2(this.minElevationRange, this.maxElevationRange);
162
- config.material.angleRange = new THREE.Vector2(this.minAngleRange, this.maxAngleRange);
163
- this.material = new PointsMaterial(config.material);
204
+ this.material.intensityRange = new THREE.Vector2(this.minIntensityRange, this.maxIntensityRange);
205
+ this.material.elevationRange = new THREE.Vector2(this.minElevationRange, this.maxElevationRange);
206
+ this.material.angleRange = new THREE.Vector2(this.minAngleRange, this.maxAngleRange);
207
+ this.material = new PointsMaterial(this.material);
164
208
  }
165
- this.material.defines = this.material.defines || {};
166
- this.mode = config.mode || PNTS_MODE.COLOR;
209
+ this.mode = mode || PNTS_MODE.COLOR;
210
+
211
+ /**
212
+ * @type {PointCloudNode | undefined}
213
+ */
214
+ this.root = undefined;
167
215
  }
168
216
  preUpdate(context, changeSources) {
169
217
  // See https://cesiumjs.org/hosted-apps/massiveworlds/downloads/Ring/WorldScaleTerrainRendering.pptx
@@ -245,9 +293,6 @@ class PointCloudLayer extends GeometryLayer {
245
293
  redraw: true,
246
294
  earlyDropFunction: cmd => !cmd.requester.visible || !this.visible
247
295
  }).then(pts => {
248
- if (this.onPointsCreated) {
249
- this.onPointsCreated(layer, pts);
250
- }
251
296
  elt.obj = pts;
252
297
  // store tightbbox to avoid ping-pong (bbox = larger => visible, tight => invisible)
253
298
  elt.tightbbox = pts.tightbbox;
@@ -256,11 +301,12 @@ class PointCloudLayer extends GeometryLayer {
256
301
  // be added nor cleaned
257
302
  this.group.add(elt.obj);
258
303
  elt.obj.updateMatrixWorld(true);
259
- elt.promise = null;
260
- }, err => {
261
- if (err.isCancelledCommandException) {
262
- elt.promise = null;
304
+ }).catch(err => {
305
+ if (!err.isCancelledCommandException) {
306
+ return err;
263
307
  }
308
+ }).finally(() => {
309
+ elt.promise = null;
264
310
  });
265
311
  }
266
312
  }
@@ -99,13 +99,13 @@ function parseAttributes(jsonAttributes) {
99
99
  * @property {boolean} isPotreeLayer - Used to checkout whether this layer
100
100
  * is a Potree2Layer. Default is `true`. You should not change this, as it is
101
101
  * used internally for optimisation.
102
+ *
103
+ * @extends PointCloudLayer
102
104
  */
103
105
  class Potree2Layer extends PointCloudLayer {
104
106
  /**
105
107
  * Constructs a new instance of Potree2 layer.
106
108
  *
107
- * @extends PointCloudLayer
108
- *
109
109
  * @example
110
110
  * // Create a new point cloud layer
111
111
  * const points = new Potree2Layer('points',
@@ -132,6 +132,11 @@ class Potree2Layer extends PointCloudLayer {
132
132
  */
133
133
  constructor(id, config) {
134
134
  super(id, config);
135
+
136
+ /**
137
+ * @type {boolean}
138
+ * @readonly
139
+ */
135
140
  this.isPotreeLayer = true;
136
141
  const resolve = this.addInitializationStep();
137
142
  this.source.whenReady.then(metadata => {
@@ -149,6 +154,8 @@ class Potree2Layer extends PointCloudLayer {
149
154
  const root = new Potree2Node(0, 0, this);
150
155
  root.bbox = boundingBox;
151
156
  root.boundingSphere = boundingBox.getBoundingSphere(new THREE.Sphere());
157
+ this.minElevationRange = this.minElevationRange ?? metadata.boundingBox.min[2];
158
+ this.maxElevationRange = this.maxElevationRange ?? metadata.boundingBox.max[2];
152
159
  root.id = 'r';
153
160
  root.depth = 0;
154
161
  root.nodeType = 2;
@@ -10,13 +10,13 @@ bboxMesh.geometry.boundingBox = box3;
10
10
  * @property {boolean} isPotreeLayer - Used to checkout whether this layer
11
11
  * is a PotreeLayer. Default is `true`. You should not change this, as it is
12
12
  * used internally for optimisation.
13
+ *
14
+ * @extends PointCloudLayer
13
15
  */
14
16
  class PotreeLayer extends PointCloudLayer {
15
17
  /**
16
18
  * Constructs a new instance of Potree layer.
17
19
  *
18
- * @extends PointCloudLayer
19
- *
20
20
  * @example
21
21
  * // Create a new point cloud layer
22
22
  * const points = new PotreeLayer('points',
@@ -37,12 +37,17 @@ class PotreeLayer extends PointCloudLayer {
37
37
  * contains three elements `name, protocol, extent`, these elements will be
38
38
  * available using `layer.name` or something else depending on the property
39
39
  * name. See the list of properties to know which one can be specified.
40
- * @param {string} [config.crs=ESPG:4326] - The CRS of the {@link View} this
40
+ * @param {string} [config.crs='ESPG:4326'] - The CRS of the {@link View} this
41
41
  * layer will be attached to. This is used to determine the extent of this
42
42
  * layer. Default to `EPSG:4326`.
43
43
  */
44
44
  constructor(id, config) {
45
45
  super(id, config);
46
+
47
+ /**
48
+ * @type {boolean}
49
+ * @readonly
50
+ */
46
51
  this.isPotreeLayer = true;
47
52
  const resolve = this.addInitializationStep();
48
53
  this.source.whenReady.then(cloud => {
@@ -57,6 +62,8 @@ class PotreeLayer extends PointCloudLayer {
57
62
  this.root = new PotreeNode(0, 0, this);
58
63
  this.root.bbox.min.set(cloud.boundingBox.lx, cloud.boundingBox.ly, cloud.boundingBox.lz);
59
64
  this.root.bbox.max.set(cloud.boundingBox.ux, cloud.boundingBox.uy, cloud.boundingBox.uz);
65
+ this.minElevationRange = this.minElevationRange ?? cloud.boundingBox.lz;
66
+ this.maxElevationRange = this.maxElevationRange ?? cloud.boundingBox.uz;
60
67
  this.extent = Extent.fromBox3(this.source.crs || 'EPSG:4326', this.root.bbox);
61
68
  return this.root.loadOctree().then(resolve);
62
69
  });
@@ -4,8 +4,18 @@ import textureConverter from "../Converter/textureConverter.js";
4
4
  import { CACHE_POLICIES } from "../Core/Scheduler/Cache.js";
5
5
  class RasterLayer extends Layer {
6
6
  constructor(id, config) {
7
- config.cacheLifeTime = config.cacheLifeTime ?? CACHE_POLICIES.TEXTURE;
8
- super(id, config);
7
+ const {
8
+ cacheLifeTime = CACHE_POLICIES.TEXTURE,
9
+ minFilter,
10
+ magFilter,
11
+ ...layerConfig
12
+ } = config;
13
+ super(id, {
14
+ ...layerConfig,
15
+ cacheLifeTime
16
+ });
17
+ this.minFilter = minFilter;
18
+ this.magFilter = magFilter;
9
19
  }
10
20
  convert(data, extentDestination) {
11
21
  return textureConverter.convert(data, extentDestination, this);