@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.
- package/build/cjs/index.js +1 -1
- package/build/cjs/index_photoviewer.js +1 -1
- package/build/esm/components/core/Basic.js +1 -1
- package/build/esm/translations/el.json +92 -1
- package/package.json +1 -1
- package/build/bundle.cjs +0 -3399
- package/build/bundle.cjs.map +0 -1
- package/build/bundle_photoviewer.cjs +0 -2510
- package/build/bundle_photoviewer.cjs.map +0 -1
- package/build/components/core/Basic.css +0 -56
- package/build/components/core/Basic.js +0 -378
- package/build/components/core/CoverageMap.css +0 -10
- package/build/components/core/CoverageMap.js +0 -169
- package/build/components/core/Editor.css +0 -33
- package/build/components/core/Editor.js +0 -398
- package/build/components/core/PhotoViewer.css +0 -70
- package/build/components/core/PhotoViewer.js +0 -650
- package/build/components/core/Viewer.css +0 -130
- package/build/components/core/Viewer.js +0 -711
- package/build/components/core/index.js +0 -10
- package/build/components/index.js +0 -11
- package/build/components/index_photoviewer.js +0 -6
- package/build/components/layout/BottomDrawer.js +0 -258
- package/build/components/layout/CorneredGrid.js +0 -143
- package/build/components/layout/Mini.js +0 -121
- package/build/components/layout/Tabs.js +0 -140
- package/build/components/layout/index.js +0 -9
- package/build/components/menus/LocationPrecisionDoc.js +0 -42
- package/build/components/menus/MapBackground.js +0 -110
- package/build/components/menus/MapFilters.js +0 -567
- package/build/components/menus/MapLayers.js +0 -238
- package/build/components/menus/MapLegend.js +0 -68
- package/build/components/menus/MiniPictureLegend.js +0 -73
- package/build/components/menus/PictureLegend.js +0 -379
- package/build/components/menus/PictureMetadata.js +0 -380
- package/build/components/menus/PlayerOptions.js +0 -93
- package/build/components/menus/QualityScoreDoc.js +0 -42
- package/build/components/menus/ReportForm.js +0 -132
- package/build/components/menus/SemanticsDoc.js +0 -38
- package/build/components/menus/SemanticsDownload.js +0 -33
- package/build/components/menus/SemanticsFilters.js +0 -153
- package/build/components/menus/SemanticsList.js +0 -413
- package/build/components/menus/SemanticsMetadata.js +0 -368
- package/build/components/menus/Share.js +0 -105
- package/build/components/menus/index.js +0 -22
- package/build/components/menus/index_photoviewer.js +0 -11
- package/build/components/styles.js +0 -557
- package/build/components/ui/AnnotationsSwitch.js +0 -159
- package/build/components/ui/Button.js +0 -77
- package/build/components/ui/ButtonGroup.css +0 -59
- package/build/components/ui/ButtonGroup.js +0 -69
- package/build/components/ui/CopyButton.js +0 -110
- package/build/components/ui/Grade.js +0 -54
- package/build/components/ui/GradeFilter.js +0 -122
- package/build/components/ui/IconSwitch.js +0 -193
- package/build/components/ui/LinkButton.js +0 -67
- package/build/components/ui/ListGroup.js +0 -66
- package/build/components/ui/ListItem.js +0 -90
- package/build/components/ui/Loader.js +0 -203
- package/build/components/ui/Map.css +0 -63
- package/build/components/ui/Map.js +0 -853
- package/build/components/ui/MapMore.js +0 -175
- package/build/components/ui/Photo.css +0 -50
- package/build/components/ui/Photo.js +0 -1502
- package/build/components/ui/Popup.js +0 -145
- package/build/components/ui/ProgressBar.js +0 -104
- package/build/components/ui/QualityScore.js +0 -147
- package/build/components/ui/SearchBar.js +0 -374
- package/build/components/ui/SemanticsEditor.js +0 -191
- package/build/components/ui/SemanticsTable.js +0 -88
- package/build/components/ui/Switch.js +0 -139
- package/build/components/ui/TogglableGroup.js +0 -157
- package/build/components/ui/index.js +0 -29
- package/build/components/ui/index_photoviewer.js +0 -21
- package/build/components/ui/widgets/CopyCoordinates.js +0 -75
- package/build/components/ui/widgets/GeoSearch.css +0 -21
- package/build/components/ui/widgets/GeoSearch.js +0 -150
- package/build/components/ui/widgets/Legend.js +0 -190
- package/build/components/ui/widgets/LevelSelect.css +0 -51
- package/build/components/ui/widgets/LevelSelect.js +0 -143
- package/build/components/ui/widgets/MapFiltersButton.js +0 -114
- package/build/components/ui/widgets/MapLayersButton.js +0 -79
- package/build/components/ui/widgets/OSMEditors.js +0 -155
- package/build/components/ui/widgets/PictureLegendActions.js +0 -99
- package/build/components/ui/widgets/Player.css +0 -7
- package/build/components/ui/widgets/Player.js +0 -154
- package/build/components/ui/widgets/SemanticsFiltersButton.js +0 -65
- package/build/components/ui/widgets/Zoom.js +0 -84
- package/build/components/ui/widgets/index.js +0 -16
- package/build/components/ui/widgets/index_photoviewer.js +0 -7
- package/build/img/arrow_360.svg +0 -14
- package/build/img/arrow_flat.svg +0 -11
- package/build/img/arrow_triangle.svg +0 -9
- package/build/img/arrow_turn.svg +0 -8
- package/build/img/bg_aerial.jpg +0 -0
- package/build/img/bg_streets.jpg +0 -0
- package/build/img/loader_base.jpg +0 -0
- package/build/img/logo_dead.svg +0 -91
- package/build/img/marker.svg +0 -17
- package/build/img/marker_blue.svg +0 -20
- package/build/img/osm.svg +0 -49
- package/build/img/panoramax.svg +0 -13
- package/build/img/switch_big.svg +0 -54
- package/build/img/switch_mini.svg +0 -48
- package/build/img/wd.svg +0 -1
- package/build/index_photoviewer.js +0 -4
- package/build/package.json +0 -148
- package/build/servers.js +0 -14
- package/build/translations/ar.json +0 -1
- package/build/translations/be.json +0 -257
- package/build/translations/br.json +0 -81
- package/build/translations/cy.json +0 -117
- package/build/translations/da.json +0 -300
- package/build/translations/de.json +0 -309
- package/build/translations/en.json +0 -294
- package/build/translations/eo.json +0 -235
- package/build/translations/es.json +0 -292
- package/build/translations/fi.json +0 -1
- package/build/translations/fr.json +0 -294
- package/build/translations/hr.json +0 -294
- package/build/translations/hu.json +0 -294
- package/build/translations/it.json +0 -306
- package/build/translations/ja.json +0 -182
- package/build/translations/ko.json +0 -1
- package/build/translations/nl.json +0 -305
- package/build/translations/nn.json +0 -1
- package/build/translations/pl.json +0 -169
- package/build/translations/pt.json +0 -296
- package/build/translations/pt_BR.json +0 -304
- package/build/translations/sv.json +0 -182
- package/build/translations/ti.json +0 -9
- package/build/translations/tr.json +0 -297
- package/build/translations/uk.json +0 -268
- package/build/translations/zh_Hant.json +0 -309
- package/build/utils/API.js +0 -928
- package/build/utils/InitParameters.js +0 -521
- package/build/utils/MapStyleComposer.js +0 -889
- package/build/utils/PanoraMapProtocol.js +0 -49
- package/build/utils/PhotoAdapter.js +0 -49
- package/build/utils/PresetsManager.js +0 -148
- package/build/utils/SemanticsMapProtocol.js +0 -144
- package/build/utils/URLHandler.js +0 -426
- package/build/utils/geocoder.js +0 -203
- package/build/utils/i18n.js +0 -128
- package/build/utils/index.js +0 -17
- package/build/utils/index_photoviewer.js +0 -14
- package/build/utils/indoor.js +0 -200
- package/build/utils/map.js +0 -788
- package/build/utils/picture.js +0 -507
- package/build/utils/semantics.js +0 -321
- package/build/utils/services.js +0 -148
- package/build/utils/utils.js +0 -433
- 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
|
-
}
|