Reorganize structure with entry point

This commit is contained in:
2024-11-18 11:57:38 +01:00
parent e9c8ac6b07
commit 59204a591f
41 changed files with 215 additions and 155 deletions

1
webgis/js/.yarnrc Normal file
View File

@@ -0,0 +1 @@
--*.modules-folder "./vendor"

View File

@@ -0,0 +1,67 @@
/**
* Component to render data for not conserved assets sheet
* @class NotConservedSheet
*/
export class NotConservedSheet {
/**
* @param {object} data
*/
set siteData(data) {
this._data = data;
}
render() {
return `
<div class="container ml-3">
<p class="p-2">
<span class="icon has-text-link">
<i class="fa fa-tag"></i>
</span>
<strong>Denominazione:</strong> ${this._data.denomination}
</p>
<p class="p-2">
<span class="icon has-text-link">
<i class="fa fa-hourglass"></i>
</span>
<strong>Periodo:</strong> ${this._data.period}
</p>
<p class="p-2">
<span class="icon has-text-link">
<i class="fa fa-map"></i>
</span>
<strong>Località generica:</strong> ${this._data.genericLocation}
</p>
<p class="mt-4 pl-2 pr-5">
<strong class="pb-3">Descrizione</strong></br>
${this._data.shortDescription}
</p>
<p class="mt-4 pl-2 pr-5">
<span class="icon has-text-link">
<i class="fa fa-book"></i>
</span>
<strong>Bibliografia:</strong> ${this.biblio(this._data.bibliography)}
</p>
<p class="p-2">
<span class="icon has-text-link">
<i class="fa fa-map"></i>
</span>
<strong>Autore scheda:</strong> ${this._data.author}
</p>
</div>`;
}
biblio(records) {
let biblio = '';
if (records.length) {
records.forEach(record => {
biblio += `
<span class="is-capitalized">${record.citation.toLowerCase()}</span>,
${record.pages};
`;
});
}
return biblio.trim().slice(0, -1);
}
}

View File

@@ -0,0 +1,55 @@
/**
* Component to render data for site documents
* @class SiteDocuments
*/
export class SiteDocuments {
/**
* @param {object} data
*/
set siteData(data) {
this._siteData = data;
}
render() {
let content = `
<div class="has-bottom-border">
<div class="p-2">
<table class="p-4 table is-fullwidth is-striped">
<thead>
<tr><th colspan=3 class="p-2 has-text-centered is-size-5">Documentazione di archivio</th>
<tr><th>Titolo</th><th>Autori</th><th>Download</th></tr>
</thead>
<tbody>
`;
for (const doc of this._siteData.documents.filter(d => d.type === 'documentazione')) {
content += `
<tr><td>${doc.title}</td><td>${doc.authors}</td><td><a class="button is-link has-text-white" href="docs/${doc.filename}">
<i class="fa fa-download mr-2"></i> PDF
</a></td></tr>
`;
}
content += `
</tbody>
<thead>
<tr><th colspan=3 class="p-2 has-text-centered is-size-5">Pubblicazioni del progetto Carta Archeologica</th>
<tr><th>Titolo</th><th>Autori</th><th>Download</th></tr>
</thead>
<tbody>
`;
for (const doc of this._siteData.documents.filter(d => d.type === 'pubblicazione')) {
content += `
<tr><td>${doc.title}</td><td>${doc.authors}</td><td><a class="button is-link has-text-white" href="docs/${doc.filename}">
<i class="fa fa-download mr-2"></i> PDF
</a></td></tr>
`;
}
content += `
</tbody>
</table>
</div>
</div>
`;
return content;
}
}

View File

@@ -0,0 +1,31 @@
/**
* Component to render data for site photos
* @class SitePhotos
*/
export class SitePhotos {
/**
* @param {object} data
*/
set siteData(data) {
this._siteData = data;
}
render() {
let content = `<div class="content has-text-centered">
<p class="is-size-5 mt-3">Fotografie</p>`;
content += `
<div style="max-width: 70%; margin: 0 auto">
<p class="is-size-6 has-text-centered">Cliccare sull'immagine per aprire la gallery</p>
<figure class="is-relative is-clickable has-text-centered" id="gallery-2">
<img src="img/${this._siteData.filename}" width="300"/>
<div class="icon overlay is-flex is-justify-content-center is-align-items-center">
<i class="is-flex fa fa-2x fa-play-circle"></i>
</div>
</figure>
</div>
</div>
`;
return content;
}
}

