ProjectSidewalk / ProjectSidewalk/RampNet

Review location precision against the STREET-LEVEL imagery Stage 1 actually consumes, not an aerial proxy (#96)

Open
#103 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

documentation
Dominant language
Python
Stars
7
Forks
1
Avg merge
4d 11h
Merged PRs (30d)
7

Description

Why

#96 §5m established that supply is not the constraint — the ArcGIS Hub synonym sweep found 1,972,275 candidate records across 65 publishers (a floor; every query hit the page cap), against a 500k target. DVRPC's Philadelphia-region layer alone is 295,389, larger than the entire current training corpus.

So the binding constraint is assessment throughput. Today that costs, per city:

  1. a municipal basemap hunt — which has failed in four distinct ways so far (§5e Esri grey tiles; §5h King County leaf-on canopy; §5h Charlotte 404ing ~25–35% of tiles that exist; and probe_basemap.py checking one downtown point while the sheet samples citywide),
  2. a 60-chip sheet build,
  3. an hour of the only reviewer we have,
  4. …who then discarded 41.7% of Seattle to tree canopy (§5l).

At that rate the queue above is decades of work. The next investment belongs in the instrument.

The proposal (Jon's)

Review against the street-level imagery Stage 1 actually consumes: show the panorama, the projected crop Stage 1 would cut, and the government point's projected bearing — and have the reviewer judge whether the ramp falls inside that crop.

Why this is better than the aerial sheet, beyond convenience
  1. It measures the actual gate quantity. §5g proved Stage 1 consumes the published coordinate only for its bearing, and cuts a ±18.37° strip around it. The aerial sheet measures a metric offset — a proxy — which then has to be pushed through a Monte Carlo to become a decision. This has the reviewer answer the real question directly.
  2. It eliminates the basemap problem. No municipal basemap, so all four failure modes above go away at once. GSV coverage is uniform and global.
  3. It looks under the canopy. Seattle's 41.7% unjudgeable was tree cover in plan view. Street level is beneath the trees.
  4. Temporal matching becomes per-record. GSV has many panos per location, each with a capture date, so you select one captured after the ramp's install date. The confound that dominated §5i/§5l is eliminated, not mitigated.

What already exists — this is mostly assembly

The rendering path is the production path, not a reimplementation:

need where
find panos near a lat/lng, with dates stage_one/dataset_generation/search_panos.py
assemble the equirectangular pano download_dataset.py::fetch_panorama(pano_id)
pano heading download_dataset.py::heading_to_azimuth(get_pano_heading(...))
the exact crop Stage 1 cuts equirectangular_to_perspective(equi, 90, azimuth, -30, 1024, 1024)[0:1024, 341:341+341]
the ±18.37° half-angle scripts/analysis/stage1_offset_tolerance.py::crop_half_angle_deg()import, never re-derive
frozen inventories + date parsing data/inventories/, temporal_gap.py::parse_ym (carries the §5c sentinel handling)
sheet scaffold: sampling, strata, localStorage, export scripts/analysis/inventory_review_sheet.py

Design decisions already taken

  • Show the full 90° perspective with the strip boundaries overlaid, not the bare 341-px strip. The reviewer must be able to distinguish "ramp just outside the crop" from "no ramp here" — that distinction is the measurement.
  • Record the verdict as an ANGULAR offset. This puts human review into the same units as §5j's automatic bearing residual (stage1_bearing_residual.py), whose null over 90,006 published records is |mean| ≤ 0.25°, |median| ~3.3°. §5j measures cities already in the corpus; this measures candidates; same units, so they cross-validate.
  • Reuse the strata sampler (--strata-year-field), because §5l showed the dated/undated split is city-specific and must be earned per city.

Calibration plan — do this first

Pilot on Denver. It is the one city with a trusted aerial answer (§5f: median 0.29 m, 92.3% within 1 m, and §5g says its coordinate error costs Stage 1 0.21%). If the street-level tool independently returns "Denver is Good", the instrument is calibrated and can be trusted where aerial fails. If it disagrees, we learn that before betting the queue on it.

Second pilot: Seattle, where the aerial instrument demonstrably struggled (41.7% unjudgeable, MEDIUM confidence). If the street-level unjudgeable rate collapses there, that is the proof the tool is worth its build cost.

Risks, stated up front

  • GSV endpoints are undocumented (search_panos.py and the tile fetch in download_dataset.py talk directly to them) and can break without notice. This is a real dependency risk that the aerial path does not have.
  • ~1,900 tile requests per 60-chip sheet, and panorama assembly is slower than tile fetch — expect 10–20 min per build. Rate limiting is the dominant unknown, same as §7's Stage 1 generation concern.
  • Angular offsets are not metric. This tool cannot produce a number comparable to Denver's 0.29 m or the paper's Table 1. That is acceptable — angular is what Stage 1 cares about — but it must be stated, and it means the aerial sheet is not fully retired.
  • Different selection bias, not none: parked vans, poles and low sun replace tree canopy. Probably smaller, but it must be measured, not assumed.
  • Sheet weight. 60 panoramas is a lot of pixels; the aerial sheet embeds tiles as data URIs and already reaches 3–5 MB. Will need downsampled context views plus full-res strips.

Lessons from the aerial sheet that must not be re-earned

  • Two-path bugs. Twice now a fix landed on one of two paths that must agree: §5h's retry logic was inert because the cache stored absences as zero-byte files; §5l's stratum was added to the verdicts template but not the chip dict the export rebuilds from, so every exported stratum came back null. When adding a field, find the second path.
  • Probe at the sample sites, not one point (probe_basemap_at_sites.py). The street-level analogue: check pano availability and date coverage at the actual sampled records before building.
  • A drop count is a claim about the fetcher until checked against the sample. Charlotte's no_imagery_dropped: 59 was reported as "outside the basemap footprint" and was false.
  • Null date sentinels — Boston's 18991230, the old 2000-01 — must go through parse_ym, never a fresh parser.
  • State what the instrument cannot measure, next to the numbers (§5e's recall note is the template).

Scope for v1

Denver, 60 records, context view + strip overlay + projected crosshair, angular verdict, reusing the production projection path and the existing sheet scaffold. Everything else is follow-on.

🤖 Generated with Claude Code (claude-opus-5[1m])

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with stage_one/dataset_generation/search_panos.py and download_dataset.py, then trace the projection call and import crop_half_angle_deg() from scripts/analysis/stage1_offset_tolerance.py. Use scripts/analysis/inventory_review_sheet.py and the existing inventory/date helpers to assemble the Denver 60-record pilot. Done means the sheet shows the context view, strip boundaries, projected crosshair, and records angular verdicts for calibration against Denver's trusted aerial result.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
computer-vision, data, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.