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.
python3 /home/geo1/maps/toll/bin/tollup.py PA update, TOLL_WEB=/home/geo1/maps/toll/corpus python3 /home/geo1/maps/toll/bin/publish.py PA, then re-run the PTC calculator (below) and rebuild.drjtbc_2026_hearings_release); they come in through the corpus.dcdbc_rates (cash, $2.00; direction not stated, charged each crossing): python3 /home/geo1/system/handbuilt/fetch_list.py PA dcdbc_rates.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.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.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).
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.
| what | where (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) |
| output | draft /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 engine | source /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 trips | b100: ~/apitest/site/cases.json, runner ~/apitest/run.py, browser check ~/apitest/browser.mjs |
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.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.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.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.
cd /home/geo1/system/handbuilt && python3 pa_state.pybash /home/geo1/system/handbuilt/chain.sh PA (logs in /tmp/pa1.log ... pa6.log; it prints the points, any point on no road and the untied count). The builders written since October 2026 share state_kit.py (sources, booths, terms, rows, write) and own_state.py (keeps a state's untied list to toll nodes inside the state, by the local geocoder; cache osm/node_state.json).cd /home/geo1/system/handbuilt; H=$(ls osm/pa_highways_*.geojsonseq); J=$(ls osm/pa_junctions_*.geojsonseq)
python3 pa_state.py && python3 osm_streets.py PA $H $J && python3 pa_state.py && python3 osm_snap.py PA && python3 toll_ways.py PA && python3 pa_state.pytoll_ways.py's output: nodes claimed by two points: {} and points on no road: [] must both be empty.OSM way <id>) needs that way in osm/pa_lane_ways.json (the builder writes it). A point on a road priced entry to exit needs gantry in its location name, or toll_ways.py treats it as a position-only exit marker.osm/ny_not_toll_nodes.json (congestion-zone gantries that are not tolls); every state's untied list leaves those out, so rebuild NJ and PA after NY.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).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.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.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.
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.
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.
| id | facility | operator | points | rate / trip rows | effective dates in the rows | source |
|---|---|---|---|---|---|---|
PA-PTC | PA Turnpike | Pennsylvania Turnpike Commission (PTC) | 113 | 40238 | 2026-01-04 | ptc_ezpass_schedule |
PA-DRPA-BFB | Benjamin Franklin Bridge | Delaware River Port Authority (DRPA) | 1 | 26 | 2024-09-01 | drpa_toll_schedule |
PA-DRPA-WWB | Walt Whitman Bridge | Delaware River Port Authority (DRPA) | 1 | 26 | 2024-09-01 | drpa_toll_schedule |
PA-DRPA-CBB | Commodore Barry Bridge | Delaware River Port Authority (DRPA) | 1 | 26 | 2024-09-01 | drpa_toll_schedule |
PA-DRPA-BRB | Betsy Ross Bridge | Delaware River Port Authority (DRPA) | 1 | 26 | 2024-09-01 | drpa_toll_schedule |
PA-DRJTBC-TM | Trenton-Morrisville Toll Bridge (Route 1) | Delaware River Joint Toll Bridge Commission (DRJTBC) | 1 | 16 | 2026-01-01 | drjtbc_current_tolls |
PA-DRJTBC-SF | Scudder Falls Toll Bridge (I-295) | Delaware River Joint Toll Bridge Commission (DRJTBC) | 1 | 16 | 2026-01-01 | drjtbc_current_tolls |
PA-DRJTBC-NH | New Hope-Lambertville Toll Bridge (Route 202) | Delaware River Joint Toll Bridge Commission (DRJTBC) | 1 | 16 | 2026-01-01 | drjtbc_current_tolls |
PA-DRJTBC-I78 | Interstate 78 Toll Bridge | Delaware River Joint Toll Bridge Commission (DRJTBC) | 1 | 16 | 2026-01-01 | drjtbc_current_tolls |
PA-DRJTBC-EP | Easton-Phillipsburg Toll Bridge (Route 22) | Delaware River Joint Toll Bridge Commission (DRJTBC) | 1 | 16 | 2026-01-01 | drjtbc_current_tolls |
PA-DRJTBC-PC | Portland-Columbia Toll Bridge (Routes 611, 46, and 94) | Delaware River Joint Toll Bridge Commission (DRJTBC) | 1 | 16 | 2026-01-01 | drjtbc_current_tolls |
PA-DRJTBC-DWG | Delaware Water Gap Toll Bridge (I-80) | Delaware River Joint Toll Bridge Commission (DRJTBC) | 1 | 16 | 2026-01-01 | drjtbc_current_tolls |
PA-DRJTBC-MM | Milford-Montague Toll Bridge (Route 206) | Delaware River Joint Toll Bridge Commission (DRJTBC) | 1 | 16 | 2026-01-01 | drjtbc_current_tolls |
PA-DFB | Dingmans Ferry Bridge | Dingman's Choice and Delaware Bridge Company (private) | 1 | 4 | 2022-07-01 | dcdbc_rates |
PA-PTC-MF | Mon/Fayette Expressway | Pennsylvania Turnpike Commission (PTC) | 12 | 120 | 2026-01-04 | ptc_toll_calculator |
PA-PTC-SB | Southern Beltway | Pennsylvania Turnpike Commission (PTC) | 3 | 66 | 2026-01-04 | ptc_toll_calculator |
PA-PTC-AKH | Amos K. Hutchinson Bypass | Pennsylvania Turnpike Commission (PTC) | 5 | 50 | 2026-01-04 | ptc_toll_calculator |
PA-PTC-BVE | Beaver Valley Expressway | Pennsylvania Turnpike Commission (PTC) | 5 | 50 | 2026-01-04 | ptc_toll_calculator |
None: every position comes from an official layer or a mapped toll node.
R1: 45, R2a: 25, R2b: 38, R2d: 68, R3: 1, direction: 1 (all in decisions.csv).
None.
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.
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.
| trip | expected |
|---|---|
| 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 |