尚未收集儲存庫資訊。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Monorepo pour ClaireAdresse, une application web permettant à un citoyen de saisir une adresse en France et d'obtenir une lecture simple, cartographique et compréhensible de son environnement avant de louer ou acheter un bien.
ClaireAdresse n'est pas un portail immobilier complet. Le produit est pensé comme un assistant de décision avant location/achat.
L'utilisateur saisit une adresse en France et obtient :
/commune/* — l'API visée est un /chat/completions compatible OpenAI, donc changer de fournisseur ne demande que LLM_BASE_URL + LLM_API_KEYfrontend/src/server-modules/PUBLIC / PRO) pilotées par drapeaux de features — cf. frontend/src/lib/site-features.ts| Module | Description | Source de données |
|---|---|---|
address | Recherche et géocodage inverse | Géoplateforme IGN (data.geopf.fr) |
analysis | Orchestration de l'analyse complète | - |
mobility | Transports et mobilité | transport.data.gouv.fr (GTFS) |
risks | Risques naturels/technologiques | Géorisques (georisques.gouv.fr) ; carte des argiles 2026 et arrêtés CatNat (GASPAR) importés |
real-estate | Contexte immobilier et prix | DVF géolocalisé, BDNB (PostgreSQL/PostGIS) |
cadastre | Parcelle, zone PLU, prescriptions | API Carto IGN (apicarto.ign.fr) |
air-quality | Qualité de l'air | EEA, indicateurs annuels des stations de fond (Postgres local) |
neighborhood | Commerces et services proches | OSM + BPE INSEE (PostgreSQL/PostGIS) |
demographics | Données socio-démographiques (population, revenus, âge) | INSEE IRIS (PostgreSQL/PostGIS) |
elections | Résultats Présidentielle 2022 T1 (commune + agrégat national) | Ministère de l'Intérieur (PostgreSQL) |
climate | Normales climatiques 1991-2020 (température, pluie, soleil) | Météo-France — normales par station pré-importées (PostgreSQL/PostGIS) ; fiches climatologiques officielles ; repère France pour la température seule |
security | Délinquance enregistrée, 10 ans, maille communale + repères département, communes de même taille (5 tranches de population, calculées à l'import) et France | SSMSI via data.gouv.fr (PostgreSQL) |
energy | Répartition des étiquettes DPE à l'adresse, dans la commune et en France | API data-fair de l'ADEME, en direct (cache mémoire 7 jours) |
rents | Loyers d'annonce par commune (appartements tous types, 1-2 pièces, 3 pièces et plus, maisons) et communes alentour pour la carte | « Carte des loyers » ANIL / ministère du Logement (PostgreSQL) ; contours de communes reconstitués depuis les IRIS |
| Couche | Type | Source | Toggle |
|---|---|---|---|
| Parcelle cadastrale | GeoJSON (polygone) | API Carto IGN | toujours visible |
| Prix immobiliers (DVF) | GeoJSON (polygones colorés par prix/m²) | DVF (PostgreSQL) | oui |
| Retrait-gonflement argiles | WMS raster | BRGM (geoservices.brgm.fr) | oui |
| Zones inondables (PPR) | GeoJSON (polygones) | Géoportail de l'Urbanisme — SUP PM1, via le WFS Géoplateforme | oui, si des zonages sont publiés |
| Zonage sismique | WMS raster | BRGM | oui |
| Zone démographique (IRIS) | GeoJSON (polygone) | INSEE IRIS (PostgreSQL) | oui |
| Contour de commune | GeoJSON (polygone) | API Découpage administratif IGN (geo.api.gouv.fr) | auto si recherche par nom de commune |
| Époques anciennes (Cassini, état-major, carte de 1950, photos aériennes 1950-65 / 1965-80 / 2000-05) | WMTS raster | Géoplateforme IGN (data.geopf.fr) | frise d'époques, card « Le lieu autrefois » |
La couche historique est posée par-dessus l'ortho-photographie actuelle, qui reste le fond : c'est elle le terme de comparaison. Conséquence, là où une couche n'a pas de donnée, MapLibre ne dessine rien et le fond transparaît — l'utilisateur verrait la photo d'aujourd'hui sous une pastille annonçant « 1965-80 ».
historicalCoverage.ts sonde donc une tuile par époque, au point demandé, avant de
dresser la frise : les époques sans donnée ne sont pas proposées, et une ligne sous la
frise les nomme. L'absence de donnée prend deux formes sur la Géoplateforme, et il
faut traiter les deux :
HTTP 404 portant <Exception>No data found</Exception> ;HTTP 200 avec une PNG palettisée entièrement transparente (1 595 octets).Les deux s'observent sur la même couche selon le zoom. Exemple vérifiable :
ORTHOIMAGERY.ORTHOPHOTOS.1965-1980 renvoie un 404 à Saint-Cyr-l'École et une tuile vide
à Versailles, tandis que Paris, Lyon, Marseille, Toulouse et Rennes sont couverts.
Le serveur utilise un cache en mémoire (InMemoryCache) avec des TTL adaptés à la fréquence de mise à jour de chaque source :
| Source | TTL cache | Stockage | Fréquence de mise à jour |
|---|---|---|---|
| Adresse (BAN) | 7 jours | in-memory | quasi-statique |
| DVF (prix immobiliers) | 7 jours | in-memory | semestriel |
| Cadastre / PLU | 7 jours | in-memory | trimestriel |
| Géorisques (risques) | 24 heures | in-memory | mensuel à trimestriel |
| Transport (GTFS) | 24 heures | in-memory | hebdomadaire à mensuel |
| Voisinage (OSM) | 7 jours | in-memory | quotidien (Geofabrik) |
| Qualité de l'air (stations EEA, Postgres local) | 30 jours | in-memory | annuel (statistiques validées) |
| Climat (Météo-France stations, normales 1991-2020) | 30 jours | in-memory (clé arrondie ~11 km) | données figées (passé) |
| Élections (Postgres local) | — | — | données figées (scrutin clos) |
| Fiscalité locale (Postgres local) | 7 jours | in-memory (clé = code commune) | annuel (DGFiP) |
| Loyers (Postgres local) | 7 jours | in-memory (code commune + clé géo) | annuel (édition de décembre) |
| Contour commune (geo.api.gouv.fr) | 30 jours | in-memory | quasi-statique |
| Mini-synthèses « En bref » (Mistral) | 90 jours | PostgreSQL (card_insights_cache) | sources annuelles (recensement, SSMSI, normales, présidentielle) |
Narrative des pages /commune/* (Mistral) | version de prompt | PostgreSQL (commune_narrative_cache) | stable tant que les données sources ne changent pas |
La clé de cache est basée sur les coordonnées arrondies (~10m de précision). Le cache in-memory est limité à 500 entrées par source ; les caches Postgres des textes LLM persistent entre redéploiements.
La clé de card_insights_cache est (geo_key, mode, model, version) : le modèle et la version de prompt sont dans la clé primaire, si bien que changer de modèle ou incrémenter CARD_INSIGHTS_PROMPT_VERSION cesse de servir les anciennes lignes sans qu'il y ait rien à supprimer.
⚠️ NEXT_PUBLIC_DEBUG=true court-circuite ce cache en lecture et en écriture : chaque affichage rappelle alors le modèle. À laisser à false dès qu'on ne débogue pas le prompt, sous peine d'épuiser le quota.
address vs communeL'application détecte automatiquement le type de la suggestion sélectionnée (champ type retourné par la BAN/Géoplateforme : housenumber, street, locality, municipality) et le réduit à un mode d'analyse binaire :
type AnalysisMode = "address" | "commune";
address : recherche par adresse précise (housenumber, street, locality)commune : recherche par nom de commune (municipality)Le mode est calculé une seule fois dans LocationAnalysisUseCase et exposé dans le DTO sous LocationAnalysisDto.mode. Tous les composants (frontend, PDF) lisent ce champ — pas besoin de re-propager le type brut de l'URL.
| Aspect | Mode address | Mode commune |
|---|---|---|
| Carte (zoom) | Marker bleu + zoom 17 (parcelle) ou 14 | Contour communal IGN bleu pâle 10 % + bordure 2 px, fitBounds automatique, marker masqué |
| Cadastre | Parcelle + zone PLU à l'adresse | Sauté (sans objet sur un territoire entier) |
| DVF (immobilier) | Les 50 ventes les plus proches à moins d'1 km, deux derniers millésimes, une médiane par type de bien ; tableau adresse par adresse | Toutes les transactions de la commune (WHERE code_commune = ?) |
| Mobilité (bus) | Liste des arrêts dans 1 km avec distance + temps de marche | Masquée (les arrêts sont mesurés depuis le centroïde, peu pertinents) |
| Mobilité (gare) | Gare la plus proche dans 20 km, avec distance + temps | Nom de la gare seul, sans distance |
AnalysisMode est exporté depuis frontend/src/server-shared/types/location-analysis.dto.ts et frontend/src/types/location-analysis.ts{ mode: AnalysisMode } (ex. RealEstateService.getMarketData)mode: AnalysisMode (ex. MobilityCard, PdfMobility)"municipality" ne devrait subsister hors du use-case (qui fait la conversion type → mode)L'écran d'analyse est organisé en sections thématiques empilées (.analysis-sections,
flex colonne), chacune introduite par un titre et contenant une ou plusieurs cards en
pleine largeur (.analysis-section-body). L'ordre et les libellés sont centralisés dans
frontend/src/components/analysis/sections.ts, et SectionNav en dérive le sommaire
latéral qui suit le défilement.
Chaque card à graphiques porte sa mini-synthèse « En bref » sous le titre — un
<p class="card-insight"> avec un liseré d'accent, dont le tag sépare visuellement
l'interprétation du modèle de la donnée sourcée qui l'entoure.
L'application présente les données factuelles sans jugement synthétique chiffré : pas de score global ni de score par module. Chaque indicateur est exposé avec ses propres unités (niveau de mobilité, niveau de risque, prix médian €/m², qualité de l'air, etc.) et — quand c'est pertinent — comparé à une référence (commune, France).
L'interprétation finale est laissée à l'utilisateur, complétée optionnellement par les mini-synthèses « En bref » : une à deux phrases sous le titre de chaque card à graphiques, qui disent ce qu'il faut comprendre des courbes plutôt que de résumer le thème. Une mention sous les cards rappelle qu'elles sont rédigées par une IA à partir des seules données affichées.
| Route | Description |
|---|---|
GET /api/address/search?q=... | Recherche d'adresse (autocomplétion) |
GET /api/location/analyze?lat=...&lon=... | Analyse complète d'une adresse |
POST /api/location/card-insights | Mini-synthèses des cards (corps = LocationAnalysisDto, réponse = { insights, generatedAt, cached }). Répond toujours 200 ou 400 : toute défaillance LLM donne insights: {} |
GET /api/health | Sonde de vivacité |
GET /api/debug/pois | Inspection des POIs voisins (debug) |
GET /api/debug/flood-zones?lat=...&lon=... | Sonde des zonages PPR d'inondation : plans retenus, sommets, poids, latence (debug) |
L'écran d'analyse expose le profil électoral de la commune sous forme de barres horizontales colorées par parti, triées par score communal décroissant, avec un repère du score national sur chaque barre et un delta en points (vert/orange) indiquant l'écart au résultat national.
burvot)elections_pres_2022_t1_commune (inscrits/votants/exprimés) + elections_pres_2022_t1_results (1 ligne par candidat × commune). L'agrégat France est stocké sous le pseudo-code 'FRANCE' calculé à l'importSous le titre de chaque card à graphiques, une à deux phrases rédigées par un LLM disent ce qu'il faut comprendre des courbes — pour le lecteur qui n'a ni le temps ni les repères pour les lire. Elles s'affichent dans l'écran d'analyse et dans l'export PDF.
securite, demographie, logement, emploi,
menages, elections, climat. La liste est la source unique
(server-shared/types/card-insights.ts) : le type, le format de sortie du prompt, le
parseur et le cache en dérivent tous.card-insights.input.ts
réplique la garde de chaque card ; le champ cles_attendues du prompt liste les
sections effectivement affichées, et le parseur ignore tout ce qui déborde.response_format: json_object).card_insights_cache, TTL 90 jours, clé
(geo_key, mode, model, version).insights: {} et la route répond 200 — les
cards s'affichent alors sans phrase, exactement comme avant la fonctionnalité.CARD_INSIGHTS_FIXTURE=1 (ou =slow, qui ajoute 1,2 s de
latence pour juger le fondu à l'apparition) sert des phrases factices sans aucun appel
au LLM.Les pages SEO /commune/* ont leur propre pipeline (commune-narrative.*) : quatre
paragraphes éditoriaux plus une légende par section affichée, en un appel, avec une
règle anti-redite entre les deux familles de clés. Cache : commune_narrative_cache.
mistral-small-latest) par défaut. L'API visée est un
/chat/completions compatible OpenAI : pointer un autre fournisseur ou un modèle local
ne demande que LLM_BASE_URL et LLM_API_KEY (qui retombe sur MISTRAL_API_KEY).Rate limit exceeded signifie quota épuisé, pas clé
invalide — et se traduit à l'écran par des cards sans phrase, sans message d'erreur) :curl -i https://api.mistral.ai/v1/chat/completions \
-H "Authorization: Bearer $MISTRAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"mistral-small-latest","messages":[{"role":"user","content":"Bonjour"}]}'
Un bouton « Télécharger PDF » dans la barre supérieure de l'écran d'analyse génère un PDF vectoriel du rapport complet, texte sélectionnable, directement dans le navigateur.
@react-pdf/renderer chargé dynamiquement à la demande (pas d'impact sur le bundle initial).PdfInsight).getCanvas().toDataURL()), avec preserveDrawingBuffer: true activé sur la carte.Jouer d'abord les migrations SQL (§ 2) : plusieurs scripts remplissent des tables qu'elles créent (
crime_*,municipales_2026_*,climate_station_*,school_sector).
cd scripts
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Configurer .env avec votre connexion PostgreSQL
# POSTGRES_URL=postgresql://user:pass@localhost:5432/mabase
# Importer OSM (voisinage) — télécharge france-latest.osm.pbf (~4.2 Go)
python import_osm.py
# Importer BPE (voisinage INSEE, complémentaire à OSM) — ~5 min
python import_bpe.py
# Importer DVF (prix immobiliers) — ~15-30 min
python import_dvf.py
# Fiche des immeubles vendus (BDNB) — migration 030 d'abord, APRÈS import_dvf.py
# (ne garde que les parcelles vendues) ; ~16 Mo lus par département, ~12 min
python import_bdnb.py
# Importer IRIS (contours, démographie, revenus) — ~5 min
# À jouer AVANT import_insee_iris.py : les contours définissent quels IRIS existent.
python import_iris.py
# Revenu médian et taux de pauvreté officiels des communes et de la France (Filosofi 2021,
# ~16 Mo) — remplacent la moyenne des IRIS dans les repères commune et France
python import_filosofi_reference.py
# Enrichir les IRIS : logement, emploi & qualifications, ménages — ~30 s
# (~105 Mo de ZIP INSEE, mis en cache dans scripts/data/insee/)
python import_insee_iris.py
python import_insee_iris.py --only logement # ou un seul axe
# Importer Présidentielle 2022 T1 — ~1 min
# Télécharger d'abord resultats-par-niveau-burvot-t1-france-entiere.xlsx (31,9 Mo)
# depuis https://www.data.gouv.fr/fr/datasets/election-presidentielle-des-10-et-24-avril-2022-resultats-definitifs-du-1er-tour/
python import_elections.py --file ~/Downloads/resultats-par-niveau-burvot-t1-france-entiere.xlsx
# Importer les Municipales 2026 (T1 + T2) — CSV data.gouv.fr, téléchargés si absents
python import_municipales.py
# Importer la délinquance SSMSI — COM (~39 Mo, 5,2 M lignes) + DEP, téléchargés si absents
python import_crime.py
# Importer la fiscalité locale DGFiP — taux, délibérations, comptes (6 CSV, ~25 Mo), téléchargés si absents
python import_local_tax.py
# Importer les loyers d'annonce par commune (ANIL, 4 CSV ~19 Mo, téléchargés si absents) et
# reconstruire les contours de communes depuis les IRIS — à lancer APRÈS import_iris.py
python import_rents.py
# Importer le zonage « zone tendue » (ministère de la Transition écologique, 1 CSV ~2 Mo)
python import_zone_tendue.py
# Importer les normales climatiques Météo-France 1991-2020 — fiches officielles, ~1 min
# (1 574 petits fichiers, mis en cache dans scripts/data/climate/fiches/)
python import_climate_fiches.py
# Géorisques en local (PLAN-GEORISQUES.md) — migration 028 d'abord
python import_argiles.py # carte d'exposition aux argiles 2026, ~440 Mo, ~3 min
python import_catnat.py # arrêtés de catastrophe naturelle (GASPAR), ~8 Mo — à relancer chaque mois
# Importer les indicateurs annuels des stations françaises sur 10 ans — moyennes NO2 / PM2.5 / PM10, jours de pic d'ozone (EEA, ~2 min, API publique)
python import_air_quality_eea.py
# Importer les indices ATMO annuels (une ville par script)
python import_atmo_paris.py
python import_atmo_lyon.py
python import_atmo_marseille.py
# Importer les secteurs de collège de Paris (opendata.paris.fr)
python import_school_sectors_paris.py
# Importer les collèges et lycées (annuaire) et les données publiées par établissement :
# résultats et valeur ajoutée, IPS, effectifs, langues, encadrement, évaluations de 6e…
# (data.education.gouv.fr, 24 jeux, ~1 Go en cache dans data/schools/) — APRÈS la migration 024
python import_schools.py
Les tables nécessaires au code applicatif (hors scripts d'import) sont dans frontend/src/server-shared/infrastructure/database/migrations/. À jouer dans l'ordre sur la base, ou en boucle :
for f in frontend/src/server-shared/infrastructure/database/migrations/*.sql; do
psql "$POSTGRES_URL" -f "$f"
done
Migrations actuelles :
002-narrative-cache.sql — table narrative_cache, cache de l'ancienne card « Synthèse ». Orpheline depuis le passage aux mini-synthèses : plus aucune requête ne la touche. Non supprimée ici (update.sh rejoue les migrations, un DROP n'a pas de retour arrière) ; une migration 019 la retirera une fois la feature stabilisée en prod003-elections.sql — tables elections_pres_2022_t1_commune + elections_pres_2022_t1_results005-commune-narrative-cache.sql — cache des synthèses des pages /commune/*008-climate-station-normales.sql / 013-climate-station-monthly.sql — normales Météo-France009 / 011-air-quality-atmo*.sql — indices Atmo multi-villes010-commune-contour-cache.sql — contours communaux012-school-sectors.sql — secteurs de collège014-drop-climate-national-tables.sql — nettoyage de deux tables climat obsolètes015-crime-ssmsi.sql — délinquance SSMSI (crime_commune, crime_reference, crime_indicateur)020-local-tax.sql — fiscalité locale DGFiP (local_tax_commune, local_tax_reference, local_tax_deliberation, local_tax_deliberation_reference, local_tax_finances)023-rents.sql — loyers d'annonce par commune (rent_commune) et contours simplifiés des communes (commune_shape)024-schools.sql — collèges et lycées (school_establishment, indexée spatialement) et données par établissement telles que publiées (school_data, JSONB), avec la date de chaque chargement (school_dataset_import)025-crime-tranches.sql — tranche de population de chaque commune pour le repère « communes de même taille » de la délinquance (crime_commune_tranche) ; les taux des tranches sont dans crime_reference (codes T1 à T5)028-georisques-local.sql — carte d'exposition aux argiles 2026 (argiles_exposition, indexée spatialement), arrêtés de catastrophe naturelle (catnat_arrete) et édition de chaque jeu (georisques_import)021-card-insights-lang.sql — colonne du cache des mini-synthèses (une phrase par langue)Le script update.sh exécute automatiquement toutes les migrations à chaque déploiement.
Elles sont idempotentes grâce à CREATE TABLE IF NOT EXISTS — avec une réserve :
CREATE MATERIALIZED VIEW IF NOT EXISTS ne met pas à jour une vue existante.
Changer la définition d'insee_aggregate demande une nouvelle migration DROP + CREATE.
Son contenu, lui, se rafraîchit avec les imports (python import_insee_iris.py).
# Installation
pnpm install
# Frontend (port 3000)
cd frontend
cp .env.example .env # configurer POSTGRES_URL
pnpm dev
| Variable | Description | Défaut |
|---|---|---|
POSTGRES_URL | URL de connexion PostgreSQL (OSM + DVF + IRIS + caches LLM) | - |
MISTRAL_API_KEY | Clé API Mistral. Sans elle, les phrases « En bref » n'apparaissent simplement pas : cards et graphiques restent identiques | - |
MISTRAL_MODEL | Modèle à utiliser (optionnel) | mistral-small-latest |
LLM_BASE_URL | Base d'une API /chat/completions compatible OpenAI (autre fournisseur, modèle local) | https://api.mistral.ai/v1 |
LLM_API_KEY | Clé de ce fournisseur ; retombe sur MISTRAL_API_KEY si absente | - |
CARD_INSIGHTS_FIXTURE | Phrases factices sans appel au LLM. slow ajoute 1,2 s de latence | - |
COMMUNE_LEGENDS_FIXTURE | Idem pour les légendes des pages /commune/*. À poser au build (pages prérendues) | - |
DISABLE_CACHE | Désactive tous les caches in-memory (1, true, yes) | - |
ANALYZE_TIMINGS | 1 : affiche dans la console du serveur la durée de chaque source d'une analyse ([analyze] cadastre : 4236 ms), pour trouver celle qui la retient | - |
NEXT_PUBLIC_DEBUG | Payload envoyé au modèle sous les cards — et court-circuite le cache des mini-synthèses | false |
NEXT_PUBLIC_HIDE_AIR_QUALITY | Coupe la rubrique « Qualité de l'air » partout (card, pages /commune/*, PDF, landing). Le titre de section redevient « Climat » | - |
NEXT_PUBLIC_SITE_VARIANT | Variante du site : PUBLIC (complet) ou PRO (voisinage + mobilité). Relu au démarrage seulement | PUBLIC |
scripts/import_dvf.pynb_logementsdvf_transactions_new puis l'échange avec dvf_transactions : le site garde ses prix pendant l'importNEARBY_SALES, RECENT_YEARS et MIN_SALES_FOR_MEDIAN dans dvf-database.provider.ts), puis une médiane par type de bien parmi elles (appartements, maisons) — pas de médiane sous 10 ventes du type. Mélangées, elles ne décrivaient aucun des deux marchés (à Boën-sur-Lignon, appartements à 917 €/m², maisons à 1 150). La card dit le nombre de ventes de chaque type, la période et le rayon ; scripts/verify les recalcule depuis les fichiers publics départementaux (une vente peut couvrir plusieurs communes)python scripts/import_dvf.py pour récupérer le nouveau millésime data.gouv.frscripts/import_bdnb.py lit à distance (requêtes HTTP « Range ») quatre tables de chaque archive départementale : rel_batiment_groupe_parcelle, batiment_groupe_ffo_bat (Fichiers fonciers), batiment_groupe_rnc (Registre national des copropriétés), batiment_groupe_adresse → table bdnb_parcelle, une ligne par parcelle vendue (2,80 millions). Reprend là où il s'est arrêté (bdnb_import) ; --force refait toutscripts/verify (checks/bdnb.py) s'en sert pour contrôler trois immeubles par adresseMILLESIME et relancer ; et après chaque import DVF, pour les nouvelles parcelles venduesAQ_Statistics_WM — statistiques annuelles calculées par l'EEA sur les séries transmises par la France (Geod'air / LCSQA). Sans comptescripts/import_air_quality_eea.py (10 dernières années validées, Preliminary='0', ~12 000 lignes ; écarte les statistiques couvrant moins de 75 % de l'année, ou moins de 2 années sur 3 pour l'ozone ; chaque import remplace toute la fenêtre) → table air_quality_station_annualAnnual mean / average, µg/m³) ; ozone en jours de dépassement de 120 µg/m³ sur 8 h, moyennés sur 3 ans (Days in exceedance averaged over 3 calendar years), comparés à la valeur cible européenne de 25 jours — l'OMS n'a pas de seuil en jours, et la moyenne annuelle d'ozone, polluant de pics estivaux, ne distingue presque rien. Les autres polluants publiés (SO₂, benzène, métaux…) sont quasi nuls partout ou mesurés par trop peu de stations, surtout industriellesLes deux sources sont combinées et dédupliquées pour un résultat complet :
scripts/import_osm.pyscripts/import_bpe.pyscripts/import_iris.py, puis
scripts/import_insee_iris.pyscripts/import_filosofi_reference.py dans filosofi_reference (migration 027). La vue
insee_aggregate en faisait la moyenne des IRIS, ce qui donnait 18,8 % de pauvreté et
23 323 € pour la France, quand l'INSEE publie 14,9 % et 23 080 € (France métropolitaine)climate/domain/reference-climates.ts. Pas de repère France pour la pluie ni l'insolation (aucune normale nationale publiée). À mettre à jour vers 2031 (période 2001-2030)scripts/import_climate_fiches.py → tables climate_station_normales et climate_station_monthly_normales (vidées puis remplies)008-climate-station-normales.sql et 013-climate-station-monthly.sql, puis python scripts/import_climate_fiches.pyL'API resultats_rapport_risque est limitée à un appel par seconde, et bloque l'adresse IP
au-delà : deux jeux sont donc importés (voir PLAN-GEORISQUES.md).
PM1
au Géoportail de l'Urbanisme, servies par le WFS de la Géoplateforme
(data.geopf.fr/wfs/ows, couches wfs_sup:assiette_sup_s et wfs_sup:generateur_sup_s)risques de mapsref.brgm.fr, qui servait
PPRN_ZONE_INOND, est cassé côté BRGM — toute requête répond une erreur de parsing
MapServer en HTTP 200 text/html. Les surfaces inondables des TRI (mapfile rapport,
DI_COVADIS_ALEA_SYNT) ont servi d'intérim mais ne couvrent que les agglomérationsapicarto.ign.fr/api/gpu/assiette-sup-s expose la même
donnée, en 11,2 s à Bordeaux contre 1,4 s par le WFS (mesuré, fenêtre de 6 km)idgen. Sans elle, filtrer les PPR
d'inondation sur le libellé rate 15 des 17 zonages de Vaison-la-Romaine, nommés d'après
leur rivière (OUVEZE_SEGURET)code_alea (11 = inondation), rejet des polygones hors
fenêtre, simplification Douglas-Peucker à 3 m avec budget de 40 000 sommets — les emprises
brutes pèsent 9 Mo à Bordeaux, 480 Ko après traitementkind: "perimeter" et
dessinés en contour pointillé, sans aplatresultats-par-niveau-burvot-t1-france-entiere.xlsx (31,9 Mo, ~70 000 bureaux de vote)scripts/import_elections.py'FRANCE'scripts/import_local_tax.py :
'FRANCE')NULL, jamais affichée « 0 % »server-modules/local-tax/infrastructure/dmto-rates.ts (taux au 1er juin 2026) — à revoir à chaque nouveau barèmeupdate.sh rejoue les migrations mais pas l'importscripts/import_rents.py → rent_commune (loyer €/m² charges comprises hors meublé, intervalle à 95 %, niveau d'estimation, nombre d'annonces, R²) ; reconstruit aussi commune_shape (contours des communes, fusion simplifiée des IRIS) pour la cartelegende_loyers. L'écran d'analyse n'affiche aucune comparaisonANNEE et FILES en tête du script, puis le relancer/lines avec geo_distance de 2 m pour l'adresse, /values_agg pour la commune et la France), cache mémoire de 7 jours par adresse, par commune et pour la Franceserver-modules/energy/. Card « Performance énergétique » après les Loyers, en mode adresse seulement (le use-case ne l'interroge pas en mode commune) ; l'essentiel : l'adresse seule, commune et France dans le détail ; ligne PDF (part de F et G). Pas de carte : une carte en carrés de 50 m autour de l'adresse a été essayée puis retirée le 27/10/2026 (peu utile pour juger un logement, lourde : jusqu'à 5 000 lignes par analyse)type_batiment:immeuble, ils doublonnent les DPE d'appartements générés à partir d'eux). Les DPE remplacés sont déjà retirés de la base par l'ADEME (vérifié le 27/10/2026)CODGEO25) ; on lit la colonne « Zonage TLV post décret 22/12/2025 »scripts/import_zone_tendue.py → rent_zone_tendue (tendue 1 430 communes, touristique 2 259, non 31 186). Lu par le module rents, affiché dans la card Loyers et la fiche PDF ; un arrondissement prend le classement de sa villeRELOCATION_CAP_UNTIL dans components/analysis/rentsModel.ts à chaque reconductionURL, COLONNE et DECRET en tête du script, puis le relancerDATASETS en tête de scripts/import_schools.pyscripts/import_schools.py →
school_establishment : collèges et lycées ouverts de l'Annuaire de l'éducation (fr-en-annuaire-education), sans les sections rattachées à un autre établissement (SEGPA, SEP, SGT) ni les établissements privés hors contrat (aucune donnée publiée), avec coordonnées, secteur, éducation prioritaire, voies, services (cantine, internat, ULIS, SEGPA) et sections (européenne, internationale, sport, arts…), liens site web et fiche ONISEPschool_data : chaque ligne des autres jeux, telle que publiée (JSONB), rattachée à l'UAI et à l'année — l'affichage n'étant pas arrêté, rien n'est réinterprété à l'import. On y trouve : offre de langues, sections internationales et sportives, lycées des métiers, internats et mode d'hébergement ; effectifs par niveau, sexe et langue ; encadrement (heures par élève, élèves par division) ; résultats et valeur ajoutée (IVAC collèges depuis 2022, IVAL lycées GT et pro depuis 2012, brevet par établissement), évaluations nationales de 6e, insertion des lycées pro (Inserjeunes) ; IPS (2016-2021 et depuis 2023) et indice d'éloignementuai à NULL : ce sont les références de comparaison--only <jeu> pour un seul jeu, --refresh pour retéléchargerLe site existe en français (langue d'origine, URL inchangées) et en anglais sous /en
(/en, /en/about, /en/analyze). Les pages /commune/* restent en français seulement.
frontend/src/i18n/messages/fr/ et en/ ; le type se déduit du français,
donc une clé absente en anglais ne compile pas.englishLaunched vaut false dans
frontend/src/lib/site-features.ts : pages noindex, absentes du sitemap, sans hreflang ni
sélecteur de langue. Le passer à true le rend public — à faire après relecture de la traduction.021).NEXT_PUBLIC_I18N_PSEUDO=1 pnpm dev entoure de ⟦ ⟧ tout texte issu d'un dictionnaire : ce qui
s'affiche sans crochets est resté en dur.robots.ts et sitemap.ts (dans frontend/src/app/) : /analyze n'est pas indexé, il dépend
d'une adresse. Le trafic de recherche vient de l'accueil, d'« À propos » et des pages commune.BRANDING dans site-features.ts (français) et
i18n/messages/en/site.ts. La FAQ de l'accueil part aussi en FAQPage JSON-LD./llms.txt (app/llms.txt/route.ts) : présentation du site pour les assistants
conversationnels — contenu de l'analyse, sources, et format du lien direct vers l'analyse
d'une adresse. Format proposé sur llmstxt.org, sans garantie d'être lu par les grands moteurs.scripts/verify/ compare, adresse par adresse, chaque chiffre du site en ligne (donc du PDF) à la
source publique qu'un journaliste consulterait, en relisant les sources sans réutiliser le code
du site. Rapport HTML avec statut et lien de vérification par chiffre : voir
scripts/verify/README.md.
claireadresse/
├── README.md
├── scripts/ # Scripts Python d'import de données
│ ├── import_osm.py # Import OpenStreetMap POIs → PostgreSQL
│ ├── import_bpe.py # Import BPE INSEE → PostgreSQL
│ ├── import_dvf.py # Import DVF géolocalisé → PostgreSQL
│ ├── import_bdnb.py # Fiche des immeubles vendus (BDNB : logements, niveaux, période)
│ ├── import_iris.py # Import INSEE IRIS (contours, démographie, revenus)
│ ├── import_filosofi_reference.py # Revenu et pauvreté officiels des communes et de la France
│ ├── import_insee_iris.py # Enrichissement IRIS : logement, emploi, ménages
│ ├── import_elections.py # Import Présidentielle 2022 T1 (agrégation par commune)
│ ├── import_municipales.py # Import Municipales 2026 (T1 + T2)
│ ├── import_crime.py # Import délinquance SSMSI (commune + dept + France + tranches de taille)
│ ├── import_local_tax.py # Import fiscalité locale DGFiP (taux, délibérations, comptes)
│ ├── import_rents.py # Import loyers d'annonce ANIL + contours de communes
│ ├── import_zone_tendue.py # Import zonage zone tendue (TLV)
│ ├── import_climate_fiches.py # Normales Météo-France 1991-2020 (fiches officielles)
│ ├── import_argiles.py # Carte d'exposition aux argiles 2026 (Géorisques)
│ ├── import_catnat.py # Arrêtés de catastrophe naturelle (GASPAR)
│ ├── import_atmo_*.py # Indices ATMO annuels (Paris, Lyon, Marseille)
│ ├── import_school_sectors_paris.py # Secteurs de collège (opendata.paris.fr)
│ ├── import_schools.py # Collèges, lycées et données par établissement (data.education.gouv.fr)
│ ├── requirements.txt # Dépendances Python
│ └── .env # Config PostgreSQL locale (non versionné)
└── frontend/ # Next.js / React / MapLibre
└── src/
├── app/
│ ├── api/ # Routes API Next.js (analyze, card-insights, search, health)
│ └── commune/[slug]/ # Pages SEO par arrondissement
├── components/
│ ├── map/ # Carte, couches WMS, toggles, contour communal, époques anciennes
│ ├── commune/ # Sections des pages SEO /commune/*
│ └── analysis/ # Cards d'analyse (Mobilité, Risques, Sécurité, Élections, Histoire, etc.)
├── features/
│ ├── location-analysis/ # Hooks (useLocationAnalysis, useCardInsights)
│ └── analysis-pdf/ # Export PDF (react-pdf) — document, sections, capture carte
├── lib/site-features.ts # Drapeaux de features des variantes PUBLIC / PRO
├── server-modules/ # Modules serveur (DDD) : address, mobility, risks, real-estate, cadastre, air-quality, neighborhood, demographics, elections, climate, security, local-tax, rents, school-sector, schools, commune-stats, summary, narrative
├── server-shared/
│ ├── infrastructure/
│ │ ├── database/ # Pool PostgreSQL + migrations SQL applicatives
│ │ └── cache/ # InMemoryCache + buildGeoKey
│ └── types/ # DTOs serveur
└── types/ # DTOs frontend
local-tax |
| Fiscalité locale : taxe foncière et TEOM (2021-2025), résidences secondaires et vacance, droits de mutation, comptes de la commune |
DGFiP via data.economie.gouv.fr (PostgreSQL) ; barème DMTO ressaisi dans dmto-rates.ts |
school-sector | Secteur de collège (donnée disponible pour Paris) | Ville de Paris (PostgreSQL/PostGIS) |
schools | Section « Enseignement » : collèges et lycées alentour (ou de la commune), résultats, valeur ajoutée, IPS, encadrement, langues, offre | Éducation nationale / DEPP (PostgreSQL/PostGIS, scripts/import_schools.py) |
commune-stats | Agrégats des pages SEO /commune/* (prix, démographie, équipements, air, élections) | PostgreSQL/PostGIS |
summary | Construction du résumé textuel (règles déterministes) — toujours produit dans le DTO, plus affiché depuis le passage aux mini-synthèses | - |
narrative | Mini-synthèses « En bref » des cards + narrative des pages /commune/*, avec cache Postgres | Mistral AI (api.mistral.ai) |
lang022-air-quality-stations.sql — indicateurs annuels par station de mesure (air_quality_station_annual, source EEA)016-municipales-2026.sql — municipales 2026017-insee-iris.sql — iris_demographics (jusque-là créée par le script d'import),
ses trois tables sœurs iris_logement / iris_emploi / iris_menages, et la vue
matérialisée insee_aggregate (repères commune et France pré-calculés)018-card-insights-cache.sql — card_insights_cache, le cache des mini-synthèses
« En bref ». Clé (geo_key, mode, model, version). Sans cette table, rien ne casse
visiblement : le provider avale ses erreurs SQL, et le seul symptôme est un appel au
LLM à chaque affichage. Penser à la jouer sur une base locale créée avant cette
migration — update.sh s'en charge en prod, rien ne le fait en devrentalBanCalendar de components/analysis/energyModel.ts. Un projet de loi d'assouplissement était en discussion au 27/10/2026