itowns 2.44.3-next.1 → 2.44.3-next.10

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 (57) hide show
  1. package/CODING.md +1 -1
  2. package/dist/455.js +1 -1
  3. package/dist/455.js.map +1 -1
  4. package/dist/debug.js +1 -1
  5. package/dist/debug.js.LICENSE.txt +1 -1
  6. package/dist/debug.js.map +1 -1
  7. package/dist/itowns.js +1 -1
  8. package/dist/itowns.js.map +1 -1
  9. package/dist/itowns_lasworker.js +1 -1
  10. package/dist/itowns_lasworker.js.map +1 -1
  11. package/dist/itowns_widgets.js +1 -1
  12. package/dist/itowns_widgets.js.map +1 -1
  13. package/examples/3dtiles_loader.html +104 -43
  14. package/examples/config.json +2 -10
  15. package/lib/Controls/GlobeControls.js +41 -22
  16. package/lib/Controls/StateControl.js +4 -1
  17. package/lib/Controls/StreetControls.js +5 -2
  18. package/lib/Converter/Feature2Mesh.js +11 -4
  19. package/lib/Converter/textureConverter.js +3 -3
  20. package/lib/Core/Feature.js +2 -2
  21. package/lib/Core/Geographic/Extent.js +74 -266
  22. package/lib/Core/Prefab/Globe/GlobeLayer.js +1 -1
  23. package/lib/Core/Prefab/Planar/PlanarLayer.js +1 -1
  24. package/lib/Core/Style.js +2 -2
  25. package/lib/Core/Tile/Tile.js +219 -0
  26. package/lib/Core/Tile/TileGrid.js +46 -0
  27. package/lib/Core/TileMesh.js +3 -2
  28. package/lib/Core/View.js +1 -1
  29. package/lib/Layer/C3DTilesLayer.js +15 -14
  30. package/lib/Layer/CopcLayer.js +1 -1
  31. package/lib/Layer/LabelLayer.js +9 -5
  32. package/lib/Layer/OGC3DTilesLayer.js +36 -9
  33. package/lib/Parser/GeoJsonParser.js +1 -1
  34. package/lib/Parser/LASParser.js +1 -1
  35. package/lib/Parser/Potree2BinParser.js +1 -1
  36. package/lib/Parser/VectorTileParser.js +1 -1
  37. package/lib/Parser/XbilParser.js +15 -4
  38. package/lib/Provider/URLBuilder.js +22 -11
  39. package/lib/Renderer/Camera.js +1 -1
  40. package/lib/Renderer/LayeredMaterial.js +1 -1
  41. package/lib/Renderer/PointsMaterial.js +1 -1
  42. package/lib/Source/CopcSource.js +1 -1
  43. package/lib/Source/TMSSource.js +9 -7
  44. package/lib/Source/VectorTilesSource.js +2 -2
  45. package/lib/Source/WFSSource.js +4 -1
  46. package/lib/Source/WMSSource.js +4 -1
  47. package/lib/ThreeExtended/capabilities/WebGL.js +16 -11
  48. package/lib/ThreeExtended/loaders/GLTFLoader.js +10 -6
  49. package/lib/ThreeExtended/loaders/KTX2Loader.js +14 -7
  50. package/lib/Utils/CameraUtils.js +5 -4
  51. package/lib/Utils/gui/C3DTilesStyle.js +2 -3
  52. package/package.json +33 -28
  53. package/examples/3dtiles_25d.html +0 -120
  54. package/examples/3dtiles_basic.html +0 -94
  55. package/examples/3dtiles_batch_table.html +0 -86
  56. package/examples/3dtiles_ion.html +0 -126
  57. package/examples/3dtiles_pointcloud.html +0 -95
@@ -11,7 +11,15 @@
11
11
  <style type="text/css">
12
12
  #description {
13
13
  z-index: 2;
14
- right: 10px;
14
+ left: 10px;
15
+ }
16
+
17
+ #featureInfoText {
18
+ font-size: small;
19
+ }
20
+
21
+ #description .marg {
22
+ margin: 10px;
15
23
  }
16
24
  </style>
17
25
 
@@ -21,12 +29,17 @@
21
29
  <div id="viewerDiv"></div>
22
30
  <div id="description">Specify the URL of a tileset to load:
23
31
  <input type="text" id="url" />
