WIP: test interactive wall layers
This commit is contained in:
@@ -7,7 +7,8 @@ export default class extends Controller {
|
||||
'menu',
|
||||
'cartography',
|
||||
'search',
|
||||
'icon'
|
||||
'icon',
|
||||
'element',
|
||||
];
|
||||
|
||||
static values = {
|
||||
@@ -169,4 +170,8 @@ export default class extends Controller {
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
removeElement() {
|
||||
this.elementTarget.remove();
|
||||
}
|
||||
}
|
||||
|
||||
+102
-29
@@ -40,15 +40,8 @@ export const INIT_ZOOM = 14;
|
||||
*/
|
||||
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();
|
||||
if (text !== null && text !== undefined && text.at && text.length) {
|
||||
capital = text.at(0).toLocaleUpperCase() + text.slice(1);
|
||||
}
|
||||
|
||||
return capital;
|
||||
@@ -62,6 +55,7 @@ GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
|
||||
let map = L.map(mapId, {
|
||||
//attributionControl: false,
|
||||
minZoom: GIS.MIN_ZOOM,
|
||||
maxZoom: GIS.MAX_ZOOM,
|
||||
}).setView(this.CENTER_COORDS, zoomLevel);
|
||||
|
||||
map.crs = L.CRS.EPSG4326;
|
||||
@@ -102,15 +96,80 @@ GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
|
||||
};
|
||||
L.control.layers(baseMap, cartography).addTo(map);
|
||||
|
||||
// TEST: marker with SVG icon...
|
||||
L.marker([40.5591380,14.2296069], { icon: Icons.wall, id: 'tiberio' })
|
||||
.on('click', () => this.activateBuildingView('tiberio'))
|
||||
.bindTooltip('Mappa USM')
|
||||
.addTo(map);
|
||||
|
||||
GisState.map = map;
|
||||
GisState.cartography.buildings = buildings;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draft implementation to activate
|
||||
* a dedicated view with details
|
||||
* to navigate building walls
|
||||
* @todo Find better name
|
||||
* @param {string} layerId
|
||||
*/
|
||||
GIS.activateBuildingView = async function(layerId) {
|
||||
const map = GisState.map;
|
||||
map.setMinZoom(19);
|
||||
map.setMaxZoom(25);
|
||||
/**
|
||||
* @type {L.GeoJSON} currentGeometry
|
||||
*/
|
||||
let currentGeometry = {};
|
||||
|
||||
map.removeLayer(GisState.baseLayers['OpenStreetMap']);
|
||||
// Remove site layers then re-add it so vector lines
|
||||
// are placed correctly above the new base map
|
||||
GisState.layers.geometries.removeFrom(map);
|
||||
map.addLayer(GisState.baseLayers['Limiti amministrativi']);
|
||||
GisState.cartography.buildings.addTo(map);
|
||||
GisState.layers.geometries.addTo(map);
|
||||
|
||||
GisState.layers.geometries.eachLayer(l => {
|
||||
if (l.options.id === layerId) {
|
||||
currentGeometry = l;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
const center = currentGeometry.getBounds().getCenter();
|
||||
|
||||
// TODO: Use DB to handle these?
|
||||
const wallTechniques = [
|
||||
'reticolata',
|
||||
'incerta',
|
||||
'mista',
|
||||
'laterizia',
|
||||
'cementizia',
|
||||
'poligonale',
|
||||
];
|
||||
|
||||
// TODO: create color lookup table...
|
||||
const wallLayer = await this.loadGeoJSON(
|
||||
`murarie/${layerId}/reticolata.geojson`,
|
||||
Options.walls,
|
||||
true
|
||||
);
|
||||
|
||||
wallLayer.addTo(map);
|
||||
|
||||
map.setView(wallLayer.getBounds().getCenter(), 20);
|
||||
// This is terrible...
|
||||
UI.addBuildingViewBar();
|
||||
map._container.classList.add('bview-border');
|
||||
}
|
||||
/**
|
||||
* Fetches references to cartography layers
|
||||
* and updates GisState
|
||||
*/
|
||||
GIS.fetchCartographyLayers = async function () {
|
||||
GIS.fetchCartographyLayers = async function() {
|
||||
const data = await this._fetchData('geoimages/menu')
|
||||
.catch(error => console.error(`Could not fetch data for geo images: ${error}`));
|
||||
|
||||
@@ -187,6 +246,9 @@ GIS.addLayerGroups = async function (map) {
|
||||
sites.markers.addTo(map);
|
||||
sites.geom.addTo(map);
|
||||
|
||||
// A layer group with all site geometries (GeoJSON)
|
||||
GisState.layers.geometries = sites.geom;
|
||||
|
||||
const groups = await Promise.all(
|
||||
[
|
||||
this.notConserved(),
|
||||
@@ -213,7 +275,7 @@ GIS.sites = async function () {
|
||||
if (record.geojson) {
|
||||
const options = record.gisId === 'grotta_azzurra' ?
|
||||
Options.grotta : Options.site;
|
||||
geom.push(await this.loadSiteLayer(record, options));
|
||||
geom.push(await this.loadSiteLayer(record, options, false));
|
||||
}
|
||||
if (record.area) {
|
||||
const options = record.gisId === 'villa_bismarck' ?
|
||||
@@ -412,7 +474,6 @@ GIS.reuse = async function () {
|
||||
* @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,
|
||||
@@ -437,6 +498,8 @@ GIS.initLayers = async function(map) {
|
||||
'Limiti amministrativi' : boundaries,
|
||||
};
|
||||
|
||||
GisState.baseLayers = baseMap;
|
||||
|
||||
return baseMap;
|
||||
}
|
||||
/**
|
||||
@@ -488,7 +551,7 @@ GIS.toggleSpherical = async function(map) {
|
||||
* @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}
|
||||
* @returns {L.GeoJSON}
|
||||
*/
|
||||
GIS.loadGeoJSON = async function (geoJSON, options, popup = true) {
|
||||
const geo = await fetch(`${BASE_URL}/geojson/${geoJSON}`)
|
||||
@@ -496,6 +559,9 @@ GIS.loadGeoJSON = async function (geoJSON, options, popup = true) {
|
||||
.catch(error => console.error(`Can't load layer ${geoJSON}. Reason: ${error}`));
|
||||
|
||||
// Show data from feature in popUp?
|
||||
/**
|
||||
* @type {L.GeoJSON} layer
|
||||
*/
|
||||
const layer = new L.geoJson(geo, {
|
||||
style: function () {
|
||||
return options;
|
||||
@@ -545,6 +611,8 @@ GIS.loadSiteLayer = async function (site, options, popup = true, area = false) {
|
||||
}
|
||||
});
|
||||
|
||||
layer.options.id = site.gisId;
|
||||
|
||||
return layer;
|
||||
}
|
||||
/**
|
||||
@@ -595,23 +663,28 @@ GIS.cacheDBData = async function (layerId, dbId) {
|
||||
* @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,
|
||||
};
|
||||
const html = String.raw;
|
||||
|
||||
return content[layerName];
|
||||
let content = html`
|
||||
<table class="table is-striped is-size-6 m-2">
|
||||
`;
|
||||
|
||||
for (let key of Object.keys(feature.properties)) {
|
||||
const label = key === 'PART_BIS' ? 'Particella' :
|
||||
key === 'PROPRIETA' ? 'Proprietà' :
|
||||
key === 'LOCALITA' ? 'Località' :
|
||||
key;
|
||||
|
||||
if (key !== 'PERCENTUAL' && key !== 'AREA' && key.toLowerCase() !== 'id') {
|
||||
content += html`
|
||||
<tr><th>${capitalize(label.toLocaleLowerCase())}</th><td>${capitalize(feature.properties[key]) ?? 'n.d.'}</td></tr>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
content += '</table>'
|
||||
|
||||
return content;
|
||||
}
|
||||
/**
|
||||
* Reproject WMS layer to map's CRS
|
||||
|
||||
+43
-48
@@ -14,61 +14,56 @@ const buildingTechColors = {
|
||||
*/
|
||||
const Icons = {};
|
||||
|
||||
Icons.site = L.icon(
|
||||
{
|
||||
iconUrl: 'img/icons/siti.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
}
|
||||
);
|
||||
Icons.notConserved = L.icon(
|
||||
{
|
||||
iconUrl: 'img/icons/non_conserv.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
}
|
||||
);
|
||||
Icons.site = L.icon({
|
||||
iconUrl: 'img/icons/siti.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
});
|
||||
Icons.notConserved = L.icon({
|
||||
iconUrl: 'img/icons/non_conserv.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
});
|
||||
|
||||
Icons.finding = L.icon(
|
||||
{
|
||||
iconUrl: 'img/icons/rinvenimenti.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
}
|
||||
);
|
||||
Icons.finding = L.icon({
|
||||
iconUrl: 'img/icons/rinvenimenti.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
});
|
||||
|
||||
Icons.prehistoric = L.icon(
|
||||
{
|
||||
iconUrl: 'img/icons/preistorici.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
}
|
||||
);
|
||||
Icons.prehistoric = L.icon({
|
||||
iconUrl: 'img/icons/preistorici.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
});
|
||||
|
||||
Icons.underwater = L.icon(
|
||||
{
|
||||
iconUrl: 'img/icons/subacquei.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
}
|
||||
);
|
||||
Icons.underwater = L.icon({
|
||||
iconUrl: 'img/icons/subacquei.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
});
|
||||
|
||||
Icons.reuse = L.icon(
|
||||
{
|
||||
iconUrl: 'img/icons/reimpiego.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
}
|
||||
);
|
||||
Icons.reuse = L.icon({
|
||||
iconUrl: 'img/icons/reimpiego.png',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
});
|
||||
|
||||
Icons.camera = L.divIcon({className: 'fa fa-camera'});
|
||||
|
||||
Icons.wall = L.icon({
|
||||
iconUrl: 'img/icons/bricks.svg',
|
||||
iconSize: [18, 27],
|
||||
iconAnchor: [10, 24],
|
||||
tooltipAnchor: [0, -22],
|
||||
});
|
||||
|
||||
/**
|
||||
* Assigns a different color to the icon
|
||||
* based on building tech type
|
||||
|
||||
@@ -42,15 +42,22 @@ Options.buildings = {
|
||||
color: '#222',
|
||||
opacity: 1,
|
||||
weight: 1.5,
|
||||
fillColor: '#5a5b5c',
|
||||
fillColor: '#717274',
|
||||
fillOpacity: 0.8
|
||||
};
|
||||
Options.boundaries = {
|
||||
color: '#222',
|
||||
opacity: 1,
|
||||
weight: 1.5,
|
||||
fillColor: '#b9bdc0',
|
||||
fillOpacity: 0.8
|
||||
fillColor: '#d8dbdd',
|
||||
fillOpacity: 0.5
|
||||
};
|
||||
Options.walls = {
|
||||
color: '#10ee47',
|
||||
fillColor: '#10ee47',
|
||||
fillOpacity: 1,
|
||||
opacity: 1,
|
||||
weight: 4.5,
|
||||
};
|
||||
Options.cluster = {
|
||||
spiderfyOnMaxZoom: false,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
/**
|
||||
* @typedef {Object} GisStateType
|
||||
* @property {L.Map|null} map
|
||||
* @property {{string: L.Layer}|null} baseLayers
|
||||
* @property {MarkerLookup} markers
|
||||
* @property {LayerGroupLookup} layers
|
||||
* @property {Object|null} bibliography
|
||||
@@ -21,6 +22,7 @@
|
||||
/** @type {GisStateType} */
|
||||
export const GisState = {
|
||||
map: null,
|
||||
baseLayers: null,
|
||||
markers: {
|
||||
sites: {},
|
||||
notConserved: {},
|
||||
@@ -31,6 +33,7 @@ export const GisState = {
|
||||
buildingTechs: {},
|
||||
},
|
||||
layers: {
|
||||
geometries: {},
|
||||
sites: {},
|
||||
notConserved: {},
|
||||
findings: {},
|
||||
@@ -44,6 +47,7 @@ export const GisState = {
|
||||
cartography : {
|
||||
historic: [],
|
||||
cadastral: null,
|
||||
buildings: null,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-1
@@ -42,7 +42,7 @@ UI.addCenterMapControl = function (map, centerCoords, zoom) {
|
||||
let controlUI = L.DomUtil.create('a', 'leaflet-draw-edit-remove', controlDiv);
|
||||
controlUI.title = 'Centra la mappa';
|
||||
controlUI.href = '#';
|
||||
controlUI.innerHTML = `
|
||||
controlUI.innerHTML = html`
|
||||
<span class="icon is-medium">
|
||||
<i>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="pt-2" viewBox="0 0 16 16">
|
||||
@@ -59,6 +59,20 @@ UI.addCenterMapControl = function (map, centerCoords, zoom) {
|
||||
let centerCtr = new L.Control.CenterControl();
|
||||
map.addControl(centerCtr);
|
||||
}
|
||||
|
||||
UI.addBuildingViewBar = function() {
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'container bview-bar has-background-primary-90 p-4';
|
||||
bar.setAttribute('data-controller', 'menu');
|
||||
bar.setAttribute('data-menu-target', 'element');
|
||||
|
||||
bar.innerHTML = html`
|
||||
<button title="Esci da mappa USM" class="delete is-pulled-right" data-action="menu#removeElement"></button>
|
||||
<p class="has-text-centered has-text-weight-semibold is-size-4 is-fullwidth p-3">Mappa Unità Stratigrafica Muraria (USM)</p>
|
||||
`;
|
||||
|
||||
document.body.appendChild(bar);
|
||||
}
|
||||
/**
|
||||
* Toggle burger menu for small screens
|
||||
* @param {string} burgerClass The CSS class of the burger element
|
||||
|
||||
Reference in New Issue
Block a user