637 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			637 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| import UI from "./ui.js";
 | |
| import API_CONFIG from "./config.js";
 | |
| import Icons from "./icons.js";
 | |
| import { SphericalPhoto } from "./components/SphericalPhoto.js";
 | |
| import { GisState } from "./state.js";
 | |
| import Options from "./layer_options.js";
 | |
| 
 | |
| const MAPBOX_TOKEN = 'pk.eyJ1Ijoibmljb3BhIiwiYSI6ImNseWNwZjJjbjFidzcya3BoYTU0bHg4NnkifQ.3036JnCXZTEMt6jVgMzVRw';
 | |
| const BASE_URL = location.href;
 | |
| 
 | |
| let API_URL = API_CONFIG.prod;
 | |
| 
 | |
| if (!BASE_URL.includes('cnr.it')) {
 | |
|     API_URL = API_CONFIG.dev;
 | |
| }
 | |
| 
 | |
| GisState.apiUrl = API_URL;
 | |
| 
 | |
| // Global leaflet and proj4
 | |
| /**
 | |
|  * @namespace GIS
 | |
|  */
 | |
| const GIS = {};
 | |
| 
 | |
| GIS.CENTER_COORDS = [40.5492, 14.2317];
 | |
| GIS.INIT_ZOOM = 14;
 | |
| GIS.MIN_ZOOM = 11;
 | |
| GIS.MAX_ZOOM = 24;
 | |
| 
 | |
| /**
 | |
|  * 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 {L.Map}
 | |
|  */
 | |
| GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
 | |
|     let map = L.map(mapId, {
 | |
|         //attributionControl: false,
 | |
|         minZoom: GIS.MIN_ZOOM,
 | |
|     }).setView(this.CENTER_COORDS, zoomLevel);
 | |
| 
 | |
|     map.crs = L.CRS.EPSG4326;
 | |
| 
 | |
|     const baseMap = await this.initLayers(map);
 | |
|     // Add scale and ruler controls
 | |
|     L.control.scale({imperial: false}).addTo(map);
 | |
|     L.control.graphicScale({fill: 'hollow', position: 'bottomright'}).addTo(map);
 | |
| 
 | |
|     let layerVincoli = await this.loadGeoJSON('vincoli.geojson', Options.constraintsArch);
 | |
|     let layerPaesistici = await this.loadGeoJSON('paesistici.geojson', Options.constraintsLand);
 | |
|     let buildings = await this.loadGeoJSON('fabbricati.geojson', Options.buildings, false);
 | |
| 
 | |
|     await this.addLayerGroups(map);
 | |
|     await this.fetchCartographyLayers();
 | |
| 
 | |
|     const reprojectedWMSLayer = GIS.reprojectWMS();
 | |
|     const wmsLayer = new reprojectedWMSLayer(
 | |
|         'https://wms.cartografia.agenziaentrate.gov.it/inspire/wms/ows01.php?',
 | |
|         {
 | |
|             layers: 'CP.CadastralParcel,codice_plla,fabbricati',
 | |
|             transparent: true,
 | |
|             format: 'image/png',
 | |
|             version: '1.1.1',
 | |
|             minZoom: this.INIT_ZOOM,
 | |
|             maxZoom: this.MAX_ZOOM,
 | |
|             tileSize: 1024,
 | |
|             opacity: 0.6,
 | |
|         }
 | |
|     );
 | |
|     const cartography = {
 | |
|         'Catasto Agenzia delle Entrate (zoom per visualizzare)' : wmsLayer,
 | |
|         'Fabbricati' : buildings,
 | |
|         'Vincoli archeologici' : layerVincoli,
 | |
|         'Vincoli archeologici indiretti' : layerPaesistici,
 | |
|     };
 | |
|     L.control.layers(baseMap, cartography).addTo(map);
 | |
| 
 | |
|     GisState.map = map;
 | |
| 
 | |
|     return map;
 | |
| }
 | |
| /**
 | |
|  * Fetches references to cartography layers
 | |
|  * and updates GisState
 | |
|  */
 | |
