Skip to content

Commit 31b8ca2

Browse files
committed
Idepix OLCI: introduced operator which generates L1b product with harmonised radiances 13-15, optionally used as input in IdepixOlciOp
1 parent 77320ea commit 31b8ca2

File tree

5 files changed

+175
-112
lines changed

5 files changed

+175
-112
lines changed

idepix-olci/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
</parent>
2727

2828
<artifactId>idepix-olci</artifactId>
29-
<version>12.0.1</version>
29+
<version>12.0.2</version>
3030

3131
<packaging>nbm</packaging>
3232

idepix-olci/src/main/java/org/esa/snap/idepix/olci/IdepixOlciClassificationOp.java

Lines changed: 7 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,6 @@ public class IdepixOlciClassificationOp extends Operator {
125125

126126
private Band[] olciReflBands;
127127

128-
private Band solarFlux13Band;
129-
private Band solarFlux14Band;
130-
private Band solarFlux15Band;
131-
132-
private RasterDataNode szaBand;
133-
134-
private Band harmoRad13Band;
135-
private Band harmoRad14Band;
136-
private Band harmoRad15Band;
137-
138128
private Band surface13Band;
139129
private Band trans13Band;
140130

@@ -188,15 +178,6 @@ public void initialize() throws OperatorException {
188178
initLakeSeaIceClassification();
189179

190180
if (o2CorrProduct != null) {
191-
szaBand = l1bProduct.getRasterDataNode("SZA");
192-
193-
solarFlux13Band = l1bProduct.getBand("solar_flux_band_13");
194-
solarFlux14Band = l1bProduct.getBand("solar_flux_band_14");
195-
solarFlux15Band = l1bProduct.getBand("solar_flux_band_15");
196-
197-
harmoRad13Band = l1bProduct.getBand("radiance_13");
198-
harmoRad14Band = l1bProduct.getBand("radiance_14");
199-
harmoRad15Band = l1bProduct.getBand("radiance_15");
200181
surface13Band = o2CorrProduct.getBand("surface_13");
201182
trans13Band = o2CorrProduct.getBand("trans_13");
202183
gf = new GeometryFactory();
@@ -300,39 +281,6 @@ public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle rectangle, P
300281
trans13Tile = getSourceTile(trans13Band, rectangle);
301282
}
302283

303-
Tile szaTile = null;
304-
if (szaBand != null) {
305-
szaTile = getSourceTile(szaBand, rectangle);
306-
}
307-
308-
Tile solarFlux13Tile = null;
309-
if (solarFlux13Band != null) {
310-
solarFlux13Tile = getSourceTile(solarFlux13Band, rectangle);
311-
}
312-
Tile solarFlux14Tile = null;
313-
if (solarFlux14Band != null) {
314-
solarFlux14Tile = getSourceTile(solarFlux14Band, rectangle);
315-
}
316-
Tile solarFlux15Tile = null;
317-
if (solarFlux15Band != null) {
318-
solarFlux15Tile = getSourceTile(solarFlux15Band, rectangle);
319-
}
320-
321-
Tile harmoRad13Tile = null;
322-
if (harmoRad13Band != null) {
323-
harmoRad13Tile = getSourceTile(harmoRad13Band, rectangle);
324-
}
325-
326-
Tile harmoRad14Tile = null;
327-
if (harmoRad14Band != null) {
328-
harmoRad14Tile = getSourceTile(harmoRad14Band, rectangle);
329-
}
330-
331-
Tile harmoRad15Tile = null;
332-
if (harmoRad15Band != null) {
333-
harmoRad15Tile = getSourceTile(harmoRad15Band, rectangle);
334-
}
335-
336284
final Band olciQualityFlagBand = l1bProduct.getBand(IdepixOlciConstants.OLCI_QUALITY_FLAGS_BAND_NAME);
337285
final Tile olciQualityFlagTile = getSourceTile(olciQualityFlagBand, rectangle);
338286

@@ -374,16 +322,10 @@ public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle rectangle, P
374322
// todo: for cglops, coastlines are treated as LAND
375323
if ((isLandFromAppliedMask && !isInlandWaterFromAppliedMask) || isCoastlineFromAppliedMask) {
376324
classifyOverLand(olciReflectanceTiles, cloudFlagTargetTile, nnTargetTile,
377-
surface13Tile, trans13Tile,
378-
solarFlux13Tile, solarFlux14Tile, solarFlux15Tile,
379-
harmoRad13Tile, harmoRad14Tile, harmoRad15Tile,
380-
szaTile, x, y);
325+
surface13Tile, trans13Tile, x, y);
381326
} else {
382327
classifyOverWater(olciQualityFlagTile, olciReflectanceTiles,
383-
cloudFlagTargetTile, nnTargetTile,
384-
solarFlux13Tile, solarFlux14Tile, solarFlux15Tile,
385-
harmoRad13Tile, harmoRad14Tile, harmoRad15Tile,
386-
szaTile, x, y, isInlandWaterFromAppliedMask);
328+
cloudFlagTargetTile, nnTargetTile, x, y, isInlandWaterFromAppliedMask);
387329
}
388330
}
389331
}
@@ -399,16 +341,10 @@ private boolean classifyCoastline(Tile olciQualityFlagTile, int x, int y, int wa
399341
}
400342

