@panoramax/web-viewer 3.2.3-develop-d7e5a16d → 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 +1 -1
- 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
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
# Advanced examples
|
|
2
|
-
|
|
3
|
-
On this page, you will discover many examples on how to do practical things, like changing the map tiles, or adding custom buttons.
|
|
4
|
-
|
|
5
|
-
## Change map background style
|
|
6
|
-
|
|
7
|
-
The viewer can be configured to use a different map background than the default one. By default, an OpenStreetMap France classic style if offered. Changing the style is done by passing a `style` parameter on viewer setup. It should follow the [MapLibre Style specification](https://maplibre.org/maplibre-style-spec) and be passed as an object, or an URL to such style:
|
|
8
|
-
|
|
9
|
-
```js
|
|
10
|
-
var viewer = new Panoramax.Viewer(
|
|
11
|
-
"viewer",
|
|
12
|
-
"https://my-panoramax-server.net/api",
|
|
13
|
-
{
|
|
14
|
-
style: "https://my.tiles.provider/basic.json",
|
|
15
|
-
map: { startWide: true }
|
|
16
|
-
}
|
|
17
|
-
);
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Note that the viewer also support PMTiles (for a simpler tile hosting), so your style file can contain vector source defined like this:
|
|
21
|
-
|
|
22
|
-
```json
|
|
23
|
-
{
|
|
24
|
-
"sources": {
|
|
25
|
-
"protomaps": {
|
|
26
|
-
"type": "vector",
|
|
27
|
-
"url": "pmtiles://https://example.com/example.pmtiles",
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
If you need to customize the received JSON style for compatibility issues, this can be done by passing an object instead of a string. Here is an example based on IGN map styling, which needs some parameter to be changed:
|
|
34
|
-
|
|
35
|
-
```js
|
|
36
|
-
fetch("https://wxs.ign.fr/essentiels/static/vectorTiles/styles/PLAN.IGN/standard.json")
|
|
37
|
-
.then(res => res.json())
|
|
38
|
-
.then(style => {
|
|
39
|
-
// Patch tms scheme to xyz to make it compatible for Maplibre GL JS
|
|
40
|
-
style.sources.plan_ign.scheme = 'xyz';
|
|
41
|
-
style.sources.plan_ign.attribution = 'Données cartographiques : © IGN';
|
|
42
|
-
|
|
43
|
-
var viewer = new Panoramax.Viewer(
|
|
44
|
-
"viewer",
|
|
45
|
-
"https://my-panoramax-server.net/api",
|
|
46
|
-
{
|
|
47
|
-
style,
|
|
48
|
-
map: { startWide: true }
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Adding aerial imagery
|
|
55
|
-
|
|
56
|
-
In complement of classic _streets_ rendering, you can add an aerial imagery as map background. This is possible using a WMS or WMTS service, and setting configuration as following (this example uses the French IGN aerial imagery):
|
|
57
|
-
|
|
58
|
-
```js
|
|
59
|
-
var viewer = new Panoramax.Viewer(
|
|
60
|
-
"viewer",
|
|
61
|
-
"https://my-panoramax-server.net/api",
|
|
62
|
-
{
|
|
63
|
-
map: {
|
|
64
|
-
startWide: true,
|
|
65
|
-
raster: {
|
|
66
|
-
type: "raster",
|
|
67
|
-
tiles: [
|
|
68
|
-
"https://data.geopf.fr/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTHOIMAGERY.ORTHOPHOTOS&STYLE=normal&FORMAT=image/jpeg&TILEMATRIXSET=PM_0_21&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}"
|
|
69
|
-
],
|
|
70
|
-
minzoom: 0,
|
|
71
|
-
maxzoom: 21,
|
|
72
|
-
attribution: "© IGN",
|
|
73
|
-
tileSize: 256
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
);
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Use another geocoder
|
|
81
|
-
|
|
82
|
-
The map offers a search bar for easily locating places based on user text search. This is handled by [MapLibre GL Geocoder](https://github.com/maplibre/maplibre-gl-geocoder). By default, the viewer uses [Nominatim](https://nominatim.org/) API, which provides geocoding using OpenStreetMap data.
|
|
83
|
-
|
|
84
|
-
You can switch to using another geocoder though, we also directly offer the [Base adresse nationale](https://adresse.data.gouv.fr/) API (French authority geocoder) that you can use like this:
|
|
85
|
-
|
|
86
|
-
```js
|
|
87
|
-
var viewer = new Panoramax.Viewer(
|
|
88
|
-
"viewer",
|
|
89
|
-
"https://my-panoramax-server.net/api",
|
|
90
|
-
{
|
|
91
|
-
map: {
|
|
92
|
-
geocoder: { engine: "ban" }
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
);
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
And you can also define your own custom geocoder using these options:
|
|
99
|
-
|
|
100
|
-
```js
|
|
101
|
-
var myOwnGeocoder = function(config) {
|
|
102
|
-
// Call your API
|
|
103
|
-
// Config parameter is based on geocoderApi.forwardGeocode.config structure
|
|
104
|
-
// Described here : https://github.com/maplibre/maplibre-gl-geocoder/blob/main/API.md#setgeocoderapi
|
|
105
|
-
|
|
106
|
-
// It returns a promise resolving on a Carmen GeoJSON FeatureCollection
|
|
107
|
-
// Format is described here : https://docs.mapbox.com/api/search/geocoding/#geocoding-response-object
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
var viewer = new Panoramax.Viewer(
|
|
111
|
-
"viewer",
|
|
112
|
-
"https://my-panoramax-server.net/api",
|
|
113
|
-
{
|
|
114
|
-
map: {
|
|
115
|
-
geocoder: { geocoderApi: {
|
|
116
|
-
forwardGeocode: myOwnGeocoder
|
|
117
|
-
} }
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
);
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Authentication against API
|
|
124
|
-
|
|
125
|
-
If the STAC API you're using needs some kind of authentication, you can pass it through Web Viewer options. Parameter `fetchOptions` allows you to set custom parameters for the [JS fetch function](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters), like the `credentials` setting. For example:
|
|
126
|
-
|
|
127
|
-
```js
|
|
128
|
-
var viewer = new Panoramax.Viewer(
|
|
129
|
-
"viewer",
|
|
130
|
-
"https://your-secured-stac.fr/api",
|
|
131
|
-
{
|
|
132
|
-
fetchOptions: {
|
|
133
|
-
credentials: "include"
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
);
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## Add custom buttons
|
|
140
|
-
|
|
141
|
-
The viewer allows you to add a custom widget, placed just over the _Share_ button (bottom-right corner). It can be defined through `widgets.customWidget` option. Here's an example to add a simple link:
|
|
142
|
-
|
|
143
|
-
```js
|
|
144
|
-
var viewer = new Panoramax.Viewer(
|
|
145
|
-
"viewer",
|
|
146
|
-
"https://my-panoramax-server.net/api",
|
|
147
|
-
{
|
|
148
|
-
widgets: {
|
|
149
|
-
customWidget: `<a
|
|
150
|
-
href="https://my-amazing-page.net/"
|
|
151
|
-
class="gvs-btn gvs-widget-bg gvs-btn-large"
|
|
152
|
-
title="Go to an amazing page">🤩</a>`
|
|
153
|
-
},
|
|
154
|
-
}
|
|
155
|
-
);
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
You can also pass more complex DOM elements:
|
|
159
|
-
|
|
160
|
-
```js
|
|
161
|
-
var myWidget = document.createElement("div");
|
|
162
|
-
myWidget.innerHTML = "...";
|
|
163
|
-
|
|
164
|
-
var viewer = new Panoramax.Viewer(
|
|
165
|
-
"viewer",
|
|
166
|
-
"https://my-panoramax-server.net/api",
|
|
167
|
-
{
|
|
168
|
-
widgets: { customWidget: myWidget }
|
|
169
|
-
}
|
|
170
|
-
);
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
## Coverage map synced with external component
|
|
175
|
-
|
|
176
|
-
Let's say you want to list all sequences of an user. You can display a standalone map which can be synced with your custom list.
|
|
177
|
-
|
|
178
|
-
```js
|
|
179
|
-
var map = new Panoramax.StandaloneMap(
|
|
180
|
-
"map",
|
|
181
|
-
"https://panoramax.ign.fr/api",
|
|
182
|
-
{
|
|
183
|
-
// Optional, to allow filtering by user
|
|
184
|
-
users: ["79b851b4-232a-4c96-ac1b-b6cf693c77ae"]
|
|
185
|
-
}
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
// Change visible map area
|
|
189
|
-
map.fitBounds([west, south, east, north]);
|
|
190
|
-
|
|
191
|
-
// Listen to user clicks on map
|
|
192
|
-
map.addEventListener("select", e => {
|
|
193
|
-
console.log("Selected sequence", e.detail.seqId, "picture", e.detail.picId);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Listen to sequence hovered on map
|
|
197
|
-
map.addEventListener("hover", e => {
|
|
198
|
-
console.log("Hovered sequence", e.detail.seqId);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// You can also programatically change selection on map
|
|
202
|
-
map.select(
|
|
203
|
-
"c463d190-06b0-47fb-98a8-b4a775a39ad6", // A sequence ID
|
|
204
|
-
"bdea1eb4-4496-46da-a4d5-b22b16e75fa8" // A picture ID (can be null if unknown)
|
|
205
|
-
);
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
## Clean-up in Single Page Application
|
|
210
|
-
|
|
211
|
-
If you're running the viewer in a Single Page Application (SPA) and want to get rid of it, you must destroy properly the component before changing view. This allows the viewer to properly remove all its event listeners and free memory.
|
|
212
|
-
|
|
213
|
-
```js
|
|
214
|
-
viewer.destroy();
|
|
215
|
-
delete viewer;
|
|
216
|
-
```
|
package/src/Editor.css
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/* General layout */
|
|
2
|
-
.gvs-editor {
|
|
3
|
-
display: flex;
|
|
4
|
-
flex-direction: column;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.gvs-editor .gvs-map,
|
|
8
|
-
.gvs-editor .gvs-psv {
|
|
9
|
-
height: 50%;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/* Map background widget */
|
|
13
|
-
.gvs-editor #gvs-map-bg {
|
|
14
|
-
display: flex;
|
|
15
|
-
gap: 20px;
|
|
16
|
-
justify-content: space-evenly;
|
|
17
|
-
margin: 10px;
|
|
18
|
-
padding: 7px;
|
|
19
|
-
width: unset;
|
|
20
|
-
border-radius: 10px;
|
|
21
|
-
position: absolute;
|
|
22
|
-
bottom: 0;
|
|
23
|
-
}
|
|
24
|
-
.gvs-editor #gvs-map-bg input { display: none; }
|
|
25
|
-
.gvs-editor #gvs-map-bg label { cursor: pointer; }
|
|
26
|
-
|
|
27
|
-
.gvs-editor #gvs-map-bg img {
|
|
28
|
-
width: 32px;
|
|
29
|
-
border-radius: 5px;
|
|
30
|
-
vertical-align: middle;
|
|
31
|
-
margin-right: 5px;
|
|
32
|
-
border: 2px solid var(--widget-bg);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.gvs-editor #gvs-map-bg input:checked + label img {
|
|
36
|
-
outline: 3px solid var(--widget-border-btn);
|
|
37
|
-
}
|
package/src/Editor.js
DELETED
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
import "./Editor.css";
|
|
2
|
-
import CoreView from "./components/CoreView";
|
|
3
|
-
import Map from "./components/Map";
|
|
4
|
-
import Photo from "./components/Photo";
|
|
5
|
-
import BackgroundAerial from "./img/bg_aerial.jpg";
|
|
6
|
-
import BackgroundStreets from "./img/bg_streets.jpg";
|
|
7
|
-
import { linkMapAndPhoto, apiFeatureToPSVNode } from "./utils/Utils";
|
|
8
|
-
import { VECTOR_STYLES } from "./utils/Map";
|
|
9
|
-
import { SYSTEM as PSSystem } from "@photo-sphere-viewer/core";
|
|
10
|
-
|
|
11
|
-
const LAYER_HEADING_ID = "sequence-headings";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Editor allows to focus on a single sequence, and preview what you edits would look like.
|
|
15
|
-
* It shows both picture and map.
|
|
16
|
-
*
|
|
17
|
-
* Note that you can use any of the [CoreView](#CoreView) class functions as well.
|
|
18
|
-
*
|
|
19
|
-
* @param {string|Element} container The DOM element to create viewer into
|
|
20
|
-
* @param {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
|
|
21
|
-
* @param {object} [options] View options.
|
|
22
|
-
* @param {string} options.selectedSequence The ID of sequence to highlight on load. Must be always defined.
|
|
23
|
-
* @param {string} [options.selectedPicture] The ID of picture to highlight on load (defaults to none)
|
|
24
|
-
* @param {object} [options.fetchOptions=null] 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))
|
|
25
|
-
* @param {object} [options.raster] The MapLibre raster source for aerial background. This must be a JSON object following [MapLibre raster source definition](https://maplibre.org/maplibre-style-spec/sources/#raster).
|
|
26
|
-
* @param {string} [options.background] Choose default map background to display (streets or aerial, if raster aerial background available). Defaults to street.
|
|
27
|
-
* @param {string|object} [options.style] 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-gl-js-docs/style-spec/), or a URL string pointing to one.
|
|
28
|
-
*
|
|
29
|
-
* @property {Map} map The map widget
|
|
30
|
-
* @property {Photo} psv The photo widget
|
|
31
|
-
*/
|
|
32
|
-
export default class Editor extends CoreView {
|
|
33
|
-
constructor(container, endpoint, options = {}){
|
|
34
|
-
super(container, endpoint, Object.assign(options, { users: [] }));
|
|
35
|
-
|
|
36
|
-
// Check sequence ID is set
|
|
37
|
-
if(!this._selectedSeqId) { this._loader.dismiss({}, "No sequence is selected"); }
|
|
38
|
-
|
|
39
|
-
// Create sub-containers
|
|
40
|
-
this.psvContainer = document.createElement("div");
|
|
41
|
-
this.mapContainer = document.createElement("div");
|
|
42
|
-
this.container.appendChild(this.psvContainer);
|
|
43
|
-
this.container.appendChild(this.mapContainer);
|
|
44
|
-
|
|
45
|
-
// Init PSV
|
|
46
|
-
try {
|
|
47
|
-
this.psv = new Photo(this, this.psvContainer);
|
|
48
|
-
this.psv._myVTour.datasource.nodeResolver = this._getNode.bind(this);
|
|
49
|
-
}
|
|
50
|
-
catch(e) {
|
|
51
|
-
let err = !PSSystem.isWebGLSupported ? this._t.gvs.error_webgl : this._t.gvs.error_psv;
|
|
52
|
-
this._loader.dismiss(e, err);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Init map
|
|
56
|
-
this._api.onceReady().then(() => {
|
|
57
|
-
try {
|
|
58
|
-
this.map = new Map(this, this.mapContainer, {
|
|
59
|
-
raster: options.raster,
|
|
60
|
-
background: options.background,
|
|
61
|
-
supplementaryStyle: this._createMapStyle(),
|
|
62
|
-
zoom: 15, // Hack to avoid _initMapPosition call
|
|
63
|
-
});
|
|
64
|
-
linkMapAndPhoto(this);
|
|
65
|
-
this._loadSequence();
|
|
66
|
-
this.map.once("load", () => {
|
|
67
|
-
if(options.raster) { this._addMapBackgroundWidget(); }
|
|
68
|
-
this._bindPicturesEvents();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Override picMarker setRotation for heading preview
|
|
72
|
-
const oldRot = this.map._picMarker.setRotation.bind(this.map._picMarker);
|
|
73
|
-
this.map._picMarker.setRotation = h => {
|
|
74
|
-
h = this._lastRelHeading === undefined ? h : h + this._lastRelHeading - this.psv.getPictureRelativeHeading();
|
|
75
|
-
return oldRot(h);
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
catch(e) {
|
|
79
|
-
this._loader.dismiss(e, this._t.gvs.error_psv);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// Events
|
|
84
|
-
this.addEventListener("select", this._onSelect.bind(this));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
getClassName() {
|
|
88
|
-
return "Editor";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Create style for GeoJSON sequence data.
|
|
93
|
-
* @private
|
|
94
|
-
*/
|
|
95
|
-
_createMapStyle() {
|
|
96
|
-
return {
|
|
97
|
-
sources: {
|
|
98
|
-
geovisio_editor_sequences: {
|
|
99
|
-
type: "geojson",
|
|
100
|
-
data: {"type": "FeatureCollection", "features": [] }
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
layers: [
|
|
104
|
-
{
|
|
105
|
-
"id": "geovisio_editor_sequences",
|
|
106
|
-
"type": "line",
|
|
107
|
-
"source": "geovisio_editor_sequences",
|
|
108
|
-
"layout": {
|
|
109
|
-
...VECTOR_STYLES.SEQUENCES.layout
|
|
110
|
-
},
|
|
111
|
-
"paint": {
|
|
112
|
-
...VECTOR_STYLES.SEQUENCES.paint
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
"id": "geovisio_editor_pictures",
|
|
117
|
-
"type": "circle",
|
|
118
|
-
"source": "geovisio_editor_sequences",
|
|
119
|
-
"layout": {
|
|
120
|
-
...VECTOR_STYLES.PICTURES.layout
|
|
121
|
-
},
|
|
122
|
-
"paint": {
|
|
123
|
-
...VECTOR_STYLES.PICTURES.paint
|
|
124
|
-
},
|
|
125
|
-
}
|
|
126
|
-
]
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Creates events handlers on pictures layer
|
|
132
|
-
* @private
|
|
133
|
-
*/
|
|
134
|
-
_bindPicturesEvents() {
|
|
135
|
-
// Pictures events
|
|
136
|
-
this.map.on("mousemove", "geovisio_editor_pictures", () => {
|
|
137
|
-
this.map.getCanvas().style.cursor = "pointer";
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
this.map.on("mouseleave", "geovisio_editor_pictures", () => {
|
|
141
|
-
this.map.getCanvas().style.cursor = "";
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
this.map.on("click", "geovisio_editor_pictures", this.map._onPictureClick.bind(this.map));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Displays currently selected sequence on map
|
|
149
|
-
* @private
|
|
150
|
-
*/
|
|
151
|
-
_loadSequence() {
|
|
152
|
-
return this._api.getSequenceItems(this._selectedSeqId).then(seq => {
|
|
153
|
-
// Hide loader after source load
|
|
154
|
-
this.map.once("sourcedata", () => {
|
|
155
|
-
this.map.setPaintProperty("geovisio_editor_sequences", "line-color", this.map._getLayerColorStyle("sequences"));
|
|
156
|
-
this.map.setPaintProperty("geovisio_editor_pictures", "circle-color", this.map._getLayerColorStyle("pictures"));
|
|
157
|
-
this.map.setLayoutProperty("geovisio_editor_sequences", "visibility", "visible");
|
|
158
|
-
this.map.setLayoutProperty("geovisio_editor_pictures", "visibility", "visible");
|
|
159
|
-
this.map.once("styledata", () => this._loader.dismiss());
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Create data source
|
|
163
|
-
this._sequenceData = seq.features;
|
|
164
|
-
this.map.getSource("geovisio_editor_sequences").setData({
|
|
165
|
-
"type": "FeatureCollection",
|
|
166
|
-
"features": [
|
|
167
|
-
{
|
|
168
|
-
"type": "Feature",
|
|
169
|
-
"properties": {
|
|
170
|
-
"id": this._selectedSeqId,
|
|
171
|
-
},
|
|
172
|
-
"geometry":
|
|
173
|
-
{
|
|
174
|
-
"type": "LineString",
|
|
175
|
-
"coordinates": seq.features.map(p => p.geometry.coordinates)
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
...seq.features.map(f => {
|
|
179
|
-
f.properties.id = f.id;
|
|
180
|
-
f.properties.sequences = [this._selectedSeqId];
|
|
181
|
-
return f;
|
|
182
|
-
})
|
|
183
|
-
]
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// Select picture if any
|
|
187
|
-
if(this._selectedPicId) {
|
|
188
|
-
const pic = seq.features.find(p => p.id === this._selectedPicId);
|
|
189
|
-
if(pic) {
|
|
190
|
-
this.select(this._selectedSeqId, this._selectedPicId, true);
|
|
191
|
-
this.map.jumpTo({ center: pic.geometry.coordinates, zoom: 18 });
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
console.log("Picture with ID", pic, "was not found");
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
// Show area of sequence otherwise
|
|
198
|
-
else {
|
|
199
|
-
const bbox = [
|
|
200
|
-
...seq.features[0].geometry.coordinates,
|
|
201
|
-
...seq.features[0].geometry.coordinates
|
|
202
|
-
];
|
|
203
|
-
|
|
204
|
-
for(let i=1; i < seq.features.length; i++) {
|
|
205
|
-
const c = seq.features[i].geometry.coordinates;
|
|
206
|
-
if(c[0] < bbox[0]) { bbox[0] = c[0]; }
|
|
207
|
-
if(c[1] < bbox[1]) { bbox[1] = c[1]; }
|
|
208
|
-
if(c[0] > bbox[2]) { bbox[2] = c[0]; }
|
|
209
|
-
if(c[1] > bbox[3]) { bbox[3] = c[1]; }
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
this.map.fitBounds(bbox, {animate: false});
|
|
213
|
-
}
|
|
214
|
-
}).catch(e => this._loader.dismiss(e, this._t.gvs.error_api));
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Get the PSV node for wanted picture.
|
|
219
|
-
*
|
|
220
|
-
* @param {string} picId The picture ID
|
|
221
|
-
* @returns The PSV node
|
|
222
|
-
* @private
|
|
223
|
-
*/
|
|
224
|
-
_getNode(picId) {
|
|
225
|
-
const f = this._sequenceData.find(f => f.properties.id === picId);
|
|
226
|
-
const n = f ? apiFeatureToPSVNode(f, this._t, this._isInternetFast) : null;
|
|
227
|
-
if(n) { delete n.links; }
|
|
228
|
-
return n;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Creates the widget to switch between aerial and streets imagery
|
|
233
|
-
* @private
|
|
234
|
-
*/
|
|
235
|
-
_addMapBackgroundWidget() {
|
|
236
|
-
// Container
|
|
237
|
-
const pnlLayers = document.createElement("div");
|
|
238
|
-
pnlLayers.id = "gvs-map-bg";
|
|
239
|
-
pnlLayers.classList.add("gvs-panel", "gvs-widget-bg", "gvs-input-group");
|
|
240
|
-
const onBgChange = e => this.map.setBackground(e.target.value);
|
|
241
|
-
|
|
242
|
-
// Radio streets
|
|
243
|
-
const radioBgStreets = document.createElement("input");
|
|
244
|
-
radioBgStreets.id = "gvs-map-bg-streets";
|
|
245
|
-
radioBgStreets.setAttribute("type", "radio");
|
|
246
|
-
radioBgStreets.setAttribute("name", "gvs-map-bg");
|
|
247
|
-
radioBgStreets.setAttribute("value", "streets");
|
|
248
|
-
radioBgStreets.addEventListener("change", onBgChange);
|
|
249
|
-
pnlLayers.appendChild(radioBgStreets);
|
|
250
|
-
|
|
251
|
-
const labelBgStreets = document.createElement("label");
|
|
252
|
-
labelBgStreets.setAttribute("for", radioBgStreets.id);
|
|
253
|
-
|
|
254
|
-
const imgBgStreets = document.createElement("img");
|
|
255
|
-
imgBgStreets.src = BackgroundStreets;
|
|
256
|
-
imgBgStreets.alt = "";
|
|
257
|
-
|
|
258
|
-
labelBgStreets.appendChild(imgBgStreets);
|
|
259
|
-
labelBgStreets.appendChild(document.createTextNode(this._t.gvs.map_background_streets));
|
|
260
|
-
pnlLayers.appendChild(labelBgStreets);
|
|
261
|
-
|
|
262
|
-
// Radio aerial
|
|
263
|
-
const radioBgAerial = document.createElement("input");
|
|
264
|
-
radioBgAerial.id = "gvs-map-bg-aerial";
|
|
265
|
-
radioBgAerial.setAttribute("type", "radio");
|
|
266
|
-
radioBgAerial.setAttribute("name", "gvs-map-bg");
|
|
267
|
-
radioBgAerial.setAttribute("value", "aerial");
|
|
268
|
-
radioBgAerial.addEventListener("change", onBgChange);
|
|
269
|
-
pnlLayers.appendChild(radioBgAerial);
|
|
270
|
-
|
|
271
|
-
const labelBgAerial = document.createElement("label");
|
|
272
|
-
labelBgAerial.setAttribute("for", radioBgAerial.id);
|
|
273
|
-
|
|
274
|
-
const imgBgAerial = document.createElement("img");
|
|
275
|
-
imgBgAerial.src = BackgroundAerial;
|
|
276
|
-
imgBgAerial.alt = "";
|
|
277
|
-
|
|
278
|
-
labelBgAerial.appendChild(imgBgAerial);
|
|
279
|
-
labelBgAerial.appendChild(document.createTextNode(this._t.gvs.map_background_aerial));
|
|
280
|
-
pnlLayers.appendChild(labelBgAerial);
|
|
281
|
-
|
|
282
|
-
this.mapContainer.appendChild(pnlLayers);
|
|
283
|
-
|
|
284
|
-
const onMapBgChange = bg => {
|
|
285
|
-
if(bg === "aerial") { radioBgAerial.checked = true; }
|
|
286
|
-
else { radioBgStreets.checked = true; }
|
|
287
|
-
};
|
|
288
|
-
this.addEventListener("map:background-changed", e => onMapBgChange(e.detail.background));
|
|
289
|
-
onMapBgChange(this.map.getBackground());
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Preview on map how the new relative heading would reflect on all pictures.
|
|
294
|
-
* This doesn't change anything on API-side, it's just a preview.
|
|
295
|
-
*
|
|
296
|
-
* @param {number} [relHeading] The new relative heading compared to sequence path. In degrees, between -180 and 180 (0 = front, -90 = left, 90 = right). Set to null to remove preview.
|
|
297
|
-
*/
|
|
298
|
-
previewSequenceHeadingChange(relHeading) {
|
|
299
|
-
const layerExists = this.map.getLayer(LAYER_HEADING_ID) !== undefined;
|
|
300
|
-
this.map._picMarkerPreview.remove();
|
|
301
|
-
|
|
302
|
-
// If no value set, remove layer
|
|
303
|
-
if(relHeading === undefined) {
|
|
304
|
-
delete this._lastRelHeading;
|
|
305
|
-
if(layerExists) {
|
|
306
|
-
this.map.setLayoutProperty(LAYER_HEADING_ID, "visibility", "none");
|
|
307
|
-
}
|
|
308
|
-
// Update selected picture marker
|
|
309
|
-
if(this._selectedPicId) {
|
|
310
|
-
this.map._picMarker.setRotation(this.psv.getXY().x);
|
|
311
|
-
}
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
this._lastRelHeading = relHeading;
|
|
316
|
-
|
|
317
|
-
// Create preview layer
|
|
318
|
-
if(!layerExists) {
|
|
319
|
-
this.map.addLayer({
|
|
320
|
-
"id": LAYER_HEADING_ID,
|
|
321
|
-
"type": "symbol",
|
|
322
|
-
"source": "geovisio_editor_sequences",
|
|
323
|
-
"layout": {
|
|
324
|
-
"icon-image": "gvs-marker",
|
|
325
|
-
"icon-overlap": "always",
|
|
326
|
-
"icon-size": 0.8,
|
|
327
|
-
},
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Change heading
|
|
332
|
-
const currentRelHeading = - this.psv.getPictureRelativeHeading();
|
|
333
|
-
this.map.setLayoutProperty(LAYER_HEADING_ID, "visibility", "visible");
|
|
334
|
-
this.map.setLayoutProperty(
|
|
335
|
-
LAYER_HEADING_ID,
|
|
336
|
-
"icon-rotate",
|
|
337
|
-
["+", ["get", "view:azimuth"], currentRelHeading, relHeading ]
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
// Skip selected picture and linestring geom
|
|
341
|
-
const filters = [["==", ["geometry-type"], "Point"]];
|
|
342
|
-
if(this._selectedPicId) { filters.push(["!=", ["get", "id"], this._selectedPicId]); }
|
|
343
|
-
this.map.setFilter(LAYER_HEADING_ID, ["all", ...filters]);
|
|
344
|
-
|
|
345
|
-
// Update selected picture marker
|
|
346
|
-
if(this._selectedPicId) {
|
|
347
|
-
this.map._picMarker.setRotation(this.psv.getXY().x);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Event handler for picture loading
|
|
353
|
-
* @private
|
|
354
|
-
*/
|
|
355
|
-
_onSelect() {
|
|
356
|
-
// Update preview of heading change
|
|
357
|
-
if(this._lastRelHeading !== undefined) {
|
|
358
|
-
this.previewSequenceHeadingChange(this._lastRelHeading);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|