@panoramax/web-viewer 3.0.2-develop-a8ea8e60

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 (125) hide show
  1. package/.dockerignore +6 -0
  2. package/.gitlab-ci.yml +71 -0
  3. package/CHANGELOG.md +428 -0
  4. package/CODE_OF_CONDUCT.md +134 -0
  5. package/Dockerfile +14 -0
  6. package/LICENSE +21 -0
  7. package/README.md +39 -0
  8. package/build/editor.html +1 -0
  9. package/build/index.css +36 -0
  10. package/build/index.css.map +1 -0
  11. package/build/index.html +1 -0
  12. package/build/index.js +25 -0
  13. package/build/index.js.map +1 -0
  14. package/build/map.html +1 -0
  15. package/build/viewer.html +1 -0
  16. package/config/env.js +104 -0
  17. package/config/getHttpsConfig.js +66 -0
  18. package/config/getPackageJson.js +25 -0
  19. package/config/jest/babelTransform.js +29 -0
  20. package/config/jest/cssTransform.js +14 -0
  21. package/config/jest/fileTransform.js +40 -0
  22. package/config/modules.js +134 -0
  23. package/config/paths.js +72 -0
  24. package/config/pnpTs.js +35 -0
  25. package/config/webpack/persistentCache/createEnvironmentHash.js +9 -0
  26. package/config/webpack.config.js +885 -0
  27. package/config/webpackDevServer.config.js +127 -0
  28. package/docs/01_Start.md +149 -0
  29. package/docs/02_Usage.md +828 -0
  30. package/docs/03_URL_settings.md +140 -0
  31. package/docs/04_Advanced_examples.md +214 -0
  32. package/docs/05_Compatibility.md +85 -0
  33. package/docs/09_Develop.md +62 -0
  34. package/docs/90_Releases.md +27 -0
  35. package/docs/images/class_diagram.drawio +129 -0
  36. package/docs/images/class_diagram.jpg +0 -0
  37. package/docs/images/screenshot.jpg +0 -0
  38. package/mkdocs.yml +45 -0
  39. package/package.json +254 -0
  40. package/public/editor.html +54 -0
  41. package/public/favicon.ico +0 -0
  42. package/public/index.html +59 -0
  43. package/public/map.html +53 -0
  44. package/public/viewer.html +67 -0
  45. package/scripts/build.js +217 -0
  46. package/scripts/start.js +176 -0
  47. package/scripts/test.js +52 -0
  48. package/src/Editor.css +37 -0
  49. package/src/Editor.js +359 -0
  50. package/src/StandaloneMap.js +114 -0
  51. package/src/Viewer.css +203 -0
  52. package/src/Viewer.js +1186 -0
  53. package/src/components/CoreView.css +64 -0
  54. package/src/components/CoreView.js +159 -0
  55. package/src/components/Loader.css +56 -0
  56. package/src/components/Loader.js +111 -0
  57. package/src/components/Map.css +65 -0
  58. package/src/components/Map.js +841 -0
  59. package/src/components/Photo.css +36 -0
  60. package/src/components/Photo.js +687 -0
  61. package/src/img/arrow_360.svg +14 -0
  62. package/src/img/arrow_flat.svg +11 -0
  63. package/src/img/arrow_triangle.svg +10 -0
  64. package/src/img/arrow_turn.svg +9 -0
  65. package/src/img/bg_aerial.jpg +0 -0
  66. package/src/img/bg_streets.jpg +0 -0
  67. package/src/img/loader_base.jpg +0 -0
  68. package/src/img/loader_hd.jpg +0 -0
  69. package/src/img/logo_dead.svg +91 -0
  70. package/src/img/marker.svg +17 -0
  71. package/src/img/marker_blue.svg +20 -0
  72. package/src/img/switch_big.svg +44 -0
  73. package/src/img/switch_mini.svg +48 -0
  74. package/src/index.js +10 -0
  75. package/src/translations/de.json +163 -0
  76. package/src/translations/en.json +164 -0
  77. package/src/translations/eo.json +6 -0
  78. package/src/translations/es.json +164 -0
  79. package/src/translations/fi.json +1 -0
  80. package/src/translations/fr.json +164 -0
  81. package/src/translations/hu.json +133 -0
  82. package/src/translations/nl.json +1 -0
  83. package/src/translations/zh_Hant.json +136 -0
  84. package/src/utils/API.js +709 -0
  85. package/src/utils/Exif.js +198 -0
  86. package/src/utils/I18n.js +75 -0
  87. package/src/utils/Map.js +382 -0
  88. package/src/utils/PhotoAdapter.js +45 -0
  89. package/src/utils/Utils.js +568 -0
  90. package/src/utils/Widgets.js +477 -0
  91. package/src/viewer/URLHash.js +334 -0
  92. package/src/viewer/Widgets.css +711 -0
  93. package/src/viewer/Widgets.js +1196 -0
  94. package/tests/Editor.test.js +125 -0
  95. package/tests/StandaloneMap.test.js +44 -0
  96. package/tests/Viewer.test.js +363 -0
  97. package/tests/__snapshots__/Editor.test.js.snap +300 -0
  98. package/tests/__snapshots__/StandaloneMap.test.js.snap +30 -0
  99. package/tests/__snapshots__/Viewer.test.js.snap +195 -0
  100. package/tests/components/CoreView.test.js +91 -0
  101. package/tests/components/Loader.test.js +38 -0
  102. package/tests/components/Map.test.js +230 -0
  103. package/tests/components/Photo.test.js +335 -0
  104. package/tests/components/__snapshots__/Loader.test.js.snap +15 -0
  105. package/tests/components/__snapshots__/Map.test.js.snap +767 -0
  106. package/tests/components/__snapshots__/Photo.test.js.snap +205 -0
  107. package/tests/data/Map_geocoder_ban.json +36 -0
  108. package/tests/data/Map_geocoder_nominatim.json +56 -0
  109. package/tests/data/Viewer_pictures_1.json +148 -0
  110. package/tests/setupTests.js +5 -0
  111. package/tests/utils/API.test.js +906 -0
  112. package/tests/utils/Exif.test.js +124 -0
  113. package/tests/utils/I18n.test.js +28 -0
  114. package/tests/utils/Map.test.js +105 -0
  115. package/tests/utils/Utils.test.js +300 -0
  116. package/tests/utils/Widgets.test.js +107 -0
  117. package/tests/utils/__snapshots__/API.test.js.snap +132 -0
  118. package/tests/utils/__snapshots__/Exif.test.js.snap +43 -0
  119. package/tests/utils/__snapshots__/Map.test.js.snap +48 -0
  120. package/tests/utils/__snapshots__/Utils.test.js.snap +41 -0
  121. package/tests/utils/__snapshots__/Widgets.test.js.snap +44 -0
  122. package/tests/viewer/URLHash.test.js +537 -0
  123. package/tests/viewer/Widgets.test.js +127 -0
  124. package/tests/viewer/__snapshots__/URLHash.test.js.snap +98 -0
  125. package/tests/viewer/__snapshots__/Widgets.test.js.snap +393 -0
