imports and setup
import sys
from pathlib import Path
module_dir = Path(".").resolve()
sys.path.append(str(module_dir))
import osmnx as ox
from network_explorer import BikeNetworkExplorerMatt Triano
April 4, 2026
April 4, 2026
On a trip to Copenhagen, I biked in a city for the first time in a decade. It was delightful and liberating, and it made sense that the majority of Copenhagen residents primarily commuted by bike. Returning home to Chicago, I bought a bike and quickly felt the difference that bike infrastructure and culture makes.
In Copenhagen, bike safety and bike theft were basically solved problems. Extensive infrastructure made routes everywhere safe to bike and from the number of unlocked bikes, it was clear that bike theft wasn’t an issue. Back in Chicago, I was sharing lanes with cars going 40 mph and despite extensive locking and uglying up my bike, I still never knew if my bike would still be there. The gap between those two experiences stuck with me. Over time I’ve learned safer, more comfortable routes, and Chicago keeps building out new infrastructure, but you wouldn’t know about these better ways from the existing popular routing tools (see the images below). So I built a tool to help find the safer routes and find secure parking areas, the missing last mile issues that keep people from biking.
You can try it at bike-map.dev.missinglastmile.net.
You already have an intuition for how routing works.
Think about a route you ride or drive regularly. At each intersection, you know which way to go. You haven’t evaluated every possible path through the city, but you can tell which options move you toward your destination and which ones don’t. You don’t consider turning away from where you’re going unless you have a good reason, like avoiding a busy street or getting over a bridge.
In computer science jargon, a street grid is called a graph (or network); a set of intersections connected by roads. Each intersection is a node, each road segment between intersections is an edge, and each edge has a cost — whatever you’d “pay” by traveling down that segment. Cost could be distance, time, effort, risk, unpleasantness; it could be anything, you just have to be able to represent it with positive numbers.
The routing algorithm I use, A* (pronounced “A star”), works the same way. It starts at the starting intersection, looks at the road segments it could go down, and for each one asks: how much did it cost to get here, how much does it cost to go down this road segment, and how far would I be from the destination? It picks the option where that total is lowest and repeats the process. This means it naturally favors direct routes, but it’s willing to explore less direct paths if the cost of the direct route is high enough, which is exactly what you want when “cost” means danger instead of distance.
Here’s what a few blocks of Chicago’s actual bike network look like as a graph: