Styled, interactive road maps of any place (OpenStreetMap) or road file, with PNG previews.
Runs on your machine from its package; the steps are below.

At most: creates or changes data: records, files, settings.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
uvx roadstyle-mcp
MCP client config
{
"mcpServers": {
"roadstyle": {
"command": "uvx",
"args": [
"roadstyle-mcp"
]
}
}
}
Beautiful, interactive road maps from Python.
One offline HTML file with real road cartography, Google Street View and a JavaScript API.
Install · Quickstart · Gallery · AI agents · Documentation

window.rs* JavaScript API and events for your own dashboard.pip install roadstyle # core
pip install "roadstyle[all]" # + studio, numeric ramps, vector tiles, lonboard, DuckDB, …
Python ≥ 3.10. Individual extras and the dev setup: Install.
From OpenStreetMap, no data needed. Any place, with osmnx (pip install osmnx):
import osmnx as ox
import roadstyle as rs
G = ox.graph_from_place("Tartu, Estonia", network_type="drive", simplify=False)
G = ox.simplify_graph(G, edge_attrs_differ=["bridge", "tunnel"]) # keep tunnels and bridges apart
rs.render_edges(ox.graph_to_gdfs(G, nodes=False)).save("tartu.html")
From your own data. Any file or GeoDataFrame with line geometry and a highway column:
import geopandas as gpd
import roadstyle as rs
edges = gpd.read_file("edges.gpkg") # LineStrings + a `highway` column, any CRS
rs.render_edges(edges).save("map.html") # open map.html: no server (Street View needs one)
More looks:
rs.render_edges(edges, basemap="dark_matter", view_3d=True) # dark, 3D bridge decks
rs.render_edges(edges, palette="carto", basemap="positron") # the classic OSM look
rs.render_edges(edges, color_by="aadt", cmap="viridis") # colour by your data
rs.render_edges(edges, palette="mono", color_options={ # several colourings,
"Traffic": {"color_by": "aadt", "cmap": "viridis"}, # switched in the browser
"Speed": {"color_by": "maxspeed_kmh", "cmap": "magma"}})
rs.render_edges(edges, tiles=True) # 100k+ edges
rs.render_dashboard(edges).save("dashboard.html") # map + query sidebar
rs.render_street_view(edges).save("street_view.html") # map and Street View side by side
No Python? roadstyle edges.gpkg -o map.html --basemap dark_matter, or click through it in the
workbench: pip install "roadstyle[studio]" && roadstyle studio.
Every look with its code: the gallery.
Only geometry and highway are required. Other columns switch features on:
| Column | Powers |
|---|---|
geometry (LineString, any CRS) | the edges. Each edge is directed: a two-way road is two edges with reversed geometry |
highway (OSM class) | colour, width, casing, draw order |
name | street labels, popup title |
oneway | direction arrows |
bridge / tunnel / layer | grade separation: tunnels below, bridges on decks above |
edge_id | popups; 64-bit ids stay exact |
| anything else | shown in the popup, queryable from JavaScript |
duckOSM (duckosm export-gis) exports exactly this, and
osmnx edges work as they are: rs.render_edges(ox.graph_to_gdfs(G, nodes=False)).
The saved page exposes window.rs* functions and rs:* events:
const ids = rsQuery(p => p.maxspeed_kmh > 30); // feature ids whose properties match
rsColor(ids, "#ff00aa"); rsFocus(ids); // paint them, fit the camera
document.addEventListener("rs:select", e => console.log(e.detail.properties));
Every function and event: JavaScript API.
MCP server. Lets any MCP-capable AI app (Claude Code, Claude Desktop, Cursor, …) draw road maps
without writing code: render_place("Tartu, Estonia"), render_file("roads.gpkg") and snapshot.
Each saves an HTML map and returns its path plus a PNG preview the agent can look at.
claude mcp add roadstyle -- uvx roadstyle-mcp
In claude_desktop_config.json:
{"mcpServers": {"roadstyle": {"command": "uvx", "args": ["roadstyle-mcp"]}}}
Maps are saved in ~/roadstyle-maps/. The PNG preview needs Chromium, once:
uvx --from roadstyle-mcp playwright install chromium.
Claude Code plugin. The MCP server and the skill below in one install:
/plugin marketplace add Khoshkhah/roadstyle
/plugin install roadstyle@roadstyle
For agents that write code:
skills/roadstyle/SKILL.md: a
skill for agents that use roadstyle (the one call, the data contract, the JS API, the traps).
Install it for Claude Code:
mkdir -p ~/.claude/skills/roadstyle && curl -fsSL -o ~/.claude/skills/roadstyle/SKILL.md \
https://raw.githubusercontent.com/Khoshkhah/roadstyle/main/skills/roadstyle/SKILL.md
AGENTS.md: for agents working on this repo.llms.txt: the docs site as a link list for LLMs.Both keys are optional. roadstyle works without them, just with less.
CARTO_API_KEY, or use a keyless base map (esri_street, esri_dark_gray, osm, blank).The default base map (voyager) and positron and dark_matter come
from CARTO. Without a key, their tiles are stamped API KEY REQUIRED. Get a free key at
carto.com/basemaps/apikey, then use any one of these:
export CARTO_API_KEY="…" # environment variable
{ "config": { "api_keys": { "carto": "…" } } }
Save that JSON as ~/.config/roadstyle/roadstyle.json, or as roadstyle.json in the folder you run
from. In Python you can pass rs.render_edges(edges, api_key="…") instead. With no key at all, use
a keyless base map: esri_street, esri_dark_gray, osm or blank.
Street View works with no key: the keyless Google embed. With a Google Maps JavaScript API key, the Street View panel and window get a Linked / Classic switch. Linked is a real panorama: the map marker walks and turns with you, and only Google's own street photos are shown. Classic is the keyless embed. To get a key:
https://example.com/*); API restrictions = Maps JavaScript API only.Then pass it in:
rs.render_edges(edges, street_view_key="AIza…") # the floating Street View window
rs.render_street_view(edges, street_view_key="AIza…") # the side-by-side page
The key is written into the page, as every browser key is, so the site restriction in step 3 is
what protects it. It also means Linked works only on the addresses you listed: to try it on
localhost, add http://localhost:*/* to the list. Keep the key out of git, for example in an
environment variable.
More: settings & base maps · Google Street View.
khoshkhah.github.io/roadstyle, with live maps on every page.
| Where | What you find |
|---|---|
| Get started | install, a first map, what your data needs |
| Guides | style the roads · colour by your data · your own layers · Google Street View · big networks · dashboards & JavaScript · on a website |
| Gallery | a picture and one line of code per look |
| Reference | every parameter · JavaScript API · settings & base maps · command line |
| Changelog | what changed in each release |
MIT. Base-map tiles come from third-party services (CARTO, OSM, Esri) with their own attribution and terms.