@panoramax/web-viewer 3.2.3 → 4.0.0-develop-9f9cf858

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 (255) hide show
  1. package/.gitlab-ci.yml +13 -6
  2. package/CHANGELOG.md +56 -1
  3. package/CODE_OF_CONDUCT.md +1 -1
  4. package/README.md +1 -1
  5. package/build/editor.html +10 -1
  6. package/build/index.css +12 -12
  7. package/build/index.css.map +1 -1
  8. package/build/index.html +1 -1
  9. package/build/index.js +2126 -14
  10. package/build/index.js.map +1 -1
  11. package/build/map.html +1 -1
  12. package/build/photo.html +1 -0
  13. package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff +0 -0
  14. package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff2 +0 -0
  15. package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff +0 -0
  16. package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff2 +0 -0
  17. package/build/viewer.html +12 -1
  18. package/build/widgets.html +1 -0
  19. package/config/jest/mocks.js +201 -0
  20. package/config/paths.js +2 -0
  21. package/config/webpack.config.js +52 -0
  22. package/docs/03_URL_settings.md +14 -16
  23. package/docs/05_Compatibility.md +59 -76
  24. package/docs/09_Develop.md +46 -11
  25. package/docs/90_Releases.md +2 -2
  26. package/docs/images/class_diagram.drawio +60 -45
  27. package/docs/images/class_diagram.jpg +0 -0
  28. package/docs/images/screenshot.jpg +0 -0
  29. package/docs/index.md +135 -0
  30. package/docs/reference/components/core/Basic.md +196 -0
  31. package/docs/reference/components/core/CoverageMap.md +210 -0
  32. package/docs/reference/components/core/Editor.md +224 -0
  33. package/docs/reference/components/core/PhotoViewer.md +307 -0
  34. package/docs/reference/components/core/Viewer.md +350 -0
  35. package/docs/reference/components/layout/BottomDrawer.md +35 -0
  36. package/docs/reference/components/layout/CorneredGrid.md +29 -0
  37. package/docs/reference/components/layout/Mini.md +45 -0
  38. package/docs/reference/components/layout/Tabs.md +45 -0
  39. package/docs/reference/components/menus/MapBackground.md +32 -0
  40. package/docs/reference/components/menus/MapFilters.md +15 -0
  41. package/docs/reference/components/menus/MapLayers.md +15 -0
  42. package/docs/reference/components/menus/MapLegend.md +15 -0
  43. package/docs/reference/components/menus/PictureLegend.md +16 -0
  44. package/docs/reference/components/menus/PictureMetadata.md +15 -0
  45. package/docs/reference/components/menus/PlayerOptions.md +15 -0
  46. package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
  47. package/docs/reference/components/menus/ReportForm.md +15 -0
  48. package/docs/reference/components/menus/ShareMenu.md +15 -0
  49. package/docs/reference/components/ui/Button.md +40 -0
  50. package/docs/reference/components/ui/ButtonGroup.md +36 -0
  51. package/docs/reference/components/ui/CopyButton.md +38 -0
  52. package/docs/reference/components/ui/Grade.md +32 -0
  53. package/docs/reference/components/ui/LinkButton.md +45 -0
  54. package/docs/reference/components/ui/ListGroup.md +22 -0
  55. package/docs/reference/components/ui/Loader.md +56 -0
  56. package/docs/reference/components/ui/Map.md +239 -0
  57. package/docs/reference/components/ui/MapMore.md +256 -0
  58. package/docs/reference/components/ui/Photo.md +385 -0
  59. package/docs/reference/components/ui/Popup.md +56 -0
  60. package/docs/reference/components/ui/ProgressBar.md +32 -0
  61. package/docs/reference/components/ui/QualityScore.md +45 -0
  62. package/docs/reference/components/ui/SearchBar.md +63 -0
  63. package/docs/reference/components/ui/TogglableGroup.md +39 -0
  64. package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
  65. package/docs/reference/components/ui/widgets/Legend.md +49 -0
  66. package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
  67. package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
  68. package/docs/reference/components/ui/widgets/OSMEditors.md +15 -0
  69. package/docs/reference/components/ui/widgets/PictureLegendActions.md +32 -0
  70. package/docs/reference/components/ui/widgets/Player.md +33 -0
  71. package/docs/reference/components/ui/widgets/Zoom.md +15 -0
  72. package/docs/reference/utils/API.md +334 -0
  73. package/docs/reference/utils/InitParameters.md +68 -0
  74. package/docs/reference/utils/URLHandler.md +107 -0
  75. package/docs/reference.md +79 -0
  76. package/docs/shortcuts.md +11 -0
  77. package/docs/tutorials/aerial_imagery.md +19 -0
  78. package/docs/tutorials/authentication.md +10 -0
  79. package/docs/tutorials/custom_widgets.md +59 -0
  80. package/docs/tutorials/map_style.md +39 -0
  81. package/docs/tutorials/migrate_v4.md +153 -0
  82. package/docs/tutorials/synced_coverage.md +43 -0
  83. package/mkdocs.yml +66 -5
  84. package/package.json +22 -17
  85. package/public/editor.html +21 -29
  86. package/public/index.html +17 -12
  87. package/public/map.html +19 -18
  88. package/public/photo.html +55 -0
  89. package/public/viewer.html +22 -26
  90. package/public/widgets.html +306 -0
  91. package/scripts/doc.js +79 -0
  92. package/src/components/core/Basic.css +48 -0
  93. package/src/components/core/Basic.js +349 -0
  94. package/src/components/core/CoverageMap.css +9 -0
  95. package/src/components/core/CoverageMap.js +139 -0
  96. package/src/components/core/Editor.css +23 -0
  97. package/src/components/core/Editor.js +390 -0
  98. package/src/components/core/PhotoViewer.css +48 -0
  99. package/src/components/core/PhotoViewer.js +499 -0
  100. package/src/components/core/Viewer.css +98 -0
  101. package/src/components/core/Viewer.js +564 -0
  102. package/src/components/core/index.js +12 -0
  103. package/src/components/index.js +13 -0
  104. package/src/components/layout/BottomDrawer.js +257 -0
  105. package/src/components/layout/CorneredGrid.js +112 -0
  106. package/src/components/layout/Mini.js +117 -0
  107. package/src/components/layout/Tabs.js +133 -0
  108. package/src/components/layout/index.js +9 -0
  109. package/src/components/menus/MapBackground.js +106 -0
  110. package/src/components/menus/MapFilters.js +400 -0
  111. package/src/components/menus/MapLayers.js +143 -0
  112. package/src/components/menus/MapLegend.js +34 -0
  113. package/src/components/menus/PictureLegend.js +257 -0
  114. package/src/components/menus/PictureMetadata.js +317 -0
  115. package/src/components/menus/PlayerOptions.js +95 -0
  116. package/src/components/menus/QualityScoreDoc.js +36 -0
  117. package/src/components/menus/ReportForm.js +133 -0
  118. package/src/components/menus/Share.js +100 -0
  119. package/src/components/menus/index.js +15 -0
  120. package/src/components/styles.js +383 -0
  121. package/src/components/ui/Button.js +77 -0
  122. package/src/components/ui/ButtonGroup.css +57 -0
  123. package/src/components/ui/ButtonGroup.js +68 -0
  124. package/src/components/ui/CopyButton.js +106 -0
  125. package/src/components/ui/Grade.js +54 -0
  126. package/src/components/ui/LinkButton.js +67 -0
  127. package/src/components/ui/ListGroup.js +66 -0
  128. package/src/components/ui/Loader.js +203 -0
  129. package/src/components/{Map.css → ui/Map.css} +5 -17
  130. package/src/components/{Map.js → ui/Map.js} +148 -156
  131. package/src/components/ui/MapMore.js +324 -0
  132. package/src/components/{Photo.css → ui/Photo.css} +6 -6
  133. package/src/components/{Photo.js → ui/Photo.js} +313 -101
  134. package/src/components/ui/Popup.js +145 -0
  135. package/src/components/ui/ProgressBar.js +104 -0
  136. package/src/components/ui/QualityScore.js +147 -0
  137. package/src/components/ui/SearchBar.js +367 -0
  138. package/src/components/ui/TogglableGroup.js +157 -0
  139. package/src/components/ui/index.js +22 -0
  140. package/src/components/ui/widgets/GeoSearch.css +21 -0
  141. package/src/components/ui/widgets/GeoSearch.js +139 -0
  142. package/src/components/ui/widgets/Legend.js +113 -0
  143. package/src/components/ui/widgets/MapFiltersButton.js +104 -0
  144. package/src/components/ui/widgets/MapLayersButton.js +79 -0
  145. package/src/components/ui/widgets/OSMEditors.js +155 -0
  146. package/src/components/ui/widgets/PictureLegendActions.js +117 -0
  147. package/src/components/ui/widgets/Player.css +7 -0
  148. package/src/components/ui/widgets/Player.js +151 -0
  149. package/src/components/ui/widgets/Zoom.js +82 -0
  150. package/src/components/ui/widgets/index.js +13 -0
  151. package/src/img/loader_base.jpg +0 -0
  152. package/src/img/panoramax.svg +13 -0
  153. package/src/img/switch_big.svg +20 -10
  154. package/src/index.js +7 -9
  155. package/src/translations/br.json +1 -0
  156. package/src/translations/da.json +38 -15
  157. package/src/translations/de.json +5 -3
  158. package/src/translations/en.json +35 -15
  159. package/src/translations/eo.json +38 -15
  160. package/src/translations/es.json +1 -1
  161. package/src/translations/fr.json +36 -16
  162. package/src/translations/hu.json +1 -1
  163. package/src/translations/it.json +39 -16
  164. package/src/translations/ja.json +182 -1
  165. package/src/translations/nl.json +106 -6
  166. package/src/translations/pl.json +1 -1
  167. package/src/translations/sv.json +182 -0
  168. package/src/translations/zh_Hant.json +35 -14
  169. package/src/utils/API.js +109 -49
  170. package/src/utils/InitParameters.js +388 -0
  171. package/src/utils/PhotoAdapter.js +1 -0
  172. package/src/utils/URLHandler.js +362 -0
  173. package/src/utils/geocoder.js +152 -0
  174. package/src/utils/{I18n.js → i18n.js} +7 -3
  175. package/src/utils/index.js +11 -0
  176. package/src/utils/{Map.js → map.js} +256 -77
  177. package/src/utils/picture.js +442 -0
  178. package/src/utils/utils.js +324 -0
  179. package/src/utils/widgets.js +55 -0
  180. package/tests/components/core/Basic.test.js +121 -0
  181. package/tests/components/core/BasicMock.js +25 -0
  182. package/tests/components/core/CoverageMap.test.js +20 -0
  183. package/tests/components/core/Editor.test.js +20 -0
  184. package/tests/components/core/PhotoViewer.test.js +57 -0
  185. package/tests/components/core/Viewer.test.js +84 -0
  186. package/tests/components/core/__snapshots__/PhotoViewer.test.js.snap +73 -0
  187. package/tests/components/core/__snapshots__/Viewer.test.js.snap +145 -0
  188. package/tests/components/ui/CopyButton.test.js +52 -0
  189. package/tests/components/ui/Loader.test.js +55 -0
  190. package/tests/components/{Map.test.js → ui/Map.test.js} +73 -61
  191. package/tests/components/{Photo.test.js → ui/Photo.test.js} +97 -63
  192. package/tests/components/ui/Popup.test.js +26 -0
  193. package/tests/components/ui/QualityScore.test.js +18 -0
  194. package/tests/components/ui/SearchBar.test.js +110 -0
  195. package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +33 -0
  196. package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
  197. package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
  198. package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +70 -6
  199. package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
  200. package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
  201. package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
  202. package/tests/utils/API.test.js +83 -83
  203. package/tests/utils/InitParameters.test.js +499 -0
  204. package/tests/utils/URLHandler.test.js +401 -0
  205. package/tests/utils/__snapshots__/API.test.js.snap +10 -0
  206. package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
  207. package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +1 -1
  208. package/tests/utils/__snapshots__/map.test.js.snap +11 -0
  209. package/tests/utils/__snapshots__/picture.test.js.snap +327 -0
  210. package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
  211. package/tests/utils/geocoder.test.js +37 -0
  212. package/tests/utils/{I18n.test.js → i18n.test.js} +8 -8
  213. package/tests/utils/map.test.js +126 -0
  214. package/tests/utils/picture.test.js +745 -0
  215. package/tests/utils/utils.test.js +288 -0
  216. package/tests/utils/widgets.test.js +31 -0
  217. package/docs/01_Start.md +0 -149
  218. package/docs/02_Usage.md +0 -831
  219. package/docs/04_Advanced_examples.md +0 -216
  220. package/src/Editor.css +0 -37
  221. package/src/Editor.js +0 -361
  222. package/src/StandaloneMap.js +0 -114
  223. package/src/Viewer.css +0 -203
  224. package/src/Viewer.js +0 -1246
  225. package/src/components/CoreView.css +0 -70
  226. package/src/components/CoreView.js +0 -175
  227. package/src/components/Loader.css +0 -74
  228. package/src/components/Loader.js +0 -120
  229. package/src/img/loader_hd.jpg +0 -0
  230. package/src/utils/Exif.js +0 -193
  231. package/src/utils/Utils.js +0 -631
  232. package/src/utils/Widgets.js +0 -562
  233. package/src/viewer/URLHash.js +0 -469
  234. package/src/viewer/Widgets.css +0 -880
  235. package/src/viewer/Widgets.js +0 -1470
  236. package/tests/Editor.test.js +0 -126
  237. package/tests/StandaloneMap.test.js +0 -45
  238. package/tests/Viewer.test.js +0 -366
  239. package/tests/__snapshots__/Editor.test.js.snap +0 -298
  240. package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
  241. package/tests/__snapshots__/Viewer.test.js.snap +0 -195
  242. package/tests/components/CoreView.test.js +0 -92
  243. package/tests/components/Loader.test.js +0 -38
  244. package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
  245. package/tests/utils/Exif.test.js +0 -124
  246. package/tests/utils/Map.test.js +0 -113
  247. package/tests/utils/Utils.test.js +0 -300
  248. package/tests/utils/Widgets.test.js +0 -107
  249. package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
  250. package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
  251. package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
  252. package/tests/viewer/URLHash.test.js +0 -559
  253. package/tests/viewer/Widgets.test.js +0 -127
  254. package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
  255. package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
