use insee codes to generate all cities measures from osmium and osm pbf

This commit is contained in:
Tykayn 2025-08-31 01:33:22 +02:00 committed by tykayn
parent fa346d522f
commit b28f8eac63
3 changed files with 633 additions and 6 deletions

View file

@ -1646,6 +1646,367 @@ final class AdminController extends AbstractController
$this->addFlash('success', "Création des Stats manquantes terminée : $createdCount communes ajoutées, $skippedCount déjà existantes, $errorCount erreurs.");
return $this->redirectToRoute('app_admin');
}
#[Route('/admin/create-stats-from-insee-csv', name: 'app_admin_create_stats_from_insee_csv')]
public function createStatsFromInseeCsv(): Response
{
$this->actionLogger->log('admin/create_stats_from_insee_csv', []);
$csvFile = 'communes_france.csv';
if (!file_exists($csvFile)) {
$this->addFlash('error', 'Le fichier CSV des communes n\'existe pas. Veuillez exécuter le script fetch_communes.py pour le générer.');
return $this->redirectToRoute('app_admin');
}
$statsRepo = $this->entityManager->getRepository(Stats::class);
$createdCount = 0;
$skippedCount = 0;
$errorCount = 0;
// Ouvrir le fichier CSV
$handle = fopen($csvFile, 'r');
if (!$handle) {
$this->addFlash('error', 'Impossible d\'ouvrir le fichier CSV des communes.');
return $this->redirectToRoute('app_admin');
}
// Lire l'en-tête pour déterminer les indices des colonnes
$header = fgetcsv($handle);
$indices = array_flip($header);
// Vérifier que les colonnes nécessaires existent
$requiredColumns = ['code', 'nom'];
foreach ($requiredColumns as $column) {
if (!isset($indices[$column])) {
$this->addFlash('error', "La colonne '$column' est manquante dans le fichier CSV.");
fclose($handle);
return $this->redirectToRoute('app_admin');
}
}
// Traiter chaque ligne du CSV
while (($data = fgetcsv($handle)) !== false) {
try {
$inseeCode = $data[$indices['code']];
// Vérifier si une Stats existe déjà pour ce code INSEE
$existingStat = $statsRepo->findOneBy(['zone' => $inseeCode]);
if ($existingStat) {
$skippedCount++;
continue;
}
// Créer un nouvel objet Stats
$stat = new Stats();
$stat->setZone($inseeCode)
->setDateCreated(new \DateTime())
->setDateModified(new \DateTime())
->setKind('insee_csv'); // Utiliser 'insee_csv' comme source
// Ajouter le nom si disponible
if (isset($indices['nom']) && !empty($data[$indices['nom']])) {
$stat->setName($data[$indices['nom']]);
}
// Ajouter la population si disponible
if (isset($indices['population']) && !empty($data[$indices['population']])) {
$stat->setPopulation((int)$data[$indices['population']]);
}
// Ajouter les codes postaux si disponibles
if (isset($indices['codesPostaux']) && !empty($data[$indices['codesPostaux']])) {
$stat->setCodesPostaux($data[$indices['codesPostaux']]);
}
// Ajouter le SIREN si disponible
if (isset($indices['siren']) && !empty($data[$indices['siren']])) {
$stat->setSiren((int)$data[$indices['siren']]);
}
// Ajouter le code EPCI si disponible
if (isset($indices['codeEpci']) && !empty($data[$indices['codeEpci']])) {
$stat->setCodeEpci((int)$data[$indices['codeEpci']]);
}
// Ne pas faire de labourage des objets avant la sauvegarde
// Persister l'objet Stats
$this->entityManager->persist($stat);
$createdCount++;
// Flush tous les 100 objets pour éviter de surcharger la mémoire
if ($createdCount % 100 === 0) {
$this->entityManager->flush();
$this->entityManager->clear(Stats::class);
}
} catch (\Exception $e) {
$errorCount++;
$this->actionLogger->log('error_create_stats_from_insee_csv', [
'insee_code' => $inseeCode ?? 'unknown',
'error' => $e->getMessage()
]);
}
}
// Flush les derniers objets
$this->entityManager->flush();
fclose($handle);
$this->addFlash('success', "Création des Stats depuis le CSV INSEE terminée : $createdCount communes ajoutées, $skippedCount déjà existantes, $errorCount erreurs.");
return $this->redirectToRoute('app_admin');
}
#[Route('/admin/retrieve-city-polygons', name: 'app_admin_retrieve_city_polygons')]
public function retrieveCityPolygons(): Response
{
$this->actionLogger->log('admin/retrieve_city_polygons', []);
// Vérifier que le dossier polygons existe, sinon le créer
$polygonsDir = __DIR__ . '/../../counting_osm_objects/polygons';
if (!is_dir($polygonsDir)) {
mkdir($polygonsDir, 0755, true);
}
// Récupérer toutes les Stats
$statsRepo = $this->entityManager->getRepository(Stats::class);
$allStats = $statsRepo->findAll();
$totalCount = count($allStats);
$existingCount = 0;
$createdCount = 0;
$errorCount = 0;
// Pour chaque Stats, récupérer le polygone si nécessaire
foreach ($allStats as $stat) {
$inseeCode = $stat->getZone();
if (!$inseeCode) {
continue;
}
$polygonFile = $polygonsDir . '/commune_' . $inseeCode . '.poly';
// Vérifier si le polygone existe déjà
if (file_exists($polygonFile)) {
$existingCount++;
continue;
}
try {
// Utiliser le script Python existant pour récupérer le polygone
$command = 'cd ' . __DIR__ . '/../../counting_osm_objects && python3 get_poly.py ' . $inseeCode;
$output = [];
$returnVar = 0;
exec($command, $output, $returnVar);
if ($returnVar === 0 && file_exists($polygonFile)) {
$createdCount++;
} else {
$errorCount++;
$this->actionLogger->log('error_retrieve_city_polygon', [
'insee_code' => $inseeCode,
'error' => 'Failed to retrieve polygon: ' . implode("\n", $output)
]);
}
} catch (\Exception $e) {
$errorCount++;
$this->actionLogger->log('error_retrieve_city_polygon', [
'insee_code' => $inseeCode,
'error' => $e->getMessage()
]);
}
}
$this->addFlash('success', "Récupération des polygones terminée : $createdCount polygones créés, $existingCount déjà existants, $errorCount erreurs sur un total de $totalCount communes.");
return $this->redirectToRoute('app_admin');
}
#[Route('/admin/extract-insee-zones', name: 'app_admin_extract_insee_zones')]
public function extractInseeZones(): Response
{
$this->actionLogger->log('admin/extract_insee_zones', []);
// Vérifier que le fichier france-latest.osm.pbf existe
$francePbfFile = __DIR__ . '/../../france-latest.osm.pbf';
if (!file_exists($francePbfFile)) {
$this->addFlash('error', 'Le fichier france-latest.osm.pbf n\'existe pas. Veuillez le télécharger depuis https://download.geofabrik.de/europe/france.html');
return $this->redirectToRoute('app_admin');
}
// Vérifier que le dossier polygons existe
$polygonsDir = __DIR__ . '/../../counting_osm_objects/polygons';
if (!is_dir($polygonsDir)) {
$this->addFlash('error', 'Le dossier des polygones n\'existe pas. Veuillez d\'abord exécuter l\'action "Récupérer les polygones des villes".');
return $this->redirectToRoute('app_admin');
}
// Créer le dossier pour les extractions JSON si nécessaire
$extractsDir = __DIR__ . '/../../insee_extracts';
if (!is_dir($extractsDir)) {
mkdir($extractsDir, 0755, true);
}
// Récupérer toutes les Stats
$statsRepo = $this->entityManager->getRepository(Stats::class);
$allStats = $statsRepo->findAll();
$totalCount = count($allStats);
$existingCount = 0;
$createdCount = 0;
$errorCount = 0;
// Pour chaque Stats, extraire les données si nécessaire
foreach ($allStats as $stat) {
$inseeCode = $stat->getZone();
if (!$inseeCode) {
continue;
}
$polygonFile = $polygonsDir . '/commune_' . $inseeCode . '.poly';
$extractPbfFile = $extractsDir . '/commune_' . $inseeCode . '.osm.pbf';
$extractJsonFile = $extractsDir . '/commune_' . $inseeCode . '.json';
// Vérifier si le polygone existe
if (!file_exists($polygonFile)) {
$this->actionLogger->log('error_extract_insee_zone', [
'insee_code' => $inseeCode,
'error' => 'Polygon file does not exist'
]);
$errorCount++;
continue;
}
// Vérifier si l'extraction JSON existe déjà
if (file_exists($extractJsonFile)) {
$existingCount++;
continue;
}
try {
// Étape 1: Extraire les données de france-latest.osm.pbf vers un fichier PBF pour la zone
$extractCommand = 'osmium extract -p ' . $polygonFile . ' ' . $francePbfFile . ' -o ' . $extractPbfFile;
$output = [];
$returnVar = 0;
exec($extractCommand, $output, $returnVar);
if ($returnVar !== 0 || !file_exists($extractPbfFile)) {
$this->actionLogger->log('error_extract_insee_zone', [
'insee_code' => $inseeCode,
'error' => 'Failed to extract PBF: ' . implode("\n", $output)
]);
$errorCount++;
continue;
}
// Étape 2: Convertir le fichier PBF en JSON
$exportCommand = 'osmium export ' . $extractPbfFile . ' -f json -o ' . $extractJsonFile;
$output = [];
$returnVar = 0;
exec($exportCommand, $output, $returnVar);
if ($returnVar === 0 && file_exists($extractJsonFile)) {
$createdCount++;
} else {
$this->actionLogger->log('error_extract_insee_zone', [
'insee_code' => $inseeCode,
'error' => 'Failed to export to JSON: ' . implode("\n", $output)
]);
$errorCount++;
}
// Supprimer le fichier PBF intermédiaire pour économiser de l'espace
if (file_exists($extractPbfFile)) {
unlink($extractPbfFile);
}
} catch (\Exception $e) {
$errorCount++;
$this->actionLogger->log('error_extract_insee_zone', [
'insee_code' => $inseeCode,
'error' => $e->getMessage()
]);
}
}
$this->addFlash('success', "Extraction des zones INSEE terminée : $createdCount extractions créées, $existingCount déjà existantes, $errorCount erreurs sur un total de $totalCount communes.");
return $this->redirectToRoute('app_admin');
}
#[Route('/admin/process-insee-extracts', name: 'app_admin_process_insee_extracts')]
public function processInseeExtracts(): Response
{
$this->actionLogger->log('admin/process_insee_extracts', []);
// Vérifier que le dossier des extractions existe
$extractsDir = __DIR__ . '/../../insee_extracts';
if (!is_dir($extractsDir)) {
$this->addFlash('error', 'Le dossier des extractions n\'existe pas. Veuillez d\'abord exécuter l\'action "Extraire les données des zones INSEE".');
return $this->redirectToRoute('app_admin');
}
// Récupérer toutes les Stats
$statsRepo = $this->entityManager->getRepository(Stats::class);
$allStats = $statsRepo->findAll();
$totalCount = count($allStats);
$processedCount = 0;
$skippedCount = 0;
$errorCount = 0;
// Pour chaque Stats, traiter les données si nécessaire
foreach ($allStats as $stat) {
$inseeCode = $stat->getZone();
if (!$inseeCode) {
continue;
}
$extractJsonFile = $extractsDir . '/commune_' . $inseeCode . '.json';
// Vérifier si l'extraction JSON existe
if (!file_exists($extractJsonFile)) {
$this->actionLogger->log('error_process_insee_extract', [
'insee_code' => $inseeCode,
'error' => 'JSON extract file does not exist'
]);
$errorCount++;
continue;
}
try {
// Utiliser la Motocultrice pour traiter les données
$result = $this->motocultrice->labourer($inseeCode);
if ($result) {
// Mettre à jour la date de labourage
$stat->setDateLabourageDone(new \DateTime());
$this->entityManager->persist($stat);
$processedCount++;
// Flush tous les 10 objets pour éviter de surcharger la mémoire
if ($processedCount % 10 === 0) {
$this->entityManager->flush();
}
} else {
$this->actionLogger->log('error_process_insee_extract', [
'insee_code' => $inseeCode,
'error' => 'Failed to process extract with Motocultrice'
]);
$errorCount++;
}
} catch (\Exception $e) {
$errorCount++;
$this->actionLogger->log('error_process_insee_extract', [
'insee_code' => $inseeCode,
'error' => $e->getMessage()
]);
}
}
// Flush les derniers objets
$this->entityManager->flush();
$this->addFlash('success', "Traitement des extractions INSEE terminé : $processedCount communes traitées, $skippedCount ignorées, $errorCount erreurs sur un total de $totalCount communes.");
return $this->redirectToRoute('app_admin');
}
/**
* Complète les données manquantes d'un objet Stats (coordonnées, budget, etc.)