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>
18 KiB
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) dansapi/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é danscan_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é parpassword_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/adminpar défaut) sont marquésmust_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é surreset.html. Tablepassword_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) dansprivate/mail_outbox.log(dossier protégé par.htaccess) — pratique en local sans serveur de mail ;mail: fonction PHPmail();smtp: client SMTP intégré (renseignerSMTP_HOST/PORT/USER/PASS/SECURE). Pensez à ajusterAPP_BASE_URLpour 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.phpdans la tableiris_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_classesimporté viaphp import_effectifs.phpdans la tableeffectifs(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.phpdans la tableips(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) etfr-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|lyceespermet 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 viaphp import_pop_insee.phpdans la tablepop_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_estimatedn'est utilisé qu'en repli (zone sans historique propre) : population de la ville × part de l'arrondissement dans les IRIS 2022. import_pop_hist.phpreste une alternative (CSV Opendatasoft, s'arrête ~2014, sans arrondissements).
- L'importeur lit le CSV dans le zip sans l'extension PHP
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=Npermet 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 dansapi/dpe_lib.php.
Coloration des IRIS (2 modes)
Quand la couche IRIS est active, un sélecteur (en bas à gauche) permet de choisir :
- Densité de population — population 2022 ÷ surface de l'IRIS (palette jaune→rouge).
- 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
apprentissageexiste 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
-
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 -
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.json → data/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) |