@@ -0,0 +1,140 @@
1
+ # URL settings
2
+
3
+ Various settings could be set from URL hash part in order to create permalinks.
4
+
5
+ These are set after the `#` symbol of the URL, following a `key=value` format, each being separated by `&` symbol.
6
+
7
+ Example:
8
+
9
+ ```urlencoded
10
+ https://panoramax.xyz/#map=19.51/48.1204522/-1.7199004&pic=890b6268-7716-4e34-ada9-69985e6c1657
11
+ ```
12
+
13
+ ## :fontawesome-solid-computer: Interface settings
14
+
15
+ ### :material-target: `focus`: main shown element
16
+
17
+ Switch to choose which element between map, picture or metadata should be shown wide at start. Examples:
18
+
19
+ - `focus=map`
20
+ - `focus=pic`
21
+ - `focus=meta`
22
+
23
+ By default, picture is shown wide.
24
+
25
+ ### :simple-speedtest: `speed`: sequence play speed
26
+
27
+ The duration of stay on a picture during sequence play (excluding image dowloading time), in milliseconds. Authorized values are between 0 and 3000. Example:
28
+
29
+ ```urlencoded
30
+ speed=1000
31
+ ```
32
+
33
+ ### :lock: `nav`: allowed navigation between pictures
34
+
35
+ Choose the allowed navigation between pictures, to eventually restrict what is visible from first shown picture. Values are:
36
+
37
+ - `nav=any` (or no value): no restriction in navigation (default)
38
+ - `nav=seq`: can only see pictures in same sequence
39
+ - `nav=none`: can only see current picture, no navigation to other picture allowed
40
+
41
+ !!! note
42
+
43
+ This parameter is intended to work on page first load. If used after page load, you may switch to another picture or fully reload pictures metadata cache in order to have expected behaviour.
44
+
45
+ ## :material-image: Picture settings
46
+
47
+ ### :material-barcode: `pic`: picture ID
48
+
49
+ The currently selected picture ID. Example:
50
+
51
+ ```urlencoded
52
+ pic=890b6268-7716-4e34-ada9-69985e6c1657
53
+ ```
54
+
55
+ ### :material-arrow-all: `xyz`: picture position
56
+
57
+ The shown position in picture, following this format:
58
+
59
+ ```
60
+ x/y/z
61
+ ```
62
+
63
+ With:
64
+
65
+ - `x`: the heading in degrees (0 = North, 90 = East, 180 = South, 270 = West)
66
+ - `y`: top/bottom position in degrees (-90 = bottom, 0 = front, 90 = top)
67
+ - `z`: zoom level (0 = minimum/wide view, 100 = maximum/full zoom)
68
+
69
+ Example:
70
+
71
+ ```urlencoded
72
+ xyz=10/25/50
73
+ ```
74
+
75
+ ## :map: Map settings
76
+
77
+ ### :fontawesome-solid-location-dot: `map`: map position and visibility
78
+
79
+ The `map` parameters handles both map visibility and position. It can take different values:
80
+
81
+ - `map=none`: to completely disable the map.
82
+ - `map=zoom/latitude/longitude`: for setting the map position (following [MapLibre GL JS hash format](https://maplibre.org/maplibre-gl-js-docs/api/map/#map-parameters)). It updates automatically when map is moved.
83
+ - no parameter set: shows the map of the whole world, or zoomed on instance area of interest.
84
+
85
+ Example:
86
+
87
+ ```urlencoded
88
+ map=19.51/48.1204522/-1.7199004
89
+ ```
90
+
91
+ !!! note
92
+
93
+ The `map=none` is intended to work __on page first load only__. Changing it dynamically will not hide the map, and will be reset on next map movement.
94
+
95
+ ### :date: `date_from` and `date_to`: filter map data by date
96
+
97
+ Minimum and maximum capture date for pictures and sequences to show on map (if map is enabled), in ISO format:
98
+
99
+ ```urlencoded
100
+ date_from=2020-01-01&date_to=2023-12-31
101
+ ```
102
+
103
+ ### :material-rotate-360: `pic_type`: filter map data by picture type
104
+
105
+ The type of picture (360° or classic) to show on map (if map is enabled). Examples:
106
+
107
+ - `pic_type=flat` for classic pictures
108
+ - `pic_type=equirectangular` for 360° pictures
109
+ - Not set for showing both
110
+
111
+ ### :camera: `camera`: filter map data by camera make and model
112
+
113
+ The camera make and model to filter shown pictures and sequences on map (if map is enabled). A fuzzy search is used to filter on map, but your string _should_ always start with camera make. Examples:
114
+
115
+ - `camera=gopro` will display all pictures taken with any _GoPro_ camera
116
+ - `camera=gopro%20max` will only show pictures taken with a _GoPro Max_ camera
117
+ - `camera=max` will not shown any picture on map, as it doesn't match any camera make
118
+
119
+ ### :material-format-paint: `theme`: map colouring for pictures and sequences
120
+
121
+ The map theme to use for displaying pictures and sequences (if map is enabled). Available themes are:
122
+
123
+ - `theme=default` (or no setting defined): single color for display (no classification)
124
+ - `theme=age`: color based on picture/sequence age (red = recent, yellow = 2+ years old)
125
+ - `theme=type`: color based on camera type (orange = classic, green = 360°)
126
+
127
+ ### :material-nature-people: `background`: map background
128
+
129
+ The map background to display (if map is enabled, and raster background configured). Available values are:
130
+
131
+ - `background=streets` (or no setting defined): classic streets map
132
+ - `background=aerial`: aerial imagery (only if raster background available)
133
+
134
+ ### :fontawesome-solid-user: `users`: filter map data by username
135
+
136
+ This parameter filters pictures and sequences shown on map to only keep those of concerned users. Each user is defined by its UUID (not username). List is comma-separated. Example:
137
+
138
+ ```urlencoded
139
+ users=abcdefgh-1234-5678-9012-abcdefgh12345678,dcf0d3be-0418-4b71-9315-0ff8a2f86a0b
140
+ ```
@@ -0,0 +1,214 @@
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://wxs.ign.fr/ortho/geoportail/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTHOIMAGERY.ORTHOPHOTOS&TILEMATRIXSET=PM&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg&STYLE=normal"
69
+ ],
70
+ attribution: "© IGN",
71
+ tileSize: 256
72
+ }
73
+ }
74
+ }
75
+ );
76
+ ```
77
+
78
+ ## Use another geocoder
79
+
80
+ 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.
81
+
82
+ 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:
83
+
84
+ ```js
85
+ var viewer = new Panoramax.Viewer(
86
+ "viewer",
87
+ "https://my-panoramax-server.net/api",
88
+ {
89
+ map: {
90
+ geocoder: { engine: "ban" }
91
+ }
92
+ }
93
+ );
94
+ ```
95
+
96
+ And you can also define your own custom geocoder using these options:
97
+
98
+ ```js
99
+ var myOwnGeocoder = function(config) {
100
+ // Call your API
101
+ // Config parameter is based on geocoderApi.forwardGeocode.config structure
102
+ // Described here : https://github.com/maplibre/maplibre-gl-geocoder/blob/main/API.md#setgeocoderapi
103
+
104
+ // It returns a promise resolving on a Carmen GeoJSON FeatureCollection
105
+ // Format is described here : https://docs.mapbox.com/api/search/geocoding/#geocoding-response-object
106
+ }
107
+
108
+ var viewer = new Panoramax.Viewer(
109
+ "viewer",
110
+ "https://my-panoramax-server.net/api",
111
+ {
112
+ map: {
113
+ geocoder: { geocoderApi: {
114
+ forwardGeocode: myOwnGeocoder
115
+ } }
116
+ }
117
+ }
118
+ );
119
+ ```
120
+
121
+ ## Authentication against API
122
+
123
+ 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:
124
+
125
+ ```js
126
+ var viewer = new Panoramax.Viewer(
127
+ "viewer",
128
+ "https://your-secured-stac.fr/api",
129
+ {
130
+ fetchOptions: {
131
+ credentials: "include"
132
+ }
133
+ }
134
+ );
135
+ ```
136
+
137
+ ## Add custom buttons
138
+
139
+ 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:
140
+
141
+ ```js
142
+ var viewer = new Panoramax.Viewer(
143
+ "viewer",
144
+ "https://my-panoramax-server.net/api",
145
+ {
146
+ widgets: {
147
+ customWidget: `<a
148
+ href="https://my-amazing-page.net/"
149
+ class="gvs-btn gvs-widget-bg gvs-btn-large"
150
+ title="Go to an amazing page">🤩</a>`
151
+ },
152
+ }
153
+ );
154
+ ```
155
+
156
+ You can also pass more complex DOM elements:
157
+
158
+ ```js
159
+ var myWidget = document.createElement("div");
160
+ myWidget.innerHTML = "...";
161
+
162
+ var viewer = new Panoramax.Viewer(
163
+ "viewer",
164
+ "https://my-panoramax-server.net/api",
165
+ {
166
+ widgets: { customWidget: myWidget }
167
+ }
168
+ );
169
+ ```
170
+
171
+
172
+ ## Coverage map synced with external component
173
+
174
+ 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.
175
+
176
+ ```js
177
+ var map = new Panoramax.StandaloneMap(
178
+ "map",
179
+ "https://panoramax.ign.fr/api",
180
+ {
181
+ // Optional, to allow filtering by user
182
+ users: ["79b851b4-232a-4c96-ac1b-b6cf693c77ae"]
183
+ }
184
+ );
185
+
186
+ // Change visible map area
187
+ map.fitBounds([west, south, east, north]);
188
+
189
+ // Listen to user clicks on map
190
+ map.addEventListener("select", e => {
191
+ console.log("Selected sequence", e.detail.seqId, "picture", e.detail.picId);
192
+ });
193
+
194
+ // Listen to sequence hovered on map
195
+ map.addEventListener("hover", e => {
196
+ console.log("Hovered sequence", e.detail.seqId);
197
+ });
198
+
199
+ // You can also programatically change selection on map
200
+ map.select(
201
+ "c463d190-06b0-47fb-98a8-b4a775a39ad6", // A sequence ID
202
+ "bdea1eb4-4496-46da-a4d5-b22b16e75fa8" // A picture ID (can be null if unknown)
203
+ );
204
+ ```
205
+
206
+
207
+ ## Clean-up in Single Page Application
208
+
209
+ 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.
210
+
211
+ ```js
212
+ viewer.destroy();
213
+ delete viewer;
214
+ ```
@@ -0,0 +1,85 @@
1
+ # Compatibility with STAC API
2
+
3
+ Panoramax viewer works best with a [Panoramax API](https://gitlab.com/panoramax/server/api), but is designed to be compatible with a wide range of [STAC API](https://github.com/radiantearth/stac-api-spec). Although, third-party STAC API needs the following requirements to work with our viewer:
4
+
5
+ - Collections corresponding to pictures sequences, and items corresponding to individual pictures
6
+ - Offer a `/search` endpoint ([documentation](https://github.com/radiantearth/stac-api-spec/tree/main/item-search))
7
+ - Picture items should have required metadata documented below.
8
+ - Offer a vector tiles endpoint for map display, either with:
9
+ - A [MapLibre Style JSON](https://maplibre.org/maplibre-style-spec/) file, advertised through landing page (`/api`) with a `xyz-style` link.
10
+ - A direct tiles URL, pointing to tiles in [MVT format](https://mapbox.github.io/vector-tile-spec/) and following layer structure described below. It must be advertised in landing page (`/api`) using [Web Map Links](https://github.com/stac-extensions/web-map-links) STAC extension (as `xyz` link).
11
+
12
+ Optional metadata could also be supplied by third-party STAC API to improve viewer usability:
13
+
14
+ - In landing page (`/api` route, corresponding to main STAC Catalog):
15
+ - An `extent` property ([following this format](https://github.com/radiantearth/stac-spec/blob/master/collection-spec/collection-spec.md#extent-object)) could be provided to make map zoom in available data area by default.
16
+ - A `collection-preview` link pointing to a formatted URL (like `https://yourserver.fr/api/collections/{id}/thumb.jpg`) which is a direct link to a thumbnail image to represent a specific sequence.
17
+ - A `item-preview` link pointing to a formatted URL (like `http://localhost:5000/api/pictures/{id}/thumb.jpg`) which is a direct link to a thumbnail image for a given picture.
18
+ - A `data` link with `application/rss+xml` media type pointing to a RSS feed of recently uploaded collections. Given link may also support a `bbox` query string parameter to filter collections by their location.
19
+ - Links `user-xyz` (MVT media type) and `user-search` (JSON media type) to allow filtering by user.
20
+ - A `report` link with `application/json` media type to allow posting pictures reports.
21
+ - A `title` property for showing proper API name in viewer debug.
22
+
23
+
24
+ ## Perspective imagery metadata
25
+
26
+ Pictures metadata follow [STAC item specification](https://github.com/radiantearth/stac-spec/blob/master/item-spec/item-spec.md), plus some extensions:
27
+
28
+ - _Perspective imagery specification_ for its pictures and sequences metadata ([documentation](https://github.com/stac-extensions/perspective-imagery))
29
+ - _Tiled assets specification_ for smooth display of high-resolution pictures ([documentation](https://github.com/stac-extensions/tiled-assets))
30
+
31
+ Viewer relies on following item metadata for display:
32
+
33
+ - `assets`
34
+ - [`roles`](https://github.com/radiantearth/stac-spec/blob/master/item-spec/item-spec.md#asset-roles): `data`, `visual` and `thumbnail`
35
+ - `type`: `image/jpeg` or `image/webp`
36
+ - `href`
37
+ - `assets_templates`
38
+ - `tiles`
39
+ - `role`: `data`
40
+ - `href`
41
+ - `geometry`
42
+ - `collection`
43
+ - `id`
44
+ - `links`
45
+ - [`rel`](https://github.com/radiantearth/stac-spec/blob/master/item-spec/item-spec.md#link-object): `prev`, `next`, `related`
46
+ - `type`: `application/geo+json`
47
+ - `id`
48
+ - `geometry`
49
+ - `datetime`
50
+ - `properties`
51
+ - `pers:interior_orientation`
52
+ - `field_of_view`
53
+ - `focal_length`
54
+ - `view:azimuth`
55
+ - `pers:roll`
56
+ - `pers:pitch`
57
+ - `datetime` or `datetimetz`
58
+ - `tiles:tile_matrix_sets`
59
+ - `geovisio`
60
+ - `type`: `TileMatrixSetType`
61
+ - `tileMatrix`
62
+ - `matrixHeight`
63
+ - `matrixWidth`
64
+ - `tileHeight`
65
+ - `tileWidth`
66
+
67
+
68
+ ## Vector tiles format
69
+
70
+ MVT Vector tiles must contain at least two layers : sequences and pictures.
71
+
72
+ Layer _sequences_:
73
+
74
+ - Available on all zoom levels
75
+ - Available properties: `id` (sequence ID)
76
+
77
+ Layer _pictures_:
78
+
79
+ - Available on zoom levels >= 15
80
+ - Available properties: `id` (picture ID), `ts` (picture date/time), `heading` (picture heading in degrees)
81
+
82
+ A supplementary layer _grid_ can be made available for low-zoom overview:
83
+
84
+ - Available on zoom levels < 6
85
+ - Available properties: `id` (grid cell ID), `nb_pictures` (amount of pictures), `coef` (value from 0 to 1, relative quantity of available pictures)
@@ -0,0 +1,62 @@
1
+ # Developing on the client
2
+
3
+ You want to work on our library and offer bug fixes or new features ? That's awesome ! 🤩
4
+
5
+ Here are some inputs about working with Panoramax web client code.
6
+
7
+ If something seems missing or incomplete, don't hesitate to contact us by [email](mailto:panieravide@riseup.net) or using [an issue](https://gitlab.com/panoramax/clients/web-viewer/-/issues). We really want Panoramax to be a collaborative project, so everyone is welcome (see our [code of conduct](https://gitlab.com/panoramax/clients/web-viewer/-/blob/develop/CODE_OF_CONDUCT.md)).
8
+
9
+ ## Architecture
10
+
11
+ The current code is split between various elements:
12
+
13
+ - __Views__: a single functional entry, like _Viewer_ (map + picture), _StandaloneMap_ or _Editor_. They share parts of code in _CoreView_ class. They specialized the behaviour of components depending on the needs.
14
+ - __Components__: reusable elements, like _Map_, _Photo_ or _Loader_. They are used in some views depending of the context.
15
+ - __Utils__: utility functions, splitted in various files for clarity (_API, I18n, Map, Utils, Widgets_).
16
+
17
+ ![Class diagram of Panoramax web viewer](./images/class_diagram.jpg)
18
+
19
+ The library is relies on various other libraries:
20
+
21
+ - [Photo Sphere Viewer](https://github.com/mistic100/Photo-Sphere-Viewer), for displaying classic and 360° pictures
22
+ - [Maplibre GL JS](https://github.com/maplibre/maplibre-gl-js), for displaying the map which shows sequences and pictures location
23
+ - [JS Library Boilerplate](https://github.com/hodgef/js-library-boilerplate), for having a ready-to-use development toolbox
24
+
25
+
26
+ ## Testing
27
+
28
+ We're trying to make Panoramax as reliable and secure as possible. To ensure this, we rely heavily on code testing. A variety of testing tools is made available:
29
+
30
+ * `npm start` : launches a dev web server on [localhost:3000](http://localhost:3000)
31
+ * `npm run test` : unit testing
32
+ * `npm run lint` : syntax checks
33
+ * `npm run coverage` : amount of tested code
34
+
35
+ If you're working on bug fixes or new features, please __make sure to add appropriate tests__ to keep Panoramax level of quality.
36
+
37
+
38
+ ## Documentation
39
+
40
+ Documentation is offered through two means in this repository:
41
+
42
+ - Code itself is documented with [JSDoc comments](https://jsdoc.app/about-getting-started)
43
+ - A broader doc is available in `docs` folder
44
+
45
+ The code documentation can be generated using the following command:
46
+
47
+ ```bash
48
+ npm run doc
49
+ ```
50
+
51
+ That will update the `docs/02_Usage.md` file with all functions and parameters.
52
+
53
+ The broader doc can be served and viewed using [Mkdocs](https://www.mkdocs.org/):
54
+
55
+ ```bash
56
+ pip install mkdocs mkdocs-material
57
+ mkdocs serve
58
+ ```
59
+
60
+ ## Make a release
61
+
62
+ See [dedicated documentation](./90_Releases.md).
@@ -0,0 +1,27 @@
1
+ # Make a release
2
+
3
+ Panoramax Web Viewer uses [semantic versioning](https://semver.org/) for its release numbers.
4
+
5
+ !!! note
6
+
7
+ On Viewer versions < 3.0, a strong constraint was put to keep versions in sync between API and Viewer. Each component could have different `PATCH` versions, but compatibility __had to be__ ensured between `MAJOR.MINOR` versions. Since Viewer version >= 3.0, any STAC-compliant API should be supported by Viewer.
8
+
9
+ Run these commands in order to issue a new release:
10
+
11
+ ```bash
12
+ git checkout develop
13
+
14
+ vim package.json # Change version
15
+ vim package-lock.json # Change version
16
+ npm run doc
17
+
18
+ vim CHANGELOG.md # Replace unreleased to version number (and links at bottom)
19
+
20
+ git add *
21
+ git commit -m "Release x.x.x"
22
+ git tag -a x.x.x -m "Release x.x.x"
23
+ git push origin develop
24
+ git checkout main
25
+ git merge develop
26
+ git push origin main --tags
27
+ ```
@@ -0,0 +1,129 @@
1
+ <mxfile host="app.diagrams.net" modified="2024-03-25T13:20:49.245Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0" etag="r1p7scxPnhYybEiXE-8z" version="23.1.2" type="device">
2
+ <diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
3
+ <mxGraphModel dx="1018" dy="574" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
4
+ <root>
5
+ <mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
6
+ <mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
7
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-1" value="CoreView" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
8
+ <mxGeometry x="20" y="40" width="160" height="34" as="geometry" />
9
+ </mxCell>
10
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-3" value="" style="line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=inherit;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-1">
11
+ <mxGeometry y="26" width="160" height="8" as="geometry" />
12
+ </mxCell>
13
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-9" value="Viewer" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
14
+ <mxGeometry x="20" y="160" width="160" height="34" as="geometry" />
15
+ </mxCell>
16
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-10" value="" style="line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=inherit;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-9">
17
+ <mxGeometry y="26" width="160" height="8" as="geometry" />
18
+ </mxCell>
19
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-11" value="StandaloneMap" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
20
+ <mxGeometry x="200" y="160" width="160" height="34" as="geometry" />
21
+ </mxCell>
22
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-12" value="" style="line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=inherit;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-11">
23
+ <mxGeometry y="26" width="160" height="8" as="geometry" />
24
+ </mxCell>
25
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-13" value="Editor" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
26
+ <mxGeometry x="380" y="160" width="160" height="34" as="geometry" />
27
+ </mxCell>
28
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-14" value="" style="line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=inherit;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-13">
29
+ <mxGeometry y="26" width="160" height="8" as="geometry" />
30
+ </mxCell>
31
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-15" value="Extends" style="endArrow=block;endSize=16;endFill=0;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-9" target="-sBJLWWXFzCuZ66xZKHM-1">
32
+ <mxGeometry width="160" relative="1" as="geometry">
33
+ <mxPoint x="310" y="260" as="sourcePoint" />
34
+ <mxPoint x="470" y="260" as="targetPoint" />
35
+ </mxGeometry>
36
+ </mxCell>
37
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-17" value="" style="endArrow=none;html=1;rounded=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
38
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
39
+ <mxPoint x="100" y="140" as="sourcePoint" />
40
+ <mxPoint x="460" y="140" as="targetPoint" />
41
+ </mxGeometry>
42
+ </mxCell>
43
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-19" value="" style="endArrow=none;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-11">
44
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
45
+ <mxPoint x="370" y="290" as="sourcePoint" />
46
+ <mxPoint x="280" y="140" as="targetPoint" />
47
+ </mxGeometry>
48
+ </mxCell>
49
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-20" value="" style="endArrow=none;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-13">
50
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
51
+ <mxPoint x="290" y="170" as="sourcePoint" />
52
+ <mxPoint x="460" y="140" as="targetPoint" />
53
+ </mxGeometry>
54
+ </mxCell>
55
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-22" value="Views" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
56
+ <mxGeometry x="10" y="10" width="540" height="200" as="geometry" />
57
+ </mxCell>
58
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-23" value="Components" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
59
+ <mxGeometry x="10" y="230" width="540" height="90" as="geometry" />
60
+ </mxCell>
61
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-26" value="Photo" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-23">
62
+ <mxGeometry x="10" y="40" width="160" height="34" as="geometry" />
63
+ </mxCell>
64
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-27" value="" style="line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=inherit;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-26">
65
+ <mxGeometry y="26" width="160" height="8" as="geometry" />
66
+ </mxCell>
67
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-28" value="Loader" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-23">
68
+ <mxGeometry x="370" y="40" width="160" height="34" as="geometry" />
69
+ </mxCell>
70
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-29" value="" style="line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=inherit;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-28">
71
+ <mxGeometry y="26" width="160" height="8" as="geometry" />
72
+ </mxCell>
73
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-24" value="Map" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-23">
74
+ <mxGeometry x="190" y="40" width="160" height="34" as="geometry" />
75
+ </mxCell>
76
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-25" value="" style="line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=inherit;" vertex="1" parent="-sBJLWWXFzCuZ66xZKHM-24">
77
+ <mxGeometry y="26" width="160" height="8" as="geometry" />
78
+ </mxCell>
79
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-30" value="Use" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-9" target="-sBJLWWXFzCuZ66xZKHM-24">
80
+ <mxGeometry width="160" relative="1" as="geometry">
81
+ <mxPoint x="370" y="300" as="sourcePoint" />
82
+ <mxPoint x="234" y="272" as="targetPoint" />
83
+ </mxGeometry>
84
+ </mxCell>
85
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-31" value="Use" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=0.506;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-10" target="-sBJLWWXFzCuZ66xZKHM-26">
86
+ <mxGeometry width="160" relative="1" as="geometry">
87
+ <mxPoint x="370" y="300" as="sourcePoint" />
88
+ <mxPoint x="530" y="300" as="targetPoint" />
89
+ </mxGeometry>
90
+ </mxCell>
91
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-32" value="Use" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-1" target="-sBJLWWXFzCuZ66xZKHM-28">
92
+ <mxGeometry x="0.3304" width="160" relative="1" as="geometry">
93
+ <mxPoint x="111" y="204" as="sourcePoint" />
94
+ <mxPoint x="290" y="280" as="targetPoint" />
95
+ <Array as="points">
96
+ <mxPoint x="580" y="49" />
97
+ <mxPoint x="580" y="287" />
98
+ </Array>
99
+ <mxPoint as="offset" />
100
+ </mxGeometry>
101
+ </mxCell>
102
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-38" value="Use" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-13" target="-sBJLWWXFzCuZ66xZKHM-24">
103
+ <mxGeometry width="160" relative="1" as="geometry">
104
+ <mxPoint x="320" y="390" as="sourcePoint" />
105
+ <mxPoint x="480" y="390" as="targetPoint" />
106
+ </mxGeometry>
107
+ </mxCell>
108
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-39" value="Use" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-11" target="-sBJLWWXFzCuZ66xZKHM-24">
109
+ <mxGeometry x="-0.5789" width="160" relative="1" as="geometry">
110
+ <mxPoint x="210" y="360" as="sourcePoint" />
111
+ <mxPoint x="240" y="270" as="targetPoint" />
112
+ <mxPoint as="offset" />
113
+ </mxGeometry>
114
+ </mxCell>
115
+ <mxCell id="-sBJLWWXFzCuZ66xZKHM-41" value="Use" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="-sBJLWWXFzCuZ66xZKHM-13" target="-sBJLWWXFzCuZ66xZKHM-26">
116
+ <mxGeometry width="160" relative="1" as="geometry">
117
+ <mxPoint x="150" y="370" as="sourcePoint" />
118
+ <mxPoint x="310" y="370" as="targetPoint" />
119
+ <Array as="points">
120
+ <mxPoint x="370" y="220" />
121
+ <mxPoint x="250" y="220" />
122
+ <mxPoint x="190" y="240" />
123
+ </Array>
124
+ </mxGeometry>
125
+ </mxCell>
126
+ </root>
127
+ </mxGraphModel>
128
+ </diagram>
129
+ </mxfile>
Binary file