@panoramax/web-viewer 5.0.0-develop-d26305dd → 5.0.0-develop-be5ba1a7

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 (153) hide show
  1. package/build/cjs/index.js +1 -1
  2. package/build/cjs/index_photoviewer.js +1 -1
  3. package/build/esm/components/core/Basic.js +1 -1
  4. package/build/esm/translations/el.json +92 -1
  5. package/package.json +1 -1
  6. package/build/bundle.cjs +0 -3399
  7. package/build/bundle.cjs.map +0 -1
  8. package/build/bundle_photoviewer.cjs +0 -2510
  9. package/build/bundle_photoviewer.cjs.map +0 -1
  10. package/build/components/core/Basic.css +0 -56
  11. package/build/components/core/Basic.js +0 -378
  12. package/build/components/core/CoverageMap.css +0 -10
  13. package/build/components/core/CoverageMap.js +0 -169
  14. package/build/components/core/Editor.css +0 -33
  15. package/build/components/core/Editor.js +0 -398
  16. package/build/components/core/PhotoViewer.css +0 -70
  17. package/build/components/core/PhotoViewer.js +0 -650
  18. package/build/components/core/Viewer.css +0 -130
  19. package/build/components/core/Viewer.js +0 -711
  20. package/build/components/core/index.js +0 -10
  21. package/build/components/index.js +0 -11
  22. package/build/components/index_photoviewer.js +0 -6
  23. package/build/components/layout/BottomDrawer.js +0 -258
  24. package/build/components/layout/CorneredGrid.js +0 -143
  25. package/build/components/layout/Mini.js +0 -121
  26. package/build/components/layout/Tabs.js +0 -140
  27. package/build/components/layout/index.js +0 -9
  28. package/build/components/menus/LocationPrecisionDoc.js +0 -42
  29. package/build/components/menus/MapBackground.js +0 -110
  30. package/build/components/menus/MapFilters.js +0 -567
  31. package/build/components/menus/MapLayers.js +0 -238
  32. package/build/components/menus/MapLegend.js +0 -68
  33. package/build/components/menus/MiniPictureLegend.js +0 -73
  34. package/build/components/menus/PictureLegend.js +0 -379
  35. package/build/components/menus/PictureMetadata.js +0 -380
  36. package/build/components/menus/PlayerOptions.js +0 -93
  37. package/build/components/menus/QualityScoreDoc.js +0 -42
  38. package/build/components/menus/ReportForm.js +0 -132
  39. package/build/components/menus/SemanticsDoc.js +0 -38
  40. package/build/components/menus/SemanticsDownload.js +0 -33
  41. package/build/components/menus/SemanticsFilters.js +0 -153
  42. package/build/components/menus/SemanticsList.js +0 -413
  43. package/build/components/menus/SemanticsMetadata.js +0 -368
  44. package/build/components/menus/Share.js +0 -105
  45. package/build/components/menus/index.js +0 -22
  46. package/build/components/menus/index_photoviewer.js +0 -11
  47. package/build/components/styles.js +0 -557
  48. package/build/components/ui/AnnotationsSwitch.js +0 -159
  49. package/build/components/ui/Button.js +0 -77
  50. package/build/components/ui/ButtonGroup.css +0 -59
  51. package/build/components/ui/ButtonGroup.js +0 -69
  52. package/build/components/ui/CopyButton.js +0 -110
  53. package/build/components/ui/Grade.js +0 -54
  54. package/build/components/ui/GradeFilter.js +0 -122
  55. package/build/components/ui/IconSwitch.js +0 -193
  56. package/build/components/ui/LinkButton.js +0 -67
  57. package/build/components/ui/ListGroup.js +0 -66
  58. package/build/components/ui/ListItem.js +0 -90
  59. package/build/components/ui/Loader.js +0 -203
  60. package/build/components/ui/Map.css +0 -63
  61. package/build/components/ui/Map.js +0 -853
  62. package/build/components/ui/MapMore.js +0 -175
  63. package/build/components/ui/Photo.css +0 -50
  64. package/build/components/ui/Photo.js +0 -1502
  65. package/build/components/ui/Popup.js +0 -145
  66. package/build/components/ui/ProgressBar.js +0 -104
  67. package/build/components/ui/QualityScore.js +0 -147
  68. package/build/components/ui/SearchBar.js +0 -374
  69. package/build/components/ui/SemanticsEditor.js +0 -191
  70. package/build/components/ui/SemanticsTable.js +0 -88
  71. package/build/components/ui/Switch.js +0 -139
  72. package/build/components/ui/TogglableGroup.js +0 -157
  73. package/build/components/ui/index.js +0 -29
  74. package/build/components/ui/index_photoviewer.js +0 -21
  75. package/build/components/ui/widgets/CopyCoordinates.js +0 -75
  76. package/build/components/ui/widgets/GeoSearch.css +0 -21
  77. package/build/components/ui/widgets/GeoSearch.js +0 -150
  78. package/build/components/ui/widgets/Legend.js +0 -190
  79. package/build/components/ui/widgets/LevelSelect.css +0 -51
  80. package/build/components/ui/widgets/LevelSelect.js +0 -143
  81. package/build/components/ui/widgets/MapFiltersButton.js +0 -114
  82. package/build/components/ui/widgets/MapLayersButton.js +0 -79
  83. package/build/components/ui/widgets/OSMEditors.js +0 -155
  84. package/build/components/ui/widgets/PictureLegendActions.js +0 -99
  85. package/build/components/ui/widgets/Player.css +0 -7
  86. package/build/components/ui/widgets/Player.js +0 -154
  87. package/build/components/ui/widgets/SemanticsFiltersButton.js +0 -65
  88. package/build/components/ui/widgets/Zoom.js +0 -84
  89. package/build/components/ui/widgets/index.js +0 -16
  90. package/build/components/ui/widgets/index_photoviewer.js +0 -7
  91. package/build/img/arrow_360.svg +0 -14
  92. package/build/img/arrow_flat.svg +0 -11
  93. package/build/img/arrow_triangle.svg +0 -9
  94. package/build/img/arrow_turn.svg +0 -8
  95. package/build/img/bg_aerial.jpg +0 -0
  96. package/build/img/bg_streets.jpg +0 -0
  97. package/build/img/loader_base.jpg +0 -0
  98. package/build/img/logo_dead.svg +0 -91
  99. package/build/img/marker.svg +0 -17
  100. package/build/img/marker_blue.svg +0 -20
  101. package/build/img/osm.svg +0 -49
  102. package/build/img/panoramax.svg +0 -13
  103. package/build/img/switch_big.svg +0 -54
  104. package/build/img/switch_mini.svg +0 -48
  105. package/build/img/wd.svg +0 -1
  106. package/build/index_photoviewer.js +0 -4
  107. package/build/package.json +0 -148
  108. package/build/servers.js +0 -14
  109. package/build/translations/ar.json +0 -1
  110. package/build/translations/be.json +0 -257
  111. package/build/translations/br.json +0 -81
  112. package/build/translations/cy.json +0 -117
  113. package/build/translations/da.json +0 -300
  114. package/build/translations/de.json +0 -309
  115. package/build/translations/en.json +0 -294
  116. package/build/translations/eo.json +0 -235
  117. package/build/translations/es.json +0 -292
  118. package/build/translations/fi.json +0 -1
  119. package/build/translations/fr.json +0 -294
  120. package/build/translations/hr.json +0 -294
  121. package/build/translations/hu.json +0 -294
  122. package/build/translations/it.json +0 -306
  123. package/build/translations/ja.json +0 -182
  124. package/build/translations/ko.json +0 -1
  125. package/build/translations/nl.json +0 -305
  126. package/build/translations/nn.json +0 -1
  127. package/build/translations/pl.json +0 -169
  128. package/build/translations/pt.json +0 -296
  129. package/build/translations/pt_BR.json +0 -304
  130. package/build/translations/sv.json +0 -182
  131. package/build/translations/ti.json +0 -9
  132. package/build/translations/tr.json +0 -297
  133. package/build/translations/uk.json +0 -268
  134. package/build/translations/zh_Hant.json +0 -309
  135. package/build/utils/API.js +0 -928
  136. package/build/utils/InitParameters.js +0 -521
  137. package/build/utils/MapStyleComposer.js +0 -889
  138. package/build/utils/PanoraMapProtocol.js +0 -49
  139. package/build/utils/PhotoAdapter.js +0 -49
  140. package/build/utils/PresetsManager.js +0 -148
  141. package/build/utils/SemanticsMapProtocol.js +0 -144
  142. package/build/utils/URLHandler.js +0 -426
  143. package/build/utils/geocoder.js +0 -203
  144. package/build/utils/i18n.js +0 -128
  145. package/build/utils/index.js +0 -17
  146. package/build/utils/index_photoviewer.js +0 -14
  147. package/build/utils/indoor.js +0 -200
  148. package/build/utils/map.js +0 -788
  149. package/build/utils/picture.js +0 -507
  150. package/build/utils/semantics.js +0 -321
  151. package/build/utils/services.js +0 -148
  152. package/build/utils/utils.js +0 -433
  153. package/build/utils/widgets.js +0 -110
