@panoramax/web-viewer 3.2.3 → 4.0.0-develop-39167b4d
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 +53 -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 +257 -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,349 @@
|
|
|
1
|
+
import { LitElement, html } from "lit";
|
|
2
|
+
import API from "../../utils/API";
|
|
3
|
+
import { getTranslations } from "../../utils/i18n";
|
|
4
|
+
import { DEFAULT_TILES } from "../../utils/map";
|
|
5
|
+
import { createWebComp } from "../../utils/widgets";
|
|
6
|
+
import { isInIframe, isInternetFast } from "../../utils/utils";
|
|
7
|
+
import JSON5 from "json5";
|
|
8
|
+
import PACKAGE_JSON from "../../../package.json";
|
|
9
|
+
import "@fontsource/atkinson-hyperlegible-next";
|
|
10
|
+
import "./Basic.css";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Event for overlaying menu opening
|
|
14
|
+
* @event Panoramax.components.core.Basic#menu-opened
|
|
15
|
+
* @type {CustomEvent}
|
|
16
|
+
* @property {Element} detail.menu The opened menu
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Basic core component is a basic container for common functions through all core components.
|
|
21
|
+
* It is not intended to be used directly, it's only to be extended by other core components.
|
|
22
|
+
* @class Panoramax.components.core.Basic
|
|
23
|
+
* @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
|
|
24
|
+
* @fires Panoramax.components.core.Basic#select
|
|
25
|
+
* @fires Panoramax.components.core.Basic#ready
|
|
26
|
+
* @fires Panoramax.components.core.Basic#broken
|
|
27
|
+
* @fires Panoramax.components.core.Basic#menu-opened
|
|
28
|
+
* @property {Panoramax.components.ui.Loader} loader The loader screen
|
|
29
|
+
* @property {Panoramax.utils.API} api The API manager
|
|
30
|
+
*/
|
|
31
|
+
export default class Basic extends LitElement {
|
|
32
|
+
/**
|
|
33
|
+
* Component properties.
|
|
34
|
+
* @memberof Panoramax.components.core.Basic#
|
|
35
|
+
* @type {Object}
|
|
36
|
+
* @mixin
|
|
37
|
+
* @property {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
|
|
38
|
+
* @property {string} [picture] The picture ID to display
|
|
39
|
+
* @property {string} [sequence] The sequence ID of the picture displayed
|
|
40
|
+
* @property {object} [fetchOptions] Set custom options for fetch calls made against API ([same syntax as fetch options parameter](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters))
|
|
41
|
+
* @property {string[]} [users=[geovisio]] List of users IDs to use for map display (defaults to general map, identified as "geovisio")
|
|
42
|
+
* @property {string|object} [mapstyle] The map's MapLibre style. This can be an a JSON object conforming to the schema described in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL string pointing to one. Defaults to OSM vector tiles.
|
|
43
|
+
* @property {string} [lang] To override language used for labels. Defaults to using user's preferred languages.
|
|
44
|
+
*/
|
|
45
|
+
static properties = {
|
|
46
|
+
picture: {type: String, reflect: true},
|
|
47
|
+
sequence: {type: String, reflect: true},
|
|
48
|
+
fetchOptions: {converter: Basic.GetJSONConverter()},
|
|
49
|
+
users: {type: Array, reflect: true},
|
|
50
|
+
mapstyle: {type: String},
|
|
51
|
+
lang: {type: String},
|
|
52
|
+
endpoint: {type: String},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
constructor(testing = false) {
|
|
56
|
+
super();
|
|
57
|
+
|
|
58
|
+
// Some defaults
|
|
59
|
+
this.users = ["geovisio"];
|
|
60
|
+
this.mapstyle = this.getAttribute("mapstyle") || DEFAULT_TILES;
|
|
61
|
+
this.lang = this.getAttribute("lang") || null;
|
|
62
|
+
this.endpoint = this.getAttribute("endpoint") || null; // No default
|
|
63
|
+
this.picture = this.getAttribute("picture") || null;
|
|
64
|
+
this.sequence = this.getAttribute("sequence") || null;
|
|
65
|
+
|
|
66
|
+
// Display version in logs
|
|
67
|
+
console.info(`📷 Panoramax ${this.getClassName()} - Version ${PACKAGE_JSON.version} (${__COMMIT_HASH__})
|
|
68
|
+
|
|
69
|
+
🆘 Issues can be reported at ${PACKAGE_JSON.repository.url}`);
|
|
70
|
+
|
|
71
|
+
if(testing) { return; }
|
|
72
|
+
|
|
73
|
+
// Internet speed check
|
|
74
|
+
this._isInternetFast = null;
|
|
75
|
+
isInternetFast().then(isFast => this._isInternetFast = isFast);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
connectedCallback() {
|
|
79
|
+
super.connectedCallback();
|
|
80
|
+
|
|
81
|
+
// Translations
|
|
82
|
+
this._t = getTranslations(this.lang);
|
|
83
|
+
|
|
84
|
+
// Show loader
|
|
85
|
+
this.loader = createWebComp("pnx-loader", {_parent: this, "no-label": isInIframe() });
|
|
86
|
+
|
|
87
|
+
if(
|
|
88
|
+
!(this._loadsAPI && this.endpoint && this._loadsAPI === this.endpoint)
|
|
89
|
+
&& !(this.api && this.api._endpoint === this.endpoint)
|
|
90
|
+
&& this.endpoint
|
|
91
|
+
) {
|
|
92
|
+
if(this._loadsAPI || this.api) {
|
|
93
|
+
delete this.api;
|
|
94
|
+
delete this._loadsAPI;
|
|
95
|
+
}
|
|
96
|
+
this._setupAPI();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Creates API and wait for initial loading
|
|
102
|
+
* @private
|
|
103
|
+
*/
|
|
104
|
+
_setupAPI() {
|
|
105
|
+
// Loader init
|
|
106
|
+
this.loader = this.loader || createWebComp("pnx-loader", {_parent: this});
|
|
107
|
+
|
|
108
|
+
if(!this.endpoint) {
|
|
109
|
+
console.warn("No endpoint is defined");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this._loadsAPI = this.endpoint;
|
|
114
|
+
let myLoadAPI = this.endpoint;
|
|
115
|
+
|
|
116
|
+
// Check if mapstyle is not a unparsed JSON
|
|
117
|
+
try {
|
|
118
|
+
this.mapstyle = JSON.parse(this.mapstyle);
|
|
119
|
+
} catch(e) { /* empty */ }
|
|
120
|
+
|
|
121
|
+
// API init
|
|
122
|
+
try {
|
|
123
|
+
this.api = new API(this.endpoint, {
|
|
124
|
+
users: this.users,
|
|
125
|
+
fetch: this.fetchOptions,
|
|
126
|
+
style: this.mapstyle,
|
|
127
|
+
});
|
|
128
|
+
this.api.onceReady()
|
|
129
|
+
.then(() => {
|
|
130
|
+
if(myLoadAPI != this._loadsAPI || !this.api) { return; }
|
|
131
|
+
|
|
132
|
+
let unavailable = this.api.getUnavailableFeatures();
|
|
133
|
+
let available = this.api.getAvailableFeatures();
|
|
134
|
+
available = unavailable.length === 0 ? "✅ All features available" : "✅ Available features: "+available.join(", ");
|
|
135
|
+
unavailable = unavailable.length === 0 ? "" : "🚫 Unavailable features: "+unavailable.join(", ");
|
|
136
|
+
console.info(`🌐 Connected to API "${this.api._metadata.name}" (${this.api._endpoint})
|
|
137
|
+
ℹ️ API runs STAC ${this.api._metadata.stac_version} ${this.api._metadata.geovisio_version ? "& GeoVisio "+this.api._metadata.geovisio_version : ""}
|
|
138
|
+
${available}
|
|
139
|
+
${unavailable}
|
|
140
|
+
`.trim());
|
|
141
|
+
})
|
|
142
|
+
.catch(e => this.loader.dismiss(e, this._t.pnx.error_api))
|
|
143
|
+
.finally(() => delete this._loadsAPI);
|
|
144
|
+
}
|
|
145
|
+
catch(e) {
|
|
146
|
+
delete this._loadsAPI;
|
|
147
|
+
if(this.loader?.dismiss) {
|
|
148
|
+
this.loader.dismiss(e, this._t.pnx.error_api);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.error(e);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Waits for component to have its first loading done.
|
|
158
|
+
*
|
|
159
|
+
* Each inheriting class must override this method.
|
|
160
|
+
* @memberof Panoramax.components.core.Basic#
|
|
161
|
+
* @returns {Promise}
|
|
162
|
+
* @fulfil {null} When initialization is complete.
|
|
163
|
+
* @reject {string} Error message
|
|
164
|
+
*/
|
|
165
|
+
onceReady() {
|
|
166
|
+
throw new Error("You must override this method on sub-class");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Waits for initial API setup.
|
|
171
|
+
* @memberof Panoramax.components.core.Basic#
|
|
172
|
+
* @returns {Promise}
|
|
173
|
+
* @fulfil {null} When API is ready.
|
|
174
|
+
* @reject {string} Error message
|
|
175
|
+
*/
|
|
176
|
+
onceAPIReady() {
|
|
177
|
+
if(this.api) {
|
|
178
|
+
return this.api.onceReady();
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
return new Promise(resolve => setTimeout(resolve, 100)).then(this.onceAPIReady.bind(this));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** @private */
|
|
186
|
+
createRenderRoot() {
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** @private */
|
|
191
|
+
attributeChangedCallback(name, _old, value) {
|
|
192
|
+
super.attributeChangedCallback(name, _old, value);
|
|
193
|
+
|
|
194
|
+
if(name === "endpoint") {
|
|
195
|
+
if(
|
|
196
|
+
!(this._loadsAPI && value && this._loadsAPI === value)
|
|
197
|
+
&& !(this.api && this.api._endpoint === value)
|
|
198
|
+
&& value
|
|
199
|
+
) {
|
|
200
|
+
if(this._loadsAPI || this.api) {
|
|
201
|
+
delete this.api;
|
|
202
|
+
delete this._loadsAPI;
|
|
203
|
+
}
|
|
204
|
+
this._setupAPI();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if(["picture", "sequence"].includes(name)) {
|
|
208
|
+
let seqId, picId, prevSeqId, prevPicId;
|
|
209
|
+
|
|
210
|
+
if(name === "picture") {
|
|
211
|
+
seqId = this.sequence;
|
|
212
|
+
prevSeqId = this.sequence;
|
|
213
|
+
picId = value;
|
|
214
|
+
prevPicId = _old;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
seqId = value;
|
|
218
|
+
prevSeqId = _old;
|
|
219
|
+
picId = this.picture;
|
|
220
|
+
prevPicId = this.picture;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Event for sequence/picture selection
|
|
225
|
+
* @event Panoramax.components.core.Basic#select
|
|
226
|
+
* @type {CustomEvent}
|
|
227
|
+
* @property {string} detail.seqId The selected sequence ID
|
|
228
|
+
* @property {string} detail.picId The selected picture ID (or null if not a precise picture clicked)
|
|
229
|
+
* @property {string} [detail.prevSeqId] The previously selected sequence ID (or null if none)
|
|
230
|
+
* @property {string} [detail.prevPicId] The previously selected picture ID (or null if none)
|
|
231
|
+
*/
|
|
232
|
+
this.dispatchEvent(new CustomEvent("select", {
|
|
233
|
+
bubbles: true,
|
|
234
|
+
composed: true,
|
|
235
|
+
detail: {
|
|
236
|
+
seqId,
|
|
237
|
+
picId,
|
|
238
|
+
prevSeqId,
|
|
239
|
+
prevPicId,
|
|
240
|
+
}
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* This allows to retrieve an always correct class name.
|
|
247
|
+
* This is crap, but avoids issues with Webpack & so on.
|
|
248
|
+
*
|
|
249
|
+
* Each inheriting class must override this method.
|
|
250
|
+
* @returns {string} The class name (for example "Basic")
|
|
251
|
+
* @memberof Panoramax.components.core.Basic#
|
|
252
|
+
*/
|
|
253
|
+
getClassName() {
|
|
254
|
+
return "Basic";
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Change the currently picture and/or sequence.
|
|
259
|
+
* Calling the method without parameters unselects.
|
|
260
|
+
* @param {string} [seqId] The sequence UUID
|
|
261
|
+
* @param {string} [picId] The picture UUID
|
|
262
|
+
* @param {boolean} [force=false] Force select even if already selected
|
|
263
|
+
* @memberof Panoramax.components.core.Basic#
|
|
264
|
+
*/
|
|
265
|
+
select(seqId = null, picId = null, force = false) {
|
|
266
|
+
if(force) {
|
|
267
|
+
this.picture = null;
|
|
268
|
+
this.sequence = null;
|
|
269
|
+
}
|
|
270
|
+
this.picture = picId;
|
|
271
|
+
this.sequence = seqId;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Is the view running in a small container (small embed or smartphone)
|
|
276
|
+
* @returns {boolean} True if container is small
|
|
277
|
+
* @memberof Panoramax.components.core.Basic#
|
|
278
|
+
*/
|
|
279
|
+
isWidthSmall() {
|
|
280
|
+
return this?.offsetWidth < 576;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Is the view running in a small-height container (small embed or smartphone)
|
|
285
|
+
* @returns {boolean} True if container height is small
|
|
286
|
+
* @memberof Panoramax.components.core.Basic#
|
|
287
|
+
*/
|
|
288
|
+
isHeightSmall() {
|
|
289
|
+
return this?.offsetHeight < 400;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** @private */
|
|
293
|
+
render() {
|
|
294
|
+
return html`<p>Should not be used directly, use Viewer/CoverageMap/Editor instead</p>`;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* List names of sub-components (like loader, api, map, psv) available in this component.
|
|
299
|
+
* @returns {string[]} Sub-components names.
|
|
300
|
+
* @memberof Panoramax.components.core.Basic#
|
|
301
|
+
*/
|
|
302
|
+
getSubComponentsNames() {
|
|
303
|
+
return ["loader", "api"];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Listen to events from this components or one of its sub-components.
|
|
308
|
+
*
|
|
309
|
+
* For example, you can listen to `map` events using prefix `map:`.
|
|
310
|
+
*
|
|
311
|
+
* ```js
|
|
312
|
+
* me.addEventListener("map:move", doSomething);
|
|
313
|
+
* ```
|
|
314
|
+
* @param {string} type The event type to listen for
|
|
315
|
+
* @param {function} listener The event handler
|
|
316
|
+
* @param {object} [options] [Any original addEventListener available options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options)
|
|
317
|
+
* @memberof Panoramax.components.core.Basic#
|
|
318
|
+
*/
|
|
319
|
+
addEventListener(type, listener, options) {
|
|
320
|
+
// Check if listener is for sub-component
|
|
321
|
+
let prefix = type.split(":").shift();
|
|
322
|
+
if(prefix && this.getSubComponentsNames().includes(prefix)) {
|
|
323
|
+
const subType = type.substring(prefix.length+1);
|
|
324
|
+
|
|
325
|
+
// Add directly if available
|
|
326
|
+
if(this[prefix]?.addEventListener) {
|
|
327
|
+
this[prefix].addEventListener(subType, listener, options);
|
|
328
|
+
}
|
|
329
|
+
// Wait for addEventListener to be available
|
|
330
|
+
else {
|
|
331
|
+
setTimeout(() => this.addEventListener(type, listener, options), 50);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Otherwise, reuse classic function
|
|
335
|
+
else {
|
|
336
|
+
super.addEventListener(type, listener, options);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** @private */
|
|
341
|
+
static GetJSONConverter() {
|
|
342
|
+
return {
|
|
343
|
+
fromAttribute: (value) => {
|
|
344
|
+
return typeof value === "object" ? value : JSON5.parse(value);
|
|
345
|
+
},
|
|
346
|
+
toAttribute: (value) => JSON.stringify(value)
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import Basic from "./Basic";
|
|
2
|
+
import Map from "../ui/Map";
|
|
3
|
+
import { getUserLayerId } from "../../utils/map";
|
|
4
|
+
import { NavigationControl } from "!maplibre-gl"; // DO NOT REMOVE THE "!": bundled builds breaks otherwise !!!
|
|
5
|
+
import "./CoverageMap.css";
|
|
6
|
+
import { default as InitParameters, alterMapState } from "../../utils/InitParameters";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Coverage Map is a basic map showing Panoramax coverage.
|
|
11
|
+
*
|
|
12
|
+
* Make sure to set width/height through CSS for proper display.
|
|
13
|
+
* @class Panoramax.components.core.CoverageMap
|
|
14
|
+
* @element pnx-coverage-map
|
|
15
|
+
* @extends Panoramax.components.core.Basic
|
|
16
|
+
* @fires Panoramax.components.core.Basic#select
|
|
17
|
+
* @fires Panoramax.components.core.Basic#ready
|
|
18
|
+
* @fires Panoramax.components.core.Basic#broken
|
|
19
|
+
* @property {Panoramax.components.ui.Loader} loader The loader screen
|
|
20
|
+
* @property {Panoramax.utils.API} api The API manager
|
|
21
|
+
* @property {Panoramax.components.ui.Map} map The MapLibre GL map itself
|
|
22
|
+
* @example
|
|
23
|
+
* ```html
|
|
24
|
+
* <pnx-coverage-map
|
|
25
|
+
* endpoint="https://panoramax.openstreetmap.fr/"
|
|
26
|
+
* map='{"bounds": [[-73.9876, 40.7661], [-73.9397, 40.8002]]}'
|
|
27
|
+
* style="width: 300px; height: 250px"
|
|
28
|
+
* />
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export default class CoverageMap extends Basic {
|
|
32
|
+
/**
|
|
33
|
+
* Component properties.
|
|
34
|
+
* @memberof Panoramax.components.core.CoverageMap#
|
|
35
|
+
* @type {Object}
|
|
36
|
+
* @mixes Panoramax.components.core.Basic#properties
|
|
37
|
+
* @property {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
|
|
38
|
+
* @property {string} [picture] The picture ID to display
|
|
39
|
+
* @property {string} [sequence] The sequence ID of the picture displayed
|
|
40
|
+
* @property {object} [fetchOptions] Set custom options for fetch calls made against API ([same syntax as fetch options parameter](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters))
|
|
41
|
+
* @property {string[]} [users=[geovisio]] List of users IDs to use for map display (defaults to general map, identified as "geovisio")
|
|
42
|
+
* @property {string|object} [mapstyle] The map's MapLibre style. This can be an a JSON object conforming to the schema described in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL string pointing to one. Defaults to OSM vector tiles.
|
|
43
|
+
* @property {string} [lang] To override language used for labels. Defaults to using user's preferred languages.
|
|
44
|
+
* @property {object} [map] [Any map option available in Map class](#Panoramax.components.ui.Map).<br />Example: `map='{"bounds": [[-73.9876, 40.7661], [-73.9397, 40.8002]]}'`
|
|
45
|
+
*/
|
|
46
|
+
static properties = {
|
|
47
|
+
map: {converter: Basic.GetJSONConverter()},
|
|
48
|
+
...Basic.properties
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
constructor() {
|
|
52
|
+
super();
|
|
53
|
+
|
|
54
|
+
this._mapContainer = document.createElement("div");
|
|
55
|
+
this.onceAPIReady().then(() => {
|
|
56
|
+
this.loader.setAttribute("value", 30);
|
|
57
|
+
this._initParams = new InitParameters(InitParameters.GetComponentProperties(CoverageMap, this));
|
|
58
|
+
this._initMap();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getClassName() {
|
|
63
|
+
return "CoverageMap";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onceReady() {
|
|
67
|
+
if(this.map && this.map.waitForEnoughMapLoaded) {
|
|
68
|
+
return this.map.waitForEnoughMapLoaded();
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
return new Promise(resolve => setTimeout(resolve, 100)).then(this.onceReady.bind(this));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** @private */
|
|
76
|
+
render() {
|
|
77
|
+
return [this.loader, this._mapContainer];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getSubComponentsNames() {
|
|
81
|
+
const scn = super.getSubComponentsNames();
|
|
82
|
+
scn.push("map");
|
|
83
|
+
return scn;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Creates map object
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
_initMap() {
|
|
91
|
+
// Override to avoid display of pictures symbols
|
|
92
|
+
class MyMap extends Map {
|
|
93
|
+
_getLayerStyleProperties(layer) {
|
|
94
|
+
if(layer === "pictures_symbols") {
|
|
95
|
+
return { layout: { visibility: "none" } };
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return super._getLayerStyleProperties(layer);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.map = new MyMap(this, this._mapContainer, Object.assign({}, this._initParams.getMapInit(), { hash: true }));
|
|
104
|
+
this.map.addControl(new NavigationControl({ showCompass: false }));
|
|
105
|
+
this.loader.setAttribute("value", 70);
|
|
106
|
+
|
|
107
|
+
this.addEventListener("select", this._onSelect.bind(this));
|
|
108
|
+
this.map.on("picture-click", e => this.select(e.seqId, e.picId));
|
|
109
|
+
this.map.on("sequence-click", e => this.select(e.seqId));
|
|
110
|
+
|
|
111
|
+
this.map.waitForEnoughMapLoaded().then(() => {
|
|
112
|
+
alterMapState(this.map, this._initParams.getMapPostInit());
|
|
113
|
+
this.map.reloadLayersStyles();
|
|
114
|
+
this.loader.dismiss();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Select event handler
|
|
120
|
+
* @private
|
|
121
|
+
* @param {object} e Event details
|
|
122
|
+
*/
|
|
123
|
+
_onSelect(e) {
|
|
124
|
+
// Move thumbnail to match selected element
|
|
125
|
+
if(e.detail.picId || e.detail.seqId) {
|
|
126
|
+
const layer = e.detail.picId ? "pictures" : "sequences";
|
|
127
|
+
const features = this.map.queryRenderedFeatures({
|
|
128
|
+
layers: [...this.map._userLayers].map(l => getUserLayerId(l, layer)),
|
|
129
|
+
filter: ["==", ["get", "id"], e.detail.picId || e.detail.seqId]
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if(features.length >= 0 && features[0] != null) {
|
|
133
|
+
this.map._attachPreviewToPictures({ features }, layer);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
customElements.define("pnx-coverage-map", CoverageMap);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* General layout */
|
|
2
|
+
pnx-editor {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
pnx-editor .pnx-map,
|
|
8
|
+
pnx-editor .pnx-psv {
|
|
9
|
+
height: 50%;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Map background widget */
|
|
13
|
+
pnx-editor pnx-map-background {
|
|
14
|
+
position: absolute;
|
|
15
|
+
padding: 7px;
|
|
16
|
+
z-index: 120;
|
|
17
|
+
border-radius: 10px;
|
|
18
|
+
border: 1px solid var(--widget-border-div);
|
|
19
|
+
background-color: var(--widget-bg);
|
|
20
|
+
color: var(--widget-font);
|
|
21
|
+
bottom: 10px;
|
|
22
|
+
left: 10px;
|
|
23
|
+
}
|