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,333 @@
1
+ import {
2
+ Cartesian2,
3
+ Color,
4
+ CustomDataSource,
5
+ BillboardGraphics,
6
+ Math as cMath,
7
+ HeadingPitchRange,
8
+ Cartesian3,
9
+ HeightReference,
10
+ VerticalOrigin,
11
+ } from "cesium";
12
+
13
+ import type CesiumView from "../droneView";
14
+
15
+ import { MarkerData, MarkerIcon, MarkerOptions } from "../app.interface";
16
+ import { addLabel } from "../utility";
17
+
18
+ /**
19
+ * Use this class to create a marker layer
20
+ * @param {MapBox} mapObject - The mapbox map object
21
+ * @param {string} uniqueName - The unique name of the layer
22
+ * @param {MarkerData[]} data - The data to be used in the layer
23
+ * @param {MarkerOptions} options - The options for the layer (see {@link MarkerOptions})
24
+ */
25
+
26
+ export default class MarkerLayer {
27
+ markerLayerId: string;
28
+
29
+ icons: BillboardGraphics[] = [];
30
+
31
+ markerSize: number = 12.5;
32
+
33
+ allowSVGs: boolean = false;
34
+
35
+ opacity: number;
36
+
37
+ layer: CustomDataSource;
38
+
39
+ /**
40
+ * @param mapObject CesiumView map object
41
+ * @param uniqueName string unique name for the layer
42
+ * @param data Array<MarkerData>
43
+ * @param options MarkerOptions
44
+ */
45
+ constructor(
46
+ public mapObject: CesiumView,
47
+ public uniqueName: string,
48
+ public data: Array<MarkerData>,
49
+ public options: MarkerOptions
50
+ ) {
51
+ this.markerLayerId = `marker-layer-${this.uniqueName}`;
52
+
53
+ this.opacity = 1;
54
+
55
+ this.allowSVGs = true;
56
+ const agent = navigator.userAgent.toLowerCase();
57
+ if (
58
+ (typeof this.options.allowSVGs !== "undefined" &&
59
+ !this.options.allowSVGs) ||
60
+ (agent.indexOf("chrome") === -1 &&
61
+ (agent.indexOf("edg") !== -1 ||
62
+ agent.indexOf("edge") !== -1 ||
63
+ agent.indexOf("msie") !== -1 ||
64
+ agent.indexOf("trident") > -1 ||
65
+ agent.indexOf("firefox") > -1 ||
66
+ agent.indexOf("safari") > -1))
67
+ ) {
68
+ this.allowSVGs = false;
69
+ }
70
+
71
+ // load icons before adding marker on map
72
+ this.loadIcons().then(() => {
73
+ this.buildLayer();
74
+ this.add(this.data).then(() => {
75
+ if (this.options.setBounds === undefined || this.options.setBounds) {
76
+ this.setBounds();
77
+ }
78
+ this.addListeners();
79
+ });
80
+ });
81
+ }
82
+
83
+ /** To initialize layer */
84
+ public buildLayer(): void {
85
+ if (
86
+ this.mapObject.viewer.dataSources.getByName("marker" + this.uniqueName)
87
+ ) {
88
+ this.mapObject.viewer.dataSources.remove(
89
+ this.mapObject.viewer.dataSources.getByName(
90
+ "marker" + this.uniqueName
91
+ )[0],
92
+ true
93
+ );
94
+ }
95
+ this.layer = new CustomDataSource("marker" + this.uniqueName);
96
+ }
97
+
98
+ /** To add layer on map */
99
+ public add(data: MarkerData[]) {
100
+ const self = this;
101
+
102
+ return new Promise<void>((resolve) => {
103
+ self.addToMap(data).then(() => {
104
+ this.mapObject.viewer.dataSources.add(this.layer);
105
+
106
+ this.mapObject.viewer.scene.requestRender();
107
+ resolve();
108
+ });
109
+ });
110
+ }
111
+
112
+ /**
113
+ *
114
+ * @param { MarkerData[] } data - load data on map
115
+ * @returns { void }
116
+ */
117
+ public async addToMap(data: MarkerData[]): Promise<void> {
118
+ const cartesians: any = [];
119
+ const cartesiansWith3D: any = [];
120
+
121
+ data.forEach((point: any) => {
122
+ if (point.point[2])
123
+ {
124
+ cartesiansWith3D.push(Cartesian3.fromDegrees(point.point[0], point.point[1], point.point[2]));
125
+ }
126
+ else{
127
+ cartesians.push(Cartesian3.fromDegrees(point.point[0], point.point[1]));
128
+ }
129
+ });
130
+
131
+ const addMarkers = (positions: Cartesian3[]): void => {
132
+ positions.forEach((position: Cartesian3, i: number) => {
133
+ const iconIndex = Object.keys(this.options.icons).findIndex(
134
+ (icon) =>
135
+ icon === data[i].properties.icon ||
136
+ (this.options.icons[+icon] &&
137
+ this.options.icons[+icon].name === data[i].properties.icon)
138
+ );
139
+
140
+ if (iconIndex >= 0) {
141
+ const selected_icon_src = this.options.icons[+iconIndex].src
142
+ for (const hash of this.icons) {
143
+ const billBoardSource = (hash.image as any)._value;
144
+ if (billBoardSource === selected_icon_src){
145
+ const pin = {
146
+ position,
147
+ billboard: hash,
148
+ label : {
149
+ text: (data[i].properties.label || ''),
150
+ font : '16px sans-serif',
151
+ eyeOffset : Cartesian3.ZERO,
152
+ showBackground: true,
153
+ pixelOffset : new Cartesian2(0,-20),
154
+ data: data[i].properties,
155
+ layerId: this.markerLayerId,
156
+ },
157
+ data: data[i].properties,
158
+ layerId: this.markerLayerId,
159
+ };
160
+ this.layer.entities.add(pin);
161
+ }
162
+ }
163
+ }
164
+ });
165
+ };
166
+
167
+ addMarkers(cartesiansWith3D);
168
+ const updatedCartesiansWith3D = await this.mapObject.viewer.scene.clampToHeightMostDetailed(cartesians);
169
+ addMarkers(updatedCartesiansWith3D);
170
+ }
171
+
172
+ /**
173
+ * Loads all specified marker icons and adds them to the map. Icons must be loaded before they can be used.
174
+ */
175
+ private loadIcons() {
176
+ const self = this;
177
+ return new Promise<void>((resolve) => {
178
+ const loadCommands: any = [];
179
+ const { icons } = self.options;
180
+
181
+ Object.keys(icons).forEach((iconID: any) => {
182
+ if (iconID !== "contains") {
183
+ console.log(icons[iconID]);
184
+ loadCommands.push(self.load(icons[iconID]));
185
+ }
186
+ });
187
+ Promise.all(loadCommands)
188
+ .then(() => {
189
+ resolve();
190
+ })
191
+ .catch(() => {
192
+ const loadingElement = document.getElementById("main-loading");
193
+ if (loadingElement) {
194
+ loadingElement.style.display = "none";
195
+ }
196
+ });
197
+ });
198
+ }
199
+
200
+ /**
201
+ *
202
+ * @param { MarkerIcon } icon - create bill board from icons
203
+ * @returns { void }
204
+ */
205
+ load(icon: MarkerIcon) {
206
+ const self = this;
207
+ return new Promise<void>((resolve) => {
208
+ let url = icon.src;
209
+ if (
210
+ this.options.allowSVGs &&
211
+ (typeof icon.options.allowSVG === "undefined" || icon.options.allowSVG)
212
+ ) {
213
+ url = url.replace(".png", ".svg");
214
+ }
215
+ const { pixelRatio } = icon.options;
216
+ console.log(url);
217
+
218
+ const image: any = new Image();
219
+ const visibility = icon?.options?.deficiency === true ? 0 : Number.POSITIVE_INFINITY
220
+ image.onload = function () {
221
+ self.icons.push(
222
+ new BillboardGraphics({
223
+ image: url,
224
+ color: Color.WHITE.withAlpha(self.opacity || 1),
225
+ disableDepthTestDistance: visibility,
226
+ verticalOrigin: VerticalOrigin.BASELINE,
227
+ pixelOffset : new Cartesian2(0,-30),
228
+ width: this.width / +(pixelRatio || 4),
229
+ height: this.height / +(pixelRatio || 4),
230
+ rotation: cMath.toRadians(self.options.rotate || 0),
231
+ })
232
+ );
233
+ resolve();
234
+ };
235
+ image.src = url;
236
+ });
237
+ }
238
+
239
+ /** Set bounds after adding a layer */
240
+ public setBounds(): void {
241
+ const time = this.options.setBoundsInstant ? 0 : undefined;
242
+ this.mapObject.viewer.flyTo(this.layer.entities, {
243
+ duration: time,
244
+ offset: new HeadingPitchRange(
245
+ cMath.toRadians(this.mapObject.viewer.camera.heading),
246
+ -Math.PI / 2,
247
+ 0
248
+ ),
249
+ });
250
+ }
251
+
252
+ public addListeners() {
253
+ var isMouseOverMarker = false;
254
+ let hoverTimer: NodeJS.Timeout | undefined;
255
+
256
+ this.mapObject.addLayerListener('click', this.markerLayerId, (event: any) => {
257
+ if(this.options.onClick){
258
+ this.options.onClick(event);
259
+ }
260
+ });
261
+
262
+ this.mapObject.addLayerListener(
263
+ 'mousemove',
264
+ this.markerLayerId,
265
+ (e: any) => {
266
+ this.mapObject.viewer.canvas.style.cursor = 'pointer';
267
+ if (!isMouseOverMarker) {
268
+ const hoverEvent = event
269
+ isMouseOverMarker = true;
270
+ hoverTimer = setTimeout(() => {
271
+ if (this.options.onHover) {
272
+ this.options.onHover(e, hoverEvent);
273
+ }
274
+ }, 1000);
275
+ }
276
+ }
277
+ );
278
+
279
+ this.mapObject.addLayerListener(
280
+ 'mouseenter',
281
+ this.markerLayerId,
282
+ (event: any) => {
283
+ this.mapObject.viewer.canvas.style.cursor = 'pointer';
284
+ }
285
+ );
286
+
287
+ this.mapObject.addLayerListener('mouseout', this.markerLayerId, () => {
288
+ isMouseOverMarker = false;
289
+ clearTimeout(hoverTimer);
290
+ this.mapObject.viewer.canvas.style.cursor = '';
291
+ if (this.options.clearHover) {
292
+ this.options.clearHover(event);
293
+ }
294
+ });
295
+ }
296
+
297
+ /** Removes all listeners added by this layer */
298
+ public removeListeners() {
299
+ this.mapObject.removeLayerListeners(this.markerLayerId);
300
+ }
301
+
302
+ /** Remove layer from map and destroy layer */
303
+ public remove(): void {
304
+ this.mapObject.viewer.dataSources.remove(this.layer, true);
305
+ this.removeListeners();
306
+ this.mapObject.viewer.scene.requestRender();
307
+ }
308
+
309
+ /** enables visibility of the layer */
310
+ public show(): void {
311
+ this.layer.show = true;
312
+ }
313
+
314
+ /** Disables visibility of the layer */
315
+ public hide(): void {
316
+ this.layer.show = false;
317
+ }
318
+
319
+ /**
320
+ * Sets opacity of the layer
321
+ * @param opacity number between 0-1
322
+ */
323
+ public setOpacity(opacity: number): void {
324
+ this.opacity = opacity;
325
+ this.icons = [];
326
+ this.loadIcons().then(() => {
327
+ this.opacity = opacity;
328
+ this.remove();
329
+ this.buildLayer();
330
+ this.add(this.data);
331
+ });
332
+ }
333
+ }
@@ -0,0 +1,326 @@
1
+ import {
2
+ Cartesian2,
3
+ Color,
4
+ CustomDataSource,
5
+ BillboardGraphics,
6
+ Math as cMath,
7
+ HeadingPitchRange,
8
+ Cartesian3,
9
+ HeightReference,
10
+ VerticalOrigin,
11
+ Entity,
12
+ ScreenSpaceEventHandler,
13
+ ScreenSpaceEventType,
14
+ } from "cesium";
15
+
16
+ import type CesiumView from "../droneView";
17
+
18
+ import { MarkerData, MarkerIcon, MarkerOptions } from "../app.interface";
19
+
20
+ /**
21
+ * Use this class to create a marker layer
22
+ * @param {MapBox} mapObject - The mapbox map object
23
+ * @param {string} uniqueName - The unique name of the layer
24
+ * @param {MarkerData[]} data - The data to be used in the layer
25
+ * @param {MarkerOptions} options - The options for the layer (see {@link MarkerOptions})
26
+ */
27
+ export default class Marker3DLayer {
28
+ markerLayerId: string;
29
+
30
+ icons: Entity[] = [];
31
+
32
+ markerSize: number = 12.5;
33
+
34
+ allowSVGs: boolean = false;
35
+
36
+ opacity: number;
37
+
38
+ layer: CustomDataSource;
39
+ handler: ScreenSpaceEventHandler;
40
+ hoverTimeout: NodeJS.Timeout | undefined;
41
+ isMouseOverMarker: boolean = false
42
+
43
+ /**
44
+ * @param mapObject CesiumView map object
45
+ * @param uniqueName string unique name for the layer
46
+ * @param data Array<MarkerData>
47
+ * @param options MarkerOptions
48
+ */
49
+ constructor(
50
+ public mapObject: CesiumView,
51
+ public uniqueName: string,
52
+ public data: Array<MarkerData>,
53
+ public options: MarkerOptions
54
+ ) {
55
+ this.markerLayerId = `marker-layer-${this.uniqueName}`;
56
+
57
+ this.opacity = 1;
58
+
59
+ // this.allowSVGs = true;
60
+ // const agent = navigator.userAgent.toLowerCase();
61
+ // if (
62
+ // (typeof this.options.allowSVGs !== "undefined" &&
63
+ // !this.options.allowSVGs) ||
64
+ // (agent.indexOf("chrome") === -1 &&
65
+ // (agent.indexOf("edg") !== -1 ||
66
+ // agent.indexOf("edge") !== -1 ||
67
+ // agent.indexOf("msie") !== -1 ||
68
+ // agent.indexOf("trident") > -1 ||
69
+ // agent.indexOf("firefox") > -1 ||
70
+ // agent.indexOf("safari") > -1))
71
+ // ) {
72
+ // this.allowSVGs = false;
73
+ // }
74
+
75
+ // load icons before adding marker on map
76
+ // this.loadIcons().then(() => {
77
+ this.buildLayer();
78
+ this.add(this.data).then(() => {
79
+ if (this.options.setBounds === undefined || this.options.setBounds) {
80
+ this.setBounds();
81
+ }
82
+ this.addListeners();
83
+ });
84
+ // });
85
+ }
86
+
87
+ /** To initialize layer */
88
+ public buildLayer(): void {
89
+ if (
90
+ this.mapObject.viewer.dataSources.getByName("marker" + this.uniqueName)
91
+ ) {
92
+ this.mapObject.viewer.dataSources.remove(
93
+ this.mapObject.viewer.dataSources.getByName(
94
+ "marker" + this.uniqueName
95
+ )[0],
96
+ true
97
+ );
98
+ }
99
+ this.layer = new CustomDataSource("marker" + this.uniqueName);
100
+ }
101
+
102
+ /** To add layer on map */
103
+ public add(data: MarkerData[]) {
104
+ const self = this;
105
+
106
+ return new Promise<void>((resolve) => {
107
+ self.addToMap(data).then(() => {
108
+ this.mapObject.viewer.dataSources.add(this.layer);
109
+
110
+ this.mapObject.viewer.scene.requestRender();
111
+ resolve();
112
+ });
113
+ });
114
+ }
115
+
116
+ /**
117
+ *
118
+ * @param { MarkerData[] } data - load data on map
119
+ * @returns { void }
120
+ */
121
+
122
+ public addToMap(data: MarkerData[]) {
123
+ return new Promise<void>((resolve) => {
124
+ const cartesians: any = [];
125
+ const cartesiansWithOutHeight: any = [];
126
+ const positionWithOutHeight: any = [];
127
+
128
+ console.log("data", data)
129
+ data.forEach((point) => {
130
+ const height = point.point[2] + 0.5;
131
+
132
+ if (point.point[2] == 0 || point.point[2] == null) {
133
+ const position = Cartesian3.fromDegrees(point.point[0], point.point[1]);
134
+ cartesiansWithOutHeight.push({ position, properties: point});
135
+ positionWithOutHeight.push(position)
136
+ } else {
137
+ const position = Cartesian3.fromDegrees(point.point[0], point.point[1], height || 0);
138
+ cartesians.push({
139
+ position,
140
+ properties: point,
141
+ });
142
+ }
143
+ });
144
+ console.log("cartesians", cartesians)
145
+ console.log("cartesiansWithOutHeight", cartesiansWithOutHeight)
146
+ console.log("positionWithOutHeight", positionWithOutHeight)
147
+
148
+ cartesians.forEach((position: any = [], i: number) => {
149
+ // const iconIndex = Object.keys(this.options.icons).findIndex(
150
+ // (icon) =>
151
+ // icon === data[i].properties.icon ||
152
+ // (this.options.icons[+icon] &&
153
+ // this.options.icons[+icon].name === data[i].properties.icon)
154
+ // );
155
+ // if (iconIndex >= 0) {
156
+ // const pin = {
157
+ // position,
158
+ // billboard: this.icons[iconIndex],
159
+ // data: data[i].properties,
160
+ // layerId: this.markerLayerId,
161
+ // };
162
+ // this.layer.entities.add(pin);
163
+ var markerProperty = position.properties.properties
164
+ this.layer.entities.add({
165
+ // name: 'Model 1',
166
+ position: position.position,
167
+ model: {uri : markerProperty.icon, scale: markerProperty.scale},
168
+ properties: position.properties
169
+ });
170
+
171
+ // Clone the model entity with different positions
172
+ // var modelEntity1 = viewer.entities.add(this.icons[iconIndex].clone());
173
+ // modelEntity1.position = position;
174
+ // }
175
+ // });
176
+ resolve();
177
+ })
178
+
179
+ this.mapObject.viewer.scene
180
+ .clampToHeightMostDetailed(positionWithOutHeight)
181
+ .then((pos) => {
182
+ cartesiansWithOutHeight.forEach((position: any = [], i: number) => {
183
+ // const iconIndex = Object.keys(this.options.icons).findIndex(
184
+ // (icon) =>
185
+ // icon === data[i].properties.icon ||
186
+ // (this.options.icons[+icon] &&
187
+ // this.options.icons[+icon].name === data[i].properties.icon)
188
+ // );
189
+ // if (iconIndex >= 0) {
190
+ // const pin = {
191
+ // position,
192
+ // billboard: this.icons[iconIndex],
193
+ // data: data[i].properties,
194
+ // layerId: this.markerLayerId,
195
+ // };
196
+ // this.layer.entities.add(pin);
197
+
198
+ var markerWithoutHeightProperty = position.properties.properties
199
+ this.layer.entities.add({
200
+ // name: 'Model 1',
201
+ position: position.position,
202
+ model: {uri : markerWithoutHeightProperty.icon, scale: markerWithoutHeightProperty.scale},
203
+ properties: position.properties
204
+ });
205
+
206
+ // Clone the model entity with different positions
207
+ // var modelEntity1 = viewer.entities.add(this.icons[iconIndex].clone());
208
+ // modelEntity1.position = position;
209
+ // }
210
+ // });
211
+ resolve();
212
+ });
213
+ })
214
+ });
215
+ }
216
+
217
+
218
+ /** Set bounds after adding a layer */
219
+ public setBounds(): void {
220
+ const time = this.options.setBoundsInstant ? 0 : undefined;
221
+ this.mapObject.viewer.flyTo(this.layer.entities, {
222
+ duration: time,
223
+ offset: new HeadingPitchRange(
224
+ cMath.toRadians(this.mapObject.viewer.camera.heading),
225
+ -Math.PI / 2,
226
+ 0
227
+ ),
228
+ });
229
+ }
230
+
231
+ public addListeners() {
232
+ this.handler = new ScreenSpaceEventHandler(this.mapObject.viewer.canvas);
233
+
234
+ // Change the event type to LEFT_CLICK
235
+ this.handler.setInputAction(
236
+ this.iconClickHandler.bind(this),
237
+ ScreenSpaceEventType.LEFT_CLICK
238
+ );
239
+
240
+ // Add MOUSE_MOVE event for on-hover functionality
241
+ this.handler.setInputAction(
242
+ this.iconHoverHandler.bind(this),
243
+ ScreenSpaceEventType.MOUSE_MOVE
244
+ );
245
+ }
246
+
247
+ iconHoverHandler(event: any) {
248
+ if (this.hoverTimeout) {
249
+ clearTimeout(this.hoverTimeout);
250
+ }
251
+ const pickedObject = this.mapObject.viewer.scene.pick(event.endPosition);
252
+
253
+ if (pickedObject && pickedObject.id) {
254
+ if (!this.isMouseOverMarker) {
255
+ this.hoverTimeout = setTimeout(() => {
256
+ this.isMouseOverMarker = true;
257
+ const hoveredMarkerProperties = pickedObject.id.properties?.properties?._value;
258
+ if (this.options.onHover) {
259
+ this.options.onHover(hoveredMarkerProperties, event);
260
+ }
261
+ }, 1000);
262
+ }
263
+ } else {
264
+ if (this.isMouseOverMarker){
265
+ clearTimeout(this.hoverTimeout);
266
+ if (this.options.clearHover) {
267
+ this.options.clearHover(event);
268
+ }
269
+ this.isMouseOverMarker = false;
270
+ }
271
+ }
272
+ }
273
+
274
+ iconClickHandler(event: any) {
275
+ const pickedObject = this.mapObject.viewer.scene.pick(event.position);
276
+
277
+ if (pickedObject && pickedObject.id) {
278
+ const selectedMarker = pickedObject.id.properties?.properties?._value
279
+
280
+ if (this.options.onClick) {
281
+ this.options.onClick(selectedMarker);
282
+ }
283
+ }
284
+ }
285
+ /** Removes all listeners added by this layer */
286
+ public removeListeners() {
287
+ this.handler.removeInputAction(ScreenSpaceEventType.LEFT_DOWN);
288
+ this.handler.removeInputAction(ScreenSpaceEventType.LEFT_UP);
289
+ this.handler.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
290
+ this.handler.removeInputAction(ScreenSpaceEventType.PINCH_END);
291
+ this.handler.removeInputAction(ScreenSpaceEventType.PINCH_START);
292
+ this.handler.removeInputAction(ScreenSpaceEventType.PINCH_MOVE);
293
+ }
294
+
295
+ /** Remove layer from map and destroy layer */
296
+ public remove(): void {
297
+ this.mapObject.viewer.dataSources.remove(this.layer, true);
298
+ this.removeListeners();
299
+ this.mapObject.viewer.scene.requestRender();
300
+ }
301
+
302
+ /** enables visibility of the layer */
303
+ public show(): void {
304
+ this.layer.show = true;
305
+ }
306
+
307
+ /** Disables visibility of the layer */
308
+ public hide(): void {
309
+ this.layer.show = false;
310
+ }
311
+
312
+ /**
313
+ * Sets opacity of the layer
314
+ * @param opacity number between 0-1
315
+ */
316
+ public setOpacity(opacity: number): void {
317
+ this.opacity = opacity;
318
+ this.icons = [];
319
+ // this.loadIcons().then(() => {
320
+ this.opacity = opacity;
321
+ this.remove();
322
+ this.buildLayer();
323
+ this.add(this.data);
324
+ // });
325
+ }
326
+ }