@@ -1,14 +1,13 @@
1
1
  import "./Map.css";
2
2
  import {
3
- forwardGeocodingBAN, forwardGeocodingNominatim, VECTOR_STYLES,
4
- TILES_PICTURES_ZOOM, getThumbGif, RASTER_LAYER_ID, combineStyles,
5
- getMissingLayerStyles, isLabelLayer, getUserLayerId, getUserSourceId, switchCoefValue,
6
- } from "../utils/Map";
7
- import { COLORS } from "../utils/Utils";
8
- import MarkerBaseSVG from "../img/marker.svg";
9
- import MarkerSelectedSVG from "../img/marker_blue.svg";
10
- import ArrowFlatSVG from "../img/arrow_flat.svg";
11
- import Arrow360SVG from "../img/arrow_360.svg";
3
+ VECTOR_STYLES, TILES_PICTURES_ZOOM, getThumbGif, RASTER_LAYER_ID, combineStyles,
4
+ getMissingLayerStyles, isLabelLayer, getUserLayerId, getUserSourceId,
5
+ } from "../../utils/map";
6
+ import { COLORS } from "../../utils/utils";
7
+ import MarkerBaseSVG from "../../img/marker.svg";
8
+ import MarkerSelectedSVG from "../../img/marker_blue.svg";
9
+ import ArrowFlatSVG from "../../img/arrow_flat.svg";
10
+ import Arrow360SVG from "../../img/arrow_360.svg";
12
11
 
