Skip to main content

Python toolkit for cross-modal accessibility analysis on transport networks.

Project description

aperta

tests docs Ruff license

A Python toolkit for cross-modal accessibility analysis on transport networks — routing, distance/time computation, utility-based travel costs, and gravity- and logsum-based accessibility metrics on networkx graphs (routed via scipy.sparse.csgraph).

Three families of aperta capabilities, illustrated on the Bern region: network preparation (estimated traffic volumes and calibrated edge speeds), path feature collection (bike-comfort scores along realized routes and aggregated per origin cell), and accessibility analysis (time-based access to hiking opportunities and cross-modal utility-based access to groceries).

The name is Latin/Italian for open — the condition that accessibility, at root, measures.

Status

Pre-1.0, alpha. Published alongside a toolkit paper (in submission). APIs may change without notice until v1.0.

Install

pip install aperta              # algorithms only
pip install 'aperta[osm]'       # + OSM ingestion (osmnx)
pip install 'aperta[examples]'  # + everything needed to run the example notebooks

Requires Python ≥ 3.11.

The osm_helpers and topography modules import their backing libraries (osmnx, rasterio, requests) lazily — install the matching [osm] / [topo] extra if you use them, otherwise an ImportError surfaces at first use.

For development:

git clone git@github.com:mmiotti/aperta.git
cd aperta
pip install -e ".[osm,topo,h3]"
python -m unittest discover -s tests -t .

If you plan to edit the example notebooks under examples/, run the jupytext + nbstripout setup once after cloning. Not needed if you're only using the library or modifying Python source.

Workflow

Aperta is organized around a six-phase workflow. Phases 4 and 5's calibration sub-step are optional; the rest is the minimum end-to-end pipeline.

  1. Load and prepare data — networks (one per mode), land use, topography, optional ground-truth data (traffic counters, travel-survey times).
  2. Map data to units — aggregate source data into the cells → zones hierarchy; snap geo units to network nodes. Snapping is two complementary functions: insert_projected_nodes optionally enriches the graph by inserting virtual nodes onto road segments where points would otherwise have no graph node within snap distance (with optional filtering, e.g. main roads only); snap_to_network_nodes then does the actual point-to-node match (optionally two-tier with a priority node set for "prefer main-road nodes" semantics).
  3. Build sparse OD pairs — the tiered OD structure with per-cell origins at near range and zone-aggregated destinations at far range, keeping per-origin compute bounded independently of network extent.
  4. (Optional) Estimate traffic flows — sampled betweenness centrality (essentially an network-based "2.5-step" travel demand model); optionally calibrate against observed traffic counter data.
  5. Estimate travel costs — shortest paths on the routing graph. Three optional features: (a) trip overheads for parking search, unlocking a bicycle, etc (usually estimated through correlation with urban characteristics such as density); (b) utility-based generalized costs and (c) edge-weight calibration against observed travel times.
  6. Calculate accessibilities — cumulative-opportunity, gravity, nearest-k, logsum (and cross-modal aggregation across per-mode results).

See the API reference for which module covers each phase and for the specific functions.

Runnable examples, in increasing depth:

  • examples/minimal/accessibility.ipynb — what aperta does in ~50 lines using only OpenStreetMap. Cambridge MA, ~10 s.
  • examples/walkthrough/accessibility.ipynb — guided tour of every primitive; walking + cycling, cross-modal logsum, path-first per-edge feature aggregation. Central Paris, ~1 min end-to-end.
  • examples/extended/ — production-scale Bern + 40 km: prep pipeline, calibration against observed travel times, traffic-flow estimation, accessibility analysis. ~30 min.

The toy-world end-to-end test in tests/test_workflow.py doubles as the smallest possible walk-through (~150 lines, runs in a second).

Quick example

The three-line core of an accessibility analysis: build the tiered OD pairs, route shortest paths, count opportunities within a travel-time budget.

from aperta import accessibility, od_pairs, routing

pairs = od_pairs.get_pairs(cells, r_cells=2000.0, node_column='node_id')
times = routing.tiered_path_costs(pairs, graph, weight='walk_time_s')
acc   = accessibility.cumulative_opportunities(
    times, {'supermarkets': weights}, {},
    [accessibility.Bin('15min', 0, 15 * 60)],
)

A complete, runnable version (OSM ingestion, plotting): examples/minimal/accessibility.ipynb.

Modules

See the API reference for module-by-module documentation.

Design

