DeepPVMapper

MCP serverEverything else

Open registry of 1.14M+ rooftop-solar detections across France, queryable by natural language.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use DeepPVMapper

Install DeepPVMapper

The server’s own address, for the clients that take one directly. Or connect ahel onceand every client you use reads it from one address, with the account kept on ahel rather than in each client’s config.

  • Claude Code

    claude mcp add --transport http --scope user deeppvmapper 'https://zelhliylrlktnasircwp.supabase.co/functions/v1/mcp/mcp'

    Run it once in your project, then open /mcp to approve any sign-in the server asks for.

  • Claude Desktop

    https://zelhliylrlktnasircwp.supabase.co/functions/v1/mcp/mcp

    Add a custom connector in Settings, paste this address, and approve the sign-in.

  • Cursor

    cursor://anysphere.cursor-deeplink/mcp/install?name=deeppvmapper&config=eyJ1cmwiOiJodHRwczovL3plbGhsaXlscmxrdG5hc2lyY3dwLnN1cGFiYXNlLmNvL2Z1bmN0aW9ucy92MS9tY3AvbWNwIn0=

    Open the link and Cursor adds the server at that address.

  • ChatGPT

    https://zelhliylrlktnasircwp.supabase.co/functions/v1/mcp/mcp

    In Settings, enable Developer mode, create an MCP app, and paste this address. Your plan and workspace must allow custom apps.

  • Codex

    codex mcp add deeppvmapper --url 'https://zelhliylrlktnasircwp.supabase.co/functions/v1/mcp/mcp'

    Run it once, then sign in with codex mcp login deeppvmapper if the server asks for an account.

From the project's README

As published by gabrielkasmi/deeppvmapper in README.md.

Large-scale rooftop PV detection pipeline for France. Classifies IGN aerial tiles with InceptionV3, segments positive patches with FCN/DeepLab, and extracts panel characteristics (surface, tilt, azimuth, installed capacity) via pypvroof.

🗺️ Explore the map and results →
🎮 Try the interactive demo →

Work carried out by Gabriel Kasmi as part of his PhD at Mines Paris-PSL (2020–2024).


Mapping data

Pre-computed detection results for French departments are available on Zenodo:


Installation

GDAL must be installed system-wide first, then via pip to match the running Python version:

# Ubuntu/Debian
apt-get install -y gdal-bin libgdal-dev libopenjp2-7

pip install "GDAL==$(gdal-config --version)"
pip install -r requirements.txt

GPU required. CUDA 11.8+, 8 GB VRAM minimum.


Data and model weights

Download model weights and runtime data from Zenodo:

Model weights are also available on Hugging Face:

The training dataset (BDAPPV):

Fill in the source paths in config.yml before running:

config keywhat goes there
source_images_dirIGN JP2 tiles + dalles.shp index shapefile
source_topo_dirBDTOPO folder (BATIMENT.shp, ZONE_D_ACTIVITE_OU_D_INTERET.shp)
source_commune_dirfolder containing communes-20210101.shp
model_dirfolder containing model_bdappv_cls.pth and model_bdappv_seg.pth

Usage

python main.py --dpt 06

--count sets tiles per classification batch (default 16 — reduce if OOM):

python main.py --dpt 06 --count 8

--config points to an alternative config file (useful for RunPod deployments):

python main.py --dpt 06 --config /workspace/config_runpod.yml

To process a subset of tiles (local testing), set tiles_list in config.yml:

tiles_list:
  - 01-2024-0850-6565-LA93-0M20-E080
  - 01-2024-0850-6570-LA93-0M20-E080

Force a full rerun (wipe prior progress):

python main.py --dpt 06 --clean

Pipeline

Four steps run sequentially inside main.py:

stepwhat happens
InitBuilds per-department auxiliary files (buildings, plants, communes) into temp/. Skipped if already present — safe to rerun after a crash.
ClassificationTiles loaded fully in memory. InceptionV3 classifies 299×299 patches; positives saved as GeoTIFFs to temp/segmentation/.
SegmentationFCN/DeepLab segments each positive patch. LAMB93 polygons extracted, sorted by tile, merged into pseudo-arrays.
Aggregationpypvroof extracts tilt/azimuth/kWp per polygon. Building filter applied. Results written to outputs_dir.

