@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.
- package/.dockerignore +6 -0
- package/.gitlab-ci.yml +71 -0
- package/CHANGELOG.md +428 -0
- package/CODE_OF_CONDUCT.md +134 -0
- package/Dockerfile +14 -0
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/build/editor.html +1 -0
- package/build/index.css +36 -0
- package/build/index.css.map +1 -0
- package/build/index.html +1 -0
- package/build/index.js +25 -0
- package/build/index.js.map +1 -0
- package/build/map.html +1 -0
- package/build/viewer.html +1 -0
- package/config/env.js +104 -0
- package/config/getHttpsConfig.js +66 -0
- package/config/getPackageJson.js +25 -0
- package/config/jest/babelTransform.js +29 -0
- package/config/jest/cssTransform.js +14 -0
- package/config/jest/fileTransform.js +40 -0
- package/config/modules.js +134 -0
- package/config/paths.js +72 -0
- package/config/pnpTs.js +35 -0
- package/config/webpack/persistentCache/createEnvironmentHash.js +9 -0
- package/config/webpack.config.js +885 -0
- package/config/webpackDevServer.config.js +127 -0
- package/docs/01_Start.md +149 -0
- package/docs/02_Usage.md +828 -0
- package/docs/03_URL_settings.md +140 -0
- package/docs/04_Advanced_examples.md +214 -0
- package/docs/05_Compatibility.md +85 -0
- package/docs/09_Develop.md +62 -0
- package/docs/90_Releases.md +27 -0
- package/docs/images/class_diagram.drawio +129 -0
- package/docs/images/class_diagram.jpg +0 -0
- package/docs/images/screenshot.jpg +0 -0
- package/mkdocs.yml +45 -0
- package/package.json +254 -0
- package/public/editor.html +54 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +59 -0
- package/public/map.html +53 -0
- package/public/viewer.html +67 -0
- package/scripts/build.js +217 -0
- package/scripts/start.js +176 -0
- package/scripts/test.js +52 -0
- package/src/Editor.css +37 -0
- package/src/Editor.js +359 -0
- package/src/StandaloneMap.js +114 -0
- package/src/Viewer.css +203 -0
- package/src/Viewer.js +1186 -0
- package/src/components/CoreView.css +64 -0
- package/src/components/CoreView.js +159 -0
- package/src/components/Loader.css +56 -0
- package/src/components/Loader.js +111 -0
- package/src/components/Map.css +65 -0
- package/src/components/Map.js +841 -0
- package/src/components/Photo.css +36 -0
- package/src/components/Photo.js +687 -0
- package/src/img/arrow_360.svg +14 -0
- package/src/img/arrow_flat.svg +11 -0
- package/src/img/arrow_triangle.svg +10 -0
- package/src/img/arrow_turn.svg +9 -0
- package/src/img/bg_aerial.jpg +0 -0
- package/src/img/bg_streets.jpg +0 -0
- package/src/img/loader_base.jpg +0 -0
- package/src/img/loader_hd.jpg +0 -0
- package/src/img/logo_dead.svg +91 -0
- package/src/img/marker.svg +17 -0
- package/src/img/marker_blue.svg +20 -0
- package/src/img/switch_big.svg +44 -0
- package/src/img/switch_mini.svg +48 -0
- package/src/index.js +10 -0
- package/src/translations/de.json +163 -0
- package/src/translations/en.json +164 -0
- package/src/translations/eo.json +6 -0
- package/src/translations/es.json +164 -0
- package/src/translations/fi.json +1 -0
- package/src/translations/fr.json +164 -0
- package/src/translations/hu.json +133 -0
- package/src/translations/nl.json +1 -0
- package/src/translations/zh_Hant.json +136 -0
- package/src/utils/API.js +709 -0
- package/src/utils/Exif.js +198 -0
- package/src/utils/I18n.js +75 -0
- package/src/utils/Map.js +382 -0
- package/src/utils/PhotoAdapter.js +45 -0
- package/src/utils/Utils.js +568 -0
- package/src/utils/Widgets.js +477 -0
- package/src/viewer/URLHash.js +334 -0
- package/src/viewer/Widgets.css +711 -0
- package/src/viewer/Widgets.js +1196 -0
- package/tests/Editor.test.js +125 -0
- package/tests/StandaloneMap.test.js +44 -0
- package/tests/Viewer.test.js +363 -0
- package/tests/__snapshots__/Editor.test.js.snap +300 -0
- package/tests/__snapshots__/StandaloneMap.test.js.snap +30 -0
- package/tests/__snapshots__/Viewer.test.js.snap +195 -0
- package/tests/components/CoreView.test.js +91 -0
- package/tests/components/Loader.test.js +38 -0
- package/tests/components/Map.test.js +230 -0
- package/tests/components/Photo.test.js +335 -0
- package/tests/components/__snapshots__/Loader.test.js.snap +15 -0
- package/tests/components/__snapshots__/Map.test.js.snap +767 -0
- package/tests/components/__snapshots__/Photo.test.js.snap +205 -0
- package/tests/data/Map_geocoder_ban.json +36 -0
- package/tests/data/Map_geocoder_nominatim.json +56 -0
- package/tests/data/Viewer_pictures_1.json +148 -0
- package/tests/setupTests.js +5 -0
- package/tests/utils/API.test.js +906 -0
- package/tests/utils/Exif.test.js +124 -0
- package/tests/utils/I18n.test.js +28 -0
- package/tests/utils/Map.test.js +105 -0
- package/tests/utils/Utils.test.js +300 -0
- package/tests/utils/Widgets.test.js +107 -0
- package/tests/utils/__snapshots__/API.test.js.snap +132 -0
- package/tests/utils/__snapshots__/Exif.test.js.snap +43 -0
- package/tests/utils/__snapshots__/Map.test.js.snap +48 -0
- package/tests/utils/__snapshots__/Utils.test.js.snap +41 -0
- package/tests/utils/__snapshots__/Widgets.test.js.snap +44 -0
- package/tests/viewer/URLHash.test.js +537 -0
- package/tests/viewer/Widgets.test.js +127 -0
- package/tests/viewer/__snapshots__/URLHash.test.js.snap +98 -0
- 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
|
+

|
|
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
|