Creiamo fantastiche mappe con prettymaps e OpenStreetMap data

Esiste una libreria scritta in Python che permette di creare bellissime mappe. Vediamo insieme come funziona.

author avatar
Francesco Strazzante 1/13/2023

Cosa è Prettymaps?

Prettymaps è una libreria open source sviluppata in Python che consente di creare mappe semplici ma spettacolari. Il codice sorgente è disponibile in Github e i dati utilizzati sono quelli di OpenStreetMap. Guardate questa mappa ad esempio: Mappa di Heerhugowaard creata con prettymaps

Noi la stiamo utilizzando con maphoto.app e crea mappe uniche:

Foto di Porto con mappa prettymaps Visualizza post in Instagram.

Quale è la fonte dei dati di prettymaps?

Prettymaps acquisisce i dati da OpenStreetMap. Per chi non conoscesse OpenStreetMap è un fantastico progetto che mette a disposizione tutte la varie informazioni che servono per generare mappe: coordinate di fiumi, strade, edifici, oceani, linee ferroviarie, ...! I dati sono forniti dalla community di OpenStreetMap, cioè persone che rilevano e mappano personalmente i vari dati. Aggiungo che il tutto è reso disponibile gratuitamente (previa menzione dei crediti, giustamente). Personalmente lo trovo un progetto eccezionale, è una risorsa di illimitato valore e dobbiamo essere grati a chi la mantiente! Maggiori informazioni qui.

Come si installa prettymaps?

Premessa: per questo progetto sto utilizzando Windows 10, ma dovrebbe funzionare tranquillamente anche su Mac e Linux. Il requisito principale per poter utilizzare questa libreria è Python3 (io utilizzo la versione 3.10.5). Quindi se non lo avete, installatelo con la guida ufficiale. Per iniziare, assicuriamoci che Python funzioni correttamente controllando la sua versione, da terminale digitiamo:
python3 --version
Python 3.10.5

Ora procediamo con l’installazione di prettymaps. Purtroppo nel momento in cui scrivo, l’ultima versione di prettymaps (0.1.3) è poco stabile, quindi ci concentreremo sulla versione precedente (0.1.2), aggiustando la versione di qualche dipendenza. Da terminale:

pip3 install prettymaps==0.1.2

Una volta terminata l’installazione avremo a disposizione prettymaps; tuttavia, come accennato, dobbiamo modificare la versione di una dipendanza (vedi bug) per farlo funzionare correttamente.

pip3 install osmnx==1.2.1

Adesso siamo pronti per creare la prima mappa!

Come creare una mappa con prettymaps?

Per generare la mappa ci basterà chiamara una funzione della libreria, dobbiamo passare però i giusti parametri (documentazione versione 0.1.2). Ecco la chiamata:
python
map = plot(
# Address:
query='Praça Ferreira do Amaral, Macau',
# Plot geometries in a circle of radius:
radius=1100,
# Matplotlib axis
ax=ax,
# Which OpenStreetMap layers to plot and their parameters:
layers={
# Perimeter (in this case, a circle)
'perimeter': {},
# Streets and their widths
'streets': {
'width': {
'motorway': 5,
'trunk': 5,
'primary': 4.5,
'secondary': 4,
'tertiary': 3.5,
'residential': 3,
'service': 2,
'unclassified': 2,
'pedestrian': 2,
'footway': 1,
}
},
# Other layers:
# Specify a name (for example, 'building') and which OpenStreetMap tags to fetch
'building': {'tags': {'building': True, 'landuse': 'construction'}, 'union': False},
'water': {'tags': {'natural': ['water', 'bay']}},
'green': {'tags': {'landuse': 'grass', 'natural': ['island', 'wood'], 'leisure': 'park'}},
'forest': {'tags': {'landuse': 'forest'}},
'parking': {'tags': {'amenity': 'parking', 'highway': 'pedestrian', 'man_made': 'pier'}}
},
# drawing_kwargs:
# Reference a name previously defined in the 'layers' argument and specify matplotlib parameters to draw it
drawing_kwargs={
'background': {'fc': '#F2F4CB', 'ec': '#dadbc1', 'hatch': 'ooo...', 'zorder': -1},
'perimeter': {'fc': '#F2F4CB', 'ec': '#dadbc1', 'lw': 0, 'hatch': 'ooo...', 'zorder': 0},
'green': {'fc': '#D0F1BF', 'ec': '#2F3737', 'lw': 1, 'zorder': 1},
'forest': {'fc': '#64B96A', 'ec': '#2F3737', 'lw': 1, 'zorder': 1},
'water': {'fc': '#a1e3ff', 'ec': '#2F3737', 'hatch': 'ooo...', 'hatch_c': '#85c9e6', 'lw': 1, 'zorder': 2},
'parking': {'fc': '#F2F4CB', 'ec': '#2F3737', 'lw': 1, 'zorder': 3},
'streets': {'fc': '#2F3737', 'ec': '#475657', 'alpha': 1, 'lw': 0, 'zorder': 3},
'building': {'palette': ['#FFC857', '#E9724C', '#C5283D'], 'ec': '#2F3737', 'lw': .5, 'zorder': 4},
}
)

Come vedete è una sola funzione ma ha tanti parametri:

  • query: query di ricerca indirizzo. Prettymaps centrerà la mappa sull'indirizzo presente
  • radius: (opzionale) se diverso da None, racchiude la mappa in una circonferenza
  • ax: assi di Matplotlib
  • layers: il nome dei livelli di OpenStreetMap da scaricare (lista livelli)
  • drawing_kwargs: parametri di disegno matplotlib dei vari livelli come 'fc', 'ec', 'fill', etc.

Completando con le varie dipendenze, questo è il file completo:

python
import os.path
from matplotlib import pyplot as plt
from prettymaps import *
fig, ax = plt.subplots(figsize=(12, 12), constrained_layout=True)
map = plot(
# Address:
query='Praça Ferreira do Amaral, Macau',
# Plot geometries in a circle of radius:
radius=1100,
# Matplotlib axis
ax=ax,
# Which OpenStreetMap layers to plot and their parameters:
layers={
# Perimeter (in this case, a circle)
'perimeter': {},
# Streets and their widths
'streets': {
'width': {
'motorway': 5,
'trunk': 5,
'primary': 4.5,
'secondary': 4,
'tertiary': 3.5,
'residential': 3,
'service': 2,
'unclassified': 2,
'pedestrian': 2,
'footway': 1,
}
},
# Other layers:
# Specify a name (for example, 'building') and which OpenStreetMap tags to fetch
'building': {'tags': {'building': True, 'landuse': 'construction'}, 'union': False},
'water': {'tags': {'natural': ['water', 'bay']}},
'green': {'tags': {'landuse': 'grass', 'natural': ['island', 'wood'], 'leisure': 'park'}},
'forest': {'tags': {'landuse': 'forest'}},
'parking': {'tags': {'amenity': 'parking', 'highway': 'pedestrian', 'man_made': 'pier'}}
},
# drawing_kwargs:
# Reference a name previously defined in the 'layers' argument and specify matplotlib parameters to draw it
drawing_kwargs={
'background': {'fc': '#F2F4CB', 'ec': '#dadbc1', 'hatch': 'ooo...', 'zorder': -1},
'perimeter': {'fc': '#F2F4CB', 'ec': '#dadbc1', 'lw': 0, 'hatch': 'ooo...', 'zorder': 0},
'green': {'fc': '#D0F1BF', 'ec': '#2F3737', 'lw': 1, 'zorder': 1},
'forest': {'fc': '#64B96A', 'ec': '#2F3737', 'lw': 1, 'zorder': 1},
'water': {'fc': '#a1e3ff', 'ec': '#2F3737', 'hatch': 'ooo...', 'hatch_c': '#85c9e6', 'lw': 1, 'zorder': 2},
'parking': {'fc': '#F2F4CB', 'ec': '#2F3737', 'lw': 1, 'zorder': 3},
'streets': {'fc': '#2F3737', 'ec': '#475657', 'alpha': 1, 'lw': 0, 'zorder': 3},
'building': {'palette': ['#FFC857', '#E9724C', '#C5283D'], 'ec': '#2F3737', 'lw': .5, 'zorder': 4},
}
)
plt.savefig(os.path.realpath("image.png"))

Eseguendo il file con Python3

python
python3 index.py

vedremo che nella cartella dove risiede il file .py si genererà un file chiamato image.png:

Foto di Macau con mappa prettymaps

Come personalizzare le mappe create con prettymaps?

Confermato che l'esempio sulla documentazione funziona correttamente, possiamo sperimentare cambiando colori e livelli: basterà agire sui vari parametri passati alla funzione! Ad esempio cambiando la query:
python
...
query='Piazza Vecchia Bergamo Alta, Bergamo'
...

