Add cartography menu with image overlays (WIP)
This commit is contained in:
		
							parent
							
								
									ecd5db5b4c
								
							
						
					
					
						commit
						8ebaea2ff8
					
				| @ -158,12 +158,12 @@ a:visited { | ||||
|     left: 35vw; | ||||
| } | ||||
| /* Menu overlay */ | ||||
| #menu { | ||||
| .menu-overlay { | ||||
|     z-index: 1000; | ||||
|     overflow-y: auto; | ||||
|     margin-top: 54px; | ||||
| } | ||||
| #menu.is-overlay { | ||||
| .menu-overlay.is-overlay { | ||||
|     left: auto; | ||||
| } | ||||
| /* Content in tabs */ | ||||
|  | ||||
| @ -25,7 +25,8 @@ | ||||
|     <script src="js/index.js" type="module"></script> | ||||
|     <title>WebGIS Isola di Capri</title> | ||||
| </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"> | ||||
|         <div class="navbar-brand"> | ||||
|             <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"> | ||||
|         <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" | ||||
|                 data-id="main" | ||||
|                 data-action="menu#toggleMenu"> | ||||
|                 <span class="icon mr-2"> | ||||
|                     <i class="fa fa-list"></i> | ||||
|                 </span> | ||||
|                 Elenco beni | ||||
|             </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"> | ||||
|                 <span class="icon is-large has-text-link"> | ||||
|                     <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..." /> | ||||
|             </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"> | ||||
|             <!-- Template to build menu items dynamically --> | ||||
|             <template id="menu-item-template"> | ||||
| @ -256,6 +266,35 @@ | ||||
|                 </ul> | ||||
|             </aside> | ||||
|         </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> | ||||
|     <!-- Bibliography citations template --> | ||||
|     <template id="biblio-item-template"> | ||||
|  | ||||
| @ -1,8 +1,37 @@ | ||||
| import { Controller } from "@hotwired/stimulus"; | ||||
| import { GisState } from "../state.js"; | ||||
| import GIS from "../gis.js"; | ||||
| 
 | ||||
| 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? | ||||
|  | ||||
| @ -2,7 +2,17 @@ import { Controller } from "@hotwired/stimulus" | ||||
| import { GisState } from '../state.js'; | ||||
| 
 | ||||
| export default class extends Controller { | ||||
|     static targets = ['list', 'menu', 'icon']; | ||||
|     static targets = [ | ||||
|         'list', | ||||
|         'menu', | ||||
|         'cartography', | ||||
|         'icon' | ||||
|     ]; | ||||
| 
 | ||||
|     static values = { | ||||
|         'cartography' : String, | ||||
|         'main' : String, | ||||
|     }; | ||||
| 
 | ||||
|     buildMenu() { | ||||
|         const groups = Object.keys(GisState.markers); | ||||
| @ -17,7 +27,6 @@ export default class extends Controller { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *  | ||||
|      * @param {String} group  | ||||
| @ -47,8 +56,50 @@ export default class extends Controller { | ||||
|         return ul; | ||||
|     } | ||||
| 
 | ||||
|     toggleMenu() { | ||||
|         this.menuTarget.classList.toggle('is-hidden'); | ||||
|     buildCartographyMenu() { | ||||
|         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() { | ||||
|  | ||||
| @ -113,13 +113,12 @@ GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) { | ||||
|     let layerPaesistici = await this.loadGeoJSON('paesistici.geojson', optionsPaesistici); | ||||
| 
 | ||||
|     await this.addLayerGroups(map); | ||||
| 
 | ||||
|     const historicCadastre = await this.imageOverlays(); | ||||
|     await this.fetchCartographyLayers(); | ||||
| 
 | ||||
|     const archeo = { | ||||
|         'Vincoli archeologici' : layerVincoli, | ||||
|         'Vincoli paesistici' : layerPaesistici, | ||||
|         'Catasto storico' : historicCadastre, | ||||
|         //'Catasto storico' : historicCadastre,
 | ||||
|     }; | ||||
|     L.control.layers(baseMap, archeo).addTo(map); | ||||
| 
 | ||||
| @ -128,35 +127,45 @@ GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) { | ||||
|     return map; | ||||
| } | ||||
| /** | ||||
|  * Load georeferenced image overlays layer group | ||||
|  * Fetches references to cartography layers | ||||
|  * and updates GisState | ||||
|  */ | ||||
| GIS.imageOverlays = async function () { | ||||
|     const data = await this._fetchData('geoimage') | ||||
| GIS.fetchCartographyLayers = async function () { | ||||
|     const data = await this._fetchData('geoimages/menu') | ||||
|         .catch(error => console.error(`Could not fetch data for geo images: ${error}`)); | ||||
| 
 | ||||
|     const overlays = L.layerGroup(); | ||||
| 
 | ||||
|     for (let image of data) { | ||||
|         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); | ||||
|     for (let item of data) { | ||||
|         let {id, label} = item; | ||||
|         GisState.cartography.historic.push({id, label}); | ||||
|     } | ||||
| } | ||||
| /** | ||||
|  * 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 | ||||
|  | ||||
| @ -11,10 +11,11 @@ | ||||
| /** | ||||
|  * @typedef {Object} GisStateType | ||||
|  * @property {L.Map|null} map | ||||
|  * @property {String|null} apiUrl | ||||
|  * @property {MarkerLookup} markers | ||||
|  * @property {LayerGroupLookup} layers | ||||
|  * @property {Object|null} bibliography | ||||
|  * @property {String|null} apiUrl | ||||
|  * @property {Object} cartography | ||||
|  */ | ||||
| 
 | ||||
| /** @type {GisStateType} */ | ||||
| @ -36,6 +37,9 @@ export const GisState = { | ||||
|     }, | ||||
|     bibliography: null, | ||||
|     apiUrl : null, | ||||
|     cartography : { | ||||
|         historic: [], | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | ||||
| @ -68,31 +68,6 @@ UI.toggleBurger = function(burgerClass) { | ||||
| 		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 | ||||
|  * @param {object} data The data retrieved from the DB to display as modal content | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user