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).
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égion | Communes | Départements | Bureau par défaut | Budget achat/loc. par défaut | TC détaillé |
|---|---|---|---|---|---|
| Île-de-France | 1 285 | 8 | La Défense, Puteaux | 153 000 € / 800 € | Oui (IDFM) |
| Bretagne | 1 202 | 4 | Gare de Rennes | 154 000 € / 520 € | Non |
| Hauts-de-France | 3 782 | 5 | Gare de Lille Flandres | 155 000 € / 540 € | Non |
| Provence-Alpes-Côte d'Azur | 961 | 6 | Gare de Marseille Saint-Charles | 176 000 € / 600 € | Non |
| Centre-Val de Loire | 1 754 | 6 | Gare d'Orléans | 133 000 € / 480 € | Non |
| Bourgogne-Franche-Comté | 3 685 | 8 | Gare de Dijon | 132 000 € / 460 € | Non |
| Normandie | 2 644 | 5 | Gare de Rouen Rive Droite | 152 000 € / 530 € | Non |
| Grand Est | 5 115 | 10 | Gare de Strasbourg | 124 000 € / 460 € | Non |
| Pays de la Loire | 1 228 | 5 | Gare de Nantes | 160 000 € / 470 € | Non |
| Nouvelle-Aquitaine | 4 293 | 12 | Gare de Bordeaux Saint-Jean | 150 000 € / 490 € | Non |
| Occitanie | 4 446 | 13 | Gare de Toulouse Matabiau | 142 000 € / 480 € | Non |
| Auvergne-Rhône-Alpes | 4 033 | 12 | Gare de Lyon Part-Dieu | 156 000 € / 500 € | Non |
| Corse | 360 | 2 | Centre-ville, Ajaccio | 179 000 € / 620 € | Non |
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.
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.
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.
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
| Couche | Choix |
|---|---|
| Framework | React 19 + TypeScript, Vite |
| Carte | Leaflet / react-leaflet, fond CartoDB Positron |
| Graphiques | Recharts |
| Service statique | nginx (image nginx:alpine) |
| Build | Docker multi-stage — node:20-alpine → nginx:alpine |
| Hébergement | GCP Cloud Run, scale-to-zero |
| Mesure d'audience | Umami, 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ée | Source |
|---|---|
| Communes, population, densité, coordonnées | API 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²/mois | ANIL « 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édian | INSEE 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ée | SSMSI, taux rapporté à la population résidente |
| Accès aux médecins généralistes | DREES, 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-dentistes | DREES, 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 urgences | INSEE, 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 2026 | Ministè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.
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/5Sé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/5Santé
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/5Scolaire
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/5Espace & 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/5score_pondéré = Σ(score_critère × poids_critère) / Σ(poids_critère)
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.
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.
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).
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ée | Signification |
|---|---|
| Prix commune | Mé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/C | Aucune 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).
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.
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.
| Script | Rôle | --region |
|---|---|---|
| fetch_idf.py | Base : population, revenu, écoles, délinquance, santé, prix DVF initial, calcul des scores | oui |
| fetch_transport.py | Gares/lignes IDFM existantes + score transport (Île-de-France uniquement) | non (IDF) |
| fetch_gares.py | Gare SNCF/TER la plus proche, régions sans équivalent IDFM | oui |
| fetch_gares_bretagne.py | Variante historique de fetch_gares.py, gardée pour mémoire | bretagne |
| fetch_trajet.py | Temps de trajet voiture (OSRM) vers le bureau par défaut de la région | oui |
| fetch_trajet_transport.py | Temps de trajet transport en commun (PRIM/Navitia, Île-de-France uniquement) | non (IDF) |
| enrich_prix_cerema.py | Prix immobilier affinés (Cerema DV3F, échelle commune) | oui |
| enrich_prix_neuf_ancien.py | Distinction neuf/VEFA vs ancien (appartements) | oui |
| enrich_prix_voisins.py | Communes 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.py | Repli EPCI pour les communes sans donnée exploitable | oui |
| enrich_loyers.py | Loyers estimés (ANIL « Carte des loyers ») | oui |
| enrich_effectifs.py | Effectifs élèves réels (rattachement par UAI) ; son score scolaire est remplacé par enrich_scolaire_acces.py | oui |
| enrich_scolaire_acces.py | Accès à l'école (distances à l'école, au collège et au lycée les plus proches), après enrich_effectifs.py | oui |
| enrich_scolaire_qualite.py | Score 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.py | oui |
| fetch_politique.py | Nuance politique municipale 2026 (affichage seul) | oui |
| enrich_code_postal.py | Code postal de chaque commune (API Géo), pour le lien vers les annonces de la fiche | oui |
| enrich_quietude.py | Score 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.py | Score Santé = APL DREES aux médecins généralistes (remplace l'ancien score FINESS) | oui |
| fix_delinquance.py | Correctif ponctuel — recalcule le taux/score sécurité | oui |
| build_gpe_lines.py | Tracés Grand Paris Express et prolongements (Île-de-France) | non (IDF) |
| build_transit_lines.py | Tracé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énement | Déclenché par |
|---|---|
| region_change | Changement de région |
| budget_mode_change | Bascule achat / location |
| weight_change | Ajustement d'un curseur de critère (débounce) |
| filter_change | Départements, trajet voiture/transport, distance gare, masquer hors-budget (débounce sur les curseurs) |
Coût récurrent
| Poste | Coût |
|---|---|
| Cloud Run — app + Umami (facturé à l'usage, scale-to-zero) | quasi gratuit |
| Base Postgres (Umami) — Neon, tier gratuit | gratuit |
| Géocodage BAN/Nominatim, OSRM public, IDFM PRIM (quota gratuit) | gratuit |
| Domaine habitoscope.fr | ~7–9 €/an |
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
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.
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.
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.
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.
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.
Calculé par OSRM hors trafic (conditions fluides) — à majorer nettement aux heures de pointe.
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.
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.
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é.
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