View File

@@ -0,0 +1,67 @@
/**
* Component to render data for site sheet
* @class SiteSheet
*/
export class SiteSheet {
/**
* @param {object} data
*/
set siteData(data) {
this._siteData = data;
}
render() {
return `<div class="container has-bottom-border">
<table class="table is-fullwidth is-striped">
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Identificazione</th></tr>
<tr><th>Denominazione</th><td>${this._siteData.denomination}</td></tr>
<tr><th>Definizione</th><td>${this._siteData.definition}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Localizzazione geografico-amministrativa</th></tr>
<tr><th>Comune</th><td>${this._siteData.municipality}</td></tr>
<tr><th>Indirizzo</th><td>${this._siteData.address}</td></tr>
<tr><th>Località</th><td>${this._siteData.place}</td></tr>
<tr><th>Localizzazione</th><td>${this._siteData.localization}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Cronologia</th></tr>
<tr><th>Periodo</th><td>${this._siteData.period}</td></tr>
<tr><th>Fase</th><td>${this._siteData.phase}</td></tr>
<tr><th>Cronologia</th><td>${this._siteData.chronology}</td></tr>
<tr><th>Motivazione cronologia</th><td>${this._siteData.motivation}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Conservazione e condizione giuridica</th></tr>
<tr><th>Stato di conservazione</th><td>${this._siteData.conservationState}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Ritrovamento e materiali</th></tr>
<tr><th>Anno di ritrovamento</th><td>${this._siteData.finding}</td></tr>
<tr><th>Materiali rinvenuti</th><td>${this._siteData.materials}</td></tr>
<tr><th>Luogo custodia materiali</th><td>${this._siteData.conservationPlace}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Descrizione</th></tr>
<tr><td class="pr-6 pl-6 pt-3" colspan="2">${this._siteData.description}</td></tr>
</table>
</div>`;
}
renderShort() {
return `
<div class="container ml-3">
<p class="p-2">
<span class="icon has-text-link">
<i class="fa fa-tag"></i>
</span>
<strong>Denominazione:</strong> ${this._siteData.denomination}
</p>
<p class="p-2">
<span class="icon has-text-link">
<i class="fa fa-hourglass"></i>
</span>
<strong>Periodo:</strong> ${this._siteData.period}
</p>
<p class="p-2">
<span class="icon has-text-link">
<i class="fa fa-map"></i>
</span>
<strong>Località generica:</strong> ${this._siteData.genericPlace}
</p>
<p class="mt-4 pl-2 pr-5">
${this._siteData.shortDescription}
</p>
</div>`;
}
}

View File

@@ -0,0 +1,31 @@
/**
* Component to render data for site surveys
* @class SiteSurveys
*/
export class SiteSurveys {
/**
* @param {object} data
*/
set siteData(data) {
this._siteData = data;
}
render() {
let content = `<div class="has-text-centered has-bottom-border mb-5 pb-5">
<p class="is-size-5 mt-3">Elaborazioni CNR da rilievi</p>`;
content += `
<div style="max-width: 70%; margin: 0 auto">
<p class="is-size-6 has-text-centered">Cliccare sull'immagine per aprire la gallery</p>
<figure class="is-relative has-text-centered is-clickable" id="gallery-1">
<img src="img/${this._siteData.filename}" width="300"/>
<div class="icon overlay is-flex is-justify-content-center is-align-items-center">
<i class="is-flex fa fa-2x fa-play-circle"></i>
</div>
</figure>
</div>
</div>
`;
return content;
}
}

411
webgis/js/gis.js Normal file
View File

@@ -0,0 +1,411 @@
'use strict';
import UI from "./ui.js";
const MAPBOX_TOKEN = 'pk.eyJ1Ijoibmljb3BhIiwiYSI6ImNseWNwZjJjbjFidzcya3BoYTU0bHg4NnkifQ.3036JnCXZTEMt6jVgMzVRw';
const BASE_URL = location.href;
let API_URL = '';
if (BASE_URL.includes('localhost')) {
console.log(BASE_URL);
API_URL = `https://caprigis-api.ddev.site`;
} else {
API_URL = 'https://testaec.electricmandarine.cloud/gisdb/gis';
}
// Global leaflet
/**
* @namespace GIS
*/
const GIS = {};
GIS.CENTER_COORDS = [40.5492, 14.2317];
GIS.INIT_ZOOM = 14;
const optionsVincoli = {
color: '#222',
opacity: 0.8,
weight: 1,
fillColor: '#fa7861',
fillOpacity: 0.8
};
const optionsSiti = {
color: '#800040',
opacity: 1,
weight: 1.5,
fillColor: '#800040',
fillOpacity: 0.8
};
const optionsPaesistici = {
color: '#222',
opacity: 1,
weight: 1.5,
fillColor: '#88d28d',
fillOpacity: 0.8
};
const optionsFabbricati = {
color: '#222',
opacity: 1,
weight: 1.5,
fillColor: '#5b5d5f',
fillOpacity: 0.8
};
const MARKER_NAMES = {
sites: {
'gradola' : 'Villa di Gradola',
'damecuta' : 'Villa di Damecuta',
'matermania' : 'Grotta di Matermania',
'arsenale' : 'Grotta dell\'Arsenale',
'tiberio' : 'Bagni di Tiberio',
'mura' : 'Mura greche',
'san_michele' : 'Villa San Michele',
},
};
/**
* Capitalize a text string
* @todo Move to utils
* @param {?string} text
* @returns {?string} The capitalized string or null
*/
function capitalize(text) {
let capital = text;
if (text) {
let words = text.split(' ');
capital = '';
for (let w of words) {
w = w[0].toUpperCase() + w.slice(1);
capital += w + ' ';
}
capital.trimEnd();
}
return capital;
}
/**
* @param {string} mapId
* @param {number} zoomLevel
* @returns {{map: Map, sites: Layer}}
*/
GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
L.LayerGroup.include({
customGetLayer : function (id) {
for (let l in this._layers) {
if (this._layers[l].id === id) {
return this._layers[l];
}
}
}
});
let map = L.map(mapId, {
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 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);
const archeo = {
'Beni archeologici (punti)' : markersGroup,
'Beni archeologici (strutture)' : sitesGroup,
'Beni non conservati' : notConservedGroup,
'Vincoli archeologici' : layerVincoli,
'Vincoli paesistici' : layerPaesistici,
};
markersGroup.addTo(map);
sitesGroup.addTo(map);
notConservedGroup.addTo(map);
L.control.layers(
baseMap,
archeo,
).addTo(map);
// TODO Horrible?
return {map: map, sites: sitesGroup};
}
/*
GIS._prepareLayers = async function(layer) {
const fromStorage = localStorage.getItem(layer.id);
let data = {};
let coords = layer.getBounds().getCenter();
if (fromStorage !== 'undefined') {
try {
data = JSON.parse(fromStorage);
const lat = data?.lat ?? coords[0];
const lon = data?.lon ?? coords[1];
coords = [lat, lon];
} catch (error) {
console.log(error);
}
} else {
data = await GIS._fetchData(layer.id);
}
// TODO: terrible!
if (!layer.id.includes('area')) {
const marker = L.marker(coords)
.addTo(map)
.bindTooltip(
Object.keys(archeo).find(k => archeo[k] === layer)
.replace(/\s\(.*$/, '')
)
.openTooltip();
if (typeof data === 'object') {
marker.on('click', () => UI.openModal(data, '#site-data'));
}
}
}
*/
/**
* Adds layers to map and returns an object
* with {baseMap, archeoLayers, sitesLayerGroup}
* @param {L.Map} map
* @returns {{baseMap: {"OpenStreetMap": L.TileLayer}, archeo: object, sitesGroup: L.LayerGroup}}
*/
GIS.initLayers = async function(map) {
let layerMater = await this.loadLayer('matermania.geojson', optionsSiti, false);
let layerMaterArea = await this.loadLayer('matermania_area.geojson', optionsSiti, false);
let layerArsenale = await this.loadLayer('arsenale.geojson', optionsSiti, false);
let layerArsenaleArea = await this.loadLayer('arsenale_area.geojson', optionsSiti, false);
let layerGradola = await this.loadLayer('gradola.geojson', optionsSiti, false);
let layerGradolaArea = await this.loadLayer('gradola_area.geojson', optionsSiti, false);
let layerMura = await this.loadLayer('mura.geojson', optionsSiti, false);
let layerSanMichele = await this.loadLayer('san_michele.geojson', optionsSiti, false);
let layerDamecuta = await this.loadLayer('damecuta.geojson', optionsSiti, false);
let layerTiberio = await this.loadLayer('tiberio.geojson', optionsSiti, false);
layerMater.id = 'matermania';
layerMaterArea.id = 'matermania_area';
layerGradola.id = 'gradola';
layerGradolaArea.id = 'gradola_area';
layerArsenale.id = 'arsenale';
layerArsenaleArea.id = 'arsenale_area';
layerMura.id = 'mura';
layerSanMichele.id = 'san_michele';
layerDamecuta.id = 'damecuta';
layerTiberio.id = 'tiberio';
let osmap = new L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxNativeZoom : 22,
maxZoom: 22,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
});
let mapbox = new L.tileLayer(`https://api.mapbox.com/v4/{id}/{z}/{x}/{y}@2x.jpg90?access_token=${MAPBOX_TOKEN}`, {
id : 'mapbox.satellite',
tileSize : 512,
zoomOffset : -1,
maxNativeZoom : 22,
maxZoom: 22,
attribution: '&copy; Mapbox'
});
let boundaries = await this.loadLayer('confini.geojson', {}, false);
let buildings = await this.loadLayer('fabbricati.geojson', optionsFabbricati, false);
let baseCatasto = new L.LayerGroup([buildings, boundaries]);
const sitesGroup = new L.LayerGroup([
layerMater,
layerMaterArea,
layerGradola,
layerGradolaArea,
layerArsenale,
layerArsenaleArea,
layerMura,
layerSanMichele,
layerDamecuta,
layerTiberio,
]);
const baseGroup = new L.LayerGroup([osmap]);
baseGroup.addTo(map);
const baseMap = {
"OpenStreetMap" : osmap,
"Satellite" : mapbox,
"Cartografia catastale" : baseCatasto,
};
return {baseMap, sitesGroup};
}
/**
* @todo Distinguere tipo di geojson per contenuto popup
* @param {string} geoJSON
* @param {{color, opacity, weight, fillColor, fillOpacity}} options Style options for features
* @param {boolean} popup Should the features have a popup?
*/
GIS.loadLayer = async function (geoJSON, options, popup = true) {
const geo = await fetch(`${BASE_URL}/geojson/${geoJSON}`)
.then(res => res.json())
.catch(error => console.error(`Can't load layer ${geoJSON}. Reason: ${error}`));
const layerId = geoJSON.replace('.geojson', '');
this.cacheDBData(layerId);
// Show data from feature in popUp?
const layer = new L.geoJson(geo, {
style: function () {
return options;
},
onEachFeature: function (feature, layer) {
if (popup) {
layer.bindPopup(GIS.featurePopup(geoJSON, feature));
}
else {
layer.on("click", async () => {
let data = GIS.layerData(layerId);
if (typeof data === 'object') {
UI.openSiteModal(data);
}
});
}
}
});
return layer;
}
/**
* Retrieves data for a given layer
* @param {string} layerId
* @returns {object} Data for this layer from DB or cache
*/
GIS.layerData = async function (layerId) {
const fromStorage = localStorage.getItem(layerId);
let data = {};
if (fromStorage !== 'undefined') {
try {
data = JSON.parse(fromStorage);
} catch (error) {
console.log(error);
}
} else {
data = await GIS._fetchData('site/' + layerId);
}
return data;
}
/**
* Cache data from DB in local storage
* for a given layer
* @param {string} layerId
*/
GIS.cacheDBData = async function (layerId) {
const data = await this._fetchData('site/' + layerId);
localStorage.setItem(layerId, JSON.stringify(data));
}
/**
* Generate proper content for features popup
* @todo Hard-coded names!!
*
* @param {string} layerName
* @param {object} feature
* @returns {string} The popup's content
*/
GIS.featurePopup = function (layerName, feature) {
const html = `
<table class="table is-striped is-size-6 m-2">
<tr><th>Oggetto</th><td>${feature.properties.OGGETTO}</td></tr>
<tr><th>Anno</th><td>${feature.properties.ANNO}</td></tr>
<tr><th>Comune</th><td>${capitalize(feature.properties.COMUNE)}</td></tr>
<tr><th>Località</th><td>${capitalize(feature.properties.LOCALITA)}</td></tr>
<tr><th>Proprietà</th><td>${capitalize(feature.properties.PROPRIETA)}</td></tr>
</table>
`;
const content = {
'vincoli.geojson' : html,
'paesistici.geojson' : html,
};
return content[layerName];
}
/**
* Fetch data from API
* @param {string} recordId
*/
GIS._fetchData = async function (recordId) {
const data = await fetch(`${API_URL}/${recordId}`)
.then(res => res.json())
.catch(err => console.log('Error fetching data from DB: ' + err));
return data;
}
export default GIS;

