Skip to content

Mapping leads

The leads product is the half of earthpv that has a human in the loop. It is a ranked queue of places where the model thinks there is solar and OpenStreetMap does not yet say so, exported in the formats mappers already use.

Pakistan, country-wide

The country-wide run applied the production checkpoint at threshold 0.3, first over 122 densely built cells and then over the full build of roughly 4,470 cells, once cell selection moved from the Overture set of buildings above 500 m2 to VIDA Open Buildings.

Product Count File
All candidates 6,566 pakistan_pv_candidates.geoparquet
Rooftop placement 1,312 same, placement == "rooftop"
Ground-adjacent 448 same
No building within range 4,806 same
New leads, excluding mapped 4,602 pakistan_pv_new_leads.geojson
New leads after the epoch veto 4,589 pakistan_pv_new_leads_epochclean.geojson
MapRoulette challenge ranked pakistan_pv_maproulette.geojson

Thirty percent of the raw candidates fall on solar already mapped in OpenStreetMap, which is the pipeline's cheapest sanity check. The rest are the queue.

How the queue is ordered

rank_score starts at the model's confidence and is multiplied by a building prior, so a detection sitting squarely on a roof outranks an equally confident detection in open country. Two further signals adjust it:

  • Glint corroboration multiplies upward, capped at four times, as the count of mutually consistent spike dates saturates. It never demotes.
  • The pre-boom epoch check demotes. Pakistan's rooftop stock is overwhelmingly post-2022, so a candidate that already looked like PV in the 2021 dry-season composite is more likely a bright roof, concrete apron or rock outcrop than a new array.

Nothing in the default export is dropped. A high-confidence detection with no building anywhere near it may be an unmapped roof or a ground-mounted farm, and it still surfaces.

The one file that does drop things

--exclude-mapped --epoch-clean --veg-max-ndvi 0.35 writes <aoi>_pv_new_leads_clean.geojson, the single export artifact that removes candidates. Two vetoes apply, and both require positive evidence: a lead that no instrument could check is always kept.

Distance to already-mapped solar. --min-distance-m 100 removes candidates within 100 m of an existing OpenStreetMap solar feature, which is not the same as intersecting one; the intersects-only version missed adjacent-but-offset duplicates.

Vegetation. Manual review of countryside leads found a lot of green fields. Measuring NDVI on the composite the model actually read does not catch them, because those fields were dark fallow or flooded paddy soil when the dry-season median was built. What does catch them is the annual cycle: every crop field greens up at some point in the year and a panel never does. A 0.35 threshold on maximum composite NDVI vetoed 596 of 5,132 leads, and the split confirms the veto is specific rather than blunt: 20.2 percent of no_building leads, 10.5 percent of ground_adjacent, and only 0.3 percent of rooftop.

Vegetation-vetoed leads are written to hard_negatives_veg.parquet and fed back as training negatives. Unlike epoch persistence, which real old PV also shows, an observed crop cycle is near-conclusive non-PV evidence, and dark fallow soil is a confusion class that German training data never contained.

Getting the leads into OpenStreetMap

pixi run earthpv export --aoi pakistan --exclude-mapped --min-distance-m 100 \
    --epoch-clean --veg-max-ndvi 0.35

That writes GeoParquet, GeoJSON and a MapRoulette challenge sorted by rank_score. Load the challenge in MapRoulette, or open the GeoJSON directly in JOSM or QGIS alongside the Esri and Bing layers.

When mapping a lead, tag it the way the rest of OpenStreetMap does (generator:source=solar on a rooftop generator, power=plant with plant:source=solar for a plant) so the next overpass-labels run picks it up as training data. Mapped installations returning through that path are what closes the flywheel.

Other regions

results/gujarat_pv_candidates.geojson and results/gujarat_pv_new_leads.geojson hold a first pass over Gujarat, India, produced with no locally cached data at all. Gujarat is the worked template for running on a new region; see the Gujarat capacity map for its first full capacity estimate (2026-08-07, segmentation-only -- no calibration quadrats exist there yet).