On success: temp/ is deleted automatically.
On crash: temp/ is kept. Rerun the same command to resume from where it stopped.


Outputs

Written to outputs_dir (default: data/):

filedescription
arrays_{dpt}.geojsonDetected PV polygons in WGS84
characteristics_{dpt}.csvPer-installation registry: surface (m²), tilt (°), azimuth (°), kWp, city code, lat, lon
aggregated_characteristics_{dpt}.csvCity-level aggregation: count, total kWp, avg surface, avg kWp
arrays_characteristics_{dpt}.geojsonPolygons enriched with all characteristics

Only residential-scale installations (1.7–36.1 kWp) located on buildings are retained.


Configuration reference

parameterdefaultdescription
temp_dirtempWorking directory. Deleted on success, kept on crash.
outputs_dirdataFinal outputs directory.
cls_threshold0.4Classification confidence threshold
cls_batch_size512Patches per GPU batch (classification)
decode_workers3Concurrent JP2 decode processes feeding the GPU — tune to your real CPU quota, not host core count
decode_stagger_s35Gap between initial decode submissions, to avoid lockstep bursty waits — rule of thumb: decode_time / decode_workers
seg_threshold0.46Segmentation binarization threshold
seg_batch_size64Images per GPU batch (segmentation)
filter_buildingTrueDiscard detections not on a building
tilt_methodlutpypvroof tilt method (lut or constant)
azimuth_methodbounding-boxpypvroof azimuth method
ic_methodclusteredpypvroof installed-capacity regression type
tiles_list(empty)Optional tile subset for partial runs

Contributing

Contributions are welcome — both code (performance, new imagery sources, models, building filters) and registry corrections via the interactive map, no coding required.

See CONTRIBUTING.md for the contribution areas, setup instructions and workflow. Issues labelled good first issue are the best entry points.


Known issues

GDAL is fragile to install, for two distinct reasons — and the fix below handles both.

  1. The pip GDAL binding must match the system libgdal version exactly. pip install GDAL fails to build, or segfaults at import, if its version differs from the system library.
  2. On images that ship a pre-installed python3-gdal apt package (common on RunPod/cloud GPU images), that package bundles its own osgeo/, which takes priority over the pip-installed one in sys.path — and its .so is often broken, regardless of what pip installs.

Run this in place of a plain pip install, e.g. right when deploying the pipeline, around the pip install -r requirements.txt step:

#!/usr/bin/env bash
set -e

# --- native GDAL lib (gdal-config must exist before pip can build the python binding) ---
apt-get update
apt-get install -y --no-install-recommends gdal-bin libgdal-dev

# --- purge python3-gdal if present: this apt package ships its own osgeo/,
#     which takes priority over the pip-installed one in sys.path, and its
#     .so is often broken ---
dpkg -l | grep -q python3-gdal && apt-get remove --purge -y python3-gdal || true
rm -rf /usr/lib/python3/dist-packages/osgeo

# --- python deps ---
pip install -r requirements.txt

# --- repin the pip GDAL binding to exactly match the native lib version
#     (requirements.txt only pins GDAL>=3.0, so pip can grab a newer
#     version than the one apt just installed -> ABI mismatch at import) ---
GDAL_VERSION=$(gdal-config --version)
pip install --no-cache-dir --force-reinstall "GDAL==${GDAL_VERSION}"

# --- check ---
python -c "
import torch
from osgeo import gdal
print('torch', torch.__version__, '| cuda', torch.cuda.is_available())
print('gdal', gdal.__version__, '| GTiff driver:', gdal.GetDriverByName('GTiff') is not None)
"

Citation

@phdthesis{kasmi2024enhancing,
  title={Enhancing the Reliability of Deep Learning Models to Improve the Observability of French Rooftop Photovoltaic Installations},
  author={Kasmi, Gabriel},
  year={2024},
  school={Universit{\'e} Paris sciences et lettres}
}

License

MIT

Advanced
Delivery
deeppvmapper MCP server → your ahel connector (mcp.ahel.ai) → your AI.
Catalog kind
mcp-server
Key
io-github-gabrielkasmi-deeppvmapper
Source
github.com/gabrielkasmi/deeppvmapper
Hosted endpoint
https://zelhliylrlktnasircwp.supabase.co/functions/v1/mcp/mcp