diff --git a/css/app.css b/css/app.css
index 41c1234..54acfa4 100644
--- a/css/app.css
+++ b/css/app.css
@@ -210,4 +210,18 @@ input:invalid {
}
.mixed-tech {
color: #f0280f;
+}
+/* Building view top bar */
+.bview-bar {
+ position: absolute;
+ top: 4.5rem;
+ width: 450px;
+ left: 50%;
+ transform: translateX(-50%);
+ border-radius: 8px;
+}
+/* Map container border for building view */
+.bview-border {
+ border:#263aac 4px solid;
+ border-radius: 8px;
}
\ No newline at end of file
diff --git a/webgis/geojson/murarie/tiberio/reticolata.geojson b/webgis/geojson/murarie/tiberio/reticolata.geojson
new file mode 100644
index 0000000..44aa3f2
--- /dev/null
+++ b/webgis/geojson/murarie/tiberio/reticolata.geojson
@@ -0,0 +1,13 @@
+{
+"type": "FeatureCollection",
+"name": "reticolata",
+"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
+"features": [
+{ "type": "Feature", "properties": { "id": null, "Comune": "Capri", "Località": "Bagni di Tiberio", "Tipologia": "Struttura Muraria", "Tecnica": "Opera reticolata", "Materiale": "Tufo", "Funzione": null, "Descrizione": "Murature di fondo e divisorie degli ambienti prospicienti il mare, posti all’estremità orientale del complesso dei Bagni di Tiberio", "Elemento": "Alzati", "Componenti": "Cubilia e Blocchetti di ammorsamento angolari" }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 14.229777980072088, 40.559287936017519 ], [ 14.229767127017078, 40.559288012101483 ], [ 14.22976727121063, 40.559297587195999 ], [ 14.229760323645737, 40.559297626052299 ], [ 14.229760154396285, 40.559286387060112 ], [ 14.229750772025486, 40.559286469247525 ] ] ] } },
+{ "type": "Feature", "properties": { "id": null, "Comune": "Capri", "Località": "Bagni di Tiberio", "Tipologia": "Struttura Muraria", "Tecnica": "Opera Reticolata", "Materiale": "Tufo", "Funzione": null, "Descrizione": "Lacerto murario in giacitura secondaria, riferibile a una struttura con fondazione in opera cementizia e alzato in opera reticolata", "Elemento": "alzato, attacco di fondazione", "Componenti": "Cubilia, scapoli di calcare, scapoli di tufo" }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 14.229748638716192, 40.559312774822097 ], [ 14.229749854780234, 40.559313917882307 ], [ 14.229749311598642, 40.559316242764893 ], [ 14.229747641045932, 40.559318431170922 ], [ 14.229745404602166, 40.559318825800922 ], [ 14.229707320793663, 40.559318057726777 ], [ 14.229688319593144, 40.559316631832786 ], [ 14.229687414956194, 40.55931462813308 ], [ 14.229689070847257, 40.559311784892955 ], [ 14.229707054884152, 40.559311704425618 ], [ 14.22973485378067, 40.5593122650738 ], [ 14.229745138689067, 40.559312472499833 ], [ 14.229748638716192, 40.559312774822097 ] ] ] } },
+{ "type": "Feature", "properties": { "id": null, "Comune": "Capri", "Località": "Bagni di Tiberio", "Tipologia": "Strutture murarie", "Tecnica": "Opera reticolata", "Materiale": "Tufo", "Funzione": null, "Descrizione": "Due murature, rispettivamente di fondo e laterale, pertinenti a un ambiente prospiciente il mare nell’area orientale del complesso dei Bagni di Tiberio", "Elemento": "Alzati", "Componenti": "Cubilia, blocchetti di tufo angolari e terminali" }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 14.229694021609493, 40.55930762924018 ], [ 14.229694082338904, 40.559310341259078 ], [ 14.229682272612358, 40.559310495088383 ], [ 14.229616809841877, 40.559314335557936 ], [ 14.229615693144396, 40.559336306937055 ] ] ] } },
+{ "type": "Feature", "properties": { "id": null, "Comune": "Capri", "Località": "Bagni di Tiberio", "Tipologia": "Strutture Murarie", "Tecnica": "Opera reticolata", "Materiale": "Tufo", "Funzione": null, "Descrizione": "Due murature pertinenti a un ambiente ubicato sul livello superiore dell’area occidentale del complesso dei Bagni di Tiberio.", "Elemento": "alzati", "Componenti": "cubilia, blocchetti di ammorsamento" }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 14.229012969950622, 40.559279324646354 ], [ 14.22901299762786, 40.559273288155325 ], [ 14.228949305222281, 40.559271595449403 ] ] ] } },
+{ "type": "Feature", "properties": { "id": null, "Comune": "Capri", "Località": "Bagni di Tiberio", "Tipologia": "Strutture Murarie", "Tecnica": "Opera reticolata", "Materiale": "Tufo", "Funzione": null, "Descrizione": "muratura in pessimo stato di conservazione, inglobata in costruzione moderna", "Elemento": "Alzato", "Componenti": "Cubilia" }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 14.228945620292212, 40.559244814778907 ], [ 14.228945097860407, 40.559260882019025 ] ] ] } },
+{ "type": "Feature", "properties": { "id": null, "Comune": "Capri", "Località": "Bagni di Tiberio", "Tipologia": "Struttura Muraria", "Tecnica": "Opera reticolata", "Materiale": "Tufo", "Funzione": "sostruttiva", "Descrizione": "Fondazione del terrazzamento prospiciente il mare, con porticato soprastante", "Elemento": "Fondazione", "Componenti": "Cubilia" }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 14.22932061572207, 40.559368986602642 ], [ 14.229319898205036, 40.559370575004976 ], [ 14.229315830697367, 40.559373603453658 ], [ 14.229313913113851, 40.559377741595505 ] ] ] } }
+]
+}
diff --git a/webgis/img/icons/bricks.svg b/webgis/img/icons/bricks.svg
new file mode 100644
index 0000000..23c2c36
--- /dev/null
+++ b/webgis/img/icons/bricks.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/webgis/js/controllers/menu_controller.js b/webgis/js/controllers/menu_controller.js
index 4fda139..9109052 100644
--- a/webgis/js/controllers/menu_controller.js
+++ b/webgis/js/controllers/menu_controller.js
@@ -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();
+ }
}
diff --git a/webgis/js/gis.js b/webgis/js/gis.js
index c04e97c..05b3aa0 100644
--- a/webgis/js/gis.js
+++ b/webgis/js/gis.js
@@ -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 = `
-
- | Oggetto | ${feature.properties.OGGETTO ?? 'n.d.'} |
- | Anno | ${feature.properties.ANNO} |
- | Comune | ${capitalize(feature.properties.COMUNE)} |
- | Località | ${capitalize(feature.properties.LOCALITA) ?? 'n.d.'} |
- | Proprietà | ${capitalize(feature.properties.PROPRIETA) ?? 'n.d.'} |
- | Foglio | ${feature.properties.FOGLIO} |
- | Particella | ${feature.properties.PART_BIS ?? 'n.d.'} |
-
- `;
- const content = {
- 'vincoli.geojson' : html,
- 'paesistici.geojson' : html,
- };
+ const html = String.raw;
- return content[layerName];
+ let content = html`
+
+ `;
+
+ 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`
+ | ${capitalize(label.toLocaleLowerCase())} | ${capitalize(feature.properties[key]) ?? 'n.d.'} |
+ `;
+ }
+ }
+
+ content += '
'
+
+ return content;
}
/**
* Reproject WMS layer to map's CRS
diff --git a/webgis/js/icons.js b/webgis/js/icons.js
index 5ac0de0..cd69bb9 100644
--- a/webgis/js/icons.js
+++ b/webgis/js/icons.js
@@ -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
diff --git a/webgis/js/layer_options.js b/webgis/js/layer_options.js
index fc236d5..b34ee9f 100644
--- a/webgis/js/layer_options.js
+++ b/webgis/js/layer_options.js
@@ -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,
diff --git a/webgis/js/state.js b/webgis/js/state.js
index 8bbe503..ce65349 100644
--- a/webgis/js/state.js
+++ b/webgis/js/state.js
@@ -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,
}
}
diff --git a/webgis/js/ui.js b/webgis/js/ui.js
index 2e35d46..3e09475 100644
--- a/webgis/js/ui.js
+++ b/webgis/js/ui.js
@@ -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`