Case Study – Matching Tow Calls to Public Crash Data
Summary
A Mason County towing company needed to know which of its tow calls lined up with official crash reports. Tahuya River Software built a nightly pipeline that pulls public Washington State Patrol collision data, matches each crash to the company's dispatch records, and publishes an interactive map. It runs unattended in production, matching roughly 1 in 10 county crashes to a tow call, and places rural highway addresses to within about 50 feet where commercial geocoding missed by more than a mile.
The problem
A tow company's dispatch system knows when and where it was called. The state knows which crashes happened and which agency worked them. The two never meet, and connecting them by hand means reading report after report.
Tying them together answers practical questions:
- Which crashes in our area did we respond to, and which went to someone else?
- Which law enforcement agency called us for each one?
- Where did our recorded tow address and the official crash location disagree?
The catch is that the two sources describe the same event differently. Dispatch addresses are typed fast by people on the phone. Crash locations come from officers' reports. Times are close but never identical, and one crash can produce two or three tow calls.
How it works
- Fetch & mergeNightly, both exports pulled automatically and merged into a running history
- GeocodeTow addresses only (crash reports already have coordinates), with a fallback for rural routes
- MatchCrash to tow call, within 3 hours, weighted by distance
- PublishInteractive map and exception lists
A crash and a tow call are only compared if they're within 3 hours of each other. Distance carries the most weight, so a nearby call beats one that merely shares a road name, and each tow call can be claimed by only one crash.
The hard part: rural addresses
The map showed pins stacked on top of each other. One point on State Route 106 held 10 different tow addresses whose house numbers spanned more than six miles of highway. Across the map, about 1 in 5 geocoded tow addresses shared a point with a different address.
The cause: the commercial geocoder has no house numbers for rural state routes like SR-106, SR-300 and SR-3. Asked for a specific address, it quietly returned a point somewhere on the road and labeled the result an address. The pipeline had been trusting that point.
The fix
- Read the geocoder's own accuracy flag instead of the result type, so road-only answers are recognized as such.
- Fall back to the US Census Bureau geocoder for road-only results. Census interpolates house numbers along the road segment. For one test address it landed about 50 feet from the true location, where the commercial result was 1.2 miles off.
- Reject wrong-number matches. The geocoder sometimes returned a confident point for a different house number on a different highway. A result now has to carry the house number that was asked for.
- Handle intersections properly. Sending "A and B" instead of "A & B" got real intersection points back.
- Mark what's still approximate. Addresses that can't be pinned stay off the matching math, fan out instead of stacking, and are labeled on the map.
After the fix, no two different addresses share a point, and the largest stack went from 10 pins to a single duplicate entry of one store.
Reliability
The job runs once a night as a scheduled container. It's built to keep working when nobody is watching:
- Catch-up after gaps. Each source keeps a watermark of its last good run. After a missed night or a failed login, the next run widens its lookback automatically: up to 60 days for crash data, 14 for dispatch. A fixed window would silently drop whatever fell outside it.
- Late-arriving reports. State crash data lags by days, so every run re-reads the last two weeks and merges changes into a running history.
- Monitoring. Each run checks in with an error-tracking service. A missed or stuck run raises an alert.
- Safe writes. Data files are written atomically, so a crash mid-run never leaves a half-written file behind.
- Evidence before changes. Every matching change is tested against a saved before-and-after snapshot of the results, so a fix can't quietly break other matches.
Stack and privacy
| Layer | Tools |
|---|---|
| Runtime | Node.js, run as a nightly cron container on Railway |
| Data collection | Playwright for the WSP Collision Analysis Tool export and the dispatch system |
| Geocoding | Mapbox, with US Census Bureau fallback for rural addresses |
| Parsing | csv-parse, SheetJS |
| Map | Self-contained HTML map, published to a token-protected page |
| Monitoring | Sentry error tracking and cron check-ins |
Privacy. Crash data comes from the public WSP Collision Analysis Tool. This project is not affiliated with or endorsed by the Washington State Patrol. The company's dispatch records stay private: the published map sits behind a token, and the public demo version uses synthetic dispatch records generated from public crash data. Only street addresses, never names, are sent to geocoding services.