| GIS.fetchCartographyLayers = async function () {
 | |
|     const data = await this._fetchData('geoimages/menu')
 | |
|         .catch(error => console.error(`Could not fetch data for geo images: ${error}`));
 | |
| 
 | |
|     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}`));
 | |
| 
 | |
|     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.6,
 | |
|             alt: `Immagine georeferita (${imageData.label})`,
 | |
|             label: `geoimage:${imageData.id}:${imageData.label}`,
 | |
|         }
 | |
|     );
 | |
| 
 | |
|     return imageOverlay;
 | |
| }
 | |
| /**
 | |
|  * Add layer groups to map
 | |
|  */
 | |
| GIS.addLayerGroups = async function (map) {
 | |
|     // Can be included in Promise.all
 | |
|     // if it returns only one group...
 | |
|     const sites = await this.sites();
 | |
|     sites.markers.addTo(map);
 | |
|     sites.geom.addTo(map);
 | |
| 
 | |
|     const groups = await Promise.all(
 | |
|         [
 | |
|             this.notConserved(),
 | |
|             this.findings(),
 | |
|             this.prehistoric(),
 | |
|             this.underwater(),
 | |
|             this.reuse(),
 | |
|         ]
 | |
|     );
 | |
|     groups.forEach(group => group.addTo(map));
 | |
| }
 | |
| /**
 | |
|  * Create markers for sites
 | |
|  * @returns {{markers: L.markerClusterGroup, geom: L.LayerGroup}}
 | |
|  */
 | |
| GIS.sites = async function () {
 | |
|     let sitesData = await fetch(`${API_URL}/sites`)
 | |
|         .then(data => data.json());
 | |
| 
 | |
|     let sites = L.markerClusterGroup(Options.cluster);
 | |
|     let geom = [];
 | |
| 
 | |
|     for (let record of sitesData) {
 | |
|         if (record.geojson) {
 | |
|             const options = record.gisId === 'grotta_azzurra' ?
 | |
|                 Options.grotta : Options.site;
 | |
|             geom.push(await this.loadSiteLayer(record, options));
 | |
|         }
 | |
|         if (record.area) {
 | |
|             const options = record.gisId === 'villa_bismarck' ?
 | |
|                 Options.bismarck : Options.site;
 | |
|             geom.push(await this.loadSiteLayer(record, options, false, true));
 | |
|         }
 | |
| 
 | |
|         const marker = L.marker(
 | |
|                 record.coordinates,
 | |
|                 {icon: Icons.site, label: record.label}
 | |
|             )
 | |
|             .bindTooltip(record.label)
 | |
|             .on(
 | |
|                 'click',
 | |
|                 () => UI.openSiteModal(record, '#site-data')
 | |
|             );
 | |
| 
 | |
|         sites.addLayer(marker);
 | |
|         // Populate app state for reuse and avoid window.Sites etc.
 | |
|         // Municipality (Capri, Anacapri) added for reuse in dynamic menu
 | |
|         marker.options.municipality = record.municipality;
 | |
|         marker.options.data = record;
 | |
|         const markerIndex = `${record.coordinates[0]} ${record.coordinates[1]}`;
 | |
|         GisState.markers.sites[markerIndex] = marker;
 | |
|     }
 | |
| 
 | |
|     GisState.layers.sites = sites;
 | |
| 
 | |
|     return {markers: sites, geom: L.layerGroup(geom)};
 | |
| }
 | |
| /**
 | |
|  * Create not conserved group
 | |
|  * @returns {L.MarkerClusterGroup}
 | |
|  */
 | |
| GIS.notConserved = async function () {
 | |
|     let notConserData = await fetch(`${API_URL}/not_conserved`)
 | |
|         .then(data => data.json());
 | |
| 
 | |
|     let notConserved = L.markerClusterGroup(Options.cluster);
 | |
| 
 | |
|     for (let record of notConserData.records) {
 | |
|         const marker = L.marker(
 | |
|                 record.coordinates,
 | |
|                 {icon: Icons.notConserved, label: record.label}
 | |
|             )
 | |
|             .bindTooltip(record.label)
 | |
|             .on('click', () => UI.openNotConserModal(record, '#not-conser-data'));
 | |
|         
 | |
|         notConserved.addLayer(marker);
 | |
|         // Populate app state for reuse and avoid window.Sites etc.
 | |
|         const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
 | |
|         marker.options.municipality = record.municipality;
 | |
|         marker.options.data = record;
 | |
|         GisState.markers.notConserved[markerLabel] = marker;
 | |
|     }
 | |
| 
 | |
|     GisState.layers.notConserved = notConserved;
 | |
| 
 | |
|     return notConserved;
 | |
| }
 | |
| /**
 | |
|  * Create findings group
 | |
|  * @returns {L.MarkerClusterGroup}
 | |
|  */
 | |
| GIS.findings = async function () {
 | |
|     let findingsData = await fetch(`${API_URL}/finding`)
 | |
|         .then(data => data.json());
 | |
| 
 | |
|     let findings = L.markerClusterGroup(
 | |
|         Options.cluster
 | |
|     );
 | |
| 
 | |
|     for (let record of findingsData) {
 | |
|         const marker = L.marker(
 | |
|                 record.coordinates,
 | |
|                 {icon: Icons.finding, label: record.label}
 | |
|             ).bindTooltip(record.label)
 | |
|             .on(
 | |
|                 'click',
 | |
|                 () => UI.openFindingModal(record, '#finding-data')
 | |
|             );
 | |
| 
 | |
|         findings.addLayer(marker);
 | |
|         const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
 | |
|         marker.options.municipality = record.municipality;
 | |
|         marker.options.data = record;
 | |
|         GisState.markers.findings[markerLabel] = marker;
 | |
|     }
 | |
| 
 | |
|     GisState.layers.findings = findings;
 | |
| 
 | |
|     return findings;
 | |
| }
 | |
| /**
 | |
|  * Create group for prehistoric sites
 | |
|  * 
 | |
|  */
 | |
| GIS.prehistoric = async function () {
 | |
|     let data = await fetch(`${API_URL}/prehistoric`)
 | |
|         .then(data => data.json());
 | |
| 
 | |
|     let prehistoric = L.markerClusterGroup(
 | |
|         Options.cluster
 | |
|     );
 | |
| 
 | |
|     for (let record of data.records) {
 | |
|         const marker = L.marker(
 | |
|                 record.coordinates,
 | |
|                 {icon: Icons.prehistoric, label: record.label}
 | |
|             ).bindTooltip(record.label)
 | |
|             .on(
 | |
|                 'click',
 | |
|                 () => UI.openPrehistModal(record, '#prehist-data')
 | |
|             );
 | |
| 
 | |
|         const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
 | |
|         marker.options.municipality = record.municipality;
 | |
|         marker.options.data = record;
 | |
|         GisState.markers.prehistoric[markerLabel] = marker;
 | |
| 
 | |
|         prehistoric.addLayer(marker);
 | |
|     }
 | |
| 
 | |
|     GisState.layers.prehistoric = prehistoric;
 | |
| 
 | |
|     return prehistoric;
 | |
| }
 | |
| /**
 | |
|  * Create underwater sites group
 | |
|  * @returns {L.MarkerClusterGroup}
 | |
|  */
 | |
| GIS.underwater = async function () {
 | |
|     let underwaterData = await fetch(`${API_URL}/underwater`)
 | |
|         .then(data => data.json());
 | |
| 
 | |
|     let underwater = L.markerClusterGroup(Options.cluster);
 | |
| 
 | |
|     for (let record of underwaterData.records) {
 | |
|         const marker = L.marker(
 | |
|                 record.coordinates,
 | |
|                 {icon: Icons.underwater}
 | |
|             ).bindTooltip(record.label)
 | |
|             .on(
 | |
|                 'click',
 | |
|                 () => UI.openUnderwaterModal(record, '#underwater-data')
 | |
|             );
 | |
| 
 | |
|         const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
 | |
|         marker.options.data = record;
 | |
|         GisState.markers.underwater[markerLabel] = marker;
 | |
| 
 | |
|         underwater.addLayer(marker);
 | |
|     }
 | |
| 
 | |
|     GisState.layers.underwater = underwater;
 | |
| 
 | |
|     return underwater;
 | |
| }
 | |
| /**
 | |
|  * Create reused sites group ('reimpiego')
 | |
|  * @returns {L.MarkerClusterGroup}
 | |
|  */
 | |
| GIS.reuse = async function () {
 | |
|     let reuseData = await fetch(`${API_URL}/reuse`)
 | |
|         .then(data => data.json());
 | |
| 
 | |
|     let reuse = L.markerClusterGroup(Options.cluster);
 | |
| 
 | |
|     for (let record of reuseData.records) {
 | |
|         const marker = L.marker(
 | |
|                 record.coordinates,
 | |
|                 {icon: Icons.reuse}
 | |
|             ).bindTooltip(record.label)
 | |
|             .on(
 | |
|                 'click',
 | |
|                 () => UI.openReuseModal(record, '#reuse-data')
 | |
|             );
 | |
| 
 | |
|         const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
 | |
|         marker.options.municipality = record.municipality;
 | |
|         marker.options.label = record.label;
 | |
|         marker.options.data = record;
 | |
|         GisState.markers.reuse[markerLabel] = marker;
 | |
| 
 | |
|         reuse.addLayer(marker);
 | |
|     }
 | |
| 
 | |
|     GisState.layers.reuse = reuse;
 | |
| 
 | |
|     return reuse;
 | |
| }
 | |
| /**
 | |
|  * Adds layers to map and returns an object
 | |
|  * with {baseMap, archeoLayers, sitesLayerGroup}
 | |
|  * @param {L.Map} map 
 | |
|  * @returns {{baseMap: {"OpenStreetMap": L.TileLayer}}}
 | |
|  */
 | |
| GIS.initLayers = async function(map) {
 | |
| 
 | |
|     let osmap = new L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
 | |
|         maxNativeZoom : GIS.MAX_ZOOM,
 | |
|         maxZoom: GIS.MAX_ZOOM,
 | |
|         attribution: '© <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 : GIS.MAX_ZOOM,
 | |
|         maxZoom: GIS.MAX_ZOOM,
 | |
|         attribution: '© Mapbox'
 | |
|     });
 | |
| 
 | |
|     let boundaries = await this.loadGeoJSON('confini.geojson', {}, false);
 | |
| 
 | |
|     const baseGroup = new L.LayerGroup([osmap]);
 | |
|     baseGroup.addTo(map);
 | |
|     const baseMap = {
 | |
|         "OpenStreetMap" : osmap,
 | |
|         "Satellite" : mapbox,
 | |
|         'Limiti amministrativi' : boundaries,
 | |
|     };
 | |
| 
 | |
|     return baseMap;
 | |
| }
 | |
| /**
 | |
|  * 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');
 | |
|     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 geoJSON filename
 | |
|  * @param {{color, opacity, weight, fillColor, fillOpacity}} options Style options for features
 | |
|  * @param {boolean} popup Should the features have a popup? 
 | |
|  * @returns {L.Layer}
 | |
|  */
 | |
| GIS.loadGeoJSON = 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}`));
 | |
