'use strict'; import UI from "./ui.js"; // 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: '#987db7', fillOpacity: 0.8 }; const optionsSiti = { color: '#800040', opacity: 1, weight: 2.5, fillColor: '#800040', fillOpacity: 0.8 }; const optionsPaesistici = { color: '#222', opacity: 1, weight: 1.5, fillColor: '#ff8000', fillOpacity: 0.8 }; const BASE_URL = location.href; const API_URL = 'http://localhost:3002/gis'; /** * 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: 11, }).setView(this.CENTER_COORDS, zoomLevel); map.crs = L.CRS.EPSG4326; const {baseMap, archeo, sitesGroup} = await this.initLayers(map); L.control.layers( baseMap, archeo, {collapsed: false} ) .addTo(map); sitesGroup.eachLayer(async 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); } const marker = L.marker(coords) .addTo(map) .bindTooltip(Object.keys(archeo).find(k => archeo[k] === layer)) .openTooltip(); if (typeof data === 'object') { marker.on('click', () => UI.openModal(data)); } }); // TODO Horrible? return {map: map, sites: sitesGroup}; } /** * 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 layerArsenale = await this.loadLayer('arsenale.geojson', optionsSiti, false); let layerGradola = await this.loadLayer('gradola.geojson', optionsSiti, false); layerMater.id = 'matermania'; layerGradola.id = 'gradola'; layerArsenale.id = 'arsenale'; let layerVincoli = await this.loadLayer('vincoli.geojson', optionsVincoli); // TODO named parameters?? let layerPaesistici = await this.loadLayer('paesistici.geojson', optionsPaesistici); let osmap = new L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxNativeZoom : 22, maxZoom: 22, attribution: '© OpenStreetMap contributors' }); const sitesGroup = new L.LayerGroup([layerMater, layerGradola, layerArsenale]); const baseGroup = new L.LayerGroup([osmap]); baseGroup.addTo(map); sitesGroup.addTo(map); const baseMap = { "OpenStreetMap" : osmap }; const archeo = { "Villa di Gradola" : layerGradola, "Grotta di Matermania" : layerMater, "Grotta dell'Arsenale" : layerArsenale, "Vincoli archeologici" : layerVincoli, "Vincoli paesistici" : layerPaesistici, }; return {baseMap, archeo, 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 () { let style = options; return style; }, onEachFeature: function (feature, layer) { if (popup) { layer.bindPopup(GIS.featurePopup(geoJSON, feature)); } else { layer.on("click", async () => { const fromStorage = localStorage.getItem(layerId); let data = {}; if (fromStorage !== 'undefined') { try { data = JSON.parse(fromStorage); } catch (error) { console.log(error); } } else { await GIS._fetchData(layerId); } if (typeof data === 'object') { UI.openModal(data); } }); } } }); return layer; } /** * Cache data from DB in local storage * for a given layer * @param {string} layerId */ GIS.cacheDBData = async function (layerId) { const data = await this._fetchData(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 = `
Oggetto | ${feature.properties.OGGETTO} |
---|---|
Anno | ${feature.properties.ANNO} |
Comune | ${capitalize(feature.properties.COMUNE)} |
Località | ${capitalize(feature.properties.LOCALITA)} |
Proprietà | ${capitalize(feature.properties.PROPRIETA)} |