drone_view 3.0.18 → 3.0.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.
@@ -0,0 +1,1070 @@
1
+ import {
2
+ Cesium3DTileset,
3
+ createWorldTerrain,
4
+ Entity,
5
+ ImageryLayer,
6
+ Ion,
7
+ Matrix4,
8
+ SceneMode,
9
+ ScreenSpaceEventHandler,
10
+ TerrainProvider,
11
+ Viewer,
12
+ WebMapServiceImageryProvider,
13
+ ScreenSpaceEventType,
14
+ Math as cMath,
15
+ EasingFunction,
16
+ MapboxStyleImageryProvider,
17
+ HeadingPitchRoll,
18
+ Color,
19
+ SplitDirection,
20
+ Cartesian3,
21
+ SceneTransforms
22
+ } from "cesium";
23
+ import type {
24
+ DrawingConfig,
25
+ MapOptions,
26
+ MapStyle,
27
+ Point,
28
+ MapCursor,
29
+ LayerData,
30
+ LayerType,
31
+ LayerOptions,
32
+ MarkerData,
33
+ MarkerOptions,
34
+ LineData,
35
+ LineOptions,
36
+ Options,
37
+ LabelConfigByLatLng,
38
+ PolygonData,
39
+ PolygonOptions,
40
+ EventListenerCallback,
41
+ EditPointOption,
42
+ AnnotationData,
43
+ MapType,
44
+ NoteData,
45
+ NoteOptions
46
+ } from "./app.interface";
47
+
48
+ import {
49
+ addLabel,
50
+ calculateArea,
51
+ cartesianToPoint,
52
+ distance2D,
53
+ getPolygonCenter,
54
+ PointToCartesian,
55
+ zoomLevelToCameraHeight,
56
+ } from "./utility";
57
+ import Measurement from "./measurement";
58
+ import MarkerLayer from "./layers/marker";
59
+ import Marker3DLayer from "./layers/marker3d";
60
+ import LineLayer from "./layers/line";
61
+ import PolygonLayer from "./layers/polygon";
62
+ import ModelLayer from "./layers/model";
63
+ import RasterLayer from "./layers/ortho";
64
+ import DroneImage from "./layers/droneImages";
65
+ import EditPolygon from "./layers/editPolygon";
66
+ import EditLine from "./layers/editLine";
67
+ import EditPoint from "./layers/editPoint";
68
+ import Annotation from "./layers/annotation";
69
+ import NoteLayer from "./layers/note";
70
+ import EventManager from "./event";
71
+ import * as Utility from "./utility";
72
+
73
+ export default class CesiumView {
74
+ options: MapOptions;
75
+
76
+ styleType: MapStyle;
77
+
78
+ mapStyle: string;
79
+
80
+ cursor: MapCursor = "grab";
81
+
82
+ eventManager: EventManager;
83
+
84
+ utility = Utility;
85
+
86
+ Cartesian3 = Cartesian3;
87
+
88
+ SceneTransforms = SceneTransforms;
89
+
90
+ DEFAULT_ZOOM_LEVEL = 16;
91
+
92
+ public MIN_ZOOM_LEVEL = 0;
93
+
94
+ public MAX_ZOOM_LEVEL = 22;
95
+
96
+ MAX_TILE_ZOOM_LEVEL = this.MAX_ZOOM_LEVEL;
97
+
98
+ OVERLAY_OPACITY = 100;
99
+
100
+ private streetMapProvider: MapboxStyleImageryProvider;
101
+
102
+ private streetMapLayer: ImageryLayer | undefined;
103
+
104
+ public viewer: Viewer;
105
+
106
+ private isZeroRotation3D: boolean = true;
107
+
108
+ isZeroRotation2D: boolean = true;
109
+
110
+ private listenHandler: any;
111
+
112
+ private cursorHandler: ScreenSpaceEventHandler;
113
+
114
+ layers: any = [];
115
+
116
+ listeners: any[];
117
+
118
+ layerListeners: any[];
119
+
120
+ private currentZoom: number;
121
+
122
+ private measurement: Measurement;
123
+
124
+ private handler: ScreenSpaceEventHandler;
125
+
126
+ private tileset: Cesium3DTileset;
127
+
128
+ // private isSideMode: boolean = false;
129
+
130
+ private terrainProvider: TerrainProvider;
131
+
132
+ public mapMaxZoomLevel = 22;
133
+
134
+ public mapMinZoomLevel = 0;
135
+
136
+ public isBoundingRactangleActive: boolean = false;
137
+
138
+ private mapLoadListner: Function;
139
+
140
+ constructor(options: MapOptions) {
141
+ this.options = options;
142
+ Ion.defaultAccessToken = options.token;
143
+ this.terrainProvider = createWorldTerrain();
144
+
145
+ if (this.options.mapboxToken)
146
+ this.streetMapProvider = new MapboxStyleImageryProvider({
147
+ styleId: "streets-v11",
148
+ accessToken: this.options.mapboxToken,
149
+ maximumLevel: 22,
150
+ minimumLevel: 0,
151
+ });
152
+ }
153
+
154
+ /** Initializes the viewer */
155
+ public init() {
156
+ const self = this;
157
+ // Initialize the Cesium Viewer in the HTML element with the cesiumContainer ID.
158
+ this.viewer = new Viewer(this.options.mapElementId, {
159
+ // terrainProvider: this.terrainProvider,
160
+ selectionIndicator: false,
161
+ infoBox: false,
162
+ animation: false,
163
+ baseLayerPicker: false,
164
+ shadows: false,
165
+ homeButton: false,
166
+ geocoder: false,
167
+ timeline: false,
168
+ skyBox: false,
169
+ navigationHelpButton: false,
170
+ maximumRenderTimeChange: Infinity,
171
+ requestRenderMode: true,
172
+ contextOptions: {
173
+ webgl: {
174
+ alpha: true,
175
+ },
176
+ },
177
+ });
178
+
179
+ this.viewer.scene.globe.show = false;
180
+ // The maximum screen space error used to drive level of detail refinement
181
+ this.viewer.scene.globe.maximumScreenSpaceError = 2;
182
+
183
+ // this.viewer.scene.globe.terrainExaggeration = 2.0;
184
+ this.viewer.scene.postProcessStages.fxaa.enabled = false;
185
+ // this.viewer.scene.globe.depthTestAgainstTerrain = true;
186
+
187
+ if (this.options.center) {
188
+ this.reCenter();
189
+ }
190
+
191
+ // Set options max zoom level
192
+ if (this.options.maxZoomLevel) {
193
+ this.viewer.scene.screenSpaceCameraController.minimumZoomDistance =
194
+ zoomLevelToCameraHeight(this.options.maxZoomLevel || 22.9);
195
+ }
196
+
197
+ // Set options min zoom level
198
+ if (this.options.minZoomLevel) {
199
+ this.viewer.scene.screenSpaceCameraController.maximumZoomDistance =
200
+ zoomLevelToCameraHeight(this.options.minZoomLevel || 0);
201
+ }
202
+
203
+ this.viewer.resolutionScale = window.devicePixelRatio;
204
+
205
+ this.cursorHandler = new ScreenSpaceEventHandler(this.viewer.canvas);
206
+
207
+ this.viewer.screenSpaceEventHandler.removeInputAction(
208
+ ScreenSpaceEventType.LEFT_DOUBLE_CLICK
209
+ );
210
+
211
+ this.listenHandler = this.resize.bind(this);
212
+
213
+ // To reset viewer scene on window resize
214
+ window.addEventListener("resize", this.listenHandler);
215
+
216
+ // To avoid old browser which doesn't support pick location
217
+ if (!this.viewer.scene.pickPositionSupported) {
218
+ // eslint-disable-next-line no-alert
219
+ window.alert("This browser does not support pickPosition.");
220
+ }
221
+
222
+ this.handler = new ScreenSpaceEventHandler(this.viewer.canvas);
223
+ this.measurement = new Measurement(this.viewer);
224
+
225
+ // To set location remains same when switching between 2D-3D mode
226
+ this.viewer.scene.morphComplete.addEventListener(() => {
227
+ if (this.tileset) {
228
+ this.viewer.zoomTo(this.tileset);
229
+ this.tileset.show = !this.is2DMode();
230
+ }
231
+ this.viewer.camera.lookAtTransform(Matrix4.IDENTITY);
232
+ });
233
+
234
+ this.eventManager = new EventManager(this);
235
+
236
+ // this.viewer.terrainProvider.readyPromise.then(() => {
237
+ // if (self.mapLoadListner) {
238
+ // self.mapLoadListner(true);
239
+ // }
240
+ // });
241
+ }
242
+
243
+ /**
244
+ * Toggle base map visibility
245
+ */
246
+ toggleBaseMap() {
247
+ this.viewer.imageryLayers.get(0).show =
248
+ !this.viewer.imageryLayers.get(0).show;
249
+ }
250
+
251
+ /**
252
+ * Set the map center
253
+ * @param lat number
254
+ * @param long number
255
+ * @param angle number heading angle in radian
256
+ */
257
+ public setCenter(
258
+ lat: string | number,
259
+ long: string | number,
260
+ height: string | number = 200,
261
+ angle: {
262
+ heading: number;
263
+ pitch: number;
264
+ roll: number;
265
+ }
266
+ ) {
267
+ if (typeof lat === "string") {
268
+ lat = parseFloat(lat);
269
+ }
270
+
271
+ if (typeof long === "string") {
272
+ long = parseFloat(long);
273
+ }
274
+
275
+ this.viewer.camera.setView({
276
+ destination: PointToCartesian({
277
+ long: long,
278
+ lat: lat,
279
+ height: height,
280
+ }),
281
+ orientation: new HeadingPitchRoll(angle.heading, angle.pitch, angle.roll),
282
+ });
283
+ }
284
+
285
+ getCenter() {
286
+ const pickPositionCartographic =
287
+ this.viewer.scene.globe.ellipsoid.cartesianToCartographic(
288
+ this.viewer.scene.camera.position
289
+ );
290
+ return {
291
+ latitude: pickPositionCartographic.latitude * (180 / Math.PI),
292
+ longitude: pickPositionCartographic.longitude * (180 / Math.PI),
293
+ height: pickPositionCartographic.height,
294
+ angle: {
295
+ heading: this.viewer.camera.heading,
296
+ pitch: this.viewer.camera.pitch,
297
+ roll: this.viewer.camera.roll,
298
+ },
299
+ };
300
+ }
301
+
302
+ /** Re-centers the map to the original center point given in the options */
303
+ public reCenter(): void {
304
+ if (this.options.center) {
305
+ this.setCenter(
306
+ this.options.center[1].toString(),
307
+ this.options.center[0].toString(),
308
+ this.options.center[2],
309
+ {
310
+ heading: this.viewer.camera.heading,
311
+ pitch: this.viewer.camera.pitch,
312
+ roll: this.viewer.camera.roll,
313
+ }
314
+ );
315
+ }
316
+ }
317
+
318
+ /** Triggers a resize */
319
+ public resize(): void {
320
+ this.viewer.scene.requestRender();
321
+ }
322
+
323
+ /** Terminates the map. Removes all listeners and removes the map from the DOM */
324
+ public terminate() {
325
+ window.removeEventListener("resize", this.listenHandler);
326
+ this.cursorHandler.removeInputAction(ScreenSpaceEventType.LEFT_DOWN);
327
+ this.cursorHandler.removeInputAction(ScreenSpaceEventType.LEFT_UP);
328
+ this.viewer.destroy();
329
+ }
330
+
331
+ /** To switch 2D Mode */
332
+ public switch2DMode(): void {
333
+ this.viewer.scene.mode = SceneMode.SCENE2D;
334
+ }
335
+
336
+ /** To switch 3D Mode */
337
+ public switch3DMode(): void {
338
+ this.viewer.scene.mode = SceneMode.SCENE3D;
339
+ }
340
+
341
+ /** to check if 2D mode is active */
342
+ public is2DMode(): boolean {
343
+ return this.viewer.scene.mode === SceneMode.SCENE2D;
344
+ }
345
+
346
+ /**
347
+ * Fly and zoom to a location
348
+ * @param { Point } location Point
349
+ * @param { number } zoom number
350
+ */
351
+ public flyTo(location: Point, zoom: number) {
352
+ this.viewer.camera.flyTo({
353
+ destination: PointToCartesian({
354
+ lat: location.lat,
355
+ long: location.long,
356
+ height: zoomLevelToCameraHeight(zoom),
357
+ }),
358
+ easingFunction: EasingFunction.EXPONENTIAL_IN_OUT,
359
+ });
360
+ }
361
+
362
+ /**
363
+ * Get an array of [latitude, longitude] for each corner of the visible map - top left clockwise
364
+ * [[south, west], [north, east]]
365
+ * @returns Array<number> current map bounds
366
+ */
367
+ public getVisibleMapBounds(): Array<Array<number>> {
368
+ const bb = this.viewer.camera.computeViewRectangle()!;
369
+ return [
370
+ [cMath.toDegrees(bb.west), cMath.toDegrees(bb.south)],
371
+ [cMath.toDegrees(bb.east), cMath.toDegrees(bb.north)],
372
+ ];
373
+ }
374
+
375
+ /** Flattens an array of [latitude, longitude] into a string */
376
+ // eslint-disable-next-line class-methods-use-this
377
+ public boundsToString(bounds: Array<Array<number>>) {
378
+ return bounds.toString();
379
+ }
380
+
381
+ /**
382
+ * Get an flattened string of latitude, longitude values for each corner of the visible map
383
+ * top left clockwise. like - south, west, north, east
384
+ * @returns string
385
+ */
386
+ public getVisibleMapBoundsString() {
387
+ return this.boundsToString(this.getVisibleMapBounds());
388
+ }
389
+
390
+ /**
391
+ * load 2D Orthographic Imagery data
392
+ * @param url string url endpoint of WMS Server
393
+ * @returns ImageryLayer
394
+ */
395
+ public addImagery(url: string): ImageryLayer {
396
+ const imageryLayer = this.viewer.scene.imageryLayers.addImageryProvider(
397
+ new WebMapServiceImageryProvider({
398
+ url,
399
+ layers: "6",
400
+ parameters: {
401
+ transparent: "true",
402
+ format: "image/png",
403
+ },
404
+ tileWidth: 216,
405
+ tileHeight: 216,
406
+ })
407
+ );
408
+ return imageryLayer;
409
+ }
410
+
411
+ /**
412
+ * To remove 2D ImageryLayer
413
+ * @param tileset ImageryLayer
414
+ */
415
+ public removeImageryLayer(imageLayer: ImageryLayer) {
416
+ this.viewer.scene.imageryLayers.remove(imageLayer);
417
+ this.viewer.scene.requestRender();
418
+ }
419
+
420
+ /**
421
+ * Get area of shape/Polygon
422
+ * @param pointData Array<Point>
423
+ * @returns number Area in square meter
424
+ */
425
+ // eslint-disable-next-line class-methods-use-this
426
+ public calculateArea(pointData: Array<Point>): number {
427
+ return calculateArea(pointData);
428
+ }
429
+
430
+ /**
431
+ * Get distance between two points
432
+ * @param lat1 Latitude point one
433
+ * @param long1 Longitude point one
434
+ * @param lat2 Latitude point two
435
+ * @param long2 Longitude point two
436
+ * @returns number distance in meter
437
+ */
438
+ // eslint-disable-next-line class-methods-use-this
439
+ public calculateDistance(
440
+ lat1: string,
441
+ long1: string,
442
+ lat2: string,
443
+ long2: string
444
+ ): number {
445
+ return distance2D(
446
+ PointToCartesian({
447
+ lat: parseFloat(lat1),
448
+ long: parseFloat(long1),
449
+ }),
450
+ PointToCartesian({ lat: parseFloat(lat2), long: parseFloat(long2) })
451
+ );
452
+ }
453
+
454
+ /** Enables maps ability to be dragged/panned */
455
+ public enablePan() {
456
+ this.viewer.scene.screenSpaceCameraController.enableLook = true;
457
+ this.viewer.scene.screenSpaceCameraController.enableTranslate = true;
458
+ this.viewer.scene.screenSpaceCameraController.enableRotate = true;
459
+ }
460
+
461
+ /** Disable maps ability to be dragged/panned */
462
+ public disablePan() {
463
+ this.viewer.scene.screenSpaceCameraController.enableLook = false;
464
+ this.viewer.scene.screenSpaceCameraController.enableTranslate = false;
465
+ this.viewer.scene.screenSpaceCameraController.enableRotate = false;
466
+ }
467
+
468
+ /**
469
+ * Get center of polygon
470
+ * @param id string Id of polygon
471
+ * @returns Point
472
+ */
473
+ public getCenterPolygon(id: string): Point {
474
+ const entity = this.viewer.entities.getById(id)!;
475
+ if (entity) {
476
+ if (entity.polygon) {
477
+ const { positions } = entity.polygon.hierarchy!.valueOf() as any;
478
+ return cartesianToPoint(getPolygonCenter(positions));
479
+ }
480
+ throw new Error("This method only works for polygon geometry");
481
+ } else {
482
+ throw new Error("Entity Doesn't Exist");
483
+ }
484
+ }
485
+
486
+ /**
487
+ * To enable drawing tool
488
+ * @param config DrawingConfig User can use this for customize color, line width etc
489
+ */
490
+ public enableDrawing(config: DrawingConfig) {
491
+ this.measurement.enable(config);
492
+ }
493
+
494
+ /**
495
+ * For updating a geometry with user data
496
+ * @param id string ID of drawn shape
497
+ * @param data - json object
498
+ */
499
+ public updateData(id: string, data: { [key: string]: string | number }) {
500
+ const entity: any = this.viewer.entities.getById(id);
501
+ entity.data = data;
502
+ }
503
+
504
+ /**
505
+ * This function is for responding when ever any drawing finish
506
+ * @param cb Callback function
507
+ */
508
+ public onDraw(cb: any) {
509
+ this.measurement.drawingFinished = cb;
510
+ }
511
+
512
+ /**
513
+ * To disable drawing tool
514
+ */
515
+ public disableDrawing() {
516
+ this.measurement.disable();
517
+ }
518
+
519
+ /**
520
+ * To remove the points on model
521
+ */
522
+ public removePoints() {
523
+ this.viewer.entities.removeAll();
524
+ this.viewer.scene.requestRender();
525
+ }
526
+
527
+ /**
528
+ * Adds a listener for events of a specified type limited to features in a specified style layer id.
529
+ *
530
+ * @param {'mousedown' | 'mouseup' | 'mouseover' | 'mouseout' | 'mousemove' | 'mouseleave' | 'click'} event The event type to listen for. Specified event must be compatible with the type of mapbox layer being used
531
+ * @param {string} layerId The id of a style layer. If you provide a `layerId`,
532
+ * the listener will be triggered only if its location is within a visible feature in these layers,
533
+ * and the event will have a `features` property containing an array of the matching features.
534
+ * @param {EventListenerCallback} fn The function to be called when the event is fired.
535
+ * @example
536
+ * // Set an event listener that will fire
537
+ * // when a feature on the countries layer of the map is clicked.
538
+ * map.on('click', 'countries', (e) => {
539
+ * console.log('the countries layer was clicked!', e);
540
+ * });
541
+ */
542
+ addLayerListener(event: string, layerId: string, fn: EventListenerCallback) {
543
+ this.eventManager.addListener(event, layerId, fn);
544
+ }
545
+
546
+ /**
547
+ * Removes all listeners for a specified layer ID
548
+ * @param {string} layerId The ID of the layer to remove listeners for
549
+ */
550
+ removeLayerListeners(layerId: string) {
551
+ this.eventManager.removeLayerListners(layerId);
552
+ }
553
+
554
+ /**
555
+ * Adds a specified layer to the map. Any existing layers with the same type and name will be removed.
556
+ * @param {LayerType} type
557
+ * @param {string} name
558
+ * @param {LayerData[]} data
559
+ * @param {LayerOptions} options
560
+ * *@param { MapType} mapType
561
+ */
562
+ public addLayer(
563
+ type: LayerType,
564
+ name: string,
565
+ data: LayerData[],
566
+ options: LayerOptions,
567
+ mapType: MapType = "all"
568
+ ) {
569
+ this.removeLayer(type, name);
570
+ let layer: any = null;
571
+ switch (type) {
572
+ case "marker":
573
+ layer = new MarkerLayer(
574
+ this,
575
+ name,
576
+ data as MarkerData[],
577
+ options as MarkerOptions
578
+ );
579
+ break;
580
+ case "annotation":
581
+ layer = new Annotation(
582
+ this,
583
+ name,
584
+ data as AnnotationData[],
585
+ options as Options
586
+ );
587
+ break;
588
+ case "marker3d":
589
+ layer = new Marker3DLayer(
590
+ this,
591
+ name,
592
+ data as MarkerData[],
593
+ options as MarkerOptions
594
+ );
595
+ break;
596
+ case "line":
597
+ layer = new LineLayer(
598
+ this,
599
+ name,
600
+ data as LineData[],
601
+ options as LineOptions
602
+ );
603
+ break;
604
+ case "polygon":
605
+ layer = new PolygonLayer(
606
+ this,
607
+ name,
608
+ data as PolygonData[],
609
+ options as PolygonOptions
610
+ );
611
+ break;
612
+ case "model":
613
+ layer = new ModelLayer(
614
+ this,
615
+ name,
616
+ data as any,
617
+ options as Options,
618
+ mapType
619
+ );
620
+ break;
621
+ case "ortho":
622
+ layer = new RasterLayer(this, name, data as any, options as Options);
623
+ case "droneImage":
624
+ layer = new DroneImage(this, name, data as any, options as Options);
625
+ break;
626
+ case "editPolygon":
627
+ layer = new EditPolygon(this, name, data as any, options as Options);
628
+ break;
629
+ case "editLine":
630
+ layer = new EditLine(this, name, data as any, options as Options);
631
+ break;
632
+ case "editPoint":
633
+ layer = new EditPoint(
634
+ this,
635
+ name,
636
+ data as any,
637
+ options as EditPointOption
638
+ );
639
+ break;
640
+ case "note":
641
+ layer = new NoteLayer(
642
+ this,
643
+ name,
644
+ data as NoteData[],
645
+ options as NoteOptions
646
+ );
647
+ break;
648
+ default:
649
+ // eslint-disable-next-line no-console
650
+ console.error(`MapBox: AddLayer: ${type} Not Found`);
651
+ return null;
652
+ }
653
+
654
+ console.log(mapType === "compare", this.isCompareMode, layer);
655
+
656
+ if (mapType === "compare" && this.isCompareMode) {
657
+ Object.keys(this.tilesetRight).forEach((typeR: string) => {
658
+ const layers = this.tilesetRight[typeR];
659
+ Object.keys(layers).forEach((nameR: string) => {
660
+ const layerRight = layers[nameR];
661
+ if (layerRight !== null) {
662
+ layerRight.remove();
663
+ }
664
+ });
665
+ });
666
+
667
+ this.tilesetRight = [];
668
+
669
+ if (typeof this.tilesetRight[type] === "undefined") {
670
+ this.tilesetRight[type] = [];
671
+ }
672
+
673
+ if (type === "model") {
674
+ this.tilesetRight[type][name] = layer;
675
+
676
+ if ((layer as ModelLayer).tileset) {
677
+ (layer as ModelLayer).tileset.splitDirection = SplitDirection.RIGHT;
678
+ }
679
+ }
680
+
681
+ return null;
682
+ }
683
+
684
+ if (typeof this.layers[type] === "undefined") {
685
+ this.layers[type] = [];
686
+ }
687
+
688
+ this.layers[type][name] = layer;
689
+
690
+ if (mapType === "default" && this.isCompareMode) {
691
+ if (type === "model") {
692
+ // (layer as ModelLayer).tileset.splitDirection = SplitDirection.LEFT;
693
+ this.tilesetLeft.push(layer as ModelLayer);
694
+ return null;
695
+ }
696
+ }
697
+
698
+ return layer;
699
+ }
700
+
701
+ /**
702
+ * Determine whether a specified layer exists on the map
703
+ * @param {LayerType} type
704
+ * @param {string} name
705
+ * @returns {boolean}
706
+ */
707
+ public hasLayer(type: LayerType, name: string): boolean {
708
+ let isLayerExist = false;
709
+ if (
710
+ typeof this.layers !== "undefined" &&
711
+ typeof this.layers[type] !== "undefined" &&
712
+ typeof this.layers[type][name] !== "undefined"
713
+ ) {
714
+ isLayerExist = true;
715
+ }
716
+ return isLayerExist;
717
+ }
718
+
719
+ /**
720
+ * Returns all layers of a specified type
721
+ * @param {LayerType} type
722
+ * @returns {Array<GenericLayer<LayerData[], LayerOptions>> }
723
+ */
724
+ public getLayers(type: LayerType) {
725
+ if (typeof this.layers[type] === "undefined") {
726
+ return [];
727
+ }
728
+ return this.layers[type];
729
+ }
730
+
731
+ /**
732
+ * Returns the data for the specified layer
733
+ * @param {LayerType} type
734
+ * @param {string} name
735
+ * @returns {LayerData[]} Data of layer
736
+ */
737
+ getLayerData(type: LayerType, name: string): LayerData[] {
738
+ if (!this.hasLayer(type, name)) {
739
+ return [];
740
+ }
741
+ return this.layers[type][name].getData();
742
+ }
743
+
744
+ /**
745
+ * Removes a specified layer from the map
746
+ * @param {LayerType} type
747
+ * @param {string} name
748
+ */
749
+ public removeLayer(type: LayerType, name: string) {
750
+ if (!this.hasLayer(type, name)) {
751
+ return;
752
+ }
753
+ const removedLayer = this.layers[type][name]
754
+ if (removedLayer.data) {
755
+ if (Array.isArray(removedLayer.data)) {
756
+ for (const item of removedLayer.data) {
757
+ const drawingLabel = item?.properties?.label;
758
+ if (drawingLabel) {
759
+ const labelLayer = this.viewer.entities.getById(drawingLabel);
760
+ if (labelLayer) {
761
+ this.viewer.entities.remove(labelLayer);
762
+ }
763
+ }
764
+ }
765
+ } else {
766
+ // Handle the case of a single object
767
+ const drawingLabel = removedLayer.data?.properties?.label;
768
+ if (drawingLabel) {
769
+ const labelLayer = this.viewer.entities.getById(drawingLabel);
770
+ if (labelLayer) {
771
+ this.viewer.entities.remove(labelLayer);
772
+ }
773
+ }
774
+ }
775
+ }
776
+ removedLayer.remove();
777
+ delete this.layers[type][name];
778
+ //console.log(this.layers[type][name]);
779
+ }
780
+
781
+ /**
782
+ * Show a specified layer
783
+ * @param {LayerType} type
784
+ * @param {string} name
785
+ */
786
+ showLayer(type: LayerType, name: string) {
787
+ if (!this.hasLayer(type, name)) {
788
+ return;
789
+ }
790
+ this.layers[type][name].show();
791
+ this.viewer.scene.requestRender();
792
+ }
793
+
794
+ /**
795
+ * Hide a specified layer
796
+ * @param {LayerType} type
797
+ * @param {string} name
798
+ */
799
+ hideLayer(type: LayerType, name: string) {
800
+ if (!this.hasLayer(type, name)) {
801
+ return;
802
+ }
803
+ this.layers[type][name].hide();
804
+ this.viewer.scene.requestRender();
805
+ }
806
+
807
+ /**
808
+ * Set the opacity (0 - 100) for a specified layer
809
+ * @param {LayerType} type
810
+ * @param {string} name
811
+ * @param {number} opacity
812
+ */
813
+ setLayerOpacity(type: LayerType, name: string, opacity: number) {
814
+ if (!this.hasLayer(type, name)) {
815
+ return;
816
+ }
817
+ this.layers[type][name].setOpacity(opacity);
818
+ }
819
+
820
+ /**
821
+ *
822
+ * @param {LayerType} type
823
+ * @param {string} name
824
+ * @param rotation
825
+ * @returns void
826
+ */
827
+ setLayerRotation(
828
+ type: LayerType,
829
+ name: string,
830
+ rotation: { rotationX: number; rotationY: number; rotationZ: number }
831
+ ) {
832
+ if (!this.hasLayer(type, name) || type !== "model") {
833
+ return;
834
+ }
835
+ this.layers[type][name].setRotation(
836
+ rotation.rotationX,
837
+ rotation.rotationY,
838
+ rotation.rotationZ
839
+ );
840
+ }
841
+
842
+ /**
843
+ *
844
+ * @param {LayerType} type
845
+ * @param {string} name
846
+ * @param rotation
847
+ * @returns void
848
+ */
849
+ setLayerPosition(type: LayerType, name: string, position: Point) {
850
+ if (!this.hasLayer(type, name) || type !== "model") {
851
+ return;
852
+ }
853
+ this.layers[type][name].setPosition(position);
854
+ }
855
+
856
+ /**
857
+ * Removes all layers from the map
858
+ */
859
+ removeAllLayers() {
860
+ Object.keys(this.layers).forEach((type) => {
861
+ const layers = this.layers[type];
862
+ Object.keys(layers).forEach((name) => {
863
+ const layer = layers[name];
864
+ if (layer !== null) {
865
+ layer.remove();
866
+ }
867
+ });
868
+ });
869
+ }
870
+
871
+ /**
872
+ * To add / remove globe from the viewer
873
+ */
874
+ toggleGlobe() {
875
+ this.viewer.scene.globe.show = !this.viewer.scene.globe.show;
876
+ this.viewer.scene.requestRender();
877
+ }
878
+
879
+ /**
880
+ * This method allows user to remove a geometry/entity
881
+ * @param id string id of entity
882
+ */
883
+ public removeEntityById(id: string) {
884
+ if (this.viewer.entities.getById(id)) {
885
+ this.viewer.entities.remove(this.viewer.entities.getById(id)!);
886
+ }
887
+ this.viewer.scene.requestRender();
888
+ }
889
+
890
+ /**
891
+ * This method allows user to add a text/label (Annotation) on map
892
+ * @param config LabelConfigByLatLng to configure color, background, fontsize etc of label
893
+ */
894
+ public addLabel(config: LabelConfigByLatLng) {
895
+ const entity = addLabel(this.viewer, {
896
+ ...config,
897
+ position: PointToCartesian(config.position),
898
+ });
899
+ this.viewer.scene.requestRender();
900
+ return entity.id;
901
+ }
902
+
903
+ /**
904
+ * This method allows user to control visibility of a geometry/entity using their Id
905
+ * @param id string id of entity
906
+ */
907
+ public toggleEntityVisibility(id: string) {
908
+ if (this.viewer.entities.getById(id)) {
909
+ this.viewer.entities.getById(id)!.show =
910
+ !this.viewer.entities.getById(id)!.show;
911
+ }
912
+ }
913
+
914
+ /**
915
+ * This method allows user zoom to a geometry/entity using their Id
916
+ * @param id - string id of entity
917
+ */
918
+ public zoomToEntityById(id: string) {
919
+ if (this.viewer.entities.getById(id)) {
920
+ this.viewer.zoomTo(this.viewer.entities.getById(id)!);
921
+ }
922
+ }
923
+
924
+ /**
925
+ * To get a geometry/entity using their Id
926
+ * @param id string id of entity
927
+ */
928
+ public getEntityById(id: string) {
929
+ if (this.viewer.entities.getById(id)) {
930
+ return this.viewer.entities.getById(id);
931
+ }
932
+
933
+ throw new Error("Entity Doesn't exist");
934
+ }
935
+
936
+ /**
937
+ * This method allows user to remove an entity using object (geometry)
938
+ * @param entity Entity
939
+ */
940
+ public removeEntity(entity: Entity) {
941
+ if (this.viewer.entities.contains(entity))
942
+ this.viewer.entities.remove(entity);
943
+ this.viewer.scene.requestRender();
944
+ }
945
+
946
+ isCompareMode: boolean = false;
947
+ private sliderHTML: any;
948
+ private tilesetRight: any = [];
949
+ private tilesetLeft: Array<ModelLayer> = [];
950
+ private handlerSlider: ScreenSpaceEventHandler;
951
+
952
+ /**
953
+ * Start the compare mode. Adds a secondary map to the right side of the original
954
+ * map with the same settings as the original map. Panning is synced between the two maps.
955
+ * Primary use is to compare two different time periods with each map having a different
956
+ * ortho layer representing each time period
957
+ */
958
+ public startCompare(): Promise<any> | null {
959
+ return new Promise<void>((resolve) => {
960
+ if (!this.viewer) {
961
+ resolve();
962
+ }
963
+ if (this.isCompareMode) this.stopCompare();
964
+ this.removeAllLayers();
965
+ this.isCompareMode = true;
966
+ this.sliderHTML = document.createElement("div");
967
+
968
+ this.sliderHTML.style.position = "absolute";
969
+ this.sliderHTML.style.left = "50%";
970
+ this.sliderHTML.style.top = "0%";
971
+ this.sliderHTML.style.backgroundColor = "#d3d3d3";
972
+ this.sliderHTML.style.width = "5px";
973
+ this.sliderHTML.style.height = "100%";
974
+ this.sliderHTML.style.zIndex = "111110";
975
+ this.sliderHTML.style.cursor = "ew-resize";
976
+
977
+ const mapContainer = document.getElementById(this.options.mapElementId)!;
978
+ mapContainer.append(this.sliderHTML);
979
+
980
+ if (this.viewer)
981
+ // Sync the position of the slider with the split position
982
+ this.viewer.scene.splitPosition =
983
+ this.sliderHTML.offsetLeft /
984
+ this.sliderHTML.parentElement!.offsetWidth;
985
+ this.handlerSlider = new ScreenSpaceEventHandler(this.sliderHTML);
986
+ let moveActive = false;
987
+
988
+ const move = (movement: any) => {
989
+ if (!moveActive) {
990
+ return;
991
+ }
992
+
993
+ const relativeOffset = movement.endPosition.x;
994
+ const splitPosition =
995
+ (this.sliderHTML.offsetLeft + relativeOffset) /
996
+ this.sliderHTML.parentElement.offsetWidth;
997
+ this.sliderHTML.style.left = `${100.0 * splitPosition}%`;
998
+ if (this.viewer) {
999
+ this.viewer.scene.splitPosition = splitPosition;
1000
+ this.viewer.scene.requestRender();
1001
+ }
1002
+ };
1003
+
1004
+ this.handlerSlider.setInputAction(() => {
1005
+ moveActive = true;
1006
+ }, ScreenSpaceEventType.LEFT_DOWN);
1007
+ this.handlerSlider.setInputAction(() => {
1008
+ moveActive = true;
1009
+ }, ScreenSpaceEventType.PINCH_START);
1010
+
1011
+ this.handlerSlider.setInputAction(move, ScreenSpaceEventType.MOUSE_MOVE);
1012
+ this.handlerSlider.setInputAction(move, ScreenSpaceEventType.PINCH_MOVE);
1013
+
1014
+ this.handlerSlider.setInputAction(() => {
1015
+ moveActive = false;
1016
+ }, ScreenSpaceEventType.LEFT_UP);
1017
+ this.handlerSlider.setInputAction(() => {
1018
+ moveActive = false;
1019
+ }, ScreenSpaceEventType.PINCH_END);
1020
+ resolve();
1021
+ });
1022
+ }
1023
+
1024
+ /**
1025
+ * Stops the Compare mode. This will remove the Compare map and restore the original map
1026
+ */
1027
+ public stopCompare() {
1028
+ if (!this.viewer) {
1029
+ return;
1030
+ }
1031
+
1032
+ if (this.isCompareMode) {
1033
+
1034
+ this.isCompareMode = false;
1035
+
1036
+ document
1037
+ .getElementById(this.options.mapElementId)
1038
+ ?.removeChild(this.sliderHTML);
1039
+
1040
+ this.handlerSlider.removeInputAction(ScreenSpaceEventType.LEFT_DOWN);
1041
+ this.handlerSlider.removeInputAction(ScreenSpaceEventType.PINCH_START);
1042
+ this.handlerSlider.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
1043
+ this.handlerSlider.removeInputAction(ScreenSpaceEventType.PINCH_MOVE);
1044
+ this.handlerSlider.removeInputAction(ScreenSpaceEventType.LEFT_UP);
1045
+ this.handlerSlider.removeInputAction(ScreenSpaceEventType.PINCH_START);
1046
+
1047
+ Object.keys(this.tilesetRight).forEach((type) => {
1048
+ const layers = this.tilesetRight[type];
1049
+ Object.keys(layers).forEach((name) => {
1050
+ const layer = layers[name];
1051
+ if (layer !== null) {
1052
+ layer.remove();
1053
+ }
1054
+ });
1055
+ });
1056
+
1057
+ this.tilesetLeft.forEach((layer: ModelLayer) => {
1058
+ if (layer instanceof ModelLayer) {
1059
+ console.log(layer);
1060
+
1061
+ layer.tileset.splitDirection = SplitDirection.NONE;
1062
+ }
1063
+ });
1064
+
1065
+ this.viewer.scene.requestRender();
1066
+ this.tilesetLeft = [];
1067
+ this.tilesetRight = [];
1068
+ }
1069
+ }
1070
+ }