Random-cell manual validation of roofclf (JOSM)¶
Audience: anyone reviewing model output in JOSM, and anyone deciding whether to
trust a roofclf capacity number.
Status: added 2026-08-06, alongside --random-cells in
scripts/tile_roofclf_detections_geojson.py. First batch (20 cells, seed 1) is in
results/pakistan_roofclf_validation/.
Why this is essential, not optional¶
The main workflow's < 400 m² half is roofclf, a
classifier fit on the current set of hand-mapped calibration quadrats. Those quadrats are the only
ground truth this project has, but they are also curated: a mapper picked each one,
and the set leans industrial/urban because that is where mapping effort went first (see
Calibration quadrats overview). A model that scores well only
on the ground its own evaluation set was drawn from is not evidence that it works
everywhere the evidence atlas reports a number for -- it is evidence that it works
there.
Random national cells are the deliberately un-curated counterpart. Nobody chose Cell
0116_0107 because it looked interesting; it was one of 4,470 scored cells with an equal
chance of being drawn. A roofclf that holds up on an unpicked sample is a much stronger
claim than one that holds up on 17 hand-picked boxes, and a roofclf that does not is
exactly the failure mode the quadrats cannot see by construction.
This is why it belongs in the full pipeline as its
own step, not as an occasional side-check: every national roofclf-score-national run
should be followed by drawing a fresh random-cell batch and reviewing it, and the
result should be recorded (see Recording results below) so skill
against the un-curated population is tracked the same way LOQO fold AUC already tracks
skill against the quadrats. A capacity number with no random-cell review behind it is
missing half its evidence.
Generating a batch¶
This reuses the exact script and output convention --all-quadrats already uses
(scripts/tile_roofclf_detections_geojson.py) -- same complete-per-region tiling, same
JOSM GeoJSON schema, same results/<aoi>_roofclf_validation/ output folder, so quadrat
and random-cell tiles sit side by side and a reviewer moves between them without
learning a second format.
What is different:
- The cells are the unit, not a hand-drawn boundary. Each region is one 0.1°
national grid cell (~11 km × 11 km, ~121 km²) -- much larger than a quadrat, so expect
more tiles per region (
--max-per-filestill caps each at 2,000 features). - Selection is random, not chosen.
--seedmakes a draw reproducible (rerun the same command and get the same cells back); pick a fresh seed for a new, independent batch. Only cells with at least one flagged building are drawn by default -- an empty cell gives a reviewer nothing to check -- but--include-empty-cellssamples the full national set including zero-detection cells, useful for spot-checking true negatives specifically. - Calibration overlap is still excluded (the same
exclude_calibration_overlapa--cellrun uses): if a randomly drawn cell happens to contain a calibration quadrat, buildings inside that quadrat's own boundary are dropped, so a random batch never silently re-reviews ground already covered by--all-quadrats.
Defaults point at the current national scoring
(data/roofclf_national_with_sppi/<aoi>/prob) and the current calibration snapshot's
threshold (data/roofclf/summary.json) -- the same "current" paths
roofclf-score-national and sub400-capacity write to and read from, so a batch drawn
right after a national re-run always reflects that run, not a stale one.
Reviewing in JOSM¶
- Load the layer(s). File -> Open, point at one or more
pakistan_roofclf_tiles_<cell>_partN_ofM.geojsonfiles underresults/pakistan_roofclf_validation/. Load the sibling.mapcssonce (Preferences -> Map Paint Styles -> +) -- every tile in a batch shares the same style, so one load covers the whole batch, not one per file. - Turn on a high-resolution background (Esri/Bing, whichever JOSM has configured) -- the polygons are Sentinel-2-scale detections, not something to judge against OSM's own tiles.
- Go feature by feature, not by area. A detection's popup (or the relation editor)
shows
p_roofclf,sppi,roof_area_m2andosm_matched; use these to understand why the model flagged it, not just whether it looks right, since a systematic pattern (e.g. every miss is a bright metal roof) is worth more than a raw pass/fail tally. - Classify each one: real PV, not PV (false positive), or unclear (imagery too old, too small, ambiguous roof material -- do not force a call the imagery cannot support).
- Never edit OSM from this layer directly. Every feature carries
do_not_upload: "earthpv model detection, NOT verified OSM data..."for exactly this reason -- these are candidate detections, not verified installations. Confirming a detection is real means mapping the panel fresh, by eye, against the imagery, the same way any other quadrat installation is mapped (quadrat mapping protocol) -- the detection tells you where to look, it does not supply the geometry to publish.
Recording results¶
There is no automated scoring here -- a random cell has no exhaustive ground truth the
way a Rule-1 quadrat does, so this cannot produce an AUC or a recall number the way
roof-classifier's LOQO folds do. What it produces is a measured precision estimate on
an unbiased sample, which the quadrats structurally cannot: per reviewed cell, record
at minimum
- cell id, seed, and threshold used (from the batch's own
earthpv.tile_bbox/regionmetadata already embedded in each GeoJSON'searthpvblock -- copy it, don't retype it) - reviewer, review date
- counts: real / false positive / unclear
- any systematic pattern noticed (a roof material, a terrain type, a size band)
into results/roofclf_random_validation_log.csv (one row per reviewed cell; create it
with a header row of date,reviewer,cell,seed,threshold,n_real,n_false_positive,
n_unclear,notes if it does not exist yet). Pooling this log's n_real /
n_false_positive across batches over time is what turns "we spot-checked some cells"
into a national precision estimate with a sample size -- a single batch is a start, not
a conclusion; CLAUDE.md's roofclf section should be updated with the pooled result
once enough batches exist to say something the 17-quadrat LOQO number doesn't already
say.
The first batch drew zero domain-matched cells -- fixed 2026-08-10¶
--random-cells draws uniformly from every scored cell with >= 1 flagged building
nationally (3,417 of 4,470), not from sub400_capacity.national_cell_domain's 163
cells that the published capacity figure is actually restricted to (only 136 of those
163 have >= 1 flagged building). The first batch (20 cells, seed 1,
results/pakistan_roofclf_validation/) landed entirely outside the domain by chance --
every cell in it has building density far below the calibrated 553-5,258/km² range --
so a full review of it would measure precision on a population that contributes exactly
0 MWp to the atlas, not the population the number describes.
Two fresh, explicitly stratified batches were drawn the same session (--cell with an
externally-computed cell list stands in for a domain-aware --random-cells, since the
script has no domain flag itself):
results/pakistan_roofclf_validation_domain/-- 20 cells drawn uniformly from the 136 domain cells with >= 1 flagged building (seed 20260810). 86,733 buildings, 113 tiles -- much larger per cell than the original batch, since domain cells are dense urban tiles, not sparse rural ones.results/pakistan_roofclf_validation_outdomain/-- 20 cells drawn uniformly from the qualifying cells OUTSIDE the domain (seed 20260811), kept as a second, explicitly-labeled population rather than silently mixed with the domain batch: a reviewer's precision estimate on THIS batch says something about the capacity outside the calibrated domain, not about the number the atlas reports.
The density-calibrated domain has since grown substantially (see Calibration quadrats for its current size), so the specific cell counts above describe the domain as it stood on 2026-08-10, not today's -- but the general lesson (a random-cell batch must be drawn domain-aware, or it silently measures a different population than the one the atlas reports) still holds for any future batch.
Neither of these two batches, nor later random-cell batches drawn against subsequent
national scoring passes, has had its per-cell counts logged to
results/roofclf_random_validation_log.csv yet -- generating the tiles is not the
validation; someone needs to open them in JOSM per the steps above and log
real/false-positive counts. Until that happens, this project's precision figures rest
entirely on the hand-picked quadrats, and the domain-restricted capacity numbers (sub-400
central/AND-gate, >= 400 m² roofclf rooftop) have no unbiased-sample check behind them at
all. See Open questions for the current status of this backlog.