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.
@@ -0,0 +1,199 @@
1
+ import axios from "axios";
2
+ import CesiumView from "../droneView";
3
+ import { Options } from "../app.interface";
4
+ import {
5
+ Cartesian2,
6
+ Cartesian3,
7
+ Color,
8
+ CustomDataSource,
9
+ Entity,
10
+ HeadingPitchRoll,
11
+ Transforms,
12
+ Math as cMath,
13
+ } from "cesium";
14
+ import { getPyramid } from "../utility";
15
+
16
+ interface droneImageData {
17
+ jsonUrl: string;
18
+ }
19
+
20
+ interface tileData {
21
+ alt: number | string;
22
+ angle: number | string;
23
+ lat: number | string;
24
+ lng: number | string;
25
+ url: string;
26
+ z_angle: number | string;
27
+ flightpitchdegree: number;
28
+ flightrolldegree: number;
29
+ flightyawdegree: number;
30
+ directionVector?: [number, number, number]
31
+ }
32
+
33
+ export default class DroneImage {
34
+ // for now default tile size of Image
35
+ // TODO will do the footprint analysis later to0 calculate image tile size dynamically
36
+ tileSize: number = 10;
37
+
38
+ droneImageLayerId: string;
39
+
40
+ layer: CustomDataSource;
41
+
42
+ activeDroneCameraId: string | null = null;
43
+
44
+ currentViewEntity: Entity | null = null;
45
+
46
+ constructor(
47
+ public mapObject: CesiumView,
48
+ public uniqueName: string,
49
+ public droneImageData: droneImageData,
50
+ public options: Options
51
+ ) {
52
+ this.droneImageLayerId = `DroneImage-layer-${this.uniqueName}`;
53
+
54
+ axios.get(droneImageData.jsonUrl).then((result) => {
55
+ this.buildLayer();
56
+ this.addCameraSymbolsOnMap(result.data);
57
+ this.mapObject.viewer.zoomTo(this.layer.entities);
58
+ this.addListeners();
59
+ });
60
+ }
61
+
62
+ /** To initialize layer */
63
+ public buildLayer(): void {
64
+ if (
65
+ this.mapObject.viewer.dataSources.getByName(
66
+ "DroneImage" + this.uniqueName
67
+ )
68
+ ) {
69
+ this.mapObject.viewer.dataSources.remove(
70
+ this.mapObject.viewer.dataSources.getByName(
71
+ "DroneImage" + this.uniqueName
72
+ )[0],
73
+ true
74
+ );
75
+ }
76
+ this.layer = new CustomDataSource("DroneImage" + this.uniqueName);
77
+
78
+ this.mapObject.viewer.dataSources.add(this.layer);
79
+ }
80
+
81
+ addCameraSymbolsOnMap(tiles: tileData[]) {
82
+ tiles.forEach((tile) => {
83
+ if (typeof tile.lng === "string") {
84
+ tile.lng = parseFloat(tile.lng);
85
+ }
86
+
87
+ if (typeof tile.lat === "string") {
88
+ tile.lat = parseFloat(tile.lat);
89
+ }
90
+
91
+ if (typeof tile.alt === "string") {
92
+ tile.alt = parseFloat(tile.alt);
93
+ }
94
+
95
+ if (typeof tile.angle === "string") {
96
+ tile.angle = parseFloat(tile.angle);
97
+ }
98
+
99
+ if (typeof tile.z_angle === "string") {
100
+ tile.z_angle = parseFloat(tile.z_angle);
101
+ }
102
+
103
+ const entity = getPyramid(
104
+ Cartesian3.fromDegrees(tile.lng, tile.lat, tile.alt),
105
+ this.droneImageLayerId,
106
+ tile.url,
107
+ tile.angle,
108
+ tile.z_angle,
109
+ tile.flightpitchdegree,
110
+ tile.flightrolldegree,
111
+ tile.flightyawdegree,
112
+ tile.directionVector
113
+ );
114
+
115
+ this.layer.entities.add(entity);
116
+
117
+ this.mapObject.viewer.scene.requestRender();
118
+ });
119
+ }
120
+
121
+ public addListeners() {
122
+ this.mapObject.addLayerListener(
123
+ "click",
124
+ this.droneImageLayerId,
125
+ (event: any) => {
126
+ if (event.features && event.features[0]) {
127
+ const feature = event.features[0];
128
+ this.mapObject.viewer.camera.setView({
129
+ destination: Cartesian3.fromDegrees(
130
+ feature.long,
131
+ feature.lat,
132
+ feature.height
133
+ ),
134
+ });
135
+
136
+ if (this.options.onClick) {
137
+ this.options.onClick(feature.properties);
138
+ }
139
+ }
140
+ }
141
+ );
142
+
143
+ this.mapObject.addLayerListener(
144
+ "mouseenter",
145
+ this.droneImageLayerId,
146
+ (event: any) => {
147
+ this.mapObject.viewer.canvas.style.cursor = "pointer";
148
+ if (this.activeDroneCameraId !== event.id) {
149
+ this.activeDroneCameraId = event.id;
150
+ if (
151
+ this.activeDroneCameraId &&
152
+ this.layer.entities.getById(this.activeDroneCameraId)
153
+ ) {
154
+ const entity: any = this.layer.entities.getById(
155
+ this.activeDroneCameraId
156
+ ) as any;
157
+ entity.cylinder.outlineColor = Color.BLUE as any;
158
+ }
159
+ }
160
+ }
161
+ );
162
+
163
+ this.mapObject.addLayerListener("mouseout", this.droneImageLayerId, () => {
164
+ this.mapObject.viewer.canvas.style.cursor = "";
165
+ if (
166
+ this.activeDroneCameraId &&
167
+ this.layer.entities.getById(this.activeDroneCameraId)
168
+ ) {
169
+ const entity: any = this.layer.entities.getById(
170
+ this.activeDroneCameraId
171
+ ) as any;
172
+ entity.cylinder.outlineColor = Color.WHITE as any;
173
+ this.activeDroneCameraId = null;
174
+ }
175
+ });
176
+ }
177
+
178
+ /** Removes all listeners added by this layer */
179
+ public removeListeners() {
180
+ this.mapObject.removeLayerListeners(this.droneImageLayerId);
181
+ }
182
+
183
+ /** Remove layer from map and destroy layer */
184
+ public remove(): void {
185
+ this.mapObject.viewer.dataSources.remove(this.layer, true);
186
+ this.removeListeners();
187
+ this.mapObject.viewer.scene.requestRender();
188
+ }
189
+
190
+ show() {
191
+ this.layer.entities.show = true;
192
+ this.mapObject.viewer.scene.requestRender();
193
+ }
194
+
195
+ hide() {
196
+ this.layer.entities.show = false;
197
+ this.mapObject.viewer.scene.requestRender();
198
+ }
199
+ }
@@ -0,0 +1,521 @@
1
+ import {
2
+ CallbackProperty,
3
+ Color,
4
+ Cartesian3,
5
+ CustomDataSource,
6
+ Entity,
7
+ Math as cMath,
8
+ HeadingPitchRange,
9
+ PolygonHierarchy,
10
+ PolylineDashMaterialProperty,
11
+ sampleTerrainMostDetailed,
12
+ ScreenSpaceEventHandler,
13
+ ScreenSpaceEventType,
14
+ HeightReference,
15
+ VerticalOrigin,
16
+ Cartographic,
17
+ } from "cesium";
18
+
19
+ import { LineData, LineOptions, Point, PolygonData, PolygonOptions } from "../app.interface";
20
+
21
+ import type CesiumView from "../droneView";
22
+
23
+ import {
24
+ cartesianArrayToPointArray,
25
+ cartesianToPoint,
26
+ getMidPoint,
27
+ pointArrayToCartesianArray,
28
+ screenToWorldPosition,
29
+ } from "../utility";
30
+
31
+ /**
32
+ * @param {CesiumView} mapObject - The viewer object
33
+ * @param {string} uniqueName - The unique name of the layer
34
+ * @param {LineData[]} data - The data to be used in the layer
35
+ * @param {LineOptions} options - The options for the layer)
36
+ */
37
+ export default class EditLine {
38
+ editLayerId: string;
39
+
40
+ pointLayer: CustomDataSource;
41
+
42
+ lineLayer: CustomDataSource;
43
+
44
+ verticsPostions: any = [];
45
+
46
+ sideCenterPositions: Cartesian3[] = [];
47
+
48
+ verticesSelected: boolean[];
49
+
50
+ selectedIndex: number = -1;
51
+
52
+ selectedAnnotationID: string | number | null;
53
+
54
+ selectedPointID: string | number | null;
55
+
56
+ pointWasDragged: boolean;
57
+
58
+ mapWasMoved: boolean;
59
+
60
+ mouseOverEntity: any | null;
61
+
62
+ opacity: number = 0.0;
63
+
64
+ handler: ScreenSpaceEventHandler;
65
+
66
+ /**
67
+ * @param { CesiumView } mapObject map object
68
+ * @param { string }uniqueName string unique name for the layer
69
+ * @param { LineData } data
70
+ * @param { LineOptions } options
71
+ */
72
+ constructor(
73
+ public mapObject: CesiumView,
74
+ public uniqueName: string,
75
+ public data: LineData,
76
+ public options: LineOptions
77
+ ) {
78
+ this.editLayerId = `polyLine-edit-layer-${this.uniqueName}`;
79
+ this.opacity = 0.0;
80
+ this.buildLayer();
81
+ this.add(this.data);
82
+ if (this.options.setBounds === undefined || this.options.setBounds) {
83
+ this.setBounds();
84
+ }
85
+ this.addListeners();
86
+ }
87
+
88
+ /** To initialize layer */
89
+ public buildLayer(): void {
90
+ if (
91
+ this.mapObject.viewer.dataSources.getByName(
92
+ "polyLine-edit-Point-" + this.uniqueName
93
+ )
94
+ ) {
95
+ this.mapObject.viewer.dataSources.remove(
96
+ this.mapObject.viewer.dataSources.getByName(
97
+ "polyLine-edit-Point-" + this.uniqueName
98
+ )[0],
99
+ true
100
+ );
101
+ }
102
+
103
+ this.pointLayer = new CustomDataSource(
104
+ `polyLine-edit-Point-${this.editLayerId}`
105
+ );
106
+
107
+ this.mapObject.viewer.dataSources.add(this.pointLayer);
108
+
109
+ if (
110
+ this.mapObject.viewer.dataSources.getByName(
111
+ "polyLine-edit-Line-" + this.uniqueName
112
+ )
113
+ ) {
114
+ this.mapObject.viewer.dataSources.remove(
115
+ this.mapObject.viewer.dataSources.getByName(
116
+ "polyLine-edit-Line-" + this.uniqueName
117
+ )[0],
118
+ true
119
+ );
120
+ }
121
+
122
+ this.lineLayer = new CustomDataSource(
123
+ `polyLine-edit-Line-${this.editLayerId}`
124
+ );
125
+
126
+ this.mapObject.viewer.dataSources.add(this.lineLayer);
127
+ }
128
+
129
+ /** To add layer on map */
130
+ public add(data: LineData) {
131
+ // const self = this;
132
+ // let positions = Cartesian3.fromDegreesArrayHeights(data.points.flat());
133
+ // if (positions[0] !== positions[positions.length - 1]) {
134
+ // positions = [...positions, positions[0]];
135
+ // }
136
+
137
+ // self.verticsPostions = positions;
138
+ // self.verticesSelected = new Array(positions.length).fill(false);
139
+
140
+ // const dynamicPositions: any = new CallbackProperty(
141
+ // () => new PolygonHierarchy(self.verticsPostions),
142
+ // false
143
+ // );
144
+
145
+ // this.updateVerticesPoints();
146
+
147
+ // const line = {
148
+ // polyline: {
149
+ // positions: new CallbackProperty(() => [...self.verticsPostions], false),
150
+ // width: 4,
151
+ // material: Color.fromCssColorString("#FF0000"),
152
+ // clampToGround: true,
153
+ // },
154
+ // };
155
+
156
+ // this.lineLayer.entities.add(line);
157
+ // }
158
+
159
+ // /**
160
+ // * Add data on map
161
+ // * @param { FreeformData } data
162
+ // */
163
+ // public addToMap(data: PolygonData) {
164
+ const self = this;
165
+ // let zOffset = 0;
166
+
167
+ // if (this.mapObject.layers.freeform) {
168
+ // zOffset = this.mapObject.layers.freeform.length;
169
+ // }
170
+
171
+ return new Promise<void>((resolve) => {
172
+ const cartesianPositions = pointArrayToCartesianArray(data.points);
173
+ const cartographicPositions =
174
+ this.mapObject.viewer.scene.globe.ellipsoid.cartesianArrayToCartographicArray(
175
+ cartesianPositions
176
+ );
177
+ this.mapObject.viewer.scene
178
+ .sampleHeightMostDetailed(cartographicPositions)
179
+ .then((pos) => {
180
+ let positions =
181
+ this.mapObject.viewer.scene.globe.ellipsoid.cartographicArrayToCartesianArray(
182
+ pos
183
+ );
184
+
185
+ // if (positions[0] !== positions[positions.length - 1]) {
186
+ // positions = [...positions];
187
+ // }
188
+
189
+ self.verticsPostions = positions;
190
+ self.verticesSelected = new Array(positions.length).fill(false);
191
+
192
+ // const dynamicPositions: any = new CallbackProperty(
193
+ // () => new PolygonHierarchy(self.verticsPostions),
194
+ // false
195
+ // );
196
+
197
+ this.updateVerticesPoints();
198
+ // this.updateNonVeticesPoints();
199
+
200
+ const line = {
201
+ polyline: {
202
+ positions: new CallbackProperty(
203
+ () => [...self.verticsPostions],
204
+ false
205
+ ),
206
+ width: 4,
207
+ material: Color.fromCssColorString("#FF0000"),
208
+ clampToGround: true,
209
+ },
210
+ };
211
+
212
+ this.lineLayer.entities.add(line);
213
+
214
+ resolve();
215
+ });
216
+ });
217
+ }
218
+
219
+ /** to set map bouds fit with layer data */
220
+ public setBounds(): void {
221
+ const time = this.options.setBoundsInstant ? 0 : undefined;
222
+ this.mapObject.viewer.flyTo(this.lineLayer.entities, {
223
+ duration: time,
224
+ offset: new HeadingPitchRange(
225
+ cMath.toRadians(this.mapObject.viewer.camera.heading),
226
+ -Math.PI / 2,
227
+ 0
228
+ ),
229
+ });
230
+ }
231
+
232
+ updateVerticesPoints(isDepth?: boolean) {
233
+ this.pointLayer.entities.values.forEach((entity: any, i: number) => {
234
+ if (entity.type === "main") {
235
+ // this.pointLayer.entities.remove(entity);
236
+ entity.position = this.verticsPostions[i];
237
+ entity.billboard.disableDepthTestDistance = isDepth
238
+ ? Number.POSITIVE_INFINITY
239
+ : undefined;
240
+ this.mapObject.viewer.scene.requestRender();
241
+ }
242
+ });
243
+
244
+ if (this.pointLayer.entities.values.length > 2) {
245
+ return;
246
+ }
247
+
248
+ const svg = `<svg height="20" width="20" xmlns="http://www.w3.org/2000/svg">
249
+ <circle cx="10" cy="10" r="8.5" stroke="red" stroke-width="1.5" fill="whiteSmoke" />
250
+ </svg>`;
251
+
252
+ console.log(this.verticsPostions.length);
253
+
254
+ this.verticsPostions.forEach((position: Cartesian3) => {
255
+ const pointMain = {
256
+ position,
257
+ billboard: {
258
+ image: `data:image/svg+xml;base64,${window.btoa(svg)}`,
259
+ verticalOrigin: VerticalOrigin.CENTER,
260
+ eyeOffset: new Cartesian3(0, 0, -10),
261
+ disableDepthTestDistance: isDepth
262
+ ? Number.POSITIVE_INFINITY
263
+ : undefined,
264
+ },
265
+ type: "main",
266
+ };
267
+ this.mapObject.viewer.scene.requestRender();
268
+
269
+ this.pointLayer.entities.add(pointMain);
270
+ });
271
+
272
+ this.mapObject.viewer.scene.requestRender();
273
+ }
274
+
275
+ /** Add all listeners for layer */
276
+ public addListeners() {
277
+ this.handler = new ScreenSpaceEventHandler(this.mapObject.viewer.canvas);
278
+ this.handler.setInputAction(
279
+ this.mouseMoveHandler.bind(this),
280
+ ScreenSpaceEventType.MOUSE_MOVE
281
+ );
282
+
283
+ this.handler.setInputAction(
284
+ this.mouseMoveHandler.bind(this),
285
+ ScreenSpaceEventType.PINCH_MOVE
286
+ );
287
+ }
288
+
289
+ mouseMoveHandler(event: any) {
290
+ if (!event.endPosition) {
291
+ return;
292
+ }
293
+
294
+ if (this.mouseOverEntity && this.selectedIndex >= 0) {
295
+ this.pointWasDragged = true;
296
+
297
+ //const point = this.mapObject.viewer.scene.pickPosition(event.endPosition);
298
+
299
+ // const point = screenToWorldPosition(this.mapObject.viewer,event.endPosition).position;
300
+
301
+ const ray = this.mapObject.viewer.scene.camera.getPickRay(
302
+ event.endPosition
303
+ )!;
304
+ const point = this.mapObject.viewer.scene.globe.pick(
305
+ ray,
306
+ this.mapObject.viewer.scene
307
+ )!;
308
+
309
+ if (!point) {
310
+ return;
311
+ }
312
+
313
+ let carto = Cartographic.fromCartesian(point);
314
+
315
+ const height = this.mapObject.viewer.scene.sampleHeight(carto);
316
+
317
+ this.verticsPostions[this.selectedIndex] = Cartesian3.fromRadians(
318
+ carto.longitude,
319
+ carto.latitude,
320
+ height
321
+ );
322
+
323
+ // console.log(Cartographic.fromCartesian(point).height);
324
+
325
+ if (this.selectedIndex === 0) {
326
+ this.verticsPostions[this.verticsPostions.length - 1] =
327
+ this.verticsPostions[0];
328
+ }
329
+
330
+ // if (this.selectedIndex === this.verticsPostions.length - 1) {
331
+ // this.verticsPostions[0] =
332
+ // this.verticsPostions[this.verticsPostions.length - 1];
333
+ // }
334
+
335
+ this.mapObject.viewer.scene.requestRender();
336
+
337
+ this.updateVerticesPoints(true);
338
+ this.mapObject.viewer.scene.requestRender();
339
+ return;
340
+ }
341
+
342
+ if (this.mapObject.viewer.scene.pickPositionSupported) {
343
+ let result: any;
344
+
345
+ const pickedObject = this.mapObject.viewer.scene.drillPick(
346
+ event.endPosition
347
+ );
348
+
349
+ const entity: any = pickedObject.filter(
350
+ (object) => object.id && object.id.billboard !== undefined
351
+ )[0];
352
+
353
+ if (entity && entity.id && entity.id instanceof Entity) {
354
+ result = {
355
+ id:
356
+ ((entity as any).id.data && (entity as any).data.id) ||
357
+ entity.id.id,
358
+ point: event.position,
359
+ target: this.mapObject.viewer,
360
+ type: "mousemove",
361
+ features: [entity.id],
362
+ };
363
+
364
+ this.mouseEnter(entity, result);
365
+ // }
366
+ } else {
367
+ if (this.mouseOverEntity && !this.pointWasDragged) {
368
+ this.mouseLeave();
369
+ }
370
+ }
371
+ }
372
+ }
373
+
374
+ /**
375
+ * On mouse enter
376
+ */
377
+ mouseEnter(entity: any, result?: any) {
378
+ if (!result.features[0]) {
379
+ return;
380
+ }
381
+
382
+ this.mapObject.viewer.canvas.style.cursor = "pointer";
383
+ this.mouseOverEntity = entity.id;
384
+
385
+ this.handler.setInputAction(
386
+ this.mouseDown.bind(this),
387
+ ScreenSpaceEventType.LEFT_DOWN
388
+ );
389
+ this.handler.setInputAction(
390
+ this.mouseUp.bind(this),
391
+ ScreenSpaceEventType.LEFT_UP
392
+ );
393
+ this.handler.setInputAction(
394
+ this.mouseDown.bind(this),
395
+ ScreenSpaceEventType.PINCH_START
396
+ );
397
+ this.handler.setInputAction(
398
+ this.mouseUp.bind(this),
399
+ ScreenSpaceEventType.PINCH_END
400
+ );
401
+ }
402
+
403
+ /**
404
+ * On mouse leave
405
+ */
406
+ private mouseLeave = () => {
407
+ this.mapObject.viewer.scene.requestRender();
408
+ this.updateVerticesPoints();
409
+ this.selectedIndex = -1;
410
+ this.pointWasDragged = false;
411
+ this.mouseOverEntity = false;
412
+ this.mapObject.viewer.canvas.style.cursor = "default";
413
+ this.mapObject.viewer.scene.requestRender();
414
+ };
415
+
416
+ /**
417
+ * On mouse down
418
+ */
419
+ private mouseDown = () => {
420
+ if (this.mouseOverEntity) {
421
+ const self = this;
422
+
423
+ if (this.mouseOverEntity.type === "main") {
424
+ const entityPosition: Cartesian3 =
425
+ // eslint-disable-next-line no-underscore-dangle
426
+ this.mouseOverEntity.position._value;
427
+
428
+ this.selectedIndex = self.verticsPostions.findIndex(
429
+ (position: Cartesian3) => {
430
+ if (
431
+ position.x === entityPosition.x &&
432
+ position.y === entityPosition.y &&
433
+ position.z === entityPosition.z
434
+ ) {
435
+ return true;
436
+ }
437
+ return false;
438
+ }
439
+ );
440
+ }
441
+
442
+ this.mapObject.disablePan();
443
+ }
444
+
445
+ this.updateVerticesPoints();
446
+ };
447
+
448
+ /**
449
+ * On mouse up
450
+ */
451
+ private mouseUp() {
452
+ if (this.selectedIndex === 0) {
453
+ this.verticsPostions[this.verticsPostions.length - 1] =
454
+ this.verticsPostions[0];
455
+ }
456
+
457
+ if (this.selectedIndex === this.verticsPostions.length - 1) {
458
+ this.verticsPostions[0] =
459
+ this.verticsPostions[this.verticsPostions.length - 1];
460
+ }
461
+
462
+ if (this.options.onPinchEnd && this.selectedIndex >= 0) {
463
+ this.options.onPinchEnd(this.getData());
464
+ }
465
+
466
+ this.mapObject.viewer.scene.requestRender();
467
+
468
+ this.updateVerticesPoints();
469
+
470
+ this.pointWasDragged = false;
471
+ this.mouseOverEntity = null;
472
+ this.selectedIndex = -1;
473
+ this.mapObject.enablePan();
474
+ this.mapObject.viewer.scene.requestRender();
475
+ }
476
+
477
+ /** Removes all listeners added by this layer */
478
+ public removeListeners() {
479
+ this.handler.removeInputAction(ScreenSpaceEventType.LEFT_DOWN);
480
+ this.handler.removeInputAction(ScreenSpaceEventType.LEFT_UP);
481
+ this.handler.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
482
+ this.handler.removeInputAction(ScreenSpaceEventType.PINCH_END);
483
+ this.handler.removeInputAction(ScreenSpaceEventType.PINCH_START);
484
+ this.handler.removeInputAction(ScreenSpaceEventType.PINCH_MOVE);
485
+ }
486
+
487
+ /**
488
+ * Returns the layer's data se
489
+ */
490
+ public getData(): PolygonData {
491
+ const data: any = {
492
+ points: cartesianArrayToPointArray(this.verticsPostions),
493
+ properties: this.data.properties,
494
+ };
495
+
496
+ return data;
497
+ }
498
+
499
+ /** @inheritdoc */
500
+ remove() {
501
+ this.mapObject.viewer.dataSources.remove(this.pointLayer, true);
502
+ this.mapObject.viewer.dataSources.remove(this.lineLayer, true);
503
+ this.removeListeners();
504
+ this.mapObject.viewer.scene.requestRender();
505
+ }
506
+
507
+ /** @inheritdoc */
508
+ show() {
509
+ this.pointLayer.show = true;
510
+ this.lineLayer.show = true;
511
+ this.mapObject.viewer.scene.requestRender();
512
+ }
513
+
514
+ /** @inheritdoc */
515
+ hide() {
516
+ this.pointLayer.show = false;
517
+ this.lineLayer.show = false;
518
+ this.mapObject.viewer.scene.requestRender();
519
+ }
520
+ }
521
+