Skip to content

Repository files navigation

StressMap

StressMap calculates cycling Level of Traffic Stress for roads and paths using data from Open Street Map.

The processing pipeline downloads an OpenStreetMap street network, interprets available road and bicycle-infrastructure tags, applies the configured LTS rules, and produces segment-level CSV and GeoJSON outputs. Ratings are calculated separately for each direction of travel where the available data allows it.

LTS values range from 1 to 4:

  • LTS 1 represents the lowest-stress conditions.
  • LTS 4 represents the highest-stress conditions.

Segments that cannot be assigned a positive rating may contain an LTS value of 0 or a missing value and are excluded from the generated map GeoJSON.

The results depend on the completeness of OpenStreetMap data. When a required value is not available, the calculation may use an assumption defined in the project configuration.

Background

This code is adapted from Bike Ottawa's LTS code, modified to include Level of Traffic Stress for intersections by Madeleine Bonsma-Fisher.

The LTS decision tables used by the project are defined in config/tables.yml. A copy of the reference document is included at config/LTS-Tables-v2.2.pdf.

Intersection LTS functions are included in the codebase, but intersection ratings are not currently generated by the standard command-line workflow.

Requirements

  • Python 3
  • pip
  • Internet access for OpenStreetMap, Overpass, and OSMnx downloads

Run all commands from the repository root.

Installation

Create a virtual environment:

python -m venv .venv

Activate it on macOS or Linux:

source .venv/bin/activate

Activate it on Windows PowerShell:

.venv\Scripts\Activate.ps1

Install the project dependencies:

python -m pip install -r requirements.txt

Quick start

Process Cambridge and generate the GeoJSON used by the local map:

python main.py process -city Cambridge --plot

The first run downloads the required OpenStreetMap data and may take some time.

Start the local web server:

python web.py

Open http://localhost:8000 in a browser.

The local map reads from plots/LTS.json. Plotting another region replaces that file with the newly generated region.

Processing data

Process one municipality

python main.py process -city Boston

Add --plot to generate map data immediately after processing:

python main.py process -city Boston --plot

Force a complete rebuild

The processing pipeline is designed to reuse intermediate files from earlier runs. Use --rebuild to regenerate the downloaded and calculated outputs.

python main.py process -city Boston --rebuild

This makes new requests to OpenStreetMap and the Overpass API, so it will take longer than reusing saved files.

Process and combine multiple municipalities

Pass municipality names as a comma-separated list:

python main.py process -cities Cambridge,Boston,Somerville,Brookline --combine --plot

The combined output is saved under the region name GreaterBoston.

The current combine step concatenates the processed edge data from each municipality. It does not perform additional edge deduplication or construct a new unified regional graph.

Combine existing results

If each municipality has already been processed:

python main.py combine -cities Cambridge,Boston,Somerville,Brookline

Generate GeoJSON from the combined CSV:

python main.py plot -city GreaterBoston --format json

Generate GeoJSON from existing LTS data

If a region has already been processed:

python main.py plot -city Cambridge --format json

This reads:

data/Cambridge_4_all_lts.csv

and creates:

plots/Cambridge_LTS.json
plots/LTS.json

Supported municipalities

The municipalities currently configured in constants.py are:

  • Arlington
  • Belmont
  • Boston
  • Brookline
  • Cambridge
  • Chelsea
  • Everett
  • Lexington
  • Malden
  • Medford
  • Newton
  • Somerville
  • Waltham
  • Watertown

Names passed to -city or -cities must match these values exactly.

Generated files

Intermediate files are saved so that later runs do not need to repeat every step.

File Description
query/<region>_ways.query Generated Overpass query for ways
query/<region>_nodes.query Generated Overpass query for nodes
query/<region>_relations.query Generated Overpass query for bicycle-route relations
data/<region>_1.json Raw Overpass response containing ways
data/<region>_nodes.json Raw Overpass response containing nodes
data/<region>_relations.json Raw Overpass response containing relations
data/<region>_2_way_tags.csv OSM way tags included when loading the street graph
data/<region>_3.graphml Unsimplified OSMnx street graph
data/<region>_4_all_lts.csv Segment-level LTS results
data/log_filter_column_counts.csv Diagnostic counts for fields used during calculation
plots/<region>_LTS.json GeoJSON for the selected region
plots/LTS.json GeoJSON loaded by the included local map

The data/, plots/ and generated query files are excluded from version control.

LTS output

The main analytical output is:

data/<region>_4_all_lts.csv

Each row represents an edge in the OSMnx network. Important fields include:

Field Description
u, v, key Identifier for an edge in the directed multigraph
osmid OpenStreetMap way ID
geometry Segment geometry in WKT format
LTS_fwd LTS in the forward graph direction
LTS_rev LTS in the reverse direction
LTS Higher of the forward and reverse LTS values
bike_allowed_fwd Whether bicycle travel is allowed in the forward direction
bike_allowed_rev Whether bicycle travel is allowed in the reverse direction
bike_lane_fwd, bike_lane_rev Interpreted bicycle-lane information by direction
separation_fwd, separation_rev Interpreted physical separation by direction
parking_fwd, parking_rev Interpreted parking conditions by direction
speed Tagged or assumed prevailing speed
lane_count Interpreted number of motor-vehicle lanes
ADT Tagged or assumed average daily traffic
zoom Minimum map zoom level assigned to the segment

Many calculated fields also have a corresponding *_rule or *_condition column. These record the OpenStreetMap value, rule, or assumption used to derive the result.

For routing or accessibility analysis, use the directional fields rather than relying only on the combined LTS value.

Rows with an LTS of 0 or without a positive LTS value are excluded from the generated map GeoJSON.

Adding another municipality

Municipality definitions are stored in constants.py. Each entry contains an OpenStreetMap relation key and value used to build the Overpass queries.

For example:

CITIES = {
    "Cambridge": {
        "key": "wikipedia",
        "value": "en:Cambridge, Massachusetts"
    }
}

To add another municipality:

  • Find the boundary relation on openstreetmap.org.
  • Identify a key and value that select the intended area in an Overpass query.
  • Add the municipality to CITIES in constants.py.
  • Run the normal process command using the new dictionary key.

The current OSMnx download step appends , Massachusetts to the municipality name. Supporting locations outside Massachusetts therefore requires an additional code change.

Configuration

The LTS calculation is controlled by files in config/.

File Purpose
tables.yml LTS decision tables
rating_dict.yml Rules and assumptions used to interpret road characteristics
lane_parse.yml Directional bicycle access and lane-parsing rules
filter_test.yml Conditions used by the filter-testing utility
LTS-Tables-v2.2.pdf Reference LTS table document

Changes to these files can affect results across the entire dataset. Review the corresponding functions in lts_functions.py before changing the calculation rules.

Project structure

.
├── main.py              # Command-line entry point
├── LTS_OSM.py           # Data download and processing pipeline
├── lts_functions.py     # OSM interpretation and LTS calculations
├── LTS_plot.py          # CSV-to-GeoJSON conversion
├── constants.py         # Configured municipalities
├── web.py               # Local HTTP server
├── config/              # LTS tables and parsing rules
├── query/               # Base Overpass query templates
├── map/                 # Local map pages
├── mapbox/              # Mapbox Tilesets configuration and notes
├── database/            # SQLite loading utilities
├── geojson/             # Additional GeoJSON utilities
└── isochrone.py         # Experimental isochrone work

main.py, LTS_OSM.py, lts_functions.py, and LTS_plot.py make up the standard processing workflow.

The intersection-LTS functions and isochrone.py are not currently part of the main command-line pipeline.

Local map

The included local map loads the generated plots/LTS.json file and displays the LTS rating for each segment.

Start it on the default port:

python web.py

Use another port if needed:

python web.py -port 8080

The page uses Mapbox GL JS for the basemap, so it still requires an internet connection.

Large GeoJSON files may load slowly in the browser. Boston output has previously been observed to be around 40 MB, although the exact size depends on the OpenStreetMap data available when processing is run.

Instructions for publishing the data through Mapbox Tilesets are available in mapbox/mapbox_readme.md.

Data and methodology notes

  • Results reflect the OpenStreetMap data available when the analysis is run.
  • Missing or inconsistent OSM tags may lead to assumed values.
  • Assumptions are defined in config/rating_dict.yml and are retained in the output rule columns.
  • The overall LTS field uses the more stressful of the two calculated directions.
  • Intersection LTS code exists in the repository but is not enabled in the current main workflow.
  • The isochrone code is experimental and requires additional graph and node outputs that are not generated by the standard CLI.
  • StressMap results should be reviewed before being used for planning or policy decisions.

License

This project is licensed under the MIT License.

About

Calculating Level of Traffic Stress using Open Street Map data

Resources

Stars

9 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages