CLI — bikescore-bna¶
bikescore-bna ships one console script, bikescore-bna, a thin shell over the
Python API:
$ bikescore-bna --help
<city> in every command is a path to a directory containing a city.toml. There is
no slug lookup against a multi-city store — a city is always an explicit path.
score¶
Run the full pipeline and write the block-level scores table.
$ bikescore-bna score <city> [OPTIONS]
| option | default | meaning |
|---|---|---|
--scenario, -s |
default |
bundled scenario name, or a path to a scenario YAML |
--set k=v |
— | config override (repeatable), e.g. --set city.default_speed=40 |
--set-file |
— | YAML file of key: value overrides (merged under --set) |
--out-dir |
<city>/runs/<timestamp> |
persist all stage outputs here (reusable by export --from) |
--out, -o |
— | also copy scores.parquet to this file |
--datasets |
<city>/datasets |
directory holding the raw inputs |
--to |
— | stop after this stage (partial run) |
Stage outputs are kept under --out-dir (never a discarded temp dir), so a later
export --from <that dir> reuses them without recomputing.
Raw inputs are discovered in the datasets directory by name (osm-*.pbf,
boundary-*.geojson, census-*.parquet, lodes_main-*.csv, lodes_aux-*.csv) — the
layout acquire writes. All stage outputs (network, LTS segments,
neighborhood ratings, …) are left in a temp working directory, printed on stderr.
$ bikescore-bna score ./aspen-colorado --scenario default --out scores.parquet
scores → scores.parquet
acquire¶
Download the raw inputs (OSM, boundary, and — for US cities — census + LODES).
$ bikescore-bna acquire <city> [OPTIONS]
| option | default | meaning |
|---|---|---|
--scenario, -s |
default |
scenario supplying the boundary transforms + extra_regions applied at acquire time |
--set k=v |
— | config override (repeatable), e.g. --set boundary.fill_holes=true |
--set-file |
— | YAML file of key: value overrides (merged under --set) |
--out-dir |
<city>/datasets |
where the content-addressed input files land |
--pbf-cache-dir |
$BIKESCORE_PBF_CACHE or ~/.bikescore-bna/pbf |
shared regional-PBF cache dir |
--force |
off | re-download the regional PBF even on a cache hit |
--dry-run |
off | report region coverage only — no OSM/census/LODES download |
--out-dir defaults to <city>/datasets/, the same place score/export read from,
so acquire <city> then score <city> works with no flags. Point it elsewhere to keep
several input sets side by side (see Working with multiple datasets).
See Data acquisition.
Any boundary transforms in the scenario / --set are
applied here, at acquire time, to produce the analysis_boundary input; with no transform
it equals the fetched boundary.
acquire --dry-run¶
When a boundary transform (or a non-zero network_buffer_m) can push the analysis extent
past the fetched region, acquire needs the neighbouring regions in extra_regions.
--dry-run fetches only the boundary and the Geofabrik index — no OSM/census/LODES
download — and prints the home / would-acquire / needed / missing regions so you can size
extra_regions before a full run:
$ bikescore-bna acquire ./washington-dc --set boundary.convex_hull=true --dry-run
field regions
home district-of-columbia
would acquire district-of-columbia
extent needs district-of-columbia, maryland, virginia
missing maryland, virginia
The extent needs regions not being acquired. Add them: --set extra_regions='[…]'
It exits non-zero (3) when regions are missing, so it doubles as a coverage guard in CI.
scenarios¶
List the bundled scenario names available to --scenario.
$ bikescore-bna scenarios
default
scenario show¶
Dump a bundled scenario's YAML so you can copy, edit, and feed it back via --scenario FILE.
Prints to stdout (redirect or pipe it), or use --out to write a file.
$ bikescore-bna scenario show default > my-scenario.yaml
# …edit my-scenario.yaml…
$ bikescore-bna score ./aspen-colorado --scenario my-scenario.yaml
| option | default | meaning |
|---|---|---|
--out, -o |
— | write the YAML to this file instead of stdout |
<name> may pin a version (e.g. default@1). A reusable scenario is one self-contained
YAML file; keep policy in the scenario and put per-run scalar tweaks in a separate
--set-file (or --set) so the scenario stays reusable.
export¶
Export a city's pipeline outputs to GeoJSON / Shapefile / CSV. Pass --from <run dir> to
reuse a prior score run's outputs without recomputing; otherwise the pipeline runs first,
persisting stage outputs under --workdir (default <city>/runs/<timestamp>). The
requested outputs are written under --out. See
Output files → Export for the target and bundle catalog.
$ bikescore-bna export <city> [OPTIONS]
| option | default | meaning |
|---|---|---|
--target, -t |
— | a single export target; requires --format |
--bundle, -b |
bna (if no --target) |
export a named bundle of targets |
--format, -f |
— | geojson | shapefile | csv (with --target) |
--out, -o |
./export |
destination directory |
--from |
— | reuse stage outputs from a prior score run dir (no recompute) |
--workdir |
<city>/runs/<timestamp> |
where to persist stage outputs when computing |
--scenario, -s |
default |
bundled scenario name or a scenario YAML path |
--set k=v |
— | config override (repeatable) |
--set-file |
— | YAML file of key: value overrides (merged under --set) |
--datasets |
<city>/datasets |
directory holding the raw inputs |
Pass either --target or --bundle, not both. With --from, the pipeline is not run —
outputs are read from the given run directory. Export the road-segment stress network as
GeoJSON:
$ bikescore-bna export ./aspen-colorado --target stress --format geojson --out ./gis
wrote gis/stress.geojson
1 file(s) → ./gis
…or the whole brokenspoke-analyzer deliverable set:
$ bikescore-bna export ./aspen-colorado --bundle bna --out ./results
export-list¶
List the exportable targets, their owner stage, supported formats, and the bundles that include each.
$ bikescore-bna export-list
validate¶
Score a city and compare each stage output against a reference directory (the
<stage>/<file>.parquet layout of a brokenspoke-analyzer export). Prints a per-stage
pass/fail table and exits non-zero if any stage differs.
$ bikescore-bna validate <city> --reference path/to/reference [--stage stress]
| option | default | meaning |
|---|---|---|
--reference, -r |
(required) | reference dir with <stage>/<file>.parquet |
--stage |
all | validate only this stage (faster partial run) |
--datasets |
<city>/datasets |
directory holding the raw inputs |
--scenario, -s / --set / --set-file |
default |
config, as for score |
--strict |
off | treat known SQL deviations as differences |
See Validation for the full workflow.
Working with multiple datasets¶
bikescore-bna is a stateless function of explicit inputs — it remembers nothing
between calls. A "dataset" is just a directory of the five role-named files (osm-*,
boundary-*, census-*, lodes_main-*, lodes_aux-*); there is no registry. So you
handle many datasets — or many cities — simply by looping the same commands, one
input directory at a time:
# same city, two input sets (e.g. this year's OSM vs a fresh re-pull)
$ bikescore-bna acquire ./aspen-colorado --out-dir ./inputs/2024
$ bikescore-bna acquire ./aspen-colorado --out-dir ./inputs/2025 --force
$ bikescore-bna score ./aspen-colorado --datasets ./inputs/2024 --out scores-2024.parquet
$ bikescore-bna score ./aspen-colorado --datasets ./inputs/2025 --out scores-2025.parquet
None of this needs any extra tooling: files are content-addressed (re-acquiring identical bytes is a no-op) and the regional-PBF cache is shared across directories (only the clip differs).
From Python the loop is just as direct. discover_inputs(dir) turns a directory into the
{role: Path} mapping score_city wants, so batching over cities is a plain for:
from bikescore_bna import build_config, discover_inputs, score_city
config = build_config("default")
for city_dir in ("./aspen-colorado", "./boulder-colorado", "./denver-colorado"):
result = score_city(discover_inputs(f"{city_dir}/datasets"), config)
...
Because the input is an explicit dict, you can also reuse most of one dataset and swap a single role — e.g. score against an alternate OSM extract without re-acquiring the rest:
from pathlib import Path
inputs = discover_inputs("./inputs/2024")
inputs["osm"] = Path("./inputs/2025/osm-abc123.pbf")
score_city(inputs, config)
What bikescore-bna deliberately does not do is track any of this: giving datasets
names and IDs, versioning them, deduping them as entities, recording provenance, or
comparing runs across them. That bookkeeping is a system-of-record concern, left to
whatever tool wraps the library; bikescore-bna itself stays a stateless function of explicit
inputs.