@panoramax/web-viewer 3.2.3-develop-f219e404 → 3.2.3-develop-6257391e
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/.gitlab-ci.yml +3 -0
- package/CHANGELOG.md +19 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/README.md +1 -1
- package/build/editor.html +10 -1
- package/build/index.css +2 -2
- package/build/index.css.map +1 -1
- package/build/index.html +1 -1
- package/build/index.js +1682 -5
- package/build/index.js.map +1 -1
- package/build/map.html +1 -1
- package/build/viewer.html +10 -1
- package/build/widgets.html +1 -0
- package/config/jest/mocks.js +172 -0
- package/config/paths.js +1 -0
- package/config/webpack.config.js +26 -0
- package/docs/03_URL_settings.md +3 -11
- package/docs/05_Compatibility.md +59 -76
- package/docs/09_Develop.md +30 -11
- package/docs/90_Releases.md +2 -2
- package/docs/images/class_diagram.drawio +28 -28
- package/docs/images/class_diagram.jpg +0 -0
- package/docs/index.md +112 -0
- package/docs/reference/components/core/Basic.md +153 -0
- package/docs/reference/components/core/CoverageMap.md +160 -0
- package/docs/reference/components/core/Editor.md +172 -0
- package/docs/reference/components/core/Viewer.md +288 -0
- package/docs/reference/components/layout/CorneredGrid.md +29 -0
- package/docs/reference/components/layout/Mini.md +45 -0
- package/docs/reference/components/menus/MapBackground.md +32 -0
- package/docs/reference/components/menus/MapFilters.md +15 -0
- package/docs/reference/components/menus/MapLayers.md +15 -0
- package/docs/reference/components/menus/MapLegend.md +15 -0
- package/docs/reference/components/menus/PictureLegend.md +15 -0
- package/docs/reference/components/menus/PictureMetadata.md +15 -0
- package/docs/reference/components/menus/PlayerOptions.md +15 -0
- package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
- package/docs/reference/components/menus/ReportForm.md +15 -0
- package/docs/reference/components/menus/ShareMenu.md +15 -0
- package/docs/reference/components/ui/Button.md +39 -0
- package/docs/reference/components/ui/ButtonGroup.md +36 -0
- package/docs/reference/components/ui/CopyButton.md +35 -0
- package/docs/reference/components/ui/Grade.md +32 -0
- package/docs/reference/components/ui/LinkButton.md +44 -0
- package/docs/reference/components/ui/Loader.md +54 -0
- package/docs/reference/components/ui/Map.md +214 -0
- package/docs/reference/components/ui/MapMore.md +233 -0
- package/docs/reference/components/ui/Photo.md +369 -0
- package/docs/reference/components/ui/Popup.md +56 -0
- package/docs/reference/components/ui/QualityScore.md +45 -0
- package/docs/reference/components/ui/SearchBar.md +63 -0
- package/docs/reference/components/ui/TogglableGroup.md +39 -0
- package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
- package/docs/reference/components/ui/widgets/Legend.md +32 -0
- package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
- package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
- package/docs/reference/components/ui/widgets/Player.md +32 -0
- package/docs/reference/components/ui/widgets/Share.md +15 -0
- package/docs/reference/components/ui/widgets/Zoom.md +15 -0
- package/docs/reference/utils/API.md +311 -0
- package/docs/reference/utils/InitParameters.md +67 -0
- package/docs/reference/utils/URLHandler.md +102 -0
- package/docs/reference.md +73 -0
- package/docs/shortcuts.md +11 -0
- package/docs/tutorials/aerial_imagery.md +19 -0
- package/docs/tutorials/authentication.md +10 -0
- package/docs/tutorials/custom_widgets.md +64 -0
- package/docs/tutorials/map_style.md +27 -0
- package/docs/tutorials/migrate_v4.md +122 -0
- package/docs/tutorials/synced_coverage.md +42 -0
- package/mkdocs.yml +60 -5
- package/package.json +10 -7
- package/public/editor.html +21 -29
- package/public/index.html +3 -3
- package/public/map.html +19 -18
- package/public/viewer.html +18 -24
- package/public/widgets.html +265 -0
- package/scripts/doc.js +77 -0
- package/src/components/core/Basic.css +44 -0
- package/src/components/core/Basic.js +258 -0
- package/src/components/core/CoverageMap.css +9 -0
- package/src/components/core/CoverageMap.js +105 -0
- package/src/components/core/Editor.css +23 -0
- package/src/components/core/Editor.js +354 -0
- package/src/components/core/Viewer.css +109 -0
- package/src/components/core/Viewer.js +707 -0
- package/src/components/core/index.js +11 -0
- package/src/components/index.js +13 -0
- package/src/components/layout/CorneredGrid.js +109 -0
- package/src/components/layout/Mini.js +117 -0
- package/src/components/layout/index.js +7 -0
- package/src/components/menus/MapBackground.js +106 -0
- package/src/components/menus/MapFilters.js +386 -0
- package/src/components/menus/MapLayers.js +143 -0
- package/src/components/menus/MapLegend.js +54 -0
- package/src/components/menus/PictureLegend.js +103 -0
- package/src/components/menus/PictureMetadata.js +188 -0
- package/src/components/menus/PlayerOptions.js +96 -0
- package/src/components/menus/QualityScoreDoc.js +36 -0
- package/src/components/menus/ReportForm.js +133 -0
- package/src/components/menus/Share.js +228 -0
- package/src/components/menus/index.js +15 -0
- package/src/components/styles.js +365 -0
- package/src/components/ui/Button.js +75 -0
- package/src/components/ui/ButtonGroup.css +49 -0
- package/src/components/ui/ButtonGroup.js +68 -0
- package/src/components/ui/CopyButton.js +71 -0
- package/src/components/ui/Grade.js +54 -0
- package/src/components/ui/LinkButton.js +68 -0
- package/src/components/ui/Loader.js +188 -0
- package/src/components/{Map.css → ui/Map.css} +5 -17
- package/src/components/{Map.js → ui/Map.js} +114 -138
- package/src/components/ui/MapMore.js +324 -0
- package/src/components/{Photo.css → ui/Photo.css} +6 -6
- package/src/components/{Photo.js → ui/Photo.js} +279 -90
- package/src/components/ui/Popup.js +145 -0
- package/src/components/ui/QualityScore.js +152 -0
- package/src/components/ui/SearchBar.js +363 -0
- package/src/components/ui/TogglableGroup.js +162 -0
- package/src/components/ui/index.js +20 -0
- package/src/components/ui/widgets/GeoSearch.css +21 -0
- package/src/components/ui/widgets/GeoSearch.js +139 -0
- package/src/components/ui/widgets/Legend.js +51 -0
- package/src/components/ui/widgets/MapFiltersButton.js +104 -0
- package/src/components/ui/widgets/MapLayersButton.js +79 -0
- package/src/components/ui/widgets/Player.css +7 -0
- package/src/components/ui/widgets/Player.js +148 -0
- package/src/components/ui/widgets/Share.js +30 -0
- package/src/components/ui/widgets/Zoom.js +82 -0
- package/src/components/ui/widgets/index.js +12 -0
- package/src/img/panoramax.svg +13 -0
- package/src/img/switch_big.svg +20 -10
- package/src/index.js +6 -9
- package/src/translations/da.json +1 -1
- package/src/translations/de.json +1 -1
- package/src/translations/en.json +5 -3
- package/src/translations/eo.json +1 -1
- package/src/translations/es.json +1 -1
- package/src/translations/fr.json +5 -3
- package/src/translations/hu.json +1 -1
- package/src/translations/it.json +1 -1
- package/src/translations/ja.json +1 -1
- package/src/translations/nl.json +1 -1
- package/src/translations/pl.json +1 -1
- package/src/translations/sv.json +33 -3
- package/src/translations/zh_Hant.json +1 -1
- package/src/utils/API.js +74 -42
- package/src/utils/InitParameters.js +354 -0
- package/src/utils/URLHandler.js +364 -0
- package/src/utils/geocoder.js +116 -0
- package/src/utils/{I18n.js → i18n.js} +3 -1
- package/src/utils/index.js +11 -0
- package/src/utils/{Map.js → map.js} +216 -80
- package/src/utils/picture.js +433 -0
- package/src/utils/utils.js +315 -0
- package/src/utils/widgets.js +93 -0
- package/tests/components/ui/CopyButton.test.js +52 -0
- package/tests/components/ui/Loader.test.js +54 -0
- package/tests/components/{Map.test.js → ui/Map.test.js} +19 -61
- package/tests/components/{Photo.test.js → ui/Photo.test.js} +89 -57
- package/tests/components/ui/Popup.test.js +24 -0
- package/tests/components/ui/QualityScore.test.js +17 -0
- package/tests/components/ui/SearchBar.test.js +107 -0
- package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +34 -0
- package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
- package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
- package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +57 -4
- package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
- package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
- package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
- package/tests/utils/API.test.js +1 -14
- package/tests/utils/InitParameters.test.js +485 -0
- package/tests/utils/URLHandler.test.js +350 -0
- package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
- package/tests/utils/__snapshots__/picture.test.js.snap +315 -0
- package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
- package/tests/utils/geocoder.test.js +37 -0
- package/tests/utils/{I18n.test.js → i18n.test.js} +1 -1
- package/tests/utils/map.test.js +67 -0
- package/tests/utils/picture.test.js +745 -0
- package/tests/utils/utils.test.js +288 -0
- package/tests/utils/widgets.test.js +90 -0
- package/docs/01_Start.md +0 -149
- package/docs/02_Usage.md +0 -831
- package/docs/04_Advanced_examples.md +0 -216
- package/src/Editor.css +0 -37
- package/src/Editor.js +0 -361
- package/src/StandaloneMap.js +0 -114
- package/src/Viewer.css +0 -203
- package/src/Viewer.js +0 -1246
- package/src/components/CoreView.css +0 -70
- package/src/components/CoreView.js +0 -175
- package/src/components/Loader.css +0 -74
- package/src/components/Loader.js +0 -120
- package/src/utils/Exif.js +0 -193
- package/src/utils/Utils.js +0 -631
- package/src/utils/Widgets.js +0 -562
- package/src/viewer/URLHash.js +0 -469
- package/src/viewer/Widgets.css +0 -880
- package/src/viewer/Widgets.js +0 -1470
- package/tests/Editor.test.js +0 -126
- package/tests/StandaloneMap.test.js +0 -45
- package/tests/Viewer.test.js +0 -366
- package/tests/__snapshots__/Editor.test.js.snap +0 -298
- package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
- package/tests/__snapshots__/Viewer.test.js.snap +0 -195
- package/tests/components/CoreView.test.js +0 -92
- package/tests/components/Loader.test.js +0 -38
- package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
- package/tests/utils/Exif.test.js +0 -124
- package/tests/utils/Map.test.js +0 -113
- package/tests/utils/Utils.test.js +0 -300
- package/tests/utils/Widgets.test.js +0 -107
- package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
- package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
- package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
- package/tests/viewer/URLHash.test.js +0 -559
- package/tests/viewer/Widgets.test.js +0 -127
- package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
- package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
- /package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import {
|
|
2
|
+
alterPSVState, MAP_FILTERS_JS2URL, alterMapState, alterViewerState
|
|
3
|
+
} from "./InitParameters";
|
|
4
|
+
|
|
5
|
+
// List of supported parameters
|
|
6
|
+
const MANAGED_PARAMETERS = [
|
|
7
|
+
"speed", "nav", "focus", "pic", "xyz", "map",
|
|
8
|
+
"background", "users", "pic_score",
|
|
9
|
+
].concat(Object.values(MAP_FILTERS_JS2URL));
|
|
10
|
+
|
|
11
|
+
// Events to listen on parent and PSV
|
|
12
|
+
const UPDATE_PARENT_EVENTS = ["focus-changed", "pictures-navigation-changed"];
|
|
13
|
+
const UPDATE_PSV_EVENTS = ["position-updated", "zoom-updated", "view-rotated", "picture-loaded", "transition-duration-changed"];
|
|
14
|
+
const UPDATE_MAP_EVENTS = ["moveend", "zoomend", "boxzoomend", "background-changed", "users-changed", "filters-changed"];
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Updates the URL query part with various parent component information.
|
|
19
|
+
*
|
|
20
|
+
* Note that you may call `listenToChanges()` for this class to be effective once parent is ready-enough.
|
|
21
|
+
*
|
|
22
|
+
* @class Panoramax.utils.URLHandler
|
|
23
|
+
* @typicalname urlHandler
|
|
24
|
+
* @param {Panoramax.components.core.Basic} parent The parent component
|
|
25
|
+
* @fires Panoramax.utils.URLHandler#url-changed
|
|
26
|
+
*/
|
|
27
|
+
export default class URLHandler extends EventTarget {
|
|
28
|
+
constructor(parent) {
|
|
29
|
+
super();
|
|
30
|
+
this._parent = parent;
|
|
31
|
+
this._delay = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Start listening to URL & parent changes through events.
|
|
36
|
+
* This leads to parent & URL updates.
|
|
37
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
38
|
+
*/
|
|
39
|
+
listenToChanges() {
|
|
40
|
+
window.addEventListener("popstate", this._onURLChange.bind(this), false);
|
|
41
|
+
UPDATE_PARENT_EVENTS.forEach(e => this._parent.addEventListener(e, this._onParentChange.bind(this)));
|
|
42
|
+
UPDATE_PSV_EVENTS.forEach(e => this._parent.psv.addEventListener(e, this._onParentChange.bind(this)));
|
|
43
|
+
if(this._parent.map) {
|
|
44
|
+
UPDATE_MAP_EVENTS.forEach(e => this._parent.map.on(e, this._onParentChange.bind(this)));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compute next values to insert in URL
|
|
50
|
+
* @returns {object} Query parameters
|
|
51
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
52
|
+
*/
|
|
53
|
+
nextURLParams() {
|
|
54
|
+
let hashParts = {};
|
|
55
|
+
|
|
56
|
+
if(typeof this._parent.psv.getTransitionDuration() == "number") {
|
|
57
|
+
hashParts.speed = this._parent.psv.getTransitionDuration();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if(![null, "any"].includes(this._parent.psv.getPicturesNavigation())) {
|
|
61
|
+
hashParts.nav = this._parent.psv.getPicturesNavigation();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const picMeta = this._parent.psv.getPictureMetadata();
|
|
65
|
+
if (picMeta) {
|
|
66
|
+
hashParts.pic = picMeta.id;
|
|
67
|
+
hashParts.xyz = this.currentPSVString();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if(this._parent.map) {
|
|
71
|
+
hashParts.map = this.currentMapString();
|
|
72
|
+
hashParts.focus = "pic";
|
|
73
|
+
if(this._parent.isMapWide()) { hashParts.focus = "map"; }
|
|
74
|
+
if(this._parent.popup.hasAttribute("visible")) { hashParts.focus = "meta"; }
|
|
75
|
+
if(this._parent.map.hasTwoBackgrounds() && this._parent.map.getBackground()) {
|
|
76
|
+
hashParts.background = this._parent.map.getBackground();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const vu = this._parent.map.getVisibleUsers();
|
|
80
|
+
if(vu.length > 1 || !vu.includes("geovisio")) {
|
|
81
|
+
hashParts.users = vu.join(",");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if(this._parent.map._mapFilters) {
|
|
85
|
+
for(let k in MAP_FILTERS_JS2URL) {
|
|
86
|
+
if(this._parent.map._mapFilters[k]) {
|
|
87
|
+
hashParts[MAP_FILTERS_JS2URL[k]] = this._parent.map._mapFilters[k];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if(hashParts.pic_score) {
|
|
91
|
+
const mapping = [null, "E", "D", "C", "B", "A"];
|
|
92
|
+
hashParts.pic_score = hashParts.pic_score.map(v => mapping[v]).join("");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
hashParts.map = "none";
|
|
98
|
+
}
|
|
99
|
+
return hashParts;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Compute next URL query string (based on `nextURLParams()`)
|
|
104
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
105
|
+
* @return {string} The query string
|
|
106
|
+
*/
|
|
107
|
+
nextURLString() {
|
|
108
|
+
let hash = "";
|
|
109
|
+
|
|
110
|
+
Object.entries(this.nextURLParams())
|
|
111
|
+
.sort((a,b) => a[0].localeCompare(b[0]))
|
|
112
|
+
.forEach(entry => {
|
|
113
|
+
let [ hashName, value ] = entry;
|
|
114
|
+
let found = false;
|
|
115
|
+
const parts = hash.split("&").map(part => {
|
|
116
|
+
const key = part.split("=")[0];
|
|
117
|
+
if (key === hashName) {
|
|
118
|
+
found = true;
|
|
119
|
+
return `${key}=${value}`;
|
|
120
|
+
}
|
|
121
|
+
return part;
|
|
122
|
+
}).filter(a => a);
|
|
123
|
+
if (!found) {
|
|
124
|
+
parts.push(`${hashName}=${value}`);
|
|
125
|
+
}
|
|
126
|
+
hash = `${parts.join("&")}`;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return `?${hash}`.replace(/^\?+/, "?");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Transforms current URL query string into key->value object
|
|
134
|
+
* @return {object} Key-value read from current URL query
|
|
135
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
136
|
+
*/
|
|
137
|
+
currentURLParams() {
|
|
138
|
+
// Get the current hash from location, stripped from its number sign
|
|
139
|
+
const hash = window.location.search.replace(/^\?/, "");
|
|
140
|
+
|
|
141
|
+
// Split the parameter-styled hash into parts and find the value we need
|
|
142
|
+
let keyvals = {};
|
|
143
|
+
hash.split("&").map(
|
|
144
|
+
part => part.split("=")
|
|
145
|
+
)
|
|
146
|
+
.filter(part => part[0] !== undefined && part[0].length > 0)
|
|
147
|
+
.forEach(part => {
|
|
148
|
+
keyvals[part[0]] = part[1];
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// If hash is compressed
|
|
152
|
+
if(keyvals.s) {
|
|
153
|
+
const shortVals = Object.fromEntries(
|
|
154
|
+
keyvals.s
|
|
155
|
+
.split(";")
|
|
156
|
+
.map(kv => [kv[0], kv.substring(1)])
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
keyvals = {};
|
|
160
|
+
|
|
161
|
+
// Used letters: b c d e f k m n p q s t u v
|
|
162
|
+
// Focus
|
|
163
|
+
if(shortVals.f === "m") { keyvals.focus = "map"; }
|
|
164
|
+
else if(shortVals.f === "p") { keyvals.focus = "pic"; }
|
|
165
|
+
else if(shortVals.f === "t") { keyvals.focus = "meta"; }
|
|
166
|
+
|
|
167
|
+
// Speed
|
|
168
|
+
if(shortVals.s !== "") { keyvals.speed = parseFloat(shortVals.s) * 100; }
|
|
169
|
+
|
|
170
|
+
// Nav
|
|
171
|
+
if(shortVals.n === "a") { keyvals.nav = "any"; }
|
|
172
|
+
else if(shortVals.n === "s") { keyvals.nav = "seq"; }
|
|
173
|
+
if(shortVals.n === "n") { keyvals.nav = "none"; }
|
|
174
|
+
|
|
175
|
+
// Pic
|
|
176
|
+
if(shortVals.p !== "") { keyvals.pic = shortVals.p; }
|
|
177
|
+
|
|
178
|
+
// XYZ
|
|
179
|
+
if(shortVals.c !== "") { keyvals.xyz = shortVals.c; }
|
|
180
|
+
|
|
181
|
+
// Map
|
|
182
|
+
if(shortVals.m !== "") { keyvals.map = shortVals.m; }
|
|
183
|
+
|
|
184
|
+
// Date
|
|
185
|
+
if(shortVals.d !== "") { keyvals.date_from = shortVals.d; }
|
|
186
|
+
if(shortVals.e !== "") { keyvals.date_to = shortVals.e; }
|
|
187
|
+
|
|
188
|
+
// Pic type
|
|
189
|
+
if(shortVals.t === "f") { keyvals.pic_type = "flat"; }
|
|
190
|
+
else if(shortVals.t === "e") { keyvals.pic_type = "equirectangular"; }
|
|
191
|
+
|
|
192
|
+
// Camera
|
|
193
|
+
if(shortVals.k !== "") { keyvals.camera = shortVals.k; }
|
|
194
|
+
|
|
195
|
+
// Theme
|
|
196
|
+
if(shortVals.v === "d") { keyvals.theme = "default"; }
|
|
197
|
+
else if(shortVals.v === "a") { keyvals.theme = "age"; }
|
|
198
|
+
else if(shortVals.v === "t") { keyvals.theme = "type"; }
|
|
199
|
+
else if(shortVals.v === "s") { keyvals.theme = "score"; }
|
|
200
|
+
|
|
201
|
+
// Background
|
|
202
|
+
if(shortVals.b === "s") { keyvals.background = "streets"; }
|
|
203
|
+
else if(shortVals.b === "a") { keyvals.background = "aerial"; }
|
|
204
|
+
|
|
205
|
+
// Users
|
|
206
|
+
if(shortVals.u !== "") { keyvals.users = shortVals.u; }
|
|
207
|
+
|
|
208
|
+
// Photoscore
|
|
209
|
+
if(shortVals.q !== "") { keyvals.pic_score = shortVals.q; }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return keyvals;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get string representation of map position
|
|
217
|
+
* @returns {string} zoom/lat/lon or zoom/lat/lon/bearing/pitch
|
|
218
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
219
|
+
*/
|
|
220
|
+
currentMapString() {
|
|
221
|
+
const center = this._parent.map.getCenter(),
|
|
222
|
+
zoom = Math.round(this._parent.map.getZoom() * 100) / 100,
|
|
223
|
+
// derived from equation: 512px * 2^z / 360 / 10^d < 0.5px
|
|
224
|
+
precision = Math.ceil((zoom * Math.LN2 + Math.log(512 / 360 / 0.5)) / Math.LN10),
|
|
225
|
+
m = Math.pow(10, precision),
|
|
226
|
+
lng = Math.round(center.lng * m) / m,
|
|
227
|
+
lat = Math.round(center.lat * m) / m,
|
|
228
|
+
bearing = this._parent.map.getBearing(),
|
|
229
|
+
pitch = this._parent.map.getPitch();
|
|
230
|
+
let hash = `${zoom}/${lat}/${lng}`;
|
|
231
|
+
|
|
232
|
+
if (bearing || pitch) hash += (`/${Math.round(bearing * 10) / 10}`);
|
|
233
|
+
if (pitch) hash += (`/${Math.round(pitch)}`);
|
|
234
|
+
|
|
235
|
+
return hash;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get PSV view position as string
|
|
240
|
+
* @returns {string} x/y/z
|
|
241
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
242
|
+
*/
|
|
243
|
+
currentPSVString() {
|
|
244
|
+
const xyz = this._parent.psv.getXYZ();
|
|
245
|
+
const x = xyz.x.toFixed(2),
|
|
246
|
+
y = xyz.y.toFixed(2),
|
|
247
|
+
z = Math.round(xyz.z || 0);
|
|
248
|
+
return `${x}/${y}/${z}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Updates map and PSV according to current hash values
|
|
253
|
+
* @private
|
|
254
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
255
|
+
*/
|
|
256
|
+
_onURLChange() {
|
|
257
|
+
let vals = this.currentURLParams();
|
|
258
|
+
alterViewerState(this._parent, vals);
|
|
259
|
+
alterPSVState(this._parent.psv, vals);
|
|
260
|
+
if(this._parent.map) { alterMapState(this._parent.map, vals); }
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Get short link URL (query replaced by Base64)
|
|
265
|
+
* @returns {str} The short link URL
|
|
266
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
267
|
+
*/
|
|
268
|
+
nextShortLink(baseUrl) {
|
|
269
|
+
const url = new URL(baseUrl);
|
|
270
|
+
const hashParts = this.nextURLParams();
|
|
271
|
+
const shortVals = {
|
|
272
|
+
f: (hashParts.focus || "").substring(0, 1),
|
|
273
|
+
s: !isNaN(parseInt(hashParts.speed)) ? Math.floor(parseInt(hashParts.speed)/100) : undefined,
|
|
274
|
+
n: (hashParts.nav || "").substring(0, 1),
|
|
275
|
+
p: hashParts.pic,
|
|
276
|
+
c: hashParts.xyz,
|
|
277
|
+
m: hashParts.map,
|
|
278
|
+
d: hashParts.date_from,
|
|
279
|
+
e: hashParts.date_to,
|
|
280
|
+
t: (hashParts.pic_type || "").substring(0, 1),
|
|
281
|
+
k: hashParts.camera,
|
|
282
|
+
v: (hashParts.theme || "").substring(0, 1),
|
|
283
|
+
b: (hashParts.background || "").substring(0, 1),
|
|
284
|
+
u: hashParts.users,
|
|
285
|
+
q: hashParts.pic_score,
|
|
286
|
+
};
|
|
287
|
+
const short = Object.entries(shortVals)
|
|
288
|
+
.filter(([,v]) => v != undefined && v != "")
|
|
289
|
+
.map(([k,v]) => `${k}${v}`)
|
|
290
|
+
.join(";");
|
|
291
|
+
url.search = `s=${short}`;
|
|
292
|
+
return url;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Returns a string containing only parameters out of URLHandler scope
|
|
297
|
+
* @param {URL} prevUrl The previously set URL
|
|
298
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
299
|
+
*/
|
|
300
|
+
getUnmanagedParameters(prevUrl) {
|
|
301
|
+
return new URLSearchParams(
|
|
302
|
+
Array.from(prevUrl.searchParams)
|
|
303
|
+
.filter(([k]) => !MANAGED_PARAMETERS.includes(k))
|
|
304
|
+
).toString();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Changes the URL hash using current viewer parameters
|
|
309
|
+
* @private
|
|
310
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
311
|
+
*/
|
|
312
|
+
_onParentChange() {
|
|
313
|
+
if(this._delay) {
|
|
314
|
+
clearTimeout(this._delay);
|
|
315
|
+
this._delay = null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
this._delay = setTimeout(() => {
|
|
319
|
+
const prevUrl = new URL(window.location.href);
|
|
320
|
+
const nextUrl = new URL(window.location.href);
|
|
321
|
+
const unmanaged = this.getUnmanagedParameters(prevUrl);
|
|
322
|
+
nextUrl.search = this._parent ? this.nextURLString() + (unmanaged.length > 0 ? "&"+unmanaged : ""): "";
|
|
323
|
+
|
|
324
|
+
// Skip hash update if no changes
|
|
325
|
+
if(prevUrl.search == nextUrl.search) { return; }
|
|
326
|
+
|
|
327
|
+
const prevPic = this.currentURLParams().pic || "";
|
|
328
|
+
const nextPic = this._parent?.psv?.getPictureMetadata()?.id || "";
|
|
329
|
+
|
|
330
|
+
const prevFocus = this.currentURLParams().focus || "";
|
|
331
|
+
const nextFocus = nextUrl.search.includes("focus=meta") ? "meta" : (nextUrl.search.includes("focus=map") ? "map" : "pic");
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
// If different pic, add entry in browser history
|
|
335
|
+
if(prevPic != nextPic) {
|
|
336
|
+
window.history.pushState(window.history.state, null, nextUrl.href);
|
|
337
|
+
}
|
|
338
|
+
// If metadata popup is open, come back to pic/map
|
|
339
|
+
else if(prevFocus != nextFocus && nextFocus == "meta") {
|
|
340
|
+
window.history.pushState(window.history.state, null, nextUrl.href);
|
|
341
|
+
}
|
|
342
|
+
// If same pic, just update viewer params
|
|
343
|
+
else {
|
|
344
|
+
window.history.replaceState(window.history.state, null, nextUrl.href);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if(this._parent) {
|
|
348
|
+
/**
|
|
349
|
+
* URL changed event
|
|
350
|
+
* @event Panoramax.utils.URLHandler#url-changed
|
|
351
|
+
* @type {CustomEvent}
|
|
352
|
+
* @property {string} detail.url The new used URL
|
|
353
|
+
*/
|
|
354
|
+
const event = new CustomEvent("url-changed", { detail: {url: nextUrl.href}});
|
|
355
|
+
this.dispatchEvent(event);
|
|
356
|
+
}
|
|
357
|
+
} catch (SecurityError) {
|
|
358
|
+
// IE11 does not allow this if the page is within an iframe created
|
|
359
|
+
// with iframe.contentWindow.document.write(...).
|
|
360
|
+
// https://github.com/mapbox/mapbox-gl-js/issues/7410
|
|
361
|
+
}
|
|
362
|
+
}, 500);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// DO NOT REMOVE THE "!": bundled builds breaks otherwise !!!
|
|
2
|
+
import maplibregl from "!maplibre-gl";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transforms a set of parameters into an URL-ready string
|
|
6
|
+
* It also removes null/undefined values
|
|
7
|
+
*
|
|
8
|
+
* @param {object} params The parameters object
|
|
9
|
+
* @return {string} The URL query part
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
function geocoderParamsToURLString(params) {
|
|
13
|
+
let p = {};
|
|
14
|
+
Object.entries(params)
|
|
15
|
+
.filter(e => e[1] !== undefined && e[1] !== null)
|
|
16
|
+
.forEach(e => p[e[0]] = e[1]);
|
|
17
|
+
|
|
18
|
+
return new URLSearchParams(p).toString();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Nominatim (OSM) geocoder, ready to use for our Map
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
export function forwardGeocodingNominatim(config) {
|
|
26
|
+
// Transform parameters into Nominatim format
|
|
27
|
+
const params = {
|
|
28
|
+
q: config.query,
|
|
29
|
+
countrycodes: config.countries,
|
|
30
|
+
limit: config.limit,
|
|
31
|
+
viewbox: config.bbox,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return fetch(`https://nominatim.openstreetmap.org/search?${geocoderParamsToURLString(params)}&format=geojson&polygon_geojson=1&addressdetails=1`)
|
|
35
|
+
.then(res => res.json())
|
|
36
|
+
.then(res => {
|
|
37
|
+
const finalRes = { features: [] };
|
|
38
|
+
const listedNames = [];
|
|
39
|
+
res.features.forEach(f => {
|
|
40
|
+
if(!listedNames.includes(f.properties.display_name)) {
|
|
41
|
+
finalRes.features.push({
|
|
42
|
+
place_type: ["place"],
|
|
43
|
+
place_name: f.properties.display_name,
|
|
44
|
+
bounds: new maplibregl.LngLatBounds(f.bbox),
|
|
45
|
+
});
|
|
46
|
+
listedNames.push(f.properties.display_name);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return finalRes;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function reverseGeocodingNominatim(lat, lon) {
|
|
54
|
+
return fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&zoom=18&format=jsonv2`)
|
|
55
|
+
.then(res => res.json())
|
|
56
|
+
.then(res => {
|
|
57
|
+
let addr = "";
|
|
58
|
+
|
|
59
|
+
if(res?.address) {
|
|
60
|
+
if(res.address.house_number) { addr = res.address.house_number; }
|
|
61
|
+
|
|
62
|
+
// Street/place/hamlet
|
|
63
|
+
let street;
|
|
64
|
+
if(res.address.road && res.address.road.length > 6) {
|
|
65
|
+
street = res.address.road;
|
|
66
|
+
}
|
|
67
|
+
else if(res.address.hamlet) {
|
|
68
|
+
street = res.address.hamlet;
|
|
69
|
+
}
|
|
70
|
+
else if(res.address.isolated_dwelling) {
|
|
71
|
+
street = res.address.isolated_dwelling;
|
|
72
|
+
}
|
|
73
|
+
if(street && addr.length > 0) { addr += " "+street; }
|
|
74
|
+
else if(street) { addr = street; }
|
|
75
|
+
|
|
76
|
+
// City
|
|
77
|
+
if(res.address.village || res.address.town || res.address.city) {
|
|
78
|
+
if(addr.length > 0) { addr += ", "; }
|
|
79
|
+
addr += res.address.village || res.address.town || res.address.city;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return addr;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Base adresse nationale (FR) geocoder, ready to use for our Map
|
|
89
|
+
* @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://github.com/maplibre/maplibre-gl-geocoder/blob/main/API.md#setgeocoderapi )
|
|
90
|
+
* @returns {object} GeoJSON Feature collection in Carmen GeoJSON format
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
export function forwardGeocodingBAN(config) {
|
|
94
|
+
// Transform parameters into BAN format
|
|
95
|
+
const params = { q: config.query, limit: config.limit };
|
|
96
|
+
if(typeof config.proximity === "string") {
|
|
97
|
+
const [lat, lon] = config.proximity.split(",").map(v => parseFloat(v.trim()));
|
|
98
|
+
params.lat = lat;
|
|
99
|
+
params.lon = lon;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const toPlaceName = p => [p.name, p.district, p.city].filter(v => v).join(", ");
|
|
103
|
+
const placeTypeToZoom = { "housenumber": 20, "street": 18, "locality": 15, "municipality": 12 };
|
|
104
|
+
|
|
105
|
+
return fetch(`https://api-adresse.data.gouv.fr/search/?${geocoderParamsToURLString(params)}`)
|
|
106
|
+
.then(res => res.json())
|
|
107
|
+
.then(res => {
|
|
108
|
+
res.features = res.features.map(f => ({
|
|
109
|
+
place_type: ["place"],
|
|
110
|
+
place_name: toPlaceName(f.properties),
|
|
111
|
+
center: new maplibregl.LngLat(...f.geometry.coordinates),
|
|
112
|
+
zoom: placeTypeToZoom[f.properties.type],
|
|
113
|
+
}));
|
|
114
|
+
return res;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -6,13 +6,15 @@ import T_es from "../translations/es.json";
|
|
|
6
6
|
import T_fr from "../translations/fr.json";
|
|
7
7
|
import T_hu from "../translations/hu.json";
|
|
8
8
|
import T_it from "../translations/it.json";
|
|
9
|
+
import T_ja from "../translations/ja.json";
|
|
9
10
|
import T_pl from "../translations/pl.json";
|
|
11
|
+
import T_sv from "../translations/sv.json";
|
|
10
12
|
import T_zh_Hant from "../translations/zh_Hant.json";
|
|
11
13
|
|
|
12
14
|
const FALLBACK_LOCALE = "en";
|
|
13
15
|
const TRANSLATIONS = {
|
|
14
16
|
"da": T_da, "de": T_de, "eo": T_eo, "en": T_en, "es": T_es, "fr": T_fr,
|
|
15
|
-
"hu": T_hu, "it": T_it, "pl": T_pl, "zh_Hant": T_zh_Hant,
|
|
17
|
+
"hu": T_hu, "it": T_it, "ja": T_ja, "pl": T_pl, "sv": T_sv, "zh_Hant": T_zh_Hant,
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as geocoder from "./geocoder";
|
|
2
|
+
import * as i18n from "./i18n";
|
|
3
|
+
import * as map from "./map";
|
|
4
|
+
import * as picture from "./picture";
|
|
5
|
+
import * as utils from "./utils";
|
|
6
|
+
import * as widgets from "./widgets";
|
|
7
|
+
|
|
8
|
+
export { geocoder, i18n, map, picture, utils, widgets };
|
|
9
|
+
export {default as API} from "./API";
|
|
10
|
+
export {default as PhotoAdapter} from "./PhotoAdapter";
|
|
11
|
+
export {default as URLHandler} from "./URLHandler";
|