Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

integration of cams auxillary data #71

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions s3tbx-c2rcc/src/assembly/c2rcc-ncep-graph.xml.vm
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ $ozone1Calendar.add(5, 1)
<sources>
<sourceProduct>input</sourceProduct>

<tomsomiStartProduct>ozone1</tomsomiStartProduct>
<tomsomiEndProduct>ozone2</tomsomiEndProduct>
<ozoneStartProduct>ozone1</ozoneStartProduct>
<ozoneEndProduct>ozone2</ozoneEndProduct>

<ncepStartProduct>ncep1</ncepStartProduct>
<ncepEndProduct>ncep2</ncepEndProduct>
<pressureStartProduct>ncep1</pressureStartProduct>
<pressureEndProduct>ncep2</pressureEndProduct>
</sources>
</node>
</graph>
8 changes: 4 additions & 4 deletions s3tbx-c2rcc/src/assembly/c2rcc-ncep-pml-graph.xml.vm
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ $ozone1Calendar.add(5, 1)
<sources>
<sourceProduct>input</sourceProduct>

<tomsomiStartProduct>ozone1</tomsomiStartProduct>
<tomsomiEndProduct>ozone2</tomsomiEndProduct>
<ozoneStartProduct>ozone1</ozoneStartProduct>
<ozoneEndProduct>ozone2</ozoneEndProduct>

<ncepStartProduct>ncep1</ncepStartProduct>
<ncepEndProduct>ncep2</ncepEndProduct>
<pressureStartProduct>ncep1</pressureStartProduct>
<pressureEndProduct>ncep2</pressureEndProduct>
</sources>
</node>
</graph>
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ public interface C2rccConfigurable {

void setAtmosphericAuxDataPath(String atmosphericAuxDataPath);

void setTomsomiStartProduct(Product tomsomiStartProduct);
void setOzoneStartProduct(Product ozoneStartProduct);

void setTomsomiEndProduct(Product tomsomiEndProduct);
void setOzoneEndProduct(Product ozoneEndProduct);

void setNcepStartProduct(Product ncepStartProduct);
void setPressureStartProduct(Product pressureStartProduct);

void setNcepEndProduct(Product ncepEndProduct);
void setPressureEndProduct(Product pressureEndProduct);

void setTemperature(double temperature);

Expand Down
32 changes: 16 additions & 16 deletions s3tbx-c2rcc/src/main/java/org/esa/s3tbx/c2rcc/C2rccOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,28 +46,28 @@ public class C2rccOperator extends Operator {
private Product sourceProduct;

@SourceProduct(description = "The first product providing ozone values for ozone interpolation. " +
"Use either the TOMSOMI and NCEP products or the atmosphericAuxdataPath to as source for ozone and air pressure.",
"Use either the TOMSOMI and NCEP products or CAMS products or the atmosphericAuxdataPath to as source for ozone and air pressure.",
optional = true,
label = "Ozone interpolation start product (TOMSOMI)")
private Product tomsomiStartProduct;
label = "Ozone interpolation start product (TOMSOMI or CAMS)")
private Product ozoneStartProduct;

@SourceProduct(description = "The second product providing ozone values for ozone interpolation. " +
"Use either the TOMSOMI and NCEP products or the atmosphericAuxdataPath to as source for ozone and air pressure.",
"Use either the TOMSOMI and NCEP products or CAMS products or the atmosphericAuxdataPath to as source for ozone and air pressure.",
optional = true,
label = "Ozone interpolation end product (TOMSOMI)")
private Product tomsomiEndProduct;
label = "Ozone interpolation end product (TOMSOMI or CAMS)")
private Product ozoneEndProduct;

@SourceProduct(description = "The first product providing air pressure values for pressure interpolation. " +
"Use either the TOMSOMI and NCEP products or the atmosphericAuxdataPath to as source for ozone and air pressure.",
"Use either the TOMSOMI and NCEP products or CAMS products or the atmosphericAuxdataPath to as source for ozone and air pressure.",
optional = true,
label = "Air pressure interpolation start product (NCEP)")
private Product ncepStartProduct;
label = "Air pressure interpolation start product (NCEP or CAMS)")
private Product pressureStartProduct;

