Portale mappe personalizzato: una guida dall'inizio alla fine
Published Apr 12, 2022 Updated Jul 03, 2023

Questo articolo descrive come creare un geoportale con una mappa personalizzata, dati propri, legenda della mappa, ricerca per nome del luogo, vista satellitare di un edificio selezionato e una streetview. Leggendo, imparerete a trasformare i dati in MBTiles, a caricarli sul vostro account MapTiler Cloud e a visualizzarli in un'applicazione sviluppata con MapTiler SDK.
La mappa finale avrà questo aspetto (fate clic su un edificio per vedere la magia):
Scaricare i dati della mappa
La prima cosa da fare è scaricare i dati di alcuni edifici dal Catasto di Spagna. Nell'esempio, utilizzeremo i dati del comune di Sant Feliu de Guixols.
I dati possono essere scaricati dal seguente link: SANT FELIU DE GUIXOLS/A.ES.SDGC.BU.17170.zip
Una volta scaricati i dati, si decomprime il file zip. All'interno del file zip è presente un file chiamato A.ES.SDGC.BU.17170.building.gml con le informazioni dell'edificio; questo file è in formato GML.
Convertire un file GML in MBTiles
Per trasformare il file GML in MBTiles abbiamo diverse opzioni:
- MapTiler Engine: questo strumento ci permette di trasformare facilmente i nostri dati in MBTiles o GeoPackage. Possiamo anche caricare direttamente i dati trasformati sul nostro account cloud. Per saperne di più, consultare l'articolo Generazione di tegole vettoriali (base).
- GDAL/ogr2ogr: se sul computer è installato GDAL/ogr2ogr, è possibile trasformare il file GML in MBTiles utilizzando il seguente comando:
ogr2ogr -f MVT guixols.mbtiles A.ES.SDGC.BU.17170.building.gml -dsco MAXZOOM=18 -dsco MINZOOM=9 -mapFieldType DateTime=Stringa
- MapTiler Engine CLI: crea flussi di lavoro automatizzati con tutta la potenza di MapTiler Engine. Per saperne di più su MapTiler Engine, consultare la pagina di supporto di MapTiler Engine . Per trasformare il file gml in mbtiles utilizzando MapTiler Engine è necessario eseguire il seguente comando:
maptiler-engine -o guixols.mbtiles A.ES.SDGC.BU.17170.building.gml
Come si vede, MapTiler Engine si occupa di calcolare i livelli di zoom ottimali per i nostri dati e di eseguire la trasformazione corrispondente dei tipi di dati. Nell'esempio GDAL, dobbiamo definire i livelli di zoom e indicare le trasformazioni dei tipi di dati alfanumerici in modo che siano validi in MBTiles.
Caricare i geodati su MapTiler Cloud
Abbiamo già detto che è possibile caricare i dati direttamente su MapTiler Cloud da MapTiler Engine; un altro modo per caricare MBTiles è attraverso l'Admin API di MapTiler Cloud .
There are numerous ways to send your requests to the API; whether you are a fan of API clients or go with the good old curl, don’t forget to set the Authorization header in the form of Token {YOUR_TOKEN} so we know it’s you making the requests. To make your life easier, we have also created a CLI utility to upload the tilesets.
Eseguire l'intero processo manualmente tramite curl comporta l'esecuzione di più chiamate API. Ad esempio, la chiamata all'API Admin per avviare l'ingest. L'API Admin restituisce un URL di Google Drive per caricare il file. Quindi chiama l'API di Google Drive per caricare il file e infine chiama l'API di amministrazione per elaborare il file.
Per semplificarvi la vita, abbiamo sviluppato l'utility MapTiler Cloud CLI per caricare i tileset. Questo strumento open-source è sviluppato in Python e consente di automatizzare il processo di caricamento dei dati nel cloud. È possibile accedere al codice nel repository GitHub di MapTiler Cloud CLI.
Per caricare i dati nel cloud utilizzeremo lo strumento MapTiler Cloud CLI.
Una volta installato lo strumento CLI, è necessario avviare l'ambiente virtuale in cui è installato lo strumento. Quindi, eseguire il seguente comando per caricare il file MBTiles su MapTiler Cloud:
maptiler-cloud --token=YOUR_CREDENTIAL_TOKEN tiles ingest guixols.mbtiles
Sviluppate un'applicazione con una mappa per visualizzare i vostri dati.
Per visualizzare i dati di MapTiler Cloud , realizzeremo un'applicazione utilizzando la libreria MapTiler SDK.
Creare una mappa
La prima cosa da fare è creare una mappa in cui visualizzare i nostri dati. Per caricare la cartografia di riferimento della nostra mappa chiameremo l'API MapTiler Cloud . MapTiler Cloud ha molti stili di mappa di base pronti all'uso per soddisfare un'ampia gamma di casi d'uso.
Se non avete mai lavorato con MapTiler SDK, vi consigliamo di dare un'occhiata all'esempio Iniziare a integrare le mappe. Guardate tutti questi esempi per scoprire quanto sia più veloce sviluppare con MapTiler SDK!
Creare un file chiamato index.html e copiare il seguente codice:
Aprite l'applicazione in un browser e dovreste vedere una mappa come la seguente immagine
Creare il controllo di geocodifica
Aggiungiamo un controllo che ci permetta di cercare le località. A tale scopo, utilizzeremo il controllo Geocoding.
Per caricare il componente geocoder, aggiungere le righe evidenziate nell'intestazione del file index.html.
Subito dopo la creazione della mappa scrivere:
const gc = new maptilersdkMaptilerGeocoder.GeocodingControl({});
map.addControl(gc, 'top-left');
A questo punto si dovrebbe vedere il controllo di ricerca nell'angolo superiore sinistro della mappa.
Visualizzare i dati delle mappe
Per aggiungere i nostri dati alla mappa, dobbiamo prima dichiarare una nuova origine dati e poi aggiungere un nuovo livello con le informazioni di questa origine dati.
L'inizializzazione della mappa nella pagina indica al browser di richiedere lo stile. Questa operazione può richiedere del tempo, a seconda della velocità con cui il server risponde alla richiesta di stile e del tempo necessario al browser per eseguire il rendering della mappa (in genere millisecondi). Queste risorse sono remote, quindi vengono eseguite in modo asincrono; è importante assicurarsi che lo stile sia caricato prima di eseguire altro codice.
L'oggetto mappa può informare il browser su alcuni eventi che si verificano quando lo stato della mappa cambia. Uno di questi eventi è l'evento load, che viene attivato quando lo stile è stato caricato nella mappa.
Aggiungere un'origine dati alla mappa
Con il metodo map.on('load', callback function) possiamo assicurarci che il resto del codice di callback non venga eseguito finché non si verifica l'evento.
map.addControl(gc, 'top-left');
map.on('load', () => {
// the rest of the code will go in here
});
Pertanto, dobbiamo chiamare la funzione addSource all'interno di una funzione map.on('load'), in modo che la nuova sorgente non venga caricata prima del rendering della mappa.
map.on('load', () => {
// the rest of the code will go in here
map.addSource("building_source", {
"type": "vector",
"url": `https://api.maptiler.com/tiles/YOUR_TILESET_ID/tiles.json`
});
});
Nello snippet di codice qui sopra si utilizza l'API di MapTiler Cloud per fare riferimento al TileJSON generato durante il caricamento dei dati su MapTiler Cloud.
Aggiungere un livello alla mappa
Una volta definita l'origine dei dati, possiamo aggiungere il livello alla mappa. A tale scopo, utilizzeremo la funzione addLayer. Subito dopo addSource, scrivere le righe successive:
map.on('load', () => {
// the rest of the code will go in here
map.addSource("building_source", {
"type": "vector",
"url": `https://api.maptiler.com/tiles/YOUR_TILESET_ID/tiles.json?key=${apiKey}`
});
map.addLayer({
"id": "building_pol",
"type": "fill",
"source": "building_source",
"source-layer": "Building",
"layout": {
"visibility": "visible"
},
"paint": {
"fill-color": "#3A1888",
"fill-opacity": [
"literal",
0.6
]
},
"filter": ["all",
["==", "$type", "Polygon"]
],
}, "airport");
});
Nel frammento di codice qui sopra, aggiungiamo il livello edifici subito prima del livello etichette aeroporto. In questo modo i poligoni degli edifici appaiono sulla mappa sotto le etichette e non nascondono i testi dei nomi delle strade, dei quartieri, ecc.
Ricaricare l'applicazione e digitare Sant Feliu de Guíxols nel motore di ricerca e selezionare la prima voce nell'elenco dei risultati. L'applicazione ingrandisce il comune di Sant Feliu de Guíxols e visualizza il layer degli edifici.
Mostra le informazioni sull'edificio quando si fa clic
Abbiamo già visto come aggiungere e visualizzare i propri dati su una mappa. La prossima cosa da fare è mostrare le informazioni relative a un edificio quando si fa clic su di esso. A tale scopo, utilizzeremo l'evento click della mappa.
Dopo aver aggiunto il livello alla mappa, scriveremo il seguente codice:
In questo frammento di codice, definiamo una funzione (createPopupContent) per creare il contenuto da visualizzare e registriamo l'evento click della mappa per il nostro livello edificio.
Per migliorare l'esperienza dell'utente, faremo in modo che il puntatore del cursore cambi quando si passa sopra un edificio; in questo modo l'utente saprà che può interagire con gli edifici.
Scrivete quanto segue dopo il punto in cui aggiungiamo l'evento click:
map.on('click', 'building_pol', function (e) {
const content = createPopupContent(e.features[0]);
new maptilersdk.Popup()
.setLngLat(e.lngLat)
.setHTML(content)
.addTo(map);
});
map.on('mouseenter', 'building_pol', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'building_pol', () => {
map.getCanvas().style.cursor = '';
});
Creare una mappa coropleta degli edifici
Per creare la mappa coropleta degli edifici, modificheremo lo stile del livello edifici e utilizzeremo i dati alfanumerici associati agli edifici per classificarli e dipingerli in base al valore di un determinato campo. In questo caso, utilizzeremo il campo currentUse per effettuare la classificazione.
Se osserviamo i dati alfanumerici del livello Edifici, vedremo che per il campo Uso corrente ci sono 6 possibili valori. Creeremo un array con questi valori e assegneremo loro un testo e un colore.
Subito dopo aver dichiarato la variabile con la chiave API, scriviamo quanto segue:
maptilersdk.config.apiKey = 'slEr7lXnTotdWgNA0oLf';
const categories_field = "currentUse";
const categories = [
{id: "1_residential", text: "Residential", color: "#FFD65F"},
{id: "2_agriculture", text: "Agriculture", color: "#35C186"},
{id: "3_industrial", text: "Industrial", color: "#805CC2"},
{id: "4_1_office", text: "Office", color: "#FF8F65"},
{id: "4_2_retail", text: "Retail", color: "#3388F1"},
{id: "4_3_publicServices", text: "Public Services", color: "#E25041"},
];
Con questo array di categorie e colori, creeremo la funzione createFillColor per generare un'espressione che determini il colore con cui dipingere gli edifici.
const createFillColor = (categories, categories_field) => {
const colors = categories.reduce((agg, item) => {
agg.push(item.id);
agg.push(item.color);
return agg;
}, []);
return [
"match",
[
"get",
categories_field
],
...colors,
"#ccc"
]
}
Modificare la proprietà fill-color del livello edifici in modo che invece di avere un colore definito ("#3A1888") utilizzi la funzione createFillColor per determinare il valore.
"paint": {
"fill-color": createFillColor(categories, categories_field),
"fill-opacity": [
"literal",
0.6
]
},
Quando si ricarica l'applicazione, si vedrà che gli edifici sono dipinti con colori diversi a seconda del loro utilizzo.
Creare una legenda interattiva della mappa
Creeremo una legenda interattiva sulla mappa, per mostrare i colori della classificazione, e cambieremo anche la visualizzazione di quali categorie sono attive o inattive.
La prima cosa da fare è creare una funzione per creare il filtro del livello. Utilizzeremo la proprietà filter del livello per determinare quali categorie vogliamo mostrare sulla mappa. Questo ci permetterà di modificare la visualizzazione delle categorie attive e inattive.
const createFilter = (categories, categories_field) => {
const filters = categories.reduce((agg, item) => {
agg.push(["in", categories_field, item.id]);
return agg;
}, []);
return [
"all",
["==", "$type", "Polygon"],
["any",
...filters
]
]
}
Modificare la proprietà del filtro del livello Edifici
"paint": {
"fill-color": createFillColor(categories, categories_field),
"fill-opacity": [
"literal",
0.6
]
},
"filter": createFilter(categories, categories_field),
}, "airport");
Creare il controllo della legenda
Prima di creare il controllo della legenda, creeremo un paio di funzioni che ci permetteranno di alternare la visibilità delle categorie. Accanto alla creazione della funzione createFilter scriviamo queste righe:
const removeAtIndex = (arr, index) => {
const copy = [...arr];
copy.splice(index, 1);
return copy;
};
const toggle = (arr, item, getValue = item => item) => {
const index = arr.findIndex(i => getValue(i) === getValue(item));
if (index === -1) return [...arr, item];
return removeAtIndex(arr, index);
}
Appena sotto il punto in cui abbiamo aggiunto il controllo di ricerca, copiate quanto segue:
map.addControl(gc, 'top-left');
class legendControl {
constructor (categories, field) {
this.categories = categories;
this.field = field;
}
onAdd(map) {
this._map = map;
this._container = document.createElement('div');
this._container.className = 'maplibregl-ctrl';
const _fragment = document.createDocumentFragment();
const _nav = document.createElement('nav');
_nav.className = 'maplibregl-ctrl filter-group';
_nav.id = 'filter-group';
_fragment.appendChild(_nav);
this.categories.forEach(element => {
const _input = document.createElement('input');
_input.type = "checkbox";
_input.id = element.id;
_input.className = "input-layers";
_input.checked = true;
const this_ = this;
_input.addEventListener('change', function (e) {
this_.updateLegend(e.target.id);
});
const _label = document.createElement('label');
_label.htmlFor = element.id;
const _text = document.createTextNode(element.text);
const _legend = document.createElement('i');
_legend.style.backgroundColor = element.color;
_label.appendChild(_text);
_label.appendChild(_legend);
_nav.appendChild(_input);
_nav.appendChild(_label);
});
this._container.appendChild(_fragment);
return this._container;
}
onRemove() {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
}
updateLegend(id) {
let filter = this._map.getFilter('building_pol');
if (filter){
const [any, ...filters] = filter[2];
filter[2] = [any, ...toggle(filters, ["in", this.field, id], (item) => item[2])];
this._map.setFilter('building_pol', filter);
}
}
}
map.addControl(new legendControl(categories, categories_field), 'bottom-left');
Ora si procederà allo stile del controllo della legenda. Nella sezione stile della pagina scriviamo:
#map {position: absolute; top: 0; bottom: 0; width: 100%;}
.filter-group {
font: 12px/20px 'Ubuntu', sans-serif;
font-weight: 400;
position: absolute;
bottom: 25px;
z-index: 1;
border-radius: 4px;
width: 150px;
color: rgba(51, 51, 89, 1);
box-shadow: 0px 15px 68px rgba(51, 51, 89, 0.15);
background: rgba(255, 255, 255, 0.9);
}
.filter-group input[type='checkbox']:first-child + label {
border-radius: 6px 6px 0 0;
}
.filter-group label:last-child {
border-radius: 0 0 6px 6px;
border: none;
}
.filter-group input[type='checkbox'] {
display: none;
}
.filter-group input[type='checkbox'] + label {
display: block;
cursor: pointer;
padding: 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
text-transform: capitalize;
}
.filter-group input[type='checkbox'] + label i {
width: 18px;
height: 18px;
float: right;
margin-right: 0 8px;
opacity: 0.7;
}
.filter-group input[type='checkbox'] + label:hover {
background-color: rgba(49, 112, 254, 0.05);
}
.filter-group input[type='checkbox'] + label:before {
content: '';
margin-right: 15px;
}
.filter-group input[type='checkbox']:checked + label:before {
content: '✔';
margin-right: 5px;
}
Ricaricate la pagina, il controllo della legenda dovrebbe trovarsi nell'angolo in basso a sinistra della mappa. Se facciamo clic su alcune categorie della legenda, vedremo che la visibilità degli edifici di quella categoria cambia.
Mostra un'immagine satellitare dell'edificio selezionato
Per creare l'immagine della vista aerea dell'edificio utilizzeremo l'API MapTiler Cloud Static Maps.
Modificheremo le informazioni mostrate quando si seleziona un edificio per mostrare l'immagine della vista satellitare dello stesso.
Per creare l'immagine satellitare dell'edificio, dobbiamo conoscere il rettangolo di selezione dell'edificio. Utilizzeremo la libreria Turf.js che ci permette di ottenere il rettangolo di selezione di una geometria.
All'inizio della pagina copiare la riga seguente per aggiungere la libreria Turf:
Aggiungere la seguente riga all'inizio della funzione createPopupContent:
Nel creaContenutoPopup chiama la funzione mappe statiche
per creare l'url dell'immagine della vista satellitare e aggiungere l'immagine al contenuto del popup.
Inoltre, modificheremo le informazioni mostrate nel campo Uso per mostrare il testo della categoria invece del codice identificativo. A tale scopo, creeremo una funzione che restituisca il testo della categoria selezionata.
Subito dopo la funzione toggle copiare le seguenti righe:
const getUse = (use, categories) => {
return categories.reduce((agg, item) => {
return item.id === use ? item.text : agg;
}, "Unknown");
}
Chiameremo questa funzione per visualizzare le informazioni nel campo Use.
Aggiornare l'applicazione e fare clic su un edificio. Verranno visualizzate le informazioni sull'edificio e un'immagine della sua vista aerea.
Sintesi
In questo articolo abbiamo visto come creare una mappa interattiva con i nostri dati utilizzando l'API di MapTiler Cloud e l'SDK di MapTiler.
Abbiamo fatto:
- Utilizzare l'Admin API per caricare i dati
- Caricare una mappa di base grazie all'API di Maps
- Usare il componente Geocoding API per cercare i luoghi
- Visualizzare i dati come tessere vettoriali utilizzando l'API Tiles
- Creare una mappa coropleta
- Creare una legenda interattiva
- Creare un'immagine della vista satellitare di un edificio utilizzando l'API Static Maps
Ulteriori informazioni su MapTiler Cloud API
Come caricare MBTiles o GeoPackage in MapTiler Cloud tramite API
Come creare mappe con MapTiler Cloud API - Casi d'uso ed esempi