@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.
- package/CHANGELOG.md +3 -0
- package/build/index.js +11 -9
- package/build/index.js.map +1 -1
- package/build/photoviewer.css +36 -0
- package/build/photoviewer.css.map +1 -0
- package/build/photoviewer.js +2257 -0
- package/build/photoviewer.js.map +1 -0
- package/config/paths.js +1 -0
- package/config/webpack.config.js +44 -165
- package/docs/index.md +14 -3
- package/package.json +1 -1
- package/src/components/core/Basic.js +2 -2
- package/src/components/core/Viewer.js +2 -30
- package/src/components/index.js +2 -2
- package/src/components/index_photoviewer.js +8 -0
- package/src/components/menus/MapFilters.js +17 -1
- package/src/components/menus/index_photoviewer.js +11 -0
- package/src/components/ui/MapMore.js +3 -84
- package/src/components/ui/index.js +1 -1
- package/src/components/ui/index_photoviewer.js +22 -0
- package/src/components/ui/widgets/MapFiltersButton.js +1 -1
- package/src/components/ui/widgets/MapLayersButton.js +1 -1
- package/src/components/ui/widgets/Zoom.js +1 -1
- package/src/components/ui/widgets/index_photoviewer.js +9 -0
- package/src/index.js +2 -2
- package/src/index_photoviewer.js +6 -0
- package/src/utils/API.js +3 -1
- package/src/utils/InitParameters.js +26 -5
- package/src/utils/geocoder.js +2 -5
- package/src/utils/index_photoviewer.js +15 -0
- package/src/utils/map.js +123 -1
- package/src/utils/services.js +8 -0
- package/tests/utils/__snapshots__/geocoder.test.js.snap +8 -8
|
@@ -289,13 +289,34 @@ export function xyzParamToPSVPosition(str) {
|
|
|
289
289
|
* @private
|
|
290
290
|
*/
|
|
291
291
|
function xywhParamToPSVPosition(str, picmeta) {
|
|
292
|
-
|
|
292
|
+
let loc = (str || "").split(",");
|
|
293
293
|
if (loc.length === 4 && !loc.some(v => isNaN(v))) {
|
|
294
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
z:
|
|
317
|
+
yaw: xOffsetPct * 2 * Math.PI,
|
|
318
|
+
pitch: yOffsetPct * Math.PI,
|
|
319
|
+
z: (1 - sizePct) * 100
|
|
299
320
|
};
|
|
300
321
|
|
|
301
322
|
return res;
|
package/src/utils/geocoder.js
CHANGED
|
@@ -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:
|
|
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:
|
|
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)
|
package/src/utils/services.js
CHANGED
|
@@ -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":
|
|
8
|
-
|
|
9
|
-
|
|
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":
|
|
26
|
-
|
|
27
|
-
|
|
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",
|