@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.
Files changed (255) hide show
  1. package/.gitlab-ci.yml +13 -6
  2. package/CHANGELOG.md +49 -1
  3. package/CODE_OF_CONDUCT.md +1 -1
  4. package/README.md +1 -1
  5. package/build/editor.html +10 -1
  6. package/build/index.css +12 -12
  7. package/build/index.css.map +1 -1
  8. package/build/index.html +1 -1
  9. package/build/index.js +2126 -14
  10. package/build/index.js.map +1 -1
  11. package/build/map.html +1 -1
  12. package/build/photo.html +1 -0
  13. package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff +0 -0
  14. package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff2 +0 -0
  15. package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff +0 -0
  16. package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff2 +0 -0
  17. package/build/viewer.html +12 -1
  18. package/build/widgets.html +1 -0
  19. package/config/jest/mocks.js +201 -0
  20. package/config/paths.js +2 -0
  21. package/config/webpack.config.js +52 -0
  22. package/docs/03_URL_settings.md +14 -16
  23. package/docs/05_Compatibility.md +59 -76
  24. package/docs/09_Develop.md +46 -11
  25. package/docs/90_Releases.md +2 -2
  26. package/docs/images/class_diagram.drawio +60 -45
  27. package/docs/images/class_diagram.jpg +0 -0
  28. package/docs/images/screenshot.jpg +0 -0
  29. package/docs/index.md +135 -0
  30. package/docs/reference/components/core/Basic.md +196 -0
  31. package/docs/reference/components/core/CoverageMap.md +210 -0
  32. package/docs/reference/components/core/Editor.md +224 -0
  33. package/docs/reference/components/core/PhotoViewer.md +307 -0
  34. package/docs/reference/components/core/Viewer.md +350 -0
  35. package/docs/reference/components/layout/BottomDrawer.md +35 -0
  36. package/docs/reference/components/layout/CorneredGrid.md +29 -0
  37. package/docs/reference/components/layout/Mini.md +45 -0
  38. package/docs/reference/components/layout/Tabs.md +45 -0
  39. package/docs/reference/components/menus/MapBackground.md +32 -0
  40. package/docs/reference/components/menus/MapFilters.md +15 -0
  41. package/docs/reference/components/menus/MapLayers.md +15 -0
  42. package/docs/reference/components/menus/MapLegend.md +15 -0
  43. package/docs/reference/components/menus/PictureLegend.md +16 -0
  44. package/docs/reference/components/menus/PictureMetadata.md +15 -0
  45. package/docs/reference/components/menus/PlayerOptions.md +15 -0
  46. package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
  47. package/docs/reference/components/menus/ReportForm.md +15 -0
  48. package/docs/reference/components/menus/ShareMenu.md +15 -0
  49. package/docs/reference/components/ui/Button.md +40 -0
  50. package/docs/reference/components/ui/ButtonGroup.md +36 -0
  51. package/docs/reference/components/ui/CopyButton.md +38 -0
  52. package/docs/reference/components/ui/Grade.md +32 -0
  53. package/docs/reference/components/ui/LinkButton.md +45 -0
  54. package/docs/reference/components/ui/ListGroup.md +22 -0
  55. package/docs/reference/components/ui/Loader.md +56 -0
  56. package/docs/reference/components/ui/Map.md +239 -0
  57. package/docs/reference/components/ui/MapMore.md +256 -0
  58. package/docs/reference/components/ui/Photo.md +385 -0
  59. package/docs/reference/components/ui/Popup.md +56 -0
  60. package/docs/reference/components/ui/ProgressBar.md +32 -0
  61. package/docs/reference/components/ui/QualityScore.md +45 -0
  62. package/docs/reference/components/ui/SearchBar.md +63 -0
  63. package/docs/reference/components/ui/TogglableGroup.md +39 -0
  64. package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
  65. package/docs/reference/components/ui/widgets/Legend.md +49 -0
  66. package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
  67. package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
  68. package/docs/reference/components/ui/widgets/OSMEditors.md +15 -0
  69. package/docs/reference/components/ui/widgets/PictureLegendActions.md +32 -0
  70. package/docs/reference/components/ui/widgets/Player.md +33 -0
  71. package/docs/reference/components/ui/widgets/Zoom.md +15 -0
  72. package/docs/reference/utils/API.md +334 -0
  73. package/docs/reference/utils/InitParameters.md +68 -0
  74. package/docs/reference/utils/URLHandler.md +107 -0
  75. package/docs/reference.md +79 -0
  76. package/docs/shortcuts.md +11 -0
  77. package/docs/tutorials/aerial_imagery.md +19 -0
  78. package/docs/tutorials/authentication.md +10 -0
  79. package/docs/tutorials/custom_widgets.md +59 -0
  80. package/docs/tutorials/map_style.md +39 -0
  81. package/docs/tutorials/migrate_v4.md +153 -0
  82. package/docs/tutorials/synced_coverage.md +43 -0
  83. package/mkdocs.yml +66 -5
  84. package/package.json +22 -17
  85. package/public/editor.html +21 -29
  86. package/public/index.html +17 -12
  87. package/public/map.html +19 -18
  88. package/public/photo.html +55 -0
  89. package/public/viewer.html +22 -26
  90. package/public/widgets.html +306 -0
  91. package/scripts/doc.js +79 -0
  92. package/src/components/core/Basic.css +48 -0
  93. package/src/components/core/Basic.js +349 -0
  94. package/src/components/core/CoverageMap.css +9 -0
  95. package/src/components/core/CoverageMap.js +139 -0
  96. package/src/components/core/Editor.css +23 -0
  97. package/src/components/core/Editor.js +390 -0
  98. package/src/components/core/PhotoViewer.css +48 -0
  99. package/src/components/core/PhotoViewer.js +499 -0
  100. package/src/components/core/Viewer.css +98 -0
  101. package/src/components/core/Viewer.js +564 -0
  102. package/src/components/core/index.js +12 -0
  103. package/src/components/index.js +13 -0
  104. package/src/components/layout/BottomDrawer.js +257 -0
  105. package/src/components/layout/CorneredGrid.js +112 -0
  106. package/src/components/layout/Mini.js +117 -0
  107. package/src/components/layout/Tabs.js +133 -0
  108. package/src/components/layout/index.js +9 -0
  109. package/src/components/menus/MapBackground.js +106 -0
  110. package/src/components/menus/MapFilters.js +400 -0
  111. package/src/components/menus/MapLayers.js +143 -0
  112. package/src/components/menus/MapLegend.js +34 -0
  113. package/src/components/menus/PictureLegend.js +253 -0
  114. package/src/components/menus/PictureMetadata.js +317 -0
  115. package/src/components/menus/PlayerOptions.js +95 -0
  116. package/src/components/menus/QualityScoreDoc.js +36 -0
  117. package/src/components/menus/ReportForm.js +133 -0
  118. package/src/components/menus/Share.js +100 -0
  119. package/src/components/menus/index.js +15 -0
  120. package/src/components/styles.js +383 -0
  121. package/src/components/ui/Button.js +77 -0
  122. package/src/components/ui/ButtonGroup.css +57 -0
  123. package/src/components/ui/ButtonGroup.js +68 -0
  124. package/src/components/ui/CopyButton.js +106 -0
  125. package/src/components/ui/Grade.js +54 -0
  126. package/src/components/ui/LinkButton.js +67 -0
  127. package/src/components/ui/ListGroup.js +66 -0
  128. package/src/components/ui/Loader.js +203 -0
  129. package/src/components/{Map.css → ui/Map.css} +5 -17
  130. package/src/components/{Map.js → ui/Map.js} +148 -156
  131. package/src/components/ui/MapMore.js +324 -0
  132. package/src/components/{Photo.css → ui/Photo.css} +6 -6
  133. package/src/components/{Photo.js → ui/Photo.js} +313 -101
  134. package/src/components/ui/Popup.js +145 -0
  135. package/src/components/ui/ProgressBar.js +104 -0
  136. package/src/components/ui/QualityScore.js +147 -0
  137. package/src/components/ui/SearchBar.js +367 -0
  138. package/src/components/ui/TogglableGroup.js +157 -0
  139. package/src/components/ui/index.js +22 -0
  140. package/src/components/ui/widgets/GeoSearch.css +21 -0
  141. package/src/components/ui/widgets/GeoSearch.js +139 -0
  142. package/src/components/ui/widgets/Legend.js +113 -0
  143. package/src/components/ui/widgets/MapFiltersButton.js +104 -0
  144. package/src/components/ui/widgets/MapLayersButton.js +79 -0
  145. package/src/components/ui/widgets/OSMEditors.js +155 -0
  146. package/src/components/ui/widgets/PictureLegendActions.js +117 -0
  147. package/src/components/ui/widgets/Player.css +7 -0
  148. package/src/components/ui/widgets/Player.js +151 -0
  149. package/src/components/ui/widgets/Zoom.js +82 -0
  150. package/src/components/ui/widgets/index.js +13 -0
  151. package/src/img/loader_base.jpg +0 -0
  152. package/src/img/panoramax.svg +13 -0
  153. package/src/img/switch_big.svg +20 -10
  154. package/src/index.js +7 -9
  155. package/src/translations/br.json +1 -0
  156. package/src/translations/da.json +38 -15
  157. package/src/translations/de.json +5 -3
  158. package/src/translations/en.json +35 -15
  159. package/src/translations/eo.json +38 -15
  160. package/src/translations/es.json +1 -1
  161. package/src/translations/fr.json +36 -16
  162. package/src/translations/hu.json +1 -1
  163. package/src/translations/it.json +39 -16
  164. package/src/translations/ja.json +182 -1
  165. package/src/translations/nl.json +106 -6
  166. package/src/translations/pl.json +1 -1
  167. package/src/translations/sv.json +182 -0
  168. package/src/translations/zh_Hant.json +35 -14
  169. package/src/utils/API.js +109 -49
  170. package/src/utils/InitParameters.js +388 -0
  171. package/src/utils/PhotoAdapter.js +1 -0
  172. package/src/utils/URLHandler.js +362 -0
  173. package/src/utils/geocoder.js +152 -0
  174. package/src/utils/{I18n.js → i18n.js} +7 -3
  175. package/src/utils/index.js +11 -0
  176. package/src/utils/{Map.js → map.js} +256 -77
  177. package/src/utils/picture.js +442 -0
  178. package/src/utils/utils.js +324 -0
  179. package/src/utils/widgets.js +55 -0
  180. package/tests/components/core/Basic.test.js +121 -0
  181. package/tests/components/core/BasicMock.js +25 -0
  182. package/tests/components/core/CoverageMap.test.js +20 -0
  183. package/tests/components/core/Editor.test.js +20 -0
  184. package/tests/components/core/PhotoViewer.test.js +57 -0
  185. package/tests/components/core/Viewer.test.js +84 -0
  186. package/tests/components/core/__snapshots__/PhotoViewer.test.js.snap +73 -0
  187. package/tests/components/core/__snapshots__/Viewer.test.js.snap +145 -0
  188. package/tests/components/ui/CopyButton.test.js +52 -0
  189. package/tests/components/ui/Loader.test.js +55 -0
  190. package/tests/components/{Map.test.js → ui/Map.test.js} +73 -61
  191. package/tests/components/{Photo.test.js → ui/Photo.test.js} +97 -63
  192. package/tests/components/ui/Popup.test.js +26 -0
  193. package/tests/components/ui/QualityScore.test.js +18 -0
  194. package/tests/components/ui/SearchBar.test.js +110 -0
  195. package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +33 -0
  196. package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
  197. package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
  198. package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +70 -6
  199. package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
  200. package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
  201. package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
  202. package/tests/utils/API.test.js +83 -83
  203. package/tests/utils/InitParameters.test.js +499 -0
  204. package/tests/utils/URLHandler.test.js +401 -0
  205. package/tests/utils/__snapshots__/API.test.js.snap +10 -0
  206. package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
  207. package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +1 -1
  208. package/tests/utils/__snapshots__/map.test.js.snap +11 -0
  209. package/tests/utils/__snapshots__/picture.test.js.snap +327 -0
  210. package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
  211. package/tests/utils/geocoder.test.js +37 -0
  212. package/tests/utils/{I18n.test.js → i18n.test.js} +8 -8
  213. package/tests/utils/map.test.js +126 -0
  214. package/tests/utils/picture.test.js +745 -0
  215. package/tests/utils/utils.test.js +288 -0
  216. package/tests/utils/widgets.test.js +31 -0
  217. package/docs/01_Start.md +0 -149
  218. package/docs/02_Usage.md +0 -831
  219. package/docs/04_Advanced_examples.md +0 -216
  220. package/src/Editor.css +0 -37
  221. package/src/Editor.js +0 -361
  222. package/src/StandaloneMap.js +0 -114
  223. package/src/Viewer.css +0 -203
  224. package/src/Viewer.js +0 -1246
  225. package/src/components/CoreView.css +0 -70
  226. package/src/components/CoreView.js +0 -175
  227. package/src/components/Loader.css +0 -74
  228. package/src/components/Loader.js +0 -120
  229. package/src/img/loader_hd.jpg +0 -0
  230. package/src/utils/Exif.js +0 -193
  231. package/src/utils/Utils.js +0 -631
  232. package/src/utils/Widgets.js +0 -562
  233. package/src/viewer/URLHash.js +0 -469
  234. package/src/viewer/Widgets.css +0 -880
  235. package/src/viewer/Widgets.js +0 -1470
  236. package/tests/Editor.test.js +0 -126
  237. package/tests/StandaloneMap.test.js +0 -45
  238. package/tests/Viewer.test.js +0 -366
  239. package/tests/__snapshots__/Editor.test.js.snap +0 -298
  240. package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
  241. package/tests/__snapshots__/Viewer.test.js.snap +0 -195
  242. package/tests/components/CoreView.test.js +0 -92
  243. package/tests/components/Loader.test.js +0 -38
  244. package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
  245. package/tests/utils/Exif.test.js +0 -124
  246. package/tests/utils/Map.test.js +0 -113
  247. package/tests/utils/Utils.test.js +0 -300
  248. package/tests/utils/Widgets.test.js +0 -107
  249. package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
  250. package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
  251. package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
  252. package/tests/viewer/URLHash.test.js +0 -559
  253. package/tests/viewer/Widgets.test.js +0 -127
  254. package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
  255. package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
@@ -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
- }