Habitoscope

Documentation technique

Outil d'aide à la décision pour choisir où habiter en France en fonction d'un lieu de travail — prix immobilier, temps de trajet, sécurité, santé, écoles, espace et nature, sur 13 régions métropolitaines, uniquement à partir de données publiques officielles. Application statique, sans backend, sans compte utilisateur.

34 788communes couvertes
13régions métropolitaines
96départements
5critères pondérables
11sources open data
0backend / base de données

01 — Vue d'ensemble

Aperçu

Habitoscope compare 34 788 communes réparties sur 13 régions métropolitaines sur cinq critères objectifs — transport, sécurité, santé, offre scolaire, espace et nature — et les croise avec le budget immobilier de l'utilisateur et son temps de trajet réel vers son lieu de travail. Chaque utilisateur peut renseigner sa propre adresse de bureau ; les temps de trajet en voiture (et en transport en commun en Île-de-France) sont recalculés en conséquence.

Le principe de conception central : aucune donnée n'est inventée ou estimée par un modèle. Chaque chiffre affiché provient d'une source publique nommée, avec sa méthodologie documentée dans l'app elle-même (panneau « Mes priorités » → « Comment sont calculés les scores et les chiffres ? ») — biais connus compris (voir Limites connues).

Positionnement

Ce n'est pas un comparateur d'annonces immobilières : Habitoscope ne liste aucun bien à vendre ni aucune annonce. C'est un outil de présélection de communes, en amont de la recherche d'annonces sur un site comme SeLoger ou Bien'ici.

02 — Fonctionnalités

Fonctionnalités

Sélecteur de région