What aperta is:

  • Path-first. Routing returns the realized route alongside the cost as a single primitive, so per-edge attributes (gradient, exposure, surface, perceived safety) aggregate along the path natively — the architectural prerequisite for utility-based and route-aware accessibility.
  • Cross-modal. Mode and network are orthogonal: one network per mode, with min / logsum aggregation across modes as a first-class operation. Generalizes to any axis of network variation — time-of-day, congestion regime, infrastructure scenario.
  • Multi-scale. A tiered cell / zone OD structure bounds per-origin computation independently of network extent — country-scale reach without country-scale destination counts.
  • Live-graph routing. Dijkstra on the graph directly, no precomputed index. Slower per query than contraction-hierarchy tools, but edge-weight changes are immediate — what makes iterative calibration and scenario comparison practical.

What aperta is not:

  • No filesystem assumptions. Algorithm functions take plain networkx graphs, pandas / geopandas frames, and numpy arrays — no file I/O.
  • No DAG engine, no global state. No caching, no dependency tracking, no orchestration. Every function takes its inputs explicitly. For DAG features, layer DVC or Snakemake on top.

Interoperability with other accessibility tools

Aperta deliberately doesn't try to do everything in-house. Two interoperability patterns are worth flagging:

  • Public transit via R5. Aperta has no native public-transit support right now (no GTFS reader, no RAPTOR-style time-dependent routing). Anything that can be expressed as a networkx graph with appropriate edge weights — including simplified transit-as-graph models — will route in aperta like any other network. For full GTFS-based transit routing (calendars, transfers, frequency-based services), the pragmatic pattern is to compute the transit OD cost matrix with R5 (via r5py), align its origins/destinations to the same cell layer aperta uses, and feed the resulting per-mode cost ODM into od_pairs.aggregate_across_modes alongside the walk / cycle / car ODMs computed by aperta. The cross-modal aggregation proceeds identically whether each per-mode ODM came from aperta's router or elsewhere.
  • Faster cost-only routing via Pandana/pandarm. Aperta's live-graph routing is the right trade-off for path-first, iterative, and scenario-comparative workloads, but for one-shot cost-only accessibility on a large fixed network, contraction-hierarchy backends like Pandana (and its recent modernized fork pandarm) route faster per query. The calibrated edge weights produced by calibration.calibrate_edge_weights are plain per-edge attributes on the networkx graph and transfer cleanly to a Pandana/pandarm network built from the same OSM extract — i.e., you can calibrate edge weights in aperta and then route with them in Pandana/pandarm.

Benchmark vs Pandana

Ultimate speed for the full accessibility stack was not aperta's goal. Nonetheless, aperta typically runs within 1–5× of Pandana on equivalent cumulative-opportunity workloads. When the area of interest (for which to calculate accessibilities) is substantially smaller than the buffer zone (destinations to consider), or when aiming to recalculate accessibilities for a select subset of locations after a graph topology or edge weight change, aperta can even be faster than Pandana. See the benchmark for the full setup and numbers, or run examples/extended/benchmark.py to reproduce.

Acknowledgments

Aperta was developed at the Chair of Ecological Systems Design at ETH Zurich in the context of the BlueCity project and LUMOS.

Cite this

If you use aperta in a publication, please cite the archived release:

@software{miotti_aperta_2026,
  author    = {Miotti, Marco},
  title     = {{Aperta: Path-first, cross-modal accessibility analysis in Python}},
  year      = 2026,
  publisher = {Zenodo},
  doi       = {10.5281/zenodo.20473787},
  url       = {https://doi.org/10.5281/zenodo.20473787}
}

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

aperta-0.2.0a0.tar.gz (212.7 kB view details)

Uploaded Source

Built Distribution

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

aperta-0.2.0a0-py3-none-any.whl (141.7 kB view details)

Uploaded Python 3

File details

Details for the file aperta-0.2.0a0.tar.gz.

File metadata

  • Download URL: aperta-0.2.0a0.tar.gz
  • Upload date:
  • Size: 212.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for aperta-0.2.0a0.tar.gz
Algorithm Hash digest
SHA256 43db6dac642c023ebc441c8369277f3cf45e27d88773d91bec76484ab098cf22
MD5 7bf244874f44ac325b0b26da00e7def0
BLAKE2b-256 7ccb217f19b3faa9ade4698202678eacf1fbc06d96a3bc3ecd721d049d6c2a4a

See more details on using hashes here.

File details

Details for the file aperta-0.2.0a0-py3-none-any.whl.

File metadata

  • Download URL: aperta-0.2.0a0-py3-none-any.whl
  • Upload date:
  • Size: 141.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for aperta-0.2.0a0-py3-none-any.whl
Algorithm Hash digest
SHA256 f770332cb320b3cc37b6d248b6465978192f84bd3851c85745910a76c2725022
MD5 1bca1d806b19030121b0295d9818b75e
BLAKE2b-256 ed18a3729a53d2dc061705aa9c6f5d6d3d7705dec14a1f4f05c1734418adc449

See more details on using hashes here.

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