Add cartography menu with image overlays (WIP)

This commit is contained in:
Nicolò P 2025-06-20 16:07:30 +02:00
parent ecd5db5b4c
commit 8ebaea2ff8
7 changed files with 169 additions and 62 deletions

View File

@ -158,12 +158,12 @@ a:visited {
left: 35vw; left: 35vw;
} }
/* Menu overlay */ /* Menu overlay */
#menu { .menu-overlay {
z-index: 1000; z-index: 1000;
overflow-y: auto; overflow-y: auto;
margin-top: 54px; margin-top: 54px;
} }
#menu.is-overlay { .menu-overlay.is-overlay {
left: auto; left: auto;
} }
/* Content in tabs */ /* Content in tabs */

View File

@ -25,7 +25,8 @@
<script src="js/index.js" type="module"></script> <script src="js/index.js" type="module"></script>
<title>WebGIS Isola di Capri</title> <title>WebGIS Isola di Capri</title>
</head> </head>
<body data-controller="menu" data-action="menu-ready@document->menu#buildMenu"> <body data-controller="menu"
data-action="menu-ready@document->menu#buildMenu menu-ready@document->menu#buildCartographyMenu">
<nav class="navbar mb-0" role="navigation"> <nav class="navbar mb-0" role="navigation">
<div class="navbar-brand"> <div class="navbar-brand">
<a href="/" class="navbar-item is-size-5 has-text-dark ml-4" title="Torna alla home page"> <a href="/" class="navbar-item is-size-5 has-text-dark ml-4" title="Torna alla home page">
@ -46,12 +47,21 @@
<hr class="navbar-divider"> <hr class="navbar-divider">
<div class="navbar-end pb-1 pt-1" id="nav-menu"> <div class="navbar-end pb-1 pt-1" id="nav-menu">
<button class="navbar-item button is-size-5 is-white mr-3" role="button" <button class="navbar-item button is-size-5 is-white mr-3" role="button"
data-id="main"
data-action="menu#toggleMenu"> data-action="menu#toggleMenu">
<span class="icon mr-2"> <span class="icon mr-2">
<i class="fa fa-list"></i> <i class="fa fa-list"></i>
</span> </span>
Elenco beni Elenco beni
</button> </button>
<button class="navbar-item button is-size-5 is-white mr-3" role="button"
data-id="cartography"
data-action="menu#toggleMenu">
<span class="icon mr-2">
<i class="fa fa-map"></i>
</span>
Cartografia
</button>
<button class="button is-outlined is-rounded is-link mr-4 mt-1" id="howto" title="Istruzioni"> <button class="button is-outlined is-rounded is-link mr-4 mt-1" id="howto" title="Istruzioni">
<span class="icon is-large has-text-link"> <span class="icon is-large has-text-link">
<i class="fas fa-question fa-lg"></i> <i class="fas fa-question fa-lg"></i>
@ -69,7 +79,7 @@
<progress id="map-progress" class="p-2 progress is-medium is-link" aria-label="Map loading..." /> <progress id="map-progress" class="p-2 progress is-medium is-link" aria-label="Map loading..." />
</div> </div>
</div> </div>
<div class="column is-hidden is-4 is-4-desktop is-5-mobile is-pulled-right is-overlay has-background-white-ter" id="menu" <div class="menu-overlay column is-hidden is-4 is-4-desktop is-5-mobile is-pulled-right is-overlay has-background-white-ter"
data-menu-target="menu" data-controller="layer"> data-menu-target="menu" data-controller="layer">
<!-- Template to build menu items dynamically --> <!-- Template to build menu items dynamically -->
<template id="menu-item-template"> <template id="menu-item-template">
@ -256,6 +266,35 @@
</ul> </ul>
</aside> </aside>
</div> </div>
<div class="menu-overlay column is-hidden is-3 is-3-desktop is-4-mobile is-pulled-right is-overlay has-background-white-ter"
data-menu-target="cartography">
<!-- Template to build menu items dynamically -->
<template id="cartography-item-template">
<li>
<label class="checkbox">
<input type="checkbox"
data-controller="layer"
data-action="layer#toggleCartography"
data-layer-id-value=""
data-layer-type-value=""
/>
</label>
</li>
</template>
<aside class="menu ml-4 mt-3" data-id="cartography-aside">
<button title="Chiudi menu" class="delete is-pulled-right" data-action="menu#close"></button>
<p class="menu-label is-size-5 mt-2 is-clickable" data-id="historic">
<span role="button" data-action="click->menu#toggle" data-id="historic">
Catasto storico
<!--
<span class="icon pl-2">
<i class="fa fa-chevron-right" data-menu-target="icon" data-id="historic"></i>
</span>
-->
</span>
</p>
</aside>
</div>
</div> </div>
<!-- Bibliography citations template --> <!-- Bibliography citations template -->
<template id="biblio-item-template"> <template id="biblio-item-template">

