@panoramax/web-viewer 5.0.0-develop-d26305dd → 5.0.0-develop-be5ba1a7

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 (153) hide show
  1. package/build/cjs/index.js +1 -1
  2. package/build/cjs/index_photoviewer.js +1 -1
  3. package/build/esm/components/core/Basic.js +1 -1
  4. package/build/esm/translations/el.json +92 -1
  5. package/package.json +1 -1
  6. package/build/bundle.cjs +0 -3399
  7. package/build/bundle.cjs.map +0 -1
  8. package/build/bundle_photoviewer.cjs +0 -2510
  9. package/build/bundle_photoviewer.cjs.map +0 -1
  10. package/build/components/core/Basic.css +0 -56
  11. package/build/components/core/Basic.js +0 -378
  12. package/build/components/core/CoverageMap.css +0 -10
  13. package/build/components/core/CoverageMap.js +0 -169
  14. package/build/components/core/Editor.css +0 -33
  15. package/build/components/core/Editor.js +0 -398
  16. package/build/components/core/PhotoViewer.css +0 -70
  17. package/build/components/core/PhotoViewer.js +0 -650
  18. package/build/components/core/Viewer.css +0 -130
  19. package/build/components/core/Viewer.js +0 -711
  20. package/build/components/core/index.js +0 -10
  21. package/build/components/index.js +0 -11
  22. package/build/components/index_photoviewer.js +0 -6
  23. package/build/components/layout/BottomDrawer.js +0 -258
  24. package/build/components/layout/CorneredGrid.js +0 -143
  25. package/build/components/layout/Mini.js +0 -121
  26. package/build/components/layout/Tabs.js +0 -140
  27. package/build/components/layout/index.js +0 -9
  28. package/build/components/menus/LocationPrecisionDoc.js +0 -42
  29. package/build/components/menus/MapBackground.js +0 -110
  30. package/build/components/menus/MapFilters.js +0 -567
  31. package/build/components/menus/MapLayers.js +0 -238
  32. package/build/components/menus/MapLegend.js +0 -68
  33. package/build/components/menus/MiniPictureLegend.js +0 -73
  34. package/build/components/menus/PictureLegend.js +0 -379
  35. package/build/components/menus/PictureMetadata.js +0 -380
  36. package/build/components/menus/PlayerOptions.js +0 -93
  37. package/build/components/menus/QualityScoreDoc.js +0 -42
  38. package/build/components/menus/ReportForm.js +0 -132
  39. package/build/components/menus/SemanticsDoc.js +0 -38
  40. package/build/components/menus/SemanticsDownload.js +0 -33
  41. package/build/components/menus/SemanticsFilters.js +0 -153
  42. package/build/components/menus/SemanticsList.js +0 -413
  43. package/build/components/menus/SemanticsMetadata.js +0 -368
  44. package/build/components/menus/Share.js +0 -105
  45. package/build/components/menus/index.js +0 -22
  46. package/build/components/menus/index_photoviewer.js +0 -11
  47. package/build/components/styles.js +0 -557
  48. package/build/components/ui/AnnotationsSwitch.js +0 -159
  49. package/build/components/ui/Button.js +0 -77
  50. package/build/components/ui/ButtonGroup.css +0 -59
  51. package/build/components/ui/ButtonGroup.js +0 -69
  52. package/build/components/ui/CopyButton.js +0 -110
  53. package/build/components/ui/Grade.js +0 -54
  54. package/build/components/ui/GradeFilter.js +0 -122
  55. package/build/components/ui/IconSwitch.js +0 -193
  56. package/build/components/ui/LinkButton.js +0 -67
  57. package/build/components/ui/ListGroup.js +0 -66
  58. package/build/components/ui/ListItem.js +0 -90
  59. package/build/components/ui/Loader.js +0 -203
  60. package/build/components/ui/Map.css +0 -63
  61. package/build/components/ui/Map.js +0 -853
  62. package/build/components/ui/MapMore.js +0 -175
  63. package/build/components/ui/Photo.css +0 -50
  64. package/build/components/ui/Photo.js +0 -1502
  65. package/build/components/ui/Popup.js +0 -145
  66. package/build/components/ui/ProgressBar.js +0 -104
  67. package/build/components/ui/QualityScore.js +0 -147
  68. package/build/components/ui/SearchBar.js +0 -374
  69. package/build/components/ui/SemanticsEditor.js +0 -191
  70. package/build/components/ui/SemanticsTable.js +0 -88
  71. package/build/components/ui/Switch.js +0 -139
  72. package/build/components/ui/TogglableGroup.js +0 -157
  73. package/build/components/ui/index.js +0 -29
  74. package/build/components/ui/index_photoviewer.js +0 -21
  75. package/build/components/ui/widgets/CopyCoordinates.js +0 -75
  76. package/build/components/ui/widgets/GeoSearch.css +0 -21
  77. package/build/components/ui/widgets/GeoSearch.js +0 -150
  78. package/build/components/ui/widgets/Legend.js +0 -190
  79. package/build/components/ui/widgets/LevelSelect.css +0 -51
  80. package/build/components/ui/widgets/LevelSelect.js +0 -143
  81. package/build/components/ui/widgets/MapFiltersButton.js +0 -114
  82. package/build/components/ui/widgets/MapLayersButton.js +0 -79
  83. package/build/components/ui/widgets/OSMEditors.js +0 -155
  84. package/build/components/ui/widgets/PictureLegendActions.js +0 -99
  85. package/build/components/ui/widgets/Player.css +0 -7
  86. package/build/components/ui/widgets/Player.js +0 -154
  87. package/build/components/ui/widgets/SemanticsFiltersButton.js +0 -65
  88. package/build/components/ui/widgets/Zoom.js +0 -84
  89. package/build/components/ui/widgets/index.js +0 -16
  90. package/build/components/ui/widgets/index_photoviewer.js +0 -7
  91. package/build/img/arrow_360.svg +0 -14
  92. package/build/img/arrow_flat.svg +0 -11
  93. package/build/img/arrow_triangle.svg +0 -9
  94. package/build/img/arrow_turn.svg +0 -8
  95. package/build/img/bg_aerial.jpg +0 -0
  96. package/build/img/bg_streets.jpg +0 -0
  97. package/build/img/loader_base.jpg +0 -0
  98. package/build/img/logo_dead.svg +0 -91
  99. package/build/img/marker.svg +0 -17
  100. package/build/img/marker_blue.svg +0 -20
  101. package/build/img/osm.svg +0 -49
  102. package/build/img/panoramax.svg +0 -13
  103. package/build/img/switch_big.svg +0 -54
  104. package/build/img/switch_mini.svg +0 -48
  105. package/build/img/wd.svg +0 -1
  106. package/build/index_photoviewer.js +0 -4
  107. package/build/package.json +0 -148
  108. package/build/servers.js +0 -14
  109. package/build/translations/ar.json +0 -1
  110. package/build/translations/be.json +0 -257
  111. package/build/translations/br.json +0 -81
  112. package/build/translations/cy.json +0 -117
  113. package/build/translations/da.json +0 -300
  114. package/build/translations/de.json +0 -309
  115. package/build/translations/en.json +0 -294
  116. package/build/translations/eo.json +0 -235
  117. package/build/translations/es.json +0 -292
  118. package/build/translations/fi.json +0 -1
  119. package/build/translations/fr.json +0 -294
  120. package/build/translations/hr.json +0 -294
  121. package/build/translations/hu.json +0 -294
  122. package/build/translations/it.json +0 -306
  123. package/build/translations/ja.json +0 -182
  124. package/build/translations/ko.json +0 -1
  125. package/build/translations/nl.json +0 -305
  126. package/build/translations/nn.json +0 -1
  127. package/build/translations/pl.json +0 -169
  128. package/build/translations/pt.json +0 -296
  129. package/build/translations/pt_BR.json +0 -304
  130. package/build/translations/sv.json +0 -182
  131. package/build/translations/ti.json +0 -9
  132. package/build/translations/tr.json +0 -297
  133. package/build/translations/uk.json +0 -268
  134. package/build/translations/zh_Hant.json +0 -309
  135. package/build/utils/API.js +0 -928
  136. package/build/utils/InitParameters.js +0 -521
  137. package/build/utils/MapStyleComposer.js +0 -889
  138. package/build/utils/PanoraMapProtocol.js +0 -49
  139. package/build/utils/PhotoAdapter.js +0 -49
  140. package/build/utils/PresetsManager.js +0 -148
  141. package/build/utils/SemanticsMapProtocol.js +0 -144
  142. package/build/utils/URLHandler.js +0 -426
  143. package/build/utils/geocoder.js +0 -203
  144. package/build/utils/i18n.js +0 -128
  145. package/build/utils/index.js +0 -17
  146. package/build/utils/index_photoviewer.js +0 -14
  147. package/build/utils/indoor.js +0 -200
  148. package/build/utils/map.js +0 -788
  149. package/build/utils/picture.js +0 -507
  150. package/build/utils/semantics.js +0 -321
  151. package/build/utils/services.js +0 -148
  152. package/build/utils/utils.js +0 -433
  153. package/build/utils/widgets.js +0 -110
@@ -1,507 +0,0 @@
1
- import { COLORS, getArrow, getDistance, getSimplifiedAngle, svgToPSVLink } from "./utils.js";
2
- const ArrowTriangleSVG = await fetch(new URL("../img/arrow_triangle.svg", import.meta.url).href).then(res => res.text());
3
- const ArrowTurnSVG = await fetch(new URL("../img/arrow_turn.svg", import.meta.url).href).then(res => res.text());
4
- const ArrowTriangle = svgToPSVLink(ArrowTriangleSVG, "white");
5
- const ArrowTurn = svgToPSVLink(ArrowTurnSVG, COLORS.NEXT);
6
-
7
- /**
8
- * Read float value from EXIF tags (to handle fractions & all)
9
- * @param {*} val The input EXIF tag value
10
- * @returns {number|undefined} The parsed value, or undefined if value is not readable
11
- * @private
12
- */
13
- export function getExifFloat(val) {
14
- // Null-like values
15
- if(
16
- [null, undefined, ""].includes(val)
17
- || typeof val === "string" && val.trim() === ""
18
- ) {
19
- return undefined;
20
- }
21
- // Already valid number
22
- else if(typeof val === "number") {
23
- return val;
24
- }
25
- // String
26
- else if(typeof val === "string") {
27
- // Check if looks like a fraction
28
- if(/^-?\d+(\.\d+)?\/-?\d+(\.\d+)?$/.test(val)) {
29
- const parts = val.split("/").map(p => parseFloat(p));
30
- return parts[0] / parts[1];
31
- }
32
-
33
- // Try a direct cast to float
34
- try { return parseFloat(val); }
35
-
36
- catch(e) {}
37
-
38
- // Unrecognized
39
- return undefined;
40
- }
41
- else { return undefined; }
42
- }
43
-
44
- /**
45
- * Find in picture metadata the GPS precision.
46
- * @param {object} picture The GeoJSON picture feature
47
- * @returns {string} The precision value (poor, fair, moderate, good, excellent, ideal, unknown)
48
- * @private
49
- */
50
- export function getGPSPrecision(picture) {
51
- let quality = null;
52
- const gpsHPosError = picture?.properties?.["quality:horizontal_accuracy"] || getExifFloat(picture?.properties?.exif?.["Exif.GPSInfo.GPSHPositioningError"]);
53
- const gpsDop = getExifFloat(picture?.properties?.exif?.["Exif.GPSInfo.GPSDOP"]);
54
-
55
- if(gpsHPosError !== undefined) {
56
- quality = `${gpsHPosError} m`;
57
- }
58
- else if(gpsDop !== undefined) {
59
- if(gpsDop < 1) { quality = "ideal"; }
60
- else if(gpsDop < 2) { quality = "excellent"; }
61
- else if(gpsDop < 5) { quality = "good"; }
62
- else if(gpsDop < 10) { quality = "moderate"; }
63
- else if(gpsDop < 20) { quality = "fair"; }
64
- else { quality = "poor"; }
65
- }
66
-
67
- return quality;
68
- }
69
-
70
- /**
71
- * Compute PSV sphere correction based on picture metadata & EXIF tags.
72
- * @param {object} picture The GeoJSON picture feature
73
- * @returns {object} The PSV sphereCorrection value
74
- * @private
75
- */
76
- export function getSphereCorrection(picture) {
77
- // Photo direction
78
- let dir = picture.properties?.["view:azimuth"];
79
- if(dir === undefined) {
80
- const v = getExifFloat(picture.properties?.exif?.["Exif.GPSInfo.GPSImgDirection"]);
81
- if(v !== undefined) {
82
- dir = v;
83
- }
84
- }
85
- dir = dir || 0;
86
-
87
- // Yaw
88
- let yaw = picture.properties?.["pers:yaw"];
89
- let exifFallbacks = ["Xmp.GPano.PoseHeadingDegrees", "Xmp.Camera.Yaw", "Exif.MpfInfo.MPFYawAngle"];
90
- if(yaw === undefined) {
91
- for(let exif of exifFallbacks) {
92
- const v = getExifFloat(picture.properties?.exif?.[exif]);
93
- if(v !== undefined) {
94
- yaw = v;
95
- break;
96
- }
97
- }
98
- }
99
- yaw = yaw || 0;
100
-
101
- // Check if yaw is applicable: different from photo direction
102
- if(Math.round(dir) === Math.round(yaw) && yaw > 0) {
103
- console.warn("Picture with UUID", picture.id, "has same GPS Image direction and Yaw, could cause rendering issues");
104
- // Yaw = 0;
105
- }
106
-
107
- // Pitch
108
- let pitch = picture.properties?.["pers:pitch"];
109
- exifFallbacks = ["Xmp.GPano.PosePitchDegrees", "Xmp.Camera.Pitch", "Exif.MpfInfo.MPFPitchAngle"];
110
- if(pitch === undefined) {
111
- for(let exif of exifFallbacks) {
112
- const v = getExifFloat(picture.properties?.exif?.[exif]);
113
- if(v !== undefined) {
114
- pitch = v;
115
- break;
116
- }
117
- }
118
- }
119
- pitch = pitch || 0;
120
-
121
- // Roll
122
- let roll = picture.properties?.["pers:roll"];
123
- exifFallbacks = ["Xmp.GPano.PoseRollDegrees", "Xmp.Camera.Roll", "Exif.MpfInfo.MPFRollAngle"];
124
- if(roll === undefined) {
125
- for(let exif of exifFallbacks) {
126
- const v = getExifFloat(picture.properties?.exif?.[exif]);
127
- if(v !== undefined) {
128
- roll = v;
129
- break;
130
- }
131
- }
132
- }
133
- roll = roll || 0;
134
-
135
- // Send result
136
- return pitch !== 0 && roll !== 0 ? {
137
- pan: yaw * Math.PI / 180,
138
- tilt: -pitch * Math.PI / 180,
139
- roll: roll * Math.PI / 180,
140
- } : {};
141
- }
142
-
143
- /**
144
- * Compute PSV panoData for cropped panorama based on picture metadata & EXIF tags.
145
- * @param {object} picture The GeoJSON picture feature
146
- * @param {object} [img] The loaded image file, with width, height properties for possible resizing
147
- * @returns {object} The PSV panoData values
148
- * @private
149
- */
150
- export function getCroppedPanoData(picture, img) {
151
- let res;
152
-
153
- if(picture.properties?.["pers:interior_orientation"]) {
154
- if(
155
- picture.properties["pers:interior_orientation"]?.["visible_area"]
156
- && picture.properties["pers:interior_orientation"]?.["sensor_array_dimensions"]
157
- ) {
158
- const va = picture.properties["pers:interior_orientation"]["visible_area"];
159
- const sad = picture.properties["pers:interior_orientation"]["sensor_array_dimensions"];
160
- try {
161
- res = {
162
- fullWidth: parseInt(sad[0]),
163
- fullHeight: parseInt(sad[1]),
164
- croppedX: parseInt(va[0]),
165
- croppedY: parseInt(va[1]),
166
- croppedWidth: parseInt(sad[0]) - parseInt(va[2]) - parseInt(va[0]),
167
- croppedHeight: parseInt(sad[1]) - parseInt(va[3]) - parseInt(va[1]),
168
- };
169
- }
170
-
171
- catch(e) {
172
- console.warn("Invalid pers:interior_orientation values for cropped panorama "+picture.id);
173
- }
174
- }
175
- }
176
-
177
- if(!res && picture.properties?.exif) {
178
- try {
179
- res = {
180
- fullWidth: parseInt(picture.properties.exif?.["Xmp.GPano.FullPanoWidthPixels"]),
181
- fullHeight: parseInt(picture.properties.exif?.["Xmp.GPano.FullPanoHeightPixels"]),
182
- croppedX: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaLeftPixels"]),
183
- croppedY: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaTopPixels"]),
184
- croppedWidth: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaImageWidthPixels"]),
185
- croppedHeight: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaImageHeightPixels"]),
186
- };
187
-
188
- // Fix images with cropped size higher than full size
189
- if(res.croppedWidth > res.fullWidth) { res.fullWidth = res.croppedWidth; }
190
- if(res.croppedHeight > res.fullHeight) { res.fullHeight = res.croppedHeight; }
191
- }
192
-
193
- catch(e) {
194
- console.warn("Invalid XMP.GPano values for cropped panorama "+picture.id);
195
- }
196
- }
197
-
198
- // Check if crop is really necessary
199
- if(res) {
200
- res = Object.fromEntries(Object.entries(res || {}).filter(e => !isNaN(e[1])));
201
- if(
202
- (!res.fullWidth && !res.croppedWidth && res.fullHeight && !res.croppedHeight)
203
- || (res.fullWidth && !res.croppedWidth && !res.fullHeight && !res.croppedHeight)
204
- || (res.fullWidth && !res.croppedWidth && res.fullHeight && !res.croppedHeight)
205
- // eslint-disable-next-line eqeqeq
206
- || (res.fullWidth == res.croppedWidth && res.fullHeight == res.croppedHeight)
207
- ) {
208
- res = {};
209
- }
210
- }
211
-
212
- // Resize if image given
213
- if(res?.fullWidth && img?.width) {
214
- let ratio = img.width / (res?.croppedWidth || res.fullWidth);
215
- res = {
216
- fullWidth: res.fullWidth !== undefined ? Math.floor(res.fullWidth * ratio) : undefined,
217
- fullHeight: res.fullHeight !== undefined ? Math.floor(res.fullHeight * ratio) : undefined,
218
- croppedWidth: res.croppedWidth !== undefined ? Math.floor(res.croppedWidth * ratio) : undefined,
219
- croppedHeight: res.croppedHeight !== undefined ? Math.floor(res.croppedHeight * ratio) : undefined,
220
- croppedX: res.croppedX !== undefined ? Math.floor(res.croppedX * ratio) : undefined,
221
- croppedY: res.croppedY !== undefined ? Math.floor(res.croppedY * ratio) : undefined,
222
- };
223
- }
224
-
225
- return res || {};
226
- }
227
-
228
- /**
229
- * Compare function to retrieve most appropriate picture in a single direction.
230
- *
231
- * @param {number[]} picPos The picture [x,y] position
232
- * @returns {function} A compare function for sorting
233
- * @private
234
- */
235
- export function sortPicturesInDirection(picPos) {
236
- return (a,b) => {
237
- // Two prev/next links = no sort
238
- if(a.rel !== "related" && b.rel !== "related") { return 0; }
239
- // First is prev/next link = goes first
240
- else if(a.rel !== "related") { return -1; }
241
- // Second is prev/next link = goes first
242
- else if(b.rel !== "related") { return 1; }
243
- // Two related links same day = nearest goes first
244
- else if(a.date === b.date) { return getDistance(picPos, a.geometry.coordinates) - getDistance(picPos, b.geometry.coordinates); }
245
- // Two related links at different day = recent goes first
246
- else { return b.date.localeCompare(a.date); }
247
- };
248
- }
249
-
250
- /**
251
- * Generates the navbar caption based on a single picture metadata
252
- *
253
- * @param {object} metadata The picture metadata
254
- * @param {object} t The labels translations container
255
- * @returns {object} Normalized object with user name, licence and date
256
- * @private
257
- */
258
- export function getNodeCaption(metadata, t) {
259
- const caption = {};
260
-
261
- // Timestamp
262
- if(metadata?.properties?.datetimetz) {
263
- caption.date = new Date(metadata.properties.datetimetz);
264
- const timeZoneMatch = metadata.properties.datetimetz.match(/([+-]\d{2}):(\d{2})$|Z$/);
265
- if (timeZoneMatch) {
266
- if (timeZoneMatch[0] === "Z") {
267
- caption.tz = "UTC";
268
- } else {
269
- caption.tz = timeZoneMatch[0];
270
- }
271
- }
272
- }
273
- else if(metadata?.properties?.datetime) {
274
- caption.date = new Date(metadata.properties.datetime);
275
- }
276
-
277
- // Producer
278
- if(metadata?.providers) {
279
- const producerRoles = metadata?.providers?.filter(el => el?.roles?.includes("producer"));
280
- if(producerRoles?.length >= 0) {
281
- // Avoid duplicates between account name and picture author
282
- const producersDeduped = {};
283
- producerRoles.map(p => p.name).forEach(p => {
284
- const pmin = p.toLowerCase().replace(/\s/g, "");
285
- if(producersDeduped[pmin]) { producersDeduped[pmin].push(p); }
286
- else { producersDeduped[pmin] = [p];}
287
- });
288
-
289
- // Keep best looking name for each
290
- caption.producer = [];
291
- Object.values(producersDeduped).forEach(pv => {
292
- const deflt = pv[0];
293
- const better = pv.find(v => v.toLowerCase() !== v);
294
- caption.producer.push(better || deflt);
295
- });
296
- }
297
- }
298
-
299
- // License
300
- if(metadata?.properties?.license) {
301
- caption.license = metadata.properties.license;
302
- // Look for URL to license
303
- if(metadata?.links) {
304
- const licenseLink = metadata.links.find(l => l?.rel === "license");
305
- if(licenseLink) {
306
- caption.license = `<a href="${licenseLink.href}" title="${t.pnx.metadata_general_license_link}" target="_blank">${caption.license}</a>`;
307
- }
308
- }
309
- }
310
-
311
- return caption;
312
- }
313
-
314
- /**
315
- * Transforms a GeoJSON feature from the STAC API into a PSV node.
316
- *
317
- * @param {object} f The API GeoJSON feature
318
- * @param {object} t The labels translations container
319
- * @param {boolean} [fastInternet] True if Internet speed is high enough for loading HD flat pictures
320
- * @param {function} [customLinkFilter] A function checking if a STAC link is acceptable to use for picture navigation
321
- * @param {function} [urlCleaner] A function that each URL goes through for cleaning (allows use of relative URLs)
322
- * @return {object} A PSV node
323
- * @private
324
- */
325
- export function apiFeatureToPSVNode(f, t, fastInternet=false, customLinkFilter=null, urlCleaner=(u => u)) {
326
- // eslint-disable-next-line eqeqeq
327
- const isHorizontalFovDefined = f.properties?.["pers:interior_orientation"]?.["field_of_view"] != null;
328
- let horizontalFov = isHorizontalFovDefined ? parseInt(f.properties["pers:interior_orientation"]["field_of_view"]) : 70;
329
- const is360 = horizontalFov === 360;
330
-
331
- const hdUrl = urlCleaner((Object.values(f.assets).find(a => a?.roles?.includes("data")) || {}).href);
332
- const matrix = f?.properties?.["tiles:tile_matrix_sets"]?.geovisio;
333
- const prev = f.links?.find?.(l => l?.rel === "prev" && l?.type === "application/geo+json");
334
- const next = f.links?.find?.(l => l?.rel === "next" && l?.type === "application/geo+json");
335
- const baseUrlWebp = Object.values(f.assets).find(a => a.roles?.includes("visual") && a.type === "image/webp");
336
- const baseUrlJpeg = Object.values(f.assets).find(a => a.roles?.includes("visual") && a.type === "image/jpeg");
337
- const baseUrl = urlCleaner((baseUrlWebp || baseUrlJpeg).href);
338
- const thumbUrl = urlCleaner((Object.values(f.assets).find(a => a.roles?.includes("thumbnail") && a.type === "image/jpeg"))?.href);
339
- const tileUrl = f?.asset_templates?.tiles_webp || f?.asset_templates?.tiles;
340
- const croppedPanoData = getCroppedPanoData(f);
341
- const origInstance = f.links?.find?.(l => l?.rel === "via" && l?.type === "application/json");
342
- const size = f.properties?.["pers:interior_orientation"]?.["sensor_array_dimensions"];
343
-
344
- let panorama;
345
-
346
- // Cropped panorama
347
- if(Object.keys(croppedPanoData).length > 0) {
348
- panorama = {
349
- baseUrl: fastInternet ? hdUrl : baseUrl,
350
- origBaseUrl: fastInternet ? hdUrl : baseUrl,
351
- hdUrl,
352
- thumbUrl,
353
- basePanoData: fastInternet ? croppedPanoData : (img) => getCroppedPanoData(f, img),
354
- cropData: croppedPanoData,
355
- width: croppedPanoData.fullWidth,
356
- // This is only to mock loading of tiles (which are not available for flat pictures)
357
- cols: 2, rows: 1, tileUrl: () => null
358
- };
359
- }
360
- // 360°
361
- else if(is360 && matrix) {
362
- panorama = {
363
- baseUrl,
364
- origBaseUrl: baseUrl,
365
- basePanoData: (img) => ({
366
- fullWidth: img.width,
367
- fullHeight: img.height,
368
- }),
369
- hdUrl,
370
- thumbUrl,
371
- cols: matrix && matrix.tileMatrix[0].matrixWidth,
372
- rows: matrix && matrix.tileMatrix[0].matrixHeight,
373
- width: matrix && (matrix.tileMatrix[0].matrixWidth * matrix.tileMatrix[0].tileWidth),
374
- tileUrl: matrix && ((col, row) => urlCleaner(tileUrl.href.replace(/\{TileCol\}/g, col).replace(/\{TileRow\}/g, row)))
375
- };
376
- }
377
- // Flat pictures: shown only using a cropped base panorama
378
- else {
379
- const getFlatCrop = (img) => {
380
- if (img.width < img.height && !isHorizontalFovDefined) {
381
- horizontalFov = 35;
382
- }
383
- const verticalFov = horizontalFov * img.height / img.width;
384
- const panoWidth = img.width * 360 / horizontalFov;
385
- const panoHeight = img.height * 180 / verticalFov;
386
-
387
- return {
388
- fullWidth: Math.floor(panoWidth),
389
- fullHeight: Math.floor(panoHeight),
390
- croppedWidth: img.width,
391
- croppedHeight: img.height,
392
- croppedX: Math.floor((panoWidth - img.width) / 2),
393
- croppedY: Math.floor((panoHeight - img.height) / 2),
394
- };
395
- };
396
-
397
- let cropData = size ? getFlatCrop({width: size[0], height: size[1]}) : null;
398
-
399
- panorama = {
400
- baseUrl: fastInternet ? hdUrl : baseUrl,
401
- origBaseUrl: fastInternet ? hdUrl : baseUrl,
402
- hdUrl,
403
- thumbUrl,
404
- basePanoData: getFlatCrop,
405
- cropData,
406
- width: cropData?.fullWidth || 2,
407
- // This is only to mock loading of tiles (which are not available for flat pictures)
408
- cols: 2, rows: 1, tileUrl: () => null
409
- };
410
- }
411
-
412
- // Cleanup empty semantics feature (metacatalog bug)
413
- if(f.properties?.semantics) {
414
- f.properties.semantics = f.properties.semantics.filter(o => Object.keys(o).length > 0);
415
- }
416
-
417
- const node = {
418
- id: f.id,
419
- caption: getNodeCaption(f, t),
420
- panorama,
421
- links: filterRelatedPicsLinks(f, customLinkFilter),
422
- gps: f.geometry.coordinates,
423
- sequence: {
424
- id: f.collection,
425
- nextPic: next ? next.id : undefined,
426
- prevPic: prev ? prev.id : undefined
427
- },
428
- sphereCorrection: getSphereCorrection(f),
429
- horizontalFov,
430
- origInstance,
431
- properties: f.properties,
432
- };
433
-
434
- return node;
435
- }
436
-
437
- /**
438
- * Filter surrounding pictures links to avoid too much arrows on viewer.
439
- * @private
440
- */
441
- export function filterRelatedPicsLinks(metadata, customFilter = null) {
442
- const picLinks = metadata.links
443
- .filter(l => ["next", "prev", "related"].includes(l?.rel) && l?.type === "application/geo+json")
444
- .filter(l => customFilter ? customFilter(l) : true)
445
- .map(l => {
446
- if(l.datetime) {
447
- l.date = l.datetime.split("T")[0];
448
- }
449
- return l;
450
- });
451
- const picPos = metadata.geometry.coordinates;
452
-
453
- // Filter to keep a single link per direction, in same sequence or most recent one
454
- const filteredLinks = [];
455
- const picSurroundings = { "N": [], "ENE": [], "ESE": [], "S": [], "WSW": [], "WNW": [] };
456
-
457
- for(let picLink of picLinks) {
458
- const a = getSimplifiedAngle(picPos, picLink.geometry.coordinates);
459
- picSurroundings[a].push(picLink);
460
- }
461
-
462
- for(let direction in picSurroundings) {
463
- const picsInDirection = picSurroundings[direction];
464
- if(picsInDirection.length === 0) { continue; }
465
- picsInDirection.sort(sortPicturesInDirection(picPos));
466
- filteredLinks.push(picsInDirection.shift());
467
- }
468
-
469
- let arrowStyle = l => l.rel === "related" ? {
470
- element: getArrow(ArrowTurn),
471
- size: { width: 64*2/3, height: 192*2/3 }
472
- } : {
473
- element: getArrow(ArrowTriangle),
474
- size: { width: 75, height: 75 }
475
- };
476
-
477
- const rectifiedYaw = - (metadata.properties?.["view:azimuth"] || 0) * (Math.PI / 180);
478
- return filteredLinks.map(l => ({
479
- nodeId: l.id,
480
- gps: l.geometry.coordinates,
481
- arrowStyle: arrowStyle(l),
482
- linkOffset: { yaw: rectifiedYaw }
483
- }));
484
- }
485
-
486
- /**
487
- * Read structured hashtags from picture metadata
488
- * @private
489
- */
490
- export function getHashTags(metadata) {
491
- if(!metadata?.properties?.semantics) { return []; }
492
-
493
- // Find hashtag entry in semantics
494
- const hashtagsTags = metadata.properties.semantics.filter(kv => kv.key === "hashtags");
495
- const readHashTags = [];
496
- hashtagsTags.forEach(htt => htt.value.split(";").forEach(v => readHashTags.push(v.trim())));
497
-
498
- return readHashTags;
499
- }
500
-
501
- /**
502
- * Check if a given picture has associated annotations.
503
- * @private
504
- */
505
- export function hasAnnotations(metadata) {
506
- return metadata?.properties?.annotations?.length > 0;
507
- }