17
webgis/js/index.js Normal file
View File

@@ -0,0 +1,17 @@
import GIS from './gis.js';
import UI from './ui.js';
document.addEventListener('DOMContentLoaded', async () => {
let progress = document.querySelector('progress');
const init = await GIS.initMap('map');
progress.classList.add('is-hidden');
let {map, sites} = init;
map._container.setAttribute('aria-busy', false);
UI.addCenterMapControl(map, GIS.CENTER_COORDS, GIS.INIT_ZOOM);
UI.toggleMenu('siti');
UI.toggleBurger('navbar-burger');
UI.sitesMenu('.menu-list', map, sites);
UI.projectInfo('#project-info');
});

15
webgis/js/jsdoc.json Normal file
View File

@@ -0,0 +1,15 @@
{
"source": {
"include": ["gis.js", "ui.js", "index.js"],
"includePattern": ".js$",
"excludePattern": "(vendor/|docs)"
},
"plugins": ["plugins/markdown"],
"opts": {
"encoding": "utf8",
"destination": "docs/",
"recurse": true
}
}

15
webgis/js/package.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "caprigis",
"version": "0.01",
"main": "index.js",
"author": "Nicolò P.",
"license": "GPLv3",
"dependencies": {
"@kalisio/leaflet-graphicscale": "^1.0.0",
"bulma": "^1.0.1",
"fontawesome-free": "^1.0.4",
"leaflet": "^1.9.4",
"spotlight.js": "^0.7.8"
},
"devDependencies": {}
}