@SourceProduct(description = "The second product providing air pressure values for pressure interpolation. " +
"Use either the TOMSOMI and NCEP products or the atmosphericAuxdataPath to as source for ozone and air pressure.",
"Use either the TOMSOMI and NCEP products or CAMS products or the atmosphericAuxdataPath to as source for ozone and air pressure.",
optional = true,
label = "Air pressure interpolation end product (NCEP)")
private Product ncepEndProduct;
label = "Air pressure interpolation end product (NCEP or CAMS)")
private Product pressureEndProduct;

@TargetProduct
private Product targetProduct;
Expand Down Expand Up @@ -163,10 +163,10 @@ private Product setSourceAndGetTarget(Operator operator) {
}

private void configure(C2rccConfigurable c2rConfigOp) {
c2rConfigOp.setTomsomiStartProduct(tomsomiStartProduct);
c2rConfigOp.setTomsomiEndProduct(tomsomiEndProduct);
c2rConfigOp.setNcepStartProduct(ncepStartProduct);
c2rConfigOp.setNcepEndProduct(ncepEndProduct);
c2rConfigOp.setOzoneStartProduct(ozoneStartProduct);
c2rConfigOp.setOzoneEndProduct(ozoneEndProduct);
c2rConfigOp.setPressureStartProduct(pressureStartProduct);
c2rConfigOp.setPressureEndProduct(pressureEndProduct);
if (StringUtils.isNotNullAndNotEmpty(validPixelExpression)) {
c2rConfigOp.setValidPixelExpression(validPixelExpression);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.esa.s3tbx.c2rcc.ancillary;

import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.core.datamodel.RasterDataNode;
import org.esa.snap.core.util.StringUtils;
import org.esa.snap.core.util.SystemUtils;

import java.io.File;
import java.util.Arrays;

/**
* @author Marco Peters
Expand All @@ -15,34 +17,39 @@ public class AtmosphericAuxdataBuilder {
private RasterDataNode ozoneRaster;
private RasterDataNode surfPressureRaster;
private String atmosphericAuxDataPath;
private Product tomsomiStartProduct;
private Product tomsomiEndProduct;
private Product ncepStartProduct;
private Product ncepEndProduct;
private Product ozoneStartProduct;
private Product ozoneEndProduct;
private Product pressureStartProduct;
private Product pressureEndProduct;

private double ozone = 330;
private double surfacePressure = 1000;
private ProductData.UTC sourceTime = null;

public void useAtmosphericAuxDataPath(String atmosphericAuxDataPath) {
this.atmosphericAuxDataPath = atmosphericAuxDataPath;
}

public void useNcepProducts(Product ncepStartProduct, Product ncepEndProduct) {
this.ncepStartProduct = ncepStartProduct;
this.ncepEndProduct = ncepEndProduct;
public void useNcepCamsProducts(Product pressureStartProduct, Product pressureEndProduct) {
this.pressureStartProduct = pressureStartProduct;
this.pressureEndProduct = pressureEndProduct;
}

public void setOzone(double ozone) {
this.ozone = ozone;
}

public void setSourceTime(ProductData.UTC sourceTime) {
this.sourceTime = sourceTime;
}

public void setSurfacePressure(double press) {
this.surfacePressure = press;
}

public void useTomsomiProducts(Product tomsomiStartProduct, Product tomsomiEndProduct) {
this.tomsomiStartProduct = tomsomiStartProduct;
this.tomsomiEndProduct = tomsomiEndProduct;
public void useTomsomiCamsProducts(Product ozoneStartProduct, Product ozoneEndProduct) {
this.ozoneStartProduct = ozoneStartProduct;
this.ozoneEndProduct = ozoneEndProduct;

}

Expand All @@ -57,13 +64,17 @@ public AtmosphericAuxdata create() throws Exception {
auxdata = new RasterAtmosphericAuxdata(ozoneRaster, surfPressureRaster);
} else {
if (StringUtils.isNullOrEmpty(atmosphericAuxDataPath)) {
if (tomsomiStartProduct == null || tomsomiEndProduct == null || ncepStartProduct == null || ncepEndProduct == null) {
if (ozoneStartProduct == null || ozoneEndProduct == null || pressureStartProduct == null || pressureEndProduct == null) {
SystemUtils.LOG.info("Atmospheric auxdata product can't be used. At least one is not specified. " +
"Using constant values for ozone (" + ozone + ") and surface pressure (" + surfacePressure + ").");
"Using constant values for ozone (" + ozone + ") and surface pressure (" + surfacePressure + ").");
auxdata = new ConstantAtmosphericAuxdata(ozone, surfacePressure);
} else {
auxdata = new AtmosphericAuxdataStatic(tomsomiStartProduct, tomsomiEndProduct, "ozone", ozone,
ncepStartProduct, ncepEndProduct, "press", surfacePressure);
boolean contains_cams_band_gtco3 = Arrays.stream(ozoneStartProduct.getBandNames()).anyMatch(name -> name.contains("gtco3"));
boolean contains_cams_band_msl = Arrays.stream(pressureStartProduct.getBandNames()).anyMatch(name -> name.contains("msl"));
String ozoneBandName = contains_cams_band_gtco3 ? "gtco3" : "ozone";
String pressureBandName = contains_cams_band_msl ? "msl" : "press";
auxdata = new AtmosphericAuxdataStatic(ozoneStartProduct, ozoneEndProduct, ozoneBandName, ozone,
pressureStartProduct, pressureEndProduct, pressureBandName, surfacePressure, sourceTime);
}
} else {
final AncDownloader ancDownloader = new AncDownloader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@

import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import java.time.LocalDateTime;
import java.util.*;

class AtmosphericAuxdataStatic implements AtmosphericAuxdata {

Expand All @@ -17,9 +16,9 @@ class AtmosphericAuxdataStatic implements AtmosphericAuxdata {
AtmosphericAuxdataStatic(Product startOzone, Product endOzone,
String ozoneBandName, double ozoneDefault,
Product startPressure, Product endPressure,
String pressureBandName, double pressureDefault) throws IOException {
this(getOzoneInterpolator(startOzone, endOzone, ozoneBandName, ozoneDefault),
getPressureInterpolator(startPressure, endPressure, pressureBandName, pressureDefault));
String pressureBandName, double pressureDefault, ProductData.UTC sourceTime) throws IOException {
this(getOzoneInterpolator(startOzone, endOzone, ozoneBandName, ozoneDefault, sourceTime),
getPressureInterpolator(startPressure, endPressure, pressureBandName, pressureDefault, sourceTime));
}

private AtmosphericAuxdataStatic(DataInterpolator ozoneInterpolator, DataInterpolator pressureInterpolator) {
Expand All @@ -46,29 +45,49 @@ public void dispose() {
pressureInterpolator.dispose();
}

private static DataInterpolator getOzoneInterpolator(Product startOzone, Product endOzone, String ozoneBandName, double ozoneDefault) throws IOException {
private static DataInterpolator getOzoneInterpolator(Product startOzone, Product endOzone, String ozoneBandName, double ozoneDefault, ProductData.UTC sourceTime) throws IOException {
if (!isValidProduct(startOzone)) {
throw new IOException("Ozone interpolation start product is invalid.");
}
if (!isValidProduct(endOzone)) {
throw new IOException("Ozone interpolation end product is invalid.");
}
final double halfDayOffset = 0.5;
final double ozoneTimeStart = getTime(startOzone) + halfDayOffset;
final double ozoneTimeEnd = getTime(endOzone) + halfDayOffset;
final double ozoneTimeStart;
final double ozoneTimeEnd;
if (ozoneBandName.equals("gtco3")) {
CAMSBandResult ozoneCAMSBandStart = getTimeCAMS(startOzone, true, sourceTime);
CAMSBandResult ozoneCAMSBandEnd = getTimeCAMS(endOzone, false, sourceTime);
ozoneTimeStart = ozoneCAMSBandStart.getTime();
ozoneTimeEnd = ozoneCAMSBandEnd.getTime();
ozoneBandName = ozoneBandName + "_" + ozoneCAMSBandStart.getPositon() + "_" + ozoneCAMSBandEnd.getPositon();
} else {
ozoneTimeStart = getTime(startOzone) + halfDayOffset;
ozoneTimeEnd = getTime(endOzone) + halfDayOffset;
}
return new DataInterpolatorStatic(ozoneTimeStart, ozoneTimeEnd, startOzone, endOzone, ozoneBandName, ozoneDefault);
}

private static DataInterpolator getPressureInterpolator(Product startPressure, Product endPressure, String pressureBandName, double pressureDefault) throws IOException {
private static DataInterpolator getPressureInterpolator(Product startPressure, Product endPressure, String pressureBandName, double pressureDefault, ProductData.UTC sourceTime) throws IOException {
if (!isValidProduct(startPressure)) {
throw new IOException("Air pressure interpolation start product is invalid.");
}
if (!isValidProduct(endPressure)) {
throw new IOException("Air pressure interpolation end product is invalid.");
}
final double threeHoursOffset = 0.125;
final double pressureTimeStart = getTime(startPressure) + threeHoursOffset;
final double pressureTimeEnd = getTime(endPressure) + threeHoursOffset;
final double pressureTimeStart;
final double pressureTimeEnd;
if (pressureBandName.equals("msl")) {
CAMSBandResult pressureCAMSBandStart = getTimeCAMS(startPressure, true, sourceTime);
CAMSBandResult pressureCAMSBandEnd = getTimeCAMS(endPressure, false, sourceTime);
pressureTimeStart = pressureCAMSBandStart.getTime();
pressureTimeEnd = pressureCAMSBandEnd.getTime();
pressureBandName = pressureBandName + "_" + pressureCAMSBandStart.getPositon() + "_" + pressureCAMSBandStart.getPositon();
} else {
pressureTimeStart = getTime(startPressure) + threeHoursOffset;
pressureTimeEnd = getTime(endPressure) + threeHoursOffset;
}
return new DataInterpolatorStatic(pressureTimeStart, pressureTimeEnd, startPressure, endPressure, pressureBandName, pressureDefault);
}

Expand All @@ -88,6 +107,34 @@ private static double getTime(Product product) {
return product.getStartTime().getMJD();
}

private static CAMSBandResult getTimeCAMS(Product product, Boolean isStartProduct, ProductData.UTC sourceTime) {
double sourceTime_mjd = sourceTime.getMJD();
ProductData.Int availableTimes = (ProductData.Int) product.getMetadataRoot().getElement("Variable_Attributes").getElement("time").getElement("Values").getAttribute("data").getData();

ArrayList<Double> mjd_times = new ArrayList<>();
Calendar calendar;
for (int time : availableTimes.getArray()) {
calendar = createCalendarByHoursSince1900(time);
mjd_times.add(ProductData.UTC.create(calendar.getTime(), 0).getMJD());
}
CAMSBandResult bandResult;
int position = Collections.binarySearch(mjd_times, sourceTime_mjd);
if (isStartProduct) {
if (position >= 0) {
bandResult = new CAMSBandResult(position, mjd_times.get(position));
} else {
bandResult = new CAMSBandResult(-1 * position - 2, mjd_times.get(-1 * position - 2));
}
} else {
if (position >= 0) {
bandResult = new CAMSBandResult(position, mjd_times.get(position));
} else {
bandResult = new CAMSBandResult(-1 * position - 1, mjd_times.get(-1 * position - 1));
}
}
return bandResult;
}

static Calendar createCalendarByFilename(String fileName) {
int year = Integer.parseInt(fileName.substring(1, 5));
int doy = Integer.parseInt(fileName.substring(5, 8));
Expand All @@ -102,4 +149,32 @@ static Calendar createCalendarByFilename(String fileName) {
return calendar;
}

static Calendar createCalendarByHoursSince1900(int hoursSince1900) {
LocalDateTime t0 = LocalDateTime.parse("1900-01-01T00:00:00");
LocalDateTime timeSince1900 = t0.plusHours(hoursSince1900);
final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ENGLISH);
calendar.clear();
calendar.set(timeSince1900.getYear(), timeSince1900.getMonthValue() - 1, timeSince1900.getDayOfMonth(),
timeSince1900.getHour(), timeSince1900.getMinute(), timeSince1900.getSecond());
return calendar;
}

static final class CAMSBandResult {
private final int position;
private final double time;

public CAMSBandResult(int position, double time) {
this.position = position;
this.time = time;
}

public int getPositon() {
return position;
}

public double getTime() {
return time;
}
}

}
Loading