13
12
  // MapLibre imports
14
13
  import "maplibre-gl/dist/maplibre-gl.css";
@@ -18,33 +17,34 @@ import * as pmtiles from "pmtiles";
18
17
  maplibregl.workerClass = maplibreglWorker;
19
18
  maplibregl.addProtocol("pmtiles", new pmtiles.Protocol().tile);
20
19
 
21
- const MAPLIBRE_OPTIONS = [ // No "style" option as it's handled by combineStyles function
22
- "antialias", "attributionControl", "bearing", "bearingSnap", "bounds",
23
- "boxZoom", "center", "clickTolerance", "collectResourceTiming",
24
- "cooperativeGestures", "crossSourceCollisions", "doubleClickZoom", "dragPan",
25
- "dragRotate", "fadeDuration", "failIfMajorPerformanceCaveat", "fitBoundsOptions",
26
- "hash", "interactive", "keyboard", "localIdeographFontFamily", "locale", "logoPosition",
27
- "maplibreLogo", "maxBounds", "maxCanvasSize", "maxPitch", "maxTileCacheSize",
28
- "maxTileCacheZoomLevels", "maxZoom", "minPitch", "minZoom", "pitch", "pitchWithRotate",
29
- "pixelRatio", "preserveDrawingBuffer", "refreshExpiredTiles", "renderWorldCopies",
30
- "scrollZoom", "touchPitch", "touchZoomRotate", "trackResize",
31
- "transformCameraUpdate", "transformRequest", "validateStyle", "zoom"
32
- ];
33
- const filterMapLibreOptions = opts => Object.fromEntries(Object.entries(opts).filter(([key]) => MAPLIBRE_OPTIONS.includes(key)));
34
-
35
20
 
