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:Noi la stiamo utilizzando con maphoto.app e crea mappe uniche:
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 --versionPython 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 axisax=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 itdrawing_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.pathfrom matplotlib import pyplot as pltfrom 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 axisax=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 itdrawing_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:
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:
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:
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
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: 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.