fix: durcir les filtres AFAI — SWIR guard, NDWI marge côtière, filtre PostGIS 1km²
- S2 : ajout garde SWIR (B11 > 0.03 = artifact atmo/cirrus), NIR abaissé à 0.06, NDWI marge côtière (> 0.05), AFAI threshold 0.020 - S3 : même logique, NIR 0.06, NDWI 0.05, MCI 0.020 - PostGIS : suppression des polygones < 1 km² après vectorisation (résidus ponctuels = bruit ou artefact atmosphérique en Caraïbe) - Auto-suppression de l'observation si plus aucun polygone valide - PROCESSING_VERSION 1.2.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ class IngestionService
|
||||
private const SARGASSUM_DN = 1;
|
||||
private const SIMPLIFY_TOLERANCE = 0.001; // degrés (~100m à l'équateur)
|
||||
private const MIN_RING_POINTS = 8; // filtre polygones < ~2 pixels (bruit)
|
||||
private const PROCESSING_VERSION = '1.1.0';
|
||||
private const PROCESSING_VERSION = '1.2.0';
|
||||
|
||||
public function __construct(
|
||||
private SentinelHubClient $sentinelHub,
|
||||
@@ -218,17 +218,46 @@ class IngestionService
|
||||
return ['type' => 'MultiPolygon', 'coordinates' => $polygons];
|
||||
}
|
||||
|
||||
private const MIN_POLYGON_AREA_M2 = 1_000_000; // 1 km² — filtre résidus AFAI
|
||||
|
||||
private function updateSpatialMeta(string $id): void
|
||||
{
|
||||
// 1) Filtrer les micro-polygones < MIN_POLYGON_AREA_M2 avant simplification
|
||||
// Un fragment < 1 km² en plein océan est du bruit ou un artefact atmo.
|
||||
$this->em->getConnection()->executeStatement(
|
||||
"UPDATE sargassum_observation
|
||||
SET geometry = (
|
||||
SELECT COALESCE(
|
||||
ST_Collect(geom),
|
||||
'GEOMETRYCOLLECTION EMPTY'::geometry
|
||||
)
|
||||
FROM (
|
||||
SELECT (ST_Dump(geometry)).geom AS geom
|
||||
) parts
|
||||
WHERE ST_Area(geom::geography) >= :minArea
|
||||
)
|
||||
WHERE id = :id",
|
||||
['id' => $id, 'minArea' => self::MIN_POLYGON_AREA_M2]
|
||||
);
|
||||
|
||||
// 2) Simplifier + calculer bbox et surface finale
|
||||
$this->em->getConnection()->executeStatement(
|
||||
'UPDATE sargassum_observation
|
||||
SET
|
||||
geometry = ST_SimplifyPreserveTopology(geometry, :tolerance),
|
||||
bbox = ST_Envelope(geometry),
|
||||
geometry = ST_SimplifyPreserveTopology(geometry, :tolerance),
|
||||
bbox = ST_Envelope(geometry),
|
||||
coverage_area = ROUND(CAST(ST_Area(geometry::geography) / 1000000 AS numeric), 2)
|
||||
WHERE id = :id',
|
||||
WHERE id = :id
|
||||
AND NOT ST_IsEmpty(geometry)',
|
||||
['id' => $id, 'tolerance' => self::SIMPLIFY_TOLERANCE]
|
||||
);
|
||||
|
||||
// 3) Supprimer l'observation si plus rien ne reste après filtrage
|
||||
$this->em->getConnection()->executeStatement(
|
||||
'DELETE FROM sargassum_observation
|
||||
WHERE id = :id AND (ST_IsEmpty(geometry) OR geometry IS NULL)',
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
private function cleanup(string $dir): void
|
||||
|
||||
@@ -158,14 +158,20 @@ function setup() {
|
||||
function evaluatePixel(sample) {
|
||||
if (sample.CLP > 0.4) { return [255]; } // nodata : nuage
|
||||
|
||||
// Protection sun glint : NIR élevé → reflet solaire ou eau turbide
|
||||
if (sample.B08 > 0.08) { return [0]; }
|
||||
// Masque nuage
|
||||
if (sample.CLP > 0.3) { return [255]; }
|
||||
|
||||
// SWIR élevé = artifact atmo, cirrus fin, aérosol, eau côtière turbide
|
||||
if (sample.B11 > 0.03) { return [0]; }
|
||||
|
||||
// NIR élevé = sun glint ou eau peu profonde
|
||||
if (sample.B08 > 0.06) { return [0]; }
|
||||
|
||||
// Masque eau : NDWI = (Green - NIR) / (Green + NIR) — terre si <= 0
|
||||
const ndwiDenom = sample.B03 + sample.B08;
|
||||
if (ndwiDenom === 0) { return [0]; }
|
||||
const ndwi = (sample.B03 - sample.B08) / ndwiDenom;
|
||||
if (ndwi <= 0) { return [0]; }
|
||||
if (ndwi <= 0.05) { return [0]; } // marge pour eau côtière peu profonde
|
||||
|
||||
const lambdaRed = 664.5;
|
||||
const lambdaNir = 832.8;
|
||||
@@ -174,7 +180,9 @@ function evaluatePixel(sample) {
|
||||
|
||||
const afai = sample.B08 - sample.B04 - (sample.B11 - sample.B04) * ratio;
|
||||
|
||||
return [afai > 0.015 ? 1 : 0];
|
||||
// Seuil 0.020 : calibré pour Sargassum fluitans/natans en Caraïbe
|
||||
// (Hu 2009 recommande post-filtrage spatial que l'on fait via PostGIS)
|
||||
return [afai > 0.020 ? 1 : 0];
|
||||
}
|
||||
EVALSCRIPT;
|
||||
}
|
||||
@@ -200,24 +208,23 @@ function setup() {
|
||||
}
|
||||
|
||||
function evaluatePixel(sample) {
|
||||
// Protection sun glint / eau turbide
|
||||
if (sample.B17 > 0.08) { return [0]; }
|
||||
// NIR élevé = sun glint
|
||||
if (sample.B17 > 0.06) { return [0]; }
|
||||
|
||||
// Masque eau : NDWI = (Green B06 560nm - NIR B17 865nm) / (Green + NIR)
|
||||
const ndwiDenom = sample.B06 + sample.B17;
|
||||
if (ndwiDenom === 0) { return [0]; }
|
||||
const ndwi = (sample.B06 - sample.B17) / ndwiDenom;
|
||||
if (ndwi <= 0) { return [0]; }
|
||||
if (ndwi <= 0.05) { return [0]; }
|
||||
|
||||
const lambdaRed = 665.0;
|
||||
const lambdaRedEdge = 708.75;
|
||||
const lambdaNir = 865.0;
|
||||
|
||||
// MCI : baseline interpolée entre Red et RedEdge
|
||||
const baseline = sample.B08 + (sample.B11 - sample.B08) * (lambdaNir - lambdaRed) / (lambdaRedEdge - lambdaRed);
|
||||
const mci = sample.B17 - baseline;
|
||||
|
||||
return [mci > 0.015 ? 1 : 0];
|
||||
return [mci > 0.020 ? 1 : 0];
|
||||
}
|
||||
EVALSCRIPT;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user