@panoramax/web-viewer 3.2.3 → 4.0.0-develop-39167b4d

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 +53 -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
@@ -0,0 +1,349 @@
1
+ import { LitElement, html } from "lit";
2
+ import API from "../../utils/API";
3
+ import { getTranslations } from "../../utils/i18n";
4
+ import { DEFAULT_TILES } from "../../utils/map";
5
+ import { createWebComp } from "../../utils/widgets";
6
+ import { isInIframe, isInternetFast } from "../../utils/utils";
7
+ import JSON5 from "json5";
8
+ import PACKAGE_JSON from "../../../package.json";
9
+ import "@fontsource/atkinson-hyperlegible-next";
10
+ import "./Basic.css";
11
+
12
+ /**
13
+ * Event for overlaying menu opening
14
+ * @event Panoramax.components.core.Basic#menu-opened
15
+ * @type {CustomEvent}
16
+ * @property {Element} detail.menu The opened menu
17
+ */
18
+
19
+ /**
20
+ * Basic core component is a basic container for common functions through all core components.
21
+ * It is not intended to be used directly, it's only to be extended by other core components.
22
+ * @class Panoramax.components.core.Basic
23
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
24
+ * @fires Panoramax.components.core.Basic#select
25
+ * @fires Panoramax.components.core.Basic#ready
26
+ * @fires Panoramax.components.core.Basic#broken
27
+ * @fires Panoramax.components.core.Basic#menu-opened
28
+ * @property {Panoramax.components.ui.Loader} loader The loader screen
29
+ * @property {Panoramax.utils.API} api The API manager
30
+ */
31
+ export default class Basic extends LitElement {
32
+ /**
33
+ * Component properties.
34
+ * @memberof Panoramax.components.core.Basic#
35
+ * @type {Object}
36
+ * @mixin
37
+ * @property {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
38
+ * @property {string} [picture] The picture ID to display
39
+ * @property {string} [sequence] The sequence ID of the picture displayed
40
+ * @property {object} [fetchOptions] 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))
41
+ * @property {string[]} [users=[geovisio]] List of users IDs to use for map display (defaults to general map, identified as "geovisio")
42
+ * @property {string|object} [mapstyle] The map's MapLibre style. This can be an a JSON object conforming to the schema described in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL string pointing to one. Defaults to OSM vector tiles.
43
+ * @property {string} [lang] To override language used for labels. Defaults to using user's preferred languages.
44
+ */
45
+ static properties = {
46
+ picture: {type: String, reflect: true},
47
+ sequence: {type: String, reflect: true},
48
+ fetchOptions: {converter: Basic.GetJSONConverter()},
49
+ users: {type: Array, reflect: true},
50
+ mapstyle: {type: String},
51
+ lang: {type: String},
52
+ endpoint: {type: String},
53
+ };
54
+
55
+ constructor(testing = false) {
56
+ super();
57
+
58
+ // Some defaults
59
+ this.users = ["geovisio"];
60
+ this.mapstyle = this.getAttribute("mapstyle") || DEFAULT_TILES;
61
+ this.lang = this.getAttribute("lang") || null;
62
+ this.endpoint = this.getAttribute("endpoint") || null; // No default
63
+ this.picture = this.getAttribute("picture") || null;
64
+ this.sequence = this.getAttribute("sequence") || null;
65
+
66
+ // Display version in logs
67
+ console.info(`📷 Panoramax ${this.getClassName()} - Version ${PACKAGE_JSON.version} (${__COMMIT_HASH__})
68
+
69
+ 🆘 Issues can be reported at ${PACKAGE_JSON.repository.url}`);
70
+
71
+ if(testing) { return; }
72
+
73
+ // Internet speed check
74
+ this._isInternetFast = null;
75
+ isInternetFast().then(isFast => this._isInternetFast = isFast);
76
+ }
77
+
78
+ connectedCallback() {
79
+ super.connectedCallback();
80
+
81
+ // Translations
82
+ this._t = getTranslations(this.lang);
83
+
84
+ // Show loader
85
+ this.loader = createWebComp("pnx-loader", {_parent: this, "no-label": isInIframe() });
86
+
87
+ if(
88
+ !(this._loadsAPI && this.endpoint && this._loadsAPI === this.endpoint)
89
+ && !(this.api && this.api._endpoint === this.endpoint)
90
+ && this.endpoint
91
+ ) {
92
+ if(this._loadsAPI || this.api) {
93
+ delete this.api;
94
+ delete this._loadsAPI;
95
+ }
96
+ this._setupAPI();
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Creates API and wait for initial loading
102
+ * @private
103
+ */
104
+ _setupAPI() {
105
+ // Loader init
106
+ this.loader = this.loader || createWebComp("pnx-loader", {_parent: this});
107
+
108
+ if(!this.endpoint) {
109
+ console.warn("No endpoint is defined");
110
+ return;
111
+ }
112
+
113
+ this._loadsAPI = this.endpoint;
114
+ let myLoadAPI = this.endpoint;
115
+
116
+ // Check if mapstyle is not a unparsed JSON
117
+ try {
118
+ this.mapstyle = JSON.parse(this.mapstyle);
119
+ } catch(e) { /* empty */ }
120
+
121
+ // API init
122
+ try {
123
+ this.api = new API(this.endpoint, {
124
+ users: this.users,
125
+ fetch: this.fetchOptions,
126
+ style: this.mapstyle,
127
+ });
128
+ this.api.onceReady()
129
+ .then(() => {
130
+ if(myLoadAPI != this._loadsAPI || !this.api) { return; }
131
+
132
+ let unavailable = this.api.getUnavailableFeatures();
133
+ let available = this.api.getAvailableFeatures();
134
+ available = unavailable.length === 0 ? "✅ All features available" : "✅ Available features: "+available.join(", ");
135
+ unavailable = unavailable.length === 0 ? "" : "🚫 Unavailable features: "+unavailable.join(", ");
136
+ console.info(`🌐 Connected to API "${this.api._metadata.name}" (${this.api._endpoint})
137
+ ℹ️ API runs STAC ${this.api._metadata.stac_version} ${this.api._metadata.geovisio_version ? "& GeoVisio "+this.api._metadata.geovisio_version : ""}
138
+ ${available}
139
+ ${unavailable}
140
+ `.trim());
141
+ })
142
+ .catch(e => this.loader.dismiss(e, this._t.pnx.error_api))
143
+ .finally(() => delete this._loadsAPI);
144
+ }
145
+ catch(e) {
146
+ delete this._loadsAPI;
147
+ if(this.loader?.dismiss) {
148
+ this.loader.dismiss(e, this._t.pnx.error_api);
149
+ }
150
+ else {
151
+ console.error(e);
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Waits for component to have its first loading done.
158
+ *
159
+ * Each inheriting class must override this method.
160
+ * @memberof Panoramax.components.core.Basic#
161
+ * @returns {Promise}
162
+ * @fulfil {null} When initialization is complete.
163
+ * @reject {string} Error message
164
+ */
165
+ onceReady() {
166
+ throw new Error("You must override this method on sub-class");
167
+ }
168
+
169
+ /**
170
+ * Waits for initial API setup.
171
+ * @memberof Panoramax.components.core.Basic#
172
+ * @returns {Promise}
173
+ * @fulfil {null} When API is ready.
174
+ * @reject {string} Error message
175
+ */
176
+ onceAPIReady() {
177
+ if(this.api) {
178
+ return this.api.onceReady();
179
+ }
180
+ else {
181
+ return new Promise(resolve => setTimeout(resolve, 100)).then(this.onceAPIReady.bind(this));
182
+ }
183
+ }
184
+
185
+ /** @private */
186
+ createRenderRoot() {
187
+ return this;
188
+ }
189
+
190
+ /** @private */
191
+ attributeChangedCallback(name, _old, value) {
192
+ super.attributeChangedCallback(name, _old, value);
193
+
194
+ if(name === "endpoint") {
195
+ if(
196
+ !(this._loadsAPI && value && this._loadsAPI === value)
197
+ && !(this.api && this.api._endpoint === value)
198
+ && value
199
+ ) {
200
+ if(this._loadsAPI || this.api) {
201
+ delete this.api;
202
+ delete this._loadsAPI;
203
+ }
204
+ this._setupAPI();
205
+ }
206
+ }
207
+ if(["picture", "sequence"].includes(name)) {
208
+ let seqId, picId, prevSeqId, prevPicId;
209
+
210
+ if(name === "picture") {
211
+ seqId = this.sequence;
212
+ prevSeqId = this.sequence;
213
+ picId = value;
214
+ prevPicId = _old;
215
+ }
216
+ else {
217
+ seqId = value;
218
+ prevSeqId = _old;
219
+ picId = this.picture;
220
+ prevPicId = this.picture;
221
+ }
222
+
223
+ /**
224
+ * Event for sequence/picture selection
225
+ * @event Panoramax.components.core.Basic#select
226
+ * @type {CustomEvent}
227
+ * @property {string} detail.seqId The selected sequence ID
228
+ * @property {string} detail.picId The selected picture ID (or null if not a precise picture clicked)
229
+ * @property {string} [detail.prevSeqId] The previously selected sequence ID (or null if none)
230
+ * @property {string} [detail.prevPicId] The previously selected picture ID (or null if none)
231
+ */
232
+ this.dispatchEvent(new CustomEvent("select", {
233
+ bubbles: true,
234
+ composed: true,
235
+ detail: {
236
+ seqId,
237
+ picId,
238
+ prevSeqId,
239
+ prevPicId,
240
+ }
241
+ }));
242
+ }
243
+ }
244
+
245
+ /**
246
+ * This allows to retrieve an always correct class name.
247
+ * This is crap, but avoids issues with Webpack & so on.
248
+ *
249
+ * Each inheriting class must override this method.
250
+ * @returns {string} The class name (for example "Basic")
251
+ * @memberof Panoramax.components.core.Basic#
252
+ */
253
+ getClassName() {
254
+ return "Basic";
255
+ }
256
+
257
+ /**
258
+ * Change the currently picture and/or sequence.
259
+ * Calling the method without parameters unselects.
260
+ * @param {string} [seqId] The sequence UUID
261
+ * @param {string} [picId] The picture UUID
262
+ * @param {boolean} [force=false] Force select even if already selected
263
+ * @memberof Panoramax.components.core.Basic#
264
+ */
265
+ select(seqId = null, picId = null, force = false) {
266
+ if(force) {
267
+ this.picture = null;
268
+ this.sequence = null;
269
+ }
270
+ this.picture = picId;
271
+ this.sequence = seqId;
272
+ }
273
+
274
+ /**
275
+ * Is the view running in a small container (small embed or smartphone)
276
+ * @returns {boolean} True if container is small
277
+ * @memberof Panoramax.components.core.Basic#
278
+ */
279
+ isWidthSmall() {
280
+ return this?.offsetWidth < 576;
281
+ }
282
+
283
+ /**
284
+ * Is the view running in a small-height container (small embed or smartphone)
285
+ * @returns {boolean} True if container height is small
286
+ * @memberof Panoramax.components.core.Basic#
287
+ */
288
+ isHeightSmall() {
289
+ return this?.offsetHeight < 400;
290
+ }
291
+
292
+ /** @private */
293
+ render() {
294
+ return html`<p>Should not be used directly, use Viewer/CoverageMap/Editor instead</p>`;
295
+ }
296
+
297
+ /**
298
+ * List names of sub-components (like loader, api, map, psv) available in this component.
299
+ * @returns {string[]} Sub-components names.
300
+ * @memberof Panoramax.components.core.Basic#
301
+ */
302
+ getSubComponentsNames() {
303
+ return ["loader", "api"];
304
+ }
305
+
306
+ /**
307
+ * Listen to events from this components or one of its sub-components.
308
+ *
309
+ * For example, you can listen to `map` events using prefix `map:`.
310
+ *
311
+ * ```js
312
+ * me.addEventListener("map:move", doSomething);
313
+ * ```
314
+ * @param {string} type The event type to listen for
315
+ * @param {function} listener The event handler
316
+ * @param {object} [options] [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options)
317
+ * @memberof Panoramax.components.core.Basic#
318
+ */
319
+ addEventListener(type, listener, options) {
320
+ // Check if listener is for sub-component
321
+ let prefix = type.split(":").shift();
322
+ if(prefix && this.getSubComponentsNames().includes(prefix)) {
323
+ const subType = type.substring(prefix.length+1);
324
+
325
+ // Add directly if available
326
+ if(this[prefix]?.addEventListener) {
327
+ this[prefix].addEventListener(subType, listener, options);
328
+ }
329
+ // Wait for addEventListener to be available
330
+ else {
331
+ setTimeout(() => this.addEventListener(type, listener, options), 50);
332
+ }
333
+ }
334
+ // Otherwise, reuse classic function
335
+ else {
336
+ super.addEventListener(type, listener, options);
337
+ }
338
+ }
339
+
340
+ /** @private */
341
+ static GetJSONConverter() {
342
+ return {
343
+ fromAttribute: (value) => {
344
+ return typeof value === "object" ? value : JSON5.parse(value);
345
+ },
346
+ toAttribute: (value) => JSON.stringify(value)
347
+ };
348
+ }
349
+ }
@@ -0,0 +1,9 @@
1
+ pnx-coverage-map {
2
+ display: block;
3
+ }
4
+
5
+ pnx-coverage-map .maplibregl-map {
6
+ position: absolute;
7
+ inset: 0;
8
+ background-color: white;
9
+ }
@@ -0,0 +1,139 @@
1
+ import Basic from "./Basic";
2
+ import Map from "../ui/Map";
3
+ import { getUserLayerId } from "../../utils/map";
4
+ import { NavigationControl } from "!maplibre-gl"; // DO NOT REMOVE THE "!": bundled builds breaks otherwise !!!
5
+ import "./CoverageMap.css";
6
+ import { default as InitParameters, alterMapState } from "../../utils/InitParameters";
7
+
8
+
9
+ /**
10
+ * Coverage Map is a basic map showing Panoramax coverage.
11
+ *
12
+ * Make sure to set width/height through CSS for proper display.
13
+ * @class Panoramax.components.core.CoverageMap
14
+ * @element pnx-coverage-map
15
+ * @extends Panoramax.components.core.Basic
16
+ * @fires Panoramax.components.core.Basic#select
17
+ * @fires Panoramax.components.core.Basic#ready
18
+ * @fires Panoramax.components.core.Basic#broken
19
+ * @property {Panoramax.components.ui.Loader} loader The loader screen
20
+ * @property {Panoramax.utils.API} api The API manager
21
+ * @property {Panoramax.components.ui.Map} map The MapLibre GL map itself
22
+ * @example
23
+ * ```html
24
+ * <pnx-coverage-map
25
+ * endpoint="https://panoramax.openstreetmap.fr/"
26
+ * map='{"bounds": [[-73.9876, 40.7661], [-73.9397, 40.8002]]}'
27
+ * style="width: 300px; height: 250px"
28
+ * />
29
+ * ```
30
+ */
31
+ export default class CoverageMap extends Basic {
32
+ /**
33
+ * Component properties.
34
+ * @memberof Panoramax.components.core.CoverageMap#
35
+ * @type {Object}
36
+ * @mixes Panoramax.components.core.Basic#properties
37
+ * @property {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
38
+ * @property {string} [picture] The picture ID to display
39
+ * @property {string} [sequence] The sequence ID of the picture displayed
40
+ * @property {object} [fetchOptions] 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))
41
+ * @property {string[]} [users=[geovisio]] List of users IDs to use for map display (defaults to general map, identified as "geovisio")
42
+ * @property {string|object} [mapstyle] The map's MapLibre style. This can be an a JSON object conforming to the schema described in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL string pointing to one. Defaults to OSM vector tiles.
43
+ * @property {string} [lang] To override language used for labels. Defaults to using user's preferred languages.
44
+ * @property {object} [map] [Any map option available in Map class](#Panoramax.components.ui.Map).<br />Example: `map='{"bounds": [[-73.9876, 40.7661], [-73.9397, 40.8002]]}'`
45
+ */
46
+ static properties = {
47
+ map: {converter: Basic.GetJSONConverter()},
48
+ ...Basic.properties
49
+ };
50
+
51
+ constructor() {
52
+ super();
53
+
54
+ this._mapContainer = document.createElement("div");
55
+ this.onceAPIReady().then(() => {
56
+ this.loader.setAttribute("value", 30);
57
+ this._initParams = new InitParameters(InitParameters.GetComponentProperties(CoverageMap, this));
58
+ this._initMap();
59
+ });
60
+ }
61
+
62
+ getClassName() {
63
+ return "CoverageMap";
64
+ }
65
+
66
+ onceReady() {
67
+ if(this.map && this.map.waitForEnoughMapLoaded) {
68
+ return this.map.waitForEnoughMapLoaded();
69
+ }
70
+ else {
71
+ return new Promise(resolve => setTimeout(resolve, 100)).then(this.onceReady.bind(this));
72
+ }
73
+ }
74
+
75
+ /** @private */
76
+ render() {
77
+ return [this.loader, this._mapContainer];
78
+ }
79
+
80
+ getSubComponentsNames() {
81
+ const scn = super.getSubComponentsNames();
82
+ scn.push("map");
83
+ return scn;
84
+ }
85
+
86
+ /**
87
+ * Creates map object
88
+ * @private
89
+ */
90
+ _initMap() {
91
+ // Override to avoid display of pictures symbols
92
+ class MyMap extends Map {
93
+ _getLayerStyleProperties(layer) {
94
+ if(layer === "pictures_symbols") {
95
+ return { layout: { visibility: "none" } };
96
+ }
97
+ else {
98
+ return super._getLayerStyleProperties(layer);
99
+ }
100
+ }
101
+ }
102
+
103
+ this.map = new MyMap(this, this._mapContainer, Object.assign({}, this._initParams.getMapInit(), { hash: true }));
104
+ this.map.addControl(new NavigationControl({ showCompass: false }));
105
+ this.loader.setAttribute("value", 70);
106
+
107
+ this.addEventListener("select", this._onSelect.bind(this));
108
+ this.map.on("picture-click", e => this.select(e.seqId, e.picId));
109
+ this.map.on("sequence-click", e => this.select(e.seqId));
110
+
111
+ this.map.waitForEnoughMapLoaded().then(() => {
112
+ alterMapState(this.map, this._initParams.getMapPostInit());
113
+ this.map.reloadLayersStyles();
114
+ this.loader.dismiss();
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Select event handler
120
+ * @private
121
+ * @param {object} e Event details
122
+ */
123
+ _onSelect(e) {
124
+ // Move thumbnail to match selected element
125
+ if(e.detail.picId || e.detail.seqId) {
126
+ const layer = e.detail.picId ? "pictures" : "sequences";
127
+ const features = this.map.queryRenderedFeatures({
128
+ layers: [...this.map._userLayers].map(l => getUserLayerId(l, layer)),
129
+ filter: ["==", ["get", "id"], e.detail.picId || e.detail.seqId]
130
+ });
131
+
132
+ if(features.length >= 0 && features[0] != null) {
133
+ this.map._attachPreviewToPictures({ features }, layer);
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ customElements.define("pnx-coverage-map", CoverageMap);
@@ -0,0 +1,23 @@
1
+ /* General layout */
2
+ pnx-editor {
3
+ display: flex;
4
+ flex-direction: column;
5
+ }
6
+
7
+ pnx-editor .pnx-map,
8
+ pnx-editor .pnx-psv {
9
+ height: 50%;
10
+ }
11
+
12
+ /* Map background widget */
13
+ pnx-editor pnx-map-background {
14
+ position: absolute;
15
+ padding: 7px;
16
+ z-index: 120;
17
+ border-radius: 10px;
18
+ border: 1px solid var(--widget-border-div);
19
+ background-color: var(--widget-bg);
20
+ color: var(--widget-font);
21
+ bottom: 10px;
22
+ left: 10px;
23
+ }