230
webgis/js/ui.js Normal file
View File

@@ -0,0 +1,230 @@
'use strict';
import Spotlight from './vendor/spotlight.js/src/js/spotlight.js';
import { SiteSheet } from './components/SiteSheet.js';
import { SiteDocuments } from './components/SiteDocuments.js';
import { SiteSurveys } from './components/SiteSurveys.js';
import { SitePhotos } from './components/SitePhotos.js';
import { NotConservedSheet } from './components/NotConservedSheet.js';
import GIS from './gis.js';
/**
* @namespace UI
*/
const UI = {};
/**
* Add a Leaflet control to center the map
* @param {Map} map The Leaflet map object
* @param {LatLngExpression} centerCoords The coordinates to center the map
* @param {number} zoom Zoom level
*/
UI.addCenterMapControl = function (map, centerCoords, zoom) {
L.Control.CenterControl = L.Control.extend({
options: {
position: 'topleft'
},
onAdd: function (map) {
let controlDiv = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar');
L.DomEvent
.addListener(controlDiv, 'click', L.DomEvent.stopPropagation)
.addListener(controlDiv, 'click', L.DomEvent.preventDefault)
.addListener(controlDiv, 'click', function () {
map.setView(centerCoords, zoom, {animate: true});
}
);
let controlUI = L.DomUtil.create('a', 'leaflet-draw-edit-remove', controlDiv);
controlUI.title = 'Centra la mappa';
controlUI.href = '#';
controlUI.innerHTML = `
<span class="icon is-medium">
<i class="fas fa-lg fa-crosshairs"></i>
</span>
`;
return controlDiv;
}
});
let centerCtr = new L.Control.CenterControl();
map.addControl(centerCtr);
}
/**
* Toggle burger menu for small screens
* @param {string} burgerClass The CSS class of the burger element
*/
UI.toggleBurger = function(burgerClass) {
const burger = document.querySelector(`.${burgerClass}`);
burger.addEventListener('click', () => {
burger.classList.toggle('is-active');
const menuId = burger.getAttribute('data-target');
document.querySelector(`#${menuId}`).classList.toggle('is-active');
});
}
/**
* Toggle side menu and rescale map container
* @param {string} triggerId The ID of the trigger element
*/
UI.toggleMenu = function (triggerId) {
const trigger = document.querySelector(`#${triggerId}`);
trigger.addEventListener('click', () => {
const menu = document.querySelector('#menu');
menu.classList.toggle('is-hidden');
menu.classList.toggle('is-2');
document.querySelector('#map').parentElement.classList.toggle('is-full');
});
}
/**
* Open a modal with DB site data
* @todo Refactor!!! Web components??
* @param {object} data The data retrieved from the DB to display as modal content
* @param {string} selector The modal selector
*/
UI.openSiteModal = async function (data, selector) {
const modal = document.querySelector(selector);
const tabs = modal.querySelector('.tabs > ul');
let siteSheet = new SiteSheet();
siteSheet.siteData = data;
modal.querySelector('#short-sheet').innerHTML = siteSheet.renderShort();
modal.querySelector('#site-sheet').innerHTML = siteSheet.render();
let images = modal.querySelector('#photos');
if (data.documents?.length) {
let siteDocs = new SiteDocuments;
siteDocs.siteData = data;
modal.querySelector('#documents').innerHTML = siteDocs.render();
}
let surveys = data.images.filter(i => i.type === 'Survey');
let photos = data.images.filter(i => i.type === 'Photo');
if (images.innerHTML.length === 0) {
let siteSurveys = new SiteSurveys;
siteSurveys.siteData = surveys.length !== 0 ? surveys[0] : undefined;
images.innerHTML += surveys ? siteSurveys.render() : '';
let sitePhotos = new SitePhotos;
sitePhotos.siteData = photos[0] ?? undefined;
images.innerHTML += photos[0] ? sitePhotos.render() : '';
}
// Move to Stimulus?
let dataTabs = modal.querySelectorAll('.data-tabs');
// TODO Awful!!!
tabs.childNodes.forEach(node => {
if (node.nodeName === 'LI') {
node.addEventListener('click', () => {
node.classList.add('is-active');
for (let el of tabs.childNodes) {
if (el.nodeName === 'LI' && el !== node) {
el.classList.remove('is-active');
}
}
for (let tab of dataTabs) {
tab.classList.add('is-hidden');
if (tab.id == node.id.replace('for-','')) {
tab.classList.remove('is-hidden');
}
}
});
}
});
modal.classList.add('is-active');
const closeBtn = modal.querySelector('.modal-close');
const modalBg = modal.querySelector('.modal-background');
const closeModal = () => modal.classList.remove('is-active');
// CLose modal when clicking either on the X button or on the background
closeBtn.addEventListener('click', () => closeModal());
modalBg.addEventListener('click', () => closeModal());
this.imageGallery('gallery-1', surveys);
this.imageGallery('gallery-2', photos);
}
/**
* @param {object} data The data retrieved from the DB to display as modal content
* @param {string} selector The modal selector
*/
UI.openNotConserModal = async function (data, selector) {
const modal = document.querySelector(selector);
let sheet = new NotConservedSheet();
sheet.siteData = data;
modal.querySelector('#not-conser-sheet').innerHTML = sheet.render();
modal.classList.add('is-active');
const closeBtn = modal.querySelector('.modal-close');
const modalBg = modal.querySelector('.modal-background');
const closeModal = () => modal.classList.remove('is-active');
// CLose modal when clicking either on the X button or on the background
closeBtn.addEventListener('click', () => closeModal());
modalBg.addEventListener('click', () => closeModal());
}
/**
* Open a modal with project info
* @param {string} selector The modal selector to open project info
*/
UI.projectInfo = function (selector) {
const modal = document.querySelector(selector);
document.querySelector('#progetto').addEventListener('click', () => {
modal.classList.add('is-active');
const closeIcon = modal.querySelector('.delete');
const modalBg = modal.querySelector('.modal-background');
const closeBtn = modal.querySelector('.buttons > .button');
const closeModal = () => modal.classList.remove('is-active');
// Close modal when clicking either on the X button or on the background
closeBtn.addEventListener('click', () => closeModal());
closeIcon.addEventListener('click', () => closeModal());
modalBg.addEventListener('click', () => closeModal());
});
}
/**
* @param {string} menuListSel Menu list selector
* @param {L.Map} map
* @param {L.LayerGroup} sites
*/
UI.sitesMenu = function (menuListSel, map, sites) {
// Close menu if arrow button is clicked...
this.toggleMenu('close-menu');
const menu = document.querySelector(menuListSel);
menu.addEventListener('click', async event => {
if (event.target.nodeName === 'A') {
const layerId = event.target.id;
// zoom to layer...
const layer = sites.customGetLayer(layerId);
const data = await GIS.layerData(layerId);
this.openSiteModal(data, '#site-data');
map.setView(
layer.getBounds().getCenter(),
19,
{animate: true, duration: 1, easeLinearity: 0.25}
);
}
});
}
/**
* Open Spotlight gallery
* @param {string} galleryId The id of the trigger element
* @param {Array<Object>} images Array of image objects from DB
*/
UI.imageGallery = function (galleryId, images) {
const element = document.querySelector(`#${galleryId}`);
if (element) {
let gallery = [];
for (let img of images) {
gallery.push({src: `img/${img.filename}`, description: img.caption});
}
document.querySelector(`#${galleryId}`).addEventListener('click', () => {
Spotlight.show(gallery);
});
}
}
export default UI;

