Arnaud 5633969d98 Version initiale — carte des établissements scolaires
Carte Leaflet (annuaire Éducation nationale) + API PHP/MySQL :
fiche établissement (effectifs, IPS, population INSEE, DPE ADEME),
GED documents/photos, commentaires par groupe, gestion utilisateurs
et politique de mots de passe. Imports opendata (effectifs, IPS,
population, DPE, IRIS).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:35:41 +02:00

Carte des établissements scolaires (France)

Carte interactive OpenStreetMap / Leaflet des ~68 500 établissements de l'annuaire de l'Éducation nationale, avec filtres côté client.

Source des données : data.education.gouv.fr — annuaire-education.

Lancer

L'application nécessite PHP + MySQL (la carte et la GED sont servies par une API PHP qui lit la base ; pas de fichier file://).

1. Initialiser la base (une seule fois)

MySQL doit tourner (Laragon le démarre). Puis :

php setup.php           # crée la base « ecoles », les tables, importe les établissements
php setup.php --reset   # réimporte les établissements depuis data/etablissements.json

Identifiants par défaut (Laragon : root / mot de passe vide) dans api/db.php.

2. Servir l'application

Avec Laragon : place le projet dans www/, puis ouvre http://ecoles.test/ (ou http://localhost/ecoles/).

Sans Laragon, serveur PHP intégré :

php -S 127.0.0.1:8000

puis ouvre http://127.0.0.1:8000/.

Fiche établissement

Un clic sur un point → « Voir la fiche complète » ouvre une fiche plein écran (overlay superposé à la carte, donc le retour ne recharge pas la carte) avec quatre onglets :

  • Informations — toutes les données de l'établissement + bouton « Localiser sur la carte », les effectifs, et le fil de commentaires de l'établissement (voir Groupes & commentaires)
  • Documents — arborescence de dossiers + fichiers / liens / photos validés
  • Photothèque — galerie des photos validées
  • Proposer un élément — tout internaute peut soumettre un fichier, une photo ou un lien

Les éléments sont stockés sur disque dans uploads/, leurs métadonnées en base (table items, type file / photo / link), indexés par UAI et rangés dans l'arborescence globale (folders).

Chaque document/photo de l'onglet Documents affiche, en plus de son libellé, sa date de dépôt et une description (texte explicatif). La description est saisie par le proposant au dépôt et modifiable ensuite par les valideurs (bouton ✏️ visible sur la fiche quand on est connecté avec un droit de validation sur l'établissement). Chaque document a son fil de commentaires dépliable (bouton 💬), soumis aux mêmes règles que les commentaires d'établissement (visibilité par groupe, dépôt réservé aux membres d'un groupe, suppression par l'auteur ou le super-admin) — voir Groupes & commentaires. Les commentaires d'un document et ceux de l'établissement restent distincts (comments.item_id).

Droits & modération

Trois niveaux :

Rôle Droits
Internaute (anonyme) Proposer des éléments (statut « en attente »)
Commentateur Compte sans droit de modération : participe aux commentaires des fiches via ses groupes
Admin de périmètre Valider/rejeter les éléments des communes de son périmètre (commune + département) ou de ses groupes valideurs ; commente aussi
Super-admin (plateforme) Tout valider, gérer l'arborescence, créer les utilisateurs et les groupes, importer l'opendata
  • Seuls les éléments validés sont visibles du public.
  • Espace d'administration : admin.html (lien en bas de la barre latérale).
  • Super-admin par défaut créé par setup.php : admin / adminà changer.
  • L'import opendata (bouton dans l'admin) télécharge la dernière version depuis data.education.gouv.fr et met la base à jour (accès Internet requis côté serveur).

Groupes d'utilisateurs & commentaires

Le super-admin organise les utilisateurs en groupes (section Groupes d'utilisateurs de l'admin) :

  • Un groupe marqué valideur donne à ses membres le droit de valider les documents des établissements de ses communes (même modèle de périmètre que les admins de périmètre, mais porté au niveau du groupe). Un utilisateur peut appartenir à plusieurs groupes ; ses droits de validation sont l'union de ceux de ses groupes valideurs.
  • Tables : user_groups (nom + can_validate), group_members (appartenance n..n), group_scopes (communes du groupe valideur). Le contrôle est centralisé dans can_validate_uai() (api/db.php), qui accepte super-admin, périmètre personnel (legacy) ou groupe valideur. API : api/groups.php (super-admin).

