mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-20 04:35:32 +00:00
introduced variable identification portable across GRIB versions
Change-Id: I1ca20cc66e99b9c495c4403f63923559a8007912
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry exported="true" kind="lib" path="lib/netcdfAll-4.6.8.jar">
|
||||
<classpathentry exported="true" kind="lib" path="lib/netcdfAll-4.6.8.jar" sourcepath="C:/data/java/thredds/grib/src/main/java">
|
||||
<attributes>
|
||||
<attribute name="javadoc_location" value="https://www.unidata.ucar.edu/software/thredds/v4.3/netcdf-java/v4.3/javadoc/"/>
|
||||
</attributes>
|
||||
|
||||
@@ -22,5 +22,5 @@ import ucar.nc2.ft.FeatureDataset;
|
||||
public interface GribWindFieldFactory {
|
||||
GribWindFieldFactory INSTANCE = new GribWindFieldFactoryImpl();
|
||||
|
||||
GribWindField createGribWindField(FeatureDataset dataSet);
|
||||
GribWindField createGribWindField(FeatureDataset... dataSet);
|
||||
}
|
||||
|
||||
+40
-37
@@ -1,7 +1,10 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import com.sap.sailing.domain.common.Bounds;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
@@ -19,9 +22,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
|
||||
import ucar.ma2.Array;
|
||||
import ucar.nc2.Attribute;
|
||||
import ucar.nc2.Dimension;
|
||||
import ucar.nc2.VariableSimpleIF;
|
||||
import ucar.nc2.dataset.VariableDS;
|
||||
import ucar.nc2.dt.GridCoordSystem;
|
||||
import ucar.nc2.dt.GridDatatype;
|
||||
@@ -41,59 +42,48 @@ import ucar.unidata.geoloc.LatLonRect;
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractGribWindFieldImpl implements GribWindField {
|
||||
private final FeatureDataset dataSet;
|
||||
private final FeatureDataset[] dataSets;
|
||||
|
||||
private final Weigher<TimePoint> timeConfidenceWeigher;
|
||||
|
||||
/**
|
||||
* The base confidence of the wind data coming from the underlying {@link #dataSet}, between 0 (not confident at
|
||||
* The base confidence of the wind data coming from the underlying {@link #dataSets}, between 0 (not confident at
|
||||
* all) and 1 (really confident observation at this point in time)
|
||||
*/
|
||||
private final double baseConfidence;
|
||||
|
||||
public AbstractGribWindFieldImpl(FeatureDataset dataSet, double baseConfidence) {
|
||||
this.dataSet = dataSet;
|
||||
public AbstractGribWindFieldImpl(double baseConfidence, FeatureDataset... dataSets) {
|
||||
this.dataSets = dataSets;
|
||||
this.baseConfidence = baseConfidence;
|
||||
timeConfidenceWeigher = ConfidenceFactory.INSTANCE.createExponentialTimeDifferenceWeigher(
|
||||
// use a minimum confidence to avoid the bearing to flip to 270deg in case all is zero
|
||||
Duration.ONE_SECOND.times(30).asMillis(), /* minimum confidence */0.0000000001);
|
||||
}
|
||||
|
||||
protected static boolean hasVariable(FeatureDataset dataSet, int variableId) {
|
||||
for (VariableSimpleIF variable : dataSet.getDataVariables()) {
|
||||
Optional<Integer> id = getVariableId(variable);
|
||||
if (id.isPresent() && id.get() == variableId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the {@code Grib1_Parameter} value which gives the ID of the variable, such as 33 for the
|
||||
* "u-component of wind" variable
|
||||
*
|
||||
* @return an optional {@link Integer} that, if present, represents the {@code Grib1_Parameter} value for the
|
||||
* variable
|
||||
*/
|
||||
protected static Optional<Integer> getVariableId(VariableSimpleIF variable) {
|
||||
final Optional<Integer> result;
|
||||
final Attribute idVariable = variable.findAttributeIgnoreCase("Grib1_Parameter");
|
||||
if (idVariable != null) {
|
||||
result = Optional.of(idVariable.getNumericValue().intValue());
|
||||
} else {
|
||||
result = Optional.empty();
|
||||
}
|
||||
return result;
|
||||
protected static boolean hasVariable(VariableSpecification variableSpecification, FeatureDataset... dataSets) {
|
||||
return variableSpecification.appearsInAnyOf(dataSets);
|
||||
}
|
||||
|
||||
protected boolean hasVariable(VariableSpecification variableSpecification) {
|
||||
return variableSpecification.appearsInAnyOf(dataSets);
|
||||
}
|
||||
|
||||
protected double getBaseConfidence() {
|
||||
return baseConfidence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bounds getBounds() {
|
||||
return toBounds(getDataSet().getBoundingBox());
|
||||
Bounds result = null;
|
||||
for (final FeatureDataset dataSet : getDataSets()) {
|
||||
final Bounds dataSetBounds = toBounds(dataSet.getBoundingBox());
|
||||
if (result == null) {
|
||||
result = dataSetBounds;
|
||||
} else {
|
||||
result = result.extend(dataSetBounds);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,11 +119,11 @@ public abstract class AbstractGribWindFieldImpl implements GribWindField {
|
||||
}
|
||||
|
||||
protected TimePoint getStartTime() {
|
||||
return toTimePoint(getDataSet().getCalendarDateStart());
|
||||
return StreamSupport.stream(Arrays.asList(getDataSets()).spliterator(), /* parallel */ false).map(dataSet->toTimePoint(dataSet.getCalendarDateEnd())).min(Comparator.naturalOrder()).get();
|
||||
}
|
||||
|
||||
private TimePoint getEndTime() {
|
||||
return toTimePoint(getDataSet().getCalendarDateEnd());
|
||||
return StreamSupport.stream(Arrays.asList(getDataSets()).spliterator(), /* parallel */ false).map(dataSet->toTimePoint(dataSet.getCalendarDateEnd())).max(Comparator.naturalOrder()).get();
|
||||
}
|
||||
|
||||
private Bounds toBounds(LatLonRect boundingBox) {
|
||||
@@ -144,8 +134,8 @@ public abstract class AbstractGribWindFieldImpl implements GribWindField {
|
||||
return new DegreePosition(point.getLatitude(), point.getLongitude());
|
||||
}
|
||||
|
||||
protected FeatureDataset getDataSet() {
|
||||
return dataSet;
|
||||
protected FeatureDataset[] getDataSets() {
|
||||
return dataSets;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,4 +244,17 @@ public abstract class AbstractGribWindFieldImpl implements GribWindField {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean isMetersPerSecond(String unit) {
|
||||
final String spacelessUnit = removeSpacesAndToLowercase(unit);
|
||||
return spacelessUnit.equals("m/s") || spacelessUnit.equals("ms^-1");
|
||||
}
|
||||
|
||||
protected boolean isDegreesTrue(String unit) {
|
||||
final String spacelessUnit = removeSpacesAndToLowercase(unit).replaceAll("_", "");
|
||||
return spacelessUnit.equals("degreetrue") || spacelessUnit.equals("degtrue");
|
||||
}
|
||||
|
||||
private String removeSpacesAndToLowercase(String unit) {
|
||||
return unit.replaceAll(" ", "").toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import ucar.nc2.VariableSimpleIF;
|
||||
|
||||
public class CompositeVariableSpecification implements VariableSpecification {
|
||||
private final VariableSpecification[] variableSpecifications;
|
||||
|
||||
public CompositeVariableSpecification(VariableSpecification... variableSpecifications) {
|
||||
super();
|
||||
this.variableSpecifications = variableSpecifications;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(VariableSimpleIF variable) {
|
||||
for (final VariableSpecification s : variableSpecifications) {
|
||||
if (s.matches(variable)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CompositeVariableSpecification [variableSpecifications=" + Arrays.toString(variableSpecifications)
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import ucar.nc2.Attribute;
|
||||
import ucar.nc2.VariableSimpleIF;
|
||||
|
||||
public class Grib1VariableSpecification implements VariableSpecification {
|
||||
private final int indicatorOfParameter;
|
||||
|
||||
public Grib1VariableSpecification(int indicatorOfParameter) {
|
||||
super();
|
||||
this.indicatorOfParameter = indicatorOfParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(VariableSimpleIF variable) {
|
||||
Optional<Integer> variableId = getGrib1VariableId(variable);
|
||||
return variableId.isPresent() && variableId.get() == indicatorOfParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the {@code Grib1_Parameter} value which gives the ID of the variable, such as 33 for the
|
||||
* "u-component of wind" variable
|
||||
*
|
||||
* @return an optional {@link Integer} that, if present, represents the {@code Grib1_Parameter} value for the
|
||||
* variable
|
||||
*/
|
||||
private static Optional<Integer> getGrib1VariableId(VariableSimpleIF variable) {
|
||||
final Optional<Integer> result;
|
||||
final Attribute idVariable = variable.findAttributeIgnoreCase("Grib1_Parameter");
|
||||
if (idVariable != null) {
|
||||
result = Optional.of(idVariable.getNumericValue().intValue());
|
||||
} else {
|
||||
result = Optional.empty();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Grib1VariableSpecification [indicatorOfParameter=" + indicatorOfParameter + "]";
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import ucar.ma2.Array;
|
||||
import ucar.nc2.Attribute;
|
||||
import ucar.nc2.VariableSimpleIF;
|
||||
|
||||
public class Grib2VariableSpecification implements VariableSpecification {
|
||||
private final int[] disciplineCategoryNumberAndMoreOptionalSpecifiers;
|
||||
|
||||
public Grib2VariableSpecification(int[] disciplineCategoryNumberAndMoreOptionalSpecifiers) {
|
||||
super();
|
||||
this.disciplineCategoryNumberAndMoreOptionalSpecifiers = disciplineCategoryNumberAndMoreOptionalSpecifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(VariableSimpleIF variable) {
|
||||
final boolean result;
|
||||
final Optional<int[]> grib2ParameterInfo = getGrib2VariableId(variable);
|
||||
if (grib2ParameterInfo.isPresent() && grib2ParameterInfo.get().length >= disciplineCategoryNumberAndMoreOptionalSpecifiers.length) {
|
||||
result = Arrays.equals(Arrays.copyOf(grib2ParameterInfo.get(), disciplineCategoryNumberAndMoreOptionalSpecifiers.length), disciplineCategoryNumberAndMoreOptionalSpecifiers);
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the {@code Grib2_Parameter} value which gives the ID of the variable, such as "VAR_0-2-1_L103" for the
|
||||
* "WIND" wind speed variable
|
||||
*
|
||||
* @return an optional {@link Integer} that, if present, represents the {@code Grib1_Parameter} value for the
|
||||
* variable
|
||||
*/
|
||||
private static Optional<int[]> getGrib2VariableId(VariableSimpleIF variable) {
|
||||
final Optional<int[]> result;
|
||||
final Attribute idVariable = variable.findAttributeIgnoreCase("Grib2_Parameter");
|
||||
if (idVariable != null) {
|
||||
final Array grib2ParameterDisciplineCategoryAndNumber = idVariable.getValues();
|
||||
if (grib2ParameterDisciplineCategoryAndNumber == null) {
|
||||
result = Optional.empty();
|
||||
} else {
|
||||
final int[] resultArray = new int[(int) grib2ParameterDisciplineCategoryAndNumber.getSize()];
|
||||
for (int i=0; i<resultArray.length; i++) {
|
||||
resultArray[i] = grib2ParameterDisciplineCategoryAndNumber.getInt(i);
|
||||
}
|
||||
result = Optional.of(resultArray);
|
||||
}
|
||||
} else {
|
||||
result = Optional.empty();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Grib2VariableSpecification [disciplineCategoryNumberAndMoreOptionalSpecifiers="
|
||||
+ Arrays.toString(disciplineCategoryNumberAndMoreOptionalSpecifiers) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
+6
-6
@@ -7,14 +7,14 @@ import ucar.nc2.ft.FeatureDataset;
|
||||
|
||||
public class GribWindFieldFactoryImpl implements GribWindFieldFactory {
|
||||
@Override
|
||||
public GribWindField createGribWindField(FeatureDataset dataSet) {
|
||||
public GribWindField createGribWindField(FeatureDataset... dataSets) {
|
||||
final GribWindField result;
|
||||
if (UVWindField.handles(dataSet)) {
|
||||
result = new UVWindField(dataSet);
|
||||
} else if (SpeedAndDirectionWindField.handles(dataSet)) {
|
||||
result = new SpeedAndDirectionWindField(dataSet);
|
||||
if (UVWindField.handles(dataSets)) {
|
||||
result = new UVWindField(dataSets);
|
||||
} else if (SpeedAndDirectionWindField.handles(dataSets)) {
|
||||
result = new SpeedAndDirectionWindField(dataSets);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Couldn't find a wind field implementation handling data set "+dataSet);
|
||||
throw new IllegalArgumentException("Couldn't find a wind field implementation handling data set(s) "+dataSets);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+41
-23
@@ -18,37 +18,55 @@ import ucar.nc2.ft.FeatureDataset;
|
||||
|
||||
public class SpeedAndDirectionWindField extends AbstractGribWindFieldImpl {
|
||||
private static final int WIND_DIRECTION_PARAMETER_ID = 31;
|
||||
private static final int WIND_SPEED_PARAMETER_ID = 32;
|
||||
private static final int WIND_DIRECTION_GRIB2_DISCIPLINE = 0;
|
||||
private static final int WIND_DIRECTION_GRIB2_PARAMETER_CATEGORY = 2;
|
||||
private static final int WIND_DIRECTION_GRIB2_PARAMETER_NUMBER = 0;
|
||||
|
||||
public SpeedAndDirectionWindField(FeatureDataset dataSet) {
|
||||
super(dataSet, /* baseConfidence */ 0.5);
|
||||
private static final int WIND_SPEED_PARAMETER_ID = 32;
|
||||
private static final int WIND_SPEED_GRIB2_DISCIPLINE = 0;
|
||||
private static final int WIND_SPEED_GRIB2_PARAMETER_CATEGORY = 2;
|
||||
private static final int WIND_SPEED_GRIB2_PARAMETER_NUMBER = 1;
|
||||
|
||||
private static final VariableSpecification windDirectionVariableSpecification =
|
||||
new CompositeVariableSpecification(new Grib1VariableSpecification(WIND_DIRECTION_PARAMETER_ID),
|
||||
new Grib2VariableSpecification(new int[] { WIND_DIRECTION_GRIB2_DISCIPLINE, WIND_DIRECTION_GRIB2_PARAMETER_CATEGORY, WIND_DIRECTION_GRIB2_PARAMETER_NUMBER }));
|
||||
|
||||
private static final VariableSpecification windSpeedVariableSpecification =
|
||||
new CompositeVariableSpecification(new Grib1VariableSpecification(WIND_SPEED_PARAMETER_ID),
|
||||
new Grib2VariableSpecification(new int[] { WIND_SPEED_GRIB2_DISCIPLINE, WIND_SPEED_GRIB2_PARAMETER_CATEGORY, WIND_SPEED_GRIB2_PARAMETER_NUMBER }));
|
||||
|
||||
public SpeedAndDirectionWindField(FeatureDataset... dataSets) {
|
||||
super(/* baseConfidence */ 0.5, dataSets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindWithConfidence<TimePoint> getWind(TimePoint timePoint, Position position) throws IOException {
|
||||
FeatureDataset dataSet = getDataSet();
|
||||
final Wind wind;
|
||||
final double confidence;
|
||||
if (dataSet instanceof GridDataset) {
|
||||
Triple<Double, TimePoint, Position> directionComponentInDegreesTrue = null;
|
||||
Triple<Double, TimePoint, Position> speedComponentInMetersPerSecond = null;
|
||||
for (final GridDatatype grid : ((GridDataset) dataSet).getGrids()) {
|
||||
final Integer variableId = getVariableId(grid.getVariable()).orElse(-1);
|
||||
if (variableId == WIND_DIRECTION_PARAMETER_ID) {
|
||||
assert getUnit(grid.getVariable()).get().equals("m/s");
|
||||
directionComponentInDegreesTrue = getValue(grid, timePoint, position);
|
||||
} else if (variableId == WIND_SPEED_PARAMETER_ID) {
|
||||
assert getUnit(grid.getVariable()).get().equals("m/s");
|
||||
speedComponentInMetersPerSecond = getValue(grid, timePoint, position);
|
||||
}
|
||||
if (directionComponentInDegreesTrue != null && speedComponentInMetersPerSecond != null) {
|
||||
break;
|
||||
Triple<Double, TimePoint, Position> directionComponentInDegreesTrue = null;
|
||||
Triple<Double, TimePoint, Position> speedComponentInMetersPerSecond = null;
|
||||
for (final FeatureDataset dataSet : getDataSets()) {
|
||||
if (dataSet instanceof GridDataset) {
|
||||
for (final GridDatatype grid : ((GridDataset) dataSet).getGrids()) {
|
||||
if (windDirectionVariableSpecification.matches(grid.getVariable())) {
|
||||
assert isDegreesTrue(getUnit(grid.getVariable()).get());
|
||||
directionComponentInDegreesTrue = getValue(grid, timePoint, position);
|
||||
} else if (windSpeedVariableSpecification.matches(grid.getVariable())) {
|
||||
assert isMetersPerSecond(getUnit(grid.getVariable()).get());
|
||||
speedComponentInMetersPerSecond = getValue(grid, timePoint, position);
|
||||
}
|
||||
if (directionComponentInDegreesTrue != null && speedComponentInMetersPerSecond != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final Wind wind;
|
||||
final double confidence;
|
||||
if (directionComponentInDegreesTrue != null && speedComponentInMetersPerSecond != null) {
|
||||
confidence = getTimeConfidence(timePoint, directionComponentInDegreesTrue.getB());
|
||||
wind = new WindImpl(directionComponentInDegreesTrue.getC(), directionComponentInDegreesTrue.getB(),
|
||||
new MeterPerSecondSpeedWithDegreeBearingImpl(speedComponentInMetersPerSecond.getA(),
|
||||
new DegreeBearingImpl(directionComponentInDegreesTrue.getA())));
|
||||
// we're getting the "from" direction from the GRIB file and need to convert to "to" here
|
||||
new DegreeBearingImpl(directionComponentInDegreesTrue.getA()).reverse()));
|
||||
} else {
|
||||
wind = null;
|
||||
confidence = 0;
|
||||
@@ -60,8 +78,8 @@ public class SpeedAndDirectionWindField extends AbstractGribWindFieldImpl {
|
||||
* Checks whether the data set has a wind speed variable (GRIB parameter #32) and a wind direction variable
|
||||
* (GRIB parameter #31).
|
||||
*/
|
||||
public static boolean handles(FeatureDataset dataSet) {
|
||||
return hasVariable(dataSet, WIND_DIRECTION_PARAMETER_ID) && hasVariable(dataSet, WIND_SPEED_PARAMETER_ID);
|
||||
public static boolean handles(FeatureDataset... dataSets) {
|
||||
return hasVariable(windDirectionVariableSpecification, dataSets) && hasVariable(windSpeedVariableSpecification, dataSets);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,33 +18,50 @@ import ucar.nc2.ft.FeatureDataset;
|
||||
|
||||
public class UVWindField extends AbstractGribWindFieldImpl {
|
||||
private static final int U_COMPONENT_OF_WIND_PARAMETER_ID = 33;
|
||||
private static final int U_COMPONENT_OF_WIND_GRIB2_DISCIPLINE = 0;
|
||||
private static final int U_COMPONENT_OF_WIND_GRIB2_PARAMETER_CATEGORY = 2;
|
||||
private static final int U_COMPONENT_OF_WIND_GRIB2_PARAMETER_NUMBER = 2;
|
||||
|
||||
private static final int V_COMPONENT_OF_WIND_PARAMETER_ID = 34;
|
||||
private static final int V_COMPONENT_OF_WIND_GRIB2_DISCIPLINE = 0;
|
||||
private static final int V_COMPONENT_OF_WIND_GRIB2_PARAMETER_CATEGORY = 2;
|
||||
private static final int V_COMPONENT_OF_WIND_GRIB2_PARAMETER_NUMBER = 3;
|
||||
|
||||
public UVWindField(FeatureDataset dataSet) {
|
||||
super(dataSet, /* baseConfidence */ 0.5);
|
||||
private static final VariableSpecification uComponentOfWindVariableSpecification =
|
||||
new CompositeVariableSpecification(new Grib1VariableSpecification(U_COMPONENT_OF_WIND_PARAMETER_ID),
|
||||
new Grib2VariableSpecification(new int[] { U_COMPONENT_OF_WIND_GRIB2_DISCIPLINE, U_COMPONENT_OF_WIND_GRIB2_PARAMETER_CATEGORY, U_COMPONENT_OF_WIND_GRIB2_PARAMETER_NUMBER }));
|
||||
|
||||
private static final VariableSpecification vComponentOfWindVariableSpecification =
|
||||
new CompositeVariableSpecification(new Grib1VariableSpecification(V_COMPONENT_OF_WIND_PARAMETER_ID),
|
||||
new Grib2VariableSpecification(new int[] { V_COMPONENT_OF_WIND_GRIB2_DISCIPLINE, V_COMPONENT_OF_WIND_GRIB2_PARAMETER_CATEGORY, V_COMPONENT_OF_WIND_GRIB2_PARAMETER_NUMBER }));
|
||||
|
||||
public UVWindField(FeatureDataset... dataSets) {
|
||||
super(/* baseConfidence */ 0.5, dataSets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindWithConfidence<TimePoint> getWind(TimePoint timePoint, Position position) throws IOException {
|
||||
FeatureDataset dataSet = getDataSet();
|
||||
final Wind wind;
|
||||
final double confidence;
|
||||
if (dataSet instanceof GridDataset) {
|
||||
Triple<Double, TimePoint, Position> uComponentInMetersPerSecond = null;
|
||||
Triple<Double, TimePoint, Position> vComponentInMetersPerSecond = null;
|
||||
for (final GridDatatype grid : ((GridDataset) dataSet).getGrids()) {
|
||||
final Integer variableId = getVariableId(grid.getVariable()).orElse(-1);
|
||||
if (variableId == U_COMPONENT_OF_WIND_PARAMETER_ID) {
|
||||
assert isMetersPerSecond(getUnit(grid.getVariable()).get());
|
||||
uComponentInMetersPerSecond = getValue(grid, timePoint, position);
|
||||
} else if (variableId == V_COMPONENT_OF_WIND_PARAMETER_ID) {
|
||||
assert isMetersPerSecond(getUnit(grid.getVariable()).get());
|
||||
vComponentInMetersPerSecond = getValue(grid, timePoint, position);
|
||||
}
|
||||
if (uComponentInMetersPerSecond != null && vComponentInMetersPerSecond != null) {
|
||||
break;
|
||||
Triple<Double, TimePoint, Position> uComponentInMetersPerSecond = null;
|
||||
Triple<Double, TimePoint, Position> vComponentInMetersPerSecond = null;
|
||||
for (final FeatureDataset dataSet : getDataSets()) {
|
||||
if (dataSet instanceof GridDataset) {
|
||||
for (final GridDatatype grid : ((GridDataset) dataSet).getGrids()) {
|
||||
if (uComponentOfWindVariableSpecification.matches(grid.getVariable())) {
|
||||
assert isMetersPerSecond(getUnit(grid.getVariable()).get());
|
||||
uComponentInMetersPerSecond = getValue(grid, timePoint, position);
|
||||
} else if (vComponentOfWindVariableSpecification.matches(grid.getVariable())) {
|
||||
assert isMetersPerSecond(getUnit(grid.getVariable()).get());
|
||||
vComponentInMetersPerSecond = getValue(grid, timePoint, position);
|
||||
}
|
||||
if (uComponentInMetersPerSecond != null && vComponentInMetersPerSecond != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uComponentInMetersPerSecond != null && vComponentInMetersPerSecond != null) {
|
||||
confidence = getTimeConfidence(timePoint, uComponentInMetersPerSecond.getB());
|
||||
final double atan2 = Math.atan2(uComponentInMetersPerSecond.getA(), vComponentInMetersPerSecond.getA());
|
||||
wind = new WindImpl(uComponentInMetersPerSecond.getC(), uComponentInMetersPerSecond.getB(),
|
||||
@@ -58,16 +75,11 @@ public class UVWindField extends AbstractGribWindFieldImpl {
|
||||
return new WindWithConfidenceImpl<TimePoint>(wind, confidence*getBaseConfidence(), timePoint, /* useSpeed */ true);
|
||||
}
|
||||
|
||||
private boolean isMetersPerSecond(String unit) {
|
||||
final String spacelessUnit = unit.replaceAll(" ", "");
|
||||
return spacelessUnit.equals("m/s") || spacelessUnit.equals("ms^-1");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the data set has a u-component of wind (GRIB parameter #33) and a v-component of wind
|
||||
* (GRIB parameter #34).
|
||||
*/
|
||||
public static boolean handles(FeatureDataset dataSet) {
|
||||
return hasVariable(dataSet, U_COMPONENT_OF_WIND_PARAMETER_ID) && hasVariable(dataSet, V_COMPONENT_OF_WIND_PARAMETER_ID);
|
||||
public static boolean handles(FeatureDataset... dataSets) {
|
||||
return hasVariable(uComponentOfWindVariableSpecification, dataSets) && hasVariable(vComponentOfWindVariableSpecification, dataSets);
|
||||
}
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import ucar.nc2.VariableSimpleIF;
|
||||
import ucar.nc2.ft.FeatureDataset;
|
||||
|
||||
/**
|
||||
* GRIB variables can be described by a GRIB1 "indicatorOfParameter" which is a single integer, such as {@code 31} for
|
||||
* the wind direction (see http://apps.ecmwf.int/codes/grib/param-db?id=3031), or by a GRIB2 integer sequence starting with
|
||||
* (discipline, parameterCategory, parameterNumber) such as (0, 2, 0) for the wind direction. An instance of a class
|
||||
* implementing this interface is able to tell for a given {@link VariableSimpleIF} whether this specification matches
|
||||
* the variable.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface VariableSpecification {
|
||||
/**
|
||||
* Checks whether the given {@code variable} conforms to this variable specification.
|
||||
*/
|
||||
boolean matches(VariableSimpleIF variable);
|
||||
|
||||
default boolean appearsInAnyOf(FeatureDataset... dataSets) {
|
||||
for (final FeatureDataset dataSet : dataSets) {
|
||||
for (final VariableSimpleIF variable : dataSet.getDataVariables()) {
|
||||
if (matches(variable)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the variable from the collection of {@code variables} that {@link #matches(VariableSimpleIF) matches}
|
||||
* this specification. If no such variable exists in {@code variables}, {@code null} is returned.
|
||||
*/
|
||||
default VariableSimpleIF getVariable(Iterable<VariableSimpleIF> variables) {
|
||||
for (final VariableSimpleIF variable : variables) {
|
||||
if (matches(variable)) {
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user