View File

@ -1,8 +1,37 @@
import { Controller } from "@hotwired/stimulus"; import { Controller } from "@hotwired/stimulus";
import { GisState } from "../state.js"; import { GisState } from "../state.js";
import GIS from "../gis.js";
export default class extends Controller { export default class extends Controller {
static targets = ['sites', 'findings', 'notconserved', 'prehist',]; static targets = [
'sites',
'findings',
'notconserved',
'prehist',
];
static values = {
'id': Number,
'type': String,
};
async toggleCartography() {
const map = GisState.map;
const id = this.idValue;
let currentLayer = await GIS.getImageOverlay(id);
let hasLayer = false;
map.eachLayer(function (layer) {
if (layer.options.label === currentLayer.options.label) {
hasLayer |= true;
currentLayer = layer;
}
});
if (!hasLayer) currentLayer.addTo(map);
else map.removeLayer(currentLayer);
}
/** /**
* @todo Use Stimulus values? * @todo Use Stimulus values?

View File

@ -2,7 +2,17 @@ import { Controller } from "@hotwired/stimulus"
import { GisState } from '../state.js'; import { GisState } from '../state.js';
export default class extends Controller { export default class extends Controller {
static targets = ['list', 'menu', 'icon']; static targets = [
'list',
'menu',
'cartography',
'icon'
];
static values = {
'cartography' : String,
'main' : String,
};
buildMenu() { buildMenu() {
const groups = Object.keys(GisState.markers); const groups = Object.keys(GisState.markers);
@ -17,7 +27,6 @@ export default class extends Controller {
} }
} }
} }
/** /**
* *
* @param {String} group * @param {String} group
@ -47,8 +56,50 @@ export default class extends Controller {
return ul; return ul;
} }
toggleMenu() { buildCartographyMenu() {
this.menuTarget.classList.toggle('is-hidden'); const historicCadastre = GisState.cartography.historic;
const template = document.getElementById('cartography-item-template');
const ul = document.createElement('ul');
ul.className = 'menu-list';
ul.id = 'historic-sub';
const aside = document.querySelector('[data-id="cartography-aside"]');
for (let geoImage of historicCadastre) {
const clone = template.content.cloneNode(true);
const label = clone.querySelector('label');
const checkbox = clone.querySelector('input[type="checkbox"]');
checkbox.dataset.layerIdValue = geoImage.id;
checkbox.dataset.layerTypeValue = 'historic';
const span = document.createElement('span');
span.className = 'pl-3';
span.textContent = geoImage.label;
label.appendChild(span);
ul.appendChild(clone);
}
aside.appendChild(ul);
}
toggleMenu(event) {
const menuId = event.target.dataset.id;
// Stupid...
if (menuId === 'main') {
this.menuTarget.classList.toggle('is-hidden');
if (!this.cartographyTarget.classList.contains('is-hidden')) {
this.cartographyTarget.classList.add('is-hidden');
}
}
if (menuId === 'cartography') {
this.cartographyTarget.classList.toggle('is-hidden');
if (!this.menuTarget.classList.contains('is-hidden')) {
this.menuTarget.classList.add('is-hidden');
}
}
} }
close() { close() {

View File

@ -113,13 +113,12 @@ GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
let layerPaesistici = await this.loadGeoJSON('paesistici.geojson', optionsPaesistici); let layerPaesistici = await this.loadGeoJSON('paesistici.geojson', optionsPaesistici);
await this.addLayerGroups(map); await this.addLayerGroups(map);
await this.fetchCartographyLayers();
const historicCadastre = await this.imageOverlays();
const archeo = { const archeo = {
'Vincoli archeologici' : layerVincoli, 'Vincoli archeologici' : layerVincoli,
'Vincoli paesistici' : layerPaesistici, 'Vincoli paesistici' : layerPaesistici,
'Catasto storico' : historicCadastre, //'Catasto storico' : historicCadastre,
}; };
L.control.layers(baseMap, archeo).addTo(map); L.control.layers(baseMap, archeo).addTo(map);
@ -128,35 +127,45 @@ GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
return map; return map;
} }
/** /**
* Load georeferenced image overlays layer group * Fetches references to cartography layers
* and updates GisState
*/ */
GIS.imageOverlays = async function () { GIS.fetchCartographyLayers = async function () {
const data = await this._fetchData('geoimage') const data = await this._fetchData('geoimages/menu')
.catch(error => console.error(`Could not fetch data for geo images: ${error}`)); .catch(error => console.error(`Could not fetch data for geo images: ${error}`));
const overlays = L.layerGroup(); for (let item of data) {
let {id, label} = item;
for (let image of data) { GisState.cartography.historic.push({id, label});
let polygonCoords = JSON.parse(image.polygon).coordinates[0];
// Image rectangle bounds are SE and NW coordinates from the PostGIS polygon
// with long/lat swapped...
const se = [polygonCoords[0][1], polygonCoords[0][0]];
const nw = [polygonCoords[2][1], polygonCoords[2][0]];
const bounds = [se, nw];
const imageOverlay = L.imageOverlay(
`/webgis/img/geo/${image.filename}`,
bounds,
{
opacity: 0.8,
alt: `Immagine georeferita dal catasto storico di Capri`
}
);
overlays.addLayer(imageOverlay);
} }
}
/**
* Load georeferenced image overlays layer group
* @param {Number} imageId - The API id of the georeferenced image
* @returns {L.ImageOverlay}
*/
GIS.getImageOverlay = async function (imageId) {
const imageData = await this._fetchData(`geoimages/${imageId}`)
.catch(error => console.error(`Could not fetch data for geo image ${imageId}: ${error}`));
return overlays; let polygonCoords = JSON.parse(imageData.polygon).coordinates[0];
// Image rectangle bounds are SE and NW coordinates from the PostGIS polygon
// with long/lat swapped...
const se = [polygonCoords[0][1], polygonCoords[0][0]];
const nw = [polygonCoords[2][1], polygonCoords[2][0]];
const bounds = [se, nw];
const imageOverlay = L.imageOverlay(
`/webgis/img/geo/${imageData.filename}`,
bounds,
{
opacity: 0.8,
alt: `Immagine georeferita (${imageData.label})`,
label: `geoimage:${imageData.id}:${imageData.label}`,
}
);
return imageOverlay;
} }
/** /**
* Add layer groups to map * Add layer groups to map

View File

@ -11,10 +11,11 @@
/** /**
* @typedef {Object} GisStateType * @typedef {Object} GisStateType
* @property {L.Map|null} map * @property {L.Map|null} map
* @property {String|null} apiUrl
* @property {MarkerLookup} markers * @property {MarkerLookup} markers
* @property {LayerGroupLookup} layers * @property {LayerGroupLookup} layers
* @property {Object|null} bibliography * @property {Object|null} bibliography
* @property {String|null} apiUrl
* @property {Object} cartography
*/ */
/** @type {GisStateType} */ /** @type {GisStateType} */
@ -36,6 +37,9 @@ export const GisState = {
}, },
bibliography: null, bibliography: null,
apiUrl : null, apiUrl : null,
cartography : {
historic: [],
}
} }
/** /**

View File

@ -68,31 +68,6 @@ UI.toggleBurger = function(burgerClass) {
document.querySelector(`#${menuId}`).classList.toggle('is-active'); document.querySelector(`#${menuId}`).classList.toggle('is-active');
}); });
} }
/**
* Toggle side menu
* @param {string} triggerId The ID of the trigger element
* @param {?string} listId The ID of the menu list to open (if any)
*/
UI.toggleMenu = function (triggerId, listId = null) {
const trigger = document.querySelector(`#${triggerId}`);
const menu = document.querySelector('#menu');
trigger.addEventListener('click', () => {
menu.classList.remove('is-hidden');
//menu.classList.add('is-3');
const lists = menu.querySelectorAll('ul');
if (listId !== null) {
const list = document.querySelector(`#${listId}`);
list.classList.remove('is-hidden');
for (let ul of lists) {
if (ul.id !== listId && !ul.id.includes('sub')) {
ul.classList.add('is-hidden');
}
}
}
});
}
/** /**
* Open a modal with DB site data * Open a modal with DB site data
* @param {object} data The data retrieved from the DB to display as modal content * @param {object} data The data retrieved from the DB to display as modal content