@panoramax/web-viewer 3.2.3-develop-d7e5a16d → 3.2.3-develop-f9c0224b

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 (221) hide show
  1. package/.gitlab-ci.yml +5 -0
  2. package/CHANGELOG.md +19 -0
  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 +2 -2
  7. package/build/index.css.map +1 -1
  8. package/build/index.html +1 -1
  9. package/build/index.js +1682 -5
  10. package/build/index.js.map +1 -1
  11. package/build/map.html +1 -1
  12. package/build/viewer.html +10 -1
  13. package/build/widgets.html +1 -0
  14. package/config/jest/mocks.js +172 -0
  15. package/config/paths.js +1 -0
  16. package/config/webpack.config.js +26 -0
  17. package/docs/03_URL_settings.md +3 -11
  18. package/docs/05_Compatibility.md +59 -76
  19. package/docs/09_Develop.md +30 -11
  20. package/docs/90_Releases.md +2 -2
  21. package/docs/images/class_diagram.drawio +28 -28
  22. package/docs/images/class_diagram.jpg +0 -0
  23. package/docs/index.md +112 -0
  24. package/docs/reference/components/core/Basic.md +153 -0
  25. package/docs/reference/components/core/CoverageMap.md +160 -0
  26. package/docs/reference/components/core/Editor.md +172 -0
  27. package/docs/reference/components/core/Viewer.md +288 -0
  28. package/docs/reference/components/layout/CorneredGrid.md +29 -0
  29. package/docs/reference/components/layout/Mini.md +45 -0
  30. package/docs/reference/components/menus/MapBackground.md +32 -0
  31. package/docs/reference/components/menus/MapFilters.md +15 -0
  32. package/docs/reference/components/menus/MapLayers.md +15 -0
  33. package/docs/reference/components/menus/MapLegend.md +15 -0
  34. package/docs/reference/components/menus/PictureLegend.md +15 -0
  35. package/docs/reference/components/menus/PictureMetadata.md +15 -0
  36. package/docs/reference/components/menus/PlayerOptions.md +15 -0
  37. package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
  38. package/docs/reference/components/menus/ReportForm.md +15 -0
  39. package/docs/reference/components/menus/ShareMenu.md +15 -0
  40. package/docs/reference/components/ui/Button.md +39 -0
  41. package/docs/reference/components/ui/ButtonGroup.md +36 -0
  42. package/docs/reference/components/ui/CopyButton.md +35 -0
  43. package/docs/reference/components/ui/Grade.md +32 -0
  44. package/docs/reference/components/ui/LinkButton.md +44 -0
  45. package/docs/reference/components/ui/Loader.md +54 -0
  46. package/docs/reference/components/ui/Map.md +214 -0
  47. package/docs/reference/components/ui/MapMore.md +233 -0
  48. package/docs/reference/components/ui/Photo.md +369 -0
  49. package/docs/reference/components/ui/Popup.md +56 -0
  50. package/docs/reference/components/ui/QualityScore.md +45 -0
  51. package/docs/reference/components/ui/SearchBar.md +63 -0
  52. package/docs/reference/components/ui/TogglableGroup.md +39 -0
  53. package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
  54. package/docs/reference/components/ui/widgets/Legend.md +32 -0
  55. package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
  56. package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
  57. package/docs/reference/components/ui/widgets/Player.md +32 -0
  58. package/docs/reference/components/ui/widgets/Share.md +15 -0
  59. package/docs/reference/components/ui/widgets/Zoom.md +15 -0
  60. package/docs/reference/utils/API.md +311 -0
  61. package/docs/reference/utils/InitParameters.md +67 -0
  62. package/docs/reference/utils/URLHandler.md +102 -0
  63. package/docs/reference.md +73 -0
  64. package/docs/shortcuts.md +11 -0
  65. package/docs/tutorials/aerial_imagery.md +19 -0
  66. package/docs/tutorials/authentication.md +10 -0
  67. package/docs/tutorials/custom_widgets.md +64 -0
  68. package/docs/tutorials/map_style.md +27 -0
  69. package/docs/tutorials/migrate_v4.md +122 -0
  70. package/docs/tutorials/synced_coverage.md +42 -0
  71. package/mkdocs.yml +60 -5
  72. package/package.json +10 -7
  73. package/public/editor.html +21 -29
  74. package/public/index.html +3 -3
  75. package/public/map.html +19 -18
  76. package/public/viewer.html +18 -24
  77. package/public/widgets.html +265 -0
  78. package/scripts/doc.js +77 -0
  79. package/src/components/core/Basic.css +44 -0
  80. package/src/components/core/Basic.js +258 -0
  81. package/src/components/core/CoverageMap.css +9 -0
  82. package/src/components/core/CoverageMap.js +105 -0
  83. package/src/components/core/Editor.css +23 -0
  84. package/src/components/core/Editor.js +354 -0
  85. package/src/components/core/Viewer.css +109 -0
  86. package/src/components/core/Viewer.js +707 -0
  87. package/src/components/core/index.js +11 -0
  88. package/src/components/index.js +13 -0
  89. package/src/components/layout/CorneredGrid.js +109 -0
  90. package/src/components/layout/Mini.js +117 -0
  91. package/src/components/layout/index.js +7 -0
  92. package/src/components/menus/MapBackground.js +106 -0
  93. package/src/components/menus/MapFilters.js +386 -0
  94. package/src/components/menus/MapLayers.js +143 -0
  95. package/src/components/menus/MapLegend.js +54 -0
  96. package/src/components/menus/PictureLegend.js +103 -0
  97. package/src/components/menus/PictureMetadata.js +188 -0
  98. package/src/components/menus/PlayerOptions.js +96 -0
  99. package/src/components/menus/QualityScoreDoc.js +36 -0
  100. package/src/components/menus/ReportForm.js +133 -0
  101. package/src/components/menus/Share.js +228 -0
  102. package/src/components/menus/index.js +15 -0
  103. package/src/components/styles.js +365 -0
  104. package/src/components/ui/Button.js +75 -0
  105. package/src/components/ui/ButtonGroup.css +49 -0
  106. package/src/components/ui/ButtonGroup.js +68 -0
  107. package/src/components/ui/CopyButton.js +71 -0
  108. package/src/components/ui/Grade.js +54 -0
  109. package/src/components/ui/LinkButton.js +68 -0
  110. package/src/components/ui/Loader.js +188 -0
  111. package/src/components/{Map.css → ui/Map.css} +5 -17
  112. package/src/components/{Map.js → ui/Map.js} +114 -138
  113. package/src/components/ui/MapMore.js +324 -0
  114. package/src/components/{Photo.css → ui/Photo.css} +6 -6
  115. package/src/components/{Photo.js → ui/Photo.js} +279 -90
  116. package/src/components/ui/Popup.js +145 -0
  117. package/src/components/ui/QualityScore.js +152 -0
  118. package/src/components/ui/SearchBar.js +363 -0
  119. package/src/components/ui/TogglableGroup.js +162 -0
  120. package/src/components/ui/index.js +20 -0
  121. package/src/components/ui/widgets/GeoSearch.css +21 -0
  122. package/src/components/ui/widgets/GeoSearch.js +139 -0
  123. package/src/components/ui/widgets/Legend.js +51 -0
  124. package/src/components/ui/widgets/MapFiltersButton.js +104 -0
  125. package/src/components/ui/widgets/MapLayersButton.js +79 -0
  126. package/src/components/ui/widgets/Player.css +7 -0
  127. package/src/components/ui/widgets/Player.js +148 -0
  128. package/src/components/ui/widgets/Share.js +30 -0
  129. package/src/components/ui/widgets/Zoom.js +82 -0
  130. package/src/components/ui/widgets/index.js +12 -0
  131. package/src/img/panoramax.svg +13 -0
  132. package/src/img/switch_big.svg +20 -10
  133. package/src/index.js +6 -9
  134. package/src/translations/da.json +1 -1
  135. package/src/translations/de.json +1 -1
  136. package/src/translations/en.json +5 -3
  137. package/src/translations/eo.json +1 -1
  138. package/src/translations/es.json +1 -1
  139. package/src/translations/fr.json +5 -3
  140. package/src/translations/hu.json +1 -1
  141. package/src/translations/it.json +1 -1
  142. package/src/translations/ja.json +1 -1
  143. package/src/translations/nl.json +1 -1
  144. package/src/translations/pl.json +1 -1
  145. package/src/translations/sv.json +1 -1
  146. package/src/translations/zh_Hant.json +1 -1
  147. package/src/utils/API.js +74 -42
  148. package/src/utils/InitParameters.js +354 -0
  149. package/src/utils/URLHandler.js +364 -0
  150. package/src/utils/geocoder.js +116 -0
  151. package/src/utils/{I18n.js → i18n.js} +3 -1
  152. package/src/utils/index.js +11 -0
  153. package/src/utils/{Map.js → map.js} +216 -80
  154. package/src/utils/picture.js +433 -0
  155. package/src/utils/utils.js +315 -0
  156. package/src/utils/widgets.js +93 -0
  157. package/tests/components/ui/CopyButton.test.js +52 -0
  158. package/tests/components/ui/Loader.test.js +54 -0
  159. package/tests/components/{Map.test.js → ui/Map.test.js} +19 -61
  160. package/tests/components/{Photo.test.js → ui/Photo.test.js} +89 -57
  161. package/tests/components/ui/Popup.test.js +24 -0
  162. package/tests/components/ui/QualityScore.test.js +17 -0
  163. package/tests/components/ui/SearchBar.test.js +107 -0
  164. package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +34 -0
  165. package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
  166. package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
  167. package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +57 -4
  168. package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
  169. package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
  170. package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
  171. package/tests/utils/API.test.js +1 -14
  172. package/tests/utils/InitParameters.test.js +485 -0
  173. package/tests/utils/URLHandler.test.js +350 -0
  174. package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
  175. package/tests/utils/__snapshots__/picture.test.js.snap +315 -0
  176. package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
  177. package/tests/utils/geocoder.test.js +37 -0
  178. package/tests/utils/{I18n.test.js → i18n.test.js} +1 -1
  179. package/tests/utils/map.test.js +67 -0
  180. package/tests/utils/picture.test.js +745 -0
  181. package/tests/utils/utils.test.js +288 -0
  182. package/tests/utils/widgets.test.js +90 -0
  183. package/docs/01_Start.md +0 -149
  184. package/docs/02_Usage.md +0 -831
  185. package/docs/04_Advanced_examples.md +0 -216
  186. package/src/Editor.css +0 -37
  187. package/src/Editor.js +0 -361
  188. package/src/StandaloneMap.js +0 -114
  189. package/src/Viewer.css +0 -203
  190. package/src/Viewer.js +0 -1246
  191. package/src/components/CoreView.css +0 -70
  192. package/src/components/CoreView.js +0 -175
  193. package/src/components/Loader.css +0 -74
  194. package/src/components/Loader.js +0 -120
  195. package/src/utils/Exif.js +0 -193
  196. package/src/utils/Utils.js +0 -631
  197. package/src/utils/Widgets.js +0 -562
  198. package/src/viewer/URLHash.js +0 -469
  199. package/src/viewer/Widgets.css +0 -880
  200. package/src/viewer/Widgets.js +0 -1470
  201. package/tests/Editor.test.js +0 -126
  202. package/tests/StandaloneMap.test.js +0 -45
  203. package/tests/Viewer.test.js +0 -366
  204. package/tests/__snapshots__/Editor.test.js.snap +0 -298
  205. package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
  206. package/tests/__snapshots__/Viewer.test.js.snap +0 -195
  207. package/tests/components/CoreView.test.js +0 -92
  208. package/tests/components/Loader.test.js +0 -38
  209. package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
  210. package/tests/utils/Exif.test.js +0 -124
  211. package/tests/utils/Map.test.js +0 -113
  212. package/tests/utils/Utils.test.js +0 -300
  213. package/tests/utils/Widgets.test.js +0 -107
  214. package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
  215. package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
  216. package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
  217. package/tests/viewer/URLHash.test.js +0 -559
  218. package/tests/viewer/Widgets.test.js +0 -127
  219. package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
  220. package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
  221. /package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +0 -0
package/src/utils/API.js CHANGED
@@ -1,17 +1,18 @@
1
- import { TILES_PICTURES_ZOOM } from "./Map";
2
- import { BASE_PANORAMA_ID } from "./Utils";
1
+ import { TILES_PICTURES_ZOOM } from "./map";
2
+ import { BASE_PANORAMA_ID } from "./utils";
3
3
 
4
4
  /**
5
- * API contains various utility functions to communicate with the backend
5
+ * API contains various utility functions to communicate with Panoramax/STAC API
6
6
  *
7
- * @param {string} endpoint The API endpoint. It corresponds to the <a href="https://github.com/radiantearth/stac-api-spec/blob/main/overview.md#example-landing-page">STAC landing page</a>, with all links describing the API capabilites.
8
- * @param {object} [options] Options received from viewer that may change API behaviour
7
+ * @class Panoramax.utils.API
8
+ * @typicalname api
9
+ * @param {string} endpoint The endpoint. It corresponds to the <a href="https://github.com/radiantearth/stac-api-spec/blob/main/overview.md#example-landing-page">STAC landing page</a>, with all links describing the API capabilites.
10
+ * @param {object} [options] Options
9
11
  * @param {string|object} [options.style] General map style
10
12
  * @param {string} [options.tiles] API route serving pictures & sequences vector tiles
11
- * @param {boolean} [options.skipReadLanding] True to not call API landing page automatically (defaults to false)
13
+ * @param {boolean} [options.skipReadLanding=false] True to not call API landing page automatically
12
14
  * @param {object} [options.fetch] Set custom options for fetch calls made against API ([same syntax as fetch options parameter](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters))
13
- * @param {string[]} [options.users] List of initial user IDs to load map styles for.
14
- * @private
15
+ * @param {string[]} [options.users] List of initial user IDs to load map styles for
15
16
  */
16
17
  export default class API {
17
18
  constructor(endpoint, options = {}) {
@@ -52,9 +53,11 @@ export default class API {
52
53
  }
53
54
 
54
55
  /**
55
- * This function resolves when API is ready to be used
56
- *
57
- * @returns {Promise} Resolves when API is ready
56
+ * Resolves when the API is ready to be used.
57
+ * @memberOf Panoramax.utils.API#
58
+ * @returns {Promise}
59
+ * @fulfil {string} "API is ready" when initialization is complete.
60
+ * @reject {string} Error message
58
61
  */
59
62
  onceReady() {
60
63
  if(this._isReady == -1) {
@@ -69,9 +72,9 @@ export default class API {
69
72
  }
70
73
 
71
74
  /**
72
- * Check if API is ready to be used
73
- *
74
- * @returns {boolean} True if ready
75
+ * Checks if the API is ready to be used.
76
+ * @memberOf Panoramax.utils.API#
77
+ * @returns {boolean} True if the API is ready, false otherwise.
75
78
  */
76
79
  isReady() {
77
80
  return this._isReady == 1;
@@ -79,6 +82,7 @@ export default class API {
79
82
 
80
83
  /**
81
84
  * List of available features offered by API
85
+ * @memberOf Panoramax.utils.API#
82
86
  * @returns {string[]} Keywords of enabled features
83
87
  */
84
88
  getAvailableFeatures() {
@@ -87,6 +91,7 @@ export default class API {
87
91
 
88
92
  /**
89
93
  * List of unavailable features on API
94
+ * @memberOf Panoramax.utils.API#
90
95
  * @returns {string[]} Keywords of disabled features
91
96
  */
92
97
  getUnavailableFeatures() {
@@ -95,7 +100,7 @@ export default class API {
95
100
 
96
101
  /**
97
102
  * Interprets JSON landing page and store important information
98
- *
103
+ * @memberOf Panoramax.utils.API#
99
104
  * @private
100
105
  */
101
106
  _parseLanding(landing, options) {
@@ -252,9 +257,12 @@ export default class API {
252
257
 
253
258
  /**
254
259
  * Loads all MapLibre Styles JSON needed at start.
260
+ * @memberOf Panoramax.utils.API#
255
261
  * @param {string|object} style General map style
256
262
  * @param {string[]} users List of user IDs to handle. Should include special user "geovisio" for general tiles loading.
257
- * @returns {Promise} Resolves when style is ready.
263
+ * @returns {Promise}
264
+ * @fulfil {null} When style is ready.
265
+ * @private
258
266
  */
259
267
  _loadMapStyles(style, users) {
260
268
  const mapUsers = new Set(users || []);
@@ -298,7 +306,7 @@ export default class API {
298
306
 
299
307
  /**
300
308
  * Get the defaults fetch options to pass during API calls
301
- *
309
+ * @memberOf Panoramax.utils.API#
302
310
  * @private
303
311
  * @returns {object} The fetch options
304
312
  */
@@ -310,7 +318,7 @@ export default class API {
310
318
 
311
319
  /**
312
320
  * Get the RequestTransformFunction for MapLibre to handle fetch options
313
- *
321
+ * @memberOf Panoramax.utils.API#
314
322
  * @private
315
323
  * @returns {function} The RequestTransformFunction
316
324
  */
@@ -335,11 +343,13 @@ export default class API {
335
343
 
336
344
  /**
337
345
  * Get sequence GeoJSON representation
338
- *
346
+ * @memberOf Panoramax.utils.API#
339
347
  * @param {string} seqId The sequence ID
340
348
  * @param {string} [next] The next link URL (only for internals)
341
349
  * @param {object} [data] The previous dataset (only for internals)
342
- * @returns {Promise} Resolves on sequence GeoJSON
350
+ * @returns {Promise}
351
+ * @fulfil {object} Sequence GeoJSON
352
+ * @reject {Error} If API is not ready or for any network issue
343
353
  */
344
354
  async getSequenceItems(seqId, next = null, data = null) {
345
355
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -368,7 +378,7 @@ export default class API {
368
378
 
369
379
  /**
370
380
  * Get full URL for listing pictures around a specific location
371
- *
381
+ * @memberOf Panoramax.utils.API#
372
382
  * @param {number} lat Latitude
373
383
  * @param {number} lon Longitude
374
384
  * @param {number} [factor] The radius to search around (in degrees)
@@ -391,13 +401,14 @@ export default class API {
391
401
 
392
402
  /**
393
403
  * Get list of pictures around a specific location
394
- *
404
+ * @memberOf Panoramax.utils.API#
395
405
  * @param {number} lat Latitude
396
406
  * @param {number} lon Longitude
397
407
  * @param {number} [factor] The radius to search around (in degrees)
398
408
  * @param {number} [limit] Max amount of pictures to retrieve
399
409
  * @param {string} [seqId] The sequence ID to filter on (by default, no filter)
400
- * @returns {object} The GeoJSON feature collection
410
+ * @returns {Promise}
411
+ * @fulfil {object} The GeoJSON feature collection
401
412
  */
402
413
  getPicturesAroundCoordinates(lat, lon, factor, limit, seqId) {
403
414
  return fetch(this.getPicturesAroundCoordinatesUrl(lat, lon, factor, limit, seqId), this._getFetchOptions())
@@ -406,10 +417,11 @@ export default class API {
406
417
 
407
418
  /**
408
419
  * Get full URL for retrieving a specific picture metadata
409
- *
420
+ * @memberOf Panoramax.utils.API#
410
421
  * @param {string} picId The picture unique identifier
411
422
  * @param {string} [seqId] The sequence ID
412
423
  * @returns {string} The corresponding URL
424
+ * @throws {Error} If API is not ready
413
425
  */
414
426
  getPictureMetadataUrl(picId, seqId) {
415
427
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -422,8 +434,10 @@ export default class API {
422
434
 
423
435
  /**
424
436
  * Get JSON style for general vector tiles
425
- * @return {Promise} The MapLibre JSON style
426
- * @fires Error If API is not ready, or no style defined.
437
+ * @memberOf Panoramax.utils.API#
438
+ * @return {Promise|object} Promise if first load, MapLibre JSON style otherwise
439
+ * @fulfil {object} The MapLibre JSON style
440
+ * @reject {Error} If API is not ready, or no style defined.
427
441
  */
428
442
  getMapStyle() {
429
443
  if(this.isReady()) { return this.mapStyle; }
@@ -471,10 +485,12 @@ export default class API {
471
485
 
472
486
  /**
473
487
  * Get JSON style for specific-user vector tiles
488
+ * @memberOf Panoramax.utils.API#
474
489
  * @param {string} userId The user UUID
475
490
  * @param {boolean} [skipReadyCheck=false] Skip check for API readyness
476
- * @return {Promise} The MapLibre JSON style
477
- * @fires Error If API is not ready, or no style defined.
491
+ * @return {Promise}
492
+ * @fulfil {object} The MapLibre JSON style
493
+ * @reject {Error} If API is not ready, or no style defined.
478
494
  */
479
495
  getUserMapStyle(userId, skipReadyCheck = false) {
480
496
  if(!skipReadyCheck && !this.isReady()) { return Promise.reject(new Error("API is not ready to use")); }
@@ -515,9 +531,10 @@ export default class API {
515
531
 
516
532
  /**
517
533
  * Find the thumbnail URL for a given picture
518
- *
534
+ * @memberOf Panoramax.utils.API#
519
535
  * @param {object} picture The picture GeoJSON feature
520
536
  * @returns {string} The thumbnail URL, or null if not found
537
+ * @throws {Error} If API is not ready
521
538
  * @private
522
539
  */
523
540
  findThumbnailInPictureFeature(picture) {
@@ -538,10 +555,12 @@ export default class API {
538
555
 
539
556
  /**
540
557
  * Get a picture thumbnail URL for a given sequence
541
- *
558
+ * @memberOf Panoramax.utils.API#
542
559
  * @param {string} seqId The sequence ID
543
560
  * @param {object} [seq] The sequence metadata (with links) if already loaded
544
- * @returns {Promise} Promise resolving on the picture thumbnail URL, or null if not found
561
+ * @returns {Promise}
562
+ * @fulfil {string|null} Promise resolving on the picture thumbnail URL, or null if not found
563
+ * @throws {Error} If API is not ready
545
564
  */
546
565
  getPictureThumbnailURLForSequence(seqId, seq) {
547
566
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -575,10 +594,12 @@ export default class API {
575
594
 
576
595
  /**
577
596
  * Get thumbnail URL for a specific picture
578
- *
597
+ * @memberOf Panoramax.utils.API#
579
598
  * @param {string} picId The picture unique identifier
580
599
  * @param {string} [seqId] The sequence ID
581
- * @returns {Promise} The corresponding URL on resolve, or undefined if no thumbnail could be found
600
+ * @returns {Promise}
601
+ * @fulfil {string|null} The corresponding URL on resolve, or null if no thumbnail could be found
602
+ * @throws {Error} If API is not ready
582
603
  */
583
604
  getPictureThumbnailURL(picId, seqId) {
584
605
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -610,9 +631,10 @@ export default class API {
610
631
 
611
632
  /**
612
633
  * Get the RSS feed URL with map parameters (if map is enabled)
613
- *
634
+ * @memberOf Panoramax.utils.API#
614
635
  * @param {LngLatBounds} [bbox] The map current bounding box, or null if not available
615
- * @returns {string} The URL, or null if no RSS feed is available
636
+ * @returns {string|null} The URL, or null if no RSS feed is available
637
+ * @throws {Error} If API is not ready
616
638
  */
617
639
  getRSSURL(bbox) {
618
640
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -632,9 +654,10 @@ export default class API {
632
654
 
633
655
  /**
634
656
  * Get full URL for retrieving a specific sequence metadata
635
- *
657
+ * @memberOf Panoramax.utils.API#
636
658
  * @param {string} seqId The sequence ID
637
659
  * @returns {string} The corresponding URL
660
+ * @throws {Error} If API is not ready
638
661
  */
639
662
  getSequenceMetadataUrl(seqId) {
640
663
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -643,8 +666,9 @@ export default class API {
643
666
 
644
667
  /**
645
668
  * Get available data bounding box
646
- *
669
+ * @memberOf Panoramax.utils.API#
647
670
  * @returns {LngLatBoundsLike} The bounding box or null if not available
671
+ * @throws {Error} If API is not ready
648
672
  */
649
673
  getDataBbox() {
650
674
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -654,8 +678,11 @@ export default class API {
654
678
 
655
679
  /**
656
680
  * Look for user ID based on user name query
681
+ * @memberOf Panoramax.utils.API#
657
682
  * @param {string} query The user name to look for
658
- * @returns {Promise} Resolves on list of potential users
683
+ * @returns {Promise}
684
+ * @fulfil {object|null} List of potential users
685
+ * @throws {Error} If API is not ready or user search not available
659
686
  */
660
687
  searchUsers(query) {
661
688
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -670,8 +697,11 @@ export default class API {
670
697
 
671
698
  /**
672
699
  * Get user name based on its ID
700
+ * @memberOf Panoramax.utils.API#
673
701
  * @param {string} userId The user UUID
674
- * @returns {Promise} Resolves on user name
702
+ * @returns {Promise}
703
+ * @throws {Error} If API is not ready
704
+ * @fulfil {string|null} The user name (or null if not found)
675
705
  */
676
706
  getUserName(userId) {
677
707
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -686,8 +716,10 @@ export default class API {
686
716
 
687
717
  /**
688
718
  * Send a report to API
719
+ * @memberOf Panoramax.utils.API#
689
720
  * @param {object} data The input form data
690
- * @returns {Promise} Resolves on report sent
721
+ * @returns {Promise}
722
+ * @fulfil {object} The JSON API response
691
723
  */
692
724
  sendReport(data) {
693
725
  if(!this.isReady()) { throw new Error("API is not ready to use"); }
@@ -715,7 +747,7 @@ export default class API {
715
747
 
716
748
  /**
717
749
  * Checks URL string validity
718
- *
750
+ * @memberOf Panoramax.utils.API
719
751
  * @param {string} str The URL to check
720
752
  * @returns {boolean} True if valid
721
753
  */
@@ -733,7 +765,7 @@ export default class API {
733
765
 
734
766
  /**
735
767
  * Checks picture or sequence ID validity
736
- *
768
+ * @memberOf Panoramax.utils.API
737
769
  * @param {string} id The ID to check
738
770
  * @returns {boolean} True if valid
739
771
  * @throws {Error} If not valid
@@ -0,0 +1,354 @@
1
+ export const MAP_FILTERS_JS2URL = {
2
+ "minDate": "date_from",
3
+ "maxDate": "date_to",
4
+ "pic_type": "pic_type",
5
+ "camera": "camera",
6
+ "theme": "theme",
7
+ "qualityscore": "pic_score",
8
+ };
9
+
10
+ const MAP_FILTERS_URL2JS = Object.fromEntries(Object.entries(MAP_FILTERS_JS2URL).map(v => [v[1], v[0]]));
11
+ const MAP_NONE = ["none", "null", "false", false];
12
+
13
+ /**
14
+ * Merges all URL parameters and component attributes into a single set of coherent settings.
15
+ *
16
+ * @class Panoramax.utils.InitParameters
17
+ * @param {object} [componentAttrs] HTML attributes from parent component
18
+ * @param {object} [urlParams] Parameters extracted from URL
19
+ */
20
+ export default class InitParameters { // eslint-disable-line import/no-unused-modules
21
+ constructor(componentAttrs = {}, urlParams = {}) {
22
+ // Skip URL parameters if disabled by component
23
+ if(componentAttrs["url-parameters"] === "false") { urlParams = {}; }
24
+
25
+ // Sanitize PSV parameters
26
+ let componentPsv = {};
27
+ if(typeof componentAttrs?.psv === "object") { componentPsv = componentAttrs.psv; }
28
+
29
+ // Sanitize Map parameters
30
+ let componentMap = {};
31
+ if(typeof componentAttrs?.map === "object") { componentMap = componentAttrs.map; }
32
+
33
+ // Extract map position from URL
34
+ let urlMap = urlParams.map && urlParams.map !== "none" ? getMapPositionFromString(urlParams.map) : null;
35
+
36
+ // Parse and prioritize all parameters
37
+ // - Overlapping between URL & component
38
+ let map = MAP_NONE.includes(urlParams.map) || MAP_NONE.includes(componentAttrs.map) ? false : true;
39
+ let focus = urlParams.focus || componentAttrs.focus;
40
+ let picture = urlParams.pic || componentAttrs.picture;
41
+ let users = urlParams.users || componentAttrs.users;
42
+ let psv_speed = urlParams.speed || componentPsv?.transitionDuration;
43
+ let psv_nav = urlParams.nav || componentPsv?.picturesNavigation;
44
+ let map_theme = urlParams.theme || componentMap.theme;
45
+ let map_background = urlParams.background || componentMap.background;
46
+ let map_center = urlMap?.center || componentMap.center;
47
+ let map_zoom = urlMap?.zoom || componentMap.zoom;
48
+ let map_pitch = urlMap?.pitch || componentMap.pitch;
49
+ let map_bearing = urlMap?.bearing || componentMap.bearing;
50
+
51
+ // - Component only
52
+ let geocoder = componentAttrs.geocoder;
53
+ let widgets = componentAttrs.widgets;
54
+ let sequence = componentAttrs.sequence;
55
+ let fetchOptions = componentAttrs.fetchOptions;
56
+ let style = componentAttrs.style;
57
+ let lang = componentAttrs.lang;
58
+ let endpoint = componentAttrs.endpoint;
59
+ let map_raster = componentMap.raster;
60
+
61
+ // - URL only
62
+ let psv_xyz = urlParams.xyz;
63
+ let map_date_from = urlParams.date_from;
64
+ let map_date_to = urlParams.date_to;
65
+ let map_pic_type = urlParams.pic_type;
66
+ let map_camera = urlParams.camera;
67
+ let map_pic_score = urlParams.pic_score;
68
+
69
+ // Check coherence
70
+ if(!["map", "meta", "pic"].includes(focus)) {
71
+ console.warn("Invalid value for parameter focus:", focus);
72
+ focus = map && !picture ? "map" : "pic";
73
+ }
74
+ else if(focus === "map" && !map) {
75
+ console.warn("Parameter focus can't be 'map' as map is disabled");
76
+ focus = "pic";
77
+ }
78
+ if(map_background == "aerial" && !map_raster) {
79
+ console.warn("Parameter background can't be 'aerial' as no aerial imagery is available");
80
+ map_background = "streets";
81
+ }
82
+ if(map && !picture) {
83
+ focus = "map";
84
+ }
85
+
86
+ // Put all attributes in appropriate container
87
+ this._parentInit = { map, users, fetchOptions, style, lang, endpoint };
88
+ this._parentPostInit = { focus, picture, sequence, geocoder, widgets, forceFocus: true };
89
+
90
+ this._psvInit = Object.fromEntries(
91
+ Object.entries(componentPsv).filter(
92
+ ([k,]) => !["transitionDuration", "picturesNavigation"].includes(k)
93
+ )
94
+ );
95
+ this._psvAny = {
96
+ transitionDuration: psv_speed,
97
+ picturesNavigation: psv_nav,
98
+ };
99
+ this._psvPostInit = { xyz: psv_xyz };
100
+
101
+ this._mapInit = { raster: map_raster };
102
+ this._mapAny = {
103
+ theme: map_theme,
104
+ background: map_background,
105
+ center: map_center,
106
+ zoom: map_zoom,
107
+ pitch: map_pitch,
108
+ bearing: map_bearing,
109
+ users,
110
+ };
111
+ this._mapPostInit = {
112
+ date_from: map_date_from,
113
+ date_to: map_date_to,
114
+ pic_type: map_pic_type,
115
+ camera: map_camera,
116
+ pic_score: map_pic_score,
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Cleans out undefined values from object.
122
+ * @param {object} obj The input object
123
+ * @returns {object} Clean object without undefined values
124
+ * @private
125
+ */
126
+ _sanitize(obj) {
127
+ return Object.fromEntries(Object.entries(obj).filter(([,v]) => v !== undefined));
128
+ }
129
+
130
+ /**
131
+ * Get core component initialization parameters.
132
+ * They must be passed as soon as possible.
133
+ * @memberof Panoramax.utils.InitParameters#
134
+ */
135
+ getParentInit() {
136
+ return this._sanitize(this._parentInit);
137
+ }
138
+
139
+ /**
140
+ * Get core component post-initialization parameters.
141
+ * They must be passed after first rendering or init.
142
+ * @memberof Panoramax.utils.InitParameters#
143
+ */
144
+ getParentPostInit() {
145
+ return this._sanitize(this._parentPostInit);
146
+ }
147
+
148
+ /**
149
+ * Get Photo Sphere Viewer initialization parameters.
150
+ * They must be passed as soon as possible.
151
+ * @memberof Panoramax.utils.InitParameters#
152
+ */
153
+ getPSVInit() {
154
+ return this._sanitize(Object.assign({}, this._psvInit, this._psvAny));
155
+ }
156
+
157
+ /**
158
+ * Get Photo Sphere Viewer post-initialization parameters.
159
+ * They must be passed after first rendering or init.
160
+ * @memberof Panoramax.utils.InitParameters#
161
+ */
162
+ getPSVPostInit() {
163
+ return this._sanitize(Object.assign({}, this._psvPostInit, this._psvAny));
164
+ }
165
+
166
+ /**
167
+ * Get MapLibre GL initialization parameters.
168
+ * They must be passed as soon as possible.
169
+ * @memberof Panoramax.utils.InitParameters#
170
+ */
171
+ getMapInit() {
172
+ return this._sanitize(Object.assign({}, this._mapInit, this._mapAny));
173
+ }
174
+
175
+ /**
176
+ * Get MapLibre GL post-initialization parameters.
177
+ * They must be passed after first rendering or init.
178
+ * @memberof Panoramax.utils.InitParameters#
179
+ */
180
+ getMapPostInit() {
181
+ return this._sanitize(Object.assign({}, this._mapPostInit, this._mapAny));
182
+ }
183
+
184
+ /**
185
+ * Reads public properties from LitElement and returns a classic key-value object.
186
+ * @param {class} compClass The component class (with static `properties` definition)
187
+ * @param {LitElement} comp The component itself
188
+ */
189
+ static GetComponentProperties(compClass, comp) {
190
+ const props = {};
191
+ for(let classK in compClass.properties) {
192
+ let classV = compClass.properties[classK];
193
+ if(!classV.state && comp[classK] != null) {
194
+ props[classK] = comp[classK];
195
+ }
196
+ }
197
+ return props;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Extracts map center, zoom & more from formatted string.
203
+ * @param {string} str The map position as hash string
204
+ * @param {Panoramax.components.ui.Map} [map] The current map
205
+ * @returns {object} { center, zoom, pitch, bearing }
206
+ * @private
207
+ */
208
+ export function getMapPositionFromString(str, map) {
209
+ const loc = (str || "").split("/");
210
+ if (loc.length >= 3 && !loc.some(v => isNaN(v))) {
211
+ const res = {
212
+ center: [+loc[2], +loc[1]],
213
+ zoom: +loc[0],
214
+ pitch: +(loc[4] || 0)
215
+ };
216
+
217
+ if(map) {
218
+ res.bearing = map.dragRotate.isEnabled() && map.touchZoomRotate.isEnabled() ? +(loc[3] || 0) : map.getBearing();
219
+ }
220
+
221
+ return res;
222
+ }
223
+ else { return null; }
224
+ }
225
+
226
+ /**
227
+ * Extracts from string xyz position
228
+ * @param {string} str The xyz position as hash string
229
+ * @returns {object} { x, y, z }
230
+ * @private
231
+ */
232
+ export function xyzParamToPSVPosition(str) {
233
+ const loc = (str || "").split("/");
234
+ if (loc.length === 3 && !loc.some(v => isNaN(v))) {
235
+ const res = {
236
+ x: +loc[0],
237
+ y: +loc[1],
238
+ z: +loc[2]
239
+ };
240
+
241
+ return res;
242
+ }
243
+ else { return null; }
244
+ }
245
+
246
+ /**
247
+ * Extracts from hash parsed keys all map filters values
248
+ * @param {*} vals Hash keys
249
+ * @returns {object} Map filters
250
+ * @private
251
+ */
252
+ export function paramsToMapFilters(vals) {
253
+ const newMapFilters = {};
254
+ for(let k in MAP_FILTERS_URL2JS) {
255
+ if(vals[k]) {
256
+ newMapFilters[MAP_FILTERS_URL2JS[k]] = vals[k];
257
+ }
258
+ }
259
+ if(newMapFilters.qualityscore) {
260
+ let values = newMapFilters.qualityscore.split("");
261
+ const mapping = {"A": 5, "B": 4, "C": 3, "D": 2, "E": 1};
262
+ newMapFilters.qualityscore = values.map(v => mapping[v]);
263
+ }
264
+ return newMapFilters;
265
+ }
266
+
267
+ /**
268
+ * Change PSV current state based on standardized parameters.
269
+ * @param {Photo} psv The Photo Sphere Viewer component to change.
270
+ * @param {object} params The parameters to apply.
271
+ */
272
+ export function alterPSVState(psv, params) {
273
+ // Change xyz position
274
+ if(params.xyz) {
275
+ psv.addEventListener("picture-loaded", () => {
276
+ const coords = xyzParamToPSVPosition(params.xyz);
277
+ psv.setXYZ(coords.x, coords.y, coords.z);
278
+ }, {once: true});
279
+ }
280
+
281
+ // Change transitionDuration
282
+ if(params.transitionDuration !== undefined) {
283
+ psv.setTransitionDuration(params.transitionDuration);
284
+ }
285
+
286
+ // Change pictures navigation mode
287
+ if(["pic", "any", "seq"].includes(params.picturesNavigation)) {
288
+ psv.setPicturesNavigation(params.picturesNavigation);
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Change MapLibre GL current state based on standardized parameters.
294
+ * @param {Map} map The MapLibre component to change.
295
+ * @param {object} params The parameters to apply.
296
+ */
297
+ export function alterMapState(map, params) {
298
+ // Map position
299
+ const mapOpts = getMapPositionFromString(params.map, map);
300
+ if(mapOpts) {
301
+ map.jumpTo(mapOpts);
302
+ }
303
+
304
+ // Visible users
305
+ let vu = Array.isArray(params.users) ? params.users : (params.users || "").split(",");
306
+ if(vu.length === 0 || (vu.length === 1 && vu[0].trim() === "")) { vu = ["geovisio"]; }
307
+ map.setVisibleUsers(vu);
308
+
309
+ // Change map filters
310
+ map.setFilters(paramsToMapFilters(params));
311
+
312
+ // Change map background
313
+ if(["aerial", "streets"].includes(params.background)) {
314
+ map.setBackground(params.background);
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Change Viewer current state based on standardized parameters.
320
+ * @param {Viewer} viewer The Viewer component to change.
321
+ * @param {object} params The parameters to apply.
322
+ */
323
+ export function alterViewerState(viewer, params) {
324
+ // Restore selected picture
325
+ if(params.picture) {
326
+ const picIds = params.picture.split(";"); // Handle multiple IDs coming from OSM
327
+ if(picIds.length > 1) {
328
+ console.warn("Multiple picture IDs passed in URL, only first one kept");
329
+ }
330
+
331
+ // Post-load changes
332
+ if(params.focus && params.focus == "meta") {
333
+ viewer.psv.addEventListener("picture-loaded", () => viewer._showPictureMetadata(), { once: true });
334
+ }
335
+
336
+ viewer.select(null, picIds[0]);
337
+ }
338
+ else {
339
+ viewer.select();
340
+ }
341
+
342
+ // Change focus
343
+ if(params.focus === "map" && viewer?.map) {
344
+ viewer.setPopup(false);
345
+ viewer._setFocus("map", null, params.forceFocus);
346
+ }
347
+ else if(params.focus === "pic") {
348
+ viewer.setPopup(false);
349
+ viewer._setFocus("pic", null, params.forceFocus);
350
+ }
351
+ else if(params.focus && params.focus === "meta") {
352
+ viewer._setFocus("pic", null, params.forceFocus);
353
+ }
354
+ }