itowns 2.45.1-next.0 → 2.45.1-next.1

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 (185) hide show
  1. package/dist/455.js +2 -0
  2. package/dist/455.js.map +1 -0
  3. package/dist/debug.js +3 -0
  4. package/dist/debug.js.LICENSE.txt +13 -0
  5. package/dist/debug.js.map +1 -0
  6. package/dist/itowns.js +3 -0
  7. package/dist/itowns.js.LICENSE.txt +5 -0
  8. package/dist/itowns.js.map +1 -0
  9. package/dist/itowns_lasparser.js +2 -0
  10. package/dist/itowns_lasparser.js.map +1 -0
  11. package/dist/itowns_lasworker.js +2 -0
  12. package/dist/itowns_lasworker.js.map +1 -0
  13. package/dist/itowns_potree2worker.js +2 -0
  14. package/dist/itowns_potree2worker.js.map +1 -0
  15. package/dist/itowns_widgets.js +2 -0
  16. package/dist/itowns_widgets.js.map +1 -0
  17. package/lib/Controls/FirstPersonControls.js +308 -0
  18. package/lib/Controls/FlyControls.js +175 -0
  19. package/lib/Controls/GlobeControls.js +1178 -0
  20. package/lib/Controls/PlanarControls.js +1025 -0
  21. package/lib/Controls/StateControl.js +432 -0
  22. package/lib/Controls/StreetControls.js +392 -0
  23. package/lib/Converter/Feature2Mesh.js +612 -0
  24. package/lib/Converter/Feature2Texture.js +174 -0
  25. package/lib/Converter/convertToTile.js +70 -0
  26. package/lib/Converter/textureConverter.js +43 -0
  27. package/lib/Core/3DTiles/C3DTBatchTable.js +131 -0
  28. package/lib/Core/3DTiles/C3DTBatchTableHierarchyExtension.js +96 -0
  29. package/lib/Core/3DTiles/C3DTBoundingVolume.js +156 -0
  30. package/lib/Core/3DTiles/C3DTExtensions.js +97 -0
  31. package/lib/Core/3DTiles/C3DTFeature.js +110 -0
  32. package/lib/Core/3DTiles/C3DTilesEnums.js +20 -0
  33. package/lib/Core/3DTiles/C3DTileset.js +99 -0
  34. package/lib/Core/3DTiles/utils/BinaryPropertyAccessor.js +100 -0
  35. package/lib/Core/AnimationPlayer.js +142 -0
  36. package/lib/Core/CopcNode.js +174 -0
  37. package/lib/Core/Deprecated/Undeprecator.js +74 -0
  38. package/lib/Core/EntwinePointTileNode.js +126 -0
  39. package/lib/Core/Feature.js +488 -0
  40. package/lib/Core/Geographic/GeoidGrid.js +108 -0
  41. package/lib/Core/Label.js +222 -0
  42. package/lib/Core/MainLoop.js +209 -0
  43. package/lib/Core/Picking.js +255 -0
  44. package/lib/Core/PointCloudNode.js +42 -0
  45. package/lib/Core/Potree2Node.js +206 -0
  46. package/lib/Core/Potree2PointAttributes.js +139 -0
  47. package/lib/Core/PotreeNode.js +101 -0
  48. package/lib/Core/Prefab/Globe/Atmosphere.js +293 -0
  49. package/lib/Core/Prefab/Globe/GlobeLayer.js +152 -0
  50. package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +110 -0
  51. package/lib/Core/Prefab/Globe/SkyShader.js +78 -0
  52. package/lib/Core/Prefab/GlobeView.js +155 -0
  53. package/lib/Core/Prefab/Planar/PlanarLayer.js +59 -0
  54. package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +71 -0
  55. package/lib/Core/Prefab/PlanarView.js +62 -0
  56. package/lib/Core/Prefab/TileBuilder.js +82 -0
  57. package/lib/Core/Prefab/computeBufferTileGeometry.js +248 -0
  58. package/lib/Core/Scheduler/Cache.js +17 -0
  59. package/lib/Core/Scheduler/CancelledCommandException.js +15 -0
  60. package/lib/Core/Scheduler/Scheduler.js +294 -0
  61. package/lib/Core/Style.js +660 -0
  62. package/lib/Core/StyleOptions.js +486 -0
  63. package/lib/Core/System/Capabilities.js +63 -0
  64. package/lib/Core/Tile/Tile.js +205 -0
  65. package/lib/Core/Tile/TileGrid.js +49 -0
  66. package/lib/Core/TileGeometry.js +124 -0
  67. package/lib/Core/TileMesh.js +108 -0
  68. package/lib/Core/View.js +1115 -0
  69. package/lib/Layer/C3DTilesLayer.js +459 -0
  70. package/lib/Layer/ColorLayer.js +154 -0
  71. package/lib/Layer/CopcLayer.js +63 -0
  72. package/lib/Layer/ElevationLayer.js +139 -0
  73. package/lib/Layer/EntwinePointTileLayer.js +71 -0
  74. package/lib/Layer/FeatureGeometryLayer.js +77 -0
  75. package/lib/Layer/GeoidLayer.js +80 -0
  76. package/lib/Layer/GeometryLayer.js +233 -0
  77. package/lib/Layer/InfoLayer.js +64 -0
  78. package/lib/Layer/LabelLayer.js +469 -0
  79. package/lib/Layer/Layer.js +335 -0
  80. package/lib/Layer/LayerUpdateState.js +89 -0
  81. package/lib/Layer/LayerUpdateStrategy.js +80 -0
  82. package/lib/Layer/OGC3DTilesLayer.js +543 -0
  83. package/lib/Layer/OrientedImageLayer.js +227 -0
  84. package/lib/Layer/PointCloudLayer.js +405 -0
  85. package/lib/Layer/Potree2Layer.js +171 -0
  86. package/lib/Layer/PotreeLayer.js +72 -0
  87. package/lib/Layer/RasterLayer.js +37 -0
  88. package/lib/Layer/ReferencingLayerProperties.js +62 -0
  89. package/lib/Layer/TiledGeometryLayer.js +459 -0
  90. package/lib/Loader/LASLoader.js +193 -0
  91. package/lib/Loader/Potree2BrotliLoader.js +261 -0
  92. package/lib/Loader/Potree2Loader.js +207 -0
  93. package/lib/Main.js +113 -0
  94. package/lib/MainBundle.js +4 -0
  95. package/lib/Parser/B3dmParser.js +174 -0
  96. package/lib/Parser/CameraCalibrationParser.js +94 -0
  97. package/lib/Parser/GDFParser.js +72 -0
  98. package/lib/Parser/GTXParser.js +75 -0
  99. package/lib/Parser/GeoJsonParser.js +212 -0
  100. package/lib/Parser/GpxParser.js +25 -0
  101. package/lib/Parser/ISGParser.js +71 -0
  102. package/lib/Parser/KMLParser.js +25 -0
  103. package/lib/Parser/LASParser.js +137 -0
  104. package/lib/Parser/MapBoxUrlParser.js +83 -0
  105. package/lib/Parser/PntsParser.js +131 -0
  106. package/lib/Parser/Potree2BinParser.js +92 -0
  107. package/lib/Parser/PotreeBinParser.js +106 -0
  108. package/lib/Parser/PotreeCinParser.js +29 -0
  109. package/lib/Parser/ShapefileParser.js +78 -0
  110. package/lib/Parser/VectorTileParser.js +215 -0
  111. package/lib/Parser/XbilParser.js +120 -0
  112. package/lib/Parser/deprecated/LegacyGLTFLoader.js +1386 -0
  113. package/lib/Parser/iGLTFLoader.js +168 -0
  114. package/lib/Process/3dTilesProcessing.js +304 -0
  115. package/lib/Process/FeatureProcessing.js +76 -0
  116. package/lib/Process/LayeredMaterialNodeProcessing.js +229 -0
  117. package/lib/Process/ObjectRemovalHelper.js +97 -0
  118. package/lib/Process/handlerNodeError.js +23 -0
  119. package/lib/Provider/3dTilesProvider.js +149 -0
  120. package/lib/Provider/DataSourceProvider.js +24 -0
  121. package/lib/Provider/Fetcher.js +233 -0
  122. package/lib/Provider/PointCloudProvider.js +45 -0
  123. package/lib/Provider/TileProvider.js +16 -0
  124. package/lib/Provider/URLBuilder.js +116 -0
  125. package/lib/Renderer/Camera.js +281 -0
  126. package/lib/Renderer/Color.js +56 -0
  127. package/lib/Renderer/ColorLayersOrdering.js +115 -0
  128. package/lib/Renderer/CommonMaterial.js +31 -0
  129. package/lib/Renderer/Label2DRenderer.js +192 -0
  130. package/lib/Renderer/LayeredMaterial.js +243 -0
  131. package/lib/Renderer/OBB.js +150 -0
  132. package/lib/Renderer/OrientedImageCamera.js +118 -0
  133. package/lib/Renderer/OrientedImageMaterial.js +167 -0
  134. package/lib/Renderer/PointsMaterial.js +485 -0
  135. package/lib/Renderer/RasterTile.js +243 -0
  136. package/lib/Renderer/RenderMode.js +31 -0
  137. package/lib/Renderer/Shader/ShaderChunk.js +160 -0
  138. package/lib/Renderer/Shader/ShaderUtils.js +47 -0
  139. package/lib/Renderer/SphereHelper.js +17 -0
  140. package/lib/Renderer/WebXR.js +51 -0
  141. package/lib/Renderer/c3DEngine.js +214 -0
  142. package/lib/Source/C3DTilesGoogleSource.js +74 -0
  143. package/lib/Source/C3DTilesIonSource.js +54 -0
  144. package/lib/Source/C3DTilesSource.js +30 -0
  145. package/lib/Source/CopcSource.js +126 -0
  146. package/lib/Source/EntwinePointTileSource.js +72 -0
  147. package/lib/Source/FileSource.js +188 -0
  148. package/lib/Source/OGC3DTilesGoogleSource.js +29 -0
  149. package/lib/Source/OGC3DTilesIonSource.js +34 -0
  150. package/lib/Source/OGC3DTilesSource.js +21 -0
  151. package/lib/Source/OrientedImageSource.js +59 -0
  152. package/lib/Source/Potree2Source.js +167 -0
  153. package/lib/Source/PotreeSource.js +82 -0
  154. package/lib/Source/Source.js +202 -0
  155. package/lib/Source/TMSSource.js +144 -0
  156. package/lib/Source/VectorTilesSource.js +182 -0
  157. package/lib/Source/WFSSource.js +170 -0
  158. package/lib/Source/WMSSource.js +167 -0
  159. package/lib/Source/WMTSSource.js +92 -0
  160. package/lib/ThreeExtended/capabilities/WebGL.js +69 -0
  161. package/lib/ThreeExtended/libs/ktx-parse.module.js +506 -0
  162. package/lib/ThreeExtended/libs/zstddec.module.js +29 -0
  163. package/lib/ThreeExtended/loaders/DDSLoader.js +200 -0
  164. package/lib/ThreeExtended/loaders/DRACOLoader.js +400 -0
  165. package/lib/ThreeExtended/loaders/GLTFLoader.js +2879 -0
  166. package/lib/ThreeExtended/loaders/KTX2Loader.js +709 -0
  167. package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
  168. package/lib/ThreeExtended/utils/BufferGeometryUtils.js +846 -0
  169. package/lib/ThreeExtended/utils/WorkerPool.js +70 -0
  170. package/lib/Utils/CameraUtils.js +554 -0
  171. package/lib/Utils/DEMUtils.js +350 -0
  172. package/lib/Utils/FeaturesUtils.js +156 -0
  173. package/lib/Utils/Gradients.js +16 -0
  174. package/lib/Utils/ThreeUtils.js +115 -0
  175. package/lib/Utils/gui/C3DTilesStyle.js +218 -0
  176. package/lib/Utils/gui/Main.js +7 -0
  177. package/lib/Utils/gui/Minimap.js +152 -0
  178. package/lib/Utils/gui/Navigation.js +245 -0
  179. package/lib/Utils/gui/Scale.js +104 -0
  180. package/lib/Utils/gui/Searchbar.js +234 -0
  181. package/lib/Utils/gui/Widget.js +80 -0
  182. package/lib/Utils/placeObjectOnGround.js +136 -0
  183. package/lib/Worker/LASLoaderWorker.js +19 -0
  184. package/lib/Worker/Potree2Worker.js +21 -0
  185. package/package.json +2 -2
