# Paris Solar Scorecard — V3 Workplan

**Goal:** Build a viral, interactive solar potential map of Paris. Tier 3 ambition: exact footprints, beautiful visualization, instant address lookup.

**Status:** Ready to build. Data sources confirmed. Claude Code executor intended.

**Agents involved:**
- **Doc** (OpenClaw) — strategy, research, validation
- **Claude Code** — data pipeline, frontend, MBTiles generation
- **Christian** — decisions, UI review, Facebook distribution

---

## Phase 1: Data Pipeline (Priority: CRITICAL PATH)

### 1.1 Download IGN BD Topo — Paris buildings

**Source:** `https://geoservices.ign.fr/telechargement` — BDTOPO® (shp or GeoPackage)

**What to get:**
- Layer: `BATIMENT` (building footprints)
- Attributes needed: `ID`, `HAUTEUR` (height), `NATURE` (building type), geometry (polygon)
- Coverage: Île-de-France tile (or Paris-specific extract if available)

**Format:** GeoPackage preferred (`.gpkg`) — single file, fast queries via GeoPandas.

**Command skeleton:**
```bash
# Download from IGN portal (manual or scripted via their catalog API)
# Then:
python3 -m pip install geopandas shapely pyproj
python3 scripts/01-extract-buildings.py
```

### 1.2 Compute building-derived solar geometry

**For each building polygon, derive:**

| Field | Method |
|---|---|
| `roof_area_m2` | Footprint area × 1.3 (typical Paris pitched roof overshoot factor) |
| `lat`, `lon` | Centroid of footprint polygon |
| `orientation_deg` | Longest edge azimuth (GIS algorithm) |
| `typical_tilt_deg` | 30° if `HAUTEUR > 12m` (Haussmann), 10° if flat modern, 45° if historic |
| `num_floors` | `HAUTEUR / 3.0` (standard floor height) |

**Paris roof style heuristics:**
- Haussmann-era (1850-1914): 45° two-sided pitched roof, zinc. `HAUTEUR` 15-25m.
- Post-war modern: flat or 5-10° pitch. `HAUTEUR` 8-15m.
- Contemporary: varied. Use `NATURE` + `HAUTEUR` to guess.

**Script:** `scripts/02-compute-geometry.py`

### 1.3 PVGIS solar irradiance lookup

**Strategy:** Don't call PVGIS for 200,000 buildings individually. Grid-based sampling.

**Approach:**
1. Define a 250m grid over Paris bounding box
2. Query PVGIS API for each grid point (peakpower=1kWp, loss=14, angle=30, aspect=0)
3. Store: `lat`, `lon`, `yearly_kwh_per_kwp`, `monthly_breakdown`
4. For each building, assign nearest grid point's irradiance
5. Scale by actual roof tilt and orientation (correction factors)

**PVGIS API call:**
```
GET https://re.jrc.ec.europa.eu/api/v5_2/PVcalc?lat={lat}&lon={lon}&peakpower=1&loss=14&mountingplace=building&angle=30&aspect=0&outputformat=json
```

**Script:** `scripts/03-pvgis-grid.py`
- Rate limit: ~1 req/sec (be polite to EU servers)
- Paris grid (~40×40 = 1,600 points) → ~30 min runtime
- Cache all responses to `data/pvgis-cache/`

### 1.4 Compute final solar score per building

**Inputs per building:**
- `roof_area_m2` (from Step 1.2)
- `usable_ratio` = 0.7 (account for setbacks, chimneys, skylights)
- `panel_efficiency` = 0.20 (modern panels)
- `yearly_kwh_per_kwp` (from PVGIS grid)
- `tilt_correction` = sin(actual_tilt) / sin(30) (normalize to PVGIS 30° baseline)
- `orientation_correction` = cos(orientation - 180) (south = 1.0, east/west = 0.85, north = 0.6)

**Formula:**
```
system_kwp = roof_area_m2 * usable_ratio * panel_efficiency
yearly_kwh = system_kwp * yearly_kwh_per_kwp * tilt_correction * orientation_correction
annual_savings_eur = yearly_kwh * 0.22  // current French residential EDF rate
co2_saved_kg = yearly_kwh * 0.06       // French grid carbon intensity
score = yearly_kwh / roof_area_m2     // kWh/m2 — normalized for comparison
```

