'Ain', '02' => 'Aisne', '03' => 'Allier', '04' => 'Alpes-de-Haute-Provence', '05' => 'Hautes-Alpes', '06' => 'Alpes-Maritimes', '07' => 'Ardèche', '08' => 'Ardennes', '09' => 'Ariège', '10' => 'Aube', '11' => 'Aude', '12' => 'Aveyron', '13' => 'Bouches-du-Rhône', '14' => 'Calvados', '15' => 'Cantal', '16' => 'Charente', '17' => 'Charente-Maritime', '18' => 'Cher', '19' => 'Corrèze', '2A' => 'Corse-du-Sud', '2B' => 'Haute-Corse', '21' => 'Côte-d\'Or', '22' => 'Côtes-d\'Armor', '23' => 'Creuse', '24' => 'Dordogne', '25' => 'Doubs', '26' => 'Drôme', '27' => 'Eure', '28' => 'Eure-et-Loir', '29' => 'Finistère', '30' => 'Gard', '31' => 'Haute-Garonne', '32' => 'Gers', '33' => 'Gironde', '34' => 'Hérault', '35' => 'Ille-et-Vilaine', '36' => 'Indre', '37' => 'Indre-et-Loire', '38' => 'Isère', '39' => 'Jura', '40' => 'Landes', '41' => 'Loir-et-Cher', '42' => 'Loire', '43' => 'Haute-Loire', '44' => 'Loire-Atlantique', '45' => 'Loiret', '46' => 'Lot', '47' => 'Lot-et-Garonne', '48' => 'Lozère', '49' => 'Maine-et-Loire', '50' => 'Manche', '51' => 'Marne', '52' => 'Haute-Marne', '53' => 'Mayenne', '54' => 'Meurthe-et-Moselle', '55' => 'Meuse', '56' => 'Morbihan', '57' => 'Moselle', '58' => 'Nièvre', '59' => 'Nord', '60' => 'Oise', '61' => 'Orne', '62' => 'Pas-de-Calais', '63' => 'Puy-de-Dôme', '64' => 'Pyrénées-Atlantiques', '65' => 'Hautes-Pyrénées', '66' => 'Pyrénées-Orientales', '67' => 'Bas-Rhin', '68' => 'Haut-Rhin', '69' => 'Rhône', '70' => 'Haute-Saône', '71' => 'Saône-et-Loire', '72' => 'Sarthe', '73' => 'Savoie', '74' => 'Haute-Savoie', '75' => 'Paris', '76' => 'Seine-Maritime', '77' => 'Seine-et-Marne', '78' => 'Yvelines', '79' => 'Deux-Sèvres', '80' => 'Somme', '81' => 'Tarn', '82' => 'Tarn-et-Garonne', '83' => 'Var', '84' => 'Vaucluse', '85' => 'Vendée', '86' => 'Vienne', '87' => 'Haute-Vienne', '88' => 'Vosges', '89' => 'Yonne', '90' => 'Territoire de Belfort', '91' => 'Essonne', '92' => 'Hauts-de-Seine', '93' => 'Seine-Saint-Denis', '94' => 'Val-de-Marne', '95' => 'Val-d\'Oise', // DOM-TOM '971' => 'Guadeloupe', '972' => 'Martinique', '973' => 'Guyane', '974' => 'La Réunion', '975' => 'Saint-Pierre-et-Miquelon', '976' => 'Mayotte', '977' => 'Saint-Barthélemy', '978' => 'Saint-Martin', '984' => 'Terres australes et antarctiques françaises', '986' => 'Wallis-et-Futuna', '987' => 'Polynésie française', '988' => 'Nouvelle-Calédonie' ]; public function __construct(PDO $db) { $this->db = $db; } /** * Vérifie si la table existe */ public function tableExists(): bool { try { $sql = "SHOW TABLES LIKE 'x_departements_contours'"; $stmt = $this->db->query($sql); return $stmt->rowCount() > 0; } catch (Exception $e) { return false; } } /** * Vérifie si la table est vide */ private function isTableEmpty(): bool { try { $sql = "SELECT COUNT(*) as count FROM x_departements_contours"; $stmt = $this->db->query($sql); $result = $stmt->fetch(); return $result['count'] == 0; } catch (Exception $e) { return true; } } /** * Récupère le contour d'un département depuis l'API geo.api.gouv.fr */ private function fetchDepartementContour(string $code, string $nom): ?array { // URL de l'API pour récupérer le contour du département en GeoJSON $url = "https://geo.api.gouv.fr/departements/{$code}?geometry=contour"; $context = stream_context_create([ 'http' => [ 'timeout' => 30, 'header' => "User-Agent: Geosector/1.0\r\n" ] ]); $response = @file_get_contents($url, false, $context); if ($response === false) { $this->log[] = "✗ Erreur API pour département $code ($nom)"; return null; } $data = json_decode($response, true); // L'API peut retourner le contour dans 'contour' ou 'geometry' if (isset($data['contour']) && isset($data['contour']['coordinates'])) { return $data['contour']; } elseif (isset($data['geometry']) && isset($data['geometry']['coordinates'])) { return $data['geometry']; } else { $this->log[] = "✗ Pas de contour pour département $code ($nom)"; // Debug : afficher les clés disponibles if (is_array($data)) { $this->log[] = " Clés disponibles : " . implode(', ', array_keys($data)); } return null; } } /** * Convertit les coordonnées GeoJSON en WKT Polygon pour MySQL */ private function geoJsonToWkt(array $coordinates): ?array { if (empty($coordinates) || !is_array($coordinates[0])) { return null; } // GeoJSON peut avoir plusieurs niveaux d'imbrication selon le type // Pour un Polygon simple if (isset($coordinates[0][0]) && is_numeric($coordinates[0][0])) { $ring = $coordinates; } // Pour un MultiPolygon, prendre le premier polygone elseif (isset($coordinates[0][0][0])) { $ring = $coordinates[0][0]; } // Pour un Polygon standard else { $ring = $coordinates[0]; } $points = []; $lats = []; $lngs = []; foreach ($ring as $point) { if (count($point) >= 2) { $lng = $point[0]; $lat = $point[1]; $points[] = "$lng $lat"; $lats[] = $lat; $lngs[] = $lng; } } if (count($points) < 3) { return null; } // Fermer le polygone si nécessaire if ($points[0] !== $points[count($points) - 1]) { $points[] = $points[0]; } return [ 'wkt' => 'POLYGON((' . implode(',', $points) . '))', 'bbox' => [ 'min_lat' => min($lats), 'max_lat' => max($lats), 'min_lng' => min($lngs), 'max_lng' => max($lngs) ] ]; } /** * Importe tous les départements */ public function importAll(): array { $this->log[] = "Début de l'import des contours départementaux"; $this->log[] = "Source : API geo.api.gouv.fr"; $this->log[] = ""; // Vérifier que la table est vide avant d'importer if (!$this->isTableEmpty()) { $this->log[] = "✗ La table x_departements_contours contient déjà des données"; return $this->log; } // Préparer la requête d'insertion $sql = "INSERT INTO x_departements_contours (code_dept, nom_dept, contour, bbox_min_lat, bbox_max_lat, bbox_min_lng, bbox_max_lng) VALUES (:code, :nom, ST_GeomFromText(:polygon, 4326), :min_lat, :max_lat, :min_lng, :max_lng)"; $stmt = $this->db->prepare($sql); $success = 0; $errors = 0; // Démarrer une transaction $this->db->beginTransaction(); try { foreach ($this->departements as $code => $nom) { // Petite pause pour ne pas surcharger l'API usleep(100000); // 100ms $contour = $this->fetchDepartementContour($code, $nom); if (!$contour) { $errors++; continue; } $wktData = $this->geoJsonToWkt($contour['coordinates']); if (!$wktData) { $this->log[] = "✗ Conversion échouée pour $code ($nom)"; $errors++; continue; } try { $stmt->execute([ 'code' => $code, 'nom' => $nom, 'polygon' => $wktData['wkt'], 'min_lat' => $wktData['bbox']['min_lat'], 'max_lat' => $wktData['bbox']['max_lat'], 'min_lng' => $wktData['bbox']['min_lng'], 'max_lng' => $wktData['bbox']['max_lng'] ]); $this->log[] = "✓ $code - $nom importé"; $success++; } catch (Exception $e) { $this->log[] = "✗ Erreur SQL pour $code ($nom) : " . $e->getMessage(); $errors++; } } // Si tout s'est bien passé, valider la transaction if ($success > 0) { $this->db->commit(); $this->log[] = ""; $this->log[] = "✓ Transaction validée"; } else { $this->db->rollBack(); $this->log[] = ""; $this->log[] = "✗ Transaction annulée (aucun import réussi)"; } } catch (Exception $e) { $this->db->rollBack(); $this->log[] = ""; $this->log[] = "✗ Erreur fatale : " . $e->getMessage(); $this->log[] = "✗ Transaction annulée"; $errors = count($this->departements); } $this->log[] = ""; $this->log[] = "Import terminé : $success réussis, $errors erreurs"; return $this->log; } /** * Exécute l'initialisation si nécessaire */ public static function runIfNeeded(PDO $db, string $username): ?array { // Vérifier que c'est bien l'admin d6soft if ($username !== 'd6soft') { return null; } $initializer = new self($db); // Vérifier si la table existe if (!$initializer->tableExists()) { return ["✗ La table x_departements_contours n'existe pas. Veuillez la créer avec le script SQL fourni."]; } // Vérifier si elle est vide if (!$initializer->isTableEmpty()) { return null; // Table déjà remplie, rien à faire } // Vérifier si le fichier local existe $localFile = __DIR__ . '/../docs/contour-des-departements.geojson'; if (file_exists($localFile)) { // Utiliser le fichier local require_once __DIR__ . '/import_departements_from_file.php'; $fileImporter = new \DepartementContoursFileImporter($db); return $fileImporter->importFromFile($localFile); } // Sinon, utiliser l'API (qui ne fonctionne pas bien actuellement) return $initializer->importAll(); } } // Si le script est exécuté directement (pour tests) if (php_sapi_name() === 'cli' && basename(__FILE__) === basename($_SERVER['PHP_SELF'] ?? __FILE__)) { require_once __DIR__ . '/../src/Config/AppConfig.php'; require_once __DIR__ . '/../src/Core/Database.php'; $appConfig = AppConfig::getInstance(); Database::init($appConfig->getDatabaseConfig()); $db = Database::getInstance(); echo "Test d'import des contours départementaux\n"; echo "========================================\n\n"; $initializer = new DepartementContoursInitializer($db); $log = $initializer->importAll(); foreach ($log as $line) { echo $line . "\n"; } }