401343
private void classifyOverWater(Tile olciQualityFlagTile, Tile[] olciReflectanceTiles,
402-
Tile cloudFlagTargetTile, Tile nnTargetTile,
403-
Tile solarFlux13Tile, Tile solarFlux14Tile, Tile solarFlux15Tile,
404-
Tile harmoRad13Tile, Tile harmoRad14Tile, Tile harmoRad15Tile,
405-
Tile szaTile, int x, int y, boolean isInlandWater) {
406-
407-
final double[] o2HarmoRefls = getO2HarmoReflectances(solarFlux13Tile, solarFlux14Tile, solarFlux15Tile,
408-
harmoRad13Tile, harmoRad14Tile, harmoRad15Tile, szaTile, x, y);
344+
Tile cloudFlagTargetTile, Tile nnTargetTile, int x, int y, boolean isInlandWater) {
409345

410-
final double nnOutput = getOlciNNOutput(x, y, olciReflectanceTiles, o2HarmoRefls);
411346

347+
double nnOutput = getOlciNNOutput(x, y, olciReflectanceTiles);
412348
if (!cloudFlagTargetTile.getSampleBit(x, y, IdepixConstants.IDEPIX_INVALID)) {
413349
cloudFlagTargetTile.setSample(x, y, IdepixConstants.IDEPIX_CLOUD_AMBIGUOUS, false);
414350
cloudFlagTargetTile.setSample(x, y, IdepixConstants.IDEPIX_CLOUD_SURE, false);
@@ -458,14 +394,9 @@ private void classifyOverWater(Tile olciQualityFlagTile, Tile[] olciReflectanceT
458394
private void classifyOverLand(Tile[] olciReflectanceTiles,
459395
Tile cloudFlagTargetTile, Tile nnTargetTile,
460396
Tile surface13Tile, Tile trans13Tile,
461-
Tile solarFlux13Tile, Tile solarFlux14Tile, Tile solarFlux15Tile,
462-
Tile harmoRad13Tile, Tile harmoRad14Tile, Tile harmoRad15Tile,
463-
Tile szaTile, int x, int y) {
397+
int x, int y) {
464398

465-
final double[] o2HarmoRefls = getO2HarmoReflectances(solarFlux13Tile, solarFlux14Tile, solarFlux15Tile,
466-
harmoRad13Tile, harmoRad14Tile, harmoRad15Tile, szaTile, x, y);
467-
468-
final double nnOutput = getOlciNNOutput(x, y, olciReflectanceTiles, o2HarmoRefls);
399+
final double nnOutput = getOlciNNOutput(x, y, olciReflectanceTiles);
469400

470401
if (!cloudFlagTargetTile.getSampleBit(x, y, IdepixConstants.IDEPIX_INVALID)) {
471402
cloudFlagTargetTile.setSample(x, y, IdepixConstants.IDEPIX_CLOUD_AMBIGUOUS, false);
@@ -524,32 +455,6 @@ private void classifyOverLand(Tile[] olciReflectanceTiles,
524455
}
525456
}
526457

527-
private static double[] getO2HarmoReflectances(Tile solarFlux13Tile, Tile solarFlux14Tile, Tile solarFlux15Tile, Tile harmoRad13Tile, Tile harmoRad14Tile, Tile harmoRad15Tile, Tile szaTile, int x, int y) {
528-
float sza;
529-
float sf13;
530-
float sf14;
531-
float sf15;
532-
float harmoRad13;
533-
float harmoRad14;
534-
float harmoRad15;
535-
if (szaTile != null && solarFlux13Tile != null && solarFlux14Tile != null && solarFlux15Tile != null &&
536-
harmoRad13Tile != null && harmoRad14Tile != null && harmoRad15Tile != null) {
537-
sza = szaTile.getSampleFloat(x, y);
538-
sf13 = solarFlux13Tile.getSampleFloat(x, y);
539-
sf14 = solarFlux14Tile.getSampleFloat(x, y);
540-
sf15 = solarFlux15Tile.getSampleFloat(x, y);
541-
harmoRad13 = harmoRad13Tile.getSampleFloat(x, y);
542-
harmoRad14 = harmoRad14Tile.getSampleFloat(x, y);
543-
harmoRad15 = harmoRad15Tile.getSampleFloat(x, y);
544-
545-
final double harmoRefl13 = RsMathUtils.radianceToReflectance(harmoRad13, sza, sf13);
546-
final double harmoRefl14 = RsMathUtils.radianceToReflectance(harmoRad14, sza, sf14);
547-
final double harmoRefl15 = RsMathUtils.radianceToReflectance(harmoRad15, sza, sf15);
548-
return new double[]{harmoRefl13, harmoRefl14, harmoRefl15};
549-
}
550-
return null;
551-
}
552-
553458
private boolean isOlciLandPixel(int x, int y, Tile olciL1bFlagTile, int waterFraction) {
554459
if (waterFraction < 0) {
555460
boolean landFlag = olciL1bFlagTile.getSampleBit(x, y, IdepixOlciConstants.L1_F_LAND);
@@ -597,7 +502,7 @@ private boolean isOlciInlandWaterPixel(int x, int y, Tile olciL1bFlagTile, int w
597502
}
598503
}
599504

600-
private double getOlciNNOutput(int x, int y, Tile[] rhoToaTiles, double[] o2HarmoRefls) {
505+
private double getOlciNNOutput(int x, int y, Tile[] rhoToaTiles) {
601506
SchillerNeuralNetWrapper nnWrapper;
602507
try {
603508
nnWrapper = olciAllNeuralNet.get();
@@ -608,11 +513,6 @@ private double getOlciNNOutput(int x, int y, Tile[] rhoToaTiles, double[] o2Harm
608513
for (int i = 0; i < nnInput.length; i++) {
609514
nnInput[i] = Math.sqrt(rhoToaTiles[i].getSampleFloat(x, y));
610515
}
611-
if (o2HarmoRefls != null) {
612-
for (int i = 0; i < 3; i++) {
613-
nnInput[i + 12] = Math.sqrt(o2HarmoRefls[i]);
614-
}
615-
}
616516
return nnWrapper.getNeuralNet().calc(nnInput)[0];
617517
}
618518

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package org.esa.snap.idepix.olci;
2+
3+
import com.bc.ceres.core.ProgressMonitor;
4+
import eu.esa.snap.core.datamodel.group.BandGroup;
5+
import org.esa.snap.core.datamodel.Band;
6+
import org.esa.snap.core.datamodel.Product;
7+
import org.esa.snap.core.gpf.Operator;
8+
import org.esa.snap.core.gpf.OperatorException;
9+
import org.esa.snap.core.gpf.OperatorSpi;
10+
import org.esa.snap.core.gpf.Tile;
11+
import org.esa.snap.core.gpf.annotations.OperatorMetadata;
12+
import org.esa.snap.core.gpf.annotations.Parameter;
13+
import org.esa.snap.core.gpf.annotations.SourceProduct;
14+
import org.esa.snap.core.gpf.annotations.TargetProduct;
15+
import org.esa.snap.core.util.ProductUtils;
16+
17+
/**
18+
* todo
19+
*
20+
* @author Olaf Danne
21+
*/
22+
@OperatorMetadata(alias = "IdepixOlciMergeO2Harmonize",
23+
category = "Raster",
24+
description = "Allows copying raster data from any number of source products to a specified 'master' product.",
25+
authors = "SNAP team",
26+
internal = true,
27+
version = "1.0",
28+
copyright = "(c) 2025 by Brockmann Consult")
29+
public class IdepixOlciMergeO2HarmoniseOp extends Operator {
30+
31+
@SourceProduct(alias = "l1bProduct",
32+
label = "OLCI L1b product",
33+
description = "The OLCI L1b source product.")
34+
private Product l1bProduct;
35+
36+
@SourceProduct(alias = "o2harmoProduct",
37+
label = "OLCI O2A harmonisation product",
38+
description = "The OLCI O2A harmonisation product.")
39+
private Product o2harmoProduct;
40+
41+
@TargetProduct
42+
private Product targetProduct;
43+
44+
@Parameter(description = "The list of source bands.", alias = "sourceBands", label = "Source Bands")
45+
private String[] sourceBandNames;
46+
47+
@Parameter(defaultValue = "1.0E-5f",
48+
description = "Defines the maximum lat/lon error in degree between the products.")
49+
private float geographicError;
50+
51+
private final String[] excludeBandNames = {"Oa13_radiance", "Oa14_radiance", "Oa15_radiance"};
52+
private final String[] excludeBandNamePrefixes = {"lambda0", "FWHM"};
53+
private final String[] excludeBandNameSufffixes = {"unc"};
54+
55+
@Override
56+
public void initialize() throws OperatorException {
57+
targetProduct = new Product(l1bProduct.getName(),
58+
l1bProduct.getProductType(),
59+
l1bProduct.getSceneRasterWidth(),
60+
l1bProduct.getSceneRasterHeight());
61+
62+
if (!l1bProduct.isCompatibleProduct(o2harmoProduct, geographicError)) {
63+
throw new OperatorException("O2A harmonisation product is not compatible to L1b product.");
64+
}
65+
66+
ProductUtils.copyProductNodes(l1bProduct, targetProduct);
67+
68+
for (Band band : l1bProduct.getBands()) {
69+
// copy only bands required by OLCI Idepix
70+
if (!targetProduct.containsRasterDataNode(band.getName())) {
71+
boolean doCopy = true;
72+
for (String excludeBandName : excludeBandNames) {
73+
if (band.getName().equals(excludeBandName)) {
74+
doCopy = false;
75+
break;
76+
}
77+
}
78+
if (doCopy) {
79+
for (String excludeBandNamePrefix : excludeBandNamePrefixes) {
80+
if (band.getName().startsWith(excludeBandNamePrefix)) {
81+
doCopy = false;
82+
break;
83+
}
84+
}
85+
}
86+
if (doCopy) {
87+
for (String excludeBandNameSufffix : excludeBandNameSufffixes) {
88+
if (band.getName().endsWith(excludeBandNameSufffix)) {
89+
doCopy = false;
90+
break;
91+
}
92+
}
93+
}
94+
if (doCopy) {
95+
ProductUtils.copyBand(band.getName(), l1bProduct, targetProduct, true);
96+
}
97+
}
98+
}
99+
100+
for (Band band : o2harmoProduct.getBands()) {
101+
if (!targetProduct.containsRasterDataNode(band.getName())) {
102+
if (!band.getName().startsWith("radiance")) {
103+
ProductUtils.copyBand(band.getName(), o2harmoProduct, targetProduct, true);
104+
} else {
105+
final int length = band.getName().length();
106+
final String bandIndex = band.getName().substring(length - 2, length);
107+
System.out.println("bandIndex = " + bandIndex);
108+
final String l1bRadBandName = "Oa" + bandIndex + "_radiance";
109+
ProductUtils.copyBand(band.getName(), o2harmoProduct, l1bRadBandName, targetProduct, true);
110+
ProductUtils.copySpectralBandProperties(l1bProduct.getBand(l1bRadBandName),
111+
targetProduct.getBand(l1bRadBandName));
112+
}
113+
}
114+
}
115+
116+
mergeAutoGrouping(l1bProduct);
117+
ProductUtils.copyMasks(l1bProduct, targetProduct);
118+
ProductUtils.copyOverlayMasks(l1bProduct, targetProduct);
119+
120+
}
121+
122+
private void mergeAutoGrouping(Product srcProduct) {
123+
final BandGroup srcAutoGrouping = srcProduct.getAutoGrouping();
124+
if (srcAutoGrouping != null && !srcAutoGrouping.isEmpty()) {
125+
final BandGroup targetAutoGrouping = targetProduct.getAutoGrouping();
126+
if (targetAutoGrouping == null) {
127+
targetProduct.setAutoGrouping(srcAutoGrouping);
128+
} else {
129+
for (String[] grouping : srcAutoGrouping) {
130+
if (!targetAutoGrouping.contains(grouping)) {
131+
targetProduct.setAutoGrouping(targetAutoGrouping + ":" + srcAutoGrouping);
132+
}
133+
}
134+
}
135+
}
136+
}
137+
138+
139+
@Override
140+
public void computeTile(Band band, Tile targetTile, ProgressMonitor pm) throws OperatorException {
141+
getLogger().warning("Wrongly configured operator. Tiles should not be requested.");
142+
}
143+
144+
public static class Spi extends OperatorSpi {
145+
146+
public Spi() {
147+
super(IdepixOlciMergeO2HarmoniseOp.class);
148+
}
149+
}
150+
}
151+

idepix-olci/src/main/java/org/esa/snap/idepix/olci/IdepixOlciOp.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ public class IdepixOlciOp extends BasisOp {
141141
"Slower, but in general more precise.")
142142
private boolean useSrtmLandWaterMask;
143143

144+
private Product l1bProductToProcess;
144145

145146
private Product classificationProduct;
146147
private Product postProcessingProduct;
@@ -221,7 +222,7 @@ private Product createTargetProduct(Product idepixProduct) {
221222
IdepixOlciUtils.setupOlciClassifBitmask(targetProduct);
222223

223224
if (outputRadiance) {
224-
IdepixIO.addRadianceBands(sourceProduct, targetProduct, radianceBandsToCopy);
225+
IdepixIO.addRadianceBands(l1bProductToProcess, targetProduct, radianceBandsToCopy);
225226
}
226227
if (outputRad2Refl) {
227228
IdepixOlciUtils.addOlciRadiance2ReflectanceBands(rad2reflProduct, targetProduct, reflBandsToCopy);
@@ -259,7 +260,17 @@ private void preProcess() {
259260
}
260261
}
261262

262-
rad2reflProduct = IdepixOlciUtils.computeRadiance2ReflectanceProduct(sourceProduct);
263+
if (useO2HarmonizedRadiancesForNN) {
264+
Map<String, Product> l1bO2MergeSourceProducts = new HashMap<>();
265+
Map<String, Object> emptyParms = new HashMap<>();
266+
l1bO2MergeSourceProducts.put("l1bProduct", sourceProduct);
267+
l1bO2MergeSourceProducts.put("o2harmoProduct", o2CorrProduct);
268+
l1bProductToProcess = GPF.createProduct("IdepixOlciMergeO2Harmonize", emptyParms, l1bO2MergeSourceProducts);
269+
} else {
270+
l1bProductToProcess = sourceProduct;
271+
}
272+
273+
rad2reflProduct = IdepixOlciUtils.computeRadiance2ReflectanceProduct(l1bProductToProcess);
263274
}
264275

265276
private void setClassificationParameters() {
@@ -281,7 +292,7 @@ private void computeCloudProduct() {
281292

282293
private void setClassificationInputProducts() {
283294
classificationInputProducts = new HashMap<>();
284-
classificationInputProducts.put("l1b", sourceProduct);
295+
classificationInputProducts.put("l1b", l1bProductToProcess);
285296
classificationInputProducts.put("rhotoa", rad2reflProduct);
286297
if (considerCloudsOverSnow) {
287298
classificationInputProducts.put("o2Corr", o2CorrProduct);
@@ -290,7 +301,7 @@ private void setClassificationInputProducts() {
290301

291302
private void postProcess(Product olciIdepixProduct) {
292303
HashMap<String, Product> input = new HashMap<>();
293-
input.put("l1b", sourceProduct);
304+
input.put("l1b", l1bProductToProcess);
294305
input.put("ctp", ctpProduct);
295306
input.put("olciCloud", olciIdepixProduct);
296307

0 commit comments

Comments
 (0)