@panoramax/web-viewer 3.2.2 → 3.2.3-develop-557df57a

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/docs/02_Usage.md CHANGED
@@ -291,6 +291,12 @@ Note that all functions of [MapLibre GL JS class Map][16] are also available.
291
291
 
292
292
  Destroy any form of life in this component
293
293
 
294
+ ### waitForEnoughMapLoaded
295
+
296
+ Helper to know when enough map background and Panoramax tiles are loaded for a proper display.
297
+
298
+ Returns **[Promise][19]** Resolves when enough is loaded
299
+
294
300
  ### reloadVectorTiles
295
301
 
296
302
  Force refresh of vector tiles data
@@ -360,16 +366,16 @@ This is useful after a map theme change.
360
366
  Photo is the component showing a single picture.
361
367
  It uses Photo Sphere Viewer as a basis, and pre-configure dialog with STAC API.
362
368
 
363
- Note that all functions of [PhotoSphereViewer Viewer class][19] are available as well.
369
+ Note that all functions of [PhotoSphereViewer Viewer class][20] are available as well.
364
370
 
365
371
  ### Parameters
366
372
 
367
373
  * `parent` **[CoreView][1]** The parent view
368
374
  * `container` **[Element][8]** The DOM element to create into
369
- * `options` **[object][10]?** The viewer options. Can be any of [Photo Sphere Viewer options][20] (optional, default `{}`)
375
+ * `options` **[object][10]?** The viewer options. Can be any of [Photo Sphere Viewer options][21] (optional, default `{}`)
370
376
 
371
377
  * `options.transitionDuration` **[number][15]?** The number of milliseconds the transition animation should be.
372
- * `options.shouldGoFast` **[function][21]?** Function returning a boolean to indicate if we may skip loading HD images.
378
+ * `options.shouldGoFast` **[function][22]?** Function returning a boolean to indicate if we may skip loading HD images.
373
379
 
374
380
  ### getPictureMetadata
375
381
 
@@ -394,7 +400,7 @@ Displays in viewer a picture near to given coordinates
394
400
  * `lat` **[number][15]** Latitude (WGS84)
395
401
  * `lon` **[number][15]** Longitude (WGS84)
396
402
 
397
- Returns **[Promise][22]** Resolves on picture ID if picture found, otherwise rejects
403
+ Returns **[Promise][19]** Resolves on picture ID if picture found, otherwise rejects
398
404
 
399
405
  ### getXY
400
406
 
@@ -676,7 +682,7 @@ Enable or disable JOSM live editing using [Remote][24]
676
682
 
677
683
  * `enabled` **[boolean][14]** Set to true to enable JOSM live
678
684
 
679
- Returns **[Promise][22]** Resolves on JOSM live being enabled or disabled
685
+ Returns **[Promise][19]** Resolves on JOSM live being enabled or disabled
680
686
 
681
687
  ### setFocus
682
688
 
@@ -812,13 +818,13 @@ Type: [object][10]
812
818
 
813
819
  [18]: https://maplibre.org/maplibre-style-spec/sources/#raster
814
820
 
815
- [19]: https://photo-sphere-viewer.js.org/api/classes/core.viewer
821
+ [19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
816
822
 
817
- [20]: https://photo-sphere-viewer.js.org/guide/config.html#standard-options
823
+ [20]: https://photo-sphere-viewer.js.org/api/classes/core.viewer
818
824
 
819
- [21]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
825
+ [21]: https://photo-sphere-viewer.js.org/guide/config.html#standard-options
820
826
 
821
- [22]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
827
+ [22]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
822
828
 
823
829
  [23]: #CoreView
824
830
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panoramax/web-viewer",
3
- "version": "3.2.2",
3
+ "version": "3.2.3-develop-557df57a",
4
4
  "description": "Panoramax web viewer for geolocated pictures",
5
5
  "main": "build/index.js",
6
6
  "author": "Panoramax team",
@@ -252,4 +252,4 @@
252
252
  ]
253
253
  ]
254
254
  }
