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.

Hai un'idea? Realizziamola insieme.

Che tu abbia un progetto chiaro in mente o solo un'idea da cui partire, parliamone. Offro una prima consulenza gratuita per capire le tue esigenze e proporti la soluzione migliore per te. Nessun impegno, solo un'opportunità per far crescere il tuo business.

Software su misura Bergamo
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 © 2025. Made with ♥ by logicALG.