36
21
  /**
37
22
  * Map is the component showing pictures and sequences geolocation.
38
23
  *
39
24
  * Note that all functions of [MapLibre GL JS class Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/) are also available.
40
25
  *
41
- * @param {CoreView} parent The parent view
26
+ * A more complete version of Map (with filters & themes) is available through [MapMore class](#Panoramax.components.ui.MapMore)
27
+ *
28
+ * ⚠️ This class doesn't inherit from [EventTarget](https://developer.mozilla.org/fr/docs/Web/API/EventTarget), so it doesn't have `addEventListener` and `dispatchEvent` functions.
29
+ * It uses instead [`on`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#on) and `fire` functions from MapLibre Map class.
30
+ * `fire` function doesn't take directly [`Event`](https://developer.mozilla.org/fr/docs/Web/API/Event) objects, but a string and object data.
31
+ * A shorthand `addEventListener` function is added for simpler usage.
32
+ * @class Panoramax.components.ui.Map
33
+ * @extends [maplibregl.Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/)
34
+ * @param {Panoramax.components.core.Basic} parent The parent view
42
35
  * @param {Element} container The DOM element to create into
43
- * @param {object} [options] The map options (any of [MapLibre GL settings](https://maplibre.org/maplibre-gl-js-docs/api/map/#map-parameters) or any supplementary option defined here)
36
+ * @param {object} [options] The map options (any of [MapLibre GL settings](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions/) or any supplementary option defined here)
44
37
  * @param {object} [options.raster] The MapLibre raster source for aerial background. This must be a JSON object following [MapLibre raster source definition](https://maplibre.org/maplibre-style-spec/sources/#raster).
45
- * @param {string} [options.background] Choose default map background to display (streets or aerial, if raster aerial background available). Defaults to street.
46
- * @param {object} [options.geocoder] Optional geocoder settings
47
- * @param {string} [options.geocoder.engine] Set the geocoder engine to use (nominatim, ban)
38
+ * @param {string} [options.background=streets] Choose default map background to display (streets or aerial, if raster aerial background available). Defaults to street.
39
+ * @param {string} [options.attributionControl.customAttribution] To override default map attribution.
40
+ * @fires Panoramax.components.ui.Map#background-changed
41
+ * @fires Panoramax.components.ui.Map#users-changed
42
+ * @fires Panoramax.components.ui.Map#sequence-hover
43
+ * @fires Panoramax.components.ui.Map#sequence-click
44
+ * @fires Panoramax.components.ui.Map#picture-click
45
+ * @fires Panoramax.components.ui.Map#ready
46
+ * @example
47
+ * const map = new Panoramax.components.ui.Map(viewer, mapNode, {center: {lat: 48.7, lng: -1.7}});
48
48
  */
