drone_view 3.0.19 → 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.
- package/.vscode/settings.json +3 -0
- package/dist/514.droneView.js +1 -1
- package/dist/800.droneView.js +1 -1
- package/dist/droneView.js +1 -1
- package/package.json +1 -1
- package/src/app.interface.ts +684 -0
- package/src/droneView.ts +1070 -0
- package/src/event.ts +359 -0
- package/src/layers/annotation.ts +175 -0
- package/src/layers/droneImages.ts +199 -0
- package/src/layers/editLine.ts +521 -0
- package/src/layers/editPoint.ts +345 -0
- package/src/layers/editPolygon.ts +511 -0
- package/src/layers/line.ts +210 -0
- package/src/layers/marker.ts +333 -0
- package/src/layers/marker3d.ts +326 -0
- package/src/layers/model.ts +253 -0
- package/src/layers/note.ts +333 -0
- package/src/layers/ortho.ts +76 -0
- package/src/layers/polygon.ts +218 -0
- package/src/measurement.ts +374 -0
- package/src/utility.ts +802 -0
package/src/droneView.ts
ADDED
@@ -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
|
+
}
|