|     
 | |
|     // 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));
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     return layer;
 | |
| }
 | |
| /**
 | |
|  * @param {object} site Site record from DB
 | |
|  * @param {{color, opacity, weight, fillColor, fillOpacity}} options Style options for features
 | |
|  * @param {boolean} popup Should the features have a popup? 
 | |
|  * @returns {L.Layer}
 | |
|  */
 | |
| GIS.loadSiteLayer = async function (site, options, popup = true, area = false) {
 | |
|     let geoJSON = site.gisId;
 | |
|     geoJSON += area ? '_area.geojson' : '.geojson';
 | |
|     const geo = await fetch(`${BASE_URL}geojson/${geoJSON}`)
 | |
|         .then(res => res.json())
 | |
|         .catch(error => console.error(`Can't load layer ${geoJSON}. Reason: ${error}`));
 | |
|     
 | |
|     this.cacheDBData(site.gisId, site.id);
 | |
| 
 | |
|     // 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(site.gisId, site.id);
 | |
|                     
 | |
|                     if (typeof data === 'object') {
 | |
|                         UI.openSiteModal(data);
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     return layer;
 | |
| }
 | |
| /**
 | |
|  * Retrieves data for a given layer
 | |
|  * @param {string} layerId 
 | |
|  * @param {string} dbId 
 | |
|  * @returns {object} Data for this layer from DB or cache
 | |
|  */
 | |
| GIS.layerData = async function (layerId, dbId) {
 | |
|     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('sites/' + dbId);
 | |
|     }
 | |
| 
 | |
|     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
 | |
|  * @param {string} layerId
 | |
|  */
 | |
| GIS.cacheDBData = async function (layerId, dbId) {
 | |
|     const data = await this._fetchData('sites/' + dbId);
 | |
|     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 ?? 'n.d.'}</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) ?? 'n.d.'}</td></tr>
 | |