verrà prodotta:

Foto di Bergamo con mappa prettymaps

Che ne dite? Direi bella ma migliorabile! Proviamo ad esempio a diminuire il raggio di stampa per far vedere solo le mura veneziane di Città Alta (Bergamo) e evidenziare le mura:

python
...
radius=900,
layers={
...
'barrier': {'tags': {'barrier': 'city_wall'}}
...
},
drawing_kwargs={
...
'barrier': {'fc': '#ff0000', 'ec': '#ff0000', 'alpha': 1, 'lw': 2, 'zorder': 5},
...
}
...

ecco il risultato:

Foto di Bergamo con mappa con mura con prettymaps Bello no? Ora una ritoccata ai colori:
python
...
drawing_kwargs={
'background': {'fc': '#fff', 'ec': '#dadbc1', 'zorder': -1},
'perimeter': {'fc': '#fff', 'ec': '#ddd', 'lw': 1, 'zorder': 0},
'green': {'fc': '#CDE2C4', 'ec': '#B7E4A5', 'hatch': 'ooo...', 'lw': 0, 'zorder': 1},
'forest': {'fc': '#CDE2C4', 'ec': '#B7E4A5', 'hatch': 'ooo...', 'lw': 0, 'zorder': 1},
'water': {'fc': '#a1e3ff', 'ec': '#2F3737', 'hatch': 'ooo...', 'hatch_c': '#85c9e6', 'lw': 1, 'zorder': 2},
'parking': {'fc': '#F2F4CB', 'ec': '#2F3737', 'lw': 1, 'zorder': 3},
'streets': {'fc': '#888', 'ec': '#888', 'alpha': 1, 'lw': 0, 'zorder': 3},
'building': {'palette': ['#FA938B', '#facf5a', '#49beb7'], 'ec': '#085f63', 'lw': 0.5, 'zorder': 4},
'barrier': {'fc': '#111', 'ec': '#111', 'alpha': 1, 'lw': 2, 'zorder': 5}
}
...

ecco la mappa finale

Foto di Bergamo con mappa con mura con prettymaps

Ora avete tutti gli strumenti per generare fantastiche mappe!

Bonus: prossimamente in prettymaps

Dalla repo ufficiale, abbiamo accesso al subreddit dedicato al progetto. Qui possiamo conoscere la community, chiedere aiuto e presentare le varie mappe create con prettymaps. Troviamo anche le varie news del progetto, pubblicate dall'autore stesso, ad esempio, si legge, che è in lavorazione una nuova versione e che porterà diverse migliorie; verrà pubblicata non appena verranno risolti alcuni bug con una libreria di terze parti. La nuova versione in realtà, è già utilizzabile ma poco stabile. Il nuovo aggiornamento porterà, tra le altre novità, la possibilità di stampare tutte le forme di edificio della determinata area: Nuova funzionalità in prettymaps 0.1.3 Fantastico!

Bonus: posso far funzionare Prettymaps con Node.js?

Si può, ma con un escamotage! Si può far partire l'esecuzione da Node.js, ma prettymaps dovrà poi girare su Python. La libreria è la stessa, si deve usare Python, l'unico scoglio è far partire lo script di Python da Node.js, ma questo è fattibile con la libreria "util":
js
import utils from 'util';
import { exec } from 'child_process';
const execute = utils.promisify(exec);
await execute('./py index.py var1 var2 varN');

Attenzione ai vari path. Consiglio di provare il comando python dalla cartella in cui è contenuto il file .js Nel file index.py utilizzo i parametri var1, var2, varN durante la chiamata alla funzione prettymaps.

Richiedi una consulenza personalizzata gratuita

Prenota subito una consulenza gratuita per scoprire come posso aiutarti a realizzare il tuo progetto di sviluppo web. Lascia che la mia esperienza e le mie competenze contribuiscano al successo della tua idea.

Contattami oggi per una valutazione dettagliata delle tue esigenze e ricevi un preventivo su misura per te!

Logo logicALG
Driven by quality and inspired by efficency

logicALG di Francesco Strazzante - Sede Legale: via Radini Tedeschi 24/G 24124 Bergamo - P.IVA 04359420165 - logicalg@pec.it - Codice Destinatario: N92GLON
Copyright © 2024. Made with ♥ by logicALG.