28
webgis/js/yarn.lock Normal file
View File

@@ -0,0 +1,28 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@kalisio/leaflet-graphicscale@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@kalisio/leaflet-graphicscale/-/leaflet-graphicscale-1.0.0.tgz#5a163e01805208b08ddafd9301e6ad0d4c603e51"
integrity sha512-iy42jngdr8c+9iymJ+TCRmotYjcRAHz+4V1OXhuDt4vy49dYjsCpeYOi27m6UOWrSkwNLqPkn2q0OXXYVhutUg==
bulma@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/bulma/-/bulma-1.0.1.tgz#e37261d6f8e1a3494c9378803d9958effb2715ce"
integrity sha512-+xv/BIAEQakHkR0QVz+s+RjNqfC53Mx9ZYexyaFNFo9wx5i76HXArNdwW7bccyJxa5mgV/T5DcVGqsAB19nBJQ==
fontawesome-free@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/fontawesome-free/-/fontawesome-free-1.0.4.tgz#c7c499708dabd59eb5dedf232b590a862e05957b"
integrity sha512-7sX6Lbg2oQiClFFFFitJlKg20h3YTBON6rdmq3uGjNwDo8G6EjF2bfj2OjjcCUmf4OvZCgyHaXfW2JseqissLw==
leaflet@^1.9.4:
version "1.9.4"
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d"
integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==
spotlight.js@^0.7.8:
version "0.7.8"
resolved "https://registry.yarnpkg.com/spotlight.js/-/spotlight.js-0.7.8.tgz#0620371701508222d736e0658e8db3fbe9ddc53b"
integrity sha512-zq822gqhjaMHt31oZi4do211NwqBcxZ4u7Xtb+OEsbLrz37dcELhBCoQXFu/0sTiomqD6tm5g6tmqvE7S6h4ng==