<?php
namespace App\Controller;
use App\Service\MeteoFranceService;
use App\DTO\StationMeteoDTO;
use App\Form\StationMeteoType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Entity\Pluviometrie;
use App\Entity\StationsMeteo;
set_time_limit(36000);
class CaniculeController extends AbstractController {
public function __construct(private MeteoFranceService $meteoService, StationMeteoType $stationMeteoType){//, HttpClientInterface $client, CacheInterface $cache, string $apiKey, string $meteoFranceTokenUrl, string $meteoFranceApiUrl) {
$this->stationMeteoType = $stationMeteoType;
}
/**
* @Route("/canicule", name="canicule")
*/
public function canicule(Request $request, EntityManagerInterface $em): Response
{
ini_set('memory_limit', '1024M'); // ou '2G' si besoin
// Créer une instance du DTO pour passer au formulaire
$stationMeteoDTO = new StationMeteoDTO();
// Créer le formulaire avec l'objet DTO
$form = $this->createForm(StationMeteoType::class, $stationMeteoDTO);
// Gérer la soumission du formulaire
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Récupérer les données du formulaire
$data = $form->getData();
// Tu as directement accès à l'objet StationMeteoDTO ici
$apikey = $data->getApikey(); // Accès via la méthode getter
$stationAPI = $data->getStationAPI(); // Accès via la méthode getter
// Appeler le service pour obtenir les données météo
$stationsData = [];
$commandeArray = []; // Initialisation pour éviter l'erreur Undefined variable
$columnDataArray = []; // Initialisation pour stocker les données des colonnes
$AnneesEtudiees = [];
foreach (range(2002, 2024) as $annee) {
$AnneesEtudiees[$annee] = [
'dateDeb' => "{$annee}-01-01T00%3A00%3A00Z",
'dateFin' => "{$annee}-12-31T00%3A00%3A00Z",
];
}
foreach ($AnneesEtudiees as $annee => $AnneeEtudiee) {
$commande = $this->meteoService->getStationDataHoraire($stationAPI, $AnneeEtudiee, $apikey);
$commandeArray[$stationAPI][$annee] = $commande;
// Pause pour respecter la limite de 50 requêtes par minute
usleep(1300000); // 1,2 secondes = 1 200 000 microsecondes
}
$erreur500 = null;
$extractedData = [];
// Récupération des fichiers à partir des commandes
foreach ($commandeArray as $stationAPI => $anneesData) {
$erreur = null;
foreach ($anneesData as $AnneeEtudiee => $commandeAnnee) {
$commandeId = $commandeAnnee['elaboreProduitAvecDemandeResponse']['return'];
$csvString = $this->meteoService->getFichier($commandeId, $apikey);
// dump($csvString);
// Pause pour respecter la limite de 50 requêtes par minute
usleep(1300000); // 1,2 secondes = 1 200 000 microsecondes
if ($csvString == 'Erreur 500') {
$erreur500[$commandeId] = $stationAPI;
$erreur = 1;
} else {
// Diviser la chaîne en lignes
$lines = explode("\n", $csvString);
// Tableau pour stocker les données extraites
$data = [];
foreach ($lines as $index => $line) {
// Ignorer la première ligne (en-tête)
if ($index === 0) {
continue;
}
// Diviser chaque ligne en colonnes par le séparateur ;
$columns = explode(";", $line);
// Vérifier qu'il y a suffisamment de colonnes
if (count($columns) > 2) {
// Extraire ce qui se situe entre le 3ème et 4ème point-virgule
$data['date'] = str_replace(",", ".", $columns[1]);
$data['temperature'] = str_replace(",", ".", $columns[10]);
$data['vitesse'] = str_replace(",", ".", $columns[48]);
$data['direction'] = str_replace(",", ".", $columns[50]);
$extractedData[] = $data;
$extractedDataParAn[$AnneeEtudiee][] = $data;
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////
// CALCUL DES CANICULES///////////////////////////////////////////////////////////
function convertirEnDonneesJournalieres($donneesHoraires) {
$donneesJournalieres = [];
foreach ($donneesHoraires as $donnee) {
$dateHeure = \DateTime::createFromFormat('YmdH', $donnee['date']);
if (!$dateHeure) {
throw new \Exception("Format de date invalide : " . $donnee['date']);
}
$jour = $dateHeure->format('Y-m-d');
$temp = floatval($donnee['temperature']);
if (!isset($donneesJournalieres[$jour])) {
$donneesJournalieres[$jour] = [
'date' => $jour,
'min' => $temp,
'max' => $temp,
];
} else {
$donneesJournalieres[$jour]['min'] = min($donneesJournalieres[$jour]['min'], $temp);
$donneesJournalieres[$jour]['max'] = max($donneesJournalieres[$jour]['max'], $temp);
}
}
// On retourne un tableau indexé numériquement pour garder l'ordre chronologique
return array_values($donneesJournalieres);
}
function detecterCaniculesAvecIBM(array $donneesJournalieres, float $seuilMin, float $seuilMax): array {
$canicules = [];
$buffer = [];
for ($i = 2; $i < count($donneesJournalieres); $i++) {
$sous_tableau = array_slice($donneesJournalieres, $i - 2, 3);
$moy_min = array_sum(array_column($sous_tableau, 'min')) / 3;
$moy_max = array_sum(array_column($sous_tableau, 'max')) / 3;
if ($moy_min >= $seuilMin && $moy_max >= $seuilMax) {
// On ajoute tous les 3 jours du trio dans le buffer
foreach ($sous_tableau as $jour) {
$buffer[ $jour['date'] ] = $jour; // clé = date, pour éviter les doublons
}
} else {
if (count($buffer) >= 3) {
$canicules[] = array_values($buffer);
}
$buffer = [];
}
}
if (count($buffer) >= 3) {
$canicules[] = array_values($buffer);
}
return $canicules;
}
function analyserCanicule(array $periode): array {
if (empty($periode)) {
return [
'debut' => null,
'fin' => null,
'duree' => 0,
'intensite_max' => null,
'severite' => 0,
];
}
$dates = array_column($periode, 'date');
$maxs = array_column($periode, 'max');
// Nouvelle méthode de calcul de la sévérité
$severite = array_sum(array_map(function ($jour) {
$moyenne = ($jour['min'] + $jour['max']) / 2;
return $moyenne - 24;
}, $periode));
return [
'debut' => reset($dates),
'fin' => end($dates),
'duree' => count($periode),
'intensite_max' => max($maxs),
'severite' => $severite,
];
}
$caniculesParAn = [];
//Préciser les seuils
$stationDB = $em->getRepository(StationsMeteo::class)->findOneBy(['IdStation' => $stationAPI]);
$departement = $stationDB->getDepartement();
$seuils = $this->getSeuilsParDepartement()[$departement];
foreach ($extractedDataParAn as $annee => $donneesHoraires) {
$donneesJournalieres = convertirEnDonneesJournalieres($donneesHoraires);
//dump($donneesJournalieres);
$caniculesParAn[$annee] = detecterCaniculesAvecIBM($donneesJournalieres, $seuils['seuil_nuit'], $seuils['seuil_jour']);// Adapter les seuils ici
}
$analysesParAn = [];
$severiteTotaleParAn = [];
foreach ($caniculesParAn as $annee => $canicules) {
foreach ($canicules as $periode) {
$analyse = analyserCanicule($periode);
$analysesParAn[$annee][] = $analyse;
// Ajout de la sévérité à la somme annuelle
if (!isset($severiteTotaleParAn[$annee])) {
$severiteTotaleParAn[$annee] = 0;
}
$severiteTotaleParAn[$annee] += $analyse['severite'];
}
}
// Trier les années par sévérité décroissante
arsort($severiteTotaleParAn);
// Extraire les 5 années les plus critiques
$top10Annees = array_slice(array_keys($severiteTotaleParAn), 0, 10, true);
// Affichage
$top10AnneesData = [];
foreach ($top10Annees as $annee) {
if (!isset($analysesParAn[$annee])) {
continue;
}
foreach ($analysesParAn[$annee] as $analyse) {
if (empty($analyse['debut'])) {
continue;
}
$dateDebut = \DateTime::createFromFormat('Y-m-d', $analyse['debut']);
$mois = $dateDebut->format('F'); // en anglais (ex: July)
$mois = strtr($mois, [// option : traduction rapide
'January' => 'janvier', 'February' => 'février', 'March' => 'mars',
'April' => 'avril', 'May' => 'mai', 'June' => 'juin',
'July' => 'juillet', 'August' => 'août', 'September' => 'septembre',
'October' => 'octobre', 'November' => 'novembre', 'December' => 'décembre',
]);
$top10AnneesData[] = [
'annee' => $annee,
'mois' => $mois,
'duree' => $analyse['duree'],
'intensite_max' => round($analyse['intensite_max'], 1),
'severite' => round($analyse['severite']),
];
}
}
$stationKey = $this->getKeyById($stationAPI);
// Passer les données au template
return $this->render('canicule.html.twig', [
'form' => $form->createView(),
'top10Canicules' => $top10AnneesData,
'stationKey' => $stationKey,
'seuils' => $seuils,
]);
// } catch (\Exception $e) {
// // Gérer les erreurs
// $this->addFlash('error', 'Erreur : ' . $e->getMessage());
// }
}
// Rendre la vue du formulaire
return $this->render('caniculeFormulaire.html.twig', [
'form' => $form->createView(),
]);
}
// Méthode pour obtenir la clé en fonction de l'ID
public function getKeyById($id)
{
// Récupérer le tableau des stations et IDs à partir de StationMeteoType
$choices = $this->stationMeteoType->getStationAPIChoices();
// Rechercher la clé correspondant à l'ID
$key = array_search($id, $choices);
// Si l'ID est trouvé, retourne la clé, sinon retourne null
return $key !== false ? $key : null;
}
public function getSeuilsParDepartement(): array {
return [
'01' => ['seuil_jour' => 35, 'seuil_nuit' => 20],
'02' => ['seuil_jour' => 33, 'seuil_nuit' => 18],
'03' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'04' => ['seuil_jour' => 36, 'seuil_nuit' => 19],
'05' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'06' => ['seuil_jour' => 31, 'seuil_nuit' => 24],
'07' => ['seuil_jour' => 35, 'seuil_nuit' => 20],
'08' => ['seuil_jour' => 33, 'seuil_nuit' => 18],
'09' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'10' => ['seuil_jour' => 35, 'seuil_nuit' => 18],
'11' => ['seuil_jour' => 35, 'seuil_nuit' => 22],
'12' => ['seuil_jour' => 36, 'seuil_nuit' => 19],
'13' => ['seuil_jour' => 35, 'seuil_nuit' => 24],
'14' => ['seuil_jour' => 31, 'seuil_nuit' => 18],
'15' => ['seuil_jour' => 32, 'seuil_nuit' => 18],
'16' => ['seuil_jour' => 36, 'seuil_nuit' => 20],
'17' => ['seuil_jour' => 35, 'seuil_nuit' => 20],
'18' => ['seuil_jour' => 35, 'seuil_nuit' => 19],
'19' => ['seuil_jour' => 36, 'seuil_nuit' => 19],
'20' => ['seuil_jour' => 33, 'seuil_nuit' => 23],
// '2B' => ['seuil_jour' => 33, 'seuil_nuit' => 23],
'21' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'22' => ['seuil_jour' => 31, 'seuil_nuit' => 18],
'23' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'24' => ['seuil_jour' => 36, 'seuil_nuit' => 20],
'25' => ['seuil_jour' => 33, 'seuil_nuit' => 19],
'26' => ['seuil_jour' => 36, 'seuil_nuit' => 21],
'27' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'28' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'29' => ['seuil_jour' => 32, 'seuil_nuit' => 19],
'30' => ['seuil_jour' => 36, 'seuil_nuit' => 23],
'31' => ['seuil_jour' => 36, 'seuil_nuit' => 21],
'32' => ['seuil_jour' => 36, 'seuil_nuit' => 20],
'33' => ['seuil_jour' => 35, 'seuil_nuit' => 21],
'34' => ['seuil_jour' => 35, 'seuil_nuit' => 22],
'35' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'36' => ['seuil_jour' => 35, 'seuil_nuit' => 19],
'37' => ['seuil_jour' => 35, 'seuil_nuit' => 19],
'38' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'39' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'40' => ['seuil_jour' => 35, 'seuil_nuit' => 20],
'41' => ['seuil_jour' => 35, 'seuil_nuit' => 19],
'42' => ['seuil_jour' => 35, 'seuil_nuit' => 19],
'43' => ['seuil_jour' => 32, 'seuil_nuit' => 18],
'44' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'45' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'46' => ['seuil_jour' => 36, 'seuil_nuit' => 20],
'47' => ['seuil_jour' => 36, 'seuil_nuit' => 20],
'48' => ['seuil_jour' => 33, 'seuil_nuit' => 18],
'49' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'50' => ['seuil_jour' => 31, 'seuil_nuit' => 18],
'51' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'52' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'53' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'54' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'55' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'56' => ['seuil_jour' => 32, 'seuil_nuit' => 18],
'57' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'58' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'59' => ['seuil_jour' => 33, 'seuil_nuit' => 18],
'60' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'61' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'62' => ['seuil_jour' => 33, 'seuil_nuit' => 18],
'63' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'64' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'65' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'66' => ['seuil_jour' => 35, 'seuil_nuit' => 23],
'67' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'68' => ['seuil_jour' => 35, 'seuil_nuit' => 19],
'69' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'70' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'71' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'72' => ['seuil_jour' => 35, 'seuil_nuit' => 20],
'73' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'74' => ['seuil_jour' => 34, 'seuil_nuit' => 19],
'75' => ['seuil_jour' => 31, 'seuil_nuit' => 21],
'76' => ['seuil_jour' => 33, 'seuil_nuit' => 18],
'77' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'78' => ['seuil_jour' => 33, 'seuil_nuit' => 20],
'79' => ['seuil_jour' => 35, 'seuil_nuit' => 20],
'80' => ['seuil_jour' => 33, 'seuil_nuit' => 18],
'81' => ['seuil_jour' => 36, 'seuil_nuit' => 21],
'82' => ['seuil_jour' => 36, 'seuil_nuit' => 21],
'83' => ['seuil_jour' => 35, 'seuil_nuit' => 23],
'84' => ['seuil_jour' => 36, 'seuil_nuit' => 21],
'85' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'86' => ['seuil_jour' => 34, 'seuil_nuit' => 20],
'87' => ['seuil_jour' => 35, 'seuil_nuit' => 19],
'88' => ['seuil_jour' => 34, 'seuil_nuit' => 18],
'89' => ['seuil_jour' => 35, 'seuil_nuit' => 19],
'90' => ['seuil_jour' => 33, 'seuil_nuit' => 18],
'91' => ['seuil_jour' => 35, 'seuil_nuit' => 20],
'92' => ['seuil_jour' => 31, 'seuil_nuit' => 21],
'93' => ['seuil_jour' => 31, 'seuil_nuit' => 21],
'94' => ['seuil_jour' => 31, 'seuil_nuit' => 21],
'95' => ['seuil_jour' => 35, 'seuil_nuit' => 20],
];
}
}