@@ -1,853 +0,0 @@
1
- import {
2
- TILES_PICTURES_ZOOM, getThumbGif, isNullCoordinates,
3
- } from "../../utils/map.js";
4
- import SemanticsMapProtocol from "../../utils/SemanticsMapProtocol.js";
5
- import PanoraMapProtocol from "../../utils/PanoraMapProtocol.js";
6
- import { DISABLED_LEVEL, initIndoorEqualOnMap } from "../../utils/indoor.js";
7
- import { svgToImg } from "../../utils/utils.js";
8
- import MapLibreStyles from "maplibre-gl/dist/maplibre-gl.css" with { type: "css" };
9
- import MapStyles from "./Map.css" with { type: "css" };
10
- document.adoptedStyleSheets.push(MapLibreStyles);
11
- document.adoptedStyleSheets.push(MapStyles);
12
-
13
- // MapLibre imports
14
- import "maplibre-gl";
15
- //Import maplibreglWorker from "maplibre-gl/dist/maplibre-gl-csp-worker.js";
16
- import * as pmtiles from "pmtiles";
17
- //Maplibregl.workerClass = maplibreglWorker;
18
- // eslint-disable-next-line no-undef
19
- maplibregl.addProtocol("pmtiles", new pmtiles.Protocol().tile);
20
- // eslint-disable-next-line no-undef
21
- maplibregl.addProtocol("panora", new PanoraMapProtocol().tile());
22
- // eslint-disable-next-line no-undef
23
- maplibregl.addProtocol("panoras", new PanoraMapProtocol().tile());
24
-
25
- const MarkerBaseSVG = await fetch(new URL("../../img/marker.svg", import.meta.url).href).then(res => res.text());
26
- const MarkerSelectedSVG = await fetch(new URL("../../img/marker_blue.svg", import.meta.url).href).then(res => res.text());
27
-
28
-
29
- /**
30
- * Map is the component showing pictures and sequences geolocation.
31
- *
32
- * Note that all functions of [MapLibre GL JS class Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/) are also available.
33
- *
34
- * A more complete version of Map (with filters & themes) is available through [MapMore class](#Panoramax.components.ui.MapMore)
35
- *
36
- * ℹ️ Many style management is done in [MapStyleComposer](#Panoramax.utils.MapStyleComposer), which is available as a property of parent component.
37
- *
38
- * ⚠️ 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.
39
- * It uses instead [`on`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#on) and `fire` functions from MapLibre Map class.
40
- * `fire` function doesn't take directly [`Event`](https://developer.mozilla.org/fr/docs/Web/API/Event) objects, but a string and object data.
41
- * A shorthand `addEventListener` function is added for simpler usage.
42
- * @class Panoramax.components.ui.Map
43
- * @extends [maplibregl.Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/)
44
- * @param {Panoramax.components.core.Basic} parent The parent view
45
- * @param {Element} container The DOM element to create into
46
- * @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)
47
- * @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).
48
- * @param {object} [options.basemaps] List of complementary basemaps, as { id: {MapLibre Style JSON or URL} }. Note that special IDs streets and aerial are reserved for default backgrounds.
49
- * @param {string} [options.background=streets] Choose default map background to display (streets or aerial, if raster aerial background available). Defaults to street.
50
- * @param {string} [options.attributionControl.customAttribution] To override default map attribution.
51
- * @param {boolean} [options.picMarkerDraggable] To make the picture marker draggable, default to false.
52
- * @param {object} [options.indoor] The indoor= MapLibre plugin options. This must be a JSON object following [IndoorEqual parameters](https://indoorequal.com/doc/maplibre-gl-indoorequal/api#parameters). Note that this is only available if [maplibre-gl-indoorequal](https://indoorequal.com/doc/maplibre-gl-indoorequal) plugin is loaded in your web page.
53
- * @param {string} [options.indoor.level] (only if indoor= plugin is enabled) The initial indoor level to display. Defaults to ground or disabled depending on indoor map availability. Do not use if visible=false.
54
- * @param {boolean} [options.indoor.visible=true] (only if indoor= plugin is enabled) The initial indoor visibility. Set to false to avoid data loading before user explicitly enables indoor through widgets. If set to false, you may not set any level option in component.
55
- * @fires Panoramax.components.ui.Map#sequence-hover
56
- * @fires Panoramax.components.ui.Map#sequence-click
57
- * @fires Panoramax.components.ui.Map#picture-click
58
- * @fires Panoramax.components.ui.Map#ready
59
- * @example
60
- * const map = new Panoramax.components.ui.Map(viewer, mapNode, {center: {lat: 48.7, lng: -1.7}});
61
- */
62
- // eslint-disable-next-line no-undef
63
- export default class Map extends maplibregl.Map {
64
- constructor(parent, container, options = {}) {
65
- super({
66
- container: container,
67
- style: parent.mapStyleComposer.getMapStyle(),
68
- center: [0,0],
69
- zoom: 0,
70
- maxZoom: 24,
71
- attributionControl: false,
72
- dragRotate: false,
73
- pitchWithRotate: false,
74
- touchZoomRotate: true,
75
- touchPitch: false,
76
- doubleClickZoom: false,
77
- canvasContextAttributes: {
78
- preserveDrawingBuffer: !parent.isWidthSmall(),
79
- },
80
- transformRequest: parent.api._getMapRequestTransform(),
81
- locale: parent._t.maplibre,
82
- hash: false,
83
- ...options
84
- });
85
-
86
- this._semMapProtocol = new SemanticsMapProtocol(parent);
87
- // eslint-disable-next-line no-undef
88
- maplibregl.addProtocol("sem", this._semMapProtocol.tile());
89
- this._loadMarkerImages();
90
-
91
- this._parent = parent;
92
- this._parent.mapStyleComposer.map = this;
93
-
94
- this._options = options;
95
- this.getContainer().classList.add("pnx-map");
96
-
97
- // Disable touch rotate
98
- if(options.touchZoomRotate === undefined) {
99
- this?.touchZoomRotate?.disableRotation();
100
- }
101
-
102
- // Handle raster source
103
- if(this._options.raster) {
104
- this._options.background = this._options.background || "streets";
105
- }
106
- // Supplementary basemaps
107
- if(this._options.basemaps) {
108
- this._parent.mapStyleComposer._createManyBasemaps(this._options.basemaps, this._options.background);
109
- }
110
-
111
- initIndoorEqualOnMap(this, ![undefined, DISABLED_LEVEL].includes(this._options.level));
112
-
113
- // eslint-disable-next-line no-undef
114
- this._attribution = new maplibregl.AttributionControl({ compact: false, ...options.attributionControl });
115
- this.addControl(this._attribution);
116
-
117
- this._initMapPosition();
118
-
119
- // Widgets and markers
120
- this._picMarker = this._getPictureMarker();
121
- this._picMarkerPreview = this._getPictureMarker(false);
122
-
123
- // Popup
124
- this._picPreviewTimer = null;
125
- // eslint-disable-next-line no-undef
126
- this._picPopup = new maplibregl.Popup({
127
- closeButton: false,
128
- closeOnClick: !this._parent.isWidthSmall(),
129
- offset: 15
130
- });
131
-
132
- // Cache for pictures and sequences thumbnails
133
- this._picThumbUrl = {};
134
- this._seqPictures = {};
135
-
136
- // Parent selection
137
- this._parent.addEventListener("select", this._parent.mapStyleComposer.updateMapStyle.bind(this._parent.mapStyleComposer));
138
-
139
- // Post styles loading handlers
140
- this._parent.mapStyleComposer.addEventListener("panoramax-changed", this._onPanoramaxChanged.bind(this));
141
-
142
- // Timeout for initial loading
143
- setTimeout(() => {
144
- if(!this.loaded() && this._parent?.loader.isVisible()) {
145
- this._parent.loader.dismiss({}, this._parent._t.map.slow_loading, async () => {
146
- await this._postLoad();
147
- this._parent.loader.dismiss();
148
- });
149
- }
150
- }, 15000);
151
-
152
- this.waitForEnoughMapLoaded().then(async () => await this._postLoad());
153
- }
154
-
155
- /**
156
- * @private
157
- */
158
- _postLoad() {
159
- this.resize();
160
-
161
- // Map background click
162
- this.on("dblclick", () => clearTimeout(this._gridFocus));
163
- this.on("click", (e) => {
164
- clearTimeout(this._gridFocus);
165
-
166
- if(e.defaultPrevented === false) {
167
- this._dropPreview();
168
- if(this._parent.isMapWide?.() && !this._parent._hasMenuOpened()) {
169
- this._gridFocus = setTimeout(() => this._parent?.grid?.toggleAway(), 100);
170
- }
171
- }
172
- });
173
-
174
- // Untoggle away widgets
175
- const untoggleGridWidgets = () => {
176
- if(this._parent.isMapWide?.() && this._parent?.grid?._hidden) {
177
- // No clearTimeout as map continuously send move events
178
- this._gridFocus = setTimeout(() => this._parent?.grid?.toggleAway(true), 100);
179
- }
180
- };
181
- this.on("mousemove", untoggleGridWidgets);
182
- this.on("touchmove", untoggleGridWidgets);
183
-
184
- /**
185
- * Event when map is ready to display.
186
- * This includes Maplibre initial load, enough map data display and styling.
187
- * @event Panoramax.components.ui.Map#ready
188
- * @type {maplibregl.util.evented.Event}
189
- */
190
- this.fire("ready");
191
- }
192
-
193
- /** @private */
194
- _dropPreview(delayed) {
195
- clearTimeout(this._picDropPreviewTimer);
196
- if(delayed) {
197
- this._picDropPreviewTimer = setTimeout(this._dropPreview.bind(this), 250);
198
- }
199
- else {
200
- clearTimeout(this._picPreviewTimer);
201
- delete this._picPopup._picId;
202
- delete this._picPopup._loading;
203
- this._picPopup.remove();
204
- this._picMarkerPreview.remove();
205
- }
206
- }
207
-
208
- /**
209
- * Destroy any form of life in this component
210
- * @memberof Panoramax.components.ui.Map#
211
- */
212
- destroy() {
213
- this.remove();
214
- delete this._semMapProtocol;
215
- delete this._parent;
216
- delete this._options;
217
- delete this._attribution;
218
- delete this._picMarker;
219
- delete this._picMarkerPreview;
220
- delete this._picPreviewTimer;
221
- delete this._picPopup;
222
- delete this._picThumbUrl;
223
- delete this._seqPictures;
224
- }
225
-
226
- /**
227
- * Helper to know when enough map background and Panoramax tiles are loaded for a proper display.
228
- * @returns {Promise} Resolves when enough is loaded
229
- * @memberof Panoramax.components.ui.Map#
230
- */
231
- waitForEnoughMapLoaded() {
232
- return new Promise((resolve) => {
233
- let nbBgTiles = 0;
234
- let nbFgTiles = 0;
235
- let nbLoadedBgTiles = 0;
236
- let nbLoadedFgTiles = 0;
237
-
238
- const onSourceDataLoading = e => {
239
- if(e.dataType === "source" && e.tile) {
240
- if(e.sourceId.startsWith("geovisio")) {
241
- nbFgTiles++;
242
- }
243
- else {
244
- nbBgTiles++;
245
- }
246
- }
247
- };
248
- const onSourceData = e => {
249
- if(e.dataType === "source" && e.tile) {
250
- if(e.sourceId.startsWith("geovisio")) {
251
- nbLoadedFgTiles++;
252
- if(e.isSourceLoaded) { nbLoadedFgTiles = nbFgTiles; }
253
- }
254
- else {
255
- nbLoadedBgTiles++;
256
- if(e.isSourceLoaded) { nbLoadedBgTiles = nbBgTiles; }
257
- }
258
- }
259
- checkEnoughLoaded();
260
- };
261
-
262
- const checkEnoughLoaded = () => {
263
- if(nbLoadedBgTiles / nbBgTiles >= 0.75 && (nbFgTiles === 0 || nbLoadedFgTiles / nbFgTiles >= 0.75)) {
264
- this.off("sourcedata", onSourceData);
265
- this.off("sourcedataloading", onSourceDataLoading);
266
- resolve();
267
- }
268
- };
269
-
270
- this.on("sourcedataloading", onSourceDataLoading);
271
- this.on("sourcedata", onSourceData);
272
- });
273
- }
274
-
275
- /**
276
- * Sets map view based on returned API bbox (if no precise option given by user).
277
- * @private
278
- * @memberof Panoramax.components.ui.Map#
279
- */
280
- _initMapPosition() {
281
- if(
282
- isNullCoordinates(this._options.center)
283
- && (!this._options.zoom || this._options.zoom === 0)
284
- && (!this._options.hash)
285
- ) {
286
- this._parent.onceAPIReady().then(() => {
287
- let bbox = this._parent?.api?.getDataBbox();
288
- if(bbox) {
289
- try {
290
- // eslint-disable-next-line no-undef
291
- bbox = new maplibregl.LngLatBounds(bbox);
292
- if(this.loaded()) { this.fitBounds(bbox, { "animate": false }); }
293
- else { this.waitForEnoughMapLoaded().then(() => this.fitBounds(bbox, { "animate": false })); }
294
- }
295
- catch(e) {
296
- console.warn("Received invalid bbox: "+bbox);
297
- }
298
- }
299
- });
300
- }
301
- }
302
-
303
- /**
304
- * Adds a new source.
305
- * This is an override of MapLibre's function to catch sources missing in MapStyleComposer
306
- */
307
- addSource(id, source) {
308
- this._parent.mapStyleComposer.registerSource(id, source);
309
- super.addSource(id, source);
310
- }
311
-
312
- /**
313
- * Adds a new map layer.
314
- * This is an override of MapLibre's function to catch layers missing in MapStyleComposer
315
- */
316
- addLayer(layer, beforeId) {
317
- this._parent.mapStyleComposer.registerLayer(layer, beforeId);
318
- super.addLayer(layer, beforeId);
319
- }
320
-
321
- /**
322
- * Load markers into map for use in map layers.
323
- * @private
324
- * @memberof Panoramax.components.ui.Map#
325
- */
326
- _loadMarkerImages() {
327
- [
328
- { id: "pnx-marker", img: MarkerBaseSVG },
329
- ].forEach(m => {
330
- const img = new Image(64, 64);
331
- img.onload = () => this.addImage(m.id, img);
332
- svgToImg(m.img, img);
333
- });
334
- }
335
-
336
- /**
337
- * Is Quality Score available in vector tiles ?
338
- * @private
339
- * @memberof Panoramax.components.ui.Map#
340
- */
341
- _hasQualityScore() {
342
- const fields = this.getStyle()?.metadata?.["panoramax:fields"] || {};
343
- return fields?.pictures?.includes("gps_accuracy") && fields?.pictures?.includes("h_pixel_density");
344
- }
345
-
346
- /**
347
- * Are 360/flat pictures stats available in vector tiles for grid layer ?
348
- * @private
349
- * @memberof Panoramax.components.ui.Map#
350
- */
351
- _hasGridStats() {
352
- const fields = this.getStyle()?.metadata?.["panoramax:fields"] || {};
353
- return fields?.grid?.includes("nb_360_pictures") && fields?.grid?.includes("nb_flat_pictures")
354
- && fields?.grid?.includes("coef_360_pictures") && fields?.grid?.includes("coef_flat_pictures");
355
- }
356
-
357
- /**
358
- * Are specific 360/flat pictures stats available for logged-in users in vector tiles for grid layer ?
359
- * @private
360
- * @memberof Panoramax.components.ui.Map#
361
- */
362
- _hasLoggedGridStats() {
363
- const fields = this.getStyle()?.metadata?.["panoramax:fields"] || {};
364
- return fields?.grid?.includes("logged_coef_360_pictures")
365
- && fields?.grid?.includes("logged_coef_flat_pictures")
366
- && fields?.grid?.includes("logged_coef");
367
- }
368
-
369
- /**
370
- * Force refresh of vector tiles data
371
- * @memberof Panoramax.components.ui.Map#
372
- */
373
- reloadVectorTiles() {
374
- if(!this._parent.mapStyleComposer?.layerRanges?.panoramax) { return; }
375
-
376
- Object
377
- .values(this._parent.mapStyleComposer.layerRanges.panoramax)
378
- .forEach(p => {
379
- Object.keys(p.sources).forEach(sid => {
380
- const sm = this.getSource(sid);
381
- if(sm) { sm.setTiles(sm.tiles); }
382
- });
383
- });
384
- }
385
-
386
- /**
387
- * Shows on map a picture position and heading.
388
- *
389
- * If no longitude & latitude are set, marker is removed from map.
390
- * @memberof Panoramax.components.ui.Map#
391
- * @param {number} lon The longitude
392
- * @param {number} lat The latitude
393
- * @param {number} heading The heading
394
- * @param {boolean} [skipCenter=false] Set to true to avoid map centering on marker
395
- * @param {string} [picId=null] The picture Id
396
- */
397
- displayPictureMarker(lon, lat, heading, skipCenter = false, picId = null) {
398
- this._picMarkerPreview.remove();
399
-
400
- // Show marker corresponding to selection
401
- if(lon !== undefined && lat !== undefined) {
402
- this._picMarker
403
- .setLngLat([lon, lat])
404
- .setRotation(heading)
405
- .addTo(this);
406
- this._picMarker.picId = picId ;
407
- }
408
- else {
409
- this._picMarker.remove();
410
- }
411
-
412
- // Update map style to see selected sequence
413
- this._parent.mapStyleComposer.updateMapStyle();
414
-
415
- // Move map to picture coordinates
416
- if(!skipCenter && lon !== undefined && lat !== undefined) {
417
- this.jumpTo({ // No animation to avoid conflict on seq=* load
418
- center: [lon, lat],
419
- zoom: this.getZoom() < TILES_PICTURES_ZOOM+2 ? TILES_PICTURES_ZOOM+2 : this.getZoom(),
420
- });
421
- }
422
- }
423
-
424
- /** @private */
425
- _onPanoramaxChanged() {
426
- const msc = this._parent.mapStyleComposer;
427
- const picLayerId = msc.layerRanges.panoramax[msc.panoramax].layers.find(l => l.id.endsWith("_pictures"))?.id;
428
- const seqPlusLayerId = msc.layerRanges.panoramax[msc.panoramax].layers.find(l => l.id.endsWith("_sequences_plus"))?.id;
429
- const gridLayerId = msc.layerRanges.panoramax[msc.panoramax].layers.find(l => l.id.endsWith("_grid"))?.id;
430
- if(!picLayerId || !seqPlusLayerId) { console.error("Panoramax source not loaded"); }
431
-
432
- // Pictures
433
- this.on("mousemove", picLayerId, e => {
434
- this.getCanvas().style.cursor = "pointer";
435
- const eCopy = Object.assign({}, e);
436
- this._attachPreviewToPictures(eCopy, picLayerId);
437
- });
438
-
439
- this.on("mouseleave", picLayerId, () => {
440
- this.getCanvas().style.cursor = "";
441
- });
442
-
443
- this.on("click", picLayerId, this._onPictureClick.bind(this));
444
-
445
- // Sequences
446
- this.on("mousemove", seqPlusLayerId, e => {
447
- if(this.getZoom() >= TILES_PICTURES_ZOOM) { return; }
448
- this._onSequenceHover(e);
449
- this.getCanvas().style.cursor = "pointer";
450
- if(e.features[0].properties.id) {
451
- const eCopy = Object.assign({}, e);
452
- this._attachPreviewToPictures(eCopy, seqPlusLayerId);
453
- }
454
- });
455
-
456
- this.on("mouseleave", seqPlusLayerId, () => {
457
- this.getCanvas().style.cursor = "";
458
- this._dropPreview(true);
459
- });
460
-
461
- this.on("click", seqPlusLayerId, e => {
462
- e.preventDefault();
463
- this._onSequenceClick(e);
464
- });
465
-
466
- // Grid
467
- if(gridLayerId) {
468
- this.on("mousemove", gridLayerId, e => {
469
- if(this.getZoom() <= TILES_PICTURES_ZOOM+1) {
470
- this.getCanvas().style.cursor = "pointer";
471
- const eCopy = Object.assign({}, e);
472
- this._attachPreviewToPictures(eCopy, gridLayerId);
473
- }
474
- });
475
-
476
- this.on("mouseleave", gridLayerId, () => {
477
- this._dropPreview();
478
- this.getCanvas().style.cursor = "";
479
- });
480
-
481
- this.on("click", gridLayerId, e => {
482
- e.preventDefault();
483
- this.flyTo({ center: e.lngLat, zoom: TILES_PICTURES_ZOOM-6 });
484
- });
485
- }
486
- }
487
-
488
- /**
489
- * Creates popup manager for preview of pictures.
490
- * @private
491
- * @param {object} e The event thrown by MapLibre
492
- * @param {string} from The event source layer
493
- * @memberof Panoramax.components.ui.Map#
494
- */
495
- _attachPreviewToPictures(e, from) {
496
- let f = e.features.pop();
497
- // eslint-disable-next-line eqeqeq
498
- if(!f || f.properties.id == this._picPopup._picId) { return; }
499
-
500
- clearTimeout(this._picPreviewTimer);
501
- clearTimeout(this._picDropPreviewTimer);
502
- this._picPopup._loading = f.properties.id;
503
- this._picPopup._picId = f.properties.id;
504
- delete this._picPopup._seqId;
505
-
506
- if(!e.lngLat) { e.lngLat = this.getCenter(); }
507
-
508
-
509
- /*
510
- * Display picture preview marker
511
- */
512
-
513
- let picPreview = null;
514
- if(from.includes("_pictures") && f.geometry.type === "Point") { // From picture hover
515
- picPreview = f;
516
- }
517
- else if(this.getZoom() < TILES_PICTURES_ZOOM && from.includes("_sequences") && f.geometry.type === "LineString") { // From sequence hover
518
- // Look for nearest sequence point
519
- let prevDist = null;
520
- for(let i=0; i < f.geometry.coordinates.length; i++) {
521
- // eslint-disable-next-line no-undef
522
- let dist = e.lngLat.distanceTo(new maplibregl.LngLat(...f.geometry.coordinates[i]));
523
- if(!prevDist || dist < prevDist) {
524
- picPreview = {
525
- type: "Feature",
526
- geometry: { type: "Point", coordinates: f.geometry.coordinates[i] },
527
- properties: {
528
- first_sequence: f.properties.id,
529
- collection: f.properties.id,
530
- }
531
- };
532
- prevDist = dist;
533
- }
534
- }
535
- }
536
-
537
- if(picPreview && picPreview.properties.heading !== undefined) {
538
- this._picPopup._seqId = picPreview.properties.first_sequence;
539
- this._picMarkerPreview
540
- .setLngLat?.(picPreview.geometry.coordinates)
541
- .setRotation(picPreview.properties.heading || 0)
542
- .addTo(this);
543
- }
544
- else { this._picMarkerPreview?.remove?.(); }
545
-
546
-
547
- /*
548
- * Display thumbnail popup
549
- */
550
-
551
- let thumbCoords = picPreview ? picPreview.geometry.coordinates : e.lngLat;
552
- this._picPopup
553
- .setLngLat(thumbCoords)
554
- .addTo(this);
555
-
556
- // Only show GIF loader if thumbnail is not in browser cache
557
- if(!picPreview && !this._picThumbUrl[f.properties.id]) {
558
- this._picPopup.setDOMContent(getThumbGif(this._parent._t));
559
- }
560
-
561
- const displayThumb = thumbUrl => {
562
- if(this._picPopup._loading === f.properties.id) {
563
- if(thumbUrl) {
564
- let content = document.createElement("img");
565
- content.classList.add("pnx-map-thumb");
566
- content.alt = this._parent._t.map.thumbnail;
567
- let img = new Image();
568
- img.src = thumbUrl;
569
-
570
- img.addEventListener("load", () => {
571
- if(this._picPopup._loading !== f.properties.id) { return; }
572
-
573
- if(f.properties.hidden) {
574
- content.children[0].src = img.src;
575
- }
576
- else {
577
- content.src = img.src;
578
- }
579
- this._picPopup.setDOMContent(content);
580
- delete this._picPopup._loading;
581
- });
582
-
583
- if(f.properties.hidden) {
584
- const legend = document.createElement("div");
585
- legend.classList.add("pnx-map-thumb-legend");
586
- legend.appendChild(document.createTextNode(this._parent._t.map.not_public));
587
- const container = document.createElement("div");
588
- container.appendChild(content);
589
- container.appendChild(legend);
590
- content = container;
591
- }
592
- }
593
- else {
594
- this._picPopup.remove();
595
- }
596
- }
597
- };
598
-
599
- if(this._picPopup._loading !== f.properties.id) { return; }
600
-
601
- // Hover on cached thumbnail
602
- if(this._picThumbUrl[picPreview?.properties?.id || f.properties.id]) {
603
- displayThumb(this._picThumbUrl[picPreview?.properties?.id || f.properties.id]);
604
- }
605
- // Throttled call to API
606
- else {
607
- clearTimeout(this._picPreviewTimer);
608
- this._picPreviewTimer = setTimeout(() => {
609
- if(this._picPopup._loading !== f.properties.id) { return; }
610
-
611
- // Hover on a single picture
612
- if(picPreview) {
613
- if(picPreview.properties.id) {
614
- this._getPictureThumbURL(picPreview.properties.id, picPreview.properties.collection).then(displayThumb);
615
- }
616
- else {
617
- this._getSequenceThumbURL(f.properties.collection, thumbCoords).then(displayThumb);
618
- }
619
- }
620
- // Hover on a grid cell
621
- else if(from.endsWith("_grid")) {
622
- this._getThumbURL(thumbCoords).then(displayThumb);
623
- }
624
- // Hover on a sequence
625
- else if(from.includes("_sequences")) {
626
- this._getSequenceThumbURL(f.properties.id, thumbCoords).then(displayThumb);
627
- }
628
- }, 50);
629
- }
630
- }
631
-
632
- /**
633
- * Get picture thumbnail URL at given coordinates
634
- *
635
- * @param {LngLat} coordinates The map coordinates
636
- * @returns {Promise} Promise resolving on picture thumbnail URL, or null on timeout
637
- * @private
638
- * @memberof Panoramax.components.ui.Map#
639
- */
640
- _getThumbURL(coordinates) {
641
- return this._parent.getAPI().getPicturesAroundCoordinates(coordinates.lat, coordinates.lng, 0.1, 1).then(res => {
642
- const p = res?.features?.pop();
643
- return p ? this._parent.getAPI().findThumbnailInPictureFeature(p) : null;
644
- });
645
- }
646
-
647
-
648
- /**
649
- * Get picture thumbnail URL for a given sequence ID
650
- *
651
- * @param {string} seqId The sequence ID
652
- * @param {LngLat} [coordinates] The map coordinates
653
- * @returns {Promise} Promise resolving on picture thumbnail URL, or null on timeout
654
- * @private
655
- * @memberof Panoramax.components.ui.Map#
656
- */
657
- _getSequenceThumbURL(seqId, coordinates) {
658
- if(coordinates) {
659
- return this._parent.getAPI().getPicturesAroundCoordinates(coordinates.lat || coordinates[1], coordinates.lng || coordinates[0], 1, 1, seqId)
660
- .then(results => {
661
- if(results?.features?.length > 0) {
662
- return this._parent.getAPI().findThumbnailInPictureFeature(results.features[0]);
663
- }
664
- else {
665
- return this._parent.getAPI().getPictureThumbnailURLForSequence(seqId);
666
- }
667
- });
668
- }
669
- else {
670
- return this._parent.getAPI().getPictureThumbnailURLForSequence(seqId);
671
- }
672
- }
673
-
674
- /**
675
- * Get picture thumbnail URL for a given picture ID.
676
- * It handles a client-side cache based on raw API responses.
677
- *
678
- * @param {string} picId The picture ID
679
- * @param {string} [seqId] The sequence ID (can speed up search if available)
680
- * @returns {Promise} Promise resolving on picture thumbnail URL, or null on timeout
681
- * @memberof Panoramax.components.ui.Map#
682
- * @private
683
- */
684
- _getPictureThumbURL(picId, seqId) {
685
- let res = Promise.resolve();
686
-
687
- if(picId) {
688
- if(this._picThumbUrl[picId] !== undefined) {
689
- res = typeof this._picThumbUrl[picId] === "string" ? Promise.resolve(this._picThumbUrl[picId]) : this._picThumbUrl[picId];
690
- }
691
- else {
692
- this._picThumbUrl[picId] = this._parent.getAPI().getPictureThumbnailURL(picId, seqId).then(url => {
693
- if(url) {
694
- this._picThumbUrl[picId] = url;
695
- return url;
696
- }
697
- else {
698
- this._picThumbUrl[picId] = null;
699
- return null;
700
- }
701
- })
702
- .catch(() => {
703
- this._picThumbUrl[picId] = null;
704
- });
705
- res = this._picThumbUrl[picId];
706
- }
707
- }
708
-
709
- return res;
710
- }
711
-
712
- /**
713
- * Create a ready-to-use picture marker
714
- *
715
- * @returns {maplibregl.Marker} The generated marker
716
- * @private
717
- * @memberof Panoramax.components.ui.Map#
718
- */
719
- _getPictureMarker(selected = true) {
720
- // eslint-disable-next-line no-undef
721
- return new maplibregl.Marker({
722
- element: svgToImg(selected ? MarkerSelectedSVG : MarkerBaseSVG),
723
- picId: null
724
- })
725
- // Only picMarker could be draggable, don't for picMarkerPreview.
726
- .setDraggable(selected && this._options.picMarkerDraggable)
727
- ;
728
- }
729
-
730
- /**
731
- * Event handler for sequence hover
732
- * @private
733
- * @param {object} e Event data
734
- * @memberof Panoramax.components.ui.Map#
735
- */
736
- _onSequenceHover(e) {
737
- e.preventDefault();
738
- if(e.features.length > 0 && e.features[0].properties?.id) {
739
- /**
740
- * Event when a sequence on map is hovered (not selected)
741
- *
742
- * @event Panoramax.components.ui.Map#sequence-hover
743
- * @type {maplibregl.util.evented.Event}
744
- * @property {string} seqId The hovered sequence ID
745
- */
746
- this.fire("sequence-hover", { seqId: e.features[0].properties.id });
747
- }
748
- }
749
-
750
- /**
751
- * Event handler for sequence click
752
- * @private
753
- * @param {object} e Event data
754
- * @memberof Panoramax.components.ui.Map#
755
- */
756
- _onSequenceClick(e) {
757
- e.preventDefault();
758
- e.originalEvent.stopImmediatePropagation();
759
-
760
- // Skip if pictures navigation is set to "pic"
761
- if(this._parent.psv?.getPicturesNavigation() === "pic") {
762
- return;
763
- }
764
-
765
- if(e.features.length > 0) {
766
- let seq = e.features[0];
767
-
768
- // If clicks on many sequences, prefer the one corresponding to picture thumbnail preview
769
- if(e.features.length > 1 && this._picPopup._seqId) {
770
- seq = e.features.find(s => s.properties.id === this._picPopup._seqId) || seq;
771
- }
772
-
773
- if(seq.properties?.id) {
774
- /**
775
- * Event when a sequence on map is clicked
776
- * @event Panoramax.components.ui.Map#sequence-click
777
- * @type {maplibregl.util.evented.Event}
778
- * @property {string} seqId The clicked sequence ID
779
- * @property {maplibregl.LngLat} coordinates The coordinates of user click
780
- */
781
- this.fire("sequence-click", {
782
- seqId: seq.properties.id,
783
- coordinates: e.lngLat
784
- });
785
- }
786
- }
787
- }
788
-
789
- /**
790
- * Event handler for picture click
791
- * @private
792
- * @param {object} e Event data
793
- * @memberof Panoramax.components.ui.Map#
794
- */
795
- _onPictureClick(e) {
796
- e.preventDefault();
797
- e.originalEvent.stopImmediatePropagation();
798
-
799
- // Skip if pictures navigation is set to "pic"
800
- if(this._parent.psv?.getPicturesNavigation() === "pic") {
801
- return;
802
- }
803
-
804
- // Skip if click happens as well on a semantic layer
805
- const features = this.queryRenderedFeatures(e.point);
806
- if(features.length > 1) {
807
- if(features.find(f => f?.source?.startsWith("sem-"))) { return; }
808
- }
809
-
810
- const f = e?.features?.length > 0 ? e.features[0] : null;
811
- if(f?.properties?.id) {
812
- // Look for a potential sequence ID
813
- let seqId = null;
814
- try {
815
- if(f.properties.sequences) {
816
- if(!Array.isArray(f.properties.sequences)) { f.properties.sequences = JSON.parse(f.properties.sequences); }
817
- seqId = f.properties.sequences.pop();
818
- }
819
- }
820
- catch(e) {
821
- console.log("Sequence ID is not available in vector tiles for picture "+f.properties.id);
822
- }
823
-
824
- /**
825
- * Event when a picture on map is clicked
826
- *
827
- * @event Panoramax.components.ui.Map#picture-click
828
- * @type {maplibregl.util.evented.Event}
829
- * @property {string} picId The clicked picture ID
830
- * @property {string} seqId The clicked picture's sequence ID
831
- * @property {object} feature The GeoJSON feature of the picture
832
- */
833
- this.fire("picture-click", {
834
- picId: f.properties.id,
835
- seqId,
836
- feature: f
837
- });
838
- }
839
- }
840
-
841
- /**
842
- * Listen to map events.
843
- * 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.
844
- * @param {string} type The event type to listen for
845
- * @param {function} listener The event handler
846
- * @param {boolean} [options.once=false] Set to true to only listen to first event.
847
- * @memberof Panoramax.components.ui.Map#
848
- */
849
- addEventListener(type, listener, options = {}) {
850
- if(options?.once) { this.once(type, listener); }
851
- else { this.on(type, listener); }
852
- }
853
- }