Commentaires de fiche — affichés directement dans l'onglet Informations :

  • Un commentaire est horodaté (date + heure), porte un texte et le nom de son auteur (table comments).
  • Il n'est visible qu'entre membres d'un même groupe que l'auteur ; le super-admin voit tout. Déposer un commentaire nécessite d'appartenir à au moins un groupe.
  • L'auteur (et le super-admin) peuvent supprimer un commentaire ; pas d'édition (horodatage figé). API : api/comments.php.
  • La connexion se fait directement dans la carte commentaires (mini-formulaire), la session étant partagée avec l'espace d'administration.
  • Pour les participants qui ne doivent pas modérer, le super-admin crée des comptes de rôle Commentateur (section Utilisateurs de l'admin) : ils se connectent et commentent via leurs groupes, sans accès à la file de validation.

Sécurité des mots de passe

  • Hachage : bcrypt (password_hash / PASSWORD_DEFAULT), vérifié par password_verify. Aucun mot de passe n'est stocké ni transmis en clair.
  • Politique de complexité (password_policy_check, api/db.php) : ≥ 8 caractères et au moins 3 types parmi minuscule / majuscule / chiffre / caractère spécial. Appliquée à la création, à la réinitialisation et au changement. Côté navigateur, une jauge de robustesse (pwpolicy.js) guide la saisie.
  • Changement forcé : les comptes créés par un admin (et admin/admin par défaut) sont marqués must_change → l'utilisateur doit choisir un nouveau mot de passe à sa première connexion (account.html?forced=1).
  • Self-service : tout utilisateur connecté peut changer son mot de passe via account.html (saisie du mot de passe actuel).
  • Mot de passe oublié : depuis l'écran de connexion → forgot.html (identifiant ou e-mail). Un lien à usage unique (valable 1 h) est envoyé par e-mail et consommé sur reset.html. Table password_resets (jeton stocké haché en SHA-256).
  • Anti-bruteforce : après 5 échecs de connexion, le compte est verrouillé 15 min (users.failed_attempts / locked_until).
  • Envoi d'e-mails — transport configurable dans api/db.php (MAIL_TRANSPORT) :
    • log (par défaut) : écrit l'e-mail (dont le lien) dans private/mail_outbox.log (dossier protégé par .htaccess) — pratique en local sans serveur de mail ;
    • mail : fonction PHP mail() ;
    • smtp : client SMTP intégré (renseigner SMTP_HOST/PORT/USER/PASS/SECURE). Pensez à ajuster APP_BASE_URL pour que les liens de réinitialisation soient corrects.

Couche IRIS & population

  • Fond clair (CartoDB Positron) par défaut ; sélecteur de couches en haut à droite.
  • Couche Contours IRIS (WFS Géoplateforme STATISTICALUNITS.IRIS:contours_iris), chargée par zone visible (zoom ≥ 11).
  • IRIS cliquables : un clic sur une zone affiche son nom, sa commune, son type, et la population du recensement INSEE 2022 (de l'IRIS et de la commune).
  • Populations importées via php import_pop.php dans la table iris_pop (population communale = somme des IRIS de la commune).
  • Bouton « 📍 Me localiser » + centrage automatique sur la position de l'internaute (nécessite HTTPS ou localhost).
  • Coloration par densité : chaque IRIS est colorié selon sa densité (population ÷ surface), avec une légende ; un clic affiche population de l'IRIS, densité, et population de la commune.

Effectifs des écoles (1er degré)

  • Dataset fr-en-ecoles-effectifs-nb_classes importé via php import_effectifs.php dans la table effectifs (uai, année → élèves, classes ; ~809 000 lignes, 55 360 écoles).
  • La fiche d'une école / collège / lycée affiche, dans l'onglet Informations, ses derniers effectifs (élèves ; classes pour le 1er degré) et un graphique d'évolution sur tout l'historique connu. (1er degré depuis 2009 ; collèges/lycées via leurs datasets dédiés.)
  • Une carte voisine montre les effectifs cumulés des écoles publiques de la commune (somme des élèves des établissements publics de la commune, par année) pour situer l'établissement dans son contexte local. API : api/effectifs.php?action=commune&uai=….
  • À côté, un graphique de l'IPS (indice de position sociale) de l'établissement. Trois datasets importés via php import_ips.php dans la table ips (clé uai + année) : fr-en-ips-ecoles-ap2022 (écoles, rentrées 2022→2024, + référence départementale), fr-en-ips-colleges-ap2022 (collèges) et fr-en-ips-lycees-ap2022 (lycées, IPS ensemble GT+Pro). Pour les écoles, la courbe de l'établissement est tracée avec la référence départementale (même échelle) ; collèges et lycées n'ont qu'une rentrée (2022-2023) pour l'instant, donc un point. API : api/ips.php?uai=…. Un IPS élevé reflète un milieu social plus favorisé (référence nationale ≈ 100). php import_ips.php --niveau=ecoles|colleges|lycees permet d'importer un seul niveau.
  • À côté de cette courbe, un graphique d'évolution de la population (INSEE) sur les recensements successifs (1968 → 2022, 9 dates) au niveau de la commune — ou de l'arrondissement pour Paris/Lyon/Marseille (déduit du code INSEE comc). Pour un arrondissement, une 2ᵉ courbe « ville entière » donne en plus l'évolution de la ville complète. Source : INSEE, base communale séries historiques du recensement (https://www.insee.fr/fr/statistiques/fichier/8582555/base-cc-serie-historique-2022_csv.zip), qui contient le vrai historique par arrondissement (Paris 75101…, Lyon 69381…, Marseille 13201…) ainsi que les totaux communes, importée via php import_pop_insee.php dans la table pop_commune. API : api/population.php?uai=…{zone, zone_estimated, ville, ville_nom}.
    • L'importeur lit le CSV dans le zip sans l'extension PHP zip (mini-lecteur ZIP + gzinflate). zone_estimated n'est utilisé qu'en repli (zone sans historique propre) : population de la ville × part de l'arrondissement dans les IRIS 2022.
    • import_pop_hist.php reste une alternative (CSV Opendatasoft, s'arrête ~2014, sans arrondissements).

Performance énergétique (DPE — ADEME)

  • Dataset dpe01tertiaire (DPE des bâtiments tertiaires depuis juillet 2021, ~532 000 diagnostics), importé via l'API data-fair de l'ADEME.

  • ⚠ Ce jeu de données n'a pas d'identifiant école (UAI) : les bâtiments sont repérés par adresse BAN + géolocalisation. Le rattachement école↔DPE est donc géographique (plus proche bâtiment de la même commune, sous 5 m par défaut — soit le même bâtiment / la même adresse), comme le rattachement école→IRIS. La distance affichée mesure la fiabilité du lien ; --radius=N permet d'élargir.

  • Import en deux temps :

    php import_dpe.php            # remplit la table « dpe » (long ; --dep=75 pour tester)
    php import_comc.php           # (si pas déjà fait) code INSEE commune des écoles
    php match_dpe.php             # rattache les DPE aux écoles (table « etab_dpe » ; --radius=N)
    
  • La fiche d'un établissement affiche, dans l'onglet Informations, le ou les DPE rattachés : étiquettes Énergie et GES (A→G, palette officielle), consommation, émissions, surface, dates et adresse du bâtiment.

  • Coloration de la carte : un sélecteur dans la légende (en bas à droite) permet de colorer les marqueurs par type d'établissement (défaut) ou par étiquette DPE (A→G ; gris = aucun DPE rattaché). Les étiquettes du sous-ensemble affiché sont chargées à la demande via api/dpe.php?action=latest.

  • Import depuis l'admin : un super-admin peut lancer l'import + le rattachement depuis admin.html (section Performance énergétique (DPE)), avec un département optionnel et un rayon réglable. Opération longue ⇒ pour le dataset complet, préférez les scripts CLI ci-dessus.

  • API : api/dpe.php?uai=… (liste triée par distance) et ?action=latest (batch, meilleur match par UAI). Logique d'import/rattachement partagée dans api/dpe_lib.php.

Coloration des IRIS (2 modes)

Quand la couche IRIS est active, un sélecteur (en bas à gauche) permet de choisir :

  1. Densité de population — population 2022 ÷ surface de l'IRIS (palette jaune→rouge).
  2. Taux d'écoliers publicsà l'échelle de l'IRIS : élèves des écoles publiques (1er degré, dernière rentrée) situées dans l'IRIS ÷ population de l'IRIS, en palette verte (vert pâle = faible taux → vert foncé = fort taux). Chaque école est rattachée à l'IRIS qui la contient (point-dans-polygone, côté navigateur).

Chaque mode a sa propre légende. Données : api/iris.php (population), api/effectifs.php?action=latest (effectifs des écoles). Le rattachement école→IRIS et le calcul du taux se font côté client sur la zone visible.

Filtres disponibles

  • Recherche texte (nom d'établissement / commune), insensible aux accents
  • Type d'établissement (École, Collège, Lycée, Médico-social, EREA…)
  • Statut (Public / Privé)
  • État (OUVERT / À FERMER)
  • Région → Académie → Département → Circonscription (sélecteurs en cascade)
  • Options proposées : Restauration, Internat, ULIS, SEGPA (logique ET ; coché = établissements proposant l'option)

Note : la colonne apprentissage existe dans la source mais n'y est jamais renseignée (0 établissement), elle n'est donc pas proposée comme filtre.

Les points sont regroupés (clustering) pour rester fluides à l'échelle nationale et colorés par type d'établissement (voir la légende).

Mettre à jour les données

  1. Télécharger l'export à jour (champs réduits) :

    curl "https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-annuaire-education/exports/json?select=identifiant_de_l_etablissement,nom_etablissement,type_etablissement,statut_public_prive,libelle_region,libelle_academie,libelle_departement,code_departement,nom_commune,code_postal,adresse_1,telephone,mail,web,etat,nom_circonscription,restauration,hebergement,ulis,segpa,apprentissage,latitude,longitude" -o raw_export.json
    
  2. Recompacter vers data/etablissements.json :

    php build_data.php
    

Fichiers

Fichier Rôle
index.html Page carte (sidebar + overlay fiche)
admin.html · admin.js Espace d'administration (connexion, modération, arbre, admins, import)
style.css Mise en forme (carte, fiche, galeries, admin, pages d'auth)
app.js Chargement (API), filtres en cascade, clustering, popups
account.html · forgot.html · reset.html Parcours mot de passe (changer / oublié / réinitialiser)
pwpolicy.js Politique de mot de passe côté client (validation + jauge)
fiche.js Fiche plein écran : infos, documents (arbre), photothèque, proposition
setup.php Crée base + tables, super-admin, arbre d'exemple, importe les établissements
api/db.php PDO MySQL + JSON + session/auth + import partagé
api/auth.php Connexion / déconnexion / utilisateur courant
api/etablissements.php GET : tous les établissements (sert la carte)
api/items.php Éléments (fichiers/photos/liens) : liste, soumission, validation, suppression
api/folders.php Arborescence globale (CRUD, super-admin)
api/users.php Admins de périmètre + leurs communes (super-admin)
api/groups.php Groupes d'utilisateurs : membres, communes, droit de validation (super-admin)
api/comments.php Commentaires de fiche (horodatés, visibles par groupe)
api/communes.php Autocomplétion des communes (affectation de périmètre)
api/import.php Import de la dernière version de l'opendata (super-admin)
api/iris.php Population INSEE 2022 d'un IRIS et de sa commune (+ densité par batch)
api/effectifs.php Historique des effectifs d'un établissement + ?action=latest (batch) + ?action=commune (cumul commune)
api/ips.php Historique de l'IPS (indice de position sociale) d'une école
api/population.php Historique de la population municipale (INSEE) de la commune
api/dpe.php DPE rattachés à un établissement (?uai=) + ?action=latest (batch, meilleur match)
api/dpe_lib.php Logique partagée DPE (création tables, import data-fair, rattachement géo)
api/import_dpe.php Import DPE + rattachement déclenché depuis l'admin (super-admin)
import_dpe.php Importe les DPE tertiaires de l'ADEME dans dpe (CLI ; appelle dpe_lib)
match_dpe.php Rattache les DPE aux écoles par proximité géographique (CLI ; table etab_dpe)
import_pop.php Importe les populations INSEE 2022 dans iris_pop (voir en-tête du script)
import_effectifs.php Importe les effectifs des écoles (1er degré) dans effectifs
import_ips.php Importe l'IPS (indice de position sociale) des écoles, collèges et lycées dans ips
import_pop_insee.php Importe l'historique des populations INSEE (base séries historiques, communes et arrondissements, 1968→2022) dans pop_commune
import_pop_hist.php Variante : historique des populations via CSV Opendatasoft (~2014) → pop_commune
import_effectifs_2d.php Importe les effectifs des collèges et lycées dans effectifs
import_comc.php Renseigne le code INSEE commune (comc) des établissements
uploads/files/ · uploads/photos/ Fichiers et photos stockés sur disque
build_data.php Convertit raw_export.jsondata/etablissements.json (source du premier import)
data/etablissements.json Jeu de données importé en base par setup.php
raw_export.json Export brut temporaire (supprimable après build)
S
Description
No description provided
Readme 132 KiB
Languages
PHP 47.7%
JavaScript 34.7%
CSS 9.7%
HTML 7.9%