Reorganize structure with entry point
This commit is contained in:
411
webgis/js/gis.js
Normal file
411
webgis/js/gis.js
Normal 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: '© <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: '© 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;
|
||||
Reference in New Issue
Block a user