Impariamo ad utilizzare il Python per poter accedere alle API di Google Maps e scrivere applicazioni e programmi per la geolocalizzazione.
In molti ci avete scritto chiedendoci informazioni su come accedere in modo semplice alle API di Google Maps. La possibilità di ottenere informazioni da inserire nella propria applicazione è allettante, quindi abbiamo pensato di fare un po’ di chiarezza.
Vediamo come.
Come richiedere Google APIs
Per prima cosa occorre dotarsi di una “API key”, vale a dire una stringa alfanumerica per poter accedere all’interfaccia d Google Maps con Python.
Il procedimento è abbastanza semplice:
Accedere al relativo link sul cloud Google. Qui troveremo dettagliatissime istruzioni su come completare il processo di iscrizione e richiesta delle API
Creare un “account di fatturazione”. Niente paura: l’account serve solo qualora si desideri utilizzare la chiave in modalità business. L’account infatti garantisce un minimo giornaliero di accessi a titolo completamente gratuito e non cumulabile. Secondo le specifiche, “Google Cloud offre una prova di addebito di 0,00 $. La prova scade alla fine di 90 giorni o dopo che l’account ha accumulato $300 di addebiti, a seconda dell’evento che si verifica per primo. Puoi annullare l’abbonamento in qualsiasi momento. Google Maps Platform offre un credito mensile ricorrente di $200.“
Dopo la configurazione, sarà possibile iniziare a utilizzare le API e gli SDK di Google Maps Platform. Google mette a disposizione numerosi tutorial su come creare software e mappe personalizzate e interattive. Basterà acquisire la propria API key e sostituirla nel codice sorgentre della propria applicazione. Notare che l’attivazione del servizio richiede qaualche minuto.
Come configurare l’ambiente di sviluppo
Se utilizzeremo Python, dovremo avere un sistema aggiornato almeno alla versione 3.5.
Ogni richiesta del servizio Web di Google Maps attraverso Python richiede una chiave API o un ID client. Le chiavi API vengono generate nella pagina “Credenziali” della scheda “API e servizi” di Google Cloud Console. Si raccomanda di mantenere segreto il valore della chiave. A questo punto possiamo scaricare il relativo modulo python con il seguente comando:
1 |
pip install -U googlemaps |
Se utilizzate il comando python3 per accedere all’interprete da lanciare, ricordatevi di utilizzare il comando pip3 anziché pip.
E’ altresì possibile utilizzare l’opzione Tools/Manage packages all’interno di Thonny.
Se l’installazione è andata a buon fine, viene fornito un programma di esempio per testare la propria chiave:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import googlemaps from datetime import datetime gmaps = googlemaps.Client(key='Add Your Key here') # Geocoding an address geocode_result = gmaps.geocode('1600 Amphitheatre Parkway, Mountain View, CA') # Look up an address with reverse geocoding reverse_geocode_result = gmaps.reverse_geocode((40.714224, -73.961452)) # Request directions via public transit now = datetime.now() directions_result = gmaps.directions("Sydney Town Hall", "Parramatta, NSW", mode="transit", departure_time=now) # Validate an address with address validation addressvalidation_result = gmaps.addressvalidation(['1600 Amphitheatre Pk'], regionCode='US', locality='Mountain View', enableUspsCass=True) |
Il programma configura il sistema, esegue la geocodifica di un indirizzzo in coordinate (Latitudine e Longitudine), esegue la geocodifica inversa da coordinate a indirizzo (se esistente), calcola il percorso per arrivare ad una nuova località (distanza, tempi, eventuali percorsi alternativi) e controlla la correttezza di un indirizzo fornito.
Tutto ciò è molto bello, ma… come estrarre i dati una volta calcolati?
Analizziamo di seguito un nuovo programma creato ad hoc per tale scopo. Per i più pigri, il programma completo si trova su github nel link a fine articolo.
Come utilizzare le API
Per prima cosa occorre inizializzare il programma:
1 2 3 4 5 6 |
import googlemaps from datetime import datetime gmaps = googlemaps.Client(key='LaVostraAPIKey') Indirizzo_generico = "via San Rocco 11 24040 Bonate Sopra Bergamo" |
- Importiamo il modulo googlemaps e datetime (per i percorsi).
- Creiamo un oggetto gmaps attraverso la nostra chiave di accesso.
- Definiamo un indirizzo generico sul quale opereremo di seguito.
La geocodifica diretta funziona nel modo seguente:
1 2 3 4 5 |
# Geocoding an address print("Geocodifica di un indirizzo in coordinate Lat/Lng : ") geocode_result = gmaps.geocode(Indirizzo_generico) print(geocode_result) print("") |
Creiamo una variabile composta (geocode_result) e vi associamo i dati ricevuti dalla chiamata del metodo .geocode(Indirizzo_generico) dell’oggetto gmaps appena creato.
Prima domanda: cosa contiene geocode result?
Se eseguissimo una print della variabile, otterremmo quanto segue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[ {'address_components': [ {'long_name': '11', 'short_name': '11', 'types': ['street_number']}, {'long_name': 'Via S. Rocco', 'short_name': 'Via S. Rocco', 'types': ['route']}, {'long_name': 'Bonate Sopra', 'short_name': 'Bonate Sopra', 'types': ['locality', 'political']}, {'long_name': 'Bonate Sopra', 'short_name': 'Bonate Sopra', 'types': ['administrative_area_level_3', 'political']}, {'long_name': 'Provincia di Bergamo', 'short_name': 'BG', 'types': ['administrative_area_level_2', 'political']}, {'long_name': 'Lombardia', 'short_name': 'Lombardia', 'types': ['administrative_area_level_1', 'political']}, {'long_name': 'Italy', 'short_name': 'IT', 'types': ['country', 'political']}, {'long_name': '24040', 'short_name': '24040', 'types': ['postal_code']} ], 'formatted_address': 'Via S. Rocco, 11, 24040 Bonate Sopra BG, Italy', 'geometry': {'bounds': {'northeast': {'lat': 45.6819113, 'lng': 9.5607779}, 'southwest': {'lat': 45.6817918, 'lng': 9.5605452} }, 'location': {'lat': 45.6818637, 'lng': 9.5606454}, 'location_type': 'ROOFTOP', 'viewport': { 'northeast': {'lat': 45.6831884302915, 'lng': 9.562010530291502}, 'southwest': {'lat': 45.6804904697085, 'lng': 9.559312569708498} } }, 'place_id': 'ChIJN3tE1OlSgUcRG4xBVMfQIN8', 'types': ['premise'] } ] |
Nonostante l’aspetto mostruoso, si tratta di una “semplice” lista con un unico elemento, benché complesso. Tale elemento è composto di un unico dizionario, con sei distinte chiavi:
- address_components
- formatted_address
- geometry
- location
- place_id
- types
Allo stesso modo possiamo accedere ad un (eventiuale) indirizzo attraveso le sue coordinate geografiche:
1 2 3 4 5 |
# Look up an address with reverse geocoding print("Geocodifica inversa: da coordinate a indirizzo : ") reverse_geocode_result = gmaps.reverse_geocode((40.714224, -73.961452)) print(reverse_geocode_result) print("") |
ottenendo un record simile al precedente.
La seguente sezione di codice ci mostra invece come accedere al percorso tra due località:
1 2 3 4 5 6 7 8 9 |
# Request directions via public transit print("Stampa itinerario e tempi di percorrenza : ") now = datetime.now() directions_result = gmaps.directions("Sydney Town Hall", "Parramatta, NSW", mode="transit", departure_time=now) print(directions_result) print("") |
o come validare un indirizzo:
1 2 3 4 5 6 7 |
# Validate an address with address validation addressvalidation_result = gmaps.addressvalidation(['1600 Amphitheatre Pk'], regionCode='US', locality='Mountain View', enableUspsCass=True) print("Validazione dell'indirizzo : \n", addressvalidation_result) print("") |
Di nuovo il risultato è un record complesso, contenente tutte le istruzioni necessarie per decodificare le informazioni. Sapendo come accedere agli elementi interni della lista, diventa più semplice determinare le informazioni necessarie.
Per i più pigri, vediamo di seguito alcuni esempi utili per la decodifica delle informazioni. Il seguente snippet di codice decodifica l’indirizzo completo a partire dalle coordinate geografiche (Latitudine e Longitudine):
1 2 3 4 5 6 7 8 9 |
# Definisco l'indirizzo completo dalle coordinate elem = (reverse_geocode_result[0]) print("Definisco l'indirizzo completo dalle coordinate : ") print(elem['address_components']) print("\nEnumero gli elementi della lista degli indirizzi : ") for single in elem['address_components']: print(" ", single) |
elem costituisce il primo (e unico) elemento della risposta del metodo .reverse_geocode(). Si tratta di un dizionario, esattamente come il metodo .geocode.
Se ne stampiamo la chiave address_components, possiamo vedere l’elenco di informazioni che abbiamo dettagliato in precedenza. Ma noi vogliamo estrarre tali informazioni in modo programmatico, quindi eseguiamo il codice delle righe 8 e 9, enumerando ciascun elemento della relativa chiave (single) del dizionario.
Il programma restituisce il seguente output:
1 2 3 4 5 6 7 8 9 |
Enumero gli elementi della lista degli indirizzi : {'long_name': '277', 'short_name': '277', 'types': ['street_number']} {'long_name': 'Bedford Avenue', 'short_name': 'Bedford Ave', 'types': ['route']} {'long_name': 'Williamsburg', 'short_name': 'Williamsburg', 'types': ['neighborhood', 'political']} {'long_name': 'Brooklyn', 'short_name': 'Brooklyn', 'types': ['political', 'sublocality', 'sublocality_level_1']} {'long_name': 'Kings County', 'short_name': 'Kings County', 'types': ['administrative_area_level_2', 'political']} {'long_name': 'New York', 'short_name': 'NY', 'types': ['administrative_area_level_1', 'political']} {'long_name': 'United States', 'short_name': 'US', 'types': ['country', 'political']} {'long_name': '11211', 'short_name': '11211', 'types': ['postal_code']} |
Stampiamo ora alcune componenti dell’indirizzo. Vediamo come accedervi:
1 2 3 4 5 6 7 8 |
# Stampo alcune componenti dell'indirizzo print("\nStampo alcune componenti dell'indirizzo :") numero = elem['address_components'][0]['long_name'] indirizzo = elem['address_components'][1]['long_name'] localita = elem['address_components'][2]['long_name'] citta = elem['address_components'][3]['long_name'] print("Numero civico : ", numero, " indirizzo : ", indirizzo, " localita : ", localita, "citta : ", citta) |
Ricapitoliamo:
- elem è l’elemento della lista.
- elem[‘address_components’] rappresenta come abbiamo visto la chiave di un dizionario il cui valore è una nuova lista di elementi
- potremo quindi accedere a ciascuno di questi elementi attraverso il loro indice, come se avessimo un array (elem[‘address_components’][0])
- ora, l’elemento zero della lista valore della chiave ‘address_components’ è di nuovo un dizionario; potremo accedervi attraverso la sua chiave ‘long_name‘ (elem[‘address_components’][0][‘long_name’])
1 2 |
Stampo alcune componenti dell'indirizzo : Numero civico : 277 indirizzo : Bedford Avenue localita : Williamsburg citta : Brooklyn |
Voilà… siamo riusciti ad ottenere l’elemento singolo desiderato dal coacervo di dati presentato nella risposta del web service/metodo.
Facile no?
Ma vediamo qualche altra API…
Distanza tra due indirizzi
Attraverso il metodo .distance_matrix impareremo a calcolare la distanza tra die indirizzi:
1 2 3 4 |
# Calcolo la distanza tra due indirizzi print("\nCalcolo la distanza tra due indirizzi : ") distanza = gmaps.distance_matrix(Indirizzo_generico, 'viale Mazzini 11 00142 Roma') print("Distanza : ", distanza) |
L’utilizzo del metodo è semplicissimo: viene richiesto l’indirizzo di partenza e un indirizzo di destinazione. Nel nostro caso Indirizzo_generico definito in precedenza e un qualsiasi indirizzo del centro di Roma.
La variabile composta distanza conterrà l’insieme delle informazioni richieste.
1 2 |
Calcolo la distanza tra due indirizzi : Distanza : {'destination_addresses': ['Viale Giuseppe Mazzini, 11, 00195 Roma RM, Italy'], 'origin_addresses': ['Via S. Rocco, 11, 24040 Bonate Sopra BG, Italy'], 'rows': [{'elements': [{'distance': {'text': '608 km', 'value': 608008}, 'duration': {'text': '6 hours 8 mins', 'value': 22067}, 'status': 'OK'}]}], 'status': 'OK'} |
Analizziamo il record della risposta, rendendolo più leggibile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ 'destination_addresses': ['Viale Giuseppe Mazzini, 11, 00195 Roma RM, Italy'], 'origin_addresses': ['Via S. Rocco, 11, 24040 Bonate Sopra BG, Italy'], 'rows': [ { 'elements': [ { 'distance': {'text': '608 km', 'value': 608008}, 'duration': {'text': '6 hours 8 mins', 'value': 22067}, 'status': 'OK' } ] } ], 'status': 'OK' } |
Il dizionario ottenuto ha quattro chiavi:
- destination_addresses ha come valore una lista di indirizzi di destinazione ( o uno, nel nostro caso, se cerchiamo la distanza tra due città)
- origin_addresses ha come valore una lista di indirizzi di partenza
- rows ha come valore una lista di dizionari in cui alla chiave elements fa riferimento una lista di informazioni (distance, duration e status)
- status riporta lo stato della query
Stampa degli elementi di una geocodifica di un indirizzo
Abbiamo visto in precedenza come “leggere” i dati da un record di risposta di una chiamata gmaps.geocode(). Vediamo ora in che modo automatizzare la procedura, ed estrarre le stesse informazioni da programma:
1 2 3 4 5 6 7 8 |
# Stampo gli elementi provenienti dalla geocodifica di un indirizzo print("\nStampo gli elementi provenienti dalla geocodifica di un indirizzo : ") coord = gmaps.geocode(Indirizzo_generico) print(coord[0]) print("\nEnumero ciascuno degli elementi della lista : ") for elem in coord[0]: print(elem) |
Anche in questo caso, otteniamo le chiavi del dizionario necessarie ad accedere a ciascuno degli elementi che ci interesssano:
1 2 3 4 5 6 |
Enumero ciascuno degli elementi della lista : address_components formatted_address geometry place_id types |
Altre chiamate utili del modulo
Di seguito vedremo come estrarre una stringa completa e preformattata contenente un indirizzo a partire dalle coordinate, senza doverla “costruire” leggendo le varie chiavi dei dizionari nella risposta, quindi estrarremo latitutine e longitudine dal record dell’indirizzo richiesto, magari per posizionare un elemento sulla mappa di riferimento.
Infine calcoleremo l’elevazione sul livello del mare di un dato indirizzo. Anche in questo caso, sarà possibile trovare alcune località sul globo al di sotto del livello del mare, con elevazione negativa.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Stampo una stringa formattata da un indirizzo generico ind_formattato = coord[0]['formatted_address'] print("Stampo una stringa formattata da un indirizzo generico : ", ind_formattato) #Estraggo Latitudine e Longitudine dal record dell'indirizzo richiesto print("\nEstraggo Latitudine e Longitudine dal record dell'indirizzo richiesto (e la classe dell'elemento ottenuto) : ") LatLng = [(coord[0]['geometry']['location']['lat'], coord[0]['geometry']['location']['lng'])] print (LatLng, type(LatLng)) # ...e calcolo l'elevazione sul livello del mare, utilizzando i dati appena estratti print("\n...e calcolo l'elevazione sul livello del mare, utilizzando i dati appena estratti") elevazione = gmaps.elevation(LatLng) print(elevazione) |
L’indirizzo formattato è già disponibile nel nostro record di risposta, sotto la chiave ‘formatted_address‘: se non abbiamo necessità di formattazione particolare, o di estrarre i campi separatamente, possiamo utilizzare direttamente questa chiave.
Latitudine e Longitudine vanno invece ricercate in profondità all’interno del nostro record di risposta, come sottochiavi della chiave ‘geometry‘.
Per trovare l’elevazione sul livello del mare, invece, esiste un metodo specifico, ‘elevation(LatLng)‘, che la calcola in base alle coordinate fornite in formato lista.
1 2 3 4 5 6 7 |
Stampo una stringa formattata da un indirizzo generico : Via S. Rocco, 11, 24040 Bonate Sopra BG, Italy Estraggo Latitudine e Longitudine dal record dell'indirizzo richiesto (e la classe dell'elemento ottenuto) : [(45.6818637, 9.5606454)] <class 'list'> ...e calcolo l'elevazione sul livello del mare, utilizzando i dati appena estratti [{'elevation': 230.5326995849609, 'location': {'lat': 45.68186, 'lng': 9.56065}, 'resolution': 152.7032318115234}] |
Come tutte le cose fatte bene, esiste una pagina di documentazione di tutte le API per python messe a disposizione da Google Maps per poter adattare e manipolare correttamente le informazioni necessarie al nostro programma.
Prossimamente pubblicheremo una videoguida su questo stesso argomento per “facilitare” il compito a chi non è abituato a leggere i dati da una pagina.
Il sorgente del programma di esempio che abbiamo utilizzato (senza ovviamente la Google API Key) si trova su Github.
Buona sperimentazione a tutti!
Join our groups on Telegram…