@jdultra/threedtiles 8.0.1 → 9.0.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.
@@ -1,676 +0,0 @@
1
- import * as THREE from 'three';
2
- import { OBB } from "../geometry/obb";
3
- import { TileLoader } from "./TileLoader";
4
- import { v4 as uuidv4 } from "uuid";
5
- import * as path from "path-browserify"
6
- import { clamp } from "three/src/math/MathUtils";
7
- //import { Octree } from 'three/addons/math/Octree.js';
8
- //import { OctreeHelper } from 'three/addons/helpers/OctreeHelper.js';
9
-
10
- const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 1);
11
- const tempVec1 = new THREE.Vector3(0, 0, 0);
12
- const tempVec2 = new THREE.Vector3(0, 0, 0);
13
- const upVector = new THREE.Vector3(0, 1, 0);
14
- const rendererSize = new THREE.Vector2(1000, 1000);
15
- const tempQuaternion = new THREE.Quaternion();
16
-
17
-
18
- class OGC3DTile extends THREE.Object3D {
19
-
20
- /**
21
- *
22
- * @param {
23
- * json: optional,
24
- * url: optional,
25
- * rootPath: optional,
26
- * parentGeometricError: optional,
27
- * parentBoundingVolume: optional,
28
- * parentRefinement: optional,
29
- * geometricErrorMultiplier: Double,
30
- * loadOutsideView: Boolean,
31
- * tileLoader : TileLoader,
32
- * meshCallback: function,
33
- * pointsCallback: function,
34
- * cameraOnLoad: camera,
35
- * parentTile: OGC3DTile,
36
- * onLoadCallback: function,
37
- * occlusionCullingService: OcclusionCullingService,
38
- * static: Boolean,
39
- * centerModel: Boolean
40
- * renderer: Renderer
41
- * } properties
42
- */
43
- constructor(properties) {
44
- super();
45
- const self = this;
46
-
47
- this.uuid = uuidv4();
48
- if (!!properties.tileLoader) {
49
- this.tileLoader = properties.tileLoader;
50
- } else {
51
- const tileLoaderOptions = {};
52
- tileLoaderOptions.meshCallback = !properties.meshCallback ? mesh => {
53
- mesh.material.wireframe = false;
54
- mesh.material.side = THREE.DoubleSide;
55
- } : properties.meshCallback;
56
- tileLoaderOptions.pointsCallback = !properties.pointsCallback ? points => {
57
- points.material.size = 0.1;
58
- points.material.sizeAttenuation = true;
59
- } : properties.pointsCallback;
60
- this.tileLoader = new TileLoader(tileLoaderOptions);
61
- }
62
- // set properties general to the entire tileset
63
- this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
64
-
65
- this.renderer = properties.renderer;
66
- this.meshCallback = properties.meshCallback;
67
- this.loadOutsideView = properties.loadOutsideView;
68
- this.cameraOnLoad = properties.cameraOnLoad;
69
- this.parentTile = properties.parentTile;
70
- this.occlusionCullingService = properties.occlusionCullingService;
71
- this.static = properties.static;
72
- if (this.occlusionCullingService) {
73
- this.color = new THREE.Color();
74
- this.color.setHex(Math.random() * 0xffffff);
75
- this.colorID = clamp(self.color.r * 255, 0, 255) << 16 ^ clamp(self.color.g * 255, 0, 255) << 8 ^ clamp(self.color.b * 255, 0, 255) << 0;
76
- }
77
- if (this.static) {
78
- this.matrixAutoUpdate = false;
79
- }
80
-
81
- // declare properties specific to the tile for clarity
82
- this.childrenTiles = [];
83
- this.meshContent;
84
- this.tileContent;
85
- this.refinement; // defaults to "REPLACE"
86
- this.rootPath;
87
- this.geometricError;
88
- this.boundingVolume;
89
- this.json; // the json corresponding to this tile
90
- this.materialVisibility = false;
91
- this.inFrustum = true;
92
- this.level = properties.level ? properties.level : 0;
93
- this.hasMeshContent = false; // true when the provided json has a content field pointing to a B3DM file
94
- this.hasUnloadedJSONContent = false; // true when the provided json has a content field pointing to a JSON file that is not yet loaded
95
- this.centerModel = properties.centerModel;
96
- this.abortController = new AbortController();
97
- this.layers.disable(0);
98
- //this.octree = new Octree();
99
-
100
- if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
101
- self.setup(properties);
102
- if (properties.onLoadCallback) properties.onLoadCallback(self);
103
-
104
- } else if (properties.url) { // If only the url to the tileset.json is provided
105
- fetch(properties.url, { signal: self.abortController.signal }).then(result => {
106
- if (!result.ok) {
107
- throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
108
- }
109
- result.json().then(json => {
110
- self.setup({ rootPath: path.dirname(properties.url), json: json });
111
- if (properties.onLoadCallback) properties.onLoadCallback(self);
112
- if (!!self.centerModel) {
113
- const tempSphere = new THREE.Sphere();
114
- if (self.boundingVolume instanceof OBB) {
115
- // box
116
- tempSphere.copy(self.boundingVolume.sphere);
117
- } else if (self.boundingVolume instanceof THREE.Sphere) {
118
- //sphere
119
- tempSphere.copy(self.boundingVolume);
120
- }
121
-
122
- //tempSphere.applyMatrix4(self.matrixWorld);
123
- if (!!this.json.boundingVolume.region) {
124
- this.transformWGS84ToCartesian(
125
- (this.json.boundingVolume.region[0] + this.json.boundingVolume.region[2]) * 0.5,
126
- (this.json.boundingVolume.region[1] + this.json.boundingVolume.region[3]) * 0.5,
127
- (this.json.boundingVolume.region[4] + this.json.boundingVolume.region[5]) * 0.5,
128
- tempVec1);
129
-
130
- tempQuaternion.setFromUnitVectors(tempVec1.normalize(), upVector.normalize());
131
- self.applyQuaternion(tempQuaternion);
132
- }
133
-
134
- self.translateX(-tempSphere.center.x * self.scale.x);
135
- self.translateY(-tempSphere.center.y * self.scale.y);
136
- self.translateZ(-tempSphere.center.z * self.scale.z);
137
-
138
- }
139
- });
140
- });
141
- }
142
- }
143
-
144
- setup(properties) {
145
- if (!!properties.json.root) {
146
- this.json = properties.json.root;
147
- if (!this.json.refinement) this.json.refinement = properties.json.refinement;
148
- if (!this.json.geometricError) this.json.geometricError = properties.json.geometricError;
149
- if (!this.json.transform) this.json.transform = properties.json.transform;
150
- if (!this.json.boundingVolume) this.json.boundingVolume = properties.json.boundingVolume;
151
- } else {
152
- this.json = properties.json;
153
- }
154
- this.rootPath = !!properties.json.rootPath ? properties.json.rootPath : properties.rootPath;
155
-
156
- // decode refinement
157
- if (!!this.json.refinement) {
158
- this.refinement = this.json.refinement;
159
- } else {
160
- this.refinement = properties.parentRefinement;
161
- }
162
- // decode geometric error
163
- if (!!this.json.geometricError) {
164
- this.geometricError = this.json.geometricError;
165
- } else {
166
- this.geometricError = properties.parentGeometricError;
167
- }
168
-
169
- // decode transform
170
- if (!!this.json.transform && !this.centerModel) {
171
- let mat = new THREE.Matrix4();
172
- mat.elements = this.json.transform;
173
- this.applyMatrix4(mat);
174
- }
175
-
176
- // decode volume
177
- if (!!this.json.boundingVolume) {
178
- if (!!this.json.boundingVolume.box) {
179
- this.boundingVolume = new OBB(this.json.boundingVolume.box);
180
- } else if (!!this.json.boundingVolume.region) {
181
- const region = this.json.boundingVolume.region;
182
- this.transformWGS84ToCartesian(region[0], region[1], region[4], tempVec1);
183
- this.transformWGS84ToCartesian(region[2], region[3], region[5], tempVec2);
184
- tempVec1.lerp(tempVec2, 0.5);
185
- this.boundingVolume = new THREE.Sphere(new THREE.Vector3(tempVec1.x, tempVec1.y, tempVec1.z), tempVec1.distanceTo(tempVec2));
186
- } else if (!!this.json.boundingVolume.sphere) {
187
- const sphere = this.json.boundingVolume.sphere;
188
- this.boundingVolume = new THREE.Sphere(new THREE.Vector3(sphere[0], sphere[1], sphere[2]), sphere[3]);
189
- } else {
190
- this.boundingVolume = properties.parentBoundingVolume;
191
- }
192
- } else {
193
- this.boundingVolume = properties.parentBoundingVolume;
194
- }
195
-
196
-
197
-
198
- if (!!this.json.content) { //if there is a content, json or otherwise, schedule it to be loaded
199
- if (!!this.json.content.uri && this.json.content.uri.includes("json")) {
200
- this.hasUnloadedJSONContent = true;
201
- } else if (!!this.json.content.url && this.json.content.url.includes("json")) {
202
- this.hasUnloadedJSONContent = true;
203
- } else {
204
- this.hasMeshContent = true;
205
- }
206
- this.load();
207
- //scheduleLoadTile(this);
208
- }
209
- }
210
-
211
- isAbsolutePathOrURL(input) {
212
- // Check if it's an absolute URL with various protocols
213
- const urlRegex = /^(?:http|https|ftp|tcp|udp):\/\/\S+/;
214
- const absoluteURL = urlRegex.test(input);
215
-
216
- // Check if it's an absolute path
217
- const absolutePath = input.startsWith('/') && !input.startsWith('//');
218
-
219
- return absoluteURL || absolutePath;
220
- }
221
-
222
- load() {
223
- var self = this;
224
- if (self.deleted) return;
225
- if (!!self.json.content) {
226
- let url;
227
- if (!!self.json.content.uri) {
228
- if (path.isAbsolute(self.json.content.uri) || self.isAbsolutePathOrURL(self.json.content.uri)) {
229
- url = self.json.content.uri;
230
- } else {
231
- url = self.rootPath + path.sep + self.json.content.uri;
232
- }
233
- } else if (!!self.json.content.url) {
234
- if (path.isAbsolute(self.json.content.url) || self.isAbsolutePathOrURL(self.json.content.url)) {
235
- url = self.json.content.url;
236
- } else {
237
- url = self.rootPath + path.sep + self.json.content.url;
238
- }
239
- }
240
-
241
- if (!!url) {
242
- if (url.includes(".b3dm") || url.includes(".glb") || url.includes(".gltf")) {
243
- self.contentURL = url;
244
-
245
- self.tileLoader.get(self.abortController, this.uuid, url, mesh => {
246
- if (!!self.deleted) return;
247
- mesh.traverse((o) => {
248
- if (o.isMesh) {
249
-
250
- o.layers.disable(0);
251
- if (self.occlusionCullingService) {
252
- const position = o.geometry.attributes.position;
253
- const colors = [];
254
- for (let i = 0; i < position.count; i++) {
255
- colors.push(self.color.r, self.color.g, self.color.b);
256
- }
257
- o.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
258
- }
259
- if (self.static) {
260
- o.matrixAutoUpdate = false;
261
- }
262
- //o.material.visible = false;
263
- }
264
- });
265
- //let s = Date.now();
266
- //self.octree.fromGraphNode( mesh );
267
- //console.log(Date.now()-s);
268
- self.add(mesh);
269
- self.updateWorldMatrix(false, true);
270
- // mesh.layers.disable(0);
271
- self.meshContent = mesh;
272
- }, !self.cameraOnLoad ? () => 0 : () => {
273
- return self.calculateDistanceToCamera(self.cameraOnLoad);
274
- }, () => self.getSiblings(),
275
- self.level,
276
- !!self.json.boundingVolume.region,
277
- self.geometricError
278
- );
279
- } else if (url.includes(".json")) {
280
- self.tileLoader.get(self.abortController, this.uuid, url, json => {
281
- if (!!self.deleted) return;
282
- if (!self.json.children) self.json.children = [];
283
- json.rootPath = path.dirname(url);
284
- self.json.children.push(json);
285
- delete self.json.content;
286
- self.hasUnloadedJSONContent = false;
287
- });
288
-
289
- }
290
-
291
- }
292
- }
293
- }
294
-
295
- dispose() {
296
-
297
- const self = this;
298
- self.childrenTiles.forEach(tile => tile.dispose());
299
- self.deleted = true;
300
- this.traverse(function (element) {
301
- if (!!element.contentURL) {
302
- self.tileLoader.invalidate(element.contentURL, element.uuid);
303
- }
304
- if (!!element.abortController) { // abort tile request
305
- element.abortController.abort();
306
- }
307
-
308
- });
309
- this.parent = null;
310
- this.parentTile = null;
311
- this.dispatchEvent({ type: 'removed' });
312
- }
313
- disposeChildren() {
314
- var self = this;
315
-
316
- self.childrenTiles.forEach(tile => tile.dispose());
317
- self.childrenTiles = [];
318
- self.children = [];
319
- if (!!self.meshContent) self.children.push(self.meshContent);
320
- }
321
-
322
-
323
- update(camera) {
324
- const frustum = new THREE.Frustum();
325
- frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
326
- this._update(camera, frustum);
327
- }
328
- _update(camera, frustum) {
329
- const self = this;
330
-
331
- const visibilityBeforeUpdate = self.materialVisibility;
332
-
333
- if (!!self.boundingVolume && !!self.geometricError) {
334
- self.metric = self.calculateUpdateMetric(camera, frustum);
335
- }
336
- self.childrenTiles.forEach(child => child._update(camera, frustum));
337
-
338
- updateNodeVisibility(self.metric);
339
- updateTree(self.metric);
340
- trimTree(self.metric, visibilityBeforeUpdate);
341
-
342
-
343
- function updateTree(metric) {
344
- // If this tile does not have mesh content but it has children
345
- if (metric < 0 && self.hasMeshContent) return;
346
- if (self.occlusionCullingService && self.hasMeshContent && !self.occlusionCullingService.hasID(self.colorID)) {
347
- return;
348
- }
349
- if (!self.hasMeshContent || (metric < self.geometricErrorMultiplier * self.geometricError && !!self.meshContent)) {
350
- if (!!self.json && !!self.json.children && self.childrenTiles.length != self.json.children.length) {
351
- loadJsonChildren();
352
- return;
353
- }
354
- }
355
- }
356
-
357
- function updateNodeVisibility(metric) {
358
-
359
- //doesn't have a mesh content
360
- if (!self.hasMeshContent) return;
361
-
362
- // mesh content not yet loaded
363
- if (!self.meshContent) {
364
- return;
365
- }
366
-
367
- // outside frustum
368
- if (metric < 0) {
369
- self.inFrustum = false;
370
- self.changeContentVisibility(!!self.loadOutsideView);
371
- return;
372
- } else {
373
- self.inFrustum = true;
374
- }
375
-
376
- // has no children
377
- if (self.childrenTiles.length == 0) {
378
- self.changeContentVisibility(true);
379
- return;
380
- }
381
-
382
- // has children
383
- if (metric >= self.geometricErrorMultiplier * self.geometricError) { // Ideal LOD or before ideal lod
384
-
385
- self.changeContentVisibility(true);
386
- } else if (metric < self.geometricErrorMultiplier * self.geometricError) { // Ideal LOD is past this one
387
- // if children are visible and have been displayed, can be hidden
388
- let allChildrenReady = true;
389
- self.childrenTiles.every(child => {
390
-
391
- if (!child.isReady()) {
392
- allChildrenReady = false;
393
- return false;
394
- }
395
- return true;
396
- });
397
- if (allChildrenReady) {
398
- self.changeContentVisibility(false);
399
- }
400
- }
401
- }
402
-
403
- function trimTree(metric, visibilityBeforeUpdate) {
404
- if (!self.hasMeshContent) return;
405
- if (!self.inFrustum) { // outside frustum
406
- self.disposeChildren();
407
- updateNodeVisibility(metric);
408
- return;
409
- }
410
- if (self.occlusionCullingService &&
411
- !visibilityBeforeUpdate &&
412
- self.hasMeshContent &&
413
- self.meshContent &&
414
- self.meshDisplayed &&
415
- self.areAllChildrenLoadedAndHidden()) {
416
-
417
- self.disposeChildren();
418
- updateNodeVisibility(metric);
419
- return;
420
- }
421
- if (metric >= self.geometricErrorMultiplier * self.geometricError) {
422
- self.disposeChildren();
423
- updateNodeVisibility();
424
- return;
425
- }
426
-
427
- }
428
-
429
- function loadJsonChildren() {
430
- self.json.children.forEach(childJSON => {
431
- let childTile = new OGC3DTile({
432
- parentTile: self,
433
- parentGeometricError: self.geometricError,
434
- parentBoundingVolume: self.boundingVolume,
435
- parentRefinement: self.refinement,
436
- json: childJSON,
437
- rootPath: self.rootPath,
438
- geometricErrorMultiplier: self.geometricErrorMultiplier,
439
- loadOutsideView: self.loadOutsideView,
440
- level: self.level + 1,
441
- tileLoader: self.tileLoader,
442
- cameraOnLoad: camera,
443
- occlusionCullingService: self.occlusionCullingService,
444
- renderer: self.renderer,
445
- static: self.static,
446
- centerModel: false
447
- });
448
- self.childrenTiles.push(childTile);
449
- self.add(childTile);
450
- });
451
- }
452
-
453
- }
454
-
455
- areAllChildrenLoadedAndHidden() {
456
- let allLoadedAndHidden = true;
457
- const self = this;
458
- this.childrenTiles.every(child => {
459
- if (child.hasMeshContent) {
460
- if (child.childrenTiles.length > 0) {
461
- allLoadedAndHidden = false;
462
- return false;
463
- }
464
- if (!child.inFrustum) {
465
- return true;
466
- };
467
- if (!child.materialVisibility || child.meshDisplayed) {
468
- allLoadedAndHidden = false;
469
- return false;
470
- } else if (self.occlusionCullingService.hasID(child.colorID)) {
471
- allLoadedAndHidden = false;
472
- return false;
473
- }
474
- } else {
475
- if (!child.areAllChildrenLoadedAndHidden()) {
476
- allLoadedAndHidden = false;
477
- return false;
478
- }
479
- }
480
- return true;
481
- });
482
- return allLoadedAndHidden;
483
- }
484
-
485
- /**
486
- * Node is ready if it is outside frustum, if it was drawn at least once or if all it's children are ready
487
- * @returns true if ready
488
- */
489
- isReady() {
490
- // if outside frustum
491
- if (!this.inFrustum) return true;
492
-
493
- // if json is not done loading
494
- if (this.hasUnloadedJSONContent) return false;
495
-
496
- // if this tile has no mesh content or if it's marked as visible false, look at children
497
- if ((!this.hasMeshContent || !this.meshContent || !this.materialVisibility) && this.childrenTiles.length > 0) {
498
- var allChildrenReady = true;
499
- this.childrenTiles.every(child => {
500
- if (!child.isReady()) {
501
- allChildrenReady = false;
502
- return false;
503
- }
504
- return true;
505
- });
506
- return allChildrenReady;
507
- }
508
-
509
- // if this tile has no mesh content
510
- if (!this.hasMeshContent) {
511
- return true;
512
- }
513
- // if mesh content not yet loaded
514
- if (!this.meshContent) {
515
- return false;
516
- }
517
-
518
- // if this tile has been marked to hide it's content
519
- if (!this.materialVisibility) {
520
- return false;
521
- }
522
-
523
- // if all meshes have been displayed once
524
- if (this.meshDisplayed) {
525
- return true;
526
- }
527
-
528
- return false;
529
-
530
- }
531
-
532
-
533
-
534
-
535
- changeContentVisibility(visibility) {
536
- const self = this;
537
- if (self.hasMeshContent && self.meshContent) {
538
- if (visibility) {
539
-
540
- self.meshContent.traverse((o) => {
541
- if (o.isMesh) {
542
- o.layers.enable(0);
543
- }
544
- });
545
- } else {
546
- self.meshContent.traverse((o) => {
547
- if (o.isMesh) {
548
- o.layers.disable(0);
549
- }
550
- });
551
- }
552
- }
553
- if (self.materialVisibility == visibility) {
554
- return;
555
- }
556
- self.materialVisibility = visibility
557
-
558
- self.meshDisplayed = true;
559
- if (!!self.meshContent.traverse) {
560
- self.meshContent.traverse(function (element) {
561
- if (element.material) setMeshVisibility(element, visibility);
562
- });
563
- } else if (!!self.meshContent.scenes) {
564
- self.meshContent.scenes.forEach(scene => scene.traverse(function (element) {
565
- if (element.material) setMeshVisibility(element, visibility);
566
- }));
567
- }
568
-
569
- function setMeshVisibility(mesh, visibility) {
570
- mesh.material.visible = visibility;
571
- if (!!visibility) {
572
- mesh.onAfterRender = () => {
573
- delete mesh.onAfterRender;
574
- self.meshDisplayed = true;
575
- };
576
- }
577
-
578
- }
579
- }
580
- calculateUpdateMetric(camera, frustum) {
581
- ////// return -1 if not in frustum
582
- if (this.boundingVolume instanceof OBB) {
583
- // box
584
- tempSphere.copy(this.boundingVolume.sphere);
585
- tempSphere.applyMatrix4(this.matrixWorld);
586
- if (!frustum.intersectsSphere(tempSphere)) return -1;
587
- } else if (this.boundingVolume instanceof THREE.Sphere) {
588
- //sphere
589
- tempSphere.copy(this.boundingVolume);
590
- tempSphere.applyMatrix4(this.matrixWorld);
591
- if (!frustum.intersectsSphere(tempSphere)) return -1;
592
- } else {
593
- console.error("unsupported shape");
594
- return -1
595
-
596
- }
597
-
598
- /////// return metric based on geometric error and distance
599
-
600
- const distance = Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
601
- if (distance == 0) {
602
- return 0;
603
- }
604
- const scale = this.matrixWorld.getMaxScaleOnAxis();
605
- if (!!this.renderer) {
606
- this.renderer.getDrawingBufferSize(rendererSize);
607
- }
608
- let s = rendererSize.y;
609
- let fov = camera.fov;
610
- if (camera.aspect < 1) {
611
- fov *= camera.aspect;
612
- s = rendererSize.x;
613
- }
614
-
615
- let lambda = 2.0 * Math.tan(0.5 * fov * 0.01745329251994329576923690768489) * distance;
616
-
617
- return (window.devicePixelRatio * 16 * lambda) / (s * scale);
618
- }
619
-
620
- getSiblings() {
621
- const self = this;
622
- const tiles = [];
623
- if (!self.parentTile) return tiles;
624
- let p = self.parentTile;
625
- while (!p.hasMeshContent && !!p.parentTile) {
626
- p = p.parentTile;
627
- }
628
- p.childrenTiles.forEach(child => {
629
- if (!!child && child != self) {
630
- while (!child.hasMeshContent && !!child.childrenTiles[0]) {
631
- child = child.childrenTiles[0];
632
- }
633
- tiles.push(child);
634
- }
635
- });
636
- return tiles;
637
- }
638
- calculateDistanceToCamera(camera) {
639
- if (this.boundingVolume instanceof OBB) {
640
- // box
641
- tempSphere.copy(this.boundingVolume.sphere);
642
- tempSphere.applyMatrix4(this.matrixWorld);
643
- //if (!frustum.intersectsSphere(tempSphere)) return -1;
644
- } else if (this.boundingVolume instanceof THREE.Sphere) {
645
- //sphere
646
- tempSphere.copy(this.boundingVolume);
647
- tempSphere.applyMatrix4(this.matrixWorld);
648
- //if (!frustum.intersectsSphere(tempSphere)) return -1;
649
- }
650
- else {
651
- console.error("unsupported shape")
652
- }
653
- return Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
654
- }
655
- setGeometricErrorMultiplier(geometricErrorMultiplier) {
656
- this.geometricErrorMultiplier = geometricErrorMultiplier;
657
- this.childrenTiles.forEach(child => child.setGeometricErrorMultiplier(geometricErrorMultiplier));
658
- }
659
-
660
- transformWGS84ToCartesian(lon, lat, h, sfct) {
661
- const a = 6378137.0;
662
- const e = 0.006694384442042;
663
- const N = a / (Math.sqrt(1.0 - (e * Math.pow(Math.sin(lat), 2))));
664
- const cosLat = Math.cos(lat);
665
- const cosLon = Math.cos(lon);
666
- const sinLat = Math.sin(lat);
667
- const sinLon = Math.sin(lon);
668
- const nPh = (N + h);
669
- const x = nPh * cosLat * cosLon;
670
- const y = nPh * cosLat * sinLon;
671
- const z = (0.993305615557957 * N + h) * sinLat;
672
-
673
- sfct.set(x, y, z);
674
- }
675
- }
676
- export { OGC3DTile };