255
- }
255
+ }
@@ -33,10 +33,10 @@
33
33
  window.onload = () => {
34
34
  editor = new Panoramax.Editor(
35
35
  "editor",
36
- "https://api.panoramax.xyz/api",
36
+ "https://panoramax.openstreetmap.fr/api",
37
37
  {
38
- selectedSequence: "561a5056-78a4-499d-9138-26b259f0765c",
39
- selectedPicture: "b9c51504-b25c-40da-bb47-e696b93233fc",
38
+ selectedSequence: "ecfe4b2c-0acd-4d3a-a10d-c3e6818755c8",
39
+ selectedPicture: "329af5c6-4761-4a6d-9c1e-674fd6daa8b6",
40
40
  background: "aerial",
41
41
  raster: {
42
42
  type: "raster",
package/src/Editor.js CHANGED
@@ -59,14 +59,11 @@ export default class Editor extends CoreView {
59
59
  raster: options.raster,
60
60
  background: options.background,
61
61
  supplementaryStyle: this._createMapStyle(),
62
+ zoom: 15, // Hack to avoid _initMapPosition call
62
63
  });
63
64
  linkMapAndPhoto(this);
64
65
  this._loadSequence();
65
66
  this.map.once("load", () => {
66
- this.map.setPaintProperty("geovisio_editor_sequences", "line-color", this.map._getLayerColorStyle("sequences"));
67
- this.map.setPaintProperty("geovisio_editor_pictures", "circle-color", this.map._getLayerColorStyle("pictures"));
68
- this.map.setLayoutProperty("geovisio_editor_sequences", "visibility", "visible");
69
- this.map.setLayoutProperty("geovisio_editor_pictures", "visibility", "visible");
70
67
  if(options.raster) { this._addMapBackgroundWidget(); }
71
68
  this._bindPicturesEvents();
72
69
  });
@@ -153,6 +150,15 @@ export default class Editor extends CoreView {
153
150
  */
154
151
  _loadSequence() {
155
152
  return this._api.getSequenceItems(this._selectedSeqId).then(seq => {
153
+ // Hide loader after source load
154
+ this.map.once("sourcedata", () => {
155
+ this.map.setPaintProperty("geovisio_editor_sequences", "line-color", this.map._getLayerColorStyle("sequences"));
156
+ this.map.setPaintProperty("geovisio_editor_pictures", "circle-color", this.map._getLayerColorStyle("pictures"));
157
+ this.map.setLayoutProperty("geovisio_editor_sequences", "visibility", "visible");
158
+ this.map.setLayoutProperty("geovisio_editor_pictures", "visibility", "visible");
159
+ this.map.once("styledata", () => this._loader.dismiss());
160
+ });
161
+
156
162
  // Create data source
157
163
  this._sequenceData = seq.features;
158
164
  this.map.getSource("geovisio_editor_sequences").setData({
@@ -177,40 +183,34 @@ export default class Editor extends CoreView {
177
183
  ]
178
184
  });
179
185
 
180
- const onMapLoad = () => {
181
- // Select picture if any
182
- if(this._selectedPicId) {
183
- const pic = seq.features.find(p => p.id === this._selectedPicId);
184
- if(pic) {
185
- this.select(this._selectedSeqId, this._selectedPicId, true);
186
- this.map.jumpTo({ center: pic.geometry.coordinates, zoom: 18 });
187
- }
188
- else {
189
- console.log("Picture with ID", pic, "was not found");
190
- }
186
+ // Select picture if any
187
+ if(this._selectedPicId) {
188
+ const pic = seq.features.find(p => p.id === this._selectedPicId);
189
+ if(pic) {
190
+ this.select(this._selectedSeqId, this._selectedPicId, true);
191
+ this.map.jumpTo({ center: pic.geometry.coordinates, zoom: 18 });
191
192
  }
192
- // Show area of sequence otherwise
193
193
  else {
194
- const bbox = [
195
- ...seq.features[0].geometry.coordinates,
196
- ...seq.features[0].geometry.coordinates
197
- ];
198
-
199
- for(let i=1; i < seq.features.length; i++) {
200
- const c = seq.features[i].geometry.coordinates;
201
- if(c[0] < bbox[0]) { bbox[0] = c[0]; }
202
- if(c[1] < bbox[1]) { bbox[1] = c[1]; }
203
- if(c[0] > bbox[2]) { bbox[2] = c[0]; }
204
- if(c[1] > bbox[3]) { bbox[3] = c[1]; }
205
- }
206
-
207
- this.map.fitBounds(bbox, {animate: false});
194
+ console.log("Picture with ID", pic, "was not found");
195
+ }
196
+ }
197
+ // Show area of sequence otherwise
198
+ else {
199
+ const bbox = [
200
+ ...seq.features[0].geometry.coordinates,
201
+ ...seq.features[0].geometry.coordinates
202
+ ];
203
+
204
+ for(let i=1; i < seq.features.length; i++) {
205
+ const c = seq.features[i].geometry.coordinates;
206
+ if(c[0] < bbox[0]) { bbox[0] = c[0]; }
207
+ if(c[1] < bbox[1]) { bbox[1] = c[1]; }
208
+ if(c[0] > bbox[2]) { bbox[2] = c[0]; }
209
+ if(c[1] > bbox[3]) { bbox[3] = c[1]; }
208
210
  }
209
- this._loader.dismiss();
210
- };
211
211
 
212
- if(this.map.loaded()) { onMapLoad(); }
213
- else { this.map.once("load", onMapLoad); }
212
+ this.map.fitBounds(bbox, {animate: false});
213
+ }
214
214
  }).catch(e => this._loader.dismiss(e, this._t.gvs.error_api));
215
215
  }
216
216
 
@@ -84,7 +84,7 @@ class StandaloneMap extends CoreView {
84
84
 
85
85
  this.map = new MyMap(this, this.mapContainer, this._options);
86
86
  this.map.addControl(new NavigationControl({ showCompass: false }));
87
- this.map.on("load", () => {
87
+ this.map.waitForEnoughMapLoaded().then(() => {
88
88
  this.map.reloadLayersStyles();
89
89
  this._loader.dismiss();
90
90
  });
package/src/Viewer.js CHANGED
@@ -192,8 +192,8 @@ class Viewer extends CoreView {
192
192
  const onceStuffReady = () => {
193
193
  this._widgets = new Widgets(this, this._options.widgets);
194
194
 
195
- // Hide mini component if small width
196
- if(this.map && this.isWidthSmall()) {
195
+ // Hide mini component if no selected picture
196
+ if(this.map && !this._options.selectedPicture) {
197
197
  this.setUnfocusedVisible(false);
198
198
  }
199
199
 
@@ -286,7 +286,9 @@ class Viewer extends CoreView {
286
286
  delete this._widgets;
287
287
  this._hash.destroy();
288
288
  delete this._hash;
289
- this.map.destroy();
289
+ if (this.map) {
290
+ this.map.destroy();
291
+ }
290
292
  delete this.map;
291
293
  delete this._mapFilters;
292
294
  this.psv.destroy();
@@ -347,8 +349,11 @@ class Viewer extends CoreView {
347
349
  this.map._getLayerSortStyle = this._getLayerSortStyle.bind(this);
348
350
  this.addEventListener("map:users-changed", resolve, { once: true });
349
351
  this.container.classList.add("gvs-has-mini");
352
+
353
+ // Map double-click: unselect if focused, toggle focus if unfocused
350
354
  this.map.on("dblclick", () => {
351
355
  if(!this.isMapWide()) { this.setFocus("map"); }
356
+ else { this.select(); }
352
357
  });
353
358
 
354
359
  if (typeof this._options.map === "object" && this._options.map.startWide) {
@@ -1003,8 +1008,8 @@ class Viewer extends CoreView {
1003
1008
  this.dispatchEvent(event);
1004
1009
 
1005
1010
  if(this._josmListener) {
1006
- this.removeEventListener("picture-loading", this._josmListener);
1007
- this.removeEventListener("picture-loaded", this._josmListener);
1011
+ this.removeEventListener("psv:picture-loading", this._josmListener);
1012
+ this.removeEventListener("psv:picture-loaded", this._josmListener);
1008
1013
  delete this._josmListener;
1009
1014
  }
1010
1015
  return Promise.resolve();
@@ -2,7 +2,7 @@ import "./CoreView.css";
2
2
  import API from "../utils/API";
3
3
  import { getTranslations } from "../utils/I18n";
4
4
  import { DEFAULT_TILES } from "../utils/Map";
5
- import { isInIframe, isInternetFast } from "../utils/Utils";
5
+ import { BASE_PANORAMA_ID, isInIframe, isInternetFast } from "../utils/Utils";
6
6
  import PACKAGE_JSON from "../../package.json";
7
7
  import Loader from "./Loader";
8
8
 
@@ -143,6 +143,7 @@ export default class CoreView extends EventTarget {
143
143
  * @param {boolean} [force=false] Force select even if already selected
144
144
  */
145
145
  select(seqId = null, picId = null, force = false) {
146
+ if(picId === BASE_PANORAMA_ID) { picId = null; }
146
147
  const prevSeqId = this._selectedSeqId || null;
147
148
  const prevPicId = this._selectedPicId || null;
148
149
  if(!force && prevPicId == picId && prevSeqId == seqId) { return; } // Avoid running if already selected
@@ -22,7 +22,7 @@
22
22
  display: flex;
23
23
  flex-direction: column;
24
24
  justify-content: center;
25
- gap: 40px;
25
+ gap: 20px;
26
26
  align-items: center;
27
27
  background: #37474F;
28
28
  z-index: 9000;
@@ -53,4 +53,22 @@
53
53
  @keyframes rotating {
54
54
  from { transform: rotate(0deg); }
55
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;
56
74
  }
@@ -83,7 +83,15 @@ export default class Loader {
83
83
  this.container.children[0].src = LogoDead;
84
84
  this.container.children[0].style.width = "200px";
85
85
  this.container.children[0].style.animation = "unset";
86
- let errHtml = `<small>${next ? this._parent._t.gvs.error_click : this._parent._t.gvs.error_retry}</small>`;
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
+
87
95
  if(errMeaningful) { errHtml = errMeaningful + "<br />" + errHtml; }
88
96
  this.container.children[1].innerHTML = `${this._parent._t.gvs.error}<br />${errHtml}`;
89
97
  if(next) {
@@ -110,11 +110,16 @@ export default class Map extends maplibregl.Map {
110
110
  }
111
111
  }, 15000);
112
112
 
113
- this.on("load", async () => {
114
- await this.setVisibleUsers(this._parent._options.users);
115
- this.reloadLayersStyles();
116
- this.resize();
117
- });
113
+ this.waitForEnoughMapLoaded().then(async () => await this._postLoad());
114
+ }
115
+
116
+ /**
117
+ * @private
118
+ */
119
+ async _postLoad() {
120
+ this.resize();
121
+ await this.setVisibleUsers(this._parent._options.users);
122
+ this.reloadLayersStyles();
118
123
  }
119
124
 
120
125
  /**
@@ -132,6 +137,54 @@ export default class Map extends maplibregl.Map {
132
137
  delete this._userLayers;
133
138
  }
134
139
 
140
+ /**
141
+ * Helper to know when enough map background and Panoramax tiles are loaded for a proper display.
142
+ * @returns {Promise} Resolves when enough is loaded
143
+ */
144
+ waitForEnoughMapLoaded() {
145
+ return new Promise((resolve) => {
146
+ let nbBgTiles = 0;
147
+ let nbFgTiles = 0;
148
+ let nbLoadedBgTiles = 0;
149
+ let nbLoadedFgTiles = 0;
150
+
151
+ const onSourceDataLoading = e => {
152
+ if(e.dataType === "source" && e.tile) {
153
+ if(e.sourceId.startsWith("geovisio")) {
154
+ nbFgTiles++;
155
+ }
156
+ else {
157
+ nbBgTiles++;
158
+ }
159
+ }
160
+ };
161
+ const onSourceData = e => {
162
+ if(e.dataType === "source" && e.tile) {
163
+ if(e.sourceId.startsWith("geovisio")) {
164
+ nbLoadedFgTiles++;
165
+ if(e.isSourceLoaded) { nbLoadedFgTiles = nbFgTiles; }
166
+ }
167
+ else {
168
+ nbLoadedBgTiles++;
169
+ if(e.isSourceLoaded) { nbLoadedBgTiles = nbBgTiles; }
170
+ }
171
+ }
172
+ checkEnoughLoaded();
173
+ };
174
+
175
+ const checkEnoughLoaded = () => {
176
+ if(nbLoadedBgTiles / nbBgTiles >= 0.75 && nbLoadedFgTiles / nbFgTiles >= 0.75) {
177
+ this.off("sourcedata", onSourceData);
178
+ this.off("sourcedataloading", onSourceDataLoading);
179
+ resolve();
180
+ }
181
+ };
182
+
183
+ this.on("sourcedataloading", onSourceDataLoading);
184
+ this.on("sourcedata", onSourceData);
185
+ });
186
+ }
187
+
135
188
  /**
136
189
  * Sets map view based on returned API bbox (if no precise option given by user).
137
190
  * @private
@@ -148,7 +201,7 @@ export default class Map extends maplibregl.Map {
148
201
  try {
149
202
  bbox = new maplibregl.LngLatBounds(bbox);
150
203
  if(this.loaded()) { this.fitBounds(bbox, { "animate": false }); }
151
- else { this.on("load", () => this.fitBounds(bbox, { "animate": false })); }
204
+ else { this.waitForEnoughMapLoaded().then(() => this.fitBounds(bbox, { "animate": false })); }
152
205
  }
153
206
  catch(e) {
154
207
  console.warn("Received invalid bbox: "+bbox);
@@ -344,20 +397,27 @@ export default class Map extends maplibregl.Map {
344
397
  this._picMarkerPreview.remove();
345
398
 
346
399
  // Show marker corresponding to selection
347
- this._picMarker
348
- .setLngLat([lon, lat])
349
- .setRotation(heading)
350
- .addTo(this);
400
+ if(lon !== undefined && lat !== undefined) {
401
+ this._picMarker
402
+ .setLngLat([lon, lat])
403
+ .setRotation(heading)
404
+ .addTo(this);
405
+ }
406
+ else {
407
+ this._picMarker.remove();
408
+ }
351
409
 
352
410
  // Update map style to see selected sequence
353
411
  this.reloadLayersStyles();
354
412
 
355
413
  // Move map to picture coordinates
356
- this.flyTo({
357
- center: [lon, lat],
358
- zoom: this.getZoom() < TILES_PICTURES_ZOOM+2 ? TILES_PICTURES_ZOOM+2 : this.getZoom(),
359
- maxDuration: 2000
360
- });
414
+ if(lon !== undefined && lat !== undefined) {
415
+ this.flyTo({
416
+ center: [lon, lat],
417
+ zoom: this.getZoom() < TILES_PICTURES_ZOOM+2 ? TILES_PICTURES_ZOOM+2 : this.getZoom(),
418
+ maxDuration: 2000
419
+ });
420
+ }
361
421
  }
362
422
 
363
423
  /**
@@ -3,7 +3,7 @@ import LoaderImgBase from "../img/loader_base.jpg";
3
3
  import LogoDead from "../img/logo_dead.svg";
4
4
  import {
5
5
  getDistance, positionToXYZ, xyzToPosition,
6
- apiFeatureToPSVNode, getRelativeHeading,
6
+ apiFeatureToPSVNode, getRelativeHeading, BASE_PANORAMA_ID,
7
7
  } from "../utils/Utils";
8
8
 
9
9
  // Photo Sphere Viewer imports
@@ -24,6 +24,17 @@ const BASE_PANORAMA = {
24
24
  rows: 1,
25
25
  tileUrl: () => null,
26
26
  };
27
+ const BASE_PANORAMA_NODE = {
28
+ id: BASE_PANORAMA_ID,
29
+ caption: "",
30
+ panorama: BASE_PANORAMA,
31
+ links: [],
32
+ gps: [0,0],
33
+ sequence: {},
34
+ sphereCorrection: {},
35
+ horizontalFov: 360,
36
+ properties: {},
37
+ };
27
38
 
28
39
  export const PSV_DEFAULT_ZOOM = 30;
29
40
  export const PSV_ANIM_DURATION = 250;
@@ -118,7 +129,7 @@ export default class Photo extends PSViewer {
118
129
  * @returns {Promise} Resolves on PSV node metadata
119
130
  */
120
131
  async _getNodeFromAPI(picId) {
121
- if(!picId) { return; }
132
+ if(!picId || picId === BASE_PANORAMA_ID) { return BASE_PANORAMA_NODE; }
122
133
 
123
134
  const picApiResponse = await fetch(
124
135
  this._parent._api.getPictureMetadataUrl(picId, this._picturesSequences[picId]),
@@ -364,7 +375,7 @@ export default class Photo extends PSViewer {
364
375
 
365
376
  /**
366
377
  * Event handler for node change in PSV.
367
- * Allows to send a custom "picture-loaded" event.
378
+ * Allows to send a custom "psv:picture-loaded" event.
368
379
  * @private
369
380
  */
370
381
  _onNodeChanged(e) {
@@ -376,7 +387,10 @@ export default class Photo extends PSViewer {
376
387
  if(e.node.id) {
377
388
  this._parent.select(e.node?.sequence?.id, e.node.id);
378
389
  const picMeta = this.getPictureMetadata();
379
- if(!picMeta) { return; }
390
+ if(!picMeta) {
391
+ this._parent.dispatchEvent(new CustomEvent("psv:picture-loaded", {detail: {}}));
392
+ return;
393
+ }
380
394
  this._prevSequence = picMeta.sequence.id;
381
395
 
382
396
  /**
@@ -453,6 +467,7 @@ export default class Photo extends PSViewer {
453
467
  * @returns {object} Picture metadata
454
468
  */
455
469
  getPictureMetadata() {
470
+ if(this._myVTour?.state?.currentNode?.id === BASE_PANORAMA_ID) { return null; }
456
471
  return this._myVTour.state.currentNode ? Object.assign({}, this._myVTour.state.currentNode) : null;
457
472
  }
458
473
 
@@ -0,0 +1 @@
1
+ {}
@@ -41,7 +41,7 @@
41
41
  "sequence_speed": "Afspillerens hastighed",
42
42
  "legend_license": "Licens: {l}",
43
43
  "legend_title": "Vis detaljer om billedet",
44
- "error_click": "Klik for at fortsætte",
44
+ "error_click": "Fortsæt",
45
45
  "error": "Vi har et problem…",
46
46
  "search_address": "Søg efter en adresse, by…",
47
47
  "sequence_pause": "Sæt sekvensen på pause",
@@ -88,7 +88,7 @@
88
88
  "filter_user": "Bruger",
89
89
  "filter_user_mypics": "Mine billeder",
90
90
  "filter_picture": "Billedtype",
91
- "filter_zoom_in": "Zoom ind for at gøre filtre synlige",
91
+ "filter_zoom_in": "Zoom ind for at se dette filter",
92
92
  "picture_flat": "Klassisk",
93
93
  "picture_360": "360°",
94
94
  "filter_qualityscore_help": "Klik for at aktivere eller deaktivere",
@@ -42,7 +42,7 @@
42
42
  "show_psv": "Bildbetrachter anzeigen",
43
43
  "show_map": "Karte anzeigen",
44
44
  "search_user": "Benutzernamen suchen…",
45
- "filter_zoom_in": "Hereinzoomen um Filter sichtbar zu machen",
45
+ "filter_zoom_in": "Hereinzoomen um diesen Filter zu sehen",
46
46
  "picture_flat": "Klassisch",
47
47
  "picture_360": "360°",
48
48
  "filter_camera_model": "Nach Kameramodell",
@@ -146,7 +146,7 @@
146
146
  "report": "Foto melden",
147
147
  "legend_title": "Zeige Bilderdetails",
148
148
  "report_wait": "Bericht senden…",
149
- "error_click": "Klicken um fortzufahren",
149
+ "error_click": "Weiter",
150
150
  "error_retry": "Bitte später erneut versuchen",
151
151
  "metadata_camera_resolution": "Auflösung",
152
152
  "filter_user_mypics": "Meine Fotos",
@@ -165,7 +165,8 @@
165
165
  "qualityscore_doc_link": "Weitere Informationen zur Berechnung des Qualitätsfaktors findest du in unserer Dokumentation.",
166
166
  "qualityscore_doc_1": "Panoramax bietet einen Qualitätsfaktor für jedes Bild. Es ermöglicht eine einfache Kartenfilterung und eine umfassende Anzeige der Verfügbarkeit hochwertiger Bilder.",
167
167
  "qualityscore_doc_2": "Die Note wird den Benutzern als A/B/C/D/E-Bewertung angezeigt (A ist die beste, E die schlechteste) und anhand dieser Skala grafisch dargestellt:",
168
- "qualityscore_doc_3": "Die Berechnung erfolgt auf der Grundlage der GPS-Genauigkeit und der Bildauflösung. Ein professionelles System wird mit A bewertet, eine 360-Grad-Action-Kamera mit B und ein Smartphone mit C/D/E."
168
+ "qualityscore_doc_3": "Die Berechnung erfolgt auf der Grundlage der GPS-Genauigkeit und der Bildauflösung. Ein professionelles System wird mit A bewertet, eine 360-Grad-Action-Kamera mit B und ein Smartphone mit C/D/E.",
169
+ "minimize_short": "Verbergen"
169
170
  },
170
171
  "psv": {
171
172
  "twoFingers": "Nutzen sie zwei Finger zum navigieren",
@@ -42,7 +42,7 @@
42
42
  "copy": "Copy",
43
43
  "copied": "Copied",
44
44
  "error": "We have a problem…",
45
- "error_click": "Click to continue",
45
+ "error_click": "Continue",
46
46
  "error_retry": "Please retry later",
47
47
  "sequence_next": "Next picture in sequence",
48
48
  "sequence_play": "Play along this sequence",
@@ -27,7 +27,7 @@
27
27
  "copy": "Kopii",
28
28
  "copied": "Kopiita",
29
29
  "error": "Ni havas problemon…",
30
- "error_click": "Alkliku por kontinui",
30
+ "error_click": "Kontinui",
31
31
  "error_retry": "Bonvolu reprovi poste",
32
32
  "search_address": "Serĉi adreson, urbon…",
33
33
  "filters": "Filtriloj",
@@ -124,7 +124,7 @@
124
124
  "contrast": "Pliigi bildkontraston",
125
125
  "metadata_general_picid": "Fotoidentigilo",
126
126
  "filter_picture": "Bilda tipo",
127
- "filter_zoom_in": "Zomu por vidi pli da filtriloj",
127
+ "filter_zoom_in": "Zomu por vidi tiu filtrilo",
128
128
  "map_background": "Bazmapo",
129
129
  "map_background_aerial": "Aera",
130
130
  "map_background_streets": "Stratoj",
@@ -161,7 +161,13 @@
161
161
  "report_success": "La raporto estis sukcese sendita. Ĝi estos reviziita baldaŭ.",
162
162
  "report_details_placeholder": "Opcie, vi povas aldoni detalojn pri la problemo",
163
163
  "report_email_placeholder": "Opcie",
164
- "report_failure": "Eraro okazis dum kreo de la raporto: {e}. Bonvolu provi denove poste."
164
+ "report_failure": "Eraro okazis dum kreo de la raporto: {e}. Bonvolu provi denove poste.",
165
+ "qualityscore_doc_3": "Ĝi estas kalkulita surbaze de GPS-precizeco kaj bildrezolucio. Profesiaj sistemoj havos A-poentaron, 360°-akcifotilo havos B-poentaron, kaj poŝtelefono havos C/D/E-poentaron.",
166
+ "qualityscore_title": "Pri la kvalita poentaro",
167
+ "qualityscore_doc_1": "Panoramax ofertas Kvalitan Poentaron por ĉiu bildo. Ĝi ebligas facilan mapfiltradon kaj ampleksan vidigon de la havebleco de altkvalitaj bildoj.",
168
+ "qualityscore_doc_link": "Pli da detaloj pri la kalkulo de la Kvalita Poentaro estas disponeblaj en nia dokumentado.",
169
+ "minimize_short": "Maski",
170
+ "qualityscore_doc_2": "La poentaro estas montrita al uzantoj kiel litero A/B/C/D/E (A estas la plej bona, E la plej malbona) kaj grafike prezentita per ĉi tiu skalo:"
165
171
  },
166
172
  "maplibre": {
167
173
  "GeolocateControl.FindMyLocation": "Trovi mian pozicion",
@@ -95,7 +95,7 @@
95
95
  "sequence_play": "Reproducir esta secuencia",
96
96
  "sequence_pause": "Pausar la secuencia",
97
97
  "sequence_prev": "Imagen anterior de la secuencia",
98
- "error_click": "Haz click para continuar",
98
+ "error_click": "Continuar",
99
99
  "error_retry": "Por favor, inténtalo más tarde",
100
100
  "error_psv": "El visor de fotos esféricas no se carga correctamente",
101
101
  "error_pic": "No se encuentra la imagen que queríamos",
@@ -42,7 +42,7 @@
42
42
  "copy": "Copier",
43
43
  "copied": "Copié",
44
44
  "error": "On a un problème…",
45
- "error_click": "Cliquez pour continuer",
45
+ "error_click": "Continuer",
46
46
  "error_retry": "Merci de réessayer plus tard",
47
47
  "sequence_next": "Photo suivante dans la séquence",
48
48
  "sequence_play": "Parcourir cette séquence",
@@ -26,7 +26,7 @@
26
26
  "copy": "Copia",
27
27
  "copied": "Copiato",
28
28
  "error": "Abbiamo un problema…",
29
- "error_click": "Clicca per continuare",
29
+ "error_click": "Continuare",
30
30
  "error_retry": "Riprovare più tardi",
31
31
  "sequence_play": "Riproduci questa sequenza",
32
32
  "sequence_pause": "Pausa la sequenza",
@@ -76,7 +76,7 @@
76
76
  "filter_date_1year": "1 anno",
77
77
  "filter_user_mypics": "Le mie foto",
78
78
  "filter_picture": "Tipo di foto",
79
- "filter_zoom_in": "Ingrandisci per rendere visibili i filtri",
79
+ "filter_zoom_in": "Ingrandisci per vedere questo filtro",
80
80
  "picture_flat": "Classica",
81
81
  "picture_360": "360°",
82
82
  "filter_qualityscore_help": "Clicca per abilitare o disabilitare",
@@ -156,7 +156,13 @@
156
156
  "metadata_exif_value": "Valore",
157
157
  "report": "Segnala foto",
158
158
  "report_details_placeholder": "Facoltativo, puoi aggiungere dei dettagli sul problema e sul perché lo ritieni rilevante",
159
- "report_success": "La segnalazione è stata inviata correttamente. Non appena possibile verrà esaminata."
159
+ "report_success": "La segnalazione è stata inviata correttamente. Non appena possibile verrà esaminata.",
160
+ "minimize_short": "Nascondi",
161
+ "qualityscore_title": "Informazioni sul punteggio di qualità",
162
+ "qualityscore_doc_1": "Panoramax propone un Punteggio della qualità per ogni foto. Ciò permette di filtrare facilmente la mappa e mostrare le foto di alta qualità disponibili.",
163
+ "qualityscore_doc_2": "Il voto è mostrato agli utenti con un valore tipo A/B/C/D/E (A è il voto migliore, E il peggiore) e graficamente viene mostrato con la seguente scala:",
164
+ "qualityscore_doc_link": "Nella nostra documentazione sono presenti maggiori dettagli sul calcolo del Punteggio della qualità.",
165
+ "qualityscore_doc_3": "Esso è calcolato basandosi sulla precisione del GPS e sulla risoluzione della foto. Un sistema professionale avrà un punteggio A, un action camera a 360° avrà un punteggio B e uno smartphone verrà classificato come C, D o E."
160
166
  },
161
167
  "psv": {
162
168
  "loadError": "Impossibile caricare l’immagine panoramica",