@panoramax/web-viewer 3.2.3-develop-f219e404 → 3.2.3-develop-6257391e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/.gitlab-ci.yml +3 -0
  2. package/CHANGELOG.md +19 -0
  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 +2 -2
  7. package/build/index.css.map +1 -1
  8. package/build/index.html +1 -1
  9. package/build/index.js +1682 -5
  10. package/build/index.js.map +1 -1
  11. package/build/map.html +1 -1
  12. package/build/viewer.html +10 -1
  13. package/build/widgets.html +1 -0
  14. package/config/jest/mocks.js +172 -0
  15. package/config/paths.js +1 -0
  16. package/config/webpack.config.js +26 -0
  17. package/docs/03_URL_settings.md +3 -11
  18. package/docs/05_Compatibility.md +59 -76
  19. package/docs/09_Develop.md +30 -11
  20. package/docs/90_Releases.md +2 -2
  21. package/docs/images/class_diagram.drawio +28 -28
  22. package/docs/images/class_diagram.jpg +0 -0
  23. package/docs/index.md +112 -0
  24. package/docs/reference/components/core/Basic.md +153 -0
  25. package/docs/reference/components/core/CoverageMap.md +160 -0
  26. package/docs/reference/components/core/Editor.md +172 -0
  27. package/docs/reference/components/core/Viewer.md +288 -0
  28. package/docs/reference/components/layout/CorneredGrid.md +29 -0
  29. package/docs/reference/components/layout/Mini.md +45 -0
  30. package/docs/reference/components/menus/MapBackground.md +32 -0
  31. package/docs/reference/components/menus/MapFilters.md +15 -0
  32. package/docs/reference/components/menus/MapLayers.md +15 -0
  33. package/docs/reference/components/menus/MapLegend.md +15 -0
  34. package/docs/reference/components/menus/PictureLegend.md +15 -0
  35. package/docs/reference/components/menus/PictureMetadata.md +15 -0
  36. package/docs/reference/components/menus/PlayerOptions.md +15 -0
  37. package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
  38. package/docs/reference/components/menus/ReportForm.md +15 -0
  39. package/docs/reference/components/menus/ShareMenu.md +15 -0
  40. package/docs/reference/components/ui/Button.md +39 -0
  41. package/docs/reference/components/ui/ButtonGroup.md +36 -0
  42. package/docs/reference/components/ui/CopyButton.md +35 -0
  43. package/docs/reference/components/ui/Grade.md +32 -0
  44. package/docs/reference/components/ui/LinkButton.md +44 -0
  45. package/docs/reference/components/ui/Loader.md +54 -0
  46. package/docs/reference/components/ui/Map.md +214 -0
  47. package/docs/reference/components/ui/MapMore.md +233 -0
  48. package/docs/reference/components/ui/Photo.md +369 -0
  49. package/docs/reference/components/ui/Popup.md +56 -0
  50. package/docs/reference/components/ui/QualityScore.md +45 -0
  51. package/docs/reference/components/ui/SearchBar.md +63 -0
  52. package/docs/reference/components/ui/TogglableGroup.md +39 -0
  53. package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
  54. package/docs/reference/components/ui/widgets/Legend.md +32 -0
  55. package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
  56. package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
  57. package/docs/reference/components/ui/widgets/Player.md +32 -0
  58. package/docs/reference/components/ui/widgets/Share.md +15 -0
  59. package/docs/reference/components/ui/widgets/Zoom.md +15 -0
  60. package/docs/reference/utils/API.md +311 -0
  61. package/docs/reference/utils/InitParameters.md +67 -0
  62. package/docs/reference/utils/URLHandler.md +102 -0
  63. package/docs/reference.md +73 -0
  64. package/docs/shortcuts.md +11 -0
  65. package/docs/tutorials/aerial_imagery.md +19 -0
  66. package/docs/tutorials/authentication.md +10 -0
  67. package/docs/tutorials/custom_widgets.md +64 -0
  68. package/docs/tutorials/map_style.md +27 -0
  69. package/docs/tutorials/migrate_v4.md +122 -0
  70. package/docs/tutorials/synced_coverage.md +42 -0
  71. package/mkdocs.yml +60 -5
  72. package/package.json +10 -7
  73. package/public/editor.html +21 -29
  74. package/public/index.html +3 -3
  75. package/public/map.html +19 -18
  76. package/public/viewer.html +18 -24
  77. package/public/widgets.html +265 -0
  78. package/scripts/doc.js +77 -0
  79. package/src/components/core/Basic.css +44 -0
  80. package/src/components/core/Basic.js +258 -0
  81. package/src/components/core/CoverageMap.css +9 -0
  82. package/src/components/core/CoverageMap.js +105 -0
  83. package/src/components/core/Editor.css +23 -0
  84. package/src/components/core/Editor.js +354 -0
  85. package/src/components/core/Viewer.css +109 -0
  86. package/src/components/core/Viewer.js +707 -0
  87. package/src/components/core/index.js +11 -0
  88. package/src/components/index.js +13 -0
  89. package/src/components/layout/CorneredGrid.js +109 -0
  90. package/src/components/layout/Mini.js +117 -0
  91. package/src/components/layout/index.js +7 -0
  92. package/src/components/menus/MapBackground.js +106 -0
  93. package/src/components/menus/MapFilters.js +386 -0
  94. package/src/components/menus/MapLayers.js +143 -0
  95. package/src/components/menus/MapLegend.js +54 -0
  96. package/src/components/menus/PictureLegend.js +103 -0
  97. package/src/components/menus/PictureMetadata.js +188 -0
  98. package/src/components/menus/PlayerOptions.js +96 -0
  99. package/src/components/menus/QualityScoreDoc.js +36 -0
  100. package/src/components/menus/ReportForm.js +133 -0
  101. package/src/components/menus/Share.js +228 -0
  102. package/src/components/menus/index.js +15 -0
  103. package/src/components/styles.js +365 -0
  104. package/src/components/ui/Button.js +75 -0
  105. package/src/components/ui/ButtonGroup.css +49 -0
  106. package/src/components/ui/ButtonGroup.js +68 -0
  107. package/src/components/ui/CopyButton.js +71 -0
  108. package/src/components/ui/Grade.js +54 -0
  109. package/src/components/ui/LinkButton.js +68 -0
  110. package/src/components/ui/Loader.js +188 -0
  111. package/src/components/{Map.css → ui/Map.css} +5 -17
  112. package/src/components/{Map.js → ui/Map.js} +114 -138
  113. package/src/components/ui/MapMore.js +324 -0
  114. package/src/components/{Photo.css → ui/Photo.css} +6 -6
  115. package/src/components/{Photo.js → ui/Photo.js} +279 -90
  116. package/src/components/ui/Popup.js +145 -0
  117. package/src/components/ui/QualityScore.js +152 -0
  118. package/src/components/ui/SearchBar.js +363 -0
  119. package/src/components/ui/TogglableGroup.js +162 -0
  120. package/src/components/ui/index.js +20 -0
  121. package/src/components/ui/widgets/GeoSearch.css +21 -0
  122. package/src/components/ui/widgets/GeoSearch.js +139 -0
  123. package/src/components/ui/widgets/Legend.js +51 -0
  124. package/src/components/ui/widgets/MapFiltersButton.js +104 -0
  125. package/src/components/ui/widgets/MapLayersButton.js +79 -0
  126. package/src/components/ui/widgets/Player.css +7 -0
  127. package/src/components/ui/widgets/Player.js +148 -0
  128. package/src/components/ui/widgets/Share.js +30 -0
  129. package/src/components/ui/widgets/Zoom.js +82 -0
  130. package/src/components/ui/widgets/index.js +12 -0
  131. package/src/img/panoramax.svg +13 -0
  132. package/src/img/switch_big.svg +20 -10
  133. package/src/index.js +6 -9
  134. package/src/translations/da.json +1 -1
  135. package/src/translations/de.json +1 -1
  136. package/src/translations/en.json +5 -3
  137. package/src/translations/eo.json +1 -1
  138. package/src/translations/es.json +1 -1
  139. package/src/translations/fr.json +5 -3
  140. package/src/translations/hu.json +1 -1
  141. package/src/translations/it.json +1 -1
  142. package/src/translations/ja.json +1 -1
  143. package/src/translations/nl.json +1 -1
  144. package/src/translations/pl.json +1 -1
  145. package/src/translations/sv.json +33 -3
  146. package/src/translations/zh_Hant.json +1 -1
  147. package/src/utils/API.js +74 -42
  148. package/src/utils/InitParameters.js +354 -0
  149. package/src/utils/URLHandler.js +364 -0
  150. package/src/utils/geocoder.js +116 -0
  151. package/src/utils/{I18n.js → i18n.js} +3 -1
  152. package/src/utils/index.js +11 -0
  153. package/src/utils/{Map.js → map.js} +216 -80
  154. package/src/utils/picture.js +433 -0
  155. package/src/utils/utils.js +315 -0
  156. package/src/utils/widgets.js +93 -0
  157. package/tests/components/ui/CopyButton.test.js +52 -0
  158. package/tests/components/ui/Loader.test.js +54 -0
  159. package/tests/components/{Map.test.js → ui/Map.test.js} +19 -61
  160. package/tests/components/{Photo.test.js → ui/Photo.test.js} +89 -57
  161. package/tests/components/ui/Popup.test.js +24 -0
  162. package/tests/components/ui/QualityScore.test.js +17 -0
  163. package/tests/components/ui/SearchBar.test.js +107 -0
  164. package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +34 -0
  165. package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
  166. package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
  167. package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +57 -4
  168. package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
  169. package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
  170. package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
  171. package/tests/utils/API.test.js +1 -14
  172. package/tests/utils/InitParameters.test.js +485 -0
  173. package/tests/utils/URLHandler.test.js +350 -0
  174. package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
  175. package/tests/utils/__snapshots__/picture.test.js.snap +315 -0
  176. package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
  177. package/tests/utils/geocoder.test.js +37 -0
  178. package/tests/utils/{I18n.test.js → i18n.test.js} +1 -1
  179. package/tests/utils/map.test.js +67 -0
  180. package/tests/utils/picture.test.js +745 -0
  181. package/tests/utils/utils.test.js +288 -0
  182. package/tests/utils/widgets.test.js +90 -0
  183. package/docs/01_Start.md +0 -149
  184. package/docs/02_Usage.md +0 -831
  185. package/docs/04_Advanced_examples.md +0 -216
  186. package/src/Editor.css +0 -37
  187. package/src/Editor.js +0 -361
  188. package/src/StandaloneMap.js +0 -114
  189. package/src/Viewer.css +0 -203
  190. package/src/Viewer.js +0 -1246
  191. package/src/components/CoreView.css +0 -70
  192. package/src/components/CoreView.js +0 -175
  193. package/src/components/Loader.css +0 -74
  194. package/src/components/Loader.js +0 -120
  195. package/src/utils/Exif.js +0 -193
  196. package/src/utils/Utils.js +0 -631
  197. package/src/utils/Widgets.js +0 -562
  198. package/src/viewer/URLHash.js +0 -469
  199. package/src/viewer/Widgets.css +0 -880
  200. package/src/viewer/Widgets.js +0 -1470
  201. package/tests/Editor.test.js +0 -126
  202. package/tests/StandaloneMap.test.js +0 -45
  203. package/tests/Viewer.test.js +0 -366
  204. package/tests/__snapshots__/Editor.test.js.snap +0 -298
  205. package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
  206. package/tests/__snapshots__/Viewer.test.js.snap +0 -195
  207. package/tests/components/CoreView.test.js +0 -92
  208. package/tests/components/Loader.test.js +0 -38
  209. package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
  210. package/tests/utils/Exif.test.js +0 -124
  211. package/tests/utils/Map.test.js +0 -113
  212. package/tests/utils/Utils.test.js +0 -300
  213. package/tests/utils/Widgets.test.js +0 -107
  214. package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
  215. package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
  216. package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
  217. package/tests/viewer/URLHash.test.js +0 -559
  218. package/tests/viewer/Widgets.test.js +0 -127
  219. package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
  220. package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
  221. /package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +0 -0
@@ -1,70 +0,0 @@
1
- /* Main container */
2
- .gvs {
3
- container-type: inline-size;
4
- box-sizing: border-box;
5
- }
6
-
7
- .gvs * { box-sizing: border-box; }
8
-
9
- /* Colors */
10
- :root {
11
- --white: #ffffff;
12
- --black: #181818;
13
- --black-pale: #1b1a17;
14
- --red: #f70000;
15
- --red-pale: #ff726f;
16
- --grey: #f5f5f5;
17
- --grey-pale: #cfd2cf;
18
- --grey-semi-dark: #808080;
19
- --grey-dark: #3e3e3e;
20
- --blue: #2954e9;
21
- --blue-dark: #0a1f69;
22
- --blue-semi: #d7dffc;
23
- --blue-pale: #f2f5ff;
24
- --blue-geovisio: #34495e;
25
- --beige: #f5f3ec;
26
- --yellow: #fec868;
27
- --orange: #ff6f00;
28
- --orange-pale: #fffafa;
29
- --green: #7ec636;
30
- --green-pale: #f0ffee;
31
- }
32
-
33
- /* Titles */
34
- .gvs h3 {
35
- font-size: 1.1em;
36
- line-height: 1.1em;
37
- font-weight: 500;
38
- margin: 10px 0 10px 0;
39
- }
40
-
41
- .gvs h4 {
42
- font-size: 1.0em;
43
- line-height: 1.0em;
44
- font-weight: 500;
45
- margin: 15px 0;
46
- }
47
-
48
- .gvs h4:first-of-type { margin-top: 0; }
49
-
50
- .gvs h4 svg {
51
- height: 18px;
52
- vertical-align: sub;
53
- margin-right: 2px;
54
- }
55
-
56
- .gvs h4 a svg {
57
- height: 16px;
58
- vertical-align: sub;
59
- margin-left: 2px;
60
- }
61
-
62
- /* Hidden elements on mobile */
63
- @container (max-width: 576px) {
64
- .gvs-mobile-hidden { display: none !important; }
65
- }
66
-
67
- /* Hidden elements on print */
68
- @media print {
69
- .gvs-print-hidden { display: none !important; }
70
- }
@@ -1,175 +0,0 @@
1
- import "./CoreView.css";
2
- import API from "../utils/API";
3
- import { getTranslations } from "../utils/I18n";
4
- import { DEFAULT_TILES } from "../utils/Map";
5
- import { BASE_PANORAMA_ID, isInIframe, isInternetFast } from "../utils/Utils";
6
- import PACKAGE_JSON from "../../package.json";
7
- import Loader from "./Loader";
8
-
9
-
10
- /**
11
- * Core view is an abstract class used for setting up any of the main Panoramax JS view components.
12
- *
13
- * It is used to prepare API, internationalization, options checks... for Viewer, StandaloneMap and Editor classes.
14
- *
15
- * @param {string|Element} container The DOM element to create viewer into
16
- * @param {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
17
- * @param {object} [options] View options.
18
- * @param {string} [options.selectedSequence] The ID of sequence to highlight on load (defaults to none)
19
- * @param {string} [options.selectedPicture] The ID of picture to highlight on load (defaults to none)
20
- * @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))
21
- * @param {string|string[]} [options.users] List of user IDs to default use for display. Defaults to all users.
22
- * @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. Defaults to OSMFR vector tiles.
23
- *
24
- * @property {object} _t The translations labels
25
- * @property {string} _selectedSeqId The selected sequence ID
26
- * @property {string} _selectedPicId The selected picture ID
27
- * @property {API} _api The API handler
28
- * @property {Loader} _loader The initial loader message
29
- * @property {object} _options The stored options
30
- * @property {Element} container The DOM container
31
- */
32
- export default class CoreView extends EventTarget {
33
- constructor(container, endpoint, options = {}) {
34
- super();
35
-
36
- this._options = options;
37
- if(this._options == null) { this._options = {}; }
38
- if(!this._options.users) { this._options.users = ["geovisio"]; }
39
- if(typeof this._options.users === "string") { this._options.users = [this._options.users]; }
40
- if(!this._options.style) { this._options.style = DEFAULT_TILES; }
41
-
42
- if(!this._options.testing) {
43
- // Display version in logs
44
- console.info(`📷 Panoramax ${this.getClassName()} - Version ${PACKAGE_JSON.version} (${__COMMIT_HASH__})
45
-
46
- 🆘 Issues can be reported at ${PACKAGE_JSON.repository.url}`);
47
- }
48
-
49
- // Translations
50
- this._t = getTranslations(this._options.lang);
51
-
52
- // Internet speed check
53
- this._isInternetFast = null;
54
- isInternetFast().then(isFast => this._isInternetFast = isFast);
55
-
56
- // Selected IDs
57
- this._selectedSeqId = this._options.selectedSequence || null;
58
- this._selectedPicId = this._options.selectedPicture || null;
59
-
60
- // Container init
61
- this.container = typeof container === "string" ? document.getElementById(container) : container;
62
- if(!(this.container instanceof Element)) { throw new Error("Container is not a valid HTML element, does it exist in your page ?"); }
63
- this.container.classList.add("gvs", `gvs-${this.getClassName().toLocaleLowerCase()}`);
64
- if(isInIframe()) { this.container.classList.add("gvs-iframed"); }
65
-
66
- // Loader init
67
- this.loaderContainer = document.createElement("div");
68
- this.container.appendChild(this.loaderContainer);
69
- this._loader = new Loader(this, this.loaderContainer);
70
-
71
- // API init)
72
- endpoint = (endpoint || "").replace("/api/search", "/api");
73
- try {
74
- this._api = new API(endpoint, {
75
- users: this._options.users,
76
- fetch: this._options?.fetchOptions,
77
- style: this._options.style,
78
- });
79
- this._api.onceReady()
80
- .then(() => {
81
- let unavailable = this._api.getUnavailableFeatures();
82
- let available = this._api.getAvailableFeatures();
83
- available = unavailable.length === 0 ? "✅ All features available" : "✅ Available features: "+available.join(", ");
84
- unavailable = unavailable.length === 0 ? "" : "🚫 Unavailable features: "+unavailable.join(", ");
85
- console.info(`🌐 Connected to API "${this._api._metadata.name}" (${this._api._endpoint})
86
- ℹ️ API runs STAC ${this._api._metadata.stac_version} ${this._api._metadata.geovisio_version ? "& GeoVisio "+this._api._metadata.geovisio_version : ""}
87
- ${available}
88
- ${unavailable}
89
- `.trim());
90
- })
91
- .catch(e => this._loader.dismiss(e, this._t.gvs.error_api));
92
- }
93
- catch(e) {
94
- this._loader.dismiss(e, this._t.gvs.error_api);
95
- }
96
- }
97
-
98
- /**
99
- * This allows to retrieve an always correct class name.
100
- * This is crap, but avoids issues with Webpack & so on.
101
- *
102
- * Each inheriting class must override this method.
103
- */
104
- getClassName() {
105
- return "CoreView";
106
- }
107
-
108
- /**
109
- * Ends all form of life in this object.
110
- *
111
- * This is useful for Single Page Applications (SPA), to remove various event listeners.
112
- */
113
- destroy() {
114
- delete this._options;
115
- delete this._t;
116
- delete this._api;
117
- delete this._loader;
118
- this.loaderContainer.remove();
119
- delete this.loaderContainer;
120
- }
121
-
122
- /**
123
- * Is the view running in a small container (small embed or smartphone)
124
- * @returns {boolean} True if container is small
125
- */
126
- isWidthSmall() {
127
- return this.container?.offsetWidth < 576;
128
- }
129
-
130
- /**
131
- * Is the view running in a small-height container (small embed or smartphone)
132
- * @returns {boolean} True if container height is small
133
- */
134
- isHeightSmall() {
135
- return this.container?.offsetHeight < 400;
136
- }
137
-
138
- /**
139
- * Change the currently picture and/or sequence.
140
- * Calling the method without parameters unselects.
141
- * @param {string} [seqId] The sequence UUID
142
- * @param {string} [picId] The picture UUID
143
- * @param {boolean} [force=false] Force select even if already selected
144
- */
145
- select(seqId = null, picId = null, force = false) {
146
- if(picId === BASE_PANORAMA_ID) { picId = null; }
147
- const prevSeqId = this._selectedSeqId || null;
148
- const prevPicId = this._selectedPicId || null;
149
- if(!force && prevPicId == picId && prevSeqId == seqId) { return; } // Avoid running if already selected
150
-
151
- this._selectedSeqId = seqId;
152
- this._selectedPicId = picId;
153
-
154
- /**
155
- * Event for sequence/picture selection
156
- *
157
- * @event select
158
- * @memberof CoreView
159
- * @type {object}
160
- * @property {object} detail Event information
161
- * @property {string} detail.seqId The selected sequence ID
162
- * @property {string} detail.picId The selected picture ID (or null if not a precise picture clicked)
163
- * @property {string} [detail.prevSeqId] The previously selected sequence ID (or null if none)
164
- * @property {string} [detail.prevPicId] The previously selected picture ID (or null if none)
165
- */
166
- this.dispatchEvent(new CustomEvent("select", {
167
- detail: {
168
- seqId,
169
- picId,
170
- prevSeqId,
171
- prevPicId,
172
- }
173
- }));
174
- }
175
- }
@@ -1,74 +0,0 @@
1
- .gvs .gvs-loader {
2
- position: relative;
3
- width: 100%;
4
- height: 100%;
5
- z-index: 0;
6
- }
7
-
8
- .gvs .gvs-loader.gvs-loader-visible {
9
- visibility: visible;
10
- opacity: 1;
11
- }
12
-
13
- /* Loader overlay */
14
- .gvs .gvs-loader {
15
- visibility: hidden;
16
- position: absolute;
17
- top: 0;
18
- right: 0;
19
- left: 0;
20
- bottom: 0;
21
- opacity: 0;
22
- display: flex;
23
- flex-direction: column;
24
- justify-content: center;
25
- gap: 20px;
26
- align-items: center;
27
- background: #37474F;
28
- z-index: 9000;
29
- transition: all 0.5s;
30
- font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
31
- font-weight: 550;
32
- color: white;
33
- font-size: 1.4em;
34
- text-align: center;
35
- }
36
-
37
- .gvs .gvs-loader a {
38
- color: white !important;
39
- }
40
-
41
- /* Flashing text */
42
- .gvs .gvs-loader > div > span {
43
- animation: blinker 2s linear infinite;
44
- }
45
- @keyframes blinker { 50% { opacity: 0.3; } }
46
-
47
- /* Rotating logo */
48
- .gvs .gvs-loader .gvs-loader-img {
49
- width: 150px;
50
- animation: rotating 1.4s linear infinite;
51
- }
52
-
53
- @keyframes rotating {
54
- from { transform: rotate(0deg); }
55
- to { transform: rotate(360deg); }
56
- }
57
-
58
- /* Call to action button */
59
- .gvs .gvs-loader .gvs-loader-cta {
60
- border: none;
61
- border-radius: 7px;
62
- font-weight: 500;
63
- font-size: 1.0em;
64
- color: white;
65
- background-color: #EF6C00;
66
- cursor: pointer;
67
- margin: 10px;
68
- padding: 5px 30px;
69
- box-shadow: 2px 2px 2px rgba(0,0,0,0.3);
70
- }
71
-
72
- .gvs .gvs-loader .gvs-loader-cta:hover {
73
- background-color: #F57C00;
74
- }
@@ -1,120 +0,0 @@
1
- import "./Loader.css";
2
- import LogoDead from "../img/logo_dead.svg";
3
- import LoaderImg from "../img/marker.svg";
4
-
5
- /**
6
- * Loader is a full-screen loading message.
7
- * @private
8
- *
9
- * @param {CoreView} parent The parent view
10
- * @param {Element} container The DOM element to create loader into
11
- */
12
- export default class Loader {
13
- constructor(parent, container) {
14
- this._parent = parent;
15
- this.container = container;
16
- this.container.classList.add("gvs-loader", "gvs-loader-visible");
17
-
18
- // Logo
19
- const logo = document.createElement("img");
20
- logo.src = LoaderImg;
21
- logo.alt = "";
22
- logo.title = this._parent._t.map.loading;
23
- logo.classList.add("gvs-loader-img");
24
- this.container.appendChild(logo);
25
-
26
- // Label (1 serious, then fun ones)
27
- const labelWrapper = document.createElement("div");
28
- const label = document.createElement("span");
29
- const nextLabelFun = () => (
30
- this._parent._t.gvs.loading_labels_fun[
31
- Math.floor(Math.random() * this._parent._t.gvs.loading_labels_fun.length)
32
- ]
33
- );
34
- label.innerHTML = this._parent._t.gvs.loading_labels_serious[
35
- Math.floor(Math.random() * this._parent._t.gvs.loading_labels_serious.length)
36
- ];
37
- const nextLabelFct = () => setTimeout(() => {
38
- label.innerHTML = nextLabelFun();
39
- this._loaderLabelChanger = nextLabelFct();
40
- }, 500 + Math.random() * 1000);
41
- this._loaderLabelChanger = nextLabelFct();
42
- labelWrapper.appendChild(label);
43
-
44
- this.container.appendChild(labelWrapper);
45
- }
46
-
47
- /**
48
- * Is the loader visible ?
49
- * @returns {boolean} True if visible
50
- */
51
- isVisible() {
52
- return this.container.classList.contains("gvs-loader-visible");
53
- }
54
-
55
- /**
56
- * Dismiss loader, or show error
57
- * @param {object} [err] Optional error object to show in browser console
58
- * @param {str} [errMeaningful] Optional error message to show to user
59
- * @param {fct} [next] Optional function to run after loader dismiss
60
- */
61
- dismiss(err = null, errMeaningful = null, next = null) {
62
- clearTimeout(this._loaderLabelChanger);
63
-
64
- if(!err) {
65
- this.container.classList.remove("gvs-loader-visible");
66
- setTimeout(() => this.container.remove(), 2000);
67
-
68
- /**
69
- * Event for viewer being ready to use (API loaded)
70
- *
71
- * @event ready
72
- * @memberof CoreView
73
- */
74
- const readyEvt = new CustomEvent("ready");
75
- this._parent.dispatchEvent(readyEvt);
76
-
77
- if(next) { next(); }
78
- }
79
- else {
80
- if(err !== true) { console.error(err); }
81
-
82
- // Change content
83
- this.container.children[0].src = LogoDead;
84
- this.container.children[0].style.width = "200px";
85
- this.container.children[0].style.animation = "unset";
86
-
87
- let errHtml;
88
- if(next) {
89
- errHtml = `<button class="gvs-loader-cta">${this._parent._t.gvs.error_click}</button>`;
90
- }
91
- else {
92
- errHtml = `<small>${this._parent._t.gvs.error_retry}</small>`;
93
- }
94
-
95
- if(errMeaningful) { errHtml = errMeaningful + "<br />" + errHtml; }
96
- this.container.children[1].innerHTML = `${this._parent._t.gvs.error}<br />${errHtml}`;
97
- if(next) {
98
- this.container.addEventListener("click", next);
99
- }
100
- const errLabel = errMeaningful || "Panoramax JS had a blocking exception";
101
-
102
- /**
103
- * Event for viewer failing to initially load
104
- *
105
- * @event broken
106
- * @memberof CoreView
107
- * @type {object}
108
- * @property {object} detail Event information
109
- * @property {string} detail.error The user-friendly error message to display
110
- */
111
- const brokenEvt = new CustomEvent("broken", {
112
- detail: { error: errLabel }
113
- });
114
- this._parent.dispatchEvent(brokenEvt);
115
-
116
- // Throw error
117
- throw new Error(errLabel);
118
- }
119
- }
120
- }
package/src/utils/Exif.js DELETED
@@ -1,193 +0,0 @@
1
- /**
2
- * Read float value from EXIF tags (to handle fractions & all)
3
- * @param {*} val The input EXIF tag value
4
- * @returns {number|undefined} The parsed value, or undefined if value is not readable
5
- * @private
6
- */
7
- export function getExifFloat(val) {
8
- // Null-like values
9
- if(
10
- [null, undefined, ""].includes(val)
11
- || typeof val === "string" && val.trim() === ""
12
- ) {
13
- return undefined;
14
- }
15
- // Already valid number
16
- else if(typeof val === "number") {
17
- return val;
18
- }
19
- // String
20
- else if(typeof val === "string") {
21
- // Check if looks like a fraction
22
- if(/^-?\d+(\.\d+)?\/-?\d+(\.\d+)?$/.test(val)) {
23
- const parts = val.split("/").map(p => parseFloat(p));
24
- return parts[0] / parts[1];
25
- }
26
-
27
- // Try a direct cast to float
28
- try { return parseFloat(val); }
29
- catch(e) {} // eslint-disable-line no-empty
30
-
31
- // Unrecognized
32
- return undefined;
33
- }
34
- else { return undefined; }
35
- }
36
-
37
- /**
38
- * Find in picture metadata the GPS precision.
39
- * @param {object} picture The GeoJSON picture feature
40
- * @returns {string} The precision value (poor, fair, moderate, good, excellent, ideal, unknown)
41
- * @private
42
- */
43
- export function getGPSPrecision(picture) {
44
- let quality = "❓";
45
- const gpsHPosError = picture?.properties?.["quality:horizontal_accuracy"] || getExifFloat(picture?.properties?.exif?.["Exif.GPSInfo.GPSHPositioningError"]);
46
- const gpsDop = getExifFloat(picture?.properties?.exif?.["Exif.GPSInfo.GPSDOP"]);
47
-
48
- if(gpsHPosError !== undefined) {
49
- quality = `${gpsHPosError} m`;
50
- }
51
- else if(gpsDop !== undefined) {
52
- if(gpsDop < 1) { quality = "ideal"; }
53
- else if(gpsDop < 2) { quality = "excellent"; }
54
- else if(gpsDop < 5) { quality = "good"; }
55
- else if(gpsDop < 10) { quality = "moderate"; }
56
- else if(gpsDop < 20) { quality = "fair"; }
57
- else { quality = "poor"; }
58
- }
59
-
60
- return quality;
61
- }
62
-
63
- /**
64
- * Compute PSV sphere correction based on picture metadata & EXIF tags.
65
- * @param {object} picture The GeoJSON picture feature
66
- * @returns {object} The PSV sphereCorrection value
67
- * @private
68
- */
69
- export function getSphereCorrection(picture) {
70
- // Photo direction
71
- let dir = picture.properties?.["view:azimuth"];
72
- if(dir === undefined) {
73
- const v = getExifFloat(picture.properties?.exif?.["Exif.GPSInfo.GPSImgDirection"]);
74
- if(v !== undefined) {
75
- dir = v;
76
- }
77
- }
78
- dir = dir || 0;
79
-
80
- // Yaw
81
- let yaw = picture.properties?.["pers:yaw"];
82
- let exifFallbacks = ["Xmp.GPano.PoseHeadingDegrees", "Xmp.Camera.Yaw", "Exif.MpfInfo.MPFYawAngle"];
83
- if(yaw === undefined) {
84
- for(let exif of exifFallbacks) {
85
- const v = getExifFloat(picture.properties?.exif?.[exif]);
86
- if(v !== undefined) {
87
- yaw = v;
88
- break;
89
- }
90
- }
91
- }
92
- yaw = yaw || 0;
93
-
94
- // Check if yaw is applicable: different from photo direction
95
- if(Math.round(dir) === Math.round(yaw) && yaw > 0) {
96
- console.warn("Picture with UUID", picture.id, "has same GPS Image direction and Yaw, could cause rendering issues");
97
- // yaw = 0;
98
- }
99
-
100
- // Pitch
101
- let pitch = picture.properties?.["pers:pitch"];
102
- exifFallbacks = ["Xmp.GPano.PosePitchDegrees", "Xmp.Camera.Pitch", "Exif.MpfInfo.MPFPitchAngle"];
103
- if(pitch === undefined) {
104
- for(let exif of exifFallbacks) {
105
- const v = getExifFloat(picture.properties?.exif?.[exif]);
106
- if(v !== undefined) {
107
- pitch = v;
108
- break;
109
- }
110
- }
111
- }
112
- pitch = pitch || 0;
113
-
114
- // Roll
115
- let roll = picture.properties?.["pers:roll"];
116
- exifFallbacks = ["Xmp.GPano.PoseRollDegrees", "Xmp.Camera.Roll", "Exif.MpfInfo.MPFRollAngle"];
117
- if(roll === undefined) {
118
- for(let exif of exifFallbacks) {
119
- const v = getExifFloat(picture.properties?.exif?.[exif]);
120
- if(v !== undefined) {
121
- roll = v;
122
- break;
123
- }
124
- }
125
- }
126
- roll = roll || 0;
127
-
128
- // Send result
129
- return pitch !== 0 && roll !== 0 ? {
130
- pan: yaw * Math.PI / 180,
131
- tilt: pitch * Math.PI / 180,
132
- roll: roll * Math.PI / 180,
133
- } : {};
134
- }
135
-
136
- /**
137
- * Compute PSV panoData for cropped panorama based on picture metadata & EXIF tags.
138
- * @param {object} picture The GeoJSON picture feature
139
- * @returns {object} The PSV panoData values
140
- * @private
141
- */
142
- export function getCroppedPanoData(picture) {
143
- let res;
144
-
145
- if(picture.properties?.["pers:interior_orientation"]) {
146
- if(
147
- picture.properties["pers:interior_orientation"]?.["visible_area"]
148
- && picture.properties["pers:interior_orientation"]?.["sensor_array_dimensions"]
149
- ) {
150
- const va = picture.properties["pers:interior_orientation"]["visible_area"];
151
- const sad = picture.properties["pers:interior_orientation"]["sensor_array_dimensions"];
152
- try {
153
- res = {
154
- fullWidth: parseInt(sad[0]),
155
- fullHeight: parseInt(sad[1]),
156
- croppedX: parseInt(va[0]),
157
- croppedY: parseInt(va[1]),
158
- croppedWidth: parseInt(sad[0]) - parseInt(va[2]) - parseInt(va[0]),
159
- croppedHeight: parseInt(sad[1]) - parseInt(va[3]) - parseInt(va[1]),
160
- };
161
- }
162
- catch(e) {
163
- console.warn("Invalid pers:interior_orientation values for cropped panorama "+picture.id);
164
- }
165
- }
166
- }
167
-
168
- if(!res && picture.properties?.exif) {
169
- try {
170
- res = {
171
- fullWidth: parseInt(picture.properties.exif?.["Xmp.GPano.FullPanoWidthPixels"]),
172
- fullHeight: parseInt(picture.properties.exif?.["Xmp.GPano.FullPanoHeightPixels"]),
173
- croppedX: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaLeftPixels"]),
174
- croppedY: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaTopPixels"]),
175
- croppedWidth: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaImageWidthPixels"]),
176
- croppedHeight: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaImageHeightPixels"]),
177
- };
178
- }
179
- catch(e) {
180
- console.warn("Invalid XMP.GPano values for cropped panorama "+picture.id);
181
- }
182
- }
183
-
184
- // Check if crop is really necessary
185
- if(res) {
186
- res = Object.fromEntries(Object.entries(res || {}).filter(e => !isNaN(e[1])));
187
- if(res.fullWidth == res.croppedWidth && res.fullHeight == res.croppedHeight) {
188
- res = {};
189
- }
190
- }
191
-
192
- return res || {};
193
- }