Crude spherical photos

This commit is contained in:
Nicolò P 2024-11-24 12:26:44 +01:00
parent 2650df69a3
commit 760fb3e960
9 changed files with 193 additions and 70 deletions

2
.gitignore vendored
View File

@ -11,3 +11,5 @@ progetto_QGIS/
conf.json
shapefile
config.js
# Don't track shperical photos
webgis/img/spherical/*.png

View File

@ -155,4 +155,9 @@ a:visited {
.docs-title {
width: 300px;
max-width: 300px;
}
/* Panoramas */
#pano-viewer {
height: 100%;
width: 100%;
}

View File

View File

@ -6,6 +6,15 @@
<link rel="stylesheet" href="../css/app.css" />
<link rel="stylesheet" href="js/vendor/spotlight.js/dist/css/spotlight.min.css" />
<link rel="stylesheet" href="js/vendor/@kalisio/leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css" />
<link rel="stylesheet" href="js/vendor/@photo-sphere-viewer/core/index.css" />
<script type="importmap">
{
"imports": {
"three": "./js/vendor/three/build/three.module.js",
"@photo-sphere-viewer/core": "./js/vendor/@photo-sphere-viewer/core/index.module.js"
}
}
</script>
<script src="js/vendor/leaflet/dist/leaflet.js"></script>
<script src="js/vendor/@kalisio/leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js"></script>
<script src="js/index.js" type="module"></script>
@ -156,5 +165,13 @@
</div>
<button class="modal-close is-large" aria-label="close"></button>
</div>
<!-- Spherical photo modal -->
<div class="modal" id="spherical-modal">
<div class="modal-background"></div>
<div class="modal-content has-background-white" style="min-height: 95vh;">
<div id="pano-viewer"></div>
</div>
<button class="modal-close is-large" aria-label="close"></button>
</div>
</body>
</html>

View File

