Pennsylvania tolls: how to maintain

Files: /srv/toll/draft/PA, last built 2026-10-09 11:14 ET · the PA toll page · map test · facts below are read from the files each time this page is built (python3 /home/geo1/system/handbuilt/maintain_pages.py PA)

Procedure for this state

Two layers. The corpus (/home/geo1/maps/toll/corpus/PA) carries the PTC 2026 E-ZPass and Toll By Plate schedules (Turnpike mainline and Northeast Extension, entry to exit) and the DRPA and DRJTBC bridge tolls; pa_state.py starts from it and adds positions, the point-priced PTC roads and the Dingmans Ferry Bridge.

When to check

The point-priced PTC roads (Turnpike 43, 576, 66, Toll 376)

  1. python3 /home/geo1/system/handbuilt/ptc_calc_fetch.py calls PTC's own toll calculator: every ordered interchange pair for a 2-axle car (which toll points a trip pays, which way), then each toll point for every class 2L..7H, both E-ZPass and Toll By Plate. It archives sources/ptc_toll_calculator/<date>/calls.jsonl and points.json. An interchange with no exit list in the calculator (M57) is skipped.
  2. pa_state.py links each located point to its PTC toll point name through PTCN. A new PTC toll point needs a new PTCN entry and a located point, or it shows as a gap.
  3. Check: the regression trips below are PTC calculator amounts, both directions, E-ZPass and Toll By Plate. After a rate change, take the new amounts from the calculator for those same trips.

Rebuild

Rates only: python3 pa_state.py. Points changed or a new OSM extract: the full chain. OSM box for PA/NJ: -80.6,38.9,-73.88,42.3 (panj). PA must also be rebuilt after NY (NY's congestion-zone non-toll gantries leave PA's untied list).

Watch out

The chain every state shares

Hosts: geo1 (192.168.50.220) holds the sources, builders, drafts, map and API; b100 (192.168.50.14) is the test client. All times are Eastern.

whatwhere (geo1 unless said)
sources list/home/geo1/maps/toll/PA/urls.txt (name <tab> URL)
archived sources/home/geo1/maps/toll/PA/sources/<name>/<YYYY-MM-DD>/ (file + meta.json: url, fetched, sha256)
builder/home/geo1/system/handbuilt/pa_state.py; shared code toll_common.py
OpenStreetMap inputs/home/geo1/system/handbuilt/osm/pa_* (cut from /home/geo1/maps/osm/us-latest.osm.pbf)
outputdraft /srv/toll/draft/PA/ (the builders write here); published /srv/toll/PA/ (copied from the draft by promote.py publish). The map and the API read the published page, or the draft while it is listed for review
toll enginesource /home/geo1/maps/web/tolls.js, served copy /srv/maps/tolls.js (the API runs the served copy)
API/home/geo1/maps/api/server.mjs on 127.0.0.1:8090 behind nginx /api/; pid in logs/test.pid
regression tripsb100: ~/apitest/site/cases.json, runner ~/apitest/run.py, browser check ~/apitest/browser.mjs

1. Refresh a source

  1. Fetch into a new dated folder: python3 /home/geo1/system/handbuilt/fetch_list.py PA [name ...] (no names = every line of urls.txt). A new source: add a line to urls.txt first, then fetch it by name.
  2. A site that refuses geo1 (HTTP 403) or scripts: fetch it on b100 in headless Chrome with node ~/apitest/fetch_b100.mjs <out> <name>=<url> ..., then rsync -a <out>/ geo1@192.168.50.220:/home/geo1/maps/toll/PA/sources/. Its meta.json records that it came from b100.
  3. Compare the new file with the previous dated one (diff, pdftotext -layout for PDFs). Same content: nothing to do. Changed amounts or a new effective date: rebuild (step 2) and update the expected amounts of the affected regression trips from the operator's own figures.

1b. The corpus (NJ, PA, VA start from it; DE is published from it)

python3 /home/geo1/maps/toll/bin/tollup.py PA update fetches the adapter's sources (bin/adapters/pa.py), stores a document only when its content changes, writes one rate file per version under /home/geo1/maps/toll/PA/rates/ and appends every price change to history.csv (every run to runs.log). TOLL_WEB=/home/geo1/maps/toll/corpus python3 /home/geo1/maps/toll/bin/publish.py PA writes the corpus /home/geo1/maps/toll/corpus/PA from it (DE: plain publish.py DE writes /srv/toll/DE). Never hand-edit either. Then rebuild the draft (step 2) so it picks up the new rates.

2. Rebuild

3. Load it into the API and test from b100

  1. The API reads the toll pages and tolls.js when it starts. Restart it: cd /home/geo1/maps/api && kill $(cat logs/test.pid); (nohup /home/geo1/maps/node/bin/node server.mjs > logs/server.log 2>&1 & echo $! > logs/test.pid) (it is started by hand, not a service).
  2. From b100 only: python3 ~/apitest/run.py ~/apitest/site/cases.json. Every trip must be OK. Add a trip for every rate or rule you change, with the amount taken from the operator's own source.
  3. Engine change (tolls.js): edit /home/geo1/maps/web/tolls.js, check with /home/geo1/maps/node/bin/node --check, copy to /srv/maps/tolls.js with sudo install -o www-data -g www-data -m 644, restart the API, run the trips.

4. OpenStreetMap refresh (all states at once)

The builders match toll points to the OSM way ids the router drives, so the extracts here must come from the same us-latest.osm.pbf the router (Valhalla, /home/geo1/maps/valhalla_data) was built from (now 2026-09-27). New OSM data means: rebuild the router tiles, then for each state cut the box, rebuild its OSM inputs and run the full chain, then compare untied_toll_nodes.csv and the estimated positions below. The box for PA and the cutting commands are in the state's procedure above; the toll-node and way index comes from python3 tollnode_ways.py PA.

5. Publish

A rebuild writes the draft (/srv/toll/draft/PA); the published page (/srv/toll/PA) does not change. python3 /home/geo1/maps/toll/bin/promote.py review PA lists the draft for review: the map and the API then read it instead of the published page. Run the regression trips from b100. After approval, python3 /home/geo1/maps/toll/bin/promote.py publish PA copies the draft to /srv/toll/PA (the previous page is kept under /home/geo1/maps/toll/PA/published/<time>/) and takes it off the review list. After a change, update the coverage line for PA in the API page (/api.html, generator /home/geo1/maps/api/doc/api_page.py) and rebuild this page: python3 /home/geo1/system/handbuilt/maintain_pages.py PA.

Sources (9)

Each fetch is kept in its own dated folder under /home/geo1/maps/toll/PA/sources/<name>/; the builder always reads the newest one, so a new fetch takes effect at the next rebuild and the old one stays for comparison.

sourcefetched (newest)fetches kepthowlisted in urls.txtrows using it
dcdbc_rates
https://dcdbc.com/ratesandrestr.php
2026-10-091fetch_list.pyyes5
drjtbc_2026_hearings_release
https://www.drjtbc.org/2025/09/bridge-commission-schedules-three-remote-hearings-on-propos
2026-10-031fetch_list.pyno
drjtbc_current_tolls
https://www.drjtbc.org/currenttolls
2026-10-031fetch_list.pyno136
drpa_toll_schedule
https://www.drpa.org/travel/toll-schedule.html
2026-10-031fetch_list.pyno108
ptc_ezpass_schedule
https://files.paturnpike.com/production/docs/default-source/resources/tolls/toll-schedule-
2026-10-031fetch_list.pyno20120
ptc_mileposts
https://services.arcgis.com/FohZlMMG15EeWv3q/arcgis/rest/services/PTC_Mileposts/FeatureSer
2026-10-031fetch_list.pyno
ptc_toll_by_plate_schedule
https://files.paturnpike.com/production/docs/default-source/resources/tolls/toll-schedule-
2026-10-031fetch_list.pyno20119
ptc_toll_calculator
https://www.paturnpike.com/toll-calculator (getenteringinterchanges, getexitinginterchange
2026-10-091fetch_list.pyno290
ptc_toll_plazas
https://services.arcgis.com/FohZlMMG15EeWv3q/arcgis/rest/services/ExitAndExplore_Prod/Feat
2026-10-031fetch_list.pyno

Facilities (18)