24
- <button onclick="setURL(document.getElementById('url').value)">
25
- Load
26
- </button>
32
+ <button onclick="readURL()">Load</button>
33
+ <div class="marg">
34
+ <button onClick="loadLyon()">Load Lyon 1 (Mesh)</button>
35
+ <button onClick="loadSete()">Load Sete (Point Cloud)</button>
36
+ <button onClick="loadLille()">Load Lille (Mesh)</button>
37
+ <div id="share"></div>
38
+ </div>
27
39
  <hr />
40
+ <p id="featureInfoText" class="marg"><em>Display feature information by clicking on it</em></p>
28
41
  <p><b>Feature Information:</b></p>
29
- <div id="featureInfo"></div>
42
+ <div id="featureInfo" class="marg"></div>
30
43
  </div>
31
44
 
32
45
  <script src="js/GUI/GuiTools.js"></script>
@@ -52,28 +65,17 @@
52
65
 
53
66
  const {
54
67
  TMSSource, WMTSSource, OGC3DTilesSource,
55
- ColorLayer, ElevationLayer, OGC3DTilesLayer,
68
+ ColorLayer, ElevationLayer, OGC3DTilesLayer, PNTS_SIZE_MODE,
56
69
  GlobeView, Coordinates, Fetcher,
57
70
  } = itowns;
58
71
 
59
- const uri = new URL(location);
60
- const state = {
61
- // URL to tileset JSON
62
- tileset: uri.searchParams.get('tileset'),
63
- // Cesium ION /
64
- assetId: uri.searchParams.get('assetId'),
65
- };
66
-
67
- function setURL(url) {
68
- if (!url) return;
72
+ window.layer = null; // 3D Tiles layer
69
73
 
70
- uri.searchParams.set('tileset', url);
71
- history.pushState(null, '', `?${uri.searchParams.toString()}`);
74
+ const uri = new URL(location);
72
75
 
73
- location.reload();
74
- }
76
+ window.gui = new dat.GUI();
75
77
 
76
- // ---- CREATE A GlobeView FOR SUPPORTING DATA VISUALIZATION ----
78
+ // ---- Create a GlobeView ----
77
79
 
78
80
  // Define camera initial position