@ -65,7 +65,7 @@ export class NotConservedSheet {
`;
this.biblioElements.push(`
<div class="p-2" id="ref-${record.id}">
<div class="p-2 mt-2" id="ref-${record.id}">
<p>${record.reference}</p>
</div>
`

View File

@ -0,0 +1,18 @@
import { Viewer } from '@photo-sphere-viewer/core';
/**
* @class SphericalPhoto
*/
export class SphericalPhoto {
basePath = '/webgis/img/spherical';
/**
* @param {string} filename Name of the spherical image
*/
setViewer(filename) {
new Viewer({
container: document.querySelector('#pano-viewer'),
panorama: `${this.basePath}/${filename}`,
defaultZoomLvl: 0,
});
}
}

View File

@ -2,6 +2,8 @@
import UI from "./ui.js";
import API_CONFIG from "./config.js";
import Icons from "./icons.js";
import { SphericalPhoto } from "./components/SphericalPhoto.js";
const MAPBOX_TOKEN = 'pk.eyJ1Ijoibmljb3BhIiwiYSI6ImNseWNwZjJjbjFidzcya3BoYTU0bHg4NnkifQ.3036JnCXZTEMt6jVgMzVRw';
const BASE_URL = location.href;
@ -111,85 +113,22 @@ GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
});
let map = L.map(mapId, {
attributionControl: false,
//attributionControl: false,
minZoom: 2,
}).setView(this.CENTER_COORDS, zoomLevel);
map.crs = L.CRS.EPSG4326;
const {baseMap, sitesGroup} = await this.initLayers(map);
let layerVincoli = await this.loadLayer('vincoli.geojson', optionsVincoli);
let layerPaesistici = await this.loadLayer('paesistici.geojson', optionsPaesistici);
let notConserData = await fetch(`${API_URL}/not_conserved`)
.then(data => data.json());
// Add scale and ruler controls
L.control.scale({imperial: false}).addTo(map);
L.control.graphicScale({fill: 'hollow', position: 'bottomright'}).addTo(map);
let sitesMarkers = [];
let siteIcon = L.icon(
{
iconUrl: 'img/icons/siti.png',
iconSize: [24, 36],
iconAnchor: [12, 32],
tooltipAnchor: [0, -26],
}
);
let notConserIcon = L.icon(
{
iconUrl: 'img/icons/non_conserv.png',
iconSize: [24, 36],
iconAnchor: [12, 32],
tooltipAnchor: [0, -26],
}
);
let layerVincoli = await this.loadLayer('vincoli.geojson', optionsVincoli);
let layerPaesistici = await this.loadLayer('paesistici.geojson', optionsPaesistici);
let notConserved = [];
for (let record of notConserData.records) {
notConserved.push(L.marker(
record.coordinates,
{icon: notConserIcon}
).bindTooltip(record.denomination)
.on(
'click',
() => UI.openNotConserModal(record, '#not-conser-data')
)
);
}
for (let id in MARKER_NAMES.sites) {
let layer = sitesGroup.customGetLayer(id);
let coords = layer.getBounds().getCenter();
const fromStorage = localStorage.getItem(id);
let data = {};
if (fromStorage !== 'undefined') {
try {
data = JSON.parse(fromStorage);
const lat = data?.coordinates[0] ?? coords.lat;
const lon = data?.coordinates[1] ?? coords.lng;
coords = [lat, lon];
} catch (error) {
console.log(error);
}
} else {
data = await GIS._fetchData('site/' + id);
}
const marker = L.marker(coords, { icon: siteIcon })
.bindTooltip(MARKER_NAMES.sites[id])
.openTooltip();
marker.on('click', () => UI.openSiteModal(data, '#site-data'));
sitesMarkers.push(marker);
}
let markersGroup = L.layerGroup(sitesMarkers);
let notConservedGroup = L.layerGroup(notConserved);
let markersGroup = await this.sitesMarkers(sitesGroup);
let notConservedGroup = await this.notConserved();
const archeo = {
'Beni archeologici (punti)' : markersGroup,
@ -211,6 +150,68 @@ GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
// TODO Horrible?
return {map: map, sites: sitesGroup};
}
/**
* Create markers for sites
* @param {L.LayerGroup} sitesGroup
* @returns {L.LayerGroup}
*/
GIS.sitesMarkers = async function (sitesGroup) {
let sitesMarkers = [];
for (let id in MARKER_NAMES.sites) {
let layer = sitesGroup.customGetLayer(id);
let coords = layer.getBounds().getCenter();
const fromStorage = localStorage.getItem(id);
let data = {};
if (fromStorage !== 'undefined') {
try {
data = JSON.parse(fromStorage);
const lat = data?.coordinates[0] ?? coords.lat;
const lon = data?.coordinates[1] ?? coords.lng;
coords = [lat, lon];
} catch (error) {
console.log(error);
}
} else {
data = await GIS._fetchData('site/' + id);
}
const marker = L.marker(coords, { icon: Icons.site })
.bindTooltip(MARKER_NAMES.sites[id])
.openTooltip();
marker.on('click', () => UI.openSiteModal(data, '#site-data'));
sitesMarkers.push(marker);
}
return L.layerGroup(sitesMarkers);
}
/**
* Create not conserved group
* @returns {L.LayerGroup}
*/
GIS.notConserved = async function () {
let notConserData = await fetch(`${API_URL}/not_conserved`)
.then(data => data.json());
let notConserved = [];
for (let record of notConserData.records) {
notConserved.push(L.marker(
record.coordinates,
{icon: Icons.notConserved}
).bindTooltip(record.denomination)
.on(
'click',
() => UI.openNotConserModal(record, '#not-conser-data')
)
);
}
return L.layerGroup(notConserved);
}
/*
GIS._prepareLayers = async function(layer) {
const fromStorage = localStorage.getItem(layer.id);
@ -321,7 +322,51 @@ GIS.initLayers = async function(map) {
return {baseMap, sitesGroup};
}
/**
* @todo Distinguere tipo di geojson per contenuto popup
* Toggle spherical photos on the map
* based on zoom level
*/
GIS.toggleSpherical = async function(map) {
// TODO Create points layer
const spherical = await this._fetchData('spherical/by/gis/gradola');
let sphereMarkers = [];
const sPhoto = new SphericalPhoto();
if (spherical) {
for (let pano of spherical) {
let marker = L.marker(pano.coordinates, {
icon: Icons.camera,
});
marker.bindTooltip('Apri foto sferica');
marker.on('click', () => {
let modal = document.querySelector('#spherical-modal');
let viewer = document.querySelector('#pano-viewer');
viewer.innerHTML = '';
modal.classList.add('is-active');
modal.querySelector('.modal-background').addEventListener('click', () => {
modal.classList.remove('is-active');
});
modal.querySelector('.modal-close').addEventListener('click', () => {
modal.classList.remove('is-active');
})
sPhoto.setViewer(pano.filename);
});
sphereMarkers.push(marker);
}
}
let group = L.layerGroup(sphereMarkers);
map.on('zoomend', () => {
if (map.getZoom() >= 19) {
group.addTo(map);
} else {
group.remove();
}
});
}
/**
* @param {string} geoJSON
* @param {{color, opacity, weight, fillColor, fillOpacity}} options Style options for features
* @param {boolean} popup Should the features have a popup?
@ -379,6 +424,14 @@ GIS.layerData = async function (layerId) {
return data;
}
/**
* Get spherical photos for a site
* @param {int} siteId
*/
GIS.getSpherical = async function (siteId) {
const data = await this._fetchData('spherical/' + siteId);
}
/**
* Cache data from DB in local storage
* for a given layer

26
webgis/js/icons.js Normal file
View File

@ -0,0 +1,26 @@
// Global leaflet
/**
* @namespace Icons
*/
const Icons = {};
Icons.site = L.icon(
{
iconUrl: 'img/icons/siti.png',
iconSize: [24, 36],
iconAnchor: [12, 32],
tooltipAnchor: [0, -26],
}
);
Icons.notConserved = L.icon(
{
iconUrl: 'img/icons/non_conserv.png',
iconSize: [24, 36],
iconAnchor: [12, 32],
tooltipAnchor: [0, -26],
}
);
Icons.camera = L.divIcon({className: 'fa fa-2x fa-camera'});
export default Icons;

View File

@ -9,6 +9,8 @@ document.addEventListener('DOMContentLoaded', async () => {
map._container.setAttribute('aria-busy', false);
GIS.toggleSpherical(map);
UI.addCenterMapControl(map, GIS.CENTER_COORDS, GIS.INIT_ZOOM);
UI.toggleMenu('siti');
UI.toggleBurger('navbar-burger');