@vidro/map-handler 1.2.12 → 1.2.19

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.
Files changed (42) hide show
  1. package/README.md +367 -48
  2. package/dist/map-handler.js +1 -1
  3. package/doc/confirmComponent.png +0 -0
  4. package/examples/full/apidemo.js +46 -43
  5. package/examples/full/cachedToken.dat +1 -1
  6. package/examples/full/cachedTokenData.dat +1 -1
  7. package/examples/full/docker/Docker_compose.yml +14 -0
  8. package/examples/full/docker/Dockerfile +27 -0
  9. package/examples/full/index.php +5 -4
  10. package/examples/full/tester.css +74 -0
  11. package/examples/full/tester.js +2 -2
  12. package/examples/react-next/README.md +282 -0
  13. package/examples/react-next/components/AuthComponent.js +88 -0
  14. package/examples/react-next/components/MapButtons.js +161 -0
  15. package/examples/react-next/components/MapFilters.js +120 -0
  16. package/examples/react-next/components/MapIframe.js +25 -0
  17. package/examples/react-next/components/MapInfo.js +36 -0
  18. package/examples/react-next/components/MapLayers.js +60 -0
  19. package/examples/react-next/components/MapList.js +43 -0
  20. package/examples/react-next/contexts/auth.js +101 -0
  21. package/examples/react-next/contexts/maps.js +158 -0
  22. package/examples/react-next/contexts/messages.js +340 -0
  23. package/examples/react-next/env.sample +3 -0
  24. package/examples/react-next/eslint.config.mjs +14 -0
  25. package/examples/react-next/hooks/useMapEvents.js +118 -0
  26. package/examples/react-next/jsconfig.json +7 -0
  27. package/examples/react-next/next.config.mjs +6 -0
  28. package/examples/react-next/package.json +24 -0
  29. package/examples/react-next/pages/_app.js +5 -0
  30. package/examples/react-next/pages/index.js +87 -0
  31. package/examples/react-next/postcss.config.mjs +8 -0
  32. package/examples/react-next/public/discord.svg +8 -0
  33. package/examples/react-next/public/favicon.ico +0 -0
  34. package/examples/react-next/public/file.svg +1 -0
  35. package/examples/react-next/public/logo.png +0 -0
  36. package/examples/react-next/public/next.svg +1 -0
  37. package/examples/react-next/shared/constants.js +47 -0
  38. package/examples/react-next/styles/globals.css +24 -0
  39. package/examples/react-next/tailwind.config.mjs +17 -0
  40. package/helpers.md +45 -0
  41. package/package.json +1 -1
  42. package/src/index.js +263 -23
