Skip to main content

Cartons

PyPI version Python versions License Cartons CI

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

Preview

Geneva → Zürich

Geneva to Zürich route

Bern → Zürich

Bern to Zürich route

Route detail

Zoomed route detail

Installation

Cartons requires Python 3.10 or newer.

python -m pip install cartons

To install the latest repository version instead:

python -m pip install "git+https://github.com/AndPan3/cartons.git"

Quick start

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.

Quick map

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.

Configurable route map

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 existing coordinates

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",
)

Coordinate order

Cartons uses two coordinate conventions depending on what you are doing.

Routing

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],
]

Direct drawing

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.

Public API

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()

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.duration

The geometry uses [longitude, latitude] order.

Multiple waypoints

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()

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()

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()

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()

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.length therefore represents coordinate degrees, not road distance in metres or kilometres. Use the routing result's distance when you need routed distance.

geo_json_geometry()

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.

OSRM servers

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.

Map tiles and attribution

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.

Geometry

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.

Errors

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.

Testing

Cartons uses automated smoke tests with GitHub Actions.

On pushes and pull requests, CI:

  1. checks out the repository
  2. sets up Python
  3. installs Cartons directly from the checked-out commit
  4. verifies the public imports
  5. runs routing tests
  6. runs map_route() tests
  7. runs quick_map() tests
  8. runs draw() tests
  9. 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.

Development

Clone the repository:

git clone https://github.com/AndPan3/cartons.git
cd cartons

Install the working copy:

python -m pip install -e .

The editable installation lets Python import your local Cartons source while you develop it.

Dependencies

Cartons builds on:

These dependencies are installed automatically with Cartons.

Scope

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.

Privacy

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.

Contributing

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.

License

Cartons is released under the MIT License. See otherfiles/LICENSE.

AI assistance

The GitHub Actions CI workflow was created with AI assistance.

The Cartons package source code was written by the maintainer.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cartons-1.4.1.tar.gz (6.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cartons-1.4.1-py3-none-any.whl (7.6 kB view details)

Uploaded Python 3

File details

Details for the file cartons-1.4.1.tar.gz.

File metadata

  • Download URL: cartons-1.4.1.tar.gz
  • Upload date:
  • Size: 6.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cartons-1.4.1.tar.gz
Algorithm Hash digest
SHA256 3eb90ac0f5a0424be5de63c5a4bf87aafbf04a12c06b3568def2825b2716d738
MD5 0c5876758d05ffae7852bd1126b006c6
BLAKE2b-256 dca3e924c1bcd03949444815d516fa6c154a641d6a7bf1dadf626570c14654cd

See more details on using hashes here.

Provenance

The following attestation bundles were made for cartons-1.4.1.tar.gz:

Publisher: python-publish.yml on AndPan3/cartons

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cartons-1.4.1-py3-none-any.whl.

File metadata

  • Download URL: cartons-1.4.1-py3-none-any.whl
  • Upload date:
  • Size: 7.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cartons-1.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a0b70fb8370932fd318cc29fb6e5b3baebb60efb6e560a6c85d7fafbaaddd759
MD5 93c0f013c21bef8526524ab2d8582c11
BLAKE2b-256 4810d4c1671f761b6bf54b9c5452d85fb91cd4b97515da94184857b9c46e4960

See more details on using hashes here.

Provenance

The following attestation bundles were made for cartons-1.4.1-py3-none-any.whl:

Publisher: python-publish.yml on AndPan3/cartons

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.4.1 This release

2 files

1.3.0

2 files

1.2.0

2 files

1.1.10

2 files

1.1.8

2 files

1.1.7

2 files

1.1.6

2 files

1.1.5

2 files

1.1.4

2 files

1.1.3

2 files

1.1.0

2 files

1.0.5

2 files

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page