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,218 @@
1
+ import {
2
+ CustomDataSource,
3
+ HeadingPitchRange,
4
+ Math as cMath,
5
+ Cartesian3,
6
+ } from "cesium";
7
+
8
+ import type CesiumView from "../droneView";
9
+
10
+ import { addPolyLineFilled, getPolygonEntity, addLabel, getPolygonCenter} from "../utility";
11
+
12
+ import { PolygonData, PolygonOptions } from "../app.interface";
13
+
14
+ /**
15
+ * Use this class to create a marker layer
16
+ * @extends {GenericLayer}
17
+ * @param {MapBox} mapObject - The mapbox map object
18
+ * @param {string} uniqueName - The unique name of the layer
19
+ * @param {PolygonData[]} data - The data to be used in the layer
20
+ * @param {PolygonOptions} options - The options for the layer (see {@link PolygonOptions})
21
+ */
22
+ export default class PolygonLayer {
23
+ polygonId: string;
24
+
25
+ uniqueName: string;
26
+
27
+ opacity: number;
28
+
29
+ mapObject: CesiumView;
30
+
31
+ options: PolygonOptions;
32
+
33
+ layer: CustomDataSource;
34
+
35
+ data: PolygonData[];
36
+
37
+ /**
38
+ * @param { CesiumView } mapObject map object
39
+ * @param { string }uniqueName string unique name for the layer
40
+ * @param { PolygonData[] } data
41
+ * @param { PolygonOptions } options
42
+ */
43
+ constructor(
44
+ mapObject: CesiumView,
45
+ uniqueName: string,
46
+ data: PolygonData[],
47
+ options: PolygonOptions
48
+ ) {
49
+ this.polygonId = `Polygon-layer-${this.uniqueName}`;
50
+ this.data = data;
51
+ this.mapObject = mapObject;
52
+ this.uniqueName = uniqueName;
53
+ this.options = options;
54
+ this.opacity = 0;
55
+ if (this.options.opacity !== undefined) {
56
+ this.opacity = this.options.opacity;
57
+ }
58
+ this.buildLayer();
59
+ this.add(this.data).then(() => {
60
+ if (this.options.setBounds === undefined || this.options.setBounds) {
61
+ this.setBounds();
62
+ }
63
+ if (!this.options.isNoListener) this.addListeners();
64
+ });
65
+ }
66
+
67
+ /** To initialize layer */
68
+ public buildLayer(): void {
69
+ if (
70
+ this.mapObject.viewer.dataSources.getByName("polygon" + this.uniqueName)
71
+ ) {
72
+ this.mapObject.viewer.dataSources.remove(
73
+ this.mapObject.viewer.dataSources.getByName(
74
+ "polygon" + this.uniqueName
75
+ )[0],
76
+ true
77
+ );
78
+ }
79
+ this.layer = new CustomDataSource("polygon" + this.uniqueName);
80
+
81
+ this.mapObject.viewer.dataSources.add(this.layer);
82
+ }
83
+
84
+ /** To add layer on map */
85
+ public add(data: PolygonData[]) {
86
+ const self = this;
87
+ return new Promise<void>((resolve) => {
88
+ const loadCommands: any = [];
89
+ data.forEach((formData: PolygonData) => {
90
+ loadCommands.push(self.addToMap(formData));
91
+ });
92
+
93
+ Promise.all(loadCommands).then(() => {
94
+ this.mapObject.viewer.scene.requestRender();
95
+ resolve();
96
+ });
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Add data on map
102
+ * @param { PolygonData } data
103
+ */
104
+ public addToMap(data: PolygonData) {
105
+ console.log(data.properties.label)
106
+ let positions = Cartesian3.fromDegreesArrayHeights(data.points.flat());
107
+ if (positions[0] !== positions[positions.length - 1]) {
108
+ positions = [...positions, positions[0]];
109
+ }
110
+ const line = addPolyLineFilled(
111
+ this.mapObject.viewer,
112
+ positions,
113
+ {
114
+ type: "LineString",
115
+ lineColor: data.properties.color || "#ff0000",
116
+ lineWidth: +data.properties.lineWidth || 3,
117
+ opacity: 1,
118
+ measurement: false
119
+ },
120
+ this.polygonId,
121
+ data.properties
122
+ );
123
+
124
+ this.layer.entities.add(line);
125
+
126
+ const polygon = getPolygonEntity(
127
+ positions,
128
+ this.polygonId,
129
+ +this.opacity,
130
+ data.properties,
131
+ true,
132
+ true
133
+ );
134
+
135
+ addLabel(this.mapObject.viewer, {
136
+ position: getPolygonCenter(positions),
137
+ text: data.properties.label,
138
+ id: data.properties.label
139
+ });
140
+
141
+ this.layer.entities.add(polygon);
142
+ }
143
+
144
+ /** to set map bouds fit with layer data */
145
+ public setBounds(): void {
146
+ const time = this.options.setBoundsInstant ? 0 : undefined;
147
+ this.mapObject.viewer.flyTo(this.layer.entities, {
148
+ duration: time,
149
+ offset: new HeadingPitchRange(
150
+ cMath.toRadians(this.mapObject.viewer.camera.heading),
151
+ -Math.PI / 2,
152
+ 0
153
+ ),
154
+ });
155
+ }
156
+
157
+ /**
158
+ * Sets opacity of the layer
159
+ * @param opacity number between 0-1
160
+ */
161
+ public setOpacity(opacity: number): void {
162
+ this.opacity = opacity;
163
+ this.buildLayer();
164
+ this.add(this.data);
165
+ }
166
+
167
+ public addListeners() {
168
+ this.mapObject.addLayerListener("click", this.polygonId, (event: any) => {
169
+ if (this.options.onClick) {
170
+ this.options.onClick(event);
171
+ }
172
+ });
173
+
174
+ this.mapObject.addLayerListener(
175
+ "mousemove",
176
+ this.polygonId,
177
+ (event: any) => {
178
+ this.mapObject.viewer.canvas.style.cursor = "pointer";
179
+ }
180
+ );
181
+
182
+ this.mapObject.addLayerListener(
183
+ "mouseenter",
184
+ this.polygonId,
185
+ (event: any) => {
186
+ this.mapObject.viewer.canvas.style.cursor = "pointer";
187
+ }
188
+ );
189
+
190
+ this.mapObject.addLayerListener("mouseout", this.polygonId, () => {
191
+ this.mapObject.viewer.canvas.style.cursor = "";
192
+ });
193
+ }
194
+
195
+ /** Removes all listeners added by this layer */
196
+ public removeListeners() {
197
+ this.mapObject.removeLayerListeners(this.polygonId);
198
+ }
199
+
200
+ /** Remove layer from map and destroy layer */
201
+ public remove(): void {
202
+ this.mapObject.viewer.dataSources.remove(this.layer, true);
203
+ this.removeListeners();
204
+ this.mapObject.viewer.scene.requestRender();
205
+ }
206
+
207
+ show() {
208
+ this.layer.entities.show = true;
209
+ this.mapObject.viewer.entities.show = true;
210
+ this.mapObject.viewer.scene.requestRender();
211
+ }
212
+
213
+ hide() {
214
+ this.layer.entities.show = false;
215
+ this.mapObject.viewer.entities.show = false
216
+ this.mapObject.viewer.scene.requestRender();
217
+ }
218
+ }
@@ -0,0 +1,374 @@
1
+ import {
2
+ CallbackProperty,
3
+ Cartesian3,
4
+ defined,
5
+ PolygonHierarchy,
6
+ ScreenSpaceEventHandler,
7
+ ScreenSpaceEventType,
8
+ Color,
9
+ Entity,
10
+ Viewer,
11
+ SceneMode,
12
+ Polyline,
13
+ } from "cesium";
14
+ import type { Mode, DrawingConfig, DrawResponse } from "./app.interface";
15
+ import * as Utility from "./utility";
16
+
17
+ export default class Measurement {
18
+ private handler: ScreenSpaceEventHandler;
19
+
20
+ private isHover: boolean = true;
21
+
22
+ private activeShape: any;
23
+
24
+ private activeShapePoints: any = [];
25
+
26
+ private config: DrawingConfig = {
27
+ type: "Polygon",
28
+ lineColor : "#ff0000",
29
+ strokeColor: "#ff0000",
30
+ measurement: false
31
+ };
32
+
33
+ private selectedPoints: Array<boolean>;
34
+
35
+ private previewPoint: Entity | undefined;
36
+
37
+ private measurementValue: number | undefined;
38
+
39
+ constructor(private viewer: Viewer) {
40
+ this.handler = new ScreenSpaceEventHandler(viewer.canvas);
41
+ }
42
+
43
+ /**
44
+ * This function is for adding distance value between each points
45
+ * @param arrayOfVert - Array of Cartesian
46
+ * @param id Id of main geometry
47
+ */
48
+ private addLabels(arrayOfVert: Array<Cartesian3>, id: string) {
49
+ for (let i = 0; i < arrayOfVert.length; i += 1) {
50
+ if (arrayOfVert[i] && arrayOfVert[i + 1]) {
51
+ const distance = Utility.distance3D([
52
+ arrayOfVert[i],
53
+ arrayOfVert[i + 1],
54
+ ]);
55
+ if (distance > 0.01) {
56
+ Utility.addLabel(this.viewer, {
57
+ id: `${id}-label-${i}`,
58
+ position: Utility.getMidPoint(arrayOfVert[i], arrayOfVert[i + 1]),
59
+ text: `${distance.toFixed(2).toString()} m`,
60
+ });
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ private getMeasurement(arrayOfVert: Array<Cartesian3>, id: string) {
67
+ for (let i = 0; i < arrayOfVert.length; i += 1) {
68
+ if (arrayOfVert[i] && arrayOfVert[i + 1]) {
69
+ const distance = Utility.distance3D([
70
+ arrayOfVert[i],
71
+ arrayOfVert[i + 1],
72
+ ]);
73
+ return distance;
74
+ }
75
+ }
76
+ }
77
+ /**
78
+ * This function is for removing distance value between each points
79
+ * @param arrayOfVert - Array of Cartesian
80
+ * @param id - id of main geometry
81
+ */
82
+ private removeLabels(arrayOfVert: Array<Cartesian3>, id: string) {
83
+ for (let i = 0; i < arrayOfVert.length; i += 1) {
84
+ if (this.viewer.entities.getById(`${id}-${i}`)) {
85
+ this.viewer.entities.remove(
86
+ this.viewer.entities.getById(`${id}-label-${i}`)!
87
+ );
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * This function is for finishing the measurement
94
+ */
95
+ private finishGeometry(): void {
96
+ this.viewer.canvas.style.cursor = "default";
97
+ // if(this.config.type === 'Point') this.activeShapePoints.pop();
98
+ let entity;
99
+
100
+ if (this.config.type === "Polygon") {
101
+ this.activeShapePoints.pop();
102
+ // entity = Utility.addPolygon(this.viewer, this.activeShapePoints as any, {
103
+ // ...this.config,
104
+ // id: this.activeShape.id,
105
+ // });
106
+ // // For dashed outline on polygon
107
+ // Utility.addPolyLine(
108
+ // this.viewer,
109
+ // [...this.activeShapePoints, this.activeShapePoints[0]],
110
+ // { ...this.config, id: `${this.activeShape.id}-Line` }
111
+ // );
112
+
113
+ entity = Utility.addPolyLine(this.viewer, [...this.activeShapePoints, this.activeShapePoints[0]], {
114
+ ...this.config,
115
+ //clamp: false,
116
+ }, false);
117
+
118
+ this.viewer.scene.requestRender();
119
+ } else if (this.config.type === "LineString") {
120
+ this.activeShapePoints.pop();
121
+ this.measurementValue = this.getMeasurement(this.activeShapePoints, this.activeShape.id);
122
+ entity = Utility.addPolyLine(this.viewer, this.activeShapePoints, {
123
+ ...this.config,
124
+ id: this.activeShape?.id,
125
+ // clamp: false
126
+ });
127
+ } else if (this.config.type === "Cross-Section") {
128
+ entity = Utility.addPolyLine(this.viewer, this.activeShapePoints, {
129
+ ...this.config,
130
+ id: this.activeShape?.id,
131
+ clamp: true
132
+ }, false);
133
+ } else {
134
+ entity = Utility.addPoint(this.viewer, this.activeShapePoints[0]);
135
+ }
136
+
137
+ if (this.drawingFinished) {
138
+ this.drawingFinished({
139
+ id: entity.id,
140
+ object: Utility.cartesianToGeojson(
141
+ this.activeShapePoints,
142
+ this.config.type,
143
+ this.measurementValue ? {measurement: this.measurementValue} : undefined
144
+ ),
145
+ }); // to responded after drawing finished
146
+ }
147
+ this.activeShape = undefined;
148
+ this.activeShapePoints = [];
149
+ if (this.previewPoint) {
150
+ this.viewer.entities.remove(this.previewPoint);
151
+ }
152
+
153
+ this.previewPoint = undefined;
154
+ }
155
+
156
+ // Callback function to respond data after drawing
157
+ public drawingFinished: (drawResponse: DrawResponse) => void;
158
+
159
+ /**
160
+ * This Function is for get position while mouse is moving on map
161
+ * @param movement - Mouse window position object contains x,y
162
+ */
163
+ private previewGeometry(movement: any) {
164
+ const earthPosition = Utility.screenToWorldPosition(
165
+ this.viewer,
166
+ movement.endPosition
167
+ )?.position;
168
+
169
+ if (defined(earthPosition) && this.config.type === "Point") {
170
+ this.previewPoint = Utility.addPoint(
171
+ this.viewer,
172
+ earthPosition,
173
+ "Preview-Point"
174
+ );
175
+ }
176
+
177
+ if (this.activeShapePoints.length > 0 && defined(earthPosition)) {
178
+ this.activeShapePoints.pop();
179
+ this.activeShapePoints.push(earthPosition);
180
+
181
+ // if (this.previewPoint && this.config.type === "Point") {
182
+ // (this.previewPoint as any).position._value = earthPosition as any;
183
+ // }
184
+ }
185
+
186
+ this.viewer.scene.requestRender();
187
+ }
188
+
189
+ /**
190
+ * To add geometry on map
191
+ * @param positions Array of positions (Cartesian)
192
+ * @returns Entity
193
+ */
194
+ private addGeometryOnMap(positions: any) {
195
+ let entity;
196
+ switch (this.config.type) {
197
+ case "LineString":
198
+ if (this.config.measurement) {
199
+ entity = Utility.addPolyLine(this.viewer, positions, {...this.config, clamp: false});
200
+ }
201
+ else {
202
+ entity = Utility.addPolyLine(this.viewer, positions, {...this.config, id: this.activeShape?.id});
203
+ }
204
+ break;
205
+ case "Cross-Section":
206
+ entity = Utility.addPolyLine(this.viewer, positions, {
207
+ ...this.config,
208
+ clamp: true,
209
+ }, false);
210
+ break;
211
+ case "Polygon":
212
+ entity = Utility.addPolyLine(this.viewer, positions, {
213
+ ...this.config,
214
+ //clamp: true,
215
+ }, false);
216
+ // entity = Utility.addPolygon(this.viewer, positions, this.config);
217
+ // Utility.addPolyLine(
218
+ // this.viewer,
219
+ // new CallbackProperty(() => this.activeShapePoints, false) as any,
220
+ // this.config
221
+ // );
222
+ break;
223
+ default:
224
+ }
225
+ return entity;
226
+ }
227
+
228
+ /**
229
+ * This function is for adding positions while user is drawing
230
+ * User can click left button to add more point into geometry
231
+ * @param movement - Mouse screen position object
232
+ */
233
+ private intermediateGeometry(movement: any) {
234
+ this.viewer.canvas.style.cursor = "crosshair";
235
+
236
+ if (this.previewPoint) {
237
+ this.viewer.entities.remove(this.previewPoint);
238
+ }
239
+
240
+ this.previewPoint = undefined;
241
+
242
+
243
+
244
+ const result = Utility.screenToWorldPosition(
245
+ this.viewer,
246
+ movement.position
247
+ );
248
+
249
+ if(result?.id?.billboard){
250
+ this.finishGeometry();
251
+ return;
252
+ }
253
+
254
+
255
+ const earthPosition = result?.position;
256
+
257
+
258
+ const self = this;
259
+ if (defined(earthPosition)) {
260
+ if (this.activeShapePoints.length === 0) {
261
+ this.activeShapePoints.push(earthPosition);
262
+ if (self.config.type === "Point") {
263
+ this.finishGeometry();
264
+ return;
265
+ }
266
+ const dynamicPositions: any = new CallbackProperty(() => {
267
+ if (self.config.type === "Polygon") {
268
+ return [...self.activeShapePoints,self.activeShapePoints[0]];
269
+ }
270
+ return self.activeShapePoints;
271
+ }, false);
272
+ this.activeShape = this.addGeometryOnMap(dynamicPositions);
273
+ }
274
+ this.activeShapePoints.push(earthPosition);
275
+
276
+ if(this.config.type === "LineString"){
277
+ this.activeShape = this.addGeometryOnMap(this.activeShapePoints);
278
+ }
279
+
280
+ if (this.activeShape) {
281
+ Utility.addPoint(
282
+ this.viewer,
283
+ earthPosition,
284
+ `${this.activeShape.id}-${(
285
+ this.activeShapePoints.length - 1
286
+ ).toString()}`
287
+ );
288
+ }
289
+
290
+ if (self.config.type === "LineString") {
291
+ this.addLabels(this.activeShapePoints, this.activeShape.id);
292
+ }
293
+
294
+ if (self.config.type === "Cross-Section") {
295
+ if (this.activeShapePoints.length > 2) {
296
+ this.handler.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
297
+ const allPointsAlongLine = Utility.getPositionsByFixDistance(
298
+ this.activeShapePoints[0],
299
+ this.activeShapePoints[1]
300
+ );
301
+ this.viewer.scene
302
+ .sampleHeightMostDetailed(
303
+ this.viewer.scene.globe.ellipsoid.cartesianArrayToCartographicArray(
304
+ allPointsAlongLine
305
+ )
306
+ )
307
+ .then((f) => {
308
+ this.activeShapePoints =
309
+ this.viewer.scene.globe.ellipsoid.cartographicArrayToCartesianArray(
310
+ f
311
+ );
312
+ Utility.addLabel(this.viewer, {
313
+ position:
314
+ this.activeShapePoints[
315
+ Math.floor(this.activeShapePoints.length / 2)
316
+ ],
317
+ text: `${Utility.distance3D(
318
+ this.viewer.scene.globe.ellipsoid.cartographicArrayToCartesianArray(
319
+ f
320
+ )
321
+ )
322
+ .toFixed(2)
323
+ .toString()} m`,
324
+ });
325
+ this.finishGeometry();
326
+ });
327
+ }
328
+ }
329
+ }
330
+
331
+ this.viewer.scene.requestRender();
332
+ }
333
+
334
+ /**
335
+ * To enable measurement mode
336
+ * @param config - DrawingConfig
337
+ */
338
+ public enable(config: DrawingConfig) {
339
+ this.isHover = false;
340
+ this.config = config;
341
+ this.handler.setInputAction(
342
+ this.previewGeometry.bind(this),
343
+ ScreenSpaceEventType.MOUSE_MOVE
344
+ );
345
+ this.handler.setInputAction(
346
+ this.intermediateGeometry.bind(this),
347
+ ScreenSpaceEventType.LEFT_CLICK
348
+ );
349
+ }
350
+
351
+ /**
352
+ * To disable measurement mode
353
+ */
354
+ public disable() {
355
+ const previewPointId = this.previewPoint?.id;
356
+ if (previewPointId) {
357
+ this.viewer.entities.removeById(previewPointId);
358
+ this.viewer.scene.requestRender();
359
+ }
360
+ this.isHover = true;
361
+ this.handler.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
362
+ this.handler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
363
+ this.handler.removeInputAction(ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
364
+ }
365
+
366
+ /**
367
+ * To assign dynamic color based on selection while editing the drawn geometry
368
+ * @param index - Index of position
369
+ * @returns - Method will return Color
370
+ */
371
+ private getColor(index: number) {
372
+ return this.selectedPoints[index] ? Color.CYAN : Color.WHITE;
373
+ }
374
+ }