@panoramax/web-viewer 3.2.3 → 4.0.0
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 +13 -6
- package/CHANGELOG.md +49 -1
- package/CODE_OF_CONDUCT.md +1 -1
- package/README.md +1 -1
- package/build/editor.html +10 -1
- package/build/index.css +12 -12
- package/build/index.css.map +1 -1
- package/build/index.html +1 -1
- package/build/index.js +2126 -14
- package/build/index.js.map +1 -1
- package/build/map.html +1 -1
- package/build/photo.html +1 -0
- package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff +0 -0
- package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff2 +0 -0
- package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff +0 -0
- package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff2 +0 -0
- package/build/viewer.html +12 -1
- package/build/widgets.html +1 -0
- package/config/jest/mocks.js +201 -0
- package/config/paths.js +2 -0
- package/config/webpack.config.js +52 -0
- package/docs/03_URL_settings.md +14 -16
- package/docs/05_Compatibility.md +59 -76
- package/docs/09_Develop.md +46 -11
- package/docs/90_Releases.md +2 -2
- package/docs/images/class_diagram.drawio +60 -45
- package/docs/images/class_diagram.jpg +0 -0
- package/docs/images/screenshot.jpg +0 -0
- package/docs/index.md +135 -0
- package/docs/reference/components/core/Basic.md +196 -0
- package/docs/reference/components/core/CoverageMap.md +210 -0
- package/docs/reference/components/core/Editor.md +224 -0
- package/docs/reference/components/core/PhotoViewer.md +307 -0
- package/docs/reference/components/core/Viewer.md +350 -0
- package/docs/reference/components/layout/BottomDrawer.md +35 -0
- package/docs/reference/components/layout/CorneredGrid.md +29 -0
- package/docs/reference/components/layout/Mini.md +45 -0
- package/docs/reference/components/layout/Tabs.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 +16 -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 +40 -0
- package/docs/reference/components/ui/ButtonGroup.md +36 -0
- package/docs/reference/components/ui/CopyButton.md +38 -0
- package/docs/reference/components/ui/Grade.md +32 -0
- package/docs/reference/components/ui/LinkButton.md +45 -0
- package/docs/reference/components/ui/ListGroup.md +22 -0
- package/docs/reference/components/ui/Loader.md +56 -0
- package/docs/reference/components/ui/Map.md +239 -0
- package/docs/reference/components/ui/MapMore.md +256 -0
- package/docs/reference/components/ui/Photo.md +385 -0
- package/docs/reference/components/ui/Popup.md +56 -0
- package/docs/reference/components/ui/ProgressBar.md +32 -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 +49 -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/OSMEditors.md +15 -0
- package/docs/reference/components/ui/widgets/PictureLegendActions.md +32 -0
- package/docs/reference/components/ui/widgets/Player.md +33 -0
- package/docs/reference/components/ui/widgets/Zoom.md +15 -0
- package/docs/reference/utils/API.md +334 -0
- package/docs/reference/utils/InitParameters.md +68 -0
- package/docs/reference/utils/URLHandler.md +107 -0
- package/docs/reference.md +79 -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 +59 -0
- package/docs/tutorials/map_style.md +39 -0
- package/docs/tutorials/migrate_v4.md +153 -0
- package/docs/tutorials/synced_coverage.md +43 -0
- package/mkdocs.yml +66 -5
- package/package.json +22 -17
- package/public/editor.html +21 -29
- package/public/index.html +17 -12
- package/public/map.html +19 -18
- package/public/photo.html +55 -0
- package/public/viewer.html +22 -26
- package/public/widgets.html +306 -0
- package/scripts/doc.js +79 -0
- package/src/components/core/Basic.css +48 -0
- package/src/components/core/Basic.js +349 -0
- package/src/components/core/CoverageMap.css +9 -0
- package/src/components/core/CoverageMap.js +139 -0
- package/src/components/core/Editor.css +23 -0
- package/src/components/core/Editor.js +390 -0
- package/src/components/core/PhotoViewer.css +48 -0
- package/src/components/core/PhotoViewer.js +499 -0
- package/src/components/core/Viewer.css +98 -0
- package/src/components/core/Viewer.js +564 -0
- package/src/components/core/index.js +12 -0
- package/src/components/index.js +13 -0
- package/src/components/layout/BottomDrawer.js +257 -0
- package/src/components/layout/CorneredGrid.js +112 -0
- package/src/components/layout/Mini.js +117 -0
- package/src/components/layout/Tabs.js +133 -0
- package/src/components/layout/index.js +9 -0
- package/src/components/menus/MapBackground.js +106 -0
- package/src/components/menus/MapFilters.js +400 -0
- package/src/components/menus/MapLayers.js +143 -0
- package/src/components/menus/MapLegend.js +34 -0
- package/src/components/menus/PictureLegend.js +253 -0
- package/src/components/menus/PictureMetadata.js +317 -0
- package/src/components/menus/PlayerOptions.js +95 -0
- package/src/components/menus/QualityScoreDoc.js +36 -0
- package/src/components/menus/ReportForm.js +133 -0
- package/src/components/menus/Share.js +100 -0
- package/src/components/menus/index.js +15 -0
- package/src/components/styles.js +383 -0
- package/src/components/ui/Button.js +77 -0
- package/src/components/ui/ButtonGroup.css +57 -0
- package/src/components/ui/ButtonGroup.js +68 -0
- package/src/components/ui/CopyButton.js +106 -0
- package/src/components/ui/Grade.js +54 -0
- package/src/components/ui/LinkButton.js +67 -0
- package/src/components/ui/ListGroup.js +66 -0
- package/src/components/ui/Loader.js +203 -0
- package/src/components/{Map.css → ui/Map.css} +5 -17
- package/src/components/{Map.js → ui/Map.js} +148 -156
- 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} +313 -101
- package/src/components/ui/Popup.js +145 -0
- package/src/components/ui/ProgressBar.js +104 -0
- package/src/components/ui/QualityScore.js +147 -0
- package/src/components/ui/SearchBar.js +367 -0
- package/src/components/ui/TogglableGroup.js +157 -0
- package/src/components/ui/index.js +22 -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 +113 -0
- package/src/components/ui/widgets/MapFiltersButton.js +104 -0
- package/src/components/ui/widgets/MapLayersButton.js +79 -0
- package/src/components/ui/widgets/OSMEditors.js +155 -0
- package/src/components/ui/widgets/PictureLegendActions.js +117 -0
- package/src/components/ui/widgets/Player.css +7 -0
- package/src/components/ui/widgets/Player.js +151 -0
- package/src/components/ui/widgets/Zoom.js +82 -0
- package/src/components/ui/widgets/index.js +13 -0
- package/src/img/loader_base.jpg +0 -0
- package/src/img/panoramax.svg +13 -0
- package/src/img/switch_big.svg +20 -10
- package/src/index.js +7 -9
- package/src/translations/br.json +1 -0
- package/src/translations/da.json +38 -15
- package/src/translations/de.json +5 -3
- package/src/translations/en.json +35 -15
- package/src/translations/eo.json +38 -15
- package/src/translations/es.json +1 -1
- package/src/translations/fr.json +36 -16
- package/src/translations/hu.json +1 -1
- package/src/translations/it.json +39 -16
- package/src/translations/ja.json +182 -1
- package/src/translations/nl.json +106 -6
- package/src/translations/pl.json +1 -1
- package/src/translations/sv.json +182 -0
- package/src/translations/zh_Hant.json +35 -14
- package/src/utils/API.js +109 -49
- package/src/utils/InitParameters.js +388 -0
- package/src/utils/PhotoAdapter.js +1 -0
- package/src/utils/URLHandler.js +362 -0
- package/src/utils/geocoder.js +152 -0
- package/src/utils/{I18n.js → i18n.js} +7 -3
- package/src/utils/index.js +11 -0
- package/src/utils/{Map.js → map.js} +256 -77
- package/src/utils/picture.js +442 -0
- package/src/utils/utils.js +324 -0
- package/src/utils/widgets.js +55 -0
- package/tests/components/core/Basic.test.js +121 -0
- package/tests/components/core/BasicMock.js +25 -0
- package/tests/components/core/CoverageMap.test.js +20 -0
- package/tests/components/core/Editor.test.js +20 -0
- package/tests/components/core/PhotoViewer.test.js +57 -0
- package/tests/components/core/Viewer.test.js +84 -0
- package/tests/components/core/__snapshots__/PhotoViewer.test.js.snap +73 -0
- package/tests/components/core/__snapshots__/Viewer.test.js.snap +145 -0
- package/tests/components/ui/CopyButton.test.js +52 -0
- package/tests/components/ui/Loader.test.js +55 -0
- package/tests/components/{Map.test.js → ui/Map.test.js} +73 -61
- package/tests/components/{Photo.test.js → ui/Photo.test.js} +97 -63
- package/tests/components/ui/Popup.test.js +26 -0
- package/tests/components/ui/QualityScore.test.js +18 -0
- package/tests/components/ui/SearchBar.test.js +110 -0
- package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +33 -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 +70 -6
- 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 +83 -83
- package/tests/utils/InitParameters.test.js +499 -0
- package/tests/utils/URLHandler.test.js +401 -0
- package/tests/utils/__snapshots__/API.test.js.snap +10 -0
- package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
- package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +1 -1
- package/tests/utils/__snapshots__/map.test.js.snap +11 -0
- package/tests/utils/__snapshots__/picture.test.js.snap +327 -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} +8 -8
- package/tests/utils/map.test.js +126 -0
- package/tests/utils/picture.test.js +745 -0
- package/tests/utils/utils.test.js +288 -0
- package/tests/utils/widgets.test.js +31 -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/img/loader_hd.jpg +0 -0
- 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
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import {
|
|
2
|
+
alterPSVState, MAP_FILTERS_JS2URL, alterMapState, alterViewerState, alterPhotoViewerState
|
|
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", "s"
|
|
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
|
+
if(this._parent.psv.getPictureId()) {
|
|
65
|
+
hashParts.pic = this._parent.psv.getPictureId();
|
|
66
|
+
}
|
|
67
|
+
const picMeta = this._parent.psv.getPictureMetadata();
|
|
68
|
+
if (picMeta) {
|
|
69
|
+
hashParts.xyz = this.currentPSVString();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if(this._parent.map) {
|
|
73
|
+
hashParts.map = this.currentMapString();
|
|
74
|
+
hashParts.focus = "pic";
|
|
75
|
+
if(this._parent.isMapWide()) { hashParts.focus = "map"; }
|
|
76
|
+
if(this._parent.map.hasTwoBackgrounds() && this._parent.map.getBackground()) {
|
|
77
|
+
hashParts.background = this._parent.map.getBackground();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const vu = this._parent.map.getVisibleUsers();
|
|
81
|
+
if(vu.length > 1 || !vu.includes("geovisio")) {
|
|
82
|
+
hashParts.users = vu.join(",");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if(this._parent.map._mapFilters) {
|
|
86
|
+
for(let k in MAP_FILTERS_JS2URL) {
|
|
87
|
+
if(this._parent.map._mapFilters[k]) {
|
|
88
|
+
hashParts[MAP_FILTERS_JS2URL[k]] = this._parent.map._mapFilters[k];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if(hashParts.pic_score) {
|
|
92
|
+
const mapping = [null, "E", "D", "C", "B", "A"];
|
|
93
|
+
hashParts.pic_score = hashParts.pic_score.map(v => mapping[v]).join("");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return hashParts;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Compute next URL query string (based on `nextURLParams()`)
|
|
102
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
103
|
+
* @return {string} The query string
|
|
104
|
+
*/
|
|
105
|
+
nextURLString() {
|
|
106
|
+
let hash = "";
|
|
107
|
+
|
|
108
|
+
Object.entries(this.nextURLParams())
|
|
109
|
+
.sort((a,b) => a[0].localeCompare(b[0]))
|
|
110
|
+
.forEach(entry => {
|
|
111
|
+
let [ hashName, value ] = entry;
|
|
112
|
+
let found = false;
|
|
113
|
+
const parts = hash.split("&").map(part => {
|
|
114
|
+
const key = part.split("=")[0];
|
|
115
|
+
if (key === hashName) {
|
|
116
|
+
found = true;
|
|
117
|
+
return `${key}=${value}`;
|
|
118
|
+
}
|
|
119
|
+
return part;
|
|
120
|
+
}).filter(a => a);
|
|
121
|
+
if (!found) {
|
|
122
|
+
parts.push(`${hashName}=${value}`);
|
|
123
|
+
}
|
|
124
|
+
hash = `${parts.join("&")}`;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return `?${hash}`.replace(/^\?+/, "?");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Transforms current URL query string into key->value object
|
|
132
|
+
* @param {boolean} [readFromHash=false] Switch to reading from hash URL part (for retro-compatibility)
|
|
133
|
+
* @return {object} Key-value read from current URL query
|
|
134
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
135
|
+
*/
|
|
136
|
+
currentURLParams(readFromHash = false) {
|
|
137
|
+
// Get the current hash from location, stripped from its number sign
|
|
138
|
+
const hash = (readFromHash ? window.location.hash : window.location.search).replace(/^[?#]/, "");
|
|
139
|
+
|
|
140
|
+
// Split the parameter-styled hash into parts and find the value we need
|
|
141
|
+
let keyvals = {};
|
|
142
|
+
hash.split("&").map(
|
|
143
|
+
part => part.split("=")
|
|
144
|
+
)
|
|
145
|
+
.filter(part => part[0] !== undefined && part[0].length > 0 && MANAGED_PARAMETERS.includes(part[0]))
|
|
146
|
+
.forEach(part => {
|
|
147
|
+
keyvals[part[0]] = part[1];
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// If hash is compressed
|
|
151
|
+
if(keyvals.s) {
|
|
152
|
+
const shortVals = Object.fromEntries(
|
|
153
|
+
keyvals.s
|
|
154
|
+
.split(";")
|
|
155
|
+
.map(kv => [kv[0], kv.substring(1)])
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
keyvals = {};
|
|
159
|
+
|
|
160
|
+
// Used letters: b c d e f k m n p q s t u v
|
|
161
|
+
// Focus
|
|
162
|
+
if(shortVals.f === "m") { keyvals.focus = "map"; }
|
|
163
|
+
else if(shortVals.f === "p") { keyvals.focus = "pic"; }
|
|
164
|
+
else if(shortVals.f === "t") { keyvals.focus = "meta"; }
|
|
165
|
+
|
|
166
|
+
// Speed
|
|
167
|
+
if(shortVals.s !== "") { keyvals.speed = parseFloat(shortVals.s) * 100; }
|
|
168
|
+
|
|
169
|
+
// Nav
|
|
170
|
+
if(shortVals.n === "a") { keyvals.nav = "any"; }
|
|
171
|
+
else if(shortVals.n === "s") { keyvals.nav = "seq"; }
|
|
172
|
+
if(shortVals.n === "n") { keyvals.nav = "none"; }
|
|
173
|
+
|
|
174
|
+
// Pic
|
|
175
|
+
if(shortVals.p !== "") { keyvals.pic = shortVals.p; }
|
|
176
|
+
|
|
177
|
+
// XYZ
|
|
178
|
+
if(shortVals.c !== "") { keyvals.xyz = shortVals.c; }
|
|
179
|
+
|
|
180
|
+
// Map
|
|
181
|
+
if(shortVals.m !== "") { keyvals.map = shortVals.m; }
|
|
182
|
+
|
|
183
|
+
// Date
|
|
184
|
+
if(shortVals.d !== "") { keyvals.date_from = shortVals.d; }
|
|
185
|
+
if(shortVals.e !== "") { keyvals.date_to = shortVals.e; }
|
|
186
|
+
|
|
187
|
+
// Pic type
|
|
188
|
+
if(shortVals.t === "f") { keyvals.pic_type = "flat"; }
|
|
189
|
+
else if(shortVals.t === "e") { keyvals.pic_type = "equirectangular"; }
|
|
190
|
+
|
|
191
|
+
// Camera
|
|
192
|
+
if(shortVals.k !== "") { keyvals.camera = shortVals.k; }
|
|
193
|
+
|
|
194
|
+
// Theme
|
|
195
|
+
if(shortVals.v === "d") { keyvals.theme = "default"; }
|
|
196
|
+
else if(shortVals.v === "a") { keyvals.theme = "age"; }
|
|
197
|
+
else if(shortVals.v === "t") { keyvals.theme = "type"; }
|
|
198
|
+
else if(shortVals.v === "s") { keyvals.theme = "score"; }
|
|
199
|
+
|
|
200
|
+
// Background
|
|
201
|
+
if(shortVals.b === "s") { keyvals.background = "streets"; }
|
|
202
|
+
else if(shortVals.b === "a") { keyvals.background = "aerial"; }
|
|
203
|
+
|
|
204
|
+
// Users
|
|
205
|
+
if(shortVals.u !== "") { keyvals.users = shortVals.u; }
|
|
206
|
+
|
|
207
|
+
// Photoscore
|
|
208
|
+
if(shortVals.q !== "") { keyvals.pic_score = shortVals.q; }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return keyvals;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get string representation of map position
|
|
216
|
+
* @returns {string} zoom/lat/lon or zoom/lat/lon/bearing/pitch
|
|
217
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
218
|
+
*/
|
|
219
|
+
currentMapString() {
|
|
220
|
+
const center = this._parent.map.getCenter(),
|
|
221
|
+
zoom = Math.round(this._parent.map.getZoom() * 100) / 100,
|
|
222
|
+
// derived from equation: 512px * 2^z / 360 / 10^d < 0.5px
|
|
223
|
+
precision = Math.ceil((zoom * Math.LN2 + Math.log(512 / 360 / 0.5)) / Math.LN10),
|
|
224
|
+
m = Math.pow(10, precision),
|
|
225
|
+
lng = Math.round(center.lng * m) / m,
|
|
226
|
+
lat = Math.round(center.lat * m) / m,
|
|
227
|
+
bearing = this._parent.map.getBearing(),
|
|
228
|
+
pitch = this._parent.map.getPitch();
|
|
229
|
+
let hash = `${zoom}/${lat}/${lng}`;
|
|
230
|
+
|
|
231
|
+
if (bearing || pitch) hash += (`/${Math.round(bearing * 10) / 10}`);
|
|
232
|
+
if (pitch) hash += (`/${Math.round(pitch)}`);
|
|
233
|
+
|
|
234
|
+
return hash;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Get PSV view position as string
|
|
239
|
+
* @returns {string} x/y/z
|
|
240
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
241
|
+
*/
|
|
242
|
+
currentPSVString() {
|
|
243
|
+
const xyz = this._parent.psv.getXYZ();
|
|
244
|
+
const x = xyz.x.toFixed(2),
|
|
245
|
+
y = xyz.y.toFixed(2),
|
|
246
|
+
z = Math.round(xyz.z || 0);
|
|
247
|
+
return `${x}/${y}/${z}`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Updates map and PSV according to current hash values
|
|
252
|
+
* @private
|
|
253
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
254
|
+
*/
|
|
255
|
+
_onURLChange() {
|
|
256
|
+
let vals = this.currentURLParams();
|
|
257
|
+
|
|
258
|
+
if(this._parent.getClassName() === "Viewer") { alterViewerState(this._parent, vals); }
|
|
259
|
+
else { alterPhotoViewerState(this._parent, vals); }
|
|
260
|
+
|
|
261
|
+
alterPSVState(this._parent.psv, vals);
|
|
262
|
+
if(this._parent.map) { alterMapState(this._parent.map, vals); }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get short link URL (query replaced by Base64)
|
|
267
|
+
* @returns {str} The short link URL
|
|
268
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
269
|
+
*/
|
|
270
|
+
nextShortLink(baseUrl) {
|
|
271
|
+
const url = new URL(baseUrl);
|
|
272
|
+
const hashParts = this.nextURLParams();
|
|
273
|
+
const shortVals = {
|
|
274
|
+
f: (hashParts.focus || "").substring(0, 1),
|
|
275
|
+
s: !isNaN(parseInt(hashParts.speed)) ? Math.floor(parseInt(hashParts.speed)/100) : undefined,
|
|
276
|
+
n: (hashParts.nav || "").substring(0, 1),
|
|
277
|
+
p: hashParts.pic,
|
|
278
|
+
c: hashParts.xyz,
|
|
279
|
+
m: hashParts.map,
|
|
280
|
+
d: hashParts.date_from,
|
|
281
|
+
e: hashParts.date_to,
|
|
282
|
+
t: (hashParts.pic_type || "").substring(0, 1),
|
|
283
|
+
k: hashParts.camera,
|
|
284
|
+
v: (hashParts.theme || "").substring(0, 1),
|
|
285
|
+
b: (hashParts.background || "").substring(0, 1),
|
|
286
|
+
u: hashParts.users,
|
|
287
|
+
q: hashParts.pic_score,
|
|
288
|
+
};
|
|
289
|
+
const short = Object.entries(shortVals)
|
|
290
|
+
.filter(([,v]) => v != undefined && v != "")
|
|
291
|
+
.map(([k,v]) => `${k}${v}`)
|
|
292
|
+
.join(";");
|
|
293
|
+
url.search = `s=${short}`;
|
|
294
|
+
return url;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Returns a string containing only parameters out of URLHandler scope
|
|
299
|
+
* @param {URL} prevUrl The previously set URL
|
|
300
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
301
|
+
*/
|
|
302
|
+
getUnmanagedParameters(prevUrl) {
|
|
303
|
+
return new URLSearchParams(
|
|
304
|
+
Array.from(prevUrl.searchParams)
|
|
305
|
+
.filter(([k]) => !MANAGED_PARAMETERS.includes(k))
|
|
306
|
+
).toString();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Changes the URL hash using current viewer parameters
|
|
311
|
+
* @private
|
|
312
|
+
* @memberof Panoramax.utils.URLHandler#
|
|
313
|
+
*/
|
|
314
|
+
_onParentChange() {
|
|
315
|
+
if(this._delay) {
|
|
316
|
+
clearTimeout(this._delay);
|
|
317
|
+
this._delay = null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
this._delay = setTimeout(() => {
|
|
321
|
+
const prevUrl = new URL(window.location.href);
|
|
322
|
+
const nextUrl = new URL(window.location.href);
|
|
323
|
+
const unmanaged = this.getUnmanagedParameters(prevUrl);
|
|
324
|
+
nextUrl.search = this._parent ? this.nextURLString() + (unmanaged.length > 0 ? "&"+unmanaged : ""): "";
|
|
325
|
+
|
|
326
|
+
// Clear out hash if older parameters appear
|
|
327
|
+
if(Object.keys(this.currentURLParams(true)).length > 0) { nextUrl.hash = ""; }
|
|
328
|
+
|
|
329
|
+
// Skip hash update if no changes
|
|
330
|
+
if(prevUrl.search == nextUrl.search) { return; }
|
|
331
|
+
|
|
332
|
+
const prevPic = this.currentURLParams().pic || "";
|
|
333
|
+
const nextPic = this._parent?.psv?.getPictureId?.() || "";
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
// If different pic, add entry in browser history
|
|
337
|
+
if(prevPic != nextPic) {
|
|
338
|
+
window.history.pushState(window.history.state, null, nextUrl.href);
|
|
339
|
+
}
|
|
340
|
+
// If same pic, just update viewer params
|
|
341
|
+
else {
|
|
342
|
+
window.history.replaceState(window.history.state, null, nextUrl.href);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if(this._parent) {
|
|
346
|
+
/**
|
|
347
|
+
* URL changed event
|
|
348
|
+
* @event Panoramax.utils.URLHandler#url-changed
|
|
349
|
+
* @type {CustomEvent}
|
|
350
|
+
* @property {string} detail.url The new used URL
|
|
351
|
+
*/
|
|
352
|
+
const event = new CustomEvent("url-changed", { detail: {url: nextUrl.href}});
|
|
353
|
+
this.dispatchEvent(event);
|
|
354
|
+
}
|
|
355
|
+
} catch (SecurityError) {
|
|
356
|
+
// IE11 does not allow this if the page is within an iframe created
|
|
357
|
+
// with iframe.contentWindow.document.write(...).
|
|
358
|
+
// https://github.com/mapbox/mapbox-gl-js/issues/7410
|
|
359
|
+
}
|
|
360
|
+
}, 500);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
* Transforms Nominatim search result into a nice-to-display address.
|
|
23
|
+
* @param {object} addr The Nominatim API "address" property
|
|
24
|
+
* @returns {string} The clean-up string for display
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
function nominatimAddressToPlaceName(addr) {
|
|
28
|
+
// API format @ https://nominatim.org/release-docs/develop/api/Output/#addressdetails
|
|
29
|
+
if(!addr || typeof addr != "object") { return ""; }
|
|
30
|
+
|
|
31
|
+
let res = "";
|
|
32
|
+
|
|
33
|
+
// House n°-like
|
|
34
|
+
if(addr.house_number) { res = addr.house_number; }
|
|
35
|
+
else if(addr.house_name) { res = addr.house_name; }
|
|
36
|
+
else {
|
|
37
|
+
const potentialNames = [
|
|
38
|
+
"emergency", "historic", "military", "natural", "landuse", "place", "railway", "man_made",
|
|
39
|
+
"aerialway", "boundary", "amenity", "aeroway", "club", "craft", "leisure", "office",
|
|
40
|
+
"mountain_pass", "shop", "tourism", "bridge", "tunnel", "waterway", "park"
|
|
41
|
+
];
|
|
42
|
+
for(let pn of potentialNames) {
|
|
43
|
+
if(addr[pn]) {
|
|
44
|
+
res = addr[pn];
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Street-like
|
|
51
|
+
let street;
|
|
52
|
+
if(addr.road && addr.road.length > 6) { street = addr.road; }
|
|
53
|
+
else {
|
|
54
|
+
const potentialNames = [
|
|
55
|
+
// Hamlet-like
|
|
56
|
+
"hamlet", "croft", "isolated_dwelling",
|
|
57
|
+
// Zone Indus-like
|
|
58
|
+
"farm", "farmyard", "industrial", "commercial", "retail", "city_block", "residential",
|
|
59
|
+
// Quarter-like
|
|
60
|
+
"neighbourhood", "allotments", "quarter",
|
|
61
|
+
// Fallback to road if nothing else found
|
|
62
|
+
"road"
|
|
63
|
+
];
|
|
64
|
+
for(let pn of potentialNames) {
|
|
65
|
+
if(addr[pn]) {
|
|
66
|
+
street = addr[pn];
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if(street && res.length > 0) { res += (addr.house_number ? " " : ", ")+street; }
|
|
73
|
+
else if(street) { res = street; }
|
|
74
|
+
|
|
75
|
+
// City
|
|
76
|
+
if(addr.village || addr.town || addr.city || addr.municipality) {
|
|
77
|
+
if(res.length > 0) { res += ", "; }
|
|
78
|
+
res += addr.village || addr.town || addr.city || addr.municipality;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return res;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Nominatim (OSM) geocoder, ready to use for our Map
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
export function forwardGeocodingNominatim(config) {
|
|
89
|
+
// Transform parameters into Nominatim format
|
|
90
|
+
const params = {
|
|
91
|
+
q: config.query,
|
|
92
|
+
countrycodes: config.countries,
|
|
93
|
+
limit: config.limit,
|
|
94
|
+
viewbox: config.bbox,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return fetch(`https://nominatim.openstreetmap.org/search?${geocoderParamsToURLString(params)}&format=geojson&polygon_geojson=1&addressdetails=1`)
|
|
98
|
+
.then(res => res.json())
|
|
99
|
+
.then(res => {
|
|
100
|
+
const finalRes = { features: [] };
|
|
101
|
+
const listedNames = [];
|
|
102
|
+
res.features.forEach(f => {
|
|
103
|
+
const plname = nominatimAddressToPlaceName(f.properties.address) || f.properties.display_name;
|
|
104
|
+
if(!listedNames.includes(plname)) {
|
|
105
|
+
finalRes.features.push({
|
|
106
|
+
place_type: ["place"],
|
|
107
|
+
place_name: plname,
|
|
108
|
+
bounds: new maplibregl.LngLatBounds(f.bbox),
|
|
109
|
+
});
|
|
110
|
+
listedNames.push(plname);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return finalRes;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function reverseGeocodingNominatim(lat, lon) {
|
|
118
|
+
return fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&zoom=18&format=jsonv2`)
|
|
119
|
+
.then(res => res.json())
|
|
120
|
+
.then(res => nominatimAddressToPlaceName(res?.address));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Base adresse nationale (FR) geocoder, ready to use for our Map
|
|
125
|
+
* @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 )
|
|
126
|
+
* @returns {object} GeoJSON Feature collection in Carmen GeoJSON format
|
|
127
|
+
* @private
|
|
128
|
+
*/
|
|
129
|
+
export function forwardGeocodingBAN(config) {
|
|
130
|
+
// Transform parameters into BAN format
|
|
131
|
+
const params = { q: config.query, limit: config.limit };
|
|
132
|
+
if(typeof config.proximity === "string") {
|
|
133
|
+
const [lat, lon] = config.proximity.split(",").map(v => parseFloat(v.trim()));
|
|
134
|
+
params.lat = lat;
|
|
135
|
+
params.lon = lon;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const toPlaceName = p => [p.name, p.district, p.city].filter(v => v).join(", ");
|
|
139
|
+
const placeTypeToZoom = { "housenumber": 20, "street": 18, "locality": 15, "municipality": 12 };
|
|
140
|
+
|
|
141
|
+
return fetch(`https://api-adresse.data.gouv.fr/search/?${geocoderParamsToURLString(params)}`)
|
|
142
|
+
.then(res => res.json())
|
|
143
|
+
.then(res => {
|
|
144
|
+
res.features = res.features.map(f => ({
|
|
145
|
+
place_type: ["place"],
|
|
146
|
+
place_name: toPlaceName(f.properties),
|
|
147
|
+
center: new maplibregl.LngLat(...f.geometry.coordinates),
|
|
148
|
+
zoom: placeTypeToZoom[f.properties.type],
|
|
149
|
+
}));
|
|
150
|
+
return res;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
@@ -6,13 +6,17 @@ 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";
|
|
10
|
+
import T_nl from "../translations/nl.json";
|
|
9
11
|
import T_pl from "../translations/pl.json";
|
|
12
|
+
import T_sv from "../translations/sv.json";
|
|
10
13
|
import T_zh_Hant from "../translations/zh_Hant.json";
|
|
11
14
|
|
|
12
15
|
const FALLBACK_LOCALE = "en";
|
|
13
16
|
const TRANSLATIONS = {
|
|
14
17
|
"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, "
|
|
18
|
+
"hu": T_hu, "it": T_it, "ja": T_ja, "nl": T_nl, "pl": T_pl, "sv": T_sv, "zh-Hant": T_zh_Hant,
|
|
19
|
+
"zh": T_zh_Hant,
|
|
16
20
|
};
|
|
17
21
|
|
|
18
22
|
/**
|
|
@@ -30,11 +34,11 @@ export function autoDetectLocale(supportedTranslations, fallback) { // eslint-ig
|
|
|
30
34
|
case "zh-TW":
|
|
31
35
|
case "zh-HK":
|
|
32
36
|
case "zh-MO":
|
|
33
|
-
language = "
|
|
37
|
+
language = "zh-Hant";
|
|
34
38
|
break;
|
|
35
39
|
case "zh-CN":
|
|
36
40
|
case "zh-SG":
|
|
37
|
-
language = "
|
|
41
|
+
language = "zh-Hans";
|
|
38
42
|
break;
|
|
39
43
|
default:
|
|
40
44
|
if (language.length > 2) {
|
|
@@ -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";
|