Phase 1 : pipeline d'ingestion Sentinel-2
- SentinelHubClient : auth OAuth2, Catalog API (cloud coverage), Process API avec evalscript AFAI binaire (UINT8, cloud-side) - IngestionService : orchestration complète — job tracking, rejet nuages, téléchargement GeoTIFF, polygonize GDAL, simplification PostGIS, persistance SargassumObservation - IngestSentinelCommand : 5 zones Antilles configurées, options --date et --zone, appelable via cron docker exec - Dockerfile : ajout gdal-bin + python3-gdal - Correction entity : paramètre immutable retiré de #[ORM\Column] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
88
backend/src/Command/IngestSentinelCommand.php
Normal file
88
backend/src/Command/IngestSentinelCommand.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Service\Ingestion\IngestionService;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:ingest-sentinel',
|
||||
description: 'Ingestion des données Sentinel-2 pour la détection de sargasses',
|
||||
)]
|
||||
class IngestSentinelCommand extends Command
|
||||
{
|
||||
// Zones d'intérêt Antilles : [minLon, minLat, maxLon, maxLat]
|
||||
private const ZONES = [
|
||||
'martinique' => [-61.30, 14.30, -60.70, 14.90],
|
||||
'guadeloupe' => [-61.90, 15.80, -61.00, 16.60],
|
||||
'sainte-lucie' => [-61.10, 13.70, -60.80, 14.20],
|
||||
'barbade' => [-59.80, 13.00, -59.30, 13.40],
|
||||
'saint-martin' => [-63.20, 17.80, -62.90, 18.20],
|
||||
];
|
||||
|
||||
public function __construct(private IngestionService $ingestionService)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addOption('date', 'd', InputOption::VALUE_OPTIONAL,
|
||||
'Date à traiter (YYYY-MM-DD). Défaut : hier.', null)
|
||||
->addOption('zone', 'z', InputOption::VALUE_OPTIONAL,
|
||||
'Zone spécifique. Défaut : toutes les zones.', null)
|
||||
->addOption('source', 's', InputOption::VALUE_OPTIONAL,
|
||||
'Source satellite (Sentinel-2 ou Sentinel-3)', 'Sentinel-2');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$dateStr = $input->getOption('date');
|
||||
$date = $dateStr
|
||||
? new \DateTimeImmutable($dateStr)
|
||||
: new \DateTimeImmutable('yesterday');
|
||||
|
||||
$source = $input->getOption('source');
|
||||
|
||||
$zones = self::ZONES;
|
||||
if ($zoneName = $input->getOption('zone')) {
|
||||
if (!isset(self::ZONES[$zoneName])) {
|
||||
$io->error(sprintf(
|
||||
"Zone inconnue : \"%s\". Zones disponibles : %s",
|
||||
$zoneName,
|
||||
implode(', ', array_keys(self::ZONES))
|
||||
));
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$zones = [$zoneName => self::ZONES[$zoneName]];
|
||||
}
|
||||
|
||||
$io->title(sprintf('Ingestion %s — %s', $source, $date->format('Y-m-d')));
|
||||
|
||||
$errors = 0;
|
||||
foreach ($zones as $name => $bbox) {
|
||||
$io->section("Zone : {$name}");
|
||||
try {
|
||||
$this->ingestionService->ingest($bbox, $date, $source);
|
||||
$io->success("OK — {$name}");
|
||||
} catch (\Throwable $e) {
|
||||
$io->error("ÉCHEC — {$name} : " . $e->getMessage());
|
||||
$errors++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors > 0) {
|
||||
$io->warning("{$errors} zone(s) en erreur.");
|
||||
}
|
||||
|
||||
return $errors === 0 ? Command::SUCCESS : Command::FAILURE;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user