**Script:** `scripts/04-compute-scores.py`

**Output:** GeoPackage with all buildings + computed fields. Or parquet for speed.

### 1.5 Shading approximation (V3 ambition)

**Method:** Ray-casting from each building roof to nearby tall buildings.

**Simplified approach:**
1. For each building, find neighbors within 50m using spatial index
2. For each neighbor, compute if it could cast a shadow on the target's roof
3. Factor: `neighbor_height / distance` — if > 0.5, significant shading possible
4. Reduce `usable_ratio` by shading factor (0.7 → 0.5 for heavily shaded)

**This is computationally heavy.** Do it as a separate pass after main scores are done. Run only for the top 10,000 highest-potential buildings first.

**Script:** `scripts/05-shading-pass.py` (optional for V0, required for V3)

---

## Phase 2: Map Visualization (Priority: CRITICAL PATH)

### 2.1 Generate PMTiles (vector tiles)

**Tool:** `tippecanoe` (Mapbox) or `pmtiles` CLI

**Input:** GeoPackage from Phase 1.5
**Output:** `paris-solar.pmtiles` (single file, ~50-200MB for Paris)

**Tippecanoe command:**
```bash
tippecanoe \
  -o paris-solar.pmtiles \
  -z16 -Z13 \
  --drop-densest-as-needed \
  --extend-zooms-if-still-dropping \
  --force \
  -L buildings:buildings-with-scores.geojson
```

**Why PMTiles:**
- Single file, serve from anywhere (S3, GitHub, this server)
- No tile server needed
- MapLibre GL JS reads it directly

### 2.2 Frontend: MapLibre GL JS + Paris building colors

**Stack:**
- MapLibre GL JS (open source, no token needed)
- Base map: CartoDB dark matter or MapTiler basic (free key)
- Building layer: custom from PMTiles with data-driven coloring

**Color scheme (per building `score`):**
| Score (kWh/m2/year) | Color | Label |
|---|---|---|
| > 180 | `#22c55e` (green) | Excellent |
| 140-180 | `#eab308` (yellow) | Good |
| 100-140 | `#f97316` (orange) | Fair |
| < 100 | `#ef4444` (red) | Poor |
| No roof / shaded | `#64748b` (gray) | Not viable |

**Interactions:**
- Hover → highlight building, show quick tooltip with address + score
- Click → sidebar opens with full scorecard
- Zoom animation → buildings fade in based on score intensity

**Files:**
- `frontend/index.html`
- `frontend/style.css`
- `frontend/map.js`

### 2.3 Address search integration

**API:** BAN (`https://api-adresse.data.gouv.fr/search/?q={address}`)

**Flow:**
1. User types address in search bar
2. BAN returns lat/lon + full label
3. Map zooms to lat/lon
4. Find nearest building in PMTiles (spatial query or pre-computed lookup)
5. Open sidebar with that building's solar scorecard
6. Update URL to `?address=12+rue+de+Rivoli` (shareable)

**Address → building matching:**
- Simple version: find building centroid closest to address lat/lon (within 25m)
- Better version: use BAN `parcelle` data or Cadastre to link address to exact building polygon

### 2.4 Scorecard sidebar UI

**Show for selected building:**
- Building address (from BAN or OSM)
- Solar score badge (Excellent/Good/Fair/Poor)
- Estimated roof area: `X m²`
- Optimal system size: `Y kWp`
- Yearly production: `Z kWh`
- Annual savings: `€X,XXX`
- CO2 saved: `X tonnes`
- Simple payback: `~X years` (assume €2.50/W installed cost)
- "Get the full audit + AG motion" CTA (email capture)

**Design:** Dark themed, card-based, mobile-responsive. Think Zillow meets Stripe's dark mode.

---

## Phase 3: Polish & Virality (Priority: HIGH — after Phase 2 works)

### 3.1 Shareable URLs
- Every address search → save `?lat=X&lon=Y&building=ID` to history
- Share button → copy link with Open Graph image preview
- Social card: screenshot-style image of the building + its solar score

### 3.2 Social sharing metadata
- `<meta property="og:image">` with dynamic scorecard preview
- Twitter card support
- WhatsApp-friendly description

### 3.3 Press page
- `/about` or `/methodology` — explain data sources (IGN, PVGIS, BAN)
- Cite public data licenses
- Build credibility for journalists

### 3.4 Mobile optimization
- Map gestures must be smooth on mobile
- Sidebar becomes bottom sheet on phones
- Address search = prominent top bar

---

## Phase 4: Distribution (Starts when Phase 2 is live)

### 4.1 Facebook group posts
- Write educational post with 3-4 surprising stats from the map
- Example: "We mapped every building in the 15th arrondissement. Here are the 10 with the most untapped solar potential — and how to check yours."
- Include direct link to map + search bar
- Post in: "Copropriété et Syndic", "Paris 15ème", energy groups

### 4.2 Reddit / Twitter / HN
- r/france post: "J'ai cartographié le potentiel solaire de tous les immeubles de Paris"
- Twitter thread with animated map GIFs
- Hacker News Show HN if we open-source the stack

### 4.3 Local press
- Pitch Le Parisien: data-driven story on Paris solar potential by arrondissement
- Use pre-computed aggregate stats ("18% of Paris 15th buildings could generate €2M/year in savings")

---

## Data Sources Summary

| Source | What | URL | Cost | Format |
|---|---|---|---|---|
| **IGN BD Topo** | Building footprints, heights | `geoservices.ign.fr` | Free (open data) | GeoPackage |
| **IGN RGE ALTI** | LIDAR elevation (V3 shading) | `geoservices.ign.fr` | Free | LAZ point clouds |
| **BAN** | Address geocoding | `api-adresse.data.gouv.fr` | Free | JSON API |
| **PVGIS** | Solar irradiance + production | `re.jrc.ec.europa.eu/api/v5_2` | Free | JSON API |
| **Cadastre** | Parcel boundaries | `cadastre.data.gouv.fr` | Free | GeoJSON/Shapefile |
| **OpenStreetMap** | Backup building data | `overpass-api.de` | Free | JSON/GeoJSON |

---

## Tech Stack

| Layer | Tool | Why |
|---|---|---|
| Data processing | Python + GeoPandas + Shapely | Standard GIS stack, fast |
| Tile generation | `tippecanoe` or `pmtiles` CLI | Single-file vector tiles |
| Map rendering | MapLibre GL JS | Free, beautiful, mobile-native |
| Base map | CartoDB Dark Matter or MapTiler | Free dark base layer |
| Hosting | Static file server (Caddy) or S3 | PMTiles = no tile server needed |
| Address search | BAN API client-side | No backend needed |
| Backend (v3) | None — static site + APIs | Only add backend if we need user accounts |

---

## Directory Structure

```
paris-solar/
├── data/                  # Raw + processed data (gitignored)
│   ├── ign-bdtopo/        # Downloaded IGN files
│   ├── pvgis-cache/       # API responses
│   └── paris-buildings.gpkg
├── scripts/               # Python data pipeline
│   ├── 01-extract-buildings.py
│   ├── 02-compute-geometry.py
│   ├── 03-pvgis-grid.py
│   ├── 04-compute-scores.py
│   └── 05-shading-pass.py
├── frontend/              # Map + UI
│   ├── index.html
│   ├── style.css
│   ├── map.js
│   └── paris-solar.pmtiles  # Generated tileset
├── docs/
│   ├── methodology.md
│   └── press-stats.md
├── README.md
├── V3-WORKPLAN.md         # This file
└── Makefile or justfile   # `make data`, `make tiles`, `make serve`
```

---

## Red Lines

- **No paid Mapbox token dependency.** Use MapLibre + free base tiles.
- **No backend for V0-V2.** Static site + APIs only. Enables GitHub Pages hosting.
- **No installer outreach for this project.** This is a viral consumer experiment, not B2B sales.
- **No pre-selected ICP.** The data from Phase 4 decides who we serve.

---

## Next Immediate Action

**Claude Code should:**
1. Set up directory structure
2. Download IGN BD Topo for Paris (or Île-de-France)
3. Run `scripts/01-extract-buildings.py` → verify building count and attributes
4. If buildings look good, proceed to `02-compute-geometry.py`
5. Run `03-pvgis-grid.py` with a small test grid (5×5 points) to verify PVGIS rate limits
6. Once data pipeline works end-to-end, move to Phase 2 (frontend)

**If stuck on IGN data download:** fall back to Overpass API + OSM building data for the first prototype. Less accurate but faster to obtain.
