In [1]:
import googlemaps
import os
import urllib
import time
import fiona
import polyline
import folium
import xyzservices.providers as xyz
import pandas as pd
import geopandas as gpd

from dotenv import load_dotenv,find_dotenv
from datetime import datetime
from shapely.geometry import LineString
In [2]:
load_dotenv(find_dotenv())
gmaps = googlemaps.Client(key=os.getenv('GOOGLE_API_KEY_GK'))
access_token = os.getenv('MAPBOX_API_KEY')
In [3]:
def make_magic(source_text,target_coords,buffer_dist,iso_times,departure_time_gmaps,departure_time_mapbox):
    iso_times = [str(el) for el in iso_times]
    iso_times = ','.join(iso_times)
    resp_source = gmaps.geocode(source_text,language='it')
    source_address = resp_source[0]['formatted_address']
    source_lat = resp_source[0]['geometry']['location']['lat']
    source_lon = resp_source[0]['geometry']['location']['lng']
    id = 1
    source_df = pd.DataFrame([{
        'id': id,
        'address': source_address,
        'lat': source_lat,
        'lon': source_lon
    }])
    source = gpd.GeoDataFrame(source_df, geometry=gpd.points_from_xy(source_df['lon'], source_df['lat'], crs=4326))
    target_lat = float(target_coords.split(',')[0].strip())
    target_lon = float(target_coords.split(',')[1].strip())
    target_df = pd.DataFrame([{
        'id_t': 1,
        'lat': target_lat,
        'lon': target_lon
    }])
    target = gpd.GeoDataFrame(target_df, geometry=gpd.points_from_xy(target_df['lon'],target_df['lat']))
    buff = source.copy()
    buff['geometry'] = buff['geometry'].to_crs(32632).buffer(buffer_dist).to_crs(4326)
    api_url = 'https://api.mapbox.com'
    service = 'isochrone'
    version = 'v1'
    profile = 'mapbox/driving-traffic'
    minutes = iso_times
    if departure_time_mapbox == '':
        depart_at = ''
    else:
        depart_at = f'&depart_at={departure_time_mapbox}'

    for attempt in range(10):
        try:
            lon = source_lon
            lat = source_lat
            url =f'{api_url}/{service}/{version}/{profile}/{lon},{lat}?contours_minutes={minutes}{depart_at}&polygons=true&access_token={access_token}'
            print(url)
            iso = gpd.read_file(url) 
            iso['center'] = id
        except urllib.error.HTTPError as err:
            print(f'tentativo = {attempt} - {err}')
            time.sleep(20)
        except fiona.errors.DriverError as err:
            print(f'Location out of road - {err}')
            df = pd.DataFrame([{'geometry': None}])
            iso = gpd.GeoDataFrame(df, geometry=df['geometry'])
            iso['center'] = id
            break
        else:
            break
            
    departure_time = datetime.strptime(departure_time_gmaps, '%Y-%m-%dT%H:%M:%SZ')
    directions_result = gmaps.directions((source_lat,source_lon),
                                        (target_lat,target_lon),
                                        mode="driving", #walking #bicycling #transit
                                        departure_time=departure_time,
                                        alternatives=True,
                                        language='it')
    print(f"Distanza: {directions_result[0]['legs'][0]['distance']['text']}")
    print(f"Durata: {directions_result[0]['legs'][0]['duration']['text']}")
    print(f"Durata con traffico: {directions_result[0]['legs'][0]['duration_in_traffic']['text']}")
    for i,direction in enumerate(directions_result,start=1):
        for leg in direction['legs']:
            print(f"Soluzione {i} - {leg['duration_in_traffic']['text']}")
            
    steps_out = list()
    steps = directions_result[0]['legs'][0]['steps']
    for i,step in enumerate(steps, start=1):
        if i == len(steps):
            desc = 'Destinazione raggiunta'
        else:
            desc = steps[i]['html_instructions']
        l = polyline.decode(step['polyline']['points'])
        l = [(el[1],el[0]) for el in l]
        geom = LineString(l)
        d = {'id': f'{i:02}','geom': geom, 'desc': desc}
        steps_out.append(d)
    gdf = gpd.GeoDataFrame(steps_out, geometry='geom',crs=4326)

    m = buff.explore(color='black',
                    tiles=xyz.CartoDB.Voyager,
                    style_kwds={'fill':False},name=f'buffer ({int(buffer_dist/1000)} KM)')
    for contour in iso['contour'].unique():
        m = iso[iso['contour']==contour].explore(m=m,color='fillColor',name=f'mapbox iso ({contour} min)')
    gdf.explore(m=m,
                name='google directions',
                column='id',
                popup='desc',
                tooltip=False,
                style_kwds={'weight':4}
    )
    m = target.explore(m=m,color='black', name='target')
    m = source.explore(m=m,color='black', name='start')
    folium.LayerControl().add_to(m)
    return m
In [4]:
source_text = 'Via Cornelio Celso 11 Roma'
#target_text = 'Via cherasco 96 Roma'
buffer_dist = 30_000
iso_times = [7,15,30]
departure_time_gmaps = '2025-02-14T13:00:00Z'
departure_time_mapbox = ''
#departure_time_mapbox = '2025-01-14T19:00:00Z'
In [5]:
make_magic(source_text,'41.98648839206951, 12.403421333214846',buffer_dist,iso_times,departure_time_gmaps,departure_time_mapbox)
https://api.mapbox.com/isochrone/v1/mapbox/driving-traffic/12.5105544,41.91218569999999?contours_minutes=7,15,30&polygons=true&access_token=pk.eyJ1IjoiZ2thcmltaXppIiwiYSI6ImNseGVyOWE2ejBoeDQybHIyYTRzb2NwcXMifQ.hA2f7LSM_CZPGRiTniMqxA
Distanza: 19,9 km
Durata: 27 min
Durata con traffico: 29 min
Soluzione 1 - 29 min
Soluzione 2 - 34 min
Out[5]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [ ]: