@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,889 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MAP_EXPR_QUALITYSCORE, MAP_EXPR_QUALITYSCORE_GPS, MAP_THEMES_STYLES, RASTER_LAYER_ID,
|
|
3
|
-
TILES_PICTURES_ZOOM, getPanoramaxLayers, hasStyleLoggedGridStats,
|
|
4
|
-
isLabelLayer, isPanoramaxEndpointSingleUser, switchCoefValue,
|
|
5
|
-
} from "./map.js";
|
|
6
|
-
import { autoDetectLocale } from "./i18n.js";
|
|
7
|
-
import { COLORS, getUserAccount } from "./utils.js";
|
|
8
|
-
|
|
9
|
-
export const BASEMAPS_DEFAULT_IDS = ["streets", "aerial"];
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Map Style Composer allows an easy management of MapLibre styles.
|
|
13
|
-
* As Panoramax Viewer composes with many sources and layers, it's helpful.
|
|
14
|
-
* There are several "Layer Ranges" (from bottom to top):
|
|
15
|
-
* - Basemap: contextual background, either raster of vector
|
|
16
|
-
* - Data Underlay: vector data below Panoramax data (indoor maps)
|
|
17
|
-
* - Panoramax: pictures, sequences, grid statistics
|
|
18
|
-
* - Data Overlay: vector data on top of Panoramax data (traffic signs...)
|
|
19
|
-
* The composer offers helpers to switch between any layer in a specific range.
|
|
20
|
-
* @class Panoramax.utils.MapStyleComposer
|
|
21
|
-
* @param {Panoramax.components.core.basic} parent The parent view
|
|
22
|
-
* @property {string} basemap The selected basemap
|
|
23
|
-
* @property {Set} dataUnderlays The visible data underlays
|
|
24
|
-
* @property {string} panoramax The selected Panoramax endpoint
|
|
25
|
-
* @property {Set} dataOverlays The visible data overlays
|
|
26
|
-
* @fires Panoramax.utils.MapStyleComposer#basemap-added
|
|
27
|
-
* @fires Panoramax.utils.MapStyleComposer#basemap-changed
|
|
28
|
-
* @fires Panoramax.utils.MapStyleComposer#dataunderlay-added
|
|
29
|
-
* @fires Panoramax.utils.MapStyleComposer#dataunderlay-changed
|
|
30
|
-
* @fires Panoramax.utils.MapStyleComposer#panoramax-added
|
|
31
|
-
* @fires Panoramax.utils.MapStyleComposer#panoramax-changed
|
|
32
|
-
* @fires Panoramax.utils.MapStyleComposer#theme-changed
|
|
33
|
-
* @fires Panoramax.utils.MapStyleComposer#filters-changed
|
|
34
|
-
* @fires Panoramax.utils.MapStyleComposer#dataoverlay-added
|
|
35
|
-
* @fires Panoramax.utils.MapStyleComposer#dataoverlay-changed
|
|
36
|
-
* @example
|
|
37
|
-
* const msc = new Panoramax.utils.MapStyleComposer(viewer)
|
|
38
|
-
*/
|
|
39
|
-
export default class MapStyleComposer extends EventTarget {
|
|
40
|
-
constructor(parent) {
|
|
41
|
-
super();
|
|
42
|
-
|
|
43
|
-
this._parent = parent;
|
|
44
|
-
this.map = null;
|
|
45
|
-
this._mapUpdateDebounce = null;
|
|
46
|
-
|
|
47
|
-
// Layers range storage
|
|
48
|
-
this.layerRanges = {
|
|
49
|
-
basemaps: {},
|
|
50
|
-
dataUnderlays: {},
|
|
51
|
-
panoramax: {},
|
|
52
|
-
dataOverlays: {}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// Selected/visible layers
|
|
56
|
-
this.basemap = null;
|
|
57
|
-
this.dataUnderlays = new Set();
|
|
58
|
-
this.panoramax = null;
|
|
59
|
-
this.dataOverlays = new Set();
|
|
60
|
-
|
|
61
|
-
// Detached sources = for layers not passing through MapStyleComposer
|
|
62
|
-
this.detachedSources = {};
|
|
63
|
-
|
|
64
|
-
// Panoramax themes & filters
|
|
65
|
-
this.panoramaxTheme = MAP_THEMES_STYLES.default.id;
|
|
66
|
-
this.panoramaxFilters = {};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Gives MapLibre JSON style to pass to Map constructor or setStyle function.
|
|
71
|
-
* This gives ready-to-use sources and layers.
|
|
72
|
-
* @returns {object} The MapLibre style object
|
|
73
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
74
|
-
*/
|
|
75
|
-
getMapStyle() {
|
|
76
|
-
let sources = {};
|
|
77
|
-
let layers = [];
|
|
78
|
-
let labelLayers = [];
|
|
79
|
-
let metadata = {};
|
|
80
|
-
let sprite = [];
|
|
81
|
-
|
|
82
|
-
// Helper to register layers split into classic/labels
|
|
83
|
-
const preserveFilters = ll => {
|
|
84
|
-
if(this.map) {
|
|
85
|
-
(ll || [])
|
|
86
|
-
.filter(l => this.map.getLayer(l.id))
|
|
87
|
-
.forEach(l => {
|
|
88
|
-
const f = this.map.getFilter(l.id);
|
|
89
|
-
if(f) { l.filter = f; }
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
return ll;
|
|
93
|
-
};
|
|
94
|
-
const addLayers = ll => {
|
|
95
|
-
(ll || []).forEach(l => {
|
|
96
|
-
if(isLabelLayer(l)) { labelLayers.push(l); }
|
|
97
|
-
else { layers.push(l); }
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
const addSprite = (s, id) => {
|
|
101
|
-
if(Array.isArray(s)) {
|
|
102
|
-
sprite = sprite.concat(s);
|
|
103
|
-
}
|
|
104
|
-
// eslint-disable-next-line eqeqeq
|
|
105
|
-
else if(typeof s === "object" && s != null) {
|
|
106
|
-
sprite.push(s);
|
|
107
|
-
}
|
|
108
|
-
else if(typeof s === "string") {
|
|
109
|
-
sprite.push({id, url: s});
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// Detached sources
|
|
114
|
-
Object
|
|
115
|
-
.entries(this.detachedSources)
|
|
116
|
-
.forEach(([sid,s]) => sources[sid] = s);
|
|
117
|
-
|
|
118
|
-
// Basemap
|
|
119
|
-
if(this.basemap && this.layerRanges.basemaps[this.basemap]) {
|
|
120
|
-
sources = Object.assign(sources, this.layerRanges.basemaps[this.basemap].sources);
|
|
121
|
-
addLayers(this.layerRanges.basemaps[this.basemap].layers);
|
|
122
|
-
metadata = this.layerRanges.basemaps[this.basemap].metadata || {};
|
|
123
|
-
addSprite(this.layerRanges.basemaps[this.basemap].sprite, this.basemap);
|
|
124
|
-
if(!metadata?.["panoramax:locales"]) {
|
|
125
|
-
metadata["panoramax:locales"] = ["fr", "en", "de", "es", "ru", "pt", "zh", "hi", "latin"];
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Data underlays
|
|
130
|
-
[...this.dataUnderlays].forEach(did => {
|
|
131
|
-
if(this.layerRanges.dataUnderlays[did]) {
|
|
132
|
-
sources = Object.assign(sources, this.layerRanges.dataUnderlays[did].sources);
|
|
133
|
-
addLayers(preserveFilters(this.layerRanges.dataUnderlays[did].layers));
|
|
134
|
-
addSprite(this.layerRanges.dataUnderlays[did].sprite, did);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Panoramax vector tile endpoint
|
|
139
|
-
if(this.panoramax && this.layerRanges.panoramax[this.panoramax]) {
|
|
140
|
-
metadata = Object.assign(metadata, this.layerRanges.panoramax[this.panoramax].metadata);
|
|
141
|
-
sources = Object.assign(sources, this.layerRanges.panoramax[this.panoramax].sources);
|
|
142
|
-
addSprite(this.layerRanges.panoramax[this.panoramax].sprite, this.panoramax);
|
|
143
|
-
let pnxLayers = this._applyPanoramaxThemeOnLayers(this.layerRanges.panoramax[this.panoramax]);
|
|
144
|
-
pnxLayers = this._applyPanoramaxFiltersOnLayers(pnxLayers);
|
|
145
|
-
addLayers(pnxLayers);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Data overlays
|
|
149
|
-
[...this.dataOverlays].forEach(did => {
|
|
150
|
-
if(this.layerRanges.dataOverlays[did]) {
|
|
151
|
-
sources = Object.assign(sources, this.layerRanges.dataOverlays[did].sources);
|
|
152
|
-
addLayers(preserveFilters(this.layerRanges.dataOverlays[did].layers));
|
|
153
|
-
addSprite(this.layerRanges.dataOverlays[did].sprite, did);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Override label layers to use preferred language
|
|
158
|
-
if(metadata["panoramax:locales"]) {
|
|
159
|
-
let prefLang = this._parent.lang || autoDetectLocale(metadata["panoramax:locales"], "latin");
|
|
160
|
-
if(prefLang.includes("-")) { prefLang = prefLang.split("-")[0]; }
|
|
161
|
-
if(prefLang.includes("_")) { prefLang = prefLang.split("_")[0]; }
|
|
162
|
-
labelLayers.forEach(l => {
|
|
163
|
-
if(l.layout["text-field"].includes("name:latin")) {
|
|
164
|
-
l.layout["text-field"] = [
|
|
165
|
-
"coalesce",
|
|
166
|
-
["get", `name:${prefLang}`],
|
|
167
|
-
["get", "name:latin"],
|
|
168
|
-
["get", "name"]
|
|
169
|
-
];
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/*
|
|
175
|
-
* Various fixes for background providers
|
|
176
|
-
*/
|
|
177
|
-
|
|
178
|
-
// OSMFR PMTiles (capital cities)
|
|
179
|
-
const citiesLayer = labelLayers.find(l => l.id === "place_label_city");
|
|
180
|
-
let capitalLayer = labelLayers.find(l => l.id === "place_label_capital");
|
|
181
|
-
if(citiesLayer && !capitalLayer) {
|
|
182
|
-
// Create capital layer from original city style
|
|
183
|
-
citiesLayer.paint = {
|
|
184
|
-
"text-color": "hsl(0, 0%, 0%)",
|
|
185
|
-
"text-halo-blur": 0,
|
|
186
|
-
"text-halo-color": "hsla(0, 0%, 100%, 1)",
|
|
187
|
-
"text-halo-width": 3,
|
|
188
|
-
};
|
|
189
|
-
citiesLayer.layout["text-letter-spacing"] = 0.1;
|
|
190
|
-
capitalLayer = JSON.parse(JSON.stringify(citiesLayer));
|
|
191
|
-
capitalLayer.id = "place_label_capital";
|
|
192
|
-
capitalLayer.filter = capitalLayer.filter.filter(f => !(f[0] === ">" && f[1] === "capital"));
|
|
193
|
-
capitalLayer.filter.push(["<=", "capital", 2]);
|
|
194
|
-
|
|
195
|
-
// Edit original city to make it less important
|
|
196
|
-
if(!citiesLayer.filter.find(f => f[0] === ">" && f[1] === "capital")) {
|
|
197
|
-
citiesLayer.filter.push([">", "capital", 2]);
|
|
198
|
-
}
|
|
199
|
-
labelLayers.push(capitalLayer);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// IGN FR Maxzoom
|
|
203
|
-
if(sources.plan_ign) { sources.plan_ign.maxzoom = 18;}
|
|
204
|
-
|
|
205
|
-
// OpenMapTiles styles
|
|
206
|
-
if(sources.openmaptiles && !(sources.openmaptiles.attribution || "").includes("OpenMapTiles")) {
|
|
207
|
-
sources.openmaptiles.attribution += " | © <a href='https://openmaptiles.org/'>OpenMapTiles</a>";
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
version: 8,
|
|
212
|
-
name: "Panoramax Map Style",
|
|
213
|
-
sources,
|
|
214
|
-
layers: layers.concat(labelLayers).filter(l => l.id),
|
|
215
|
-
metadata,
|
|
216
|
-
sprite: sprite.length > 0 ? sprite : undefined,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Throttled function to force map data refresh.
|
|
222
|
-
* This allows to make actual effect of layers adds/switches.
|
|
223
|
-
* @param {boolean} [reloadSem=false] Force reload of semantics layers (in case of API switch)
|
|
224
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
225
|
-
*/
|
|
226
|
-
updateMapStyle(reloadSem = false) {
|
|
227
|
-
clearTimeout(this._mapUpdateDebounce);
|
|
228
|
-
this._mapUpdateDebounce = setTimeout(() => {
|
|
229
|
-
this.map?.setStyle(this.getMapStyle(), {diff: true});
|
|
230
|
-
|
|
231
|
-
if(reloadSem) {
|
|
232
|
-
this.map._semMapProtocol.clearCache();
|
|
233
|
-
Object
|
|
234
|
-
.entries(this.map?.getStyle()?.sources || {})
|
|
235
|
-
.filter(([,v]) => v.tiles && v.tiles[0].startsWith("sem"))
|
|
236
|
-
.forEach(([k,]) => {
|
|
237
|
-
const s = this.map.getSource(k);
|
|
238
|
-
s.setTiles(s.tiles);
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
}, 10);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Checks if given source already exists in composer, or add it as detached to wait.
|
|
246
|
-
* This is useful for sources added directly to MapLibre not through the MapStyleComposer.
|
|
247
|
-
* @param {string} id The source ID
|
|
248
|
-
* @param {object} source The MapLibre source definition
|
|
249
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
250
|
-
*/
|
|
251
|
-
registerSource(id, source) {
|
|
252
|
-
let found = false;
|
|
253
|
-
const lrids = Object.keys(this.layerRanges);
|
|
254
|
-
for(let i=0; i < lrids.length; i++) {
|
|
255
|
-
const lr = this.layerRanges[lrids[i]];
|
|
256
|
-
const styles = Object.keys(lr);
|
|
257
|
-
for(let j=0; j < styles.length; j++) {
|
|
258
|
-
const styleId = styles[j];
|
|
259
|
-
if(styleId === id) {
|
|
260
|
-
found = true;
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
if(found) { break; }
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if(!found) {
|
|
268
|
-
// Shorten indoor= attribution
|
|
269
|
-
if(id === "indoorequal") {
|
|
270
|
-
source.attribution = "<a href=\"https://indoorequal.org/\" target=\"_blank\">© indoor=</a>";
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
this.detachedSources[id] = source;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Checks if given layer already exists in composer, or add it as data under/overlay.
|
|
279
|
-
* This is useful for layers added directly to MapLibre not through the MapStyleComposer.
|
|
280
|
-
* @param {object} layer The MapLibre layer definition
|
|
281
|
-
* @param {string} [beforeId] The ID of an existing layer to put this one before
|
|
282
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
283
|
-
*/
|
|
284
|
-
registerLayer(layer, beforeId) {
|
|
285
|
-
let layerRange = "dataOverlays";
|
|
286
|
-
if(layer.source === "indoorequal" || beforeId) {
|
|
287
|
-
layerRange = "dataUnderlays";
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Skip indoorequal heatmap if disabled
|
|
291
|
-
if(layer.source === "indoorequal" && layer.id === "indoor-heat" && this._parent?._initParams?.getMapInit()?.indoor?.heatmap === false) {
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Look for source in existing layers for appropriate layerRange
|
|
296
|
-
let found = false;
|
|
297
|
-
Object
|
|
298
|
-
.values(this.layerRanges[layerRange])
|
|
299
|
-
.forEach(s => {
|
|
300
|
-
if(s.sources[layer.source]) {
|
|
301
|
-
if(!s.layers) { s.layers = []; }
|
|
302
|
-
s.layers.push(layer);
|
|
303
|
-
found = true;
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
if(found) { return; }
|
|
308
|
-
|
|
309
|
-
// If none found, add it
|
|
310
|
-
let style = { sources: {}, layers: [layer] };
|
|
311
|
-
if(this.detachedSources[layer.source]) {
|
|
312
|
-
style.sources[layer.source] = this.detachedSources[layer.source];
|
|
313
|
-
delete this.detachedSources[layer.source];
|
|
314
|
-
|
|
315
|
-
if(layerRange === "dataOverlays") {
|
|
316
|
-
this.addDataOverlay(layer.id, style, true);
|
|
317
|
-
}
|
|
318
|
-
else if(layerRange === "dataUnderlays") {
|
|
319
|
-
this.addDataUnderlay(layer.id, style, true);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Wait for some layer to be available
|
|
326
|
-
* @param {string} type The kind of layer (panoramax, dataOverlays, dataUnderlays, basemaps)
|
|
327
|
-
* @param {string} id The ID of layer to wait for
|
|
328
|
-
* @returns {Promise} Resolves when layer is ready
|
|
329
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
330
|
-
*/
|
|
331
|
-
async waitFor(type, id) {
|
|
332
|
-
if(this.layerRanges[type][id]) {
|
|
333
|
-
return await Promise.resolve();
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
return await new Promise(resolve => setTimeout(resolve, 250)).then(() => this.waitFor(type, id));
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Creates a new basemap
|
|
342
|
-
* @param {string} id Identifier for this basemap
|
|
343
|
-
* @param {object} style The MapLibre-like style definition (object with sources and layers)
|
|
344
|
-
* @param {boolean} [switchOn=false] Immediate map display ?
|
|
345
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
346
|
-
*/
|
|
347
|
-
async addBasemap(id, style, switchOn = false) {
|
|
348
|
-
// Load from string URL
|
|
349
|
-
if(typeof style === "string") { style = await fetch(style).then(res => res.json()); }
|
|
350
|
-
|
|
351
|
-
this.layerRanges.basemaps[id] = style;
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Event for basemap added
|
|
355
|
-
* @event Panoramax.utils.MapStyleComposer#basemap-added
|
|
356
|
-
* @type {CustomEvent}
|
|
357
|
-
* @property {string} [detail.basemap] The added basemap ID
|
|
358
|
-
*/
|
|
359
|
-
this.dispatchEvent(new CustomEvent("basemap-added", { detail: { basemap: id }}));
|
|
360
|
-
|
|
361
|
-
if(switchOn) { this.switchBasemap(id); }
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Makes this basemap now visible, instead of current one.
|
|
366
|
-
* @param {string} id The basemap ID
|
|
367
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
368
|
-
*/
|
|
369
|
-
async switchBasemap(id) {
|
|
370
|
-
if(id && !this.layerRanges.basemaps[id]) {
|
|
371
|
-
await this.waitFor("basemaps", id);
|
|
372
|
-
}
|
|
373
|
-
if(this.basemap === id || !id) { return; }
|
|
374
|
-
this.basemap = id;
|
|
375
|
-
this.updateMapStyle();
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Event for basemap changes
|
|
379
|
-
* @event Panoramax.utils.MapStyleComposer#basemap-changed
|
|
380
|
-
* @type {CustomEvent}
|
|
381
|
-
* @property {string} [detail.basemap] The basemap ID
|
|
382
|
-
*/
|
|
383
|
-
this.dispatchEvent(new CustomEvent("basemap-changed", { detail: { basemap: id } }));
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Does this style contain many different basemaps ?
|
|
388
|
-
* @returns {boolean} True if 2 or more basemaps are available
|
|
389
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
390
|
-
*/
|
|
391
|
-
hasManyBasemaps() {
|
|
392
|
-
return Object.keys(this.layerRanges.basemaps).length >= 2;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Does this style contains two main basemaps "aerial" & "streets" ?
|
|
397
|
-
* @returns {boolean} True if two main basemaps available
|
|
398
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
399
|
-
*/
|
|
400
|
-
hasStreetsAerialBasemaps() {
|
|
401
|
-
// eslint-disable-next-line eqeqeq
|
|
402
|
-
return this.layerRanges.basemaps.aerial != null && this.layerRanges.basemaps.streets != null;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Does this style contains any other basemaps than two main ones (aerial/streets)
|
|
407
|
-
* @returns {boolean} True if other basemaps available
|
|
408
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
409
|
-
*/
|
|
410
|
-
hasComplementaryBasemaps() {
|
|
411
|
-
// eslint-disable-next-line eqeqeq
|
|
412
|
-
return Object.keys(this.layerRanges.basemaps).find(b => !BASEMAPS_DEFAULT_IDS.includes(b)) != null;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Creates a new data underlay
|
|
417
|
-
* @param {string} id Identifier for this basemap
|
|
418
|
-
* @param {object} style The MapLibre-like style definition (object with sources and layers)
|
|
419
|
-
* @param {boolean} [switchOn=false] Immediate map display ?
|
|
420
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
421
|
-
*/
|
|
422
|
-
addDataUnderlay(id, style, switchOn = false) {
|
|
423
|
-
this.layerRanges.dataUnderlays[id] = style;
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Event for data underlay added
|
|
427
|
-
* @event Panoramax.utils.MapStyleComposer#dataunderlay-added
|
|
428
|
-
* @type {CustomEvent}
|
|
429
|
-
* @property {string} [detail.dataUnderlay] The added data underlay ID
|
|
430
|
-
*/
|
|
431
|
-
this.dispatchEvent(new CustomEvent("dataunderlay-added", { detail: { dataUnderlay: id }}));
|
|
432
|
-
|
|
433
|
-
if(switchOn) { this.switchDataUnderlayVisibility(id, true); }
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Change visibility of a given data underlay.
|
|
438
|
-
* @param {string} id The Identifier of data underlay to change
|
|
439
|
-
* @param {boolean} visible Set to true to make visible, false to hide
|
|
440
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
441
|
-
*/
|
|
442
|
-
async switchDataUnderlayVisibility(id, visible) {
|
|
443
|
-
if(id && !this.layerRanges.dataUnderlays[id]) {
|
|
444
|
-
await this.waitFor("dataUnderlays", id);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
if(visible) { this.dataUnderlays.add(id); }
|
|
448
|
-
else { this.dataUnderlays.delete(id); }
|
|
449
|
-
this.updateMapStyle();
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Event for data underlays changes
|
|
453
|
-
* @event Panoramax.utils.MapStyleComposer#dataunderlay-changed
|
|
454
|
-
* @type {CustomEvent}
|
|
455
|
-
* @property {string} [detail.dataUnderlay] The data underlay ID
|
|
456
|
-
* @property {boolean} [detail.visible] True if visible
|
|
457
|
-
*/
|
|
458
|
-
this.dispatchEvent(new CustomEvent("dataunderlay-changed", { detail: { dataUnderlay: id , visible } }));
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Creates a new Panoramax Vector Tile endpoint.
|
|
463
|
-
* @param {string} id The identifier for this endpoint
|
|
464
|
-
* @param {object} style The MapLibre Style JSON object
|
|
465
|
-
* @param {boolean} [switchOn=false] Immediate map display ?
|
|
466
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
467
|
-
*/
|
|
468
|
-
addPanoramaxEndpoint(id, style, switchOn = false) {
|
|
469
|
-
// Override tiles protocol (for tile crowdiness)
|
|
470
|
-
Object
|
|
471
|
-
.values(style?.sources || {})
|
|
472
|
-
.filter(s => s.tiles)
|
|
473
|
-
.forEach(s => s.tiles = s.tiles.map(t => t.replace(/^http/, "panora")));
|
|
474
|
-
|
|
475
|
-
this.layerRanges.panoramax[id] = style;
|
|
476
|
-
|
|
477
|
-
// Add layers styles
|
|
478
|
-
this.layerRanges.panoramax[id].layers = getPanoramaxLayers(style);
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Event for Panoramax endpoint added
|
|
482
|
-
* @event Panoramax.utils.MapStyleComposer#panoramax-added
|
|
483
|
-
* @type {CustomEvent}
|
|
484
|
-
* @property {string} [detail.endpoint] The added Panoramax endpoint ID
|
|
485
|
-
*/
|
|
486
|
-
this.dispatchEvent(new CustomEvent("panoramax-added", { detail: { endpoint: id }}));
|
|
487
|
-
|
|
488
|
-
if(switchOn) { this.switchPanoramaxEndpoint(id); }
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Uses this Panoramax Vector Tile endpoint instead of current one.
|
|
493
|
-
* @param {string} id The endpoint ID
|
|
494
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
495
|
-
*/
|
|
496
|
-
async switchPanoramaxEndpoint(id) {
|
|
497
|
-
if(id && !this.layerRanges.panoramax[id]) {
|
|
498
|
-
await this.waitFor("panoramax", id);
|
|
499
|
-
}
|
|
500
|
-
if(!id || id === this.panoramax) { return; }
|
|
501
|
-
this.panoramax = id;
|
|
502
|
-
|
|
503
|
-
this.updateMapStyle(true);
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* Event for Panoramax endpoint changes
|
|
507
|
-
* @event Panoramax.utils.MapStyleComposer#panoramax-changed
|
|
508
|
-
* @type {CustomEvent}
|
|
509
|
-
* @property {string} [detail.endpoint] The new selected Panoramax endpoint
|
|
510
|
-
*/
|
|
511
|
-
this.dispatchEvent(new CustomEvent("panoramax-changed", { detail: { endpoint: id } }));
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* Is the currently selected Panoramax endpoint showing data of a single user ?
|
|
516
|
-
* @returns {boolean} True if single user
|
|
517
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
518
|
-
*/
|
|
519
|
-
isPanoramaxEndpointSingleUser() {
|
|
520
|
-
return isPanoramaxEndpointSingleUser(this.panoramax);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Changes the selected Panoramax map theme
|
|
525
|
-
* @param {string} theme One of "default", "age", "type", "score", "gps"
|
|
526
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
527
|
-
*/
|
|
528
|
-
setPanoramaxTheme(theme) {
|
|
529
|
-
if(theme === this.panoramaxTheme) { return; }
|
|
530
|
-
this.panoramaxTheme = theme;
|
|
531
|
-
this.updateMapStyle();
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* Event for Panoramax theme changes
|
|
535
|
-
* @event Panoramax.utils.MapStyleComposer#theme-changed
|
|
536
|
-
* @type {CustomEvent}
|
|
537
|
-
* @property {string} [detail.theme] The new selected theme
|
|
538
|
-
*/
|
|
539
|
-
this.dispatchEvent(new CustomEvent("theme-changed", { detail: { theme } }));
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Changes the selected Panoramax filters
|
|
544
|
-
* @param {object} filters Filtering values
|
|
545
|
-
* @param {string} [filters.minDate] Start date for pictures (format YYYY-MM-DD)
|
|
546
|
-
* @param {string} [filters.maxDate] End date for pictures (format YYYY-MM-DD)
|
|
547
|
-
* @param {string} [filters.pic_type] Type of picture to keep (flat, equirectangular)
|
|
548
|
-
* @param {number[]} [filters.qualityscore] QualityScore values, as a list of 1 to 5 grades
|
|
549
|
-
* @param {number[]} [filters.gps] GPS precision grades, as a list of 1 to 5 grades
|
|
550
|
-
* @param {object} [filters.featuresRestrictions] Rules to restrict display of pictures & sequences
|
|
551
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
552
|
-
*/
|
|
553
|
-
setPanoramaxFilters(filters) {
|
|
554
|
-
this.panoramaxFilters = filters;
|
|
555
|
-
this.updateMapStyle();
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Event for Panoramax filters changes
|
|
559
|
-
* @event Panoramax.utils.MapStyleComposer#filters-changed
|
|
560
|
-
* @type {CustomEvent}
|
|
561
|
-
* @property {string} [detail.minDate] The minimum date in time range (ISO format)
|
|
562
|
-
* @property {string} [detail.maxDate] The maximum date in time range (ISO format)
|
|
563
|
-
* @property {string} [detail.pic_type] Camera type (equirectangular, flat, null/empty string for both)
|
|
564
|
-
* @property {number[]} [detail.qualityscore] QualityScore values, as a list of 1 to 5 grades
|
|
565
|
-
* @property {number[]} [detail.gps] GPS precision grades, as a list of 1 to 5 grades
|
|
566
|
-
* @property {object} [detail.featuresRestrictions] Rules to restrict display of pictures & sequences
|
|
567
|
-
*/
|
|
568
|
-
this.dispatchEvent(new CustomEvent("filters-changed", { detail: filters }));
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/**
|
|
572
|
-
* Creates a new data overlay
|
|
573
|
-
* @param {string} id Identifier for this basemap
|
|
574
|
-
* @param {object} style The MapLibre-like style definition (object with sources and layers)
|
|
575
|
-
* @param {boolean} [switchOn=false] Immediate map display ?
|
|
576
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
577
|
-
*/
|
|
578
|
-
addDataOverlay(id, style, switchOn = false) {
|
|
579
|
-
this.layerRanges.dataOverlays[id] = style;
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Event for data overlay added
|
|
583
|
-
* @event Panoramax.utils.MapStyleComposer#dataoverlay-added
|
|
584
|
-
* @type {CustomEvent}
|
|
585
|
-
* @property {string} [detail.dataOverlay] The added data overlay ID
|
|
586
|
-
*/
|
|
587
|
-
this.dispatchEvent(new CustomEvent("dataoverlay-added", { detail: { dataOverlay: id }}));
|
|
588
|
-
|
|
589
|
-
if(switchOn) { this.switchDataOverlayVisibility(id, true); }
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Change visibility of a given data Overlay.
|
|
594
|
-
* @param {string} id The Identifier of data Overlay to change
|
|
595
|
-
* @param {boolean} visible Set to true to make visible, false to hide
|
|
596
|
-
* @memberof Panoramax.utils.MapStyleComposer#
|
|
597
|
-
*/
|
|
598
|
-
async switchDataOverlayVisibility(id, visible) {
|
|
599
|
-
if(id && !this.layerRanges.dataOverlays[id]) {
|
|
600
|
-
await this.waitFor("dataOverlays", id);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
if(visible) { this.dataOverlays.add(id); }
|
|
604
|
-
else { this.dataOverlays.delete(id); }
|
|
605
|
-
this.updateMapStyle();
|
|
606
|
-
|
|
607
|
-
/**
|
|
608
|
-
* Event for data overlays changes
|
|
609
|
-
* @event Panoramax.utils.MapStyleComposer#dataoverlay-changed
|
|
610
|
-
* @type {CustomEvent}
|
|
611
|
-
* @property {string} [detail.dataOverlay] The data overlay ID
|
|
612
|
-
* @property {boolean} [detail.visible] True if visible
|
|
613
|
-
*/
|
|
614
|
-
this.dispatchEvent(new CustomEvent("dataoverlay-changed", { detail: { dataOverlay: id , visible } }));
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
/** @private */
|
|
618
|
-
async _createPanoramaxEndpointFromAPI(id, api, switchOn = true) {
|
|
619
|
-
let style;
|
|
620
|
-
|
|
621
|
-
if(api._endpoints.style) { // API Style JSON
|
|
622
|
-
style = api._endpoints.style;
|
|
623
|
-
}
|
|
624
|
-
else if(api._endpoints.tiles) { // API tiles
|
|
625
|
-
style = { "sources": { "geovisio": { "tiles": [ api._endpoints.tiles ] } } };
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// Load from URL
|
|
629
|
-
if(style && typeof style === "string") {
|
|
630
|
-
style = await fetch(style, api._getFetchOptions()).then(res => res.json());
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
if(style && style.sources) {
|
|
634
|
-
this.addPanoramaxEndpoint(id, style, switchOn);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/** @private */
|
|
639
|
-
async _createPanoramaxEndpointForUser(userId) {
|
|
640
|
-
// Skip if user ID is invalid
|
|
641
|
-
if(!isPanoramaxEndpointSingleUser(userId)) { return; }
|
|
642
|
-
|
|
643
|
-
// Skip if it already exist
|
|
644
|
-
if(this.layerRanges.panoramax[userId]) {
|
|
645
|
-
this.switchPanoramaxEndpoint(userId);
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
let style, api, apiUserId;
|
|
650
|
-
|
|
651
|
-
// User from metacatalog
|
|
652
|
-
if(userId.startsWith("metacatalog_")) {
|
|
653
|
-
api = this._parent.apiMC;
|
|
654
|
-
apiUserId = userId.replace(/^metacatalog_/, "");
|
|
655
|
-
}
|
|
656
|
-
// User from local API
|
|
657
|
-
else {
|
|
658
|
-
api = this._parent.api;
|
|
659
|
-
apiUserId = userId;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// Find style from API
|
|
663
|
-
if(api._endpoints.user_style) { // API user style JSON
|
|
664
|
-
style = api._endpoints.user_style.replace(/\{userId\}/g, apiUserId);
|
|
665
|
-
}
|
|
666
|
-
else if(api._endpoints.user_tiles) { // API user tiles
|
|
667
|
-
style = { "sources": { [`geovisio_${apiUserId}`]: {
|
|
668
|
-
"tiles": [ api._endpoints.user_tiles.replace(/\{userId\}/g, apiUserId) ]
|
|
669
|
-
} } };
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// Load from URL
|
|
673
|
-
if(style && typeof style === "string") {
|
|
674
|
-
style = await fetch(style, api._getFetchOptions()).then(res => res.json());
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
if(style && style.sources) {
|
|
678
|
-
this.addPanoramaxEndpoint(userId, style, true);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/** @private */
|
|
683
|
-
_createManyBasemaps(basemaps, switchId) {
|
|
684
|
-
const maxzoomRgx = /\{z:(\d+)\}/;
|
|
685
|
-
Object
|
|
686
|
-
.entries(basemaps)
|
|
687
|
-
.forEach(([id, def]) => {
|
|
688
|
-
// Transform shortcuts for TMS into classic definition
|
|
689
|
-
if(typeof def === "string" && def.includes(".png")) {
|
|
690
|
-
let maxzoom, attribution;
|
|
691
|
-
|
|
692
|
-
// Look for maxzoom if embed
|
|
693
|
-
const maxzoomMatcher = def.match(maxzoomRgx);
|
|
694
|
-
if(maxzoomMatcher && maxzoomMatcher.length > 1) {
|
|
695
|
-
maxzoom = parseInt(maxzoomMatcher[1]);
|
|
696
|
-
def = def.replace(maxzoomRgx, "{z}");
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// Auto append OSM attribution
|
|
700
|
-
if(def.includes("osm") || def.includes("openstreetmap")) {
|
|
701
|
-
attribution = "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a>";
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
def = {
|
|
705
|
-
sources: {
|
|
706
|
-
[id]: {
|
|
707
|
-
type: "raster",
|
|
708
|
-
tiles: [def],
|
|
709
|
-
tileSize: 256,
|
|
710
|
-
maxzoom,
|
|
711
|
-
attribution,
|
|
712
|
-
}
|
|
713
|
-
},
|
|
714
|
-
layers: [{
|
|
715
|
-
"id": id,
|
|
716
|
-
"type": "raster",
|
|
717
|
-
"source": id,
|
|
718
|
-
}]
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
this.addBasemap(id, def, id === switchId);
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
/** @private */
|
|
727
|
-
_createRasterBasemap(raster) {
|
|
728
|
-
this.addBasemap("aerial", {
|
|
729
|
-
sources: { [RASTER_LAYER_ID]: raster },
|
|
730
|
-
layers: [{
|
|
731
|
-
"id": RASTER_LAYER_ID,
|
|
732
|
-
"type": "raster",
|
|
733
|
-
"source": RASTER_LAYER_ID,
|
|
734
|
-
}]
|
|
735
|
-
});
|
|
736
|
-
return RASTER_LAYER_ID;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
/** @private */
|
|
740
|
-
_applyPanoramaxThemeOnLayers(style) {
|
|
741
|
-
const picId = this._parent.psv ? (this._parent.psv?._myVTour?.state?.loadingNode || this._parent.psv?._myVTour?.state?.currentNode?.id) : this._parent.picture;
|
|
742
|
-
const seqId = this._parent.psv ? (picId ? this._parent.psv?._picturesSequences[picId] : null) : this._parent.sequence;
|
|
743
|
-
|
|
744
|
-
style.layers = style.layers.map(layer => {
|
|
745
|
-
const isPictures = layer.id.endsWith("_pictures");
|
|
746
|
-
const isSequences = layer.id.endsWith("_sequences");
|
|
747
|
-
const isGrid = layer.id.endsWith("_grid");
|
|
748
|
-
|
|
749
|
-
if(!layer.layout) { layer.layout = {}; }
|
|
750
|
-
if(!layer.paint) { layer.paint = {}; }
|
|
751
|
-
|
|
752
|
-
if(isPictures || isSequences) {
|
|
753
|
-
// -- Sort --
|
|
754
|
-
// Values
|
|
755
|
-
// - 100 : on top / selected feature
|
|
756
|
-
// - 90 : hidden feature
|
|
757
|
-
// - 20-80 : custom ranges
|
|
758
|
-
// - 10 : basic feature
|
|
759
|
-
// - 0 : on bottom / feature with undefined property
|
|
760
|
-
|
|
761
|
-
const layerSort = ["case", ["==", ["get", "hidden"], true], 90];
|
|
762
|
-
|
|
763
|
-
// Selected sequence style
|
|
764
|
-
if(isSequences && seqId) {
|
|
765
|
-
layerSort.push(["==", ["get", "id"], seqId], 100);
|
|
766
|
-
}
|
|
767
|
-
else if(isPictures && seqId) {
|
|
768
|
-
layerSort.push(["in", seqId, ["get", "sequences"]], 100);
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
layerSort.push(...MAP_THEMES_STYLES[this.panoramaxTheme].sort);
|
|
772
|
-
layerSort.push(10);
|
|
773
|
-
layer.layout[`${isPictures ? "circle" : "line"}-sort-key`] = layerSort;
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
// -- Color --
|
|
777
|
-
const layerColor = ["case", ["==", ["get", "hidden"], true], COLORS.HIDDEN];
|
|
778
|
-
|
|
779
|
-
// Selected sequence style
|
|
780
|
-
if(isSequences && seqId) {
|
|
781
|
-
layerColor.push(["==", ["get", "id"], seqId], COLORS.SELECTED);
|
|
782
|
-
}
|
|
783
|
-
else if(isPictures && seqId) {
|
|
784
|
-
layerColor.push(["in", seqId, ["get", "sequences"]], COLORS.SELECTED);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
layerColor.push(...MAP_THEMES_STYLES[this.panoramaxTheme].color);
|
|
788
|
-
layer.paint[`${isPictures ? "circle" : "line"}-color`] = layerColor;
|
|
789
|
-
if(isPictures) { layer.paint["circle-stroke-color"] = layerColor; }
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
if(isGrid) {
|
|
793
|
-
let newType = "coef";
|
|
794
|
-
if(this.panoramaxFilters.pic_type) {
|
|
795
|
-
newType = this.panoramaxFilters.pic_type === "flat" ? "coef_flat_pictures" : "coef_360_pictures";
|
|
796
|
-
if(hasStyleLoggedGridStats(style) && getUserAccount()) {
|
|
797
|
-
newType = "logged_" + newType;
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
layer = switchCoefValue(layer, newType);
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
return layer;
|
|
804
|
-
});
|
|
805
|
-
|
|
806
|
-
return style.layers;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
/** @private */
|
|
810
|
-
_applyPanoramaxFiltersOnLayers(layers) {
|
|
811
|
-
const pf = this.panoramaxFilters;
|
|
812
|
-
|
|
813
|
-
layers.forEach(layer => {
|
|
814
|
-
const isPictures = layer.id.endsWith("_pictures");
|
|
815
|
-
const isSequences = layer.id.endsWith("_sequences") || layer.id.endsWith("_sequences_plus");
|
|
816
|
-
|
|
817
|
-
if(!isPictures && !isSequences) { return; }
|
|
818
|
-
let layerFilters = [];
|
|
819
|
-
|
|
820
|
-
if(pf.minDate && pf.minDate !== "") {
|
|
821
|
-
if(isPictures) { layerFilters.push([">=", ["get", "ts"], pf.minDate]); }
|
|
822
|
-
else if(isSequences) { layerFilters.push([">=", ["get", "date"], pf.minDate]); }
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
if(pf.maxDate && pf.maxDate !== "") {
|
|
826
|
-
if(isSequences) { layerFilters.push(["<=", ["get", "date"], pf.maxDate]); }
|
|
827
|
-
|
|
828
|
-
// Get tomorrow date for pictures filtering
|
|
829
|
-
// (because ts is date+time, so comparing date only string would fail otherwise)
|
|
830
|
-
if(isPictures) {
|
|
831
|
-
let d = new Date(pf.maxDate);
|
|
832
|
-
d.setDate(d.getDate() + 1);
|
|
833
|
-
d = d.toISOString().split("T")[0];
|
|
834
|
-
layerFilters.push(["<=", ["get", "ts"], d]);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
if(pf.pic_type && pf.pic_type !== "") {
|
|
839
|
-
pf.pic_type = pf.pic_type === "flat" ? "flat" : "equirectangular";
|
|
840
|
-
if(isSequences) { layerFilters.push(["==", ["get", "type"], pf.pic_type]); }
|
|
841
|
-
else if(isPictures) { layerFilters.push(["==", ["get", "type"], pf.pic_type]); }
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
if(pf.qualityscore && pf.qualityscore.length > 0) {
|
|
845
|
-
if(isSequences) { layerFilters.push(["in", MAP_EXPR_QUALITYSCORE, ["literal", pf.qualityscore]]); }
|
|
846
|
-
else if (isPictures) { layerFilters.push(["in", MAP_EXPR_QUALITYSCORE, ["literal", pf.qualityscore]]); }
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
if(pf.gps && pf.gps.length > 0) {
|
|
850
|
-
if(isSequences) { layerFilters.push(["in", MAP_EXPR_QUALITYSCORE_GPS, ["literal", pf.gps]]); }
|
|
851
|
-
else if(isPictures) { layerFilters.push(["in", MAP_EXPR_QUALITYSCORE_GPS, ["literal", pf.gps]]); }
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
if(isPictures && pf.featuresRestrictions?.pictures && pf.featuresRestrictions.pictures.features?.length >= 0) {
|
|
855
|
-
if(pf.featuresRestrictions.pictures.rule === "keep") {
|
|
856
|
-
layerFilters.push(["in", ["get", "id"], ["literal", pf.featuresRestrictions.pictures.features]]);
|
|
857
|
-
}
|
|
858
|
-
else if(pf.featuresRestrictions.pictures.features.length > 0) {
|
|
859
|
-
layerFilters.push(["!", ["in", ["get", "id"], ["literal", pf.featuresRestrictions.pictures.features]]]);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
if(isSequences && pf.featuresRestrictions?.sequences && pf.featuresRestrictions.sequences.features?.length >= 0) {
|
|
864
|
-
if(pf.featuresRestrictions.sequences.rule === "keep") {
|
|
865
|
-
layerFilters.push(["in", ["get", "id"], ["literal", pf.featuresRestrictions.sequences.features]]);
|
|
866
|
-
}
|
|
867
|
-
else if(pf.featuresRestrictions.sequences.features.length > 0) {
|
|
868
|
-
layerFilters.push(["!", ["in", ["get", "id"], ["literal", pf.featuresRestrictions.sequences.features]]]);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
if(layerFilters.length === 0) { layerFilters = ["literal", true]; }
|
|
874
|
-
else {
|
|
875
|
-
layerFilters.unshift("all");
|
|
876
|
-
if(isPictures) {
|
|
877
|
-
layerFilters = ["step", ["zoom"],
|
|
878
|
-
true,
|
|
879
|
-
TILES_PICTURES_ZOOM, layerFilters
|
|
880
|
-
];
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
if(layerFilters) { layer.filter = layerFilters; }
|
|
885
|
-
});
|
|
886
|
-
|
|
887
|
-
return layers;
|
|
888
|
-
}
|
|
889
|
-
}
|