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>
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* Import de l'historique des populations légales communales — source officielle INSEE.
|
||||
* Base « séries historiques » du recensement (Comparateur de territoires) :
|
||||
* https://www.insee.fr/fr/statistiques/fichier/8582555/base-cc-serie-historique-2022_csv.zip
|
||||
*
|
||||
* Population municipale à 9 recensements : 1968, 1975, 1982, 1990, 1999, 2006, 2011,
|
||||
* 2016, 2022 (colonnes D68_POP … D99_POP puis P06_POP … P22_POP). Cette base est au
|
||||
* niveau commune ET arrondissement municipal : elle contient donc le VRAI historique
|
||||
* par arrondissement pour Paris (75101…), Lyon (69381…) et Marseille (13201…), ainsi
|
||||
* que les totaux communes (75056 / 69123 / 13055).
|
||||
*
|
||||
* Remplit la table pop_commune (comc, annee, pop). Lit le CSV dans le zip sans
|
||||
* l'extension PHP « zip » (mini-lecteur ZIP + gzinflate).
|
||||
*
|
||||
* Usage : php import_pop_insee.php
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
require __DIR__ . '/api/db.php';
|
||||
|
||||
ini_set('memory_limit', '1024M');
|
||||
|
||||
const INSEE_SERIE_URL = 'https://www.insee.fr/fr/statistiques/fichier/8582555/base-cc-serie-historique-2022_csv.zip';
|
||||
const INSEE_CSV_NAME = 'base-cc-serie-historique-2022.CSV';
|
||||
|
||||
// Colonne de population → année du recensement.
|
||||
const POP_COLS = [
|
||||
'P22_POP' => 2022, 'P16_POP' => 2016, 'P11_POP' => 2011, 'P06_POP' => 2006,
|
||||
'D99_POP' => 1999, 'D90_POP' => 1990, 'D82_POP' => 1982, 'D75_POP' => 1975, 'D68_POP' => 1968,
|
||||
];
|
||||
|
||||
function say(string $m): void { echo $m . "\n"; flush(); }
|
||||
|
||||
/**
|
||||
* Lit une entrée d'un fichier ZIP (méthodes « stored » et « deflate ») sans ext/zip.
|
||||
* Renvoie le contenu décompressé, ou null si absente.
|
||||
*/
|
||||
function zip_entry(string $path, string $name): ?string
|
||||
{
|
||||
$fh = fopen($path, 'rb');
|
||||
if (!$fh) {
|
||||
return null;
|
||||
}
|
||||
$size = filesize($path);
|
||||
$look = min($size, 22 + 65536);
|
||||
fseek($fh, $size - $look);
|
||||
$buf = fread($fh, $look);
|
||||
$eo = strrpos($buf, "PK\x05\x06");
|
||||
if ($eo === false) { fclose($fh); return null; }
|
||||
$eocd = unpack('Vsig/vdisk/vcddisk/ventries/vtotal/Vcdsize/Vcdoffset/vcomlen', substr($buf, $eo, 22));
|
||||
|
||||
fseek($fh, $eocd['cdoffset']);
|
||||
$cd = fread($fh, $eocd['cdsize']);
|
||||
$off = 0;
|
||||
$found = null;
|
||||
$n = strlen($cd);
|
||||
while ($off + 46 <= $n && substr($cd, $off, 4) === "PK\x01\x02") {
|
||||
$h = unpack('vver/vverneed/vflags/vmethod/vmtime/vmdate/Vcrc/Vcsize/Vusize/vnlen/velen/vclen/vdisk/viattr/Veattr/Vloffset', substr($cd, $off + 4, 42));
|
||||
$fname = substr($cd, $off + 46, $h['nlen']);
|
||||
if ($fname === $name) { $found = $h; break; }
|
||||
$off += 46 + $h['nlen'] + $h['elen'] + $h['clen'];
|
||||
}
|
||||
if (!$found) { fclose($fh); return null; }
|
||||
|
||||
fseek($fh, $found['loffset']);
|
||||
$lh = unpack('Vsig/vver/vflags/vmethod/vmtime/vmdate/Vcrc/Vcsize/Vusize/vnlen/velen', fread($fh, 30));
|
||||
fseek($fh, $found['loffset'] + 30 + $lh['nlen'] + $lh['elen']);
|
||||
$comp = fread($fh, $found['csize']);
|
||||
fclose($fh);
|
||||
|
||||
if ($found['method'] === 0) {
|
||||
return $comp;
|
||||
}
|
||||
if ($found['method'] === 8) {
|
||||
return gzinflate($comp);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$pdo->exec(
|
||||
'CREATE TABLE IF NOT EXISTS pop_commune (
|
||||
comc VARCHAR(5) NOT NULL,
|
||||
annee SMALLINT NOT NULL,
|
||||
pop INT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (comc, annee),
|
||||
INDEX idx_comc (comc)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
|
||||
);
|
||||
|
||||
$zipPath = __DIR__ . '/data/insee/serie_hist.zip';
|
||||
if (!is_file($zipPath)) {
|
||||
@mkdir(dirname($zipPath), 0777, true);
|
||||
say('… Téléchargement de la base INSEE (~6,7 Mo) …');
|
||||
$fp = fopen($zipPath, 'w');
|
||||
$ch = curl_init(INSEE_SERIE_URL);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_FILE => $fp, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 600,
|
||||
CURLOPT_USERAGENT => 'ecoles-import/1.0',
|
||||
]);
|
||||
$ok = curl_exec($ch);
|
||||
$err = curl_error($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
if (!$ok || filesize($zipPath) < 100000) {
|
||||
@unlink($zipPath);
|
||||
throw new RuntimeException('Téléchargement échoué : ' . $err);
|
||||
}
|
||||
}
|
||||
|
||||
say('… Lecture du CSV …');
|
||||
$csv = zip_entry($zipPath, INSEE_CSV_NAME);
|
||||
if ($csv === null || $csv === '') {
|
||||
throw new RuntimeException('CSV introuvable dans le zip : ' . INSEE_CSV_NAME);
|
||||
}
|
||||
|
||||
$lines = preg_split('/\r\n|\n|\r/', $csv);
|
||||
$header = str_getcsv(array_shift($lines), ';');
|
||||
$header[0] = preg_replace('/^\xEF\xBB\xBF/', '', (string) $header[0]); // BOM
|
||||
$iCode = array_search('CODGEO', $header, true);
|
||||
if ($iCode === false) {
|
||||
throw new RuntimeException('Colonne CODGEO introuvable.');
|
||||
}
|
||||
$colIdx = [];
|
||||
foreach (POP_COLS as $col => $annee) {
|
||||
$k = array_search($col, $header, true);
|
||||
if ($k !== false) {
|
||||
$colIdx[$k] = $annee;
|
||||
}
|
||||
}
|
||||
if (!$colIdx) {
|
||||
throw new RuntimeException('Aucune colonne de population trouvée.');
|
||||
}
|
||||
|
||||
$ins = $pdo->prepare('REPLACE INTO pop_commune (comc, annee, pop) VALUES (?, ?, ?)');
|
||||
$pdo->exec('TRUNCATE TABLE pop_commune');
|
||||
$pdo->beginTransaction();
|
||||
$n = 0;
|
||||
foreach ($lines as $line) {
|
||||
if ($line === '') {
|
||||
continue;
|
||||
}
|
||||
$row = str_getcsv($line, ';');
|
||||
$code = trim((string) ($row[$iCode] ?? ''));
|
||||
if (!preg_match('/^[0-9AB]\w{4}$/', $code)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($colIdx as $k => $annee) {
|
||||
$v = $row[$k] ?? '';
|
||||
if ($v === '' || !is_numeric(str_replace([' ', ','], ['', '.'], $v))) {
|
||||
continue;
|
||||
}
|
||||
$ins->execute([$code, $annee, (int) round((float) str_replace([' ', ','], ['', '.'], $v))]);
|
||||
if (++$n % 10000 === 0) {
|
||||
$pdo->commit();
|
||||
$pdo->beginTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
$pdo->commit();
|
||||
|
||||
$communes = (int) $pdo->query('SELECT COUNT(DISTINCT comc) FROM pop_commune')->fetchColumn();
|
||||
$arr = (int) $pdo->query("SELECT COUNT(DISTINCT comc) FROM pop_commune WHERE comc BETWEEN '13201' AND '13216' OR comc BETWEEN '69381' AND '69389' OR comc LIKE '751__'")->fetchColumn();
|
||||
say("✔ pop_commune : $n lignes ($communes codes dont $arr arrondissements PLM, INSEE séries historiques 1968→2022).");
|
||||
say('🎉 Terminé.');
|
||||
} catch (Throwable $e) {
|
||||
if (isset($pdo) && $pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
http_response_code(500);
|
||||
say('!! ERREUR : ' . $e->getMessage());
|
||||
exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user