idfacilityoperatorpointsrate / trip rowseffective dates in the rowssource
PA-PTCPA TurnpikePennsylvania Turnpike Commission (PTC)113402382026-01-04ptc_ezpass_schedule
PA-DRPA-BFBBenjamin Franklin BridgeDelaware River Port Authority (DRPA)1262024-09-01drpa_toll_schedule
PA-DRPA-WWBWalt Whitman BridgeDelaware River Port Authority (DRPA)1262024-09-01drpa_toll_schedule
PA-DRPA-CBBCommodore Barry BridgeDelaware River Port Authority (DRPA)1262024-09-01drpa_toll_schedule
PA-DRPA-BRBBetsy Ross BridgeDelaware River Port Authority (DRPA)1262024-09-01drpa_toll_schedule
PA-DRJTBC-TMTrenton-Morrisville Toll Bridge (Route 1)Delaware River Joint Toll Bridge Commission (DRJTBC)1162026-01-01drjtbc_current_tolls
PA-DRJTBC-SFScudder Falls Toll Bridge (I-295)Delaware River Joint Toll Bridge Commission (DRJTBC)1162026-01-01drjtbc_current_tolls
PA-DRJTBC-NHNew Hope-Lambertville Toll Bridge (Route 202)Delaware River Joint Toll Bridge Commission (DRJTBC)1162026-01-01drjtbc_current_tolls
PA-DRJTBC-I78Interstate 78 Toll BridgeDelaware River Joint Toll Bridge Commission (DRJTBC)1162026-01-01drjtbc_current_tolls
PA-DRJTBC-EPEaston-Phillipsburg Toll Bridge (Route 22)Delaware River Joint Toll Bridge Commission (DRJTBC)1162026-01-01drjtbc_current_tolls
PA-DRJTBC-PCPortland-Columbia Toll Bridge (Routes 611, 46, and 94)Delaware River Joint Toll Bridge Commission (DRJTBC)1162026-01-01drjtbc_current_tolls
PA-DRJTBC-DWGDelaware Water Gap Toll Bridge (I-80)Delaware River Joint Toll Bridge Commission (DRJTBC)1162026-01-01drjtbc_current_tolls
PA-DRJTBC-MMMilford-Montague Toll Bridge (Route 206)Delaware River Joint Toll Bridge Commission (DRJTBC)1162026-01-01drjtbc_current_tolls
PA-DFBDingmans Ferry BridgeDingman's Choice and Delaware Bridge Company (private)142022-07-01dcdbc_rates
PA-PTC-MFMon/Fayette ExpresswayPennsylvania Turnpike Commission (PTC)121202026-01-04ptc_toll_calculator
PA-PTC-SBSouthern BeltwayPennsylvania Turnpike Commission (PTC)3662026-01-04ptc_toll_calculator
PA-PTC-AKHAmos K. Hutchinson BypassPennsylvania Turnpike Commission (PTC)5502026-01-04ptc_toll_calculator
PA-PTC-BVEBeaver Valley ExpresswayPennsylvania Turnpike Commission (PTC)5502026-01-04ptc_toll_calculator

Positions placed by estimate (0): re-check when OpenStreetMap or the operator changes

None: every position comes from an official layer or a mapped toll node.

Decisions by rule

R1: 45, R2a: 25, R2b: 38, R2d: 68, R3: 1, direction: 1 (all in decisions.csv).

Open gaps (0)

None.

Untied OSM toll nodes: 224

Toll booths / gantries mapped in OpenStreetMap on or near this state's roads that no toll point claims; a route through one lists it under unpriced. After an OSM refresh, look at new ones here (untied_toll_nodes.csv): a new gantry on a priced road means a point is missing.

Regression trips for PA (18)

Run from b100 (never on geo1): python3 ~/apitest/run.py ~/apitest/site/cases.json. Every trip must come back OK; a changed amount is either a rate change (update the expected amount from the operator's own source) or a fault.

tripexpected
PA MF exit 2 -> exit 36 (E-ZPass, PTC calculator $7.34)$7.34
PA MF exit 2 -> exit 36 (Toll By Plate, PTC calculator $16.4)$16.40
PA MF exit 36 -> exit 2 (E-ZPass, PTC calculator $7.34)$7.34
PA MF exit 36 -> exit 2 (Toll By Plate, PTC calculator $16.4)$16.40
PA SB exit 2 -> exit 16 (E-ZPass, PTC calculator $4.95)$4.95
PA SB exit 2 -> exit 16 (Toll By Plate, PTC calculator $9.84)$9.84
PA SB exit 16 -> exit 2 (E-ZPass, PTC calculator $4.95)$4.95
PA SB exit 16 -> exit 2 (Toll By Plate, PTC calculator $9.84)$9.84
PA AKH exit 1 -> exit 12 (E-ZPass, PTC calculator $3.18)$3.18
PA AKH exit 1 -> exit 12 (Toll By Plate, PTC calculator $6.36)$6.36
PA AKH exit 12 -> exit 1 (E-ZPass, PTC calculator $3.18)$3.18
PA AKH exit 12 -> exit 1 (Toll By Plate, PTC calculator $6.36)$6.36
PA BVE exit 15 -> exit 31 (E-ZPass, PTC calculator $4.83)$4.83
PA BVE exit 15 -> exit 31 (Toll By Plate, PTC calculator $9.66)$9.66
PA BVE exit 31 -> exit 15 (E-ZPass, PTC calculator $4.83)$4.83
PA BVE exit 31 -> exit 15 (Toll By Plate, PTC calculator $9.66)$9.66
PA Dingmans Ferry Bridge PA->NJ, cash (operator $2.00)$2.00
PA Dingmans Ferry Bridge NJ->PA, cash (operator $2.00, direction not stated)$2.00