Skip to main content

crashmap

Road-safety network screening in Python. crashmap ranks road segments, sliding windows and intersections by crash risk with the methods of the Highway Safety Manual (AASHTO 2010, Part B, Chapter 4), fits negative binomial safety performance functions (SPFs), computes Empirical Bayes (EB) estimates, evaluates treatments with before-after studies and finds crash hotspots.

It is written for road-safety engineers and analysts who identify high-risk sites (black spots) from crash records. The core needs only NumPy; SciPy, pandas and GeoPandas are optional.

pip install crashmap            # NumPy only
pip install "crashmap[all]"     # + SciPy, pandas, GeoPandas/Shapely

Quick start

import crashmap as cm

crashes = cm.read_crashes_csv("crashes.csv")           # x, y, severity, date ...
roads = cm.read_roads_geojson("roads.geojson")         # LineStrings with an "aadt" attribute

segments = cm.fixed_length_segments(roads, 500)        # 500 m sections
snap = cm.snap_to_lines(crashes, segments, max_distance=25)
print(snap.summary())                                  # good / fair / ambiguous / unmatched

sites = cm.build_segment_sites(segments, crashes, snap, years=5)
spf = cm.load_spf("spf.toml")                          # or cm.SPF.segment(a, b, k=...)
result = cm.screen(sites, "eb", spf=spf)

for row in result.top(10):                             # ties at the cut are kept
    print(row["rank"], row["id"], round(row["score"], 2), row["expected"])

cm.write_geojson(result, "ranked.geojson")
cm.write_report(result, "screening.html")

Coordinates must be planar (metres in a projected CRS). The CLI detects longitude/latitude input and projects it to a local metric frame (crashmap.LocalProjection); output GeoJSON is written back in lon/lat.

Command line

crashmap screen crashes.csv roads.geojson --method eb --spf spf.toml -o ranked.geojson --report report.html
crashmap screen crashes.csv roads.geojson --method critical-rate --segment-length 500
crashmap screen crashes.csv roads.geojson --site-type window --window 480 --step 160
crashmap screen crashes.csv roads.geojson --site-type intersection --max-distance 76 --method rate
crashmap fit-spf sites.csv --years 5 --length-unit km -o spf.toml --cure cure.csv
crashmap before-after treated.csv --method eb --k 0.4 -o result.json

crashmap <command> --help lists every option (column names, severity scale, --dayfirst dates, EPDO weights, confidence level, ...).

Methods

Method (screen(method=...)) Score (higher = more promising) Needs Reference
frequency Average crash frequency, crashes/year crashes HSM Ch. 4
rate Crashes per million vehicle-km (or -mi); per million entering vehicles at intersections AADT, length / entering volume HSM Ch. 4
critical-rate Observed minus critical rate, R_c = R_a + P sqrt(R_a/E) + 1/(2E) AADT, length / entering volume HSM Ch. 4
epdo EPDO average crash frequency, sum(w_s N_s)/years; default weights K 542, A/B/C 11, O 1 severity HSM Ch. 4
rsi Relative severity index: average crash cost (by severity or crash type) severity or type, costs HSM Ch. 4
excess-predicted (N_obs - N_pred)/years SPF HSM Ch. 4
eb Excess expected frequency (N_exp - N_pred)/years, N_exp = w N_pred + (1-w) N_obs, w = 1/(1 + k sum N_pred) SPF with k HSM Ch. 4; Part C App. A
proportion Probability that the share of a crash type exceeds the threshold proportion (Beta-binomial) crash type or severity HSM Ch. 4

Other building blocks:

Area Functions
Crash data read_crashes_csv, read_crashes_geojson, crashes_from_dataframe, CrashSet (KABCO severities, dates, crash types), normalize_severity (KABCO, fatal/serious/slight, labels, 1..5 codes), parse_datetime
Roads and sites read_roads_geojson, roads_from_geodataframe, fixed_length_segments, homogeneous_segments, sliding_windows, extract_intersections, build_segment_sites, build_window_sites, build_intersection_sites
Snapping snap_to_lines, snap_to_points with max_distance and quality flags good / fair / ambiguous / unmatched
SPFs SPF.segment (N = L exp(a + b ln AADT)), SPF.intersection (N = exp(a + b ln AADT_maj + c ln AADT_min)), custom forms, k constant or k/L, TOML/JSON I/O, fit_spf, fit_nb2 (pure-NumPy NB2 maximum likelihood), cure_plot
Before-after naive_before_after, comparison_group_before_after, eb_before_after -> CMF, standard error, confidence interval (Hauer 1997)
Spatial kde_grid / kde_at (planar KDE), network_kde (built-in simple NKDE or an optional nkde backend), getis_ord_gi_star with distance-band or touching weights
Ranking and output rank (min/max/dense/ordinal/average ties), top_n, to_markdown, to_html (escaped), ranked_geojson, write_geojson
Synthetic data synthetic_network, synthetic_sites, simulate_nb_counts -- crashes drawn from a known SPF

SPF files

[spf]
name = "Rural two-lane segments (HSM base model)"
form = "segment"          # N = L * exp(a + b ln AADT), crashes per year
a = -8.2276               # ln(365e-6) - 0.312
b = 1.0
k = 0.236
k_form = "per_length"     # k_i = k / L_i
length_unit = "mi"

form = "intersection" takes a, b, c; form = "custom" takes an intercept, an exposure variable and [[spf.terms]] tables with variable, coefficient and transform (ln or linear). fit_spf(...).spf.save("spf.toml") writes the same layout. SPFs must be calibrated to local conditions before use.