49
49
  export default class Map extends maplibregl.Map {
50
50
  constructor(parent, container, options = {}) {
@@ -59,16 +59,20 @@ export default class Map extends maplibregl.Map {
59
59
  pitchWithRotate: false,
60
60
  touchZoomRotate: true,
61
61
  touchPitch: false,
62
- preserveDrawingBuffer: !parent.isWidthSmall(),
63
- transformRequest: parent._api._getMapRequestTransform(),
62
+ doubleClickZoom: false,
63
+ canvasContextAttributes: {
64
+ preserveDrawingBuffer: !parent.isWidthSmall(),
65
+ },
66
+ transformRequest: parent.api._getMapRequestTransform(),
64
67
  locale: parent._t.maplibre,
65
- ...filterMapLibreOptions(options)
68
+ hash: false,
69
+ ...options
66
70
  });
67
71
  this._loadMarkerImages();
68
72
 
69
73
  this._parent = parent;
70
74
  this._options = options;
71
- this.getContainer().classList.add("gvs-map");
75
+ this.getContainer().classList.add("pnx-map");
72
76
 
73
77
  // Disable touch rotate
74
78
  if(options.touchZoomRotate === undefined) {
@@ -80,10 +84,9 @@ export default class Map extends maplibregl.Map {
80
84
  this._options.background = this._options.background || "streets";
81
85
  }
82
86
 
83
- this._attribution = new maplibregl.AttributionControl({ compact: false });
87
+ this._attribution = new maplibregl.AttributionControl({ compact: false, ...options.attributionControl });
84
88
  this.addControl(this._attribution);
85
89
 
86
- this._initGeocoder();
87
90
  this._initMapPosition();
88
91
 
89
92
  // Widgets and markers
@@ -97,16 +100,16 @@ export default class Map extends maplibregl.Map {
97
100
  // Sequences and pictures per users
98
101
  this._userLayers = new Set();
99
102
 
100
- // Hover event
101
- this.on("mousemove", "sequences", this._onSequenceHover.bind(this));
102
-
103
103
  // Parent selection
104
104
  this._parent.addEventListener("select", this.reloadLayersStyles.bind(this));
105
105
 
106
106
  // Timeout for initial loading
107
107
  setTimeout(() => {
108
- if(!this.loaded() && this._parent._loader.isVisible()) {
109
- this._parent._loader.dismiss({}, this._parent._t.map.slow_loading, () => {});
108
+ if(!this.loaded() && this._parent?.loader.isVisible()) {
109
+ this._parent.loader.dismiss({}, this._parent._t.map.slow_loading, async () => {
110
+ await this._postLoad();
111
+ this._parent.loader.dismiss();
112
+ });
110
113
  }
111
114
  }, 15000);
112
115
 
@@ -118,12 +121,21 @@ export default class Map extends maplibregl.Map {
118
121
  */
119
122
  async _postLoad() {
120
123
  this.resize();
121
- await this.setVisibleUsers(this._parent._options.users);
124
+ await this.setVisibleUsers(this._parent.users);
122
125
  this.reloadLayersStyles();
126
+
127
+ /**
128
+ * Event when map is ready to display.
129
+ * This includes Maplibre initial load, enough map data display and styling.
130
+ * @event Panoramax.components.ui.Map#ready
131
+ * @type {maplibregl.util.evented.Event}
132
+ */
133
+ this.fire("ready");
123
134
  }
124
135
 
125
136
  /**
126
137
  * Destroy any form of life in this component
138
+ * @memberof Panoramax.components.ui.Map#
127
139
  */
128
140
  destroy() {
129
141
  this.remove();
@@ -140,6 +152,7 @@ export default class Map extends maplibregl.Map {
140
152
  /**
141
153
  * Helper to know when enough map background and Panoramax tiles are loaded for a proper display.
142
154
  * @returns {Promise} Resolves when enough is loaded
155
+ * @memberof Panoramax.components.ui.Map#
143
156
  */
144
157
  waitForEnoughMapLoaded() {
145
158
  return new Promise((resolve) => {
@@ -173,7 +186,7 @@ export default class Map extends maplibregl.Map {
173
186
  };
174
187
 
175
188
  const checkEnoughLoaded = () => {
176
- if(nbLoadedBgTiles / nbBgTiles >= 0.75 && nbLoadedFgTiles / nbFgTiles >= 0.75) {
189
+ if(nbLoadedBgTiles / nbBgTiles >= 0.75 && (nbFgTiles === 0 || nbLoadedFgTiles / nbFgTiles >= 0.75)) {
177
190
  this.off("sourcedata", onSourceData);
178
191
  this.off("sourcedataloading", onSourceDataLoading);
179
192
  resolve();
@@ -188,6 +201,7 @@ export default class Map extends maplibregl.Map {
188
201
  /**
189
202
  * Sets map view based on returned API bbox (if no precise option given by user).
190
203
  * @private
204
+ * @memberof Panoramax.components.ui.Map#
191
205
  */
192
206
  _initMapPosition() {
193
207
  if(
@@ -195,8 +209,8 @@ export default class Map extends maplibregl.Map {
195
209
  && (!this._options.zoom || this._options.zoom === 0)
196
210
  && (!this._options.hash)
197
211
  ) {
198
- this._parent._api.onceReady().then(() => {
199
- let bbox = this._parent?._api?.getDataBbox();
212
+ this._parent.onceAPIReady().then(() => {
213
+ let bbox = this._parent?.api?.getDataBbox();
200
214
  if(bbox) {
201
215
  try {
202
216
  bbox = new maplibregl.LngLatBounds(bbox);
@@ -211,35 +225,16 @@ export default class Map extends maplibregl.Map {
211
225
  }
212
226
  }
213
227
 
214
- /**
215
- * Creates the geocoder search bar
216
- * @private
217
- */
218
- _initGeocoder() {
219
- const engines = { "ban": forwardGeocodingBAN, "nominatim": forwardGeocodingNominatim };
220
- const engine = this._options?.geocoder?.engine || "nominatim";
221
- this.geocoder = engines[engine];
222
- this._geolocate = new maplibregl.GeolocateControl({
223
- positionOptions: {
224
- enableHighAccuracy: true,
225
- timeout: 60000, // Max 1 minute for first position
226
- maximumAge: 300000, // Accepts 5 minutes old position
227
- },
228
- showAccuracyCircle: true,
229
- showUserLocation: true,
230
- trackUserLocation: true,
231
- }).onAdd(this);
232
- }
233
-
234
228
  /**
235
229
  * Load markers into map for use in map layers.
236
230
  * @private
231
+ * @memberof Panoramax.components.ui.Map#
237
232
  */
238
233
  _loadMarkerImages() {
239
234
  [
240
- { id: "gvs-marker", img: MarkerBaseSVG },
241
- { id: "gvs-arrow-flat", img: ArrowFlatSVG },
242
- { id: "gvs-arrow-360", img: Arrow360SVG },
235
+ { id: "pnx-marker", img: MarkerBaseSVG },
236
+ { id: "pnx-arrow-flat", img: ArrowFlatSVG },
237
+ { id: "pnx-arrow-360", img: Arrow360SVG },
243
238
  ].forEach(m => {
244
239
  const img = new Image(64, 64);
245
240
  img.onload = () => this.addImage(m.id, img);
@@ -250,6 +245,7 @@ export default class Map extends maplibregl.Map {
250
245
  /**
251
246
  * Is Quality Score available in vector tiles ?
252
247
  * @private
248
+ * @memberof Panoramax.components.ui.Map#
253
249
  */
254
250
  _hasQualityScore() {
255
251
  const fields = this.getStyle()?.metadata?.["panoramax:fields"] || {};
@@ -259,6 +255,7 @@ export default class Map extends maplibregl.Map {
259
255
  /**
260
256
  * Are 360/flat pictures stats available in vector tiles for grid layer ?
261
257
  * @private
258
+ * @memberof Panoramax.components.ui.Map#
262
259
  */
263
260
  _hasGridStats() {
264
261
  const fields = this.getStyle()?.metadata?.["panoramax:fields"] || {};
@@ -268,6 +265,7 @@ export default class Map extends maplibregl.Map {
268
265
 
269
266
  /**
270
267
  * Force refresh of vector tiles data
268
+ * @memberof Panoramax.components.ui.Map#
271
269
  */
272
270
  reloadVectorTiles() {
273
271
  [...this._userLayers].forEach(dl => {
@@ -279,6 +277,7 @@ export default class Map extends maplibregl.Map {
279
277
  /**
280
278
  * Check if map offers aerial imagery as well as streets rendering.
281
279
  * @returns {boolean} True if aerial imagery is available for display
280
+ * @memberof Panoramax.components.ui.Map#
282
281
  */
283
282
  hasTwoBackgrounds() {
284
283
  return this.getLayer(RASTER_LAYER_ID) !== undefined;
@@ -287,6 +286,7 @@ export default class Map extends maplibregl.Map {
287
286
  /**
288
287
  * Get the currently selected map background
289
288
  * @returns {string} aerial or streets
289
+ * @memberof Panoramax.components.ui.Map#
290
290
  */
291
291
  getBackground() {
292
292
  if(!this.getLayer(RASTER_LAYER_ID)) {
@@ -300,6 +300,8 @@ export default class Map extends maplibregl.Map {
300
300
  /**
301
301
  * Change the shown background in map.
302
302
  * @param {string} bg The new background to display (aerial or streets)
303
+ * @memberof Panoramax.components.ui.Map#
304
+ * @throws {Error} If not aerial imagery is available
303
305
  */
304
306
  setBackground(bg) {
305
307
  if(!this.getLayer(RASTER_LAYER_ID) && bg === "aerial") { throw new Error("No aerial imagery available"); }
@@ -309,20 +311,18 @@ export default class Map extends maplibregl.Map {
309
311
  /**
310
312
  * Event for map background changes
311
313
  *
312
- * @event map:background-changed
313
- * @memberof CoreView
314
- * @type {object}
315
- * @property {object} detail Event information
316
- * @property {string} [detail.background] The new selected background (aerial, streets)
314
+ * @event Panoramax.components.ui.Map#background-changed
315
+ * @type {maplibregl.util.evented.Event}
316
+ * @property {string} [background] The new selected background (aerial, streets)
317
317
  */
318
- const event = new CustomEvent("map:background-changed", { detail: { background: bg || "streets" }});
319
- this._parent.dispatchEvent(event);
318
+ this.fire("background-changed", { background: bg || "streets" });
320
319
  }
321
320
  }
322
321
 
323
322
  /**
324
323
  * Get the currently visible users
325
324
  * @returns {string[]} List of visible users
325
+ * @memberof Panoramax.components.ui.Map#
326
326
  */
327
327
  getVisibleUsers() {
328
328
  return [...this._userLayers].filter(l => (
@@ -332,11 +332,12 @@ export default class Map extends maplibregl.Map {
332
332
 
333
333
  /**
334
334
  * Make given user layers visible on map, and hide all others (if any)
335
- *
335
+ * @memberof Panoramax.components.ui.Map#
336
336
  * @param {string|string[]} visibleIds The user layers IDs to display
337
337
  */
338
338
  async setVisibleUsers(visibleIds = []) {
339
339
  if(typeof visibleIds === "string") { visibleIds = [visibleIds]; }
340
+ else if(visibleIds === null) { visibleIds = []; }
340
341
 
341
342
  // Create any missing user layer
342
343
  await Promise.all(
@@ -359,20 +360,18 @@ export default class Map extends maplibregl.Map {
359
360
  /**
360
361
  * Event for visible users changes
361
362
  *
362
- * @event map:users-changed
363
- * @memberof CoreView
364
- * @type {object}
365
- * @property {object} detail Event information
366
- * @property {string[]} [detail.usersIds] The list of newly selected users
363
+ * @event Panoramax.components.ui.Map#users-changed
364
+ * @type {maplibregl.util.evented.Event}
365
+ * @property {string[]} [usersIds] The list of newly selected users
367
366
  */
368
- const event = new CustomEvent("map:users-changed", { detail: { usersIds: visibleIds }});
369
- this._parent.dispatchEvent(event);
367
+ this.fire("users-changed", { usersIds: visibleIds });
370
368
  }
371
369
 
372
370
  /**
373
371
  * Filter the visible data content in all visible map layers
374
372
  * @param {string} dataType sequences or pictures
375
373
  * @param {object} filter The MapLibre GL filter rule to apply
374
+ * @memberof Panoramax.components.ui.Map#
376
375
  */
377
376
  filterUserLayersContent(dataType, filter) {
378
377
  [...this._userLayers].forEach(l => {
@@ -388,12 +387,13 @@ export default class Map extends maplibregl.Map {
388
387
 
389
388
  /**
390
389
  * Shows on map a picture position and heading.
391
- *
390
+ * @memberof Panoramax.components.ui.Map#
392
391
  * @param {number} lon The longitude
393
392
  * @param {number} lat The latitude
394
393
  * @param {number} heading The heading
394
+ * @param {boolean} [skipCenter=false] Set to true to avoid map centering on marker
395
395
  */
396
- displayPictureMarker(lon, lat, heading) {
396
+ displayPictureMarker(lon, lat, heading, skipCenter = false) {
397
397
  this._picMarkerPreview.remove();
398
398
 
399
399
  // Show marker corresponding to selection
@@ -411,7 +411,7 @@ export default class Map extends maplibregl.Map {
411
411
  this.reloadLayersStyles();
412
412
 
413
413
  // Move map to picture coordinates
414
- if(lon !== undefined && lat !== undefined) {
414
+ if(!skipCenter && lon !== undefined && lat !== undefined) {
415
415
  this.flyTo({
416
416
  center: [lon, lat],
417
417
  zoom: this.getZoom() < TILES_PICTURES_ZOOM+2 ? TILES_PICTURES_ZOOM+2 : this.getZoom(),
@@ -423,6 +423,7 @@ export default class Map extends maplibregl.Map {
423
423
  /**
424
424
  * Forces reload of pictures/sequences layer styles.
425
425
  * This is useful after a map theme change.
426
+ * @memberof Panoramax.components.ui.Map#
426
427
  */
427
428
  reloadLayersStyles() {
428
429
  const updateStyle = (layer, style) => {
@@ -438,30 +439,12 @@ export default class Map extends maplibregl.Map {
438
439
  ["pictures", "pictures_symbols", "sequences"].forEach(l => {
439
440
  updateStyle(l, this._getLayerStyleProperties(l));
440
441
  });
441
-
442
- // Also handle the grid stats
443
- if(this._hasGridStats() && this._parent?._mapFilters) {
444
- let newType = "coef";
445
- if(this._parent._mapFilters?.type) {
446
- newType = this._parent._mapFilters.type == "flat" ? "coef_flat_pictures" : "coef_360_pictures";
447
- }
448
- this.getStyle().layers
449
- .filter(l => l.id.endsWith("_grid"))
450
- .forEach(l => {
451
- const newl = switchCoefValue(l, newType);
452
- for(let p in newl.layout) {
453
- this.setLayoutProperty(l.id, p, newl.layout[p]);
454
- }
455
- for(let p in newl.paint) {
456
- this.setPaintProperty(l.id, p, newl.paint[p]);
457
- }
458
- });
459
- }
460
442
  }
461
443
 
462
444
  /**
463
445
  * Creates source and layers for pictures and sequences.
464
446
  * @private
447
+ * @memberof Panoramax.components.ui.Map#
465
448
  * @param {string} id The source and layer ID prefix
466
449
  */
467
450
  async _createPicturesTilesLayer(id) {
@@ -470,7 +453,7 @@ export default class Map extends maplibregl.Map {
470
453
 
471
454
  // Load style from API
472
455
  if(id !== "geovisio" && !this.getSource(`geovisio_${id}`)) {
473
- const style = await this._parent._api.getUserMapStyle(id);
456
+ const style = await this._parent.api.getUserMapStyle(id);
474
457
  Object.entries(style.sources).forEach(([sId, s]) => this.addSource(sId, s));
475
458
  style.layers = style.layers || [];
476
459
  const layers = style.layers.concat(getMissingLayerStyles(style.sources, style.layers));
@@ -511,6 +494,7 @@ export default class Map extends maplibregl.Map {
511
494
  // Sequences
512
495
  const seqPlusLayerId = getUserLayerId(id, "sequences_plus");
513
496
  this.on("mousemove", seqPlusLayerId, e => {
497
+ this._onSequenceHover(e);
514
498
  if(this.getZoom() <= TILES_PICTURES_ZOOM+1) {
515
499
  this.getCanvas().style.cursor = "pointer";
516
500
  if(e.features[0].properties.id) {
@@ -580,6 +564,7 @@ export default class Map extends maplibregl.Map {
580
564
  *
581
565
  * @returns {object} Paint/layout properties
582
566
  * @private
567
+ * @memberof Panoramax.components.ui.Map#
583
568
  */
584
569
  _getLayerStyleProperties(layer) {
585
570
  if(layer === "pictures_symbols") {
@@ -587,9 +572,9 @@ export default class Map extends maplibregl.Map {
587
572
  "paint": {},
588
573
  "layout": {
589
574
  "icon-image": ["case",
590
- ["==", ["get", "id"], this._parent._selectedPicId], "",
591
- ["==", ["get", "type"], "equirectangular"], "gvs-arrow-360",
592
- "gvs-arrow-flat"
575
+ ["==", ["get", "id"], this._parent.picture], "",
576
+ ["==", ["get", "type"], "equirectangular"], "pnx-arrow-360",
577
+ "pnx-arrow-flat"
593
578
  ],
594
579
  "symbol-sort-key": this._getLayerSortStyle(layer),
595
580
  },
@@ -614,6 +599,7 @@ export default class Map extends maplibregl.Map {
614
599
  /**
615
600
  * Retrieve map layer color scheme according to selected theme.
616
601
  * @private
602
+ * @memberof Panoramax.components.ui.Map#
617
603
  */
618
604
  _getLayerColorStyle(layer) {
619
605
  // Hidden style
@@ -623,7 +609,7 @@ export default class Map extends maplibregl.Map {
623
609
  ];
624
610
 
625
611
  // Selected sequence style
626
- const seqId = this._parent._selectedSeqId;
612
+ const seqId = this._parent.sequence;
627
613
  if(layer == "sequences" && seqId) {
628
614
  s.push(["==", ["get", "id"], seqId], COLORS.SELECTED);
629
615
  }
@@ -640,6 +626,7 @@ export default class Map extends maplibregl.Map {
640
626
  /**
641
627
  * Retrieve map sort key according to selected theme.
642
628
  * @private
629
+ * @memberof Panoramax.components.ui.Map#
643
630
  */
644
631
  _getLayerSortStyle(layer) {
645
632
  // Values
@@ -654,7 +641,7 @@ export default class Map extends maplibregl.Map {
654
641
  ];
655
642
 
656
643
  // Selected sequence style
657
- const seqId = this._parent._selectedSeqId;
644
+ const seqId = this._parent.sequence;
658
645
  if(layer == "sequences" && seqId) {
659
646
  s.push(["==", ["get", "id"], seqId], 100);
660
647
  }
@@ -671,6 +658,7 @@ export default class Map extends maplibregl.Map {
671
658
  * @private
672
659
  * @param {object} e The event thrown by MapLibre
673
660
  * @param {string} from The event source layer
661
+ * @memberof Panoramax.components.ui.Map#
674
662
  */
675
663
  _attachPreviewToPictures(e, from) {
676
664
  let f = e.features.pop();
@@ -720,7 +708,7 @@ export default class Map extends maplibregl.Map {
720
708
 
721
709
  if(thumbUrl) {
722
710
  let content = document.createElement("img");
723
- content.classList.add("gvs-map-thumb");
711
+ content.classList.add("pnx-map-thumb");
724
712
  content.alt = this._parent._t.map.thumbnail;
725
713
  let img = new Image();
726
714
  img.src = thumbUrl;
@@ -737,7 +725,7 @@ export default class Map extends maplibregl.Map {
737
725
 
738
726
  if(f.properties.hidden) {
739
727
  const legend = document.createElement("div");
740
- legend.classList.add("gvs-map-thumb-legend");
728
+ legend.classList.add("pnx-map-thumb-legend");
741
729
  legend.appendChild(document.createTextNode(this._parent._t.map.not_public));
742
730
  const container = document.createElement("div");
743
731
  container.appendChild(content);
@@ -772,11 +760,12 @@ export default class Map extends maplibregl.Map {
772
760
  * @param {LngLat} coordinates The map coordinates
773
761
  * @returns {Promise} Promise resolving on picture thumbnail URL, or null on timeout
774
762
  * @private
763
+ * @memberof Panoramax.components.ui.Map#
775
764
  */
776
765
  _getThumbURL(coordinates) {
777
- return this._parent._api.getPicturesAroundCoordinates(coordinates[1], coordinates[0], 0.1, 1).then(res => {
766
+ return this._parent.api.getPicturesAroundCoordinates(coordinates[1], coordinates[0], 0.1, 1).then(res => {
778
767
  const p = res?.features?.pop();
779
- return p ? this._parent._api.findThumbnailInPictureFeature(p) : null;
768
+ return p ? this._parent.api.findThumbnailInPictureFeature(p) : null;
780
769
  });
781
770
  }
782
771
 
@@ -788,21 +777,22 @@ export default class Map extends maplibregl.Map {
788
777
  * @param {LngLat} [coordinates] The map coordinates
789
778
  * @returns {Promise} Promise resolving on picture thumbnail URL, or null on timeout
790
779
  * @private
780
+ * @memberof Panoramax.components.ui.Map#
791
781
  */
792
782
  _getSequenceThumbURL(seqId, coordinates) {
793
783
  if(coordinates) {
794
- return this._parent._api.getPicturesAroundCoordinates(coordinates.lat, coordinates.lng, 1, 1, seqId)
784
+ return this._parent.api.getPicturesAroundCoordinates(coordinates.lat, coordinates.lng, 1, 1, seqId)
795
785
  .then(results => {
796
786
  if(results?.features?.length > 0) {
797
- return this._parent._api.findThumbnailInPictureFeature(results.features[0]);
787
+ return this._parent.api.findThumbnailInPictureFeature(results.features[0]);
798
788
  }
799
789
  else {
800
- return this._parent._api.getPictureThumbnailURLForSequence(seqId);
790
+ return this._parent.api.getPictureThumbnailURLForSequence(seqId);
801
791
  }
802
792
  });
803
793
  }
804
794
  else {
805
- return this._parent._api.getPictureThumbnailURLForSequence(seqId);
795
+ return this._parent.api.getPictureThumbnailURLForSequence(seqId);
806
796
  }
807
797
  }
808
798
 
@@ -813,7 +803,7 @@ export default class Map extends maplibregl.Map {
813
803
  * @param {string} picId The picture ID
814
804
  * @param {string} [seqId] The sequence ID (can speed up search if available)
815
805
  * @returns {Promise} Promise resolving on picture thumbnail URL, or null on timeout
816
- *
806
+ * @memberof Panoramax.components.ui.Map#
817
807
  * @private
818
808
  */
819
809
  _getPictureThumbURL(picId, seqId) {
@@ -824,7 +814,7 @@ export default class Map extends maplibregl.Map {
824
814
  res = typeof this._picThumbUrl[picId] === "string" ? Promise.resolve(this._picThumbUrl[picId]) : this._picThumbUrl[picId];
825
815
  }
826
816
  else {
827
- this._picThumbUrl[picId] = this._parent._api.getPictureThumbnailURL(picId, seqId).then(url => {
817
+ this._picThumbUrl[picId] = this._parent.api.getPictureThumbnailURL(picId, seqId).then(url => {
828
818
  if(url) {
829
819
  this._picThumbUrl[picId] = url;
830
820
  return url;
@@ -849,6 +839,7 @@ export default class Map extends maplibregl.Map {
849
839
  *
850
840
  * @returns {maplibregl.Marker} The generated marker
851
841
  * @private
842
+ * @memberof Panoramax.components.ui.Map#
852
843
  */
853
844
  _getPictureMarker(selected = true) {
854
845
  const img = document.createElement("img");
@@ -863,6 +854,7 @@ export default class Map extends maplibregl.Map {
863
854
  * Event handler for sequence hover
864
855
  * @private
865
856
  * @param {object} e Event data
857
+ * @memberof Panoramax.components.ui.Map#
866
858
  */
867
859
  _onSequenceHover(e) {
868
860
  e.preventDefault();
@@ -870,17 +862,11 @@ export default class Map extends maplibregl.Map {
870
862
  /**
871
863
  * Event when a sequence on map is hovered (not selected)
872
864
  *
873
- * @event map:sequence-hover
874
- * @memberof CoreView
875
- * @type {object}
876
- * @property {object} detail Event information
877
- * @property {string} detail.seqId The hovered sequence ID
865
+ * @event Panoramax.components.ui.Map#sequence-hover
866
+ * @type {maplibregl.util.evented.Event}
867
+ * @property {string} seqId The hovered sequence ID
878
868
  */
879
- this._parent.dispatchEvent(new CustomEvent("map:sequence-hover", {
880
- detail: {
881
- seqId: e.features[0].properties.id
882
- }
883
- }));
869
+ this.fire("sequence-hover", { seqId: e.features[0].properties.id });
884
870
  }
885
871
  }
886
872
 
@@ -888,26 +874,22 @@ export default class Map extends maplibregl.Map {
888
874
  * Event handler for sequence click
889
875
  * @private
890
876
  * @param {object} e Event data
877
+ * @memberof Panoramax.components.ui.Map#
891
878
  */
892
879
  _onSequenceClick(e) {
893
880
  e.preventDefault();
894
881
  if(e.features.length > 0 && e.features[0].properties?.id) {
895
882
  /**
896
883
  * Event when a sequence on map is clicked
897
- *
898
- * @event map:sequence-click
899
- * @memberof CoreView
900
- * @type {object}
901
- * @property {object} detail Event information
902
- * @property {string} detail.seqId The clicked sequence ID
903
- * @property {maplibregl.LngLat} detail.coordinates The coordinates of user click
884
+ * @event Panoramax.components.ui.Map#sequence-click
885
+ * @type {maplibregl.util.evented.Event}
886
+ * @property {string} seqId The clicked sequence ID
887
+ * @property {maplibregl.LngLat} coordinates The coordinates of user click
904
888
  */
905
- this._parent.dispatchEvent(new CustomEvent("map:sequence-click", {
906
- detail: {
907
- seqId: e.features[0].properties.id,
908
- coordinates: e.lngLat
909
- }
910
- }));
889
+ this.fire("sequence-click", {
890
+ seqId: e.features[0].properties.id,
891
+ coordinates: e.lngLat
892
+ });
911
893
  }
912
894
  }
913
895
 
@@ -915,6 +897,7 @@ export default class Map extends maplibregl.Map {
915
897
  * Event handler for picture click
916
898
  * @private
917
899
  * @param {object} e Event data
900
+ * @memberof Panoramax.components.ui.Map#
918
901
  */
919
902
  _onPictureClick(e) {
920
903
  e.preventDefault();
@@ -935,21 +918,30 @@ export default class Map extends maplibregl.Map {
935
918
  /**
936
919
  * Event when a picture on map is clicked
937
920
  *
938
- * @event map:picture-click
939
- * @memberof CoreView
940
- * @type {object}
941
- * @property {object} detail Event information
942
- * @property {string} detail.picId The clicked picture ID
943
- * @property {string} detail.seqId The clicked picture's sequence ID
944
- * @property {object} detail.feature The GeoJSON feature of the picture
921
+ * @event Panoramax.components.ui.Map#picture-click
922
+ * @type {maplibregl.util.evented.Event}
923
+ * @property {string} picId The clicked picture ID
924
+ * @property {string} seqId The clicked picture's sequence ID
925
+ * @property {object} feature The GeoJSON feature of the picture
945
926
  */
946
- this._parent.dispatchEvent(new CustomEvent("map:picture-click", {
947
- detail: {
948
- picId: f.properties.id,
949
- seqId,
950
- feature: f
951
- }
952
- }));
927
+ this.fire("picture-click", {
928
+ picId: f.properties.id,
929
+ seqId,
930
+ feature: f
931
+ });
953
932
  }
954
933
  }
934
+
935
+ /**
936
+ * Listen to map events.
937
+ * This is a binder to [`on`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#on) and [`once`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#once) MapLibre GL functions.
938
+ * @param {string} type The event type to listen for
939
+ * @param {function} listener The event handler
940
+ * @param {boolean} [options.once=false] Set to true to only listen to first event.
941
+ * @memberof Panoramax.components.ui.Map#
942
+ */
943
+ addEventListener(type, listener, options = {}) {
944
+ if(options?.once) { this.once(type, listener); }
945
+ else { this.on(type, listener); }
946
+ }
955
947
  }