79
81
  const placement = {
@@ -85,7 +87,17 @@
85
87
  const viewerDiv = document.getElementById('viewerDiv');
86
88
 
87
89
  // Create a GlobeView
88
- const view = new GlobeView(viewerDiv, placement, {});
90
+ const view = new GlobeView(viewerDiv, placement, {
91
+ controls: {
92
+ minDistance: 100,
93
+ }
94
+ });
95
+
96
+ // Enable various compression support for 3D Tiles tileset:
97
+ // - `KHR_draco_mesh_compression` mesh compression extension
98
+ // - `KHR_texture_basisu` texture compresion extension
99
+ itowns.enableDracoLoader('./libs/draco/');
100
+ itowns.enableKtx2Loader('./lib/basis/', view.renderer);
89
101
 
90
102
  // Add ambient light to globally illuminates all objects
91
103
  const light = new AmbientLight(0x404040, 15);
@@ -94,36 +106,68 @@
94
106
  // Setup loading screen
95
107
  setupLoadingScreen(viewerDiv, view);
96
108
 
97
- // Setup debug menu
98
- const menuGlobe = new GuiTools('menuDiv', view, 300);
99
- debug.createTileDebugUI(menuGlobe.gui, view, view.tileLayer);
100
-
101
-
102
- // ---- ADD A BASEMAP ----
109
+ // ---- Add a basemap ----
103
110
 
104
111
  // Add one imagery layer to the scene. This layer's properties are
105
112
  // defined in a json file, but it cou ld be defined as a plain js
106
113
  // object. See `Layer` documentation for more info.
107
114
  Fetcher.json('./layers/JSONLayers/OPENSM.json').then((config) => {
108
- const layer = new ColorLayer('Ortho', {
115
+ const colorLayer = new ColorLayer('Ortho', {
109
116
  ...config,
110
117
  source: new TMSSource(config.source),
111
118
  });
112
- view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe));
119
+ view.addLayer(colorLayer);
113
120
  });
114
121
 
115
- // ---- ADD 3D TILES TILESET ----
122
+ // ---- Add 3D terrain ----
116
123
 
117
- // Enable various compression support for 3D Tiles tileset:
118
- // - `KHR_draco_mesh_compression` mesh compression extension
119
- // - `KHR_texture_basisu` texture compresion extension
120
- itowns.enableDracoLoader('./libs/draco/');
121
- itowns.enableKtx2Loader('./lib/basis/', view.renderer);
124
+ // Add two elevation layers: world terrain and a more precise terrain for france
125
+ // These will deform iTowns globe geometry to represent terrain elevation.
126
+ function addElevationLayerFromConfig(config) {
127
+ config.source = new itowns.WMTSSource(config.source);
128
+ var elevationLayer = new itowns.ElevationLayer(config.id, config);
129
+ view.addLayer(elevationLayer);
130
+ }
131
+ itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig);
132
+ itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig);
133
+
134
+ // ---------- 3D Tiles loading
135
+
136
+ function readURL() {
137
+ const url = document.getElementById('url').value;
138
+
139
+ if (url) {
140
+ setUrl(url);
141
+ }
142
+ }
143
+
144
+ function setUrl(url) {
145
+ if (!url) return;
146
+
147
+ const input_url = document.getElementById('url');
148
+ if (!input_url) return;
149
+
150
+ uri.searchParams.set('copc', url);
151
+ history.replaceState(null, null, `?${uri.searchParams.toString()}`);
152
+
153
+ input_url.value = url;
154
+ load(url);
155
+ }
156
+
157
+
158
+ function load(url) {
159
+ const source = new OGC3DTilesSource({ url });
122
160
 
123
- if (state.tileset) {
124
- const source = new OGC3DTilesSource({ url: state.tileset });
125
- const layer = new OGC3DTilesLayer('3DTiles', {
161
+ if (window.layer) {
162
+ gui.removeFolder('Layer 3DTiles');
163
+ view.removeLayer('3DTiles');
164
+ view.notifyChange();
165
+ window.layer.delete();
166
+ }
167
+
168
+ window.layer = new OGC3DTilesLayer('3DTiles', {
126
169
  source,
170
+ pntsSizeMode: PNTS_SIZE_MODE.ATTENUATED,
127
171
  });
128
172
 
129
173
  // Add an event for picking the 3D Tiles layer and displaying
@@ -131,20 +175,37 @@
131
175
  const pickingArgs = {
132
176
  htmlDiv: document.getElementById('featureInfo'),
133
177
  view,
134
- layer,
178
+ layer: window.layer,
135
179
  };
136
180
 
137
181
  // Add the layer to our view
138
- view.addLayer(layer).then((layer) => {
182
+ view.addLayer(window.layer).then((layer) => {
139
183
  zoomToLayer(view, layer);
140
184
  window.addEventListener('click',
141
185
  (event) => fillHTMLWithPickingInfo(event, pickingArgs), false);
142
186
  });
143
187
 
144
- debug.createOGC3DTilesDebugUI(menuGlobe.gui, view, layer);
188
+ window.layer.whenReady
189
+ .then(() => debug.createOGC3DTilesDebugUI(gui, view, window.layer));
190
+ }
191
+
192
+ function loadLyon() {
193
+ setUrl("https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/refs/heads/master/3DTiles/lyon1-4978/tileset.json");
194
+ }
195
+
196
+ function loadSete() {
197
+ setUrl("https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds/pnts-sete-2021-0756_6256/tileset.json");
145
198
  }
146
199
 
147
- window.setURL = setURL;
200
+ function loadLille() {
201
+ setUrl("https://webimaging.lillemetropole.fr/externe/maillage/2020_mel_5cm/tileset.json");
202
+ }
203
+
204
+ window.readURL = readURL;
205
+ window.loadLyon = loadLyon;
206
+ window.loadSete = loadSete;
207
+ window.loadLille = loadLille;
208
+
148
209
  </script>
149
210
  </body>
150
211
  </html>
@@ -14,16 +14,8 @@
14
14
  "geoid_geoidLayer": "Display geoid heights"
15
15
  },
16
16
 
17
- "3d Tiles": {
18
- "3dtiles_basic": "on 3D map",
19
- "3dtiles_25d": "On 2.5D map",
20
- "3dtiles_batch_table": "Batch table Hierarchy extension",
21
- "3dtiles_ion": "From Cesium ion",
22
- "3dtiles_pointcloud": "Pointcloud classification"
23
- },
24
-
25
- "3D Tiles (new)": {
26
- "3dtiles_loader": "3D Tiles tileset loader"
17
+ "3D Tiles": {
18
+ "3dtiles_loader": "3D Tiles loader"
27
19
  },
28
20
 
29
21
  "Pointcloud": {
@@ -149,7 +149,7 @@ let previous;
149
149
  * @param {object} [options] An object with one or more configuration properties. Any property of GlobeControls
150
150
  * can be passed in this object.
151
151
  * @property {number} zoomFactor The factor the scale is multiplied by when dollying (zooming) in or
152
- * divided by when dollying out. Default is 2.
152
+ * divided by when dollying out. Default is 1.1.
153
153
  * @property {number} rotateSpeed Speed camera rotation in orbit and panoramic mode. Default is 0.25.
154
154
  * @property {number} minDistance Minimum distance between ground and camera in meters (Perspective Camera only).
155
155
  * Default is 250.
@@ -170,6 +170,8 @@ let previous;
170
170
  * @property {boolean} enableDamping Enable damping or not (simulates the lag that a real camera
171
171
  * operator introduces while operating a heavy physical camera). Default is true.
172
172
  * @property {boolean} dampingMoveFactor the damping move factor. Default is 0.25.
173
+ * @property {StateControl~State} stateControl redefining which controls state is triggered by the keyboard/mouse
174
+ * event (For example, rewrite the PAN movement to be triggered with the 'left' mouseButton instead of 'right').
173
175
  */
174
176
  class GlobeControls extends THREE.EventDispatcher {
175
177
  constructor(view, placement) {
@@ -180,7 +182,7 @@ class GlobeControls extends THREE.EventDispatcher {
180
182
  this.camera = view.camera3D;
181
183
 
182
184
  // State control
183
- this.states = new StateControl(this.view);
185
+ this.states = new StateControl(this.view, options.stateControl);
184
186
 
185
187
  // this.enabled property has moved to StateControl
186
188
  Object.defineProperty(this, 'enabled', {
@@ -197,7 +199,7 @@ class GlobeControls extends THREE.EventDispatcher {
197
199
  console.warn('Controls zoomSpeed parameter is deprecated. Use zoomFactor instead.');
198
200
  options.zoomFactor = options.zoomFactor || options.zoomSpeed;
199
201
  }
200
- this.zoomFactor = options.zoomFactor || 1.25;
202
+ this.zoomFactor = options.zoomFactor || 1.1;
201
203
 
202
204
  // Limits to how far you can dolly in and out ( PerspectiveCamera only )
203
205
  this.minDistance = options.minDistance || 250;
@@ -291,10 +293,10 @@ class GlobeControls extends THREE.EventDispatcher {
291
293
  this.lookAtCoordinate(placement, false);
292
294
  coordCameraTarget.crs = this.view.referenceCrs;
293
295
  }
294
- get dollyInScale() {
296
+ get zoomInScale() {
295
297
  return this.zoomFactor;
296
298
  }
297
- get dollyOutScale() {
299
+ get zoomOutScale() {
298
300
  return 1 / this.zoomFactor;
299
301
  }
300
302
  get isPaused() {
@@ -353,11 +355,13 @@ class GlobeControls extends THREE.EventDispatcher {
353
355
  this.panUp(deltaY * (this.camera.top - this.camera.bottom) / gfx.height);
354
356
  }
355
357
  }
358
+
359
+ // For Mobile
356
360
  dolly(delta) {
357
361
  if (delta === 0) {
358
362
  return;
359
363
  }
360
- dollyScale = delta > 0 ? this.dollyInScale : this.dollyOutScale;
364
+ dollyScale = delta > 0 ? this.zoomInScale : this.zoomOutScale;
361
365
  if (this.camera.isPerspectiveCamera) {
362
366
  orbitScale /= dollyScale;
363
367
  } else if (this.camera.isOrthographicCamera) {
@@ -554,6 +558,7 @@ class GlobeControls extends THREE.EventDispatcher {
554
558
 
555
559
  // Initialize dolly movement.
556
560
  dollyStart.copy(event.viewCoords);
561
+ this.view.getPickingPositionFromDepth(event.viewCoords, pickedPosition); // mouse position
557
562
 
558
563
  // Initialize pan movement.
559
564
  panStart.copy(event.viewCoords);
@@ -585,9 +590,11 @@ class GlobeControls extends THREE.EventDispatcher {
585
590
  handleDolly(event) {
586
591
  dollyEnd.copy(event.viewCoords);
587
592
  dollyDelta.subVectors(dollyEnd, dollyStart);
588
- this.dolly(-dollyDelta.y);
589
593
  dollyStart.copy(dollyEnd);
590
- this.update();
594
+ event.delta = dollyDelta.y;
595
+ if (event.delta != 0) {
596
+ this.handleZoom(event);
597
+ }
591
598
  }
592
599
  handlePan(event) {
593
600
  if (event.viewCoords) {
@@ -712,21 +719,33 @@ class GlobeControls extends THREE.EventDispatcher {
712
719
  handleZoom(event) {
713
720
  this.player.stop();
714
721
  CameraUtils.stop(this.view, this.camera);
715
- this.updateTarget();
716
- const delta = -event.delta;
717
- this.dolly(delta);
718
- const previousRange = this.getRange(pickedPosition);
719
- this.update();
720
- const newRange = this.getRange(pickedPosition);
721
- if (Math.abs(newRange - previousRange) / previousRange > 0.001) {
722
- this.dispatchEvent({
723
- type: CONTROL_EVENTS.RANGE_CHANGED,
724
- previous: previousRange,
725
- new: newRange
726
- });
722
+ const zoomScale = event.delta > 0 ? this.zoomInScale : this.zoomOutScale;
723
+ let point = event.type === 'dolly' ? pickedPosition : this.view.getPickingPositionFromDepth(event.viewCoords); // get cursor position
724
+ let range = this.getRange();
725
+ range *= zoomScale;
726
+ if (point && range > this.minDistance && range < this.maxDistance) {
727
+ // check if the zoom is in the allowed interval
728
+ const camPos = xyz.setFromVector3(cameraTarget.position).as('EPSG:4326', c).toVector3();
729
+ point = xyz.setFromVector3(point).as('EPSG:4326', c).toVector3();
730
+ if (camPos.x * point.x < 0) {
731
+ // Correct rotation at 180th meridian by using 0 <= longitude <=360 for interpolation purpose
732
+ if (camPos.x - point.x > 180) {
733
+ point.x += 360;
734
+ } else if (point.x - camPos.x > 180) {
735
+ camPos.x += 360;
736
+ }
737
+ }
738
+ point.lerp(
739
+ // point interpol between mouse cursor and cam pos
740
+ camPos, zoomScale // interpol factor
741
+ );
742
+ point = c.setFromVector3(point).as('EPSG:4978', xyz);
743
+ return this.lookAtCoordinate({
744
+ // update view to the interpolate point
745
+ coord: point,
746
+ range
747
+ }, false);
727
748
  }
728
- this.dispatchEvent(this.startEvent);
729
- this.dispatchEvent(this.endEvent);
730
749
  }
731
750
  onTouchStart(event) {
732
751
  // CameraUtils.stop(view);
@@ -376,9 +376,12 @@ class StateControl extends THREE.EventDispatcher {
376
376
  onMouseWheel(event) {
377
377
  event.preventDefault();
378
378
  if (this.enabled && this.ZOOM.enable) {
379
+ viewCoords.copy(this._view.eventToViewCoords(event));
380
+ this.currentState = this.NONE;
379
381
  this.dispatchEvent({
380
382
  type: this.ZOOM._event,
381
- delta: event.deltaY
383
+ delta: event.deltaY,
384
+ viewCoords
382
385
  });
383
386
  }
384
387
  }
@@ -290,7 +290,7 @@ class StreetControls extends FirstPersonControls {
290
290
  this.end.lookAt(position);
291
291
  this.tween = new TWEEN.Tween({
292
292
  t: 0
293
- }, this.tweenGroup).to({
293
+ }).to({
294
294
  t: 1
295
295
  }, time).easing(TWEEN.Easing.Quadratic.Out).onComplete(() => {
296
296
  this.stopAnimations();
@@ -298,6 +298,7 @@ class StreetControls extends FirstPersonControls {
298
298
  // 'manually' slerp the Quaternion to avoid rotation issues
299
299
  this.camera.quaternion.slerpQuaternions(startQuaternion, this.end.quaternion, d.t);
300
300
  }).start();
301
+ this.tweenGroup.add(this.tween);
301
302
  this.animationFrameRequester = () => {
302
303
  this.tweenGroup.update();
303
304
  // call reset from super class FirsPersonControls to make mouse rotation managed by FirstPersonControl still aligned
@@ -325,12 +326,13 @@ class StreetControls extends FirstPersonControls {
325
326
  resolve = r;
326
327
  });
327
328
  this.stopAnimations();
328
- this.tween = new TWEEN.Tween(this.camera.position, this.tweenGroup) // Create a new tween that modifies camera position
329
+ this.tween = new TWEEN.Tween(this.camera.position) // Create a new tween that modifies camera position
329
330
  .to(position.clone(), time).easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
330
331
  .onComplete(() => {
331
332
  this.stopAnimations();
332
333
  resolve();
333
334
  }).start();
335
+ this.tweenGroup.add(this.tween);
334
336
  this.animationFrameRequester = () => {
335
337
  this.tweenGroup.update();
336
338
  this.view.notifyChange(this.camera);
@@ -343,6 +345,7 @@ class StreetControls extends FirstPersonControls {
343
345
  if (this.tween) {
344
346
  this.tween.stop();
345
347
  this.tween = undefined;
348
+ this.tweenGroup.removeAll();
346
349
  }
347
350
  if (this.animationFrameRequester) {
348
351
  this.view.removeFrameRequester(MAIN_LOOP_EVENTS.BEFORE_RENDER, this.animationFrameRequester);
@@ -27,8 +27,8 @@ const crsWGS84 = 'EPSG:4326';
27
27
  class FeatureMesh extends THREE.Group {
28
28
  #currentCrs;
29
29
  #originalCrs;
30
- #collection = new THREE.Group();
31
- #place = new THREE.Group();
30
+ #collection = (() => new THREE.Group())();
31
+ #place = (() => new THREE.Group())();
32
32
  constructor(meshes, collection) {
33
33
  super();
34
34
  this.meshes = new THREE.Group().add(...meshes);
@@ -55,8 +55,15 @@ class FeatureMesh extends THREE.Group {
55
55
  // calculate the scale transformation to transform the feature.extent
56
56
  // to feature.extent.as(crs)
57
57
  coord.crs = Crs.formatToEPSG(this.#originalCrs);
58
- extent.copy(this.extent).applyMatrix4(this.#collection.matrix);
59
- extent.as(coord.crs, extent);
58
+ // TODO: An extent here could be either a geographic extent (for
59
+ // features from WFS) or a tiled extent (for features from MVT).
60
+ // Unify both behavior.
61
+ if (this.extent.isExtent) {
62
+ extent.copy(this.extent).applyMatrix4(this.#collection.matrix);
63
+ extent.as(coord.crs, extent);
64
+ } else {
65
+ this.extent.toExtent(coord.crs, extent);
66
+ }
60
67
  extent.spatialEuclideanDimensions(dim_ref);
61
68
  extent.planarDimensions(dim);
62
69
  if (dim.x && dim.y) {
@@ -15,15 +15,15 @@ function textureColorLayer(texture, layer) {
15
15
  return textureLayer(texture, layer);
16
16
  }
17
17
  export default {
18
- convert(data, extentDestination, layer) {
18
+ convert(data, destinationTile, layer) {
19
19
  let texture;
20
20
  if (data.isFeatureCollection) {
21
21
  const backgroundLayer = layer.source.backgroundLayer;
22
22
  const backgroundColor = backgroundLayer && backgroundLayer.paint ? new THREE.Color(backgroundLayer.paint['background-color']) : undefined;
23
- extentDestination.as(CRS.formatToEPSG(layer.crs), extentTexture);
23
+ destinationTile.toExtent(CRS.formatToEPSG(layer.crs), extentTexture);
24
24
  texture = Feature2Texture.createTextureFromFeature(data, extentTexture, layer.subdivisionThreshold, layer.style, backgroundColor);
25
25
  texture.features = data;
26
- texture.extent = extentDestination;
26
+ texture.extent = destinationTile;
27
27
  } else if (data.isTexture) {
28
28
  texture = data;
29
29
  } else {
@@ -332,8 +332,8 @@ const alignYtoEast = new THREE.Quaternion();
332
332
  */
333
333
 
334
334
  export class FeatureCollection extends THREE.Object3D {
335
- #transformToLocalSystem = transformToLocalSystem2D;
336
- #setLocalSystem = doNothing;
335
+ #transformToLocalSystem = (() => transformToLocalSystem2D)();
336
+ #setLocalSystem = (() => doNothing)();
337
337
  /**
338
338
  * @param {FeatureBuildingOptions|Layer} options The building options .
339
339
  */