A lightweight Python toolkit for OSRM routing, route geometry conversion, and interactive Folium maps.
Cartons provides a small API for going from coordinates to routes, maps, Shapely geometry, and GeoJSON without having to wire together OSRM, RoutingPy, Folium, and Shapely yourself.
coordinates
│
├── route() ──────────────> OSRM route
│
├── map_route() ──────────> configurable Folium map
│
├── quick_map() ──────────> quick Folium map
│
├── line_string_route() ──> Shapely LineString
│
└── geo_json_geometry() ──> GeoJSON geometry
Cartons requires Python 3.10 or newer.
python -m pip install cartonsTo install the latest repository version instead:
python -m pip install "git+https://github.com/AndPan3/cartons.git"import cartons
coords = [
[7.4442153, 46.94686], # Bern
[8.5431302, 47.3668725], # Zürich
]
result = cartons.route(
"https://router.project-osrm.org",
coords,
"driving",
)
print(result.distance)
print(result.duration)Routing coordinates are supplied as:
[longitude, latitude]
Cartons requires at least two coordinates.
If you just want to route some coordinates and display the result:
import cartons
coords = [
[7.4442153, 46.94686],
[8.5431302, 47.3668725],
]
m = cartons.quick_map(
"https://router.project-osrm.org",
coords,
"driving",
)
m.save("route.html")quick_map() uses a simple predefined map style and automatically fits the map around the returned route.
Use map_route() when you want control over the map appearance:
import cartons
coords = [
[7.4442153, 46.94686],
[8.5431302, 47.3668725],
]
m = cartons.map_route(
"https://router.project-osrm.org",
coords,
color="red",
weight=5,
tiles="CartoDB Positron",
attribution="© CartoDB Positron",
osrm_profile="driving",
marker=True,
)
m.save("route.html")map_route():
- requests a route from OSRM
- converts the returned
[lon, lat]geometry to Folium's[lat, lon]order - draws the route as a
PolyLine - optionally adds start and end markers
- automatically fits the map around the route
- returns a
folium.Map
draw() does not calculate a route.
Use it when you already have coordinates that you want to display:
import cartons
coords = [
[46.94686, 7.4442153],
[47.3668725, 8.5431302],
]
m = cartons.draw(coords)
m.save("line.html")Because these coordinates are passed directly to Folium, draw() expects:
[latitude, longitude]
You can customize the map:
m = cartons.draw(
coords,
color="purple",
weight=7,
tiles="CartoDB Positron",
attribution="© CartoDB Positron",
)Cartons uses two coordinate conventions depending on what you are doing.
Functions that send coordinates to OSRM use:
[longitude, latitude]
This applies to:
cartons.route()
cartons.map_route()
cartons.quick_map()
cartons.line_string_route()
cartons.geo_json_geometry()Example:
coords = [
[7.4442153, 46.94686],
[8.5431302, 47.3668725],
]cartons.draw() uses Folium-ready coordinates:
[latitude, longitude]
Example:
coords = [
[46.94686, 7.4442153],
[47.3668725, 8.5431302],
]map_route() and quick_map() handle the OSRM → Folium coordinate conversion internally.
Cartons currently exposes six functions from the package root:
| Function | Purpose | Returns |
|---|---|---|
route() |
Calculate an OSRM route | RoutingPy route result |
map_route() |
Calculate and draw a configurable route | folium.Map |
quick_map() |
Calculate and quickly draw a route | folium.Map |
draw() |
Draw existing coordinates without routing | folium.Map |
line_string_route() |
Calculate a route and convert its geometry | shapely.LineString |
geo_json_geometry() |
Calculate a route and convert its geometry to GeoJSON | str |
All six can be imported directly:
from cartons import (
route,
map_route,
quick_map,
draw,
line_string_route,
geo_json_geometry,
)route(base_url, coords, osrm_profile)Calculates a route using an OSRM-compatible server.
result = cartons.route(
"https://router.project-osrm.org",
[
[6.143158, 46.204391],
[8.541694, 47.376887],
],
"driving",
)Cartons requests the full route overview from RoutingPy/OSRM.
The returned RoutingPy object can provide data such as:
result.geometry
result.distance
result.durationThe geometry uses [longitude, latitude] order.
More than two coordinates can be supplied:
coords = [
[6.143158, 46.204391], # Geneva
[7.447447, 46.948271], # Bern
[8.541694, 47.376887], # Zürich
]
result = cartons.route(
"https://router.project-osrm.org",
coords,
"driving",
)Coordinates are visited in the order supplied.
map_route(
base_url,
coords,
color,
weight,
tiles,
attribution,
osrm_profile,
marker=True,
)Calculates a route and displays it on a configurable Folium map.
| Parameter | Description |
|---|---|
base_url |
OSRM server URL |
coords |
Routing coordinates in [lon, lat] order |
color |
Route line color |
weight |
Route line width |
tiles |
Folium tile provider or tile URL |
attribution |
Attribution for the tile source |
osrm_profile |
Profile passed to OSRM |
marker |
Whether to add start/end markers; defaults to True |
Returns a folium.Map.
quick_map(base_url, coords, osrm_profile)A simpler route-to-map helper.
It calculates the route, creates a Folium map, draws the route, fits the map around it, and returns the resulting folium.Map.
Use map_route() instead when you need custom styling or endpoint markers.
draw(
coords,
color="blue",
weight=5,
tiles="CartoDB Positron",
attribution="© CartoDB Positron",
)Draws an existing path without contacting OSRM.
coords must already be in Folium's [latitude, longitude] order.
Returns a folium.Map.
line_string_route(coords, osrm_profile, base_url)Calculates an OSRM route and converts the returned geometry to a Shapely LineString.
line = cartons.line_string_route(
[
[7.4442153, 46.94686],
[8.5431302, 47.3668725],
],
"driving",
"https://router.project-osrm.org",
)
print(line)The coordinates remain geographic longitude/latitude coordinates.
LineString.lengththerefore represents coordinate degrees, not road distance in metres or kilometres. Use the routing result's distance when you need routed distance.
geo_json_geometry(coords, osrm_profile, base_url)Calculates a route, converts it to a Shapely LineString, and serializes that geometry as GeoJSON.
geojson = cartons.geo_json_geometry(
[
[7.4442153, 46.94686],
[8.5431302, 47.3668725],
],
"driving",
"https://router.project-osrm.org",
)
print(geojson)The return value is a GeoJSON geometry string, not a complete GeoJSON Feature or FeatureCollection.
Cartons is a client library. It does not contain its own routing engine.
You provide an OSRM-compatible server:
base_url = "https://router.project-osrm.org"The public OSRM demo server is useful for development and experimentation. Applications with production requirements should use an appropriate routing service or their own OSRM deployment.
The meaning and availability of routing profiles depend on the configured OSRM server.
Folium maps load map tiles from the configured tile provider.
When choosing custom tiles, make sure you follow the provider's usage and attribution requirements.
Cartons does not host or proxy map tiles.
OSRM route geometry is represented as:
[longitude, latitude]
line_string_route() preserves that order when constructing the Shapely geometry.
For Folium maps, Cartons internally converts routed geometry to:
[latitude, longitude]
because that is the order expected by Folium/Leaflet.
All public functions that operate on coordinate sequences require at least two coordinates.
For example:
cartons.route(
"https://router.project-osrm.org",
[[7.4442153, 46.94686]],
"driving",
)raises:
ValueError: At least 2 coordinates are required.
Errors from RoutingPy, OSRM, Folium, Shapely, the network, or an invalid server configuration are otherwise allowed to propagate to the caller.
Cartons uses automated smoke tests with GitHub Actions.
On pushes and pull requests, CI:
- checks out the repository
- sets up Python
- installs Cartons directly from the checked-out commit
- verifies the public imports
- runs routing tests
- runs
map_route()tests - runs
quick_map()tests - runs
draw()tests - runs geometry-format tests
If any test script exits with an error, the CI job fails.
This keeps the tests intentionally simple: the smoke suite checks that the package installs and its main public functionality executes successfully.
Clone the repository:
git clone https://github.com/AndPan3/cartons.git
cd cartonsInstall the working copy:
python -m pip install -e .The editable installation lets Python import your local Cartons source while you develop it.
Cartons builds on:
These dependencies are installed automatically with Cartons.
Cartons intentionally stays small.
Its job is primarily:
coordinates → route → geometry/map
It is not intended to replace a complete GIS framework, routing server, geocoder, or navigation application.
Routing functions send the coordinates you provide to the configured OSRM server.
Interactive Folium maps may also cause the browser displaying the map to request tiles from the configured tile provider.
Choose routing and tile services appropriate for the sensitivity of your data.
Issues and pull requests are welcome.
When changing public functionality, run the smoke-test scripts before submitting the change. GitHub Actions will also run them automatically after the change is pushed.
Cartons is released under the MIT License. See otherfiles/LICENSE.
The GitHub Actions CI workflow was created with AI assistance.
The Cartons package source code was written by the maintainer.