Validation

The test suite (98 % line coverage) checks the implementation against independent results; all data are synthetic.

  • NB2 maximum likelihood vs statsmodels. On a synthetic 80-site data set (tests/data/nb2_reference.csv), fit_nb2 reproduces statsmodels 0.15.0 NegativeBinomial(loglike_method="nb2") to 1e-12: coefficients -5.387829 and 0.673613, alpha 0.282714, standard errors 1.508463, 0.157971 and 0.085230, log-likelihood -186.414967.
  • Parameter recovery. Crashes simulated from N = L exp(-6.2 + 0.75 ln AADT) with k = 0.5 (3,000 sites) are fitted back within four standard errors; CURE residuals stay inside the +/-2 sigma band.
  • Hand calculations. EB weights and expected crashes (including the HSM rural two-lane base model with k = 0.236/L), critical rates and the HSM P values (1.036/1.282/1.645/2.326), crash rates, EPDO and RSI, the threshold-proportion Beta prior, and Hauer's naive, comparison-group and EB before-after formulas.
  • Known truth. On a synthetic grid where two roads carry four times the SPF risk, eb and excess-predicted rank exactly those two roads first; EB estimates have lower error than raw counts; a simulated treatment with CMF 0.70 applied to high-count sites is recovered by the EB before-after study (0.703 +/- 0.008) while the naive study overstates the effect (0.669).
  • Ranking edge cases. All tie rules, nan scores, empty input, and top-N cuts through ties.

Limitations

  • Planar geometry only; use a projected CRS (or the local projection, adequate for city- and corridor-scale studies).
  • Roads must be noded (split at junctions) for intersection extraction and network KDE. Crashes are snapped to the geometrically nearest site; junction crashes are flagged ambiguous but not reassigned.
  • SPFs are user supplied or fitted with a constant overdispersion. k_form="per_length" in fit_spf is an approximation (alpha scaled at the mean length), not a length-dependent dispersion model. No calibration factor (C) is estimated.
  • The EB screening uses totals over the study period without yearly calibration factors (HSM C_y); proportion requires extra-binomial variation between sites and raises an error otherwise.
  • Before-after standard errors use Hauer's large-sample approximations; the comparison-group method assumes a Poisson comparison ratio unless its variance is given. Confidence intervals are normal approximations.
  • The built-in network KDE is the "simple" kernel of Xie & Yan (2008), which inflates densities at junctions with three or more legs. The optional nkde backend (or any callable) can replace it.
  • Getis-Ord p-values are not corrected for multiple testing.
  • Screening ranks sites for diagnosis; it does not establish causes or countermeasures. Crash records are personal data: keep them out of public repositories.

References

  • AASHTO (2010). Highway Safety Manual, 1st ed. American Association of State Highway and Transportation Officials, Washington, DC. Part B, Chapter 4 (network screening); Part C, Appendix A (EB method).
  • Hauer, E. (1997). Observational Before-After Studies in Road Safety. Pergamon, Oxford.
  • Hauer, E., Harwood, D. W., Council, F. M., Griffith, M. S. (2002). Estimating safety by the empirical Bayes method: a tutorial. Transportation Research Record 1784, 126-131.
  • Persaud, B., Lyon, C. (2007). Empirical Bayes before-after safety studies: lessons learned from two decades of experience and future directions. Accident Analysis & Prevention 39(3), 546-555.
  • Hauer, E., Bamfo, J. (1997). Two tools for finding what function links the dependent variable to the explanatory variables. Proceedings of the ICTCT 1997 Conference, Lund.
  • Cameron, A. C., Trivedi, P. K. (2013). Regression Analysis of Count Data, 2nd ed. Cambridge University Press.
  • Ord, J. K., Getis, A. (1995). Local spatial autocorrelation statistics: distributional issues and an application. Geographical Analysis 27(4), 286-306.
  • Xie, Z., Yan, J. (2008). Kernel density estimation of traffic accidents in a network space. Computers, Environment and Urban Systems 32(5), 396-406.

Citation

See CITATION.cff. MIT licensed, (c) 2026 Yusuf Eminoğlu.

Metadata

Release files for crashmap 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for crashmap 0.1.1
File Size Uploaded Provenance Actions
crashmap-0.1.1.tar.gz 84.8 kB With provenance Details for crashmap-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for crashmap 0.1.1
File Interpreter ABI Platform Actions
crashmap-0.1.1-py3-none-any.whl Python 3 none any Details for crashmap-0.1.1-py3-none-any.whl

Total release size: 151.7 kB

Release files / crashmap-0.1.1.tar.gz

Download URL crashmap-0.1.1.tar.gz
Size 84.8 kB
Tags Source
SHA-256 checksum
How to use checksums
ef0dbea479c51ba979966c9f3f0e2f6fc65da898603b0054ade69df2e12809f2
BLAKE2b-256 checksum
How to use checksums
dfe652404b33120efa0a09c269148c339e19578a05a3b35866bc006646dfafef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Release files / crashmap-0.1.1-py3-none-any.whl

Download URL crashmap-0.1.1-py3-none-any.whl
Size 66.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42ab01fa92cb3dffbc44e98730f719c0919b69b4e02b352fd0b1d69662e6d4d8
BLAKE2b-256 checksum
How to use checksums
8b26d8bc58b8944039c7d5bf2631d59f7a8f56aa6eefbebf7b293cd94c98899d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

Security for crashmap 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for crashmap-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

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