@panoramax/web-viewer 4.1.0-develop-6d60695e → 4.1.0-develop-2b222b1c

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.
@@ -289,13 +289,34 @@ export function xyzParamToPSVPosition(str) {
289
289
  * @private
290
290
  */
291
291
  function xywhParamToPSVPosition(str, picmeta) {
292
- const loc = (str || "").split(",");
292
+ let loc = (str || "").split(",");
293
293
  if (loc.length === 4 && !loc.some(v => isNaN(v))) {
294
- const size = picmeta?.properties?.["pers:interior_orientation"]?.sensor_array_dimensions;
294
+ let [ locX, locY, locW, locH ] = loc.map(v => parseFloat(v));
295
+ let basePanoData = picmeta.panorama.basePanoData;
296
+ if(typeof basePanoData === "function") {
297
+ const size = picmeta?.properties?.["pers:interior_orientation"]?.sensor_array_dimensions;
298
+ if(size && size.length == 2) { basePanoData = basePanoData({width: size[0], height: size[1]}); }
299
+ else { basePanoData = null; }
300
+ }
301
+
302
+ if(!basePanoData?.fullWidth || !basePanoData?.fullHeight) { return null; }
303
+
304
+ const croppedX = basePanoData?.croppedX || 0;
305
+ const croppedY = basePanoData?.croppedY || 0;
306
+
307
+ // Compute offset from texture center
308
+ const xOffsetPx = croppedX + locX + locW / 2 - basePanoData.fullWidth / 2;
309
+ const yOffsetPx = -(croppedY + locY + locH / 2 - basePanoData.fullHeight / 2);
310
+ const xOffsetPct = xOffsetPx / basePanoData.fullWidth;
311
+ const yOffsetPct = yOffsetPx / basePanoData.fullHeight;
312
+ const xSizePct = locW / basePanoData.fullWidth;
313
+ const ySizePct = locH / basePanoData.fullHeight;
314
+ const sizePct = (xSizePct + ySizePct) / 2;
315
+
295
316
  const res = {
296
- textureX: +loc[0] + loc[2] / 2,
297
- textureY: +loc[1] + loc[3] / 2,
298
- z: size && size.length == 2 ? (1 - (((loc[2] / size[0]) + (loc[3] / size[1])) / 2)) * 75 : null,
317
+ yaw: xOffsetPct * 2 * Math.PI,
318
+ pitch: yOffsetPct * Math.PI,
319
+ z: (1 - sizePct) * 100
299
320
  };
300
321
 
301
322
  return res;
@@ -1,6 +1,3 @@
1
- // DO NOT REMOVE THE "!": bundled builds breaks otherwise !!!
2
- import maplibregl from "!maplibre-gl";
3
-
4
1
  import { NominatimBaseUrl, AdresseDataGouvBaseURL } from "./services";
5
2
 
6
3
  const PLACETYPE_ZOOM = {
@@ -58,7 +55,7 @@ export function forwardGeocodingNominatim(config) {
58
55
  finalRes.features.push({
59
56
  place_type: ["place"],
60
57
  place_name: plname,
61
- center: new maplibregl.LngLat(...f.geometry.coordinates),
58
+ center: f.geometry.coordinates,
62
59
  zoom: PLACETYPE_ZOOM[f.properties?.geocoding?.type],
63
60
  });
64
61
  listedNames.push(plname);
@@ -195,7 +192,7 @@ export function forwardGeocodingStandard(config, endpoint) {
195
192
  finalRes.features.push({
196
193
  place_type: ["place"],
197
194
  place_name: plname,
198
- center: new maplibregl.LngLat(...f.geometry.coordinates),
195
+ center: f.geometry.coordinates,
199
196
  zoom: PLACETYPE_ZOOM[f.properties.type],
200
197
  });
201
198
  listedNames.push(plname);
@@ -0,0 +1,15 @@
1
+ /* eslint-disable import/no-unused-modules */
2
+
3
+ // Lightweight export for PhotoViewer
4
+ import * as i18n from "./i18n";
5
+ import * as picture from "./picture";
6
+ import * as semantics from "./semantics";
7
+ import * as services from "./services";
8
+ import * as utils from "./utils";
9
+ import * as widgets from "./widgets";
10
+
11
+ export { i18n, picture, semantics, services, utils, widgets };
12
+ export {default as API} from "./API";
13
+ export {default as PhotoAdapter} from "./PhotoAdapter";
14
+ export {default as PresetsManager} from "./PresetsManager";
15
+ export {default as URLHandler} from "./URLHandler";
package/src/utils/map.js CHANGED
@@ -3,7 +3,6 @@ import { COLORS, QUALITYSCORE_RES_FLAT_VALUES, QUALITYSCORE_RES_360_VALUES, QUAL
3
3
  import { autoDetectLocale } from "./i18n";
4
4
  import { isNullId } from "./utils";
5
5
 
6
- export const DEFAULT_TILES = "https://panoramax.openstreetmap.fr/pmtiles/basic.json";
7
6
  export const RASTER_LAYER_ID = "pnx-aerial";
8
7
 
9
8
  export const TILES_PICTURES_ZOOM = 15;
@@ -62,6 +61,13 @@ export const VECTOR_STYLES = {
62
61
  }
63
62
  };
64
63
 
64
+ export const MAP_THEMES = {
65
+ DEFAULT: "default",
66
+ AGE: "age",
67
+ TYPE: "type",
68
+ SCORE: "score",
69
+ };
70
+
65
71
 
66
72
  // See MapLibre docs for explanation of expressions magic: https://maplibre.org/maplibre-style-spec/expressions/
67
73
  const MAP_EXPR_QUALITYSCORE_RES_360 = ["case", ["has", "h_pixel_density"], ["step", ["get", "h_pixel_density"], ...QUALITYSCORE_RES_360_VALUES], 1];
@@ -362,6 +368,122 @@ export function getMissingLayerStyles(sources = {}, layers = []) {
362
368
  return newLayers;
363
369
  }
364
370
 
371
+ /**
372
+ * Get comprehensive values from map filters form
373
+ * @param {Element} mapFiltersMenu The MapFilters DOM Element
374
+ * @param {Element} mapThemeSelect The Map Theme select DOM Element
375
+ * @param {boolean} hasQualityScore Set to true if vector tiles embed quality score data
376
+ * @return {object} The map filters as JSON
377
+ */
378
+ export function mapFiltersFormValues(mapFiltersMenu, mapThemeSelect, hasQualityScore) {
379
+ const fMinDate = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-date-from");
380
+ const fMaxDate = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-date-end");
381
+ const fTypes = mapFiltersMenu?.shadowRoot.querySelectorAll("input[name='pnx-filter-type']");
382
+
383
+ let type = "";
384
+ for(let fTypeId = 0 ; fTypeId < fTypes.length; fTypeId++) {
385
+ const fType = fTypes[fTypeId];
386
+ if(fType.checked) {
387
+ type = fType.value;
388
+ break;
389
+ }
390
+ }
391
+
392
+ let qualityscore = [];
393
+ if(hasQualityScore) {
394
+ const fScore = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-qualityscore");
395
+ qualityscore = (fScore?.grade || "").split(",").map(v => parseInt(v)).filter(v => !isNaN(v));
396
+ if(qualityscore.length == 5) { qualityscore = []; }
397
+ }
398
+
399
+ return {
400
+ minDate: fMinDate?.value,
401
+ maxDate: fMaxDate?.value,
402
+ pic_type: type,
403
+ theme: mapThemeSelect?.value,
404
+ qualityscore,
405
+ };
406
+ }
407
+
408
+ /**
409
+ * Transforms map filters into MapLibre-like JSON filters
410
+ * @param {object} filters Filters returned by MapFilters widgets
411
+ * @param {boolean} [hasGridStats=false] Set to true if vector tiles offer grid statistics
412
+ * @returns {object} Set of variables { mapFilters, mapSeqFilters, mapPicFilters, reloadMapStyle }
413
+ */
414
+ export function mapFiltersToLayersFilters(filters, hasGridStats = false) {
415
+ let mapFilters = {};
416
+ let mapSeqFilters = [];
417
+ let mapPicFilters = [];
418
+ let reloadMapStyle = false;
419
+
420
+ if(filters.minDate && filters.minDate !== "") {
421
+ mapFilters.minDate = filters.minDate;
422
+ mapSeqFilters.push([">=", ["get", "date"], filters.minDate]);
423
+ mapPicFilters.push([">=", ["get", "ts"], filters.minDate]);
424
+ }
425
+
426
+ if(filters.maxDate && filters.maxDate !== "") {
427
+ mapFilters.maxDate = filters.maxDate;
428
+ mapSeqFilters.push(["<=", ["get", "date"], filters.maxDate]);
429
+
430
+ // Get tomorrow date for pictures filtering
431
+ // (because ts is date+time, so comparing date only string would fail otherwise)
432
+ let d = new Date(filters.maxDate);
433
+ d.setDate(d.getDate() + 1);
434
+ d = d.toISOString().split("T")[0];
435
+ mapPicFilters.push(["<=", ["get", "ts"], d]);
436
+ }
437
+
438
+ if(filters.pic_type && filters.pic_type !== "") {
439
+ mapFilters.pic_type = filters.pic_type === "flat" ? "flat" : "equirectangular";
440
+ mapSeqFilters.push(["==", ["get", "type"], mapFilters.pic_type]);
441
+ mapPicFilters.push(["==", ["get", "type"], mapFilters.pic_type]);
442
+ }
443
+ if(hasGridStats) {
444
+ reloadMapStyle = true;
445
+ }
446
+
447
+ if(filters.camera && filters.camera !== "") {
448
+ mapFilters.camera = filters.camera;
449
+ // low/high model hack : to enable fuzzy filtering of camera make and model
450
+ const lowModel = filters.camera.toLowerCase().trim() + " ";
451
+ const highModel = filters.camera.toLowerCase().trim() + "zzzzzzzzzzzzzzzzzzzz";
452
+ const collator = ["collator", { "case-sensitive": false, "diacritic-sensitive": false } ];
453
+ mapSeqFilters.push([">=", ["get", "model"], lowModel, collator]);
454
+ mapSeqFilters.push(["<=", ["get", "model"], highModel, collator]);
455
+ mapPicFilters.push([">=", ["get", "model"], lowModel, collator]);
456
+ mapPicFilters.push(["<=", ["get", "model"], highModel, collator]);
457
+ }
458
+
459
+ if(filters.qualityscore && filters.qualityscore.length > 0) {
460
+ mapFilters.qualityscore = filters.qualityscore;
461
+ mapSeqFilters.push(["in", MAP_EXPR_QUALITYSCORE, ["literal", mapFilters.qualityscore]]);
462
+ mapPicFilters.push(["in", MAP_EXPR_QUALITYSCORE, ["literal", mapFilters.qualityscore]]);
463
+ }
464
+
465
+ if(filters.theme && Object.values(MAP_THEMES).includes(filters.theme)) {
466
+ mapFilters.theme = filters.theme;
467
+ reloadMapStyle = true;
468
+ }
469
+
470
+ if(mapSeqFilters.length == 0) { mapSeqFilters = null; }
471
+ else {
472
+ mapSeqFilters.unshift("all");
473
+ }
474
+
475
+ if(mapPicFilters.length == 0) { mapPicFilters = null; }
476
+ else {
477
+ mapPicFilters.unshift("all");
478
+ mapPicFilters = ["step", ["zoom"],
479
+ true,
480
+ TILES_PICTURES_ZOOM, mapPicFilters
481
+ ];
482
+ }
483
+
484
+ return { mapFilters, mapSeqFilters, mapPicFilters, reloadMapStyle };
485
+ }
486
+
365
487
  /**
366
488
  * Get cleaned-up layer ID for a specific user.
367
489
  * @param {string} userId The user UUID (or "geovisio" for general layer)
@@ -4,6 +4,14 @@
4
4
  */
5
5
 
6
6
 
7
+ /**
8
+ * Default map background tiles
9
+ * @returns {string} The website URL
10
+ */
11
+ export function MapTiles() {
12
+ return "https://panoramax.openstreetmap.fr/pmtiles/basic.json";
13
+ }
14
+
7
15
  /**
8
16
  * Panoramax website URL
9
17
  * @returns {string} The website URL
@@ -4,10 +4,10 @@ exports[`forwardGeocodingBAN works 1`] = `
4
4
  Object {
5
5
  "features": Array [
6
6
  Object {
7
- "center": Object {
8
- "lat": 47.8,
9
- "lng": -1.7,
10
- },
7
+ "center": Array [
8
+ 2.290084,
9
+ 49.897443,
10
+ ],
11
11
  "place_name": "8 Boulevard du Port, Amiens",
12
12
  "place_type": Array [
13
13
  "place",
@@ -22,10 +22,10 @@ exports[`forwardGeocodingNominatim works 1`] = `
22
22
  Object {
23
23
  "features": Array [
24
24
  Object {
25
- "center": Object {
26
- "lat": 47.8,
27
- "lng": -1.7,
28
- },
25
+ "center": Array [
26
+ 2.3483915,
27
+ 48.8534951,
28
+ ],
29
29
  "place_name": "Paris, Île-de-France",
30
30
  "place_type": Array [
31
31
  "place",