@@ -0,0 +1,459 @@
1
+ import * as THREE from 'three';
2
+ import GeometryLayer from "./GeometryLayer.js";
3
+ import { InfoTiledGeometryLayer } from "./InfoLayer.js";
4
+ import Picking from "../Core/Picking.js";
5
+ import convertToTile from "../Converter/convertToTile.js";
6
+ import ObjectRemovalHelper from "../Process/ObjectRemovalHelper.js";
7
+ import { ImageryLayers } from "./Layer.js";
8
+ import { CACHE_POLICIES } from "../Core/Scheduler/Cache.js";
9
+ const subdivisionVector = new THREE.Vector3();
10
+ const boundingSphereCenter = new THREE.Vector3();
11
+
12
+ /**
13
+ * @property {InfoTiledGeometryLayer} info - Status information of layer
14
+ * @property {boolean} isTiledGeometryLayer - Used to checkout whether this
15
+ * layer is a TiledGeometryLayer. Default is true. You should not change this,
16
+ * as it is used internally for optimisation.
17
+ * @property {boolean} hideSkirt (default false) - Used to hide the skirt (tile borders).
18
+ * Useful when the layer opacity < 1
19
+ *
20
+ * @extends GeometryLayer
21
+ */
22
+ class TiledGeometryLayer extends GeometryLayer {
23
+ /**
24
+ * A layer extending the {@link GeometryLayer}, but with a tiling notion.
25
+ *
26
+ * `TiledGeometryLayer` is the ground where `ColorLayer` and `ElevationLayer` are attached.
27
+ * `TiledGeometryLayer` is a quadtree data structure. At zoom 0,
28
+ * there is a single tile for the whole earth. At zoom level 1,
29
+ * the single tile splits into 4 tiles (2x2 tile square).
30
+ * Each zoom level quadtree divides the geometry tiles of the one before it.
31
+ * The camera distance determines how the tiles are subdivided for optimal data display.
32
+ *
33
+ * Some `GeometryLayer` can also be attached to the `TiledGeometryLayer` if they want to take advantage of the quadtree.
34
+ *
35
+ * ![tiled geometry](/docs/static/images/tiledGeometry.jpeg)
36
+ * _In `GlobeView`, **red lines** represents the **WGS84 grid** and **orange lines** the **Pseudo-mercator grid**._
37
+ * _In this picture, there are tiles with 3 different zoom/levels._
38
+ *
39
+ * The zoom/level is based on [tiled web map](https://en.wikipedia.org/wiki/Tiled_web_map).
40
+ * It corresponds at meters by pixel. If the projection tile exceeds a certain pixel size (on screen)
41
+ * then it is subdivided into 4 tiles with a zoom greater than 1.
42
+ *
43
+ * @param {string} id - The id of the layer, that should be unique. It is
44
+ * not mandatory, but an error will be emitted if this layer is added a
45
+ * {@link View} that already has a layer going by that id.
46
+ * @param {THREE.Object3D} object3d - The object3d used to contain the
47
+ * geometry of the TiledGeometryLayer. It is usually a `THREE.Group`, but it
48
+ * can be anything inheriting from a `THREE.Object3d`.
49
+ * @param {Array} schemeTile - extents Array of root tiles
50
+ * @param {Object} builder - builder geometry object
51
+ * @param {Object} [config] - Optional configuration, all elements in it
52
+ * will be merged as is in the layer. For example, if the configuration
53
+ * contains three elements `name, protocol, extent`, these elements will be
54
+ * available using `layer.name` or something else depending on the property
55
+ * name.
56
+ * @param {Source} [config.source] - Description and options of the source.
57
+ *
58
+ * @throws {Error} `object3d` must be a valid `THREE.Object3d`.
59
+ */
60
+ constructor(id, object3d, schemeTile, builder, config) {
61
+ const {
62
+ sseSubdivisionThreshold = 1.0,
63
+ minSubdivisionLevel,
64
+ maxSubdivisionLevel,
65
+ maxDeltaElevationLevel,
66
+ tileMatrixSets,
67
+ diffuse,
68
+ showOutline = false,
69
+ segments,
70
+ disableSkirt = false,
71
+ materialOptions,
72
+ ...configGeometryLayer
73
+ } = config;
74
+ super(id, object3d, {
75
+ ...configGeometryLayer,
76
+ // cacheLifeTime = CACHE_POLICIES.INFINITE because the cache is handled by the builder
77
+ cacheLifeTime: CACHE_POLICIES.INFINITE,
78
+ source: false
79
+ });
80
+
81
+ /**
82
+ * @type {boolean}
83
+ * @readonly
84
+ */
85
+ this.isTiledGeometryLayer = true;
86
+ this.protocol = 'tile';
87
+
88
+ // TODO : this should be add in a preprocess method specific to GeoidLayer.
89
+ this.object3d.geoidHeight = 0;
90
+
91
+ /**
92
+ * @type {boolean}
93
+ */
94
+ this.disableSkirt = disableSkirt;
95
+ this._hideSkirt = !!config.hideSkirt;
96
+
97
+ /**
98
+ * @type {number}
99
+ */
100
+ this.sseSubdivisionThreshold = sseSubdivisionThreshold;
101
+
102
+ /**
103
+ * @type {number}
104
+ */
105
+ this.minSubdivisionLevel = minSubdivisionLevel;
106
+
107
+ /**
108
+ * @type {number}
109
+ */
110
+ this.maxSubdivisionLevel = maxSubdivisionLevel;
111
+
112
+ /**
113
+ * @type {number}
114
+ * @deprecated
115
+ */
116
+ this.maxDeltaElevationLevel = maxDeltaElevationLevel;
117
+ this.segments = segments;
118
+ this.schemeTile = schemeTile;
119
+ this.builder = builder;
120
+ this.info = new InfoTiledGeometryLayer(this);
121
+ if (!this.schemeTile) {
122
+ throw new Error(`Cannot init tiled layer without schemeTile for layer ${this.id}`);
123
+ }
124
+ if (!this.builder) {
125
+ throw new Error(`Cannot init tiled layer without builder for layer ${this.id}`);
126
+ }
127
+ this.maxScreenSizeNode = this.sseSubdivisionThreshold * (this.sizeDiagonalTexture * 2);
128
+ this.tileMatrixSets = tileMatrixSets;
129
+ this.materialOptions = materialOptions;
130
+
131
+ /*
132
+ * @type {boolean}
133
+ */
134
+ this.showOutline = showOutline;
135
+
136
+ /**
137
+ * @type {THREE.Vector3 | undefined}
138
+ */
139
+ this.diffuse = diffuse;
140
+ this.level0Nodes = [];
141
+ const promises = [];
142
+ for (const root of this.schemeTile) {
143
+ promises.push(this.convert(undefined, root));
144
+ }
145
+ this._promises.push(Promise.all(promises).then(level0s => {
146
+ this.level0Nodes = level0s;
147
+ this.object3d.add(...level0s);
148
+ this.object3d.updateMatrixWorld();
149
+ }));
150
+ }
151
+ get hideSkirt() {
152
+ return this._hideSkirt;
153
+ }
154
+ set hideSkirt(value) {
155
+ if (!this.level0Nodes) {
156
+ return;
157
+ }
158
+ this._hideSkirt = value;
159
+ for (const node of this.level0Nodes) {
160
+ node.traverse(obj => {
161
+ if (obj.isTileMesh) {
162
+ obj.geometry.hideSkirt = value;
163
+ }
164
+ });
165
+ }
166
+ }
167
+ /**
168
+ * Picking method for this layer. It uses the {@link Picking#pickTilesAt}
169
+ * method.
170
+ *
171
+ * @param {View} view - The view instance.
172
+ * @param {Object} coordinates - The coordinates to pick in the view. It
173
+ * should have at least `x` and `y` properties.
174
+ * @param {number} radius - Radius of the picking circle.
175
+ * @param {Array} target - Array to push picking result.
176
+ *
177
+ * @return {Array} An array containing all targets picked under the
178
+ * specified coordinates.
179
+ */
180
+ pickObjectsAt(view, coordinates) {
181
+ let radius = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.options.defaultPickingRadius;
182
+ let target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
183
+ return Picking.pickTilesAt(view, coordinates, radius, this, target);
184
+ }
185
+
186
+ /**
187
+ * Does pre-update work on the context:
188
+ * <ul>
189
+ * <li>update the `colorLayers` and `elevationLayers`</li>
190
+ * <li>update the `maxElevationLevel`</li>
191
+ * </ul>
192
+ *
193
+ * Once this work is done, it returns a list of nodes to update. Depending
194
+ * on the origin of `sources`, it can return a few things:
195
+ * <ul>
196
+ * <li>if `sources` is empty, it returns the first node of the layer
197
+ * (stored as `level0Nodes`), which will trigger the update of the whole
198
+ * tree</li>
199
+ * <li>if the update is triggered by a camera move, the whole tree is
200
+ * returned too</li>
201
+ * <li>if `source.layer` is this layer, it means that `source` is a node; a
202
+ * common ancestor will be found if there are multiple sources, with the
203
+ * default common ancestor being the first source itself</li>
204
+ * <li>else it returns the whole tree</li>
205
+ * </ul>
206
+ *
207
+ * @param {Object} context - The context of the update; see the {@link
208
+ * MainLoop} for more informations.
209
+ * @param {Set<GeometryLayer|TileMesh>} sources - A list of sources to
210
+ * generate a list of nodes to update.
211
+ *
212
+ * @return {TileMesh[]} The array of nodes to update.
213
+ */
214
+ preUpdate(context, sources) {
215
+ if (sources.has(undefined) || sources.size == 0) {
216
+ return this.level0Nodes;
217
+ }
218
+ context.colorLayers = context.view.getLayers((l, a) => a && a.id == this.id && l.isColorLayer);
219
+ context.elevationLayers = context.view.getLayers((l, a) => a && a.id == this.id && l.isElevationLayer);
220
+ context.maxElevationLevel = -1;
221
+ for (const e of context.elevationLayers) {
222
+ context.maxElevationLevel = Math.max(e.source.zoom.max, context.maxElevationLevel);
223
+ }
224
+ if (context.maxElevationLevel == -1) {
225
+ context.maxElevationLevel = Infinity;
226
+ }
227
+
228
+ // Prepare ColorLayer sequence order
229
+ // In this moment, there is only one color layers sequence, because they are attached to tileLayer.
230
+ // In future, the sequence must be returned by parent geometry layer.
231
+ this.colorLayersOrder = ImageryLayers.getColorLayersIdOrderedBySequence(context.colorLayers);
232
+ let commonAncestor;
233
+ for (const source of sources.values()) {
234
+ if (source.isCamera) {
235
+ // if the change is caused by a camera move, no need to bother
236
+ // to find common ancestor: we need to update the whole tree:
237
+ // some invisible tiles may now be visible
238
+ return this.level0Nodes;
239
+ }
240
+ if (source.layer === this) {
241
+ if (!commonAncestor) {
242
+ commonAncestor = source;
243
+ } else {
244
+ commonAncestor = source.findCommonAncestor(commonAncestor);
245
+ if (!commonAncestor) {
246
+ return this.level0Nodes;
247
+ }
248
+ }
249
+ if (commonAncestor.material == null) {
250
+ commonAncestor = undefined;
251
+ }
252
+ }
253
+ }
254
+ if (commonAncestor) {
255
+ return [commonAncestor];
256
+ } else {
257
+ return this.level0Nodes;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Update a node of this layer. The node will not be updated if:
263
+ * <ul>
264
+ * <li>it does not have a parent, then it is removed</li>
265
+ * <li>its parent is being subdivided</li>
266
+ * <li>is not visible in the camera</li>
267
+ * </ul>
268
+ *
269
+ * @param {Object} context - The context of the update; see the {@link
270
+ * MainLoop} for more informations.
271
+ * @param {Layer} layer - Parameter to be removed once all update methods
272
+ * have been aligned.
273
+ * @param {TileMesh} node - The node to update.
274
+ *
275
+ * @returns {Object}
276
+ */
277
+ update(context, layer, node) {
278
+ if (!node.parent) {
279
+ return ObjectRemovalHelper.removeChildrenAndCleanup(this, node);
280
+ }
281
+ // early exit if parent' subdivision is in progress
282
+ if (node.parent.pendingSubdivision) {
283
+ node.visible = false;
284
+ node.material.visible = false;
285
+ this.info.update(node);
286
+ return undefined;
287
+ }
288
+
289
+ // do proper culling
290
+ node.visible = !this.culling(node, context.camera);
291
+ if (node.visible) {
292
+ let requestChildrenUpdate = false;
293
+ node.material.visible = true;
294
+ this.info.update(node);
295
+ if (node.pendingSubdivision || TiledGeometryLayer.hasEnoughTexturesToSubdivide(context, node) && this.subdivision(context, this, node)) {
296
+ this.subdivideNode(context, node);
297
+ // display iff children aren't ready
298
+ node.material.visible = node.pendingSubdivision;
299
+ this.info.update(node);
300
+ requestChildrenUpdate = true;
301
+ }
302
+ if (node.material.visible) {
303
+ if (!requestChildrenUpdate) {
304
+ return ObjectRemovalHelper.removeChildren(this, node);
305
+ }
306
+ }
307
+ return requestChildrenUpdate ? node.children.filter(n => n.layer == this) : undefined;
308
+ }
309
+ node.material.visible = false;
310
+ this.info.update(node);
311
+ return ObjectRemovalHelper.removeChildren(this, node);
312
+ }
313
+ convert(requester, extent) {
314
+ return convertToTile.convert(requester, extent, this);
315
+ }
316
+ countColorLayersTextures() {
317
+ return arguments.length;
318
+ }
319
+
320
+ // eslint-disable-next-line
321
+ culling(node, camera) {
322
+ return !camera.isBox3Visible(node.obb.box3D, node.matrixWorld);
323
+ }
324
+
325
+ /**
326
+ * Tell if a node has enough elevation or color textures to subdivide.
327
+ * Subdivision is prevented if:
328
+ * <ul>
329
+ * <li>the node is covered by at least one elevation layer and if the node
330
+ * doesn't have an elevation texture yet</li>
331
+ * <li>a color texture is missing</li>
332
+ * </ul>
333
+ *
334
+ * @param {Object} context - The context of the update; see the {@link
335
+ * MainLoop} for more informations.
336
+ * @param {TileMesh} node - The node to subdivide.
337
+ *
338
+ * @returns {boolean} False if the node can not be subdivided, true
339
+ * otherwise.
340
+ */
341
+ static hasEnoughTexturesToSubdivide(context, node) {
342
+ const layerUpdateState = node.layerUpdateState || {};
343
+ let nodeLayer = node.material.getElevationLayer();
344
+ for (const e of context.elevationLayers) {
345
+ const extents = node.getExtentsByProjection(e.crs);
346
+ const zoom = extents[0].zoom;
347
+ if (zoom > e.zoom.max || zoom < e.zoom.min) {
348
+ continue;
349
+ }
350
+ if (!e.frozen && e.ready && e.source.extentInsideLimit(node.extent, zoom) && (!nodeLayer || nodeLayer.level < 0)) {
351
+ // no stop subdivision in the case of a loading error
352
+ if (layerUpdateState[e.id] && layerUpdateState[e.id].inError()) {
353
+ continue;
354
+ }
355
+ return false;
356
+ }
357
+ }
358
+ for (const c of context.colorLayers) {
359
+ if (c.frozen || !c.visible || !c.ready) {
360
+ continue;
361
+ }
362
+ const extents = node.getExtentsByProjection(c.crs);
363
+ const zoom = extents[0].zoom;
364
+ if (zoom > c.zoom.max || zoom < c.zoom.min) {
365
+ continue;
366
+ }
367
+ // no stop subdivision in the case of a loading error
368
+ if (layerUpdateState[c.id] && layerUpdateState[c.id].inError()) {
369
+ continue;
370
+ }
371
+ nodeLayer = node.material.getLayer(c.id);
372
+ if (c.source.extentInsideLimit(node.extent, zoom) && (!nodeLayer || nodeLayer.level < 0)) {
373
+ return false;
374
+ }
375
+ }
376
+ return true;
377
+ }
378
+
379
+ /**
380
+ * Subdivides a node of this layer. If the node is currently in the process
381
+ * of subdivision, it will not do anything here. The subdivision of a node
382
+ * will occur in four part, to create a quadtree. The extent of the node
383
+ * will be divided in four parts: north-west, north-east, south-west and
384
+ * south-east. Once all four nodes are created, they will be added to the
385
+ * current node and the view of the context will be notified of this change.
386
+ *
387
+ * @param {Object} context - The context of the update; see the {@link
388
+ * MainLoop} for more informations.
389
+ * @param {TileMesh} node - The node to subdivide.
390
+ * @return {Promise} { description_of_the_return_value }
391
+ */
392
+ subdivideNode(context, node) {
393
+ if (!node.pendingSubdivision && !node.children.some(n => n.layer == this)) {
394
+ const extents = node.extent.subdivision();
395
+ // TODO: pendingSubdivision mechanism is fragile, get rid of it
396
+ node.pendingSubdivision = true;
397
+ const command = {
398
+ /* mandatory */
399
+ view: context.view,
400
+ requester: node,
401
+ layer: this,
402
+ priority: 10000,
403
+ /* specific params */
404
+ extentsSource: extents,
405
+ redraw: false
406
+ };
407
+ return context.scheduler.execute(command).then(children => {
408
+ for (const child of children) {
409
+ node.add(child);
410
+ child.updateMatrixWorld(true);
411
+ }
412
+ node.pendingSubdivision = false;
413
+ context.view.notifyChange(node, false);
414
+ }, err => {
415
+ node.pendingSubdivision = false;
416
+ if (!err.isCancelledCommandException) {
417
+ throw new Error(err);
418
+ }
419
+ });
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Test the subdvision of a node, compared to this layer.
425
+ *
426
+ * @param {Object} context - The context of the update; see the {@link
427
+ * MainLoop} for more informations.
428
+ * @param {PlanarLayer} layer - This layer, parameter to be removed.
429
+ * @param {TileMesh} node - The node to test.
430
+ *
431
+ * @return {boolean} - True if the node is subdivisable, otherwise false.
432
+ */
433
+ subdivision(context, layer, node) {
434
+ if (node.level < this.minSubdivisionLevel) {
435
+ return true;
436
+ }
437
+ if (this.maxSubdivisionLevel <= node.level) {
438
+ return false;
439
+ }
440
+ subdivisionVector.setFromMatrixScale(node.matrixWorld);
441
+ boundingSphereCenter.copy(node.boundingSphere.center).applyMatrix4(node.matrixWorld);
442
+ const distance = Math.max(0.0, context.camera.camera3D.position.distanceTo(boundingSphereCenter) - node.boundingSphere.radius * subdivisionVector.x);
443
+
444
+ // Size projection on pixel of bounding
445
+ if (context.camera.camera3D.isOrthographicCamera) {
446
+ const camera3D = context.camera.camera3D;
447
+ const preSSE = context.camera._preSSE * 2 * camera3D.zoom / (camera3D.top - camera3D.bottom);
448
+ node.screenSize = preSSE * node.boundingSphere.radius * subdivisionVector.x;
449
+ } else {
450
+ node.screenSize = context.camera._preSSE * (2 * node.boundingSphere.radius * subdivisionVector.x) / distance;
451
+ }
452
+
453
+ // The screen space error is calculated to have a correct texture display.
454
+ // For the projection of a texture's texel to be less than or equal to one pixel
455
+ const sse = node.screenSize / (this.sizeDiagonalTexture * 2);
456
+ return this.sseSubdivisionThreshold < sse;
457
+ }
458
+ }
459
+ export default TiledGeometryLayer;
@@ -0,0 +1,193 @@
1
+ import { LazPerf } from 'laz-perf';
2
+ import { Las } from 'copc';
3
+
4
+ /**
5
+ * @typedef {Object} Header - Partial LAS header.
6
+ * @property {number} header.pointDataRecordFormat - Type of point data
7
+ * records contained by the buffer.
8
+ * @property {number} header.pointDataRecordLength - Size (in bytes) of the
9
+ * point data records. If the specified size is larger than implied by the
10
+ * point data record format (see above) the remaining bytes are user-specfic
11
+ * "extra bytes". Those are described by an Extra Bytes VLR.
12
+ * @property {number[]} header.scale - Scale factors (an array `[xScale,
13
+ * yScale, zScale]`) multiplied to the X, Y, Z point record values.
14
+ * @property {number[]} header.offset - Offsets (an array `[xOffset,
15
+ * xOffset, zOffset]`) added to the scaled X, Y, Z point record values.
16
+ */
17
+
18
+ function defaultColorEncoding(header) {
19
+ return header.majorVersion === 1 && header.minorVersion <= 2 ? 8 : 16;
20
+ }
21
+
22
+ /**
23
+ * @classdesc
24
+ * Loader for LAS and LAZ (LASZip) point clouds. It uses the copc.js library and
25
+ * the laz-perf decoder under the hood.
26
+ *
27
+ * The laz-perf web assembly module is lazily fetched at runtime when a parsing
28
+ * request is initiated. Location of laz-perf wasm defaults to the unpkg
29
+ * repository.
30
+ */
31
+ class LASLoader {
32
+ constructor() {
33
+ this._wasmPath = 'https://cdn.jsdelivr.net/npm/laz-perf@0.0.6/lib';
34
+ this._wasmPromise = null;
35
+ }
36
+ _initDecoder() {
37
+ if (this._wasmPromise) {
38
+ return this._wasmPromise;
39
+ }
40
+ this._wasmPromise = LazPerf.create({
41
+ locateFile: file => `${this._wasmPath}/${file}`
42
+ });
43
+ return this._wasmPromise;
44
+ }
45
+ _parseView(view, options) {
46
+ const colorDepth = options.colorDepth ?? 16;
47
+ const getPosition = ['X', 'Y', 'Z'].map(view.getter);
48
+ const getIntensity = view.getter('Intensity');
49
+ const getReturnNumber = view.getter('ReturnNumber');
50
+ const getNumberOfReturns = view.getter('NumberOfReturns');
51
+ const getClassification = view.getter('Classification');
52
+ const getPointSourceID = view.getter('PointSourceId');
53
+ const getColor = view.dimensions.Red ? ['Red', 'Green', 'Blue'].map(view.getter) : undefined;
54
+ const getScanAngle = view.getter('ScanAngle');
55
+ const positions = new Float32Array(view.pointCount * 3);
56
+ const intensities = new Uint16Array(view.pointCount);
57
+ const returnNumbers = new Uint8Array(view.pointCount);
58
+ const numberOfReturns = new Uint8Array(view.pointCount);
59
+ const classifications = new Uint8Array(view.pointCount);
60
+ const pointSourceIDs = new Uint16Array(view.pointCount);
61
+ const colors = getColor ? new Uint8Array(view.pointCount * 4) : undefined;
62
+ /*
63
+ As described by the LAS spec, Scan Angle is encoded:
64
+ - as signed char in a valid range from -90 to +90 (degrees) prior to the LAS 1.4 Point Data Record Formats (PDRF) 6
65
+ - as a signed short in a valid range from -30 000 to +30 000. Those values represents scan angles from -180 to +180
66
+ degrees with an increment of 0.006 for PDRF >= 6.
67
+ The copc.js library does the degree convertion and stores it as a `Float32`.
68
+ */
69
+ const scanAngles = new Float32Array(view.pointCount);
70
+
71
+ // For precision we take the first point that will be use as origin for a local referentiel.
72
+ const origin = getPosition.map(f => f(0)).map(val => Math.floor(val));
73
+ for (let i = 0; i < view.pointCount; i++) {
74
+ // `getPosition` apply scale and offset transform to the X, Y, Z
75
+ // values. See https://github.com/connormanning/copc.js/blob/master/src/las/extractor.ts.
76
+ const [x, y, z] = getPosition.map(f => f(i));
77
+ positions[i * 3] = x - origin[0];
78
+ positions[i * 3 + 1] = y - origin[1];
79
+ positions[i * 3 + 2] = z - origin[2];
80
+ intensities[i] = getIntensity(i);
81
+ returnNumbers[i] = getReturnNumber(i);
82
+ numberOfReturns[i] = getNumberOfReturns(i);
83
+ if (getColor) {
84
+ // Note that we do not infer color depth as it is expensive
85
+ // (i.e. traverse the whole view to check if there exists a red,
86
+ // green or blue value > 255).
87
+ let [r, g, b] = getColor.map(f => f(i));
88
+ if (colorDepth === 16) {
89
+ r /= 256;
90
+ g /= 256;
91
+ b /= 256;
92
+ }
93
+ colors[i * 4] = r;
94
+ colors[i * 4 + 1] = g;
95
+ colors[i * 4 + 2] = b;
96
+ colors[i * 4 + 3] = 255;
97
+ }
98
+ classifications[i] = getClassification(i);
99
+ pointSourceIDs[i] = getPointSourceID(i);
100
+ scanAngles[i] = getScanAngle(i);
101
+ }
102
+ return {
103
+ position: positions,
104
+ intensity: intensities,
105
+ returnNumber: returnNumbers,
106
+ numberOfReturns,
107
+ classification: classifications,
108
+ pointSourceID: pointSourceIDs,
109
+ color: colors,
110
+ scanAngle: scanAngles,
111
+ origin
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Set LazPerf decoder path.
117
+ * @param {string} path - path to `laz-perf.wasm` folder.
118
+ */
119
+ set lazPerf(path) {
120
+ this._wasmPath = path;
121
+ this._wasmPromise = null;
122
+ }
123
+
124
+ /**
125
+ * Parses a LAS or LAZ (LASZip) chunk. Note that this function is
126
+ * **CPU-bound** and shall be parallelised in a dedicated worker.
127
+ * @param {Uint8Array} data - File chunk data.
128
+ * @param {Object} options - Parsing options.
129
+ * @param {Header} options.header - Partial LAS header.
130
+ * @param {number} options.pointCount - Number of points encoded in this
131
+ * data chunk.
132
+ * @param {Las.ExtraBytes[]} [options.eb] - Extra bytes LAS VLRs
133
+ * headers.
134
+ * @param {8 | 16} [options.colorDepth] - Color depth encoding (in bits).
135
+ * Either 8 or 16 bits. Defaults to 8 bits for LAS 1.2 and 16 bits for later
136
+ * versions (as mandatory by the specification).
137
+ */
138
+ async parseChunk(data, options) {
139
+ const {
140
+ header,
141
+ eb,
142
+ pointCount
143
+ } = options;
144
+ const {
145
+ pointDataRecordFormat,
146
+ pointDataRecordLength
147
+ } = header;
148
+ const colorDepth = options.colorDepth ?? defaultColorEncoding(header);
149
+ const bytes = new Uint8Array(data);
150
+ const pointData = await Las.PointData.decompressChunk(bytes, {
151
+ pointCount,
152
+ pointDataRecordFormat,
153
+ pointDataRecordLength
154
+ }, this._initDecoder());
155
+ const view = Las.View.create(pointData, header, eb);
156
+ const attributes = this._parseView(view, {
157
+ colorDepth
158
+ });
159
+ return {
160
+ attributes
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Parses a LAS or LAZ (LASZip) file. Note that this function is
166
+ * **CPU-bound** and shall be parallelised in a dedicated worker.
167
+ * @param {ArrayBuffer} data - Binary data to parse.
168
+ * @param {Object} [options] - Parsing options.
169
+ * @param {8 | 16} [options.colorDepth] - Color depth encoding (in bits).
170
+ * Either 8 or 16 bits. Defaults to 8 bits for LAS 1.2 and 16 bits for later
171
+ * versions (as mandatory by the specification)
172
+ */
173
+ async parseFile(data) {
174
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
175
+ const bytes = new Uint8Array(data);
176
+ const pointData = await Las.PointData.decompressFile(bytes, this._initDecoder());
177
+ const header = Las.Header.parse(bytes);
178
+ const colorDepth = options.colorDepth ?? defaultColorEncoding(header);
179
+ const getter = async (begin, end) => bytes.slice(begin, end);
180
+ const vlrs = await Las.Vlr.walk(getter, header);
181
+ const ebVlr = Las.Vlr.find(vlrs, 'LASF_Spec', 4);
182
+ const eb = ebVlr && Las.ExtraBytes.parse(await Las.Vlr.fetch(getter, ebVlr));
183
+ const view = Las.View.create(pointData, header, eb);
184
+ const attributes = this._parseView(view, {
185
+ colorDepth
186
+ });
187
+ return {
188
+ header,
189
+ attributes
190
+ };
191
+ }
192
+ }
193
+ export default LASLoader;