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,253 @@
1
+ import {
2
+ Cesium3DTileset,
3
+ Cesium3DTileStyle,
4
+ HeadingPitchRange,
5
+ ShadowMode,
6
+ Math as cMath,
7
+ Transforms,
8
+ Cartesian3,
9
+ HeadingPitchRoll,
10
+ Matrix4,
11
+ SplitDirection,
12
+ } from "cesium";
13
+ import { Options, ModelData, Point, MapType } from "../app.interface";
14
+ import type CesiumView from "../droneView";
15
+ import { cartesianToPoint } from "../utility";
16
+ /**
17
+ * Use this class to create a Ortho layer
18
+ * @param { MapBox } mapObject - The mapbox map object
19
+ * @param { string } uniqueName - The unique name of the layer
20
+ * @param { ModelData } data - The data to be used in the layer
21
+ * @param { Options } options - The options for the layer (see {@link Options})
22
+ */
23
+ export default class ModelLayer {
24
+ tilsetLayerLayers: Cesium3DTileset;
25
+
26
+ data: ModelData;
27
+
28
+ modelLayerId: string;
29
+
30
+ type: string;
31
+
32
+ uniqueName: string;
33
+
34
+ opacity: number;
35
+
36
+ mapObject: CesiumView;
37
+
38
+ options: Options;
39
+
40
+ tileset: Cesium3DTileset;
41
+
42
+ mapType: MapType;
43
+
44
+ constructor(
45
+ mapObject: CesiumView,
46
+ uniqueName: string,
47
+ data: ModelData,
48
+ options: Options,
49
+ mapType: MapType
50
+
51
+ ) {
52
+ this.modelLayerId = `Model-${this.uniqueName}`;
53
+ this.mapType = mapType;
54
+ this.opacity = 1;
55
+ this.data = data;
56
+ this.mapObject = mapObject;
57
+ this.uniqueName = uniqueName;
58
+ this.options = options;
59
+ this.addLayer(data, true);
60
+ }
61
+
62
+ getSplitDirection() {
63
+ if (this.mapType && this.mapType === 'default') {
64
+ return SplitDirection.LEFT;
65
+ }
66
+
67
+ if (this.mapType && this.mapType === 'compare') {
68
+ return SplitDirection.RIGHT;
69
+ }
70
+
71
+ return SplitDirection.NONE;
72
+ }
73
+
74
+ /**
75
+ * To load 3D data and return tileset for future use (visibility, remove, zoom)
76
+ * @param url string Url or Path to main tileset.json file
77
+ * @returns Cesium3DTileset
78
+ */
79
+ public async addTilset(
80
+ url: string,
81
+ color: string = '#ffffff',
82
+ shadow: boolean,
83
+ modelMatrix: Matrix4,
84
+ setBound: boolean = true
85
+ ) {
86
+ const tileset = await new Cesium3DTileset({
87
+ url,
88
+ shadows: shadow ? ShadowMode.ENABLED : ShadowMode.DISABLED,
89
+ maximumScreenSpaceError: 2,
90
+ skipLevelOfDetail: true,
91
+ immediatelyLoadDesiredLevelOfDetail: true,
92
+ dynamicScreenSpaceError: true,
93
+ splitDirection: this.getSplitDirection(),
94
+ // modelMatrix: modelMatrix,
95
+ });
96
+
97
+ tileset.readyPromise.then((tileset => {
98
+
99
+ const center = cartesianToPoint(tileset.boundingSphere.center);
100
+
101
+ if (+(center.height || 0) < -1000) {
102
+ tileset.modelMatrix = modelMatrix;
103
+ }else{
104
+ this.data.center = center;
105
+ }
106
+
107
+ // if(!tileset.asset || !tileset.asset.extras || !tileset.asset.extras.ion || !tileset.asset.extras.ion.georeferenced){
108
+ // console.log("pppp");
109
+ // }
110
+
111
+ if (color) {
112
+ tileset.style = new Cesium3DTileStyle({
113
+ color: {
114
+ conditions: [["true", `color("${color}", ${this.opacity})`]],
115
+ },
116
+ });
117
+ }
118
+ this.mapObject.viewer.scene.primitives.add(tileset);
119
+ this.tileset = tileset;
120
+ if (setBound) this.mapObject.viewer.zoomTo(tileset);
121
+ // this.tilsetLayerLayers = tilsetLayer;
122
+ this.mapObject.viewer.scene.requestRender();
123
+ return tileset;
124
+ }))
125
+
126
+ }
127
+
128
+ setBounds() {
129
+ const time = this.options.setBoundsInstant ? 0 : undefined;
130
+ this.mapObject.viewer.flyTo(this.tilsetLayerLayers, {
131
+ duration: time,
132
+ offset: new HeadingPitchRange(
133
+ cMath.toRadians(this.mapObject.viewer.camera.heading),
134
+ -Math.PI / 2,
135
+ 0
136
+ ),
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Add a raster layer to the map
142
+ * @param {number} index - The index of the layer
143
+ * @param {ModelData} data - The data to be used in the layer
144
+ */
145
+ async addLayer(layer: ModelData, setBound: boolean) {
146
+ const color = layer.color || "#ffffff";
147
+ const isShadow = layer.shadow || true;
148
+ const { url } = layer;
149
+ const { center } = layer;
150
+ const { rotationX } = layer || 0;
151
+ const { rotationY } = layer || 0;
152
+ const { rotationZ } = layer || 0;
153
+
154
+ const modelMatrix = Transforms.headingPitchRollToFixedFrame(
155
+ Cartesian3.fromDegrees(+center?.long || 0, +center?.lat || 0, +(center?.height || 0)),
156
+ new HeadingPitchRoll(
157
+ cMath.toRadians(rotationX),
158
+ cMath.toRadians(rotationY),
159
+ cMath.toRadians(rotationZ)
160
+ )
161
+ );
162
+
163
+ const tilsetLayer: any = await this.addTilset(
164
+ url,
165
+ color,
166
+ isShadow,
167
+ modelMatrix,
168
+ setBound
169
+ );
170
+
171
+ }
172
+
173
+ /** Remove layer from map and destroy layer */
174
+ public remove(): void {
175
+ this.mapObject.viewer.scene.primitives.remove(this.tileset);
176
+ this.mapObject.viewer.scene.requestRender();
177
+ }
178
+
179
+ /** Show model */
180
+ public show(): void{
181
+ this.tileset.show = true;
182
+ this.mapObject.viewer.scene.requestRender();
183
+ }
184
+
185
+ /** Hide model */
186
+ public hide(): void{
187
+ this.tileset.show = false;
188
+ this.mapObject.viewer.scene.requestRender();
189
+ }
190
+
191
+ /** @inheritdoc */
192
+ setOpacity(opacity: number) {
193
+ this.opacity = opacity;
194
+ this.remove();
195
+ this.addLayer(this.data, false);
196
+ this.mapObject.viewer.scene.requestRender();
197
+ }
198
+
199
+ setRotation(rotationX: number, rotationY: number, rotationZ: number) {
200
+
201
+ if (this.tileset.asset.extras.ion.georeferenced) {
202
+ return;
203
+ }
204
+
205
+ if (rotationX) {
206
+ this.data.rotationX = rotationX;
207
+ }
208
+
209
+ if (rotationY) {
210
+ this.data.rotationY = rotationY;
211
+ }
212
+
213
+ if (rotationZ) {
214
+ this.data.rotationZ = rotationZ;
215
+ }
216
+
217
+ const { center } = this.data;
218
+
219
+ const modelMatrix = Transforms.headingPitchRollToFixedFrame(
220
+ Cartesian3.fromDegrees(+center.long, +center.lat, +(center.height || 0)),
221
+ new HeadingPitchRoll(
222
+ cMath.toRadians(rotationX || 0),
223
+ cMath.toRadians(rotationY || 0),
224
+ cMath.toRadians(rotationZ || 0)
225
+ )
226
+ );
227
+
228
+ this.tileset.modelMatrix = modelMatrix;
229
+ this.mapObject.viewer.scene.requestRender();
230
+ }
231
+
232
+ setPosition(position: Point) {
233
+ if (this.tileset?.asset?.extras?.ion?.georeferenced) {
234
+ return;
235
+ }
236
+ this.data.center = position;
237
+ const modelMatrix = Transforms.headingPitchRollToFixedFrame(
238
+ Cartesian3.fromDegrees(
239
+ +this.data.center.long,
240
+ +this.data.center.lat,
241
+ +(this.data.center.height || 0)
242
+ ),
243
+ new HeadingPitchRoll(
244
+ cMath.toRadians(this.data.rotationX || 0),
245
+ cMath.toRadians(this.data.rotationY || 0),
246
+ cMath.toRadians(this.data.rotationZ || 0)
247
+ )
248
+ );
249
+
250
+ this.tileset.modelMatrix = modelMatrix;
251
+ this.mapObject.viewer.scene.requestRender();
252
+ }
253
+ }
@@ -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 { NoteData, NoteIcon, NoteOptions } from "../app.interface";
16
+ import { addLabel } from "../utility";
17
+
18
+ /**
19
+ * Use this class to create a note layer
20
+ * @param {MapBox} mapObject - The mapbox map object
21
+ * @param {string} uniqueName - The unique name of the layer
22
+ * @param {NoteData[]} data - The data to be used in the layer
23
+ * @param {NoteOptions} options - The options for the layer (see {@link NoteOptions})
24
+ */
25
+
26
+ export default class NoteLayer {
27
+ noteLayerId: string;
28
+
29
+ icons: BillboardGraphics[] = [];
30
+
31
+ noteSize: 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<NoteData>
43
+ * @param options NoteOptions
44
+ */
45
+ constructor(
46
+ public mapObject: CesiumView,
47
+ public uniqueName: string,
48
+ public data: Array<NoteData>,
49
+ public options: NoteOptions
50
+ ) {
51
+ this.noteLayerId = `note-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 note 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("note" + this.uniqueName)
87
+ ) {
88
+ this.mapObject.viewer.dataSources.remove(
89
+ this.mapObject.viewer.dataSources.getByName(
90
+ "note" + this.uniqueName
91
+ )[0],
92
+ true
93
+ );
94
+ }
95
+ this.layer = new CustomDataSource("note" + this.uniqueName);
96
+ }
97
+
98
+ /** To add layer on map */
99
+ public add(data: NoteData[]) {
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 { NoteData[] } data - load data on map
115
+ * @returns { void }
116
+ */
117
+ public async addToMap(data: NoteData[]): 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 addNotes = (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.noteLayerId,
156
+ },
157
+ data: data[i].properties,
158
+ layerId: this.noteLayerId,
159
+ };
160
+ this.layer.entities.add(pin);
161
+ }
162
+ }
163
+ }
164
+ });
165
+ };
166
+
167
+ addNotes(cartesiansWith3D);
168
+ const updatedCartesiansWith3D = await this.mapObject.viewer.scene.clampToHeightMostDetailed(cartesians);
169
+ addNotes(updatedCartesiansWith3D);
170
+ }
171
+
172
+ /**
173
+ * Loads all specified note 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 { NoteIcon } icon - create bill board from icons
203
+ * @returns { void }
204
+ */
205
+ load(icon: NoteIcon) {
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 isMouseOverNote = false;
254
+ let hoverTimer: NodeJS.Timeout | undefined;
255
+
256
+ this.mapObject.addLayerListener('click', this.noteLayerId, (event: any) => {
257
+ if(this.options.onClick){
258
+ this.options.onClick(event);
259
+ }
260
+ });
261
+
262
+ this.mapObject.addLayerListener(
263
+ 'mousemove',
264
+ this.noteLayerId,
265
+ (e: any) => {
266
+ this.mapObject.viewer.canvas.style.cursor = 'pointer';
267
+ if (!isMouseOverNote) {
268
+ const hoverEvent = event
269
+ isMouseOverNote = 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.noteLayerId,
282
+ (event: any) => {
283
+ this.mapObject.viewer.canvas.style.cursor = 'pointer';
284
+ }
285
+ );
286
+
287
+ this.mapObject.addLayerListener('mouseout', this.noteLayerId, () => {
288
+ isMouseOverNote = 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.noteLayerId);
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,76 @@
1
+ import {
2
+ ImageryLayer,
3
+ WebMapServiceImageryProvider,
4
+ WebMercatorTilingScheme,
5
+ } from "cesium";
6
+ import type { LayerOptions, OrthoData } from "../app.interface";
7
+ import type CesiumView from "../droneView";
8
+ /**
9
+ * Use this class to create a Ortho layer
10
+ * @param {CesiumView} mapObject - The CesiumView map object
11
+ * @param {string} uniqueName - The unique name of the layer
12
+ * @param {OrthoData} data - The data to be used in the layer
13
+ * @param {LayerOptions} options - The options for the layer (see {@link LayerOptions})
14
+ */
15
+ class RasterLayer {
16
+ imageryLayer: ImageryLayer;
17
+
18
+ orthoId: string;
19
+
20
+ constructor(
21
+ public mapObject: CesiumView,
22
+ public uniqueName: string,
23
+ public data: OrthoData,
24
+ public options: LayerOptions
25
+ ) {
26
+ this.orthoId = `ortho-layer-${uniqueName}`;
27
+
28
+ this.addRaster(data);
29
+ }
30
+
31
+ /**
32
+ * Add a raster layer to the map
33
+ * @param {OrthoData} data - The data to be used in the layer
34
+ */
35
+ addRaster(layer: OrthoData) {
36
+ let opacity = 1;
37
+
38
+ console.log(layer);
39
+
40
+ const imageryLayer =
41
+ this.mapObject.viewer.scene.imageryLayers.addImageryProvider(
42
+ new WebMapServiceImageryProvider({
43
+ url: layer.url,
44
+ layers: 'ortho',
45
+ parameters: {
46
+ transparent: "true",
47
+ format: "image/png",
48
+ },
49
+ tileWidth: layer.tileSize || 256,
50
+ tileHeight: layer.tileSize || 256,
51
+ maximumLevel: layer.maxZoom || 23,
52
+ minimumLevel: layer.minZoom || 0,
53
+ credit: layer.credit,
54
+ tilingScheme: new WebMercatorTilingScheme(),
55
+ })
56
+ );
57
+
58
+ imageryLayer.alpha = opacity;
59
+
60
+ this.imageryLayer = imageryLayer;
61
+
62
+ // this.mapObject.viewer.zoomTo(this.imageryLayer)
63
+ }
64
+
65
+ /** Remove layer from map and destroy layer */
66
+ public remove(): void {
67
+ this.mapObject.viewer.scene.imageryLayers.remove(this.imageryLayer, true);
68
+ this.mapObject.viewer.scene.requestRender();
69
+ }
70
+
71
+ setOpacity(opacity: number) {
72
+ this.imageryLayer.alpha = opacity * 0.01;
73
+ this.mapObject.viewer.scene.requestRender();
74
+ }
75
+ }
76
+ export default RasterLayer;