Routing in Python with casaGeoTools

Introduction

In this tutorial, we use a simple Jupyter Notebook to walk through three practical examples that demonstrate how routing can be integrated into Python-based geospatial workflows. The examples show how to calculate multiple routes in batch, compare different routing strategies, and generate alternative routes.

This tutorial is designed for data scientists, geospatial analysts, and developers working with spatial data in Python.

Step 1

First, import the required Python packages, including Pandas, Shapely, GeoPandas, and Folium. Folium is used to visualize the calculated routes on interactive maps.

Next, adjust the Pandas display settings. This ensures that the notebook shows all rows and columns of the result tables, which is helpful when inspecting the routing results.

Python Script


import os

import pandas as pd
import shapely
from dotenv import load_dotenv
import geopandas as gpd
import folium

# casaGeoTools Beta modules
from casageo import spatial as cs
from casageo import tools as ct



# Ensure dataframes are printed completely
pd.set_option("display.max_rows", None)
pd.set_option("display.max_columns",None)
pd.set_option("display.width",999)



Step 2 - Define Routing Inputs

First, we initialize the casaGeoTools client. The API key is loaded from a local .env file to keep credentials separate from the notebook.

Next, we define several Shapely points representing the origin and destination locations used in the routing examples.

These locations are referenced throughout the examples:

  • Headquarters casaGeo, Itzehoe (izet)

  • Elmshorn Central Station (iz)

  • Hamburg Central Station (hh)

  • Kiel Central Station (ki)

  • Lübeck Central Station (hl)

Python Script



# Load environment variables (API key) and initialize casaGeoTools client
load_dotenv()
API_KEY = os.getenv("CASAGEOTOOLS_API_KEY")

if not API_KEY:
raise ValueError(
"No CASAGEOTOOLS_API_KEY found in .env file. Please set your casaGeoTools API key."
)

cga = ct.CasaGeoClient(API_KEY)



point_izet = shapely.Point(9.4854461, 53.9580118)
point_iz = shapely.Point(9.660475, 53.753110)
point_hh = shapely.Point(10.008223, 53.553089)
point_ki = shapely.Point(10.13008, 54.31367)
point_hl = shapely.Point(10.66865, 53.86621)


Example 1 - Batch Routing

In the first example, we demonstrate batch routing. We calculate routes from our headquarters in Itzehoe as the origin to three different destinations: the central stations in Hamburg, Kiel, and Lübeck.

To do this, we create a DataFrame where each row represents a routing request. Each entry contains the origin, destination, routing mode, and transport mode. The routes are generated using the routes_batch function. casaGeoTools processes all rows in the DataFrame and returns the results as a GeoDataFrame containing the route geometries.

Finally, we visualize the routes on an interactive Folium map. Each route geometry is added to the map as a GeoJSON layer, making it easy to inspect the results.

Python Script



# Create first DataFrame for the batch routing with three different destinations
list_routing_1 = pd.DataFrame([
{"id": 1, "origin": point_izet, "destination": point_hh, "routing_mode": "fast", "transport_mode": "car",
"destination_name": "Hamburg Central Station"},
{"id": 2, "origin": point_izet, "destination": point_ki, "routing_mode": "fast","transport_mode": "car",
"destination_name": "Kiel Central Station"},
{"id": 3, "origin": point_izet, "destination": point_hl, "routing_mode": "fast", "transport_mode": "car",
"destination_name": "Lübeck Central Station"},
])
# Generate routes
cgs = cs.CasaGeoSpatial(cga)

try:
routes_1_gdf = cgs.routes_batch(
list_routing_1,
)
except Exception as e:
routes_1_gdf = gpd.GeoDataFrame().join(list_routing_1.set_index("id"), on="id")
print(f"⚠️ Error during batch routing: {e}")

# Generate map
colors = ['blue','red','purple', 'black']
map_1 = folium.Map(location=[53.85, 9.51], zoom_start=8)
for idx, row in routes_1_gdf.iterrows():
if row['geometry'] is not None:
color = colors[row['id'] % len(colors)]
folium.GeoJson(
row['geometry'],
style_function=lambda x, c=color: {
'color': c,
'weight': 3,
'opacity': 0.7
}
).add_to(map_1)



Example 2 - Fast vs Short Route

In this example, we calculate two routes between the same origin — our headquarters — and Elmshorn Central Station as the destination. One request uses the fast routing mode, which optimizes for travel time, while the second request uses the short routing mode, which optimizes for distance.

This demonstrates how different routing strategies affect the resulting route geometry. The routes are then visualized on an interactive map to compare the paths.

Python Script


list_routing_2 = pd.DataFrame([
{"id": 1, "origin": point_izet, "destination": point_iz, "routing_mode": "fast","transport_mode": "car",
"destination_name": "Elmshorn Central Station, fastest way"},
{"id": 2, "origin": point_izet, "destination": point_iz, "routing_mode": "short","transport_mode": "car",
"destination_name": "Elmshorn Central Station, shortest way"},
])



# Generate routes

cgs = cs.CasaGeoSpatial(cga)

try:
routes_2_gdf = cgs.routes_batch(
list_routing_2,
)
except Exception as e:
routes_2_gdf = gpd.GeoDataFrame().join(list_routing_2.set_index("id"), on="id")
print(f"⚠️ Error during batch routing: {e}")



# interactive map

map_2 = folium.Map(location=[53.85, 9.51], zoom_start=10)
for idx, row in routes_2_gdf.iterrows():
if row['geometry'] is not None:
color = colors[row['id'] % len(colors)]
folium.GeoJson(
row['geometry'],
style_function=lambda x, c=color: {
'color': c,
'weight': 3,
'opacity': 0.7
}
).add_to(map_2)




Example 3 - Alternative Routes

Finally, we request several alternative routes and visualize them on an interactive map. In our example, the alternatives parameter is set to three, meaning that up to three additional route options may be returned. The parameter supports values up to seven alternatives.

Alternative routes are only returned if they represent meaningful route variations. In this example, the routing service returns four routes in total: the main route and three alternatives. As before, we create an interactive map to inspect the results.

Python Script


# Create third DataFrame for alternative routes

list_routing_3 = pd.DataFrame([
{"id": 1, "origin": point_izet, "destination": point_hh,"routing_mode": "fast", "transport_mode": "car", "destination_name": "Hamburg Central Station", "alternatives": 3}
])



# Generate routes

try:
routes_3_gdf = cgs.routes_batch(
list_routing_3,
)
except Exception as e:
routes_3_gdf = gpd.GeoDataFrame().join(list_routing_3.set_index("id"), on="id")
print(f"⚠️ Error during batch routing: {e}")



map_3 = folium.Map(location=[53.55, 10], zoom_start=11)
for idx, row in routes_3_gdf.iterrows():
if row['geometry'] is not None:
color = colors[row['subid'] % len(colors)]
folium.GeoJson(
row['geometry'],
style_function=lambda x, c=color: {
'color': c,
'weight': 3,
'opacity': 0.7
}
).add_to(map_3)




Download

To jumpstart your development, you can download this example as a Jupyter Notebook below.