itowns 2.44.3-next.2 → 2.44.3-next.20

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 (44) 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.map +1 -1
  7. package/dist/itowns_widgets.js +1 -1
  8. package/dist/itowns_widgets.js.map +1 -1
  9. package/examples/3dtiles_loader.html +105 -44
  10. package/examples/config.json +2 -10
  11. package/examples/images/itowns_logo.svg +123 -0
  12. package/examples/js/plugins/COGParser.js +1 -1
  13. package/lib/Controls/GlobeControls.js +38 -21
  14. package/lib/Controls/StateControl.js +5 -2
  15. package/lib/Converter/Feature2Mesh.js +9 -2
  16. package/lib/Converter/textureConverter.js +3 -3
  17. package/lib/Core/Geographic/Extent.js +74 -266
  18. package/lib/Core/Prefab/Globe/GlobeLayer.js +1 -1
  19. package/lib/Core/Prefab/GlobeView.js +0 -4
  20. package/lib/Core/Prefab/Planar/PlanarLayer.js +1 -1
  21. package/lib/Core/Tile/Tile.js +219 -0
  22. package/lib/Core/Tile/TileGrid.js +46 -0
  23. package/lib/Core/TileMesh.js +2 -1
  24. package/lib/Core/View.js +13 -6
  25. package/lib/Layer/C3DTilesLayer.js +15 -14
  26. package/lib/Layer/LabelLayer.js +8 -4
  27. package/lib/Layer/OGC3DTilesLayer.js +62 -31
  28. package/lib/Parser/GeoJsonParser.js +1 -1
  29. package/lib/Parser/VectorTileParser.js +1 -1
  30. package/lib/Parser/XbilParser.js +14 -2
  31. package/lib/Provider/URLBuilder.js +22 -11
  32. package/lib/Renderer/PointsMaterial.js +1 -1
  33. package/lib/Source/TMSSource.js +9 -7
  34. package/lib/Source/VectorTilesSource.js +2 -2
  35. package/lib/Source/WFSSource.js +14 -8
  36. package/lib/Source/WMSSource.js +20 -10
  37. package/lib/Source/WMTSSource.js +13 -7
  38. package/lib/Utils/gui/C3DTilesStyle.js +2 -3
  39. package/package.json +9 -3
  40. package/examples/3dtiles_25d.html +0 -120
  41. package/examples/3dtiles_basic.html +0 -94
  42. package/examples/3dtiles_batch_table.html +0 -86
  43. package/examples/3dtiles_ion.html +0 -126
  44. package/examples/3dtiles_pointcloud.html +0 -95
@@ -0,0 +1,219 @@
1
+ import * as THREE from 'three';
2
+ import Coordinates from "../Geographic/Coordinates.js";
3
+ import CRS from "../Geographic/Crs.js";
4
+ import Extent from "../Geographic/Extent.js";
5
+ import { getInfoTms, getCountTiles } from "./TileGrid.js";
6
+ const _tmsCoord = new THREE.Vector2();
7
+ const _dimensionTile = new THREE.Vector2();
8
+ const r = {
9
+ row: 0,
10
+ col: 0,
11
+ invDiff: 0
12
+ };
13
+ function _rowColfromParent(/** @type {Tile} */tile, /** @type {number} */zoom) {
14
+ const diffLevel = tile.zoom - zoom;
15
+ const diff = 2 ** diffLevel;
16
+ r.invDiff = 1 / diff;
17
+ r.row = (tile.row - tile.row % diff) * r.invDiff;
18
+ r.col = (tile.col - tile.col % diff) * r.invDiff;
19
+ return r;
20
+ }
21
+ const _extent = new Extent('EPSG:4326', [0, 0, 0, 0]);
22
+ const _extent2 = new Extent('EPSG:4326', [0, 0, 0, 0]);
23
+ const _c = new Coordinates('EPSG:4326', 0, 0);
24
+ class Tile {
25
+ /**
26
+ * Tile is a geographical bounding rectangle defined by zoom, row and column.
27
+ *
28
+ * @param {String} crs projection of limit values.
29
+ * @param {number} [zoom=0] zoom value
30
+ * @param {number} [row=0] row value
31
+ * @param {number} [col=0] column value
32
+ */
33
+ constructor(crs) {
34
+ let zoom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
35
+ let row = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
36
+ let col = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
37
+ this.isTile = true;
38
+ this.crs = crs;
39
+ this.zoom = zoom;
40
+ this.row = row;
41
+ this.col = col;
42
+ }
43
+
44
+ /**
45
+ * Clone this tile
46
+ * @return {Tile} cloned tile
47
+ */
48
+ clone() {
49
+ return new Tile(this.crs, this.zoom, this.row, this.col);
50
+ }
51
+
52
+ /**
53
+ * Convert tile to the specified extent.
54
+ * @param {string} crs the projection of destination.
55
+ * @param {Extent} target copy the destination to target.
56
+ * @return {Extent}
57
+ */
58
+ toExtent(crs, target) {
59
+ CRS.isValid(crs);
60
+ target = target || new Extent('EPSG:4326', [0, 0, 0, 0]);
61
+ const {
62
+ epsg,
63
+ globalExtent,
64
+ globalDimension
65
+ } = getInfoTms(this.crs);
66
+ const countTiles = getCountTiles(this.crs, this.zoom);
67
+ _dimensionTile.set(1, 1).divide(countTiles).multiply(globalDimension);
68
+ target.west = globalExtent.west + (globalDimension.x - _dimensionTile.x * (countTiles.x - this.col));
69
+ target.east = target.west + _dimensionTile.x;
70
+ target.south = globalExtent.south + _dimensionTile.y * (countTiles.y - this.row - 1);
71
+ target.north = target.south + _dimensionTile.y;
72
+ target.crs = epsg;
73
+ target.zoom = this.zoom;
74
+ return crs == epsg ? target : target.as(crs, target);
75
+ }
76
+
77
+ /**
78
+ * Return true if `tile` is inside this tile.
79
+ *
80
+ * @param {Tile} tile the tile to check
81
+ *
82
+ * @return {boolean}
83
+ */
84
+ isInside(tile) {
85
+ if (this.zoom == tile.zoom) {
86
+ return this.row == tile.row && this.col == tile.col;
87
+ } else if (this.zoom < tile.zoom) {
88
+ return false;
89
+ } else {
90
+ _rowColfromParent(this, tile.zoom);
91
+ return r.row == tile.row && r.col == tile.col;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Return the translation and scale to transform this tile to input tile.
97
+ *
98
+ * @param {Tile} tile input tile
99
+ * @param {THREE.Vector4} target copy the result to target.
100
+ * @return {THREE.Vector4} {x: translation on west-east, y: translation on south-north, z: scale on west-east, w: scale on south-north}
101
+ */
102
+ offsetToParent(tile) {
103
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector4();
104
+ if (this.crs != tile.crs) {
105
+ throw new Error('unsupported mix');
106
+ }
107
+ _rowColfromParent(this, tile.zoom);
108
+ return target.set(this.col * r.invDiff - r.col, this.row * r.invDiff - r.row, r.invDiff, r.invDiff);
109
+ }
110
+
111
+ /**
112
+ * Return parent tile with input level
113
+ *
114
+ * @param {number} levelParent level of parent.
115
+ * @return {Tile}
116
+ */
117
+ tiledExtentParent(levelParent) {
118
+ if (levelParent && levelParent < this.zoom) {
119
+ _rowColfromParent(this, levelParent);
120
+ return new Tile(this.crs, levelParent, r.row, r.col);
121
+ } else {
122
+ return this;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Set zoom, row and column values
128
+ *
129
+ * @param {number} [zoom=0] zoom value
130
+ * @param {number} [row=0] row value
131
+ * @param {number} [col=0] column value
132
+ *
133
+ * @return {Tile}
134
+ */
135
+ set() {
136
+ let zoom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
137
+ let row = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
138
+ let col = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
139
+ this.zoom = zoom;
140
+ this.row = row;
141
+ this.col = col;
142
+ return this;
143
+ }
144
+
145
+ /**
146
+ * Copy to this tile to input tile.
147
+ * @param {Tile} tile
148
+ * @return {Tile} copied extent
149
+ */
150
+ copy(tile) {
151
+ this.crs = tile.crs;
152
+ return this.set(tile.zoom, tile.row, tile.col);
153
+ }
154
+
155
+ /**
156
+ * Return values of tile in string, separated by the separator input.
157
+ * @param {string} separator
158
+ * @return {string}
159
+ */
160
+ toString() {
161
+ let separator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
162
+ return `${this.zoom}${separator}${this.row}${separator}${this.col}`;
163
+ }
164
+ }
165
+
166
+ /**
167
+ * @param {Extent} e
168
+ * @param {string} tms
169
+ * @returns {Tile[]}
170
+ */
171
+ export function tiledCovering(e, tms) {
172
+ if (e.crs == 'EPSG:4326' && tms == CRS.tms_3857) {
173
+ const WMTS_PM = [];
174
+ const extent = _extent.copy(e).as(CRS.formatToEPSG(tms), _extent2);
175
+ const {
176
+ globalExtent,
177
+ globalDimension,
178
+ sTs
179
+ } = getInfoTms(CRS.formatToEPSG(tms));
180
+ extent.clampByExtent(globalExtent);
181
+ extent.planarDimensions(_dimensionTile);
182
+ const zoom = e.zoom + 1 || Math.floor(Math.log2(Math.round(globalDimension.x / (_dimensionTile.x * sTs.x))));
183
+ const countTiles = getCountTiles(tms, zoom);
184
+ const center = extent.center(_c);
185
+ _tmsCoord.x = center.x - globalExtent.west;
186
+ _tmsCoord.y = globalExtent.north - extent.north;
187
+ _tmsCoord.divide(globalDimension).multiply(countTiles).floor();
188
+
189
+ // ]N; N+1] => N
190
+ const maxRow = Math.ceil((globalExtent.north - extent.south) / globalDimension.x * countTiles.y) - 1;
191
+ for (let r = maxRow; r >= _tmsCoord.y; r--) {
192
+ WMTS_PM.push(new Tile(tms, zoom, r, _tmsCoord.x));
193
+ }
194
+ return WMTS_PM;
195
+ } else {
196
+ const target = new Tile(tms, 0, 0, 0);
197
+ const {
198
+ globalExtent,
199
+ globalDimension,
200
+ sTs,
201
+ isInverted
202
+ } = getInfoTms(e.crs);
203
+ const center = e.center(_c);
204
+ e.planarDimensions(_dimensionTile);
205
+ // Each level has 2^n * 2^n tiles...
206
+ // ... so we count how many tiles of the same width as tile we can fit in the layer
207
+ // ... 2^zoom = tilecount => zoom = log2(tilecount)
208
+ const zoom = Math.floor(Math.log2(Math.round(globalDimension.x / (_dimensionTile.x * sTs.x))));
209
+ const countTiles = getCountTiles(tms, zoom);
210
+
211
+ // Now that we have computed zoom, we can deduce x and y (or row / column)
212
+ _tmsCoord.x = center.x - globalExtent.west;
213
+ _tmsCoord.y = isInverted ? globalExtent.north - center.y : center.y - globalExtent.south;
214
+ _tmsCoord.divide(globalDimension).multiply(countTiles).floor();
215
+ target.set(zoom, _tmsCoord.y, _tmsCoord.x);
216
+ return [target];
217
+ }
218
+ }
219
+ export default Tile;
@@ -0,0 +1,46 @@
1
+ import * as THREE from 'three';
2
+ import CRS from "../Geographic/Crs.js";
3
+ import Extent from "../Geographic/Extent.js";
4
+ const _countTiles = new THREE.Vector2();
5
+ const _dim = new THREE.Vector2();
6
+ export const globalExtentTMS = new Map();
7
+ export const schemeTiles = new Map();
8
+ const extent4326 = new Extent('EPSG:4326', -180, 180, -90, 90);
9
+ globalExtentTMS.set('EPSG:4326', extent4326);
10
+
11
+ // Compute global extent of TMS in EPSG:3857
12
+ // It's square whose a side is between -180° to 180°.
13
+ // So, west extent, it's 180 convert in EPSG:3857
14
+ const extent3857 = extent4326.as('EPSG:3857');
15
+ extent3857.clampSouthNorth(extent3857.west, extent3857.east);
16
+ globalExtentTMS.set('EPSG:3857', extent3857);
17
+ schemeTiles.set('default', new THREE.Vector2(1, 1));
18
+ schemeTiles.set(CRS.tms_3857, schemeTiles.get('default'));
19
+ schemeTiles.set(CRS.tms_4326, new THREE.Vector2(2, 1));
20
+ export function getInfoTms(/** @type {string} */crs) {
21
+ const epsg = CRS.formatToEPSG(crs);
22
+ const globalExtent = globalExtentTMS.get(epsg);
23
+ const globalDimension = globalExtent.planarDimensions(_dim);
24
+ const tms = CRS.formatToTms(crs);
25
+ const sTs = schemeTiles.get(tms) || schemeTiles.get('default');
26
+ // The isInverted parameter is to be set to the correct value, true or false
27
+ // (default being false) if the computation of the coordinates needs to be
28
+ // inverted to match the same scheme as OSM, Google Maps or other system.
29
+ // See link below for more information
30
+ // https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates/
31
+ // in crs includes ':NI' => tms isn't inverted (NOT INVERTED)
32
+ const isInverted = !tms.includes(':NI');
33
+ return {
34
+ epsg,
35
+ globalExtent,
36
+ globalDimension,
37
+ sTs,
38
+ isInverted
39
+ };
40
+ }
41
+ export function getCountTiles(/** @type {string} */crs, /** @type {number} */zoom) {
42
+ const sTs = schemeTiles.get(CRS.formatToTms(crs)) || schemeTiles.get('default');
43
+ const count = 2 ** zoom;
44
+ _countTiles.set(count, count).multiply(sTs);
45
+ return _countTiles;
46
+ }
@@ -1,6 +1,7 @@
1
1
  import * as THREE from 'three';
2
2
  import CRS from "./Geographic/Crs.js";
3
3
  import { geoidLayerIsVisible } from "../Layer/GeoidLayer.js";
4
+ import { tiledCovering } from "./Tile/Tile.js";
4
5
 
5
6
  /**
6
7
  * A TileMesh is a THREE.Mesh with a geometricError and an OBB
@@ -29,7 +30,7 @@ class TileMesh extends THREE.Mesh {
29
30
  this.boundingSphere = new THREE.Sphere();
30
31
  this.obb.box3D.getBoundingSphere(this.boundingSphere);
31
32
  for (const tms of layer.tileMatrixSets) {
32
- this.#_tms.set(tms, this.extent.tiledCovering(tms));
33
+ this.#_tms.set(tms, tiledCovering(this.extent, tms));
33
34
  }
34
35
  this.frustumCulled = false;
35
36
  this.matrixAutoUpdate = false;
package/lib/Core/View.js CHANGED
@@ -30,7 +30,8 @@ export const VIEW_EVENTS = {
30
30
  LAYER_ADDED: 'layer-added',
31
31
  INITIALIZED: 'initialized',
32
32
  COLOR_LAYERS_ORDER_CHANGED,
33
- CAMERA_MOVED: 'camera-moved'
33
+ CAMERA_MOVED: 'camera-moved',
34
+ DISPOSED: 'disposed'
34
35
  };
35
36
 
36
37
  /**
@@ -109,9 +110,11 @@ const coordinates = new Coordinates('EPSG:4326');
109
110
  const viewers = [];
110
111
  // Size of the camera frustrum, in meters
111
112
  let screenMeters;
113
+ let id = 0;
112
114
 
113
115
  /**
114
- * @property {HTMLElement} domElement - Thhe domElement holding the canvas where the view is displayed
116
+ * @property {number} id - The id of the view. It's incremented at each new view instance, starting at 0.
117
+ * @property {HTMLElement} domElement - The domElement holding the canvas where the view is displayed
115
118
  * @property {String} referenceCrs - The coordinate reference system of the view
116
119
  * @property {MainLoop} mainLoop - itowns mainloop scheduling the operations
117
120
  * @property {THREE.Scene} scene - threejs scene of the view
@@ -136,10 +139,10 @@ class View extends THREE.EventDispatcher {
136
139
  * var view = itowns.View('EPSG:4326', viewerDiv, { camera: { type: itowns.CAMERA_TYPE.ORTHOGRAPHIC } });
137
140
  * var customControls = itowns.THREE.OrbitControls(view.camera3D, viewerDiv);
138
141
  *
139
- * @param {string} crs - The default CRS of Three.js coordinates. Should be a cartesian CRS.
142
+ * @param {String} crs - The default CRS of Three.js coordinates. Should be a cartesian CRS.
140
143
  * @param {HTMLElement} viewerDiv - Where to instanciate the Three.js scene in the DOM
141
144
  * @param {Object} [options] - Optional properties.
142
- * @param {object} [options.camera] - Options for the camera associated to the view. See {@link Camera} options.
145
+ * @param {Object} [options.camera] - Options for the camera associated to the view. See {@link Camera} options.
143
146
  * @param {MainLoop} [options.mainLoop] - {@link MainLoop} instance to use, otherwise a default one will be constructed
144
147
  * @param {WebGLRenderer|Object} [options.renderer] - {@link WebGLRenderer} instance to use, otherwise
145
148
  * a default one will be constructed. In this case, if options.renderer is an object, it will be used to
@@ -159,6 +162,7 @@ class View extends THREE.EventDispatcher {
159
162
  }
160
163
  super();
161
164
  this.domElement = viewerDiv;
165
+ this.id = id++;
162
166
  this.referenceCrs = crs;
163
167
  let engine;
164
168
  // options.renderer can be 2 separate things:
@@ -272,8 +276,6 @@ class View extends THREE.EventDispatcher {
272
276
  }
273
277
  // remove alls frameRequester
274
278
  this.removeAllFrameRequesters();
275
- // remove alls events
276
- this.removeAllEvents();
277
279
  // remove all layers
278
280
  const layers = this.getLayers(l => !l.isTiledGeometryLayer && !l.isAtmosphere);
279
281
  for (const layer of layers) {
@@ -290,6 +292,11 @@ class View extends THREE.EventDispatcher {
290
292
  viewers.splice(id, 1);
291
293
  // Remove remaining objects in the scene (e.g. helpers, debug, etc.)
292
294
  this.scene.traverse(ObjectRemovalHelper.cleanup);
295
+ this.dispatchEvent({
296
+ type: VIEW_EVENTS.DISPOSED
297
+ });
298
+ // remove alls events
299
+ this.removeAllEvents();
293
300
  }
294
301
 
295
302
  /**
@@ -102,6 +102,7 @@ class C3DTilesLayer extends GeometryLayer {
102
102
  * @param {View} view The view
103
103
  */
104
104
  constructor(id, config, view) {
105
+ console.warn('C3DTilesLayer is deprecated and will be removed in iTowns 3.0 version. Use OGC3DTilesLayer instead.');
105
106
  super(id, new THREE.Group(), {
106
107
  source: config.source
107
108
  });
@@ -143,7 +144,7 @@ class C3DTilesLayer extends GeometryLayer {
143
144
  }
144
145
 
145
146
  /** @type {Style} */
146
- this._style = config.style || null;
147
+ this.style = config.style || null;
147
148
 
148
149
  /** @type {Map<string, THREE.MeshStandardMaterial>} */
149
150
  this.#fillColorMaterialsBuffer = new Map();
@@ -368,21 +369,15 @@ class C3DTilesLayer extends GeometryLayer {
368
369
  if (c3DTileFeature.object3d != object3d) {
369
370
  continue; // this feature do not belong to object3d
370
371
  }
372
+ this._style.context.setGeometry({
373
+ properties: c3DTileFeature
374
+ });
375
+
371
376
  /** @type {THREE.Color} */
372
- let color = null;
373
- if (typeof this._style.fill.color === 'function') {
374
- color = new THREE.Color(this._style.fill.color(c3DTileFeature));
375
- } else {
376
- color = new THREE.Color(this._style.fill.color);
377
- }
377
+ const color = new THREE.Color(this._style.fill.color);
378
378
 
379
379
  /** @type {number} */
380
- let opacity = null;
381
- if (typeof this._style.fill.opacity === 'function') {
382
- opacity = this._style.fill.opacity(c3DTileFeature);
383
- } else {
384
- opacity = this._style.fill.opacity;
385
- }
380
+ const opacity = this._style.fill.opacity;
386
381
  const materialId = color.getHexString() + opacity;
387
382
  let material = null;
388
383
  if (this.#fillColorMaterialsBuffer.has(materialId)) {
@@ -445,7 +440,13 @@ class C3DTilesLayer extends GeometryLayer {
445
440
  return this.#fillColorMaterialsBuffer.size;
446
441
  }
447
442
  set style(value) {
448
- this._style = value;
443
+ if (value instanceof Style) {
444
+ this._style = value;
445
+ } else if (!value) {
446
+ this._style = null;
447
+ } else {
448
+ this._style = new Style(value);
449
+ }
449
450
  this.updateStyle();
450
451
  }
451
452
  get style() {
@@ -216,17 +216,21 @@ class LabelLayer extends GeometryLayer {
216
216
  *
217
217
  * @param {FeatureCollection} data - The FeatureCollection to read the
218
218
  * labels from.
219
- * @param {Extent} extent
219
+ * @param {Extent|Tile} extentOrTile
220
220
  *
221
221
  * @return {Label[]} An array containing all the created labels.
222
222
  */
223
- convert(data, extent) {
223
+ convert(data, extentOrTile) {
224
224
  const labels = [];
225
225
 
226
226
  // Converting the extent now is faster for further operation
227
- extent.as(data.crs, _extent);
227
+ if (extentOrTile.isExtent) {
228
+ extentOrTile.as(data.crs, _extent);
229
+ } else {
230
+ extentOrTile.toExtent(data.crs, _extent);
231
+ }
228
232
  coord.crs = data.crs;
229
- context.setZoom(extent.zoom);
233
+ context.setZoom(extentOrTile.zoom);
230
234
  data.features.forEach(f => {
231
235
  // TODO: add support for LINE and POLYGON
232
236
  if (f.type !== FEATURE_TYPES.POINT) {
@@ -6,19 +6,20 @@ import { DRACOLoader } from "../ThreeExtended/loaders/DRACOLoader.js";
6
6
  import { KTX2Loader } from "../ThreeExtended/loaders/KTX2Loader.js";
7
7
  import ReferLayerProperties from "./ReferencingLayerProperties.js";
8
8
  import PointsMaterial, { PNTS_MODE, PNTS_SHAPE, PNTS_SIZE_MODE, ClassificationScheme } from "../Renderer/PointsMaterial.js";
9
+ import { VIEW_EVENTS } from "../Core/View.js";
9
10
  const _raycaster = new THREE.Raycaster();
10
11
 
12
+ // Stores lruCache, downloadQueue and parseQueue for each id of view {@link View}
13
+ // every time a tileset has been added
14
+ // https://github.com/iTowns/itowns/issues/2426
15
+ const viewers = {};
16
+
11
17
  // Internal instance of GLTFLoader, passed to 3d-tiles-renderer-js to support GLTF 1.0 and 2.0
12
18
  // Temporary exported to be used in deprecated B3dmParser
13
19
  export const itownsGLTFLoader = new iGLTFLoader();
14
20
  itownsGLTFLoader.register(() => new GLTFMeshFeaturesExtension());
15
21
  itownsGLTFLoader.register(() => new GLTFStructuralMetadataExtension());
16
22
  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
23
  export const OGC3DTILES_LAYER_EVENTS = {
23
24
  /**
24
25
  * Fired when a new root or child tile set is loaded
@@ -52,7 +53,19 @@ export const OGC3DTILES_LAYER_EVENTS = {
52
53
  * @property {Object} tile - the tile metadata from the tileset
53
54
  * @property {boolean} visible - the tile visible state
54
55
  */
55
- TILE_VISIBILITY_CHANGE: 'tile-visibility-change'
56
+ TILE_VISIBILITY_CHANGE: 'tile-visibility-change',
57
+ /**
58
+ * Fired when a new batch of tiles start loading (can be fired multiple times, e.g. when the camera moves and new tiles
59
+ * start loading)
60
+ * @event OGC3DTilesLayer#tiles-load-start
61
+ */
62
+ TILES_LOAD_START: 'tiles-load-start',
63
+ /**
64
+ * Fired when all visible tiles are loaded (can be fired multiple times, e.g. when the camera moves and new tiles
65
+ * are loaded)
66
+ * @event OGC3DTilesLayer#tiles-load-end
67
+ */
68
+ TILES_LOAD_END: 'tiles-load-end'
56
69
  };
57
70
 
58
71
  /**
@@ -95,6 +108,13 @@ export function enableKtx2Loader(path, renderer) {
95
108
  class OGC3DTilesLayer extends GeometryLayer {
96
109
  /**
97
110
  * Layer for [3D Tiles](https://www.ogc.org/standard/3dtiles/) datasets.
111
+ *
112
+ * Advanced configuration note: 3D Tiles rendering is delegated to 3DTilesRendererJS that exposes several
113
+ * configuration options accessible through the tilesRenderer property of this class. see the
114
+ * [3DTilesRendererJS doc](https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/README.md). Also note that
115
+ * the cache is shared amongst 3D tiles layers and can be configured through tilesRenderer.lruCache (see the
116
+ * [following documentation](https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/README.md#lrucache-1).
117
+ *
98
118
  * @extends Layer
99
119
  *
100
120
  * @param {String} id - unique layer id.
@@ -109,8 +129,8 @@ class OGC3DTilesLayer extends GeometryLayer {
109
129
  * @param {String} [config.pntsSizeMode= PNTS_SIZE_MODE.VALUE] {@link PointsMaterial} Point cloud size mode (passed to {@link PointsMaterial}).
110
130
  * Only 'VALUE' or 'ATTENUATED' are possible. VALUE use constant size, ATTENUATED compute size depending on distance
111
131
  * 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.
132
+ * @param {Number} [config.pntsMinAttenuatedSize=3] Minimum scale used by 'ATTENUATED' size mode.
133
+ * @param {Number} [config.pntsMaxAttenuatedSize=10] Maximum scale used by 'ATTENUATED' size mode.
114
134
  */
115
135
  constructor(id, config) {
116
136
  super(id, new THREE.Group(), {
@@ -122,17 +142,17 @@ class OGC3DTilesLayer extends GeometryLayer {
122
142
  if (config.source.isOGC3DTilesIonSource) {
123
143
  this.tilesRenderer.registerPlugin(new CesiumIonAuthPlugin({
124
144
  apiToken: config.source.accessToken,
125
- assetId: config.source.assetId
145
+ assetId: config.source.assetId,
146
+ autoRefreshToken: true
126
147
  }));
127
148
  } else if (config.source.isOGC3DTilesGoogleSource) {
128
149
  this.tilesRenderer.registerPlugin(new GoogleCloudAuthPlugin({
129
- apiToken: config.source.key
150
+ apiToken: config.source.key,
151
+ autoRefreshToken: true
130
152
  }));
131
153
  }
132
154
  this.tilesRenderer.registerPlugin(new ImplicitTilingPlugin());
133
155
  this.tilesRenderer.manager.addHandler(/\.gltf$/, itownsGLTFLoader);
134
- this._setupCacheAndQueues();
135
- this._setupEvents();
136
156
  this.object3d.add(this.tilesRenderer.group);
137
157
 
138
158
  // Add an initialization step that is resolved when the root tileset is loaded (see this._setup below), meaning
@@ -167,29 +187,31 @@ class OGC3DTilesLayer extends GeometryLayer {
167
187
  this.pntsShape = config.pntsShape ?? PNTS_SHAPE.CIRCLE;
168
188
  this.classification = config.classification ?? ClassificationScheme.DEFAULT;
169
189
  this.pntsSizeMode = config.pntsSizeMode ?? PNTS_SIZE_MODE.VALUE;
170
- this.pntsMinAttenuatedSize = config.pntsMinAttenuatedSize || 1;
171
- this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize || 7;
190
+ this.pntsMinAttenuatedSize = config.pntsMinAttenuatedSize || 3;
191
+ this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize || 10;
172
192
  }
173
193
 
174
194
  /**
175
- * Sets the lruCache and download and parse queues so they are shared amongst all tilesets.
195
+ * Sets the lruCache and download and parse queues so they are shared amongst
196
+ * all tilesets from a same {@link View} view.
197
+ * @param {View} view - view associated to this layer.
176
198
  * @private
177
199
  */
178
- _setupCacheAndQueues() {
179
- if (lruCache === null) {
180
- lruCache = this.tilesRenderer.lruCache;
200
+ _setupCacheAndQueues(view) {
201
+ const id = view.id;
202
+ if (viewers[id]) {
203
+ this.tilesRenderer.lruCache = viewers[id].lruCache;
204
+ this.tilesRenderer.downloadQueue = viewers[id].downloadQueue;
205
+ this.tilesRenderer.parseQueue = viewers[id].parseQueue;
181
206
  } else {
182
- this.tilesRenderer.lruCache = lruCache;
183
- }
184
- if (downloadQueue === null) {
185
- downloadQueue = this.tilesRenderer.downloadQueue;
186
- } else {
187
- this.tilesRenderer.downloadQueue = downloadQueue;
188
- }
189
- if (parseQueue === null) {
190
- parseQueue = this.tilesRenderer.parseQueue;
191
- } else {
192
- this.tilesRenderer.parseQueue = parseQueue;
207
+ viewers[id] = {
208
+ lruCache: this.tilesRenderer.lruCache,
209
+ downloadQueue: this.tilesRenderer.downloadQueue,
210
+ parseQueue: this.tilesRenderer.parseQueue
211
+ };
212
+ view.addEventListener(VIEW_EVENTS.DISPOSED, evt => {
213
+ delete viewers[evt.target.id];
214
+ });
193
215
  }
194
216
  }
195
217
 
@@ -233,6 +255,9 @@ class OGC3DTilesLayer extends GeometryLayer {
233
255
  });
234
256
  view.notifyChange(this);
235
257
  });
258
+ this._setupCacheAndQueues(view);
259
+ this._setupEvents();
260
+
236
261
  // Start loading tileset and tiles
237
262
  this.tilesRenderer.update();
238
263
  }
@@ -274,7 +299,7 @@ class OGC3DTilesLayer extends GeometryLayer {
274
299
 
275
300
  // Setup classification bufferAttribute
276
301
  if (model.isPoints) {
277
- const classificationData = batchTable?.getData('Classification');
302
+ const classificationData = batchTable?.getPropertyArray('Classification');
278
303
  if (classificationData) {
279
304
  geometry.setAttribute('classification', new THREE.BufferAttribute(classificationData, 1));
280
305
  }
@@ -348,7 +373,13 @@ class OGC3DTilesLayer extends GeometryLayer {
348
373
  _raycaster.near = camera.near;
349
374
  _raycaster.far = camera.far;
350
375
  _raycaster.firstHitOnly = true;
351
- _raycaster.intersectObject(this.tilesRenderer.group, true, target);
376
+ const picked = _raycaster.intersectObject(this.tilesRenderer.group, true);
377
+ // Store the layer of the picked object to conform to the interface of what's returned by Picking.js (used for
378
+ // other GeometryLayers
379
+ picked.forEach(p => {
380
+ p.layer = this;
381
+ });
382
+ target.push(...picked);
352
383
  return target;
353
384
  }
354
385
 
@@ -195,7 +195,7 @@ export default {
195
195
  _in.crs = _in.crs || readCRS(json);
196
196
  if (out.filteringExtent) {
197
197
  if (typeof out.filteringExtent == 'boolean') {
198
- out.filterExtent = options.extent.as(_in.crs);
198
+ out.filterExtent = options.extent.isExtent ? options.extent.as(_in.crs) : options.extent.toExtent(_in.crs);
199
199
  } else if (out.filteringExtent.isExtent) {
200
200
  out.filterExtent = out.filteringExtent;
201
201
  }
@@ -1,7 +1,7 @@
1
1
  import { Vector2, Vector3 } from 'three';
2
2
  import Protobuf from 'pbf';
3
3
  import { VectorTile } from '@mapbox/vector-tile';
4
- import { globalExtentTMS } from "../Core/Geographic/Extent.js";
4
+ import { globalExtentTMS } from "../Core/Tile/TileGrid.js";
5
5
  import { FeatureCollection, FEATURE_TYPES } from "../Core/Feature.js";
6
6
  import { deprecatedParsingOptionsToNewOne } from "../Core/Deprecated/Undeprecator.js";
7
7
  import Coordinates from "../Core/Geographic/Coordinates.js";
@@ -67,10 +67,22 @@ export function computeMinMaxElevation(texture, pitch, options) {
67
67
  }
68
68
  }
69
69
  }
70
- if (options.zmin > min) {
70
+ }
71
+
72
+ // Clamp values to zmin and zmax values configured in ElevationLayer
73
+ if (options.zmin != null) {
74
+ if (min < options.zmin) {
71
75
  min = options.zmin;
72
76
  }
73
- if (options.zmax < max) {
77
+ if (max < options.zmin) {
78
+ max = options.zmin;
79
+ }
80
+ }
81
+ if (options.zmax != null) {
82
+ if (min > options.zmax) {
83
+ min = options.zmax;
84
+ }
85
+ if (max > options.zmax) {
74
86
  max = options.zmax;
75
87
  }
76
88
  }