|             <tr><th>Proprietà</th><td>${capitalize(feature.properties.PROPRIETA) ?? 'n.d.'}</td></tr>
 | |
|             <tr><th>Foglio</th><td>${feature.properties.FOGLIO}</td></tr>
 | |
|             <tr><th>Particella</th><td>${feature.properties.PART_BIS ?? 'n.d.'}</td></tr>
 | |
|         </table>
 | |
|     `;
 | |
|     const content = {
 | |
|         'vincoli.geojson' : html,
 | |
|         'paesistici.geojson' : html,
 | |
|     };
 | |
| 
 | |
|     return content[layerName];
 | |
| }
 | |
| /**
 | |
|  * Reproject WMS layer to map's CRS
 | |
|  * @todo Parametrize CRS?
 | |
|  * @returns {L.TileLayer.WMS}
 | |
|  */
 | |
| GIS.reprojectWMS = function (crs = 'EPSG:4258') {
 | |
|     // Define EPSG:4258
 | |
|     proj4.defs('EPSG:4258', "+proj=longlat +ellps=GRS80 +no_defs +type=crs");
 | |
| 
 | |
|     const reprojectedWMSLayer =  L.TileLayer.WMS.extend({
 | |
|         getTileUrl(tilePoint) {
 | |
|             const map = GisState.map;
 | |
|             const tileSize = this.getTileSize();
 | |
| 
 | |
|             const nwPoint = L.point(
 | |
|                 tilePoint.x * tileSize.x,
 | |
|                 tilePoint.y * tileSize.y,
 | |
|             )
 | |
|             const sePoint = nwPoint.add(L.point(tileSize));
 | |
| 
 | |
|             const nw = map.unproject(nwPoint, tilePoint.z);
 | |
|             const se = map.unproject(sePoint, tilePoint.z);
 | |
| 
 | |
|             const [minX, minY] = proj4('EPSG:4326', 'EPSG:4258', [nw.lng, se.lat]);
 | |
|             const [maxX, maxY] = proj4('EPSG:4326', 'EPSG:4258', [se.lng, nw.lat]);
 | |
| 
 | |
|             const bbox = [minX, minY, maxX, maxY].join(',');
 | |
| 
 | |
|             return this._url + L.Util.getParamString({
 | |
|                     ...this.wmsParams,
 | |
|                     bbox,
 | |
|                     width: tileSize.x,
 | |
|                     height: tileSize.y,
 | |
|                     srs: 'EPSG:4258'
 | |
|                 },
 | |
|                 this._url,
 | |
|                 true
 | |
|             );
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     return reprojectedWMSLayer;
 | |
| }
 | |
| /**
 | |
|  * Fetch data from API
 | |
|  * @param {string} recordUri The URI to be appendend to the API's base URL
 | |
|  */
 | |
| GIS._fetchData = async function (recordUri) {
 | |
|     const data = await fetch(`${API_URL}/${recordUri}`)
 | |
|         .then(res => res.json())
 | |
|         .catch(err => console.log('Error fetching data from DB: ' + err));
 | |
| 
 | |
|     return data;
 | |
| }
 | |
| 
 | |
| export default GIS;
 |