Menu déroulant stylé maison (pas un <select> natif, qui hériterait de l'apparence claire du système au lieu du thème sombre du site) pour basculer entre les 13 régions. Les données de chaque région (communes, gares, tracés) ne sont téléchargées qu'à la demande, au moment où elle est sélectionnée — voir Architecture.

Carte et classement

Carte interactive (fond CartoDB Positron) avec un marqueur coloré par commune (rouge → vert selon le score pondéré), les gares et lignes de transport disponibles pour la région active, et un filtre par mode affiché en dehors de la carte pour ne pas masquer le fond. En regard, un classement trié (colonnes cliquables, valeurs manquantes toujours en fin de liste, survol d'un score = rappel de sa méthode et de son biais connu) et un graphique prix / revenu par commune.

Priorités et budget

Le poids de chaque critère (0 à 5) se règle par curseur ; classement et carte se mettent à jour en direct. Le budget total et la surface recherchée se saisissent au choix par curseur ou en valeur exacte, pour un budget appartement ou maison, avec une option pour inclure les frais de notaire/agence. Le budget par défaut change selon la région active (voir Budget & accessibilité).

Bureau personnalisé

Par défaut, les temps de trajet sont calculés vers un lieu réel et vérifiable de la région (gare principale — voir Régions couvertes). Chaque utilisateur peut saisir sa propre adresse de travail : elle est géocodée (API BAN du gouvernement, avec repli Nominatim/OpenStreetMap), puis les temps de trajet sont recalculés (détail en § Temps de trajet).

Fiche commune

Cliquer une commune (carte, classement ou graphique) ouvre sa fiche : score pondéré, verdict budgétaire, prix appartement/maison, loyer estimé, revenu médian, densité, temps de trajet, gare la plus proche, lignes desservies (Île-de-France), taux de délinquance, offre santé et scolaire (avec effectif réel d'élèves), et la nuance politique de la municipalité élue en 2026 (affichage informatif uniquement, voir limites).

Favoris et export

Une commune peut être marquée d'une étoile depuis le classement ou la carte, sauvegardée localement, comparable entre régions et exportable — voir § Favoris.

03 — Couverture

Régions couvertes

13 régions métropolitaines, chacune avec ses propres défauts (bureau, budget, seuils de trajet) calculés depuis ses propres données réelles — jamais une valeur nationale recopiée d'une région à l'autre. Le budget par défaut = médiane réelle du prix/loyer au m² de la région × 50 m² (arrondi) ; le plafond de trajet voiture = maximum réel observé dans la région ; le trajet par défaut = 1ᵉʳ quartile réel, choisi pour ne pas afficher toutes les communes ni aucune au premier chargement.

RégionCommunesDépartementsBureau par défautBudget achat/loc. par défautTC détaillé
Île-de-France1 2858La Défense, Puteaux153 000 € / 800 €Oui (IDFM)
Bretagne1 2024Gare de Rennes154 000 € / 520 €Non
Hauts-de-France3 7825Gare de Lille Flandres155 000 € / 540 €Non
Provence-Alpes-Côte d'Azur9616Gare de Marseille Saint-Charles176 000 € / 600 €Non
Centre-Val de Loire1 7546Gare d'Orléans133 000 € / 480 €Non
Bourgogne-Franche-Comté3 6858Gare de Dijon132 000 € / 460 €Non
Normandie2 6445Gare de Rouen Rive Droite152 000 € / 530 €Non
Grand Est5 11510Gare de Strasbourg124 000 € / 460 €Non
Pays de la Loire1 2285Gare de Nantes160 000 € / 470 €Non
Nouvelle-Aquitaine4 29312Gare de Bordeaux Saint-Jean150 000 € / 490 €Non
Occitanie4 44613Gare de Toulouse Matabiau142 000 € / 480 €Non
Auvergne-Rhône-Alpes4 03312Gare de Lyon Part-Dieu156 000 € / 500 €Non
Corse3602Centre-ville, Ajaccio179 000 € / 620 €Non
Île-de-France, cas particulier

Seule l'Île-de-France dispose d'une desserte détaillée (RER, Transilien, métro, tram, Grand Paris Express) et d'un temps de trajet en transport en commun calculé — grâce aux données IDFM, sans équivalent nationalement homogène ailleurs. Les 12 autres régions n'ont que la gare SNCF/TER la plus proche (desserte simple) et le temps de trajet voiture.

Corse

Aucune gare SNCF (réseau ferré insulaire hors périmètre du référentiel national utilisé) — la Corse est incluse avec une desserte ferroviaire vide plutôt qu'exclue du produit.

04 — Architecture

Architecture

Application 100 % statique : React consomme des fichiers JSON précalculés pour les indicateurs et scores par critère. Le client calcule le classement pondéré, les budgets et les filtres. Les seuls appels réseau à l'exécution servent le fond de carte, le géocodage d'une adresse personnalisée et le calcul de trajet voiture associé — des services publics, aucun backend propriétaire.

Sources Open data officiel DVF, INSEE, SSMSI, FINESS, Éducation nationale…
→
Pipeline Scripts Python pipeline/*.py --region <id>, exécutés à la main
→
Données figées JSON versionné src/data/<region>.json, dans Git
→
Client React (Vite) lit, filtre, trie — calcule le classement pondéré et les budgets
→
Service nginx / Cloud Run fichiers statiques, sans état
Multi-région — chargement à la demande

Embarquer les 13 régions au chargement initial ferait ~25 Mo de JS pour n'importe quel visiteur, même s'il ne consulte qu'une seule région — écarté après l'avoir mesuré en pratique. Les données lourdes (communes, gares, tracés) d'une région ne sont récupérées qu'au moment où elle est sélectionnée (import() dynamique, un module par région) ; seule une petite configuration légère (libellés, défauts) est chargée d'avance pour peupler le sélecteur.

Cas particulier — bureau personnalisé

Adresse saisie → géocodage (api-adresse.data.gouv.fr, repli nominatim.openstreetmap.org) → en Île-de-France, le transport en commun est résolu par lecture d'une matrice gare-à-gare précalculée une seule fois hors ligne (voir § Temps de trajet) → la voiture est recalculée en direct via l'API table d'OSRM public, pour toutes les régions. Aucune de ces étapes ne touche un serveur applicatif : tout part du navigateur.

Pile technique

CoucheChoix
FrameworkReact 19 + TypeScript, Vite
CarteLeaflet / react-leaflet, fond CartoDB Positron
GraphiquesRecharts
Service statiquenginx (image nginx:alpine)
BuildDocker multi-stage — node:20-alpine → nginx:alpine
HébergementGCP Cloud Run, scale-to-zero
Mesure d'audienceUmami, auto-hébergé sur Cloud Run, sans cookie, événements custom (voir § Déploiement)

05 — Provenance

Sources de données

Onze familles de sources publiques, chacune citée à l'endroit exact où elle est utilisée dans le code.

DonnéeSource
Communes, population, densité, coordonnéesAPI Géo (INSEE)
Prix immobilier vente €/m²Cerema DV3F (dérivé de DVF/DGFiP) ; Cerema ne publie rien sous 11 ventes : estimation à partir des communes voisines (signalée « ≈ »), puis repli sur la moyenne EPCI — absent structurellement en Alsace-Moselle (régime du Livre Foncier, pas une lacune de collecte)
Loyers estimés €/m²/moisANIL « Carte des loyers » — un modèle à partir d'annonces (leboncoin/SeLoger), pas une transaction officielle brute ; signalé « estimé » (impute) quand la commune manque d'annonces et que la valeur vient d'une zone élargie
Revenu médianINSEE FiLoSoFi
Accès à l'école (score Scolaire)Annuaire de l'Éducation (établissements géolocalisés, public et privé) : distance à l'école, au collège et au lycée général ou technologique les plus proches
Effectifs élèves (école + collège + lycée)4 jeux data.education.gouv.fr, rattachés à la commune via l'identifiant d'établissement (UAI) et non via leur propre champ commune — voir Limites connues
Délinquance enregistréeSSMSI, taux rapporté à la population résidente
Accès aux médecins généralistesDREES, APL (accessibilité potentielle localisée) aux médecins généralistes de 65 ans ou moins, millésime 2024 — activité réelle des médecins, âge de la population et communes voisines. Remplace FINESS, qui ne contient pas les médecins de ville
Accès aux chirurgiens-dentistesDREES, APL aux chirurgiens-dentistes, millésime 2024 (ETP pour 100 000 hab. standardisés) — affiché seul, ne participe pas au score Santé
Distance aux spécialistes (ophtalmo, pédiatre, gynéco) et aux urgencesINSEE, Base permanente des équipements (BPE) 2025, coordonnées précises par équipement — distance à vol d'oiseau au plus proche, même logique que les distances aux écoles. Ne participe pas au score Santé
Espace & nature (score absolu national)INSEE recensement 2021 (part de maisons), CORINE Land Cover 2018 (SDES : imperméabilisation, infrastructures, espaces naturels, agricoles ouverts et eau dans un rayon de 10 km), densité de population et sites de baignade surveillés du ministère chargé de la Santé
Gares (12 régions hors IDF)Référentiel national des gares SNCF/TER (data.gouv.fr) — gare la plus proche par commune, recherche non filtrée par département (communes frontalières)
Transport détaillé (Île-de-France)IDFM (open data GTFS + API PRIM/Navitia) ; tracés Grand Paris Express via SGP
Résultats municipales 2026Ministère de l'Intérieur — affichage informatif uniquement

Détail exact des formules dans l'app : panneau « Mes priorités » → « Comment sont calculés les scores et les chiffres ? ».

06 — Méthode

Méthodologie de score

Chaque score va de 0 à 10. La sécurité, la santé et les transports reposent sur des rangs percentiles régionaux, qui ne permettent pas une comparaison absolue entre régions. Le scolaire et « Espace & nature » utilisent chacun une formule absolue commune à toute la France. Une commune à 6/10 en sécurité peut être objectivement sûre : elle est simplement moins bien classée que d'autres sur cet indicateur précis, dans ce jeu de données précis, pour cette région précise.

transport

Transports

Île-de-France : lignes existantes ≤ 2 km + lignes futures (GPE) + bonus de proximité gare. Autres régions : proximité à la gare SNCF/TER la plus proche.

poids par défaut : 4/5
securite

Sécurité

Taux de délinquance SSMSI /1000 hab., rang inversé — 10 = taux le plus faible de la région. Détail par type de faits (cambriolages, violences physiques, violences sexuelles, vols contre les personnes, vols liés aux véhicules, dégradations) disponible dans la fiche de chaque commune.

poids par défaut : 4/5
sante

Santé

APL de la DREES aux médecins généralistes (médecins de 65 ans ou moins) : nombre de consultations de médecine générale accessibles par an et par habitant, en tenant compte de l'activité des médecins, de l'âge de la population et des communes voisines. Rang percentile régional. Ne couvre ni l'hôpital, ni les spécialistes (hors dentistes), ni les urgences. L'APL aux chirurgiens-dentistes (ETP pour 100 000 hab.) est aussi affiché dans la fiche de chaque commune, à titre indicatif — il ne participe pas au score Santé.

poids par défaut : 3/5
scolaire

Scolaire

Moitié accès, moitié qualité. Accès : distance à l'école, au collège et au lycée général ou technologique les plus proches (école 40 %, collège 40 %, lycée 20 %) ; 10 tant que l'école est à moins de 1 km, le collège à moins de 3 km et le lycée à moins de 5 km, puis décroissante jusqu'à 0 à 8, 20 et 30 km. Qualité : résultats des collèges (2/3) et lycées (1/3) proches, publiés par la DEPP (ministère de l'Éducation) sur les 3 dernières sessions : deux tiers de résultats bruts (taux de réussite au brevet et au bac, notes, mentions), un tiers de valeur ajoutée (les mêmes résultats comparés à ce qu'on attend vu le profil des élèves). Note absolue (indépendante des autres communes), pas un rang : le score brut est ensuite étiré linéairement pour couvrir 0-10 (brut 4,7 ou moins → 0 ; 8,2 ou plus → 10), ce qui le rend comparable en amplitude aux autres critères.

poids par défaut : 3/5
calme

Espace & nature

Indicateur composite absolu, commun à toute la France, équilibré en deux blocs. Cadre aéré (50 %) : densité sur une échelle logarithmique (35 % du bloc), part de maisons (20 %), faible imperméabilisation (25 %) et faible part d'infrastructures/zones d'activité (20 %). Accès à la nature (50 %) : espaces naturels, agricoles ouverts et eau dans la commune et les communes dont le centre est situé à moins de 10 km. La transformation à rendements décroissants valorise surtout le passage d'un environnement minéral à un accès réel à la nature. Une baignade surveillée peut renforcer jusqu'à 25 % de ce second bloc : plein effet à 3 km ou moins, aucun à partir de 15 km. Les sites intérieurs ne comptent comme nature que si le paysage proche comporte déjà au moins 20 % d'espaces ouverts ; les sites côtiers comptent toujours. Les seuils sont fixes et les deux sous-notes sont affichées dans la fiche commune.

poids par défaut : 3/5
score_pondéré = Σ(score_critère × poids_critère) / Σ(poids_critère)
Sécurité — biais connu

Le taux de délinquance reflète les faits enregistrés au lieu de commission, rapportés à la population résidente — mécaniquement plus élevé dans les pôles urbains/commerciaux et les communes-relais (gares), où une partie des faits touche des personnes de passage, pas les habitants.

Espace & nature — limites connues

Ce n'est pas une mesure de bruit (il n'existe pas de donnée de bruit uniforme pour toute la France : les cartes stratégiques ne couvrent que les grandes agglomérations et les grands axes) mais un indicateur d'environnement résidentiel. CORINE est trop grossier pour distinguer tous les jardins privés ou les différences entre quartiers. La nature proche est estimée depuis les centroïdes communaux, pas depuis la distance exacte aux lisières, plages ou plans d'eau. Le composant infrastructure pénalise les zones d'activité et grands axes présents dans la commune, mais pas leur bruit réel ni toutes les nuisances des communes voisines. Les espaces agricoles contribuent moins que les forêts, zones humides et surfaces en eau. La baignade repose sur la distance à vol d'oiseau depuis le centre communal et ne couvre que les sites faisant l'objet d'un suivi sanitaire : une petite plage ou un point de baignade non surveillé peut donc manquer.

Scolaire — limites connues

Les résultats bruts reflètent en partie le milieu social des élèves, et les établissements privés sous contrat, qui sélectionnent leurs élèves, sont inclus. La valeur ajoutée (résultats comparés à l'attendu) sert de contrepoids, mais ne peut pas être utilisée seule : le taux de réussite étant plafonné à 100 %, elle pénalise structurellement les établissements déjà très performants, et un score fondé sur elle seule plaçait Saint-Denis (60-70 % de réussite au brevet) devant Paris 16e (95-100 %). Aucun indicateur national équivalent n'existe pour les écoles primaires ; le score ne couvre ni le climat scolaire, ni les options, et le secteur de la carte scolaire n'est pas publié (on moyenne les collèges à moins de 15 km, les 4 plus proches, pondérés par la distance). Les valeurs annuelles sont bruitées (corrélation de 0,5 à 0,65 d'une année à l'autre pour la valeur ajoutée), d'où la moyenne sur 3 sessions et l'exclusion des établissements de moins de 60 candidats cumulés. La distance est à vol d'oiseau depuis le centre de la commune : dans une grande commune rurale, le bourg peut en être éloigné. Avant sa refonte, le score rapportait les élèves scolarisés dans la commune à sa population : un village accueillant un internat (Beaucamps-Ligny, 845 habitants, 2 984 élèves) obtenait 10/10, et les 40 % de communes sans établissement propre obtenaient 0/10.

07 — Accessibilité

Budget & accessibilité

Pour chaque commune, deux contraintes sont comparées au budget disponible, et la plus exigeante des deux détermine le verdict « accessible / hors budget » :

  • Coût de la surface visée — surface souhaitée × prix médian au m² de la commune.
  • Ticket d'entrée réel — 1ᵉʳ quartile des ventes DVF du type de bien, pour ne pas suggérer une commune où le bien le moins cher du marché dépasse déjà le budget (terrain inclus pour les maisons).
Budget par défaut, par région

Le budget d'achat par défaut = médiane réelle du prix/m² appartement de la région × 50 m² (arrondi au millier) ; le loyer par défaut suit la même logique sur la médiane des loyers. Calculé une fois par région lors du pipeline, jamais une valeur nationale unique recopiée partout — voir le tableau en § Régions couvertes.

Étiquette prix affichéeSignification
Prix communeMédiane des ventes récentes à l'échelle communale (Cerema DV3F)
Prix « ≈ estimé d'après les communes voisines »Commune à moins de 11 ventes sur 2023-2025 : Cerema ne publie pas de médiane (secret statistique), et une médiane sur si peu de ventes serait de toute façon trop instable (une seule vente peut la faire varier de 50 % ou plus). Moyenne pondérée (nombre de ventes, proximité) des 3 à 4 communes voisines les plus proches ayant un prix publié — rayon 15 km pour les appartements, 20 km pour les maisons. Précision mesurée en leave-one-out sur les communes à 20 ventes ou plus : environ ±20 % (appartements), ±13 % (maisons), contre ±26 % / ±17 % pour la moyenne EPCI sur le même échantillon. Signalé « ≈ » dans le classement et la fiche détail
Prix « moyenne intercommunale »Dernier repli, à l'échelle de l'EPCI, quand la commune a quelques ventes (moins de 11) et que ses voisines ne suffisent pas à l'estimer — signalé dans la fiche détail et le classement. Jamais appliqué à une commune sans aucune vente de ce type de bien
N/CAucune vente de ce type de bien sur 2023-2025 dans la commune (village sans appartement, par exemple), ou ni ses voisines ni son EPCI n'ont assez de ventes fiables, ou région structurellement hors périmètre DVF (Alsace-Moselle)

08 — Mobilité

Temps de trajet

Le calcul diffère selon la région et selon qu'on utilise le bureau par défaut ou une adresse personnalisée.

Voiture — toutes régions

Pour le bureau par défaut, temps précalculés une fois pour toutes via l'API table d'OSRM. Pour une adresse personnalisée, recalcul en direct via la même API, par lots, entièrement côté client.

Transport en commun — Île-de-France uniquement

Pour tous les bureaux, y compris celui par défaut, l'application consulte côté client une matrice gare-à-gare précalculée (officeMatrix.json, issue de Motis avec GTFS IDFM et OSM). Aucun appel Navitia n'est effectué à l'exécution. Si le bureau et la commune sont rattachés à la même gare, le temps est estimé à pied à 4,5 km/h depuis leur distance à vol d'oiseau. Le script PRIM/Navitia fournit les données de référence, mais ne régénère pas cette matrice ; son générateur historique n'est pas inclus dans le dépôt. Les 12 autres régions n'ont pas cette donnée : le filtre et l'affichage « trajet transport » restent masqués pour elles (voir Régions couvertes).

Seuils de filtre, par région

Le plafond du curseur « trajet voiture max » = le temps de trajet réel le plus élevé observé dans la région ; sa valeur par défaut = le 1ᵉʳ quartile réel de la distribution — choisi pour que le premier affichage ne montre ni toutes les communes, ni aucune. Les deux sont recalculés par région, pas une valeur globale.

Hors trafic

Le temps voiture est calculé par OSRM en conditions fluides — à majorer nettement aux heures de pointe.

09 — Comparaison personnelle

Favoris

Une étoile sur chaque commune du classement (ou dans la popup de la carte, ouverte au clic) l'ajoute à une liste de favoris. C'est un instantané des critères au moment du clic, pas une référence vivante — changer ensuite de budget ou de pondération ne modifie pas rétroactivement un favori déjà sauvegardé, pour rester comparable entre deux ajouts faits à des moments différents.

  • Sélection de plage — Maj+clic sur une étoile favorise toute la plage entre le dernier clic et celui-ci (ordre d'affichage courant).
  • Persistance — stockée en local (localStorage), globale entre régions : possible de comparer une commune bretonne à une commune provençale dans la même liste.
  • Copier la liste — copie les noms au format commune; commune; … dans le presse-papier, pour coller directement dans une recherche ailleurs.
  • Export CSV — disponible sur le tableau de classement (liste affichée, selon les filtres/tri actifs), avec BOM UTF-8 pour un affichage correct des accents dans Excel.

Aucune de ces données ne quitte le navigateur : pas de compte, pas de synchronisation serveur.

10 — Fabrication des données

Pipeline de données

Scripts Python, exécutés à la main et versionnés dans pipeline/. Aucun ne tourne en production — ils produisent les fichiers JSON que l'app consomme ensuite tels quels. La plupart acceptent un flag --region (dict REGIONS local par script ; passer explicitement la région, car le défaut est idf ou toutes les régions selon le script) plutôt qu'un module partagé — pas d'abstraction commune pour 13 régions seulement, jugée prématurée.

ScriptRôle--region
fetch_idf.pyBase : population, revenu, écoles, délinquance, santé, prix DVF initial, calcul des scoresoui
fetch_transport.pyGares/lignes IDFM existantes + score transport (Île-de-France uniquement)non (IDF)
fetch_gares.pyGare SNCF/TER la plus proche, régions sans équivalent IDFMoui
fetch_gares_bretagne.pyVariante historique de fetch_gares.py, gardée pour mémoirebretagne
fetch_trajet.pyTemps de trajet voiture (OSRM) vers le bureau par défaut de la régionoui
fetch_trajet_transport.pyTemps de trajet transport en commun (PRIM/Navitia, Île-de-France uniquement)non (IDF)
enrich_prix_cerema.pyPrix immobilier affinés (Cerema DV3F, échelle commune)oui
enrich_prix_neuf_ancien.pyDistinction neuf/VEFA vs ancien (appartements)oui
enrich_prix_voisins.pyCommunes masquées par Cerema (moins de 11 ventes) : estimation à partir des communes voisines (après neuf/ancien, avant le repli EPCI)oui
enrich_prix_epci_fallback.pyRepli EPCI pour les communes sans donnée exploitableoui
enrich_loyers.pyLoyers estimés (ANIL « Carte des loyers »)oui
enrich_effectifs.pyEffectifs élèves réels (rattachement par UAI) ; son score scolaire est remplacé par enrich_scolaire_acces.pyoui
enrich_scolaire_acces.pyAccès à l'école (distances à l'école, au collège et au lycée les plus proches), après enrich_effectifs.pyoui
enrich_scolaire_qualite.pyScore Scolaire final = 50 % accès + 50 % qualité (résultats bruts et valeur ajoutée DEPP des collèges et lycées proches), après enrich_scolaire_acces.pyoui
fetch_politique.pyNuance politique municipale 2026 (affichage seul)oui
enrich_code_postal.pyCode postal de chaque commune (API Géo), pour le lien vers les annonces de la ficheoui
enrich_quietude.pyScore absolu Espace & nature : 50 % Cadre aéré et 50 % Accès à la nature, à partir de CORINE, du recensement INSEE et des sites de baignade surveillés du ministère chargé de la Santéoui
enrich_sante_apl.pyScore Santé = APL DREES aux médecins généralistes (remplace l'ancien score FINESS)oui
fix_delinquance.pyCorrectif ponctuel — recalcule le taux/score sécuritéoui
build_gpe_lines.pyTracés Grand Paris Express et prolongements (Île-de-France)non (IDF)
build_transit_lines.pyTracés RER/Transilien/métro/tram existants (Île-de-France)non (IDF)

Ordre d'exécution type pour une région : base → prix → neuf/ancien → voisines (<11 ventes) → repli EPCI → loyers → trajet voiture → gares → politique (optionnel) → effectifs scolaires → espace et nature → santé APL → accès scolaire → qualité scolaire. Relancer la base impose de refaire les enrichissements ; relancer les effectifs ou l'accès seul écrase le score scolaire final. L'Île-de-France utilise la desserte IDFM à la place des gares SNCF/TER et ajoute les données de référence transport et les tracés.

11 — Mise en ligne

Déploiement, coût & mesure

L'app tourne sur GCP Cloud Run, servie par nginx à partir d'un build statique — pas de serveur applicatif, pas de base de données. Le service redescend à zéro instance (donc zéro coût) en l'absence de trafic.

Sécurité HTTP

En-têtes appliqués par nginx sur chaque réponse, CSP mise à jour à chaque nouveau domaine externe appelé :

X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; img-src 'self' data: https://*.basemaps.cartocdn.com;
  style-src 'self' 'unsafe-inline'; script-src 'self' https://umami-326325825186.europe-west1.run.app;
  connect-src 'self' https://api-adresse.data.gouv.fr https://router.project-osrm.org
  https://nominatim.openstreetmap.org https://umami-326325825186.europe-west1.run.app;
  object-src 'none'; base-uri 'self'; frame-ancestors 'none'

Les fichiers de /assets/ (noms fingerprintés par Vite) sont mis en cache navigateur « pour toujours » (immutable, max-age=31536000) — un visiteur qui revient ne re-télécharge rien.

Référencement

robots.txt et sitemap.xml réels (pas un fallback SPA qui renvoie la page d'accueil), données structurées JSON-LD (WebApplication) et URL canonique dans index.html. Le <title>/<meta description> statiques sont ceux lus par les robots qui n'exécutent pas de JavaScript (la plupart des robots IA, notamment) — distincts du titre dynamique mis à jour par région une fois l'app chargée. Le build pré-rend aussi 13 pages région, 96 pages département, 482 fiches communales sélectionnées selon un seuil de qualité et 3 guides dédiés à Paris, Lyon et Marseille, avec fil d'Ariane, canonical, JSON-LD et maillage interne.

Mesure d'audience

Umami auto-hébergé, sans cookie, sans donnée personnelle. Au-delà du pageview automatique, des événements custom sont envoyés sur les décisions produit réelles, avec debounce sur les curseurs continus pour ne pas spammer un événement par pixel glissé :

ÉvénementDéclenché par
region_changeChangement de région
budget_mode_changeBascule achat / location
weight_changeAjustement d'un curseur de critère (débounce)
filter_changeDépartements, trajet voiture/transport, distance gare, masquer hors-budget (débounce sur les curseurs)

Coût récurrent

PosteCoût
Cloud Run — app + Umami (facturé à l'usage, scale-to-zero)quasi gratuit
Base Postgres (Umami) — Neon, tier gratuitgratuit
Géocodage BAN/Nominatim, OSRM public, IDFM PRIM (quota gratuit)gratuit
Domaine habitoscope.fr~7–9 €/an
Budget de facturation

Alerte GCP à 8 €/mois (seuils 50/90/100 %), volontairement sans arrêt automatique de la facturation — un tel mécanisme couperait tout le projet, donc le site en production, en même temps que la dépense.

Domaine personnalisé

habitoscope.fr est mappé directement sur Cloud Run, sans intermédiaire — certificat HTTPS géré et renouvelé automatiquement par Google, redirection www → domaine nu.

12 — Lucidité

Limites connues

Rangs relatifs, pas absolus

La sécurité, la santé et les transports reposent sur des rangs au sein de leur région : le classement pondéré ne constitue donc pas une comparaison absolue entre régions. Le scolaire et « Espace & nature » utilisent chacun une formule absolue commune, décrite dans la méthodologie.

Santé

Le score Santé ne mesure que l'accès aux médecins généralistes (APL) : ni hôpital, ni spécialistes, ni urgences. Ces données existent maintenant à part dans la fiche commune (dentistes via APL, ophtalmo/pédiatre/gynéco/urgences via distance BPE) mais ne participent pas au score — ce sont des distances à vol d'oiseau au plus proche équipement, pas un indicateur pondéré par l'activité comme l'APL. Avant la mise en place de l'APL, le score comptait les établissements FINESS par habitant (surtout pharmacies, EHPAD, services d'aide à domicile, sans les médecins de ville), ce qui donnait 9,97/10 à un village de 152 habitants avec un seul établissement.

Sécurité, espace & nature, scolaire

Biais structurels documentés en détail en § Méthodologie de score — communes-relais mécaniquement mal notées en sécurité, différences entre quartiers mal visibles dans Espace & nature, distance à vol d'oiseau depuis le centre de la commune pour le score scolaire.

Loyers modélisés

La donnée ANIL est un modèle basé sur des annonces, pas une transaction officielle brute comme le DVF — voir Sources de données.

Alsace-Moselle

Aucun prix de vente DVF disponible (régime du Livre Foncier) — les communes des départements 57/67/68 affichent systématiquement « N/C » pour les prix.

Trajet voiture

Calculé par OSRM hors trafic (conditions fluides) — à majorer nettement aux heures de pointe.

Charge sur OSRM/Nominatim publics

Le géocodage et le calcul voiture d'une adresse personnalisée dépendent de serveurs de démonstration publics, non prévus pour un usage production intensif à grande échelle — à auto-héberger si le trafic devenait significatif.

Prix immobilier

Secret statistique en dessous d'un certain nombre de ventes : certaines communes (souvent rurales) affichent « N/C » faute d'échantillon fiable, même au niveau EPCI.

Transport en commun hors Île-de-France

Pas de source open data nationale homogène équivalente à IDFM — 12 régions sur 13 n'ont que la gare la plus proche, sans temps de trajet transport calculé.

Nuance politique

Affichage informatif uniquement, volontairement exclu des scores pondérés. Absente (N/C) pour les communes de moins de 3 500 habitants, où le Ministère de l'Intérieur n'attribue pas de nuance officielle.

13 — Reproductibilité

Reproduire en local

Application

cd ou-habiter-app
npm install
npm run dev      # http://localhost:5173
npm run build    # build de prod dans dist/

Pipeline de données

Depuis la racine du projet. Nécessite Python 3 et openpyxl ; certains jeux de données (Cerema DV3F, jeton PRIM pour l'Île-de-France) sont à récupérer manuellement — voir le README racine pour le détail. Exemple pour une région hors IDF (Bretagne) :

python3 pipeline/fetch_idf.py --region bretagne
python3 pipeline/enrich_prix_cerema.py --region bretagne
python3 pipeline/enrich_prix_neuf_ancien.py --region bretagne
python3 pipeline/enrich_prix_voisins.py --region bretagne
python3 pipeline/enrich_prix_epci_fallback.py --region bretagne
python3 pipeline/enrich_loyers.py data/loyer_appartement.csv data/loyer_maison.csv --region bretagne
python3 pipeline/fetch_trajet.py --region bretagne
python3 pipeline/fetch_gares.py --region bretagne
python3 pipeline/enrich_effectifs.py --region bretagne
python3 pipeline/enrich_quietude.py --region bretagne
python3 pipeline/enrich_sante_apl.py --region bretagne
python3 pipeline/enrich_scolaire_acces.py --region bretagne
python3 pipeline/enrich_scolaire_qualite.py --region bretagne

14 — Repère

Structure du dépôt

ou-habiter-app/ # application React/Vite/TypeScript src/ App.tsx # composition de l'UI, état, calculs live office.ts # géocodage BAN/Nominatim, matrice transport, OSRM live scoring.ts # pondération des scores, évaluation budget favorites.ts # favoris : type, stockage local, instantané analytics.ts # événements Umami, debounce csv.ts # génération/téléchargement CSV côté client components/ # CommuneMap, RankingTable, BudgetPanel, WeightPanel, FavoritesPanel… data/ regions.ts # RegionMeta/RegionData/REGION_META, chargement à la demande communes.ts # types Commune, normalizeCommunes() <region>.json # une région = un fichier (idf.json, bretagne.json…) public/ docs.html # cette page robots.txt, sitemap.xml # référencement Dockerfile # build multi-stage node → nginx nginx.conf # en-têtes de sécurité, cache /assets/ pipeline/ # scripts Python --region <id> — production des données legacy/ # scripts superseded, gardés pour mémoire