@@ -0,0 +1,47 @@
1
+ export const INFO_EVENTS = {
2
+ NO_RESULTS: "no results",
3
+ NO_LAYER: "no layer",
4
+ INVALID_GEOMETRY: "invalid geometry",
5
+ ZOOM_OVERFLOW: "zoomlevel overflow",
6
+ NO_GEOJSON_RESULTS: "no geojson results",
7
+ };
8
+ export const COMMON = {
9
+ START: "start",
10
+ REJECT: "reject",
11
+ };
12
+ export const GEOLOCATION_EVENTS = {
13
+ ERROR: "geolocation error",
14
+ GEOLOCATING: "localizing",
15
+ GEOLOCATED: "localized",
16
+ ERROR_USER_DENIED: "user denied geolocation",
17
+ };
18
+
19
+ export const MEASURING_EVENTS = {
20
+ MEASURING: "measuring",
21
+ MEASURE_END: "measured",
22
+ };
23
+
24
+ export const MAP_EVENTS = {
25
+ INFO: "info",
26
+ COORDINATES: "coordinates",
27
+ GET_TOC: "getToc",
28
+ ZOOM_CHANGE: "onZoomChange",
29
+ CENTER_CHANGE: "onCenterChange",
30
+ LOADED: "loaded",
31
+ ERROR: "error",
32
+ AVAILABLE_WMS_LAYERS: "availableWMSLayers",
33
+ CAPABILITIES: "capabilities",
34
+ GISWATER_FILTERS_APPLIED: "GiswaterFiltersApplied",
35
+ GEOLOCATION: "geolocation",
36
+ END_MEASURE: "MeasureEnd",
37
+ START_MEASURE: "MeasureStart",
38
+ UNLOADED: "unloaded",
39
+ CENTER_CHANGE: "onCenterChange",
40
+ ACTIVE_LAYER: "activeLayer",
41
+ WMS_LAYERS: "availableWMSLayers",
42
+ STATUS: "status",
43
+ GEOM_ADDED: "geomAdded",
44
+ LAYERS: "layers",
45
+ VERSION: "version",
46
+ SCREENSHOT: "screenshot",
47
+ };
@@ -0,0 +1,24 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ /* Update tailwind to hide input type number arrows */
6
+ @layer base {
7
+
8
+ input[type="number"]::-webkit-inner-spin-button,
9
+ input[type="number"]::-webkit-outer-spin-button {
10
+ -webkit-appearance: none;
11
+ margin: 0;
12
+ }
13
+ }
14
+
15
+ html,
16
+ body {
17
+ padding: 0;
18
+ margin: 0;
19
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
20
+ Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
21
+ color: #666666;
22
+ min-height: 100vh;
23
+ background-color: white;
24
+ }
@@ -0,0 +1,17 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: [
4
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
5
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
6
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
7
+ ],
8
+ theme: {
9
+ extend: {
10
+ colors: {
11
+ background: "var(--background)",
12
+ foreground: "var(--foreground)",
13
+ },
14
+ },
15
+ },
16
+ plugins: [],
17
+ };
package/helpers.md ADDED
@@ -0,0 +1,45 @@
1
+ ##### Image to ArrayBuffer
2
+
3
+
4
+ >Helper function to fetch image as ArrayBuffer
5
+
6
+ ```
7
+ const fetchImageAsArrayBuffer = async (path) => {
8
+ try {
9
+ const response = await fetch(path); // Fetch the image from the path
10
+ const buffer = await response.arrayBuffer(); // Convert response to ArrayBuffer
11
+ return buffer; // Return the binary data
12
+ } catch (error) {
13
+ console.error("Error fetching image:", error);
14
+ throw error;
15
+ }
16
+ };
17
+ ```
18
+
19
+ >Helper function to set image as ArrayBuffer with vanilla js
20
+
21
+
22
+ ```
23
+ // Simulate a call to Dropbox or other service that can
24
+ // return an image as an ArrayBuffer.
25
+ var xhr = new XMLHttpRequest();
26
+
27
+ // Use JSFiddle logo as a sample image to avoid complicating
28
+ // this example with cross-domain issues.
29
+ xhr.open( "GET", "http://fiddle.jshell.net/img/logo.png", true );
30
+
31
+ // Ask for the result as an ArrayBuffer.
32
+ xhr.responseType = "arraybuffer";
33
+
34
+ xhr.onload = function( e ) {
35
+ // Obtain a blob: URL for the image data.
36
+ var arrayBufferView = new Uint8Array( this.response );
37
+ var blob = new Blob( [ arrayBufferView ], { type: "image/jpeg" } );
38
+ var urlCreator = window.URL || window.webkitURL;
39
+ var imageUrl = urlCreator.createObjectURL( blob );
40
+ var img = document.querySelector( "#photo" );
41
+ img.src = imageUrl;
42
+ };
43
+
44
+ xhr.send();
45
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vidro/map-handler",
3
- "version": "1.2.12",
3
+ "version": "1.2.19",
4
4
  "description": "Tool to achieve the easiest way of communication with the map",
5
5
  "homepage": "https://github.com/Vidro-Software-SL/maphandler",
6
6
  "repository": {
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter } from "events";
2
2
  import { iframeCommunicator } from "./shared/iframe-communicator";
3
+
3
4
  class Communicator extends EventEmitter {
4
5
  constructor(data) {
5
6
  super();
@@ -15,19 +16,25 @@ class Communicator extends EventEmitter {
15
16
  this.sessionToken = data.sessionToken;
16
17
  }
17
18
 
19
+ removeListener(event, listener) {
20
+ super.removeListener(event, listener);
21
+ }
18
22
  onMessageReceived = (e) => {
19
23
  switch (e.data.type) {
20
24
  case "onZoomChange":
21
- this.emitEvent("onZoomChange", e.data.zoom, e.data.domId);
25
+ this.emitEvent("onZoomChange", e.data, e.data.domId);
26
+ break;
27
+ case "onCenterChange":
28
+ this.emitEvent("onCenterChange", e.data, e.data.domId);
22
29
  break;
23
30
  case "geomAdded":
24
31
  this.emitEvent("geomAdded", e.data, e.data.domId);
25
32
  break;
26
33
  case "layers":
27
- this.emitEvent("layers", e.data.layers, e.data.domId);
34
+ this.emitEvent("layers", e.data, e.data.domId);
28
35
  break;
29
36
  case "geoJSONlayers":
30
- this.emitEvent("geoJSONlayers", e.data.layers, e.data.domId);
37
+ this.emitEvent("geoJSONlayers", e.data, e.data.domId);
31
38
  break;
32
39
  case "info":
33
40
  this.emitEvent("info", e.data, e.data.domId);
@@ -82,6 +89,22 @@ class Communicator extends EventEmitter {
82
89
  case "status":
83
90
  this.emitEvent("status", e.data, e.data.domId);
84
91
  break;
92
+ case "MeasureEnd":
93
+ this.emitEvent("MeasureEnd", e.data, e.data.domId);
94
+ break;
95
+ case "queue":
96
+ this.emitEvent("queue", e.data, e.data.domId);
97
+ break;
98
+ case "version":
99
+ this.emitEvent("version", e.data, e.data.domId);
100
+ break;
101
+ case "hover":
102
+ this.emitEvent("hover", e.data, e.data.domId);
103
+ break;
104
+ case "screenshot":
105
+ this.emitEvent("screenshot", e.data, e.data.domId);
106
+ break;
107
+
85
108
  //case "getLegend": this.emitEvent("getLegend", e.data,e.data.domId); break;
86
109
  }
87
110
  };
@@ -114,12 +137,19 @@ class Communicator extends EventEmitter {
114
137
  texts: options?.texts,
115
138
  style: options?.style,
116
139
  drawOnEnd: options?.drawOnEnd,
140
+ showConfirm: options?.showConfirm,
117
141
  sessionToken: this.sessionToken,
118
142
  });
119
143
  };
120
144
 
145
+ CancelAddGeom = (s) => {
146
+ this.com.sendMessageToMap({
147
+ type: "CancelAddGeom",
148
+ });
149
+ };
150
+
121
151
  loadMultipleLayers = (layers) => {
122
- if (typeof layers === "undefined") {
152
+ if (typeof layers !== "undefined") {
123
153
  this.com.sendMessageToMap({
124
154
  type: "loadMultipleLayers",
125
155
  layers: layers,
@@ -129,29 +159,45 @@ class Communicator extends EventEmitter {
129
159
  }
130
160
  };
131
161
 
162
+ toggleGroup = (layers) => {
163
+ this.com.sendMessageToMap({
164
+ type: "toggleGroup",
165
+ layers,
166
+ });
167
+ };
168
+
132
169
  toggleLayer = (layer, properties) => {
133
170
  if (typeof properties === "undefined") {
134
171
  properties = {
135
172
  gutter: null,
136
173
  transparent: null,
137
174
  singletile: null,
175
+ zIndex: null,
138
176
  };
139
177
  }
140
178
 
141
179
  if (properties.singletile !== null) {
142
180
  if (typeof properties.singletile !== "boolean") {
143
181
  properties.singletile = null;
144
- this.emit("error", { error: "singleTile must be a Boolean" });
182
+ this.emit("error", {
183
+ error: "singletile must be a Boolean",
184
+ type: "error",
185
+ });
145
186
  }
146
187
  }
147
- if (properties.gutter !== "" && properties.gutter !== null) {
188
+ if (
189
+ properties.gutter !== "" &&
190
+ properties.gutter !== null &&
191
+ !properties.singletile
192
+ ) {
148
193
  if (isNaN(parseInt(properties.gutter))) {
149
194
  properties.gutter = null;
150
- this.emit("error", { error: "Gutter must be a number" });
195
+ this.emit("error", { type: "error", error: "Gutter must be a number" });
151
196
  }
152
197
  if (properties.singletile) {
153
198
  properties.gutter = null;
154
199
  this.emit("error", {
200
+ type: "error",
155
201
  error:
156
202
  "Gutter can only be user with multitile layers; set singletile to false",
157
203
  });
@@ -161,7 +207,10 @@ class Communicator extends EventEmitter {
161
207
  if (properties.transparent !== null) {
162
208
  if (typeof properties.transparent !== "boolean") {
163
209
  properties.transparent = null;
164
- this.emit("error", { error: "transparent must be a Boolean" });
210
+ this.emit("error", {
211
+ type: "error",
212
+ error: "transparent must be a Boolean",
213
+ });
165
214
  }
166
215
  }
167
216
  this.com.sendMessageToMap({
@@ -173,9 +222,27 @@ class Communicator extends EventEmitter {
173
222
  transparent: properties.transparent,
174
223
  singletile: properties.singletile,
175
224
  sessionToken: this.sessionToken,
225
+ zIndex: !isNaN(parseInt(properties.zIndex))
226
+ ? parseInt(properties.zIndex)
227
+ : null,
176
228
  });
177
229
  };
178
230
 
231
+ removeLayer = (layer) => {
232
+ this.com.sendMessageToMap({
233
+ type: "removeLayer",
234
+ layer: layer,
235
+ sessionToken: this.sessionToken,
236
+ });
237
+ };
238
+
239
+ displayLayer = (layer) => {
240
+ this.com.sendMessageToMap({
241
+ type: "displayLayer",
242
+ layer: layer,
243
+ sessionToken: this.sessionToken,
244
+ });
245
+ };
179
246
  setActiveLayer = (layer) => {
180
247
  this.com.sendMessageToMap({
181
248
  type: "setActiveLayer",
@@ -191,6 +258,22 @@ class Communicator extends EventEmitter {
191
258
  });
192
259
  };
193
260
 
261
+ bringLayerToTop = (layer) => {
262
+ this.com.sendMessageToMap({
263
+ type: "bringLayerToTop",
264
+ layer: layer,
265
+ sessionToken: this.sessionToken,
266
+ });
267
+ };
268
+
269
+ bringLayerToBottom = (layer) => {
270
+ this.com.sendMessageToMap({
271
+ type: "bringLayerToBottom",
272
+ layer: layer,
273
+ sessionToken: this.sessionToken,
274
+ });
275
+ };
276
+
194
277
  loadWMSAvailableLayers = () => {
195
278
  this.com.sendMessageToMap({
196
279
  type: "loadWMSAvailableLayers",
@@ -218,18 +301,61 @@ class Communicator extends EventEmitter {
218
301
  });
219
302
  };
220
303
 
221
- AddMultipleGeometries = (geoms) => {
222
- if (typeof geoms === "undefined") {
304
+ DrawGeometries = (geoms) => {
305
+ if (typeof geoms !== "undefined") {
223
306
  this.com.sendMessageToMap({
224
- type: "AddMultipleGeometries",
225
- geoms,
307
+ type: "DrawGeometries",
308
+ geoms: geoms,
226
309
  });
227
310
  } else {
228
- this.emit("error", { error: "no geoms" });
311
+ this.emit("error", { type: "error", error: "no geoms" });
312
+ }
313
+ };
314
+
315
+ RemoveGeometriesByProperty = (layer, property, value) => {
316
+ if (
317
+ typeof layer !== "undefined" &&
318
+ typeof property !== "undefined" &&
319
+ typeof value !== "undefined"
320
+ ) {
321
+ this.com.sendMessageToMap({
322
+ type: "RemoveGeometriesByProperty",
323
+ layer,
324
+ property,
325
+ value,
326
+ });
327
+ } else {
328
+ this.emit("error", {
329
+ type: "error",
330
+ error: "no layer, property or value",
331
+ });
332
+ }
333
+ };
334
+
335
+ UpdateGeometriesByProperty = (layer, property, value, style) => {
336
+ if (
337
+ typeof layer !== "undefined" &&
338
+ typeof property !== "undefined" &&
339
+ typeof style !== "undefined" &&
340
+ typeof value !== "undefined"
341
+ ) {
342
+ this.com.sendMessageToMap({
343
+ type: "UpdateGeometriesByProperty",
344
+ layer,
345
+ property,
346
+ value,
347
+ style,
348
+ });
349
+ } else {
350
+ this.emit("error", {
351
+ type: "error",
352
+ error: "no layer, property or value",
353
+ });
229
354
  }
230
355
  };
231
356
 
232
357
  DrawGeometry = (geom, styles, name, id) => {
358
+ console.warn("DrawGeometry is deprecated. Use DrawGeometries");
233
359
  const sty = {
234
360
  stroke_color: styles.stroke_color ? styles.stroke_color : null,
235
361
  fill_color: styles.fill_color ? styles.fill_color : null,
@@ -266,7 +392,7 @@ class Communicator extends EventEmitter {
266
392
  const _id = typeof id == "undefined" ? null : id;
267
393
  if (!_id) {
268
394
  console.error("No element id");
269
- this.emit("error", { error: "No element id" });
395
+ this.emit("error", { type: "error", error: "No element id" });
270
396
  return;
271
397
  }
272
398
  this.com.sendMessageToMap({
@@ -283,6 +409,37 @@ class Communicator extends EventEmitter {
283
409
  });
284
410
  };
285
411
 
412
+ zoomToScale = (scale) => {
413
+ const allowedScales = [
414
+ "1:100",
415
+ "1:200",
416
+ "1:400",
417
+ "1:500",
418
+ "1:1000",
419
+ "1:2000",
420
+ "1:5000",
421
+ "1:10000",
422
+ "1:50000",
423
+ ];
424
+
425
+ if (!allowedScales.includes(scale)) {
426
+ console.error(
427
+ `Invalid scale: ${scale}. Allowed values are: ${allowedScales.join(
428
+ ", "
429
+ )}`
430
+ );
431
+ this.emit("error", {
432
+ type: "error",
433
+ error: `Invalid scale: ${scale}`,
434
+ });
435
+ return;
436
+ }
437
+ this.com.sendMessageToMap({
438
+ type: "zoomToScale",
439
+ sessionToken: this.sessionToken,
440
+ scale: scale,
441
+ });
442
+ };
286
443
  zoomToCoordinates = (lat, lon, zoomLevel) => {
287
444
  if (!isNaN(parseInt(zoomLevel))) {
288
445
  this.com.sendMessageToMap({
@@ -294,6 +451,15 @@ class Communicator extends EventEmitter {
294
451
  }
295
452
  };
296
453
 
454
+ zoomToGeometry = (geom, limits) => {
455
+ this.com.sendMessageToMap({
456
+ type: "zoomToGeometry",
457
+ sessionToken: this.sessionToken,
458
+ geom,
459
+ limits,
460
+ });
461
+ };
462
+
297
463
  infoFromCoordinates = (type, layer, hitTolerance, format) => {
298
464
  const _layer = typeof layer == "undefined" ? null : layer;
299
465
  const _hitTolerance =
@@ -339,10 +505,11 @@ class Communicator extends EventEmitter {
339
505
  });
340
506
  };
341
507
 
342
- Geolocalize = (toggle) => {
508
+ Geolocalize = (toggle, options) => {
343
509
  this.com.sendMessageToMap({
344
510
  type: "Geolocalize",
345
511
  toggle: toggle,
512
+ options: options,
346
513
  sessionToken: this.sessionToken,
347
514
  });
348
515
  };
@@ -350,7 +517,7 @@ class Communicator extends EventEmitter {
350
517
  /* deprecated since v1.1.19 */
351
518
  toggleGiswaterTiled = (toggle, tiled) => {
352
519
  this.com.sendMessageToMap({
353
- type: "toggleGiswaterTiled",
520
+ type: "toggleTiled",
354
521
  toggle: toggle,
355
522
  tiled: tiled,
356
523
  sessionToken: this.sessionToken,
@@ -407,7 +574,7 @@ class Communicator extends EventEmitter {
407
574
  sessionToken: this.sessionToken,
408
575
  });
409
576
  } else {
410
- this.emit("error", { error: "No geoJSON data" });
577
+ this.emit("error", { type: "error", error: "No geoJSON data" });
411
578
  return;
412
579
  }
413
580
  };
@@ -427,7 +594,7 @@ class Communicator extends EventEmitter {
427
594
  sessionToken: this.sessionToken,
428
595
  });
429
596
  } else {
430
- this.emit("error", { error: "No geoJSON data" });
597
+ this.emit("error", { type: "error", error: "No geoJSON data" });
431
598
  return;
432
599
  }
433
600
  };
@@ -442,7 +609,10 @@ class Communicator extends EventEmitter {
442
609
  try {
443
610
  filtersJson = JSON.parse(filters);
444
611
  } catch (e) {
445
- this.emit("error", { error: "Filters is not a valid JSON" });
612
+ this.emit("error", {
613
+ type: "error",
614
+ error: "Filters is not a valid JSON",
615
+ });
446
616
  return;
447
617
  }
448
618
  }
@@ -453,7 +623,7 @@ class Communicator extends EventEmitter {
453
623
  sessionToken: this.sessionToken,
454
624
  });
455
625
  } else {
456
- this.emit("error", { error: "No filters" });
626
+ this.emit("error", { type: "error", error: "No filters" });
457
627
  return;
458
628
  }
459
629
  };
@@ -468,18 +638,30 @@ class Communicator extends EventEmitter {
468
638
  try {
469
639
  filtersJson = JSON.parse(filters);
470
640
  } catch (e) {
471
- this.emit("error", { error: "Filters is not a valid JSON" });
641
+ this.emit("error", {
642
+ type: "error",
643
+ error: "Filters is not a valid JSON",
644
+ });
472
645
  return;
473
646
  }
474
647
  }
648
+ const isValid = filtersJson.every((item) => Array.isArray(item.filters));
475
649
 
650
+ // isValid will be true if all elements have "filters" property that is an array
651
+ if (!isValid) {
652
+ this.emit("error", {
653
+ type: "error",
654
+ error: "Filters is not a valid JSON - missing filters array",
655
+ });
656
+ return;
657
+ }
476
658
  return this.com.sendMessageToMap({
477
659
  type: "setFilters",
478
660
  filters: filtersJson,
479
661
  sessionToken: this.sessionToken,
480
662
  });
481
663
  } else {
482
- this.emit("error", { error: "No filters" });
664
+ this.emit("error", { type: "error", error: "No filters" });
483
665
  return;
484
666
  }
485
667
  };
@@ -492,7 +674,7 @@ class Communicator extends EventEmitter {
492
674
  sessionToken: this.sessionToken,
493
675
  });
494
676
  } else {
495
- this.emit("error", { error: "No layer_name" });
677
+ this.emit("error", { type: "error", error: "No layer_name" });
496
678
  return;
497
679
  }
498
680
  };
@@ -596,6 +778,64 @@ class Communicator extends EventEmitter {
596
778
  sessionToken: this.sessionToken,
597
779
  });
598
780
  };
781
+
782
+ setBboxSize = (bbox) => {
783
+ if (!isNaN(parseInt(bbox))) {
784
+ this.com.sendMessageToMap({
785
+ type: "setBoundingBoxSize",
786
+ bbox: bbox,
787
+ sessionToken: this.sessionToken,
788
+ });
789
+ } else {
790
+ console.error("bbox is not a integer");
791
+ }
792
+ };
793
+ addIcon = ({ icon, coordinates }) => {
794
+ // Validate icon
795
+ if (!(icon instanceof ArrayBuffer)) {
796
+ this.emit("error", {
797
+ type: "error",
798
+ error: "Invalid icon: Expected an ArrayBuffer.",
799
+ });
800
+ }
801
+
802
+ // Validate coordinates
803
+ if (!Array.isArray(coordinates)) {
804
+ this.emit("error", {
805
+ type: "error",
806
+ error: "Invalid coordinates: Expected an array [longitude, latitude].",
807
+ });
808
+ }
809
+
810
+ // Ensure coordinates contain exactly two numeric values
811
+ if (
812
+ coordinates.length !== 2 ||
813
+ typeof coordinates[0] !== "number" ||
814
+ typeof coordinates[1] !== "number"
815
+ ) {
816
+ this.emit("error", {
817
+ type: "error",
818
+ error:
819
+ "Invalid coordinates: Expected an array with two numeric values [longitude, latitude].",
820
+ });
821
+ }
822
+
823
+ // Send the message to the map
824
+ this.com.sendMessageToMap({
825
+ type: "AddIcon",
826
+ icon,
827
+ coordinates,
828
+ sessionToken: this.sessionToken,
829
+ });
830
+ };
831
+
832
+ screenshot = (options) => {
833
+ this.com.sendMessageToMap({
834
+ type: "screenshot",
835
+ options,
836
+ sessionToken: this.sessionToken,
837
+ });
838
+ };
599
839
  }
600
840
 
601
841
  export { Communicator };