Skip to main content

Classify OpenStreetMap ways by Level of Traffic Stress (LTS) using the Furth methodology.

Project description

osm-lts

PyPI version Python versions CI License: MIT

Classify OpenStreetMap ways by Level of Traffic Stress (LTS) using the Furth methodology.

LTS is a 1–4 scale from "kid-comfortable" (1) to "strong-and-fearless only" (4). It's the standard advocacy and planning input for "where is the bike network actually rideable for a typical adult" — far more honest than miles of "bike infrastructure" because it captures whether that infrastructure is on a calm street or a six-lane arterial.

Install

pip install osm-lts

Pure Python, no dependencies, Python 3.9+.

Use

from osm_lts import classify

classify({"highway": "residential", "maxspeed": "25 mph"})
# <LTS.MOST_ADULTS: 2>

classify({"highway": "primary"})
# <LTS.STRONG_AND_FEARLESS: 4>

classify({"highway": "cycleway"})
# <LTS.KID_COMFORTABLE: 1>

classify({"highway": "footway"})
# None — outside scope (not relevant to cyclist stress)

The function takes any Mapping[str, str] of OSM tags. Numeric tags (maxspeed, lanes) tolerate units ("25 mph", "50 km/h", "4;3") — only the leading digits are read. The result is an IntEnum, so int(classify(tags)) gives you the bare LTS value for serialization.

CLI

The package ships with an osm-lts command for batch jobs:

echo '{"highway": "residential", "maxspeed": "25 mph"}' | osm-lts classify
# {"tags": {"highway": "residential", "maxspeed": "25 mph"}, "lts": 2}

osm-lts classify --in ways.jsonl --out lts.jsonl

Input is JSON, JSONL, or a single JSON array — auto-detected.

How it works

The classifier mirrors Furth's published rules:

Tier Description Example triggers
LTS 1 Suitable for children highway=cycleway, living_street, cycleway=track
LTS 2 Most adults will tolerate residential ≤25 mph, bike lane on a slow street
LTS 3 Experienced cyclists only tertiary, fast residential, bike lane on a faster street
LTS 4 Strong-and-fearless only primary / trunk, >35 mph, ≥3 lanes and >30 mph

The branches evaluate top-to-bottom and short-circuit on the first match. Order matters — a cycleway=track on a 40 mph arterial returns LTS 1 because separation wins over speed. Highways outside scope (motorway, footway, sidewalk, steps, pedestrian) return None.

When maxspeed or lanes are missing, highway-typical defaults fill in:

from osm_lts import (
    DEFAULT_SPEED_MPH_BY_HIGHWAY,
    DEFAULT_LANE_COUNT_BY_HIGHWAY,
    EXCLUDED_HIGHWAYS,
)

These are public so callers can read them in their own UIs (e.g. "we assumed 25 mph because the way was untagged").

Customizing the rules

Wrap a Classifier instance to override any of the defaults. Useful for modeling a city or country whose posted-speed conventions or in-scope highway set differ from the US-centric defaults the package ships with.

from osm_lts import Classifier, EXCLUDED_HIGHWAYS

# Stricter unknown-speed default:
strict = Classifier(speed_mph_fallback=20)
strict({"highway": "residential"})  # <LTS.MOST_ADULTS: 2>

# Drop pedestrian-priority paths out of scope entirely:
narrower = Classifier(excluded_highways=EXCLUDED_HIGHWAYS | {"path"})
narrower({"highway": "path", "bicycle": "designated"})  # None

# Per-highway speed overrides:
slower_residential = Classifier(speed_mph_by_highway={"residential": 20})

Classifier is a frozen dataclass — instances are hashable and thread-safe to share. Use dataclasses.replace(clf, ...) for tweaked copies.

Origin

Extracted from the Bike Streets city-mapping platform.

Development

pip install -e '.[test]'
pytest

License

MIT. See LICENSE.

Project details


Download files

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

Source Distribution

osm_lts-0.3.0.tar.gz (13.1 kB view details)

Uploaded Source

Built Distribution

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

osm_lts-0.3.0-py3-none-any.whl (11.0 kB view details)

Uploaded Python 3

File details

Details for the file osm_lts-0.3.0.tar.gz.

File metadata

  • Download URL: osm_lts-0.3.0.tar.gz
  • Upload date:
  • Size: 13.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for osm_lts-0.3.0.tar.gz
Algorithm Hash digest
SHA256 bbed55080f528f05dd38bcb8b76b5a388957182727449fa874c4ae77c394a913
MD5 86249de8019024860d58f4fbfb223c83
BLAKE2b-256 acedecbc486094bc210df9fb98a5324a71e89f377f7ab8f68c9a98d48e723d4c

See more details on using hashes here.

Provenance

The following attestation bundles were made for osm_lts-0.3.0.tar.gz:

Publisher: release.yml on bikestreets/osm-lts

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

File details

Details for the file osm_lts-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: osm_lts-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 11.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for osm_lts-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 00ae875a3e96e22b008b0f7e65b6b76213d016b8711d21d44d1d75901ec83ba6
MD5 35809e53cb89111a2ef529c489275722
BLAKE2b-256 48a6da6ffb7ffa2c3f23e8baf66a28c6928db57cc38754937a9877e9a00e75e0

See more details on using hashes here.

Provenance

The following attestation bundles were made for osm_lts-0.3.0-py3-none-any.whl:

Publisher: release.yml on bikestreets/osm-lts

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page