mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-20 04:35:32 +00:00
preparing for multi-file upload for both Expedition and GRIB; introduced MappingIterable and MappingIterator
moved wind import servlet package structure a little Change-Id: I9b573d4c91191933383c87397ac8584d9463b4bb
This commit is contained in:
@@ -7,86 +7,8 @@ Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ClassPath: .,
|
||||
lib/netcdfAll-4.6.8.jar
|
||||
Export-Package: ucar.atd.dorade,
|
||||
ucar.coord,
|
||||
ucar.httpservices,
|
||||
ucar.ma2,
|
||||
ucar.nc2,
|
||||
ucar.nc2.constants,
|
||||
ucar.nc2.dataset,
|
||||
ucar.nc2.dataset.conv,
|
||||
ucar.nc2.dataset.transform,
|
||||
ucar.nc2.dods,
|
||||
ucar.nc2.dt,
|
||||
ucar.nc2.dt.grid,
|
||||
ucar.nc2.dt.grid.gis,
|
||||
ucar.nc2.dt.image.image,
|
||||
ucar.nc2.dt.point,
|
||||
ucar.nc2.dt.point.decode,
|
||||
ucar.nc2.dt.radial,
|
||||
ucar.nc2.dt.trajectory,
|
||||
ucar.nc2.ft,
|
||||
ucar.nc2.ft.coordsys,
|
||||
ucar.nc2.ft.fmrc,
|
||||
ucar.nc2.ft.point,
|
||||
ucar.nc2.ft.point.bufr,
|
||||
ucar.nc2.ft.point.collection,
|
||||
ucar.nc2.ft.point.remote,
|
||||
ucar.nc2.ft.point.standard,
|
||||
ucar.nc2.ft.point.standard.plug,
|
||||
ucar.nc2.ft.point.writer,
|
||||
ucar.nc2.ft.radial,
|
||||
ucar.nc2.ft.scan,
|
||||
ucar.nc2.ft.swath,
|
||||
ucar.nc2.ft2,
|
||||
ucar.nc2.geotiff,
|
||||
ucar.nc2.grib,
|
||||
ucar.nc2.grib.collection,
|
||||
ucar.nc2.grib.grib1,
|
||||
ucar.nc2.grib.grib1.tables,
|
||||
ucar.nc2.grib.grib2,
|
||||
ucar.nc2.grib.grib2.table,
|
||||
ucar.nc2.grib.writer,
|
||||
ucar.nc2.iosp,
|
||||
ucar.nc2.iosp.bufr,
|
||||
ucar.nc2.iosp.bufr.tables,
|
||||
ucar.nc2.iosp.bufr.writer,
|
||||
ucar.nc2.iosp.cinrad,
|
||||
ucar.nc2.iosp.dmsp,
|
||||
ucar.nc2.iosp.dorade,
|
||||
ucar.nc2.iosp.fysat,
|
||||
ucar.nc2.iosp.fysat.util,
|
||||
ucar.nc2.iosp.gini,
|
||||
ucar.nc2.iosp.grads,
|
||||
ucar.nc2.iosp.hdf4,
|
||||
ucar.nc2.iosp.hdf5,
|
||||
ucar.nc2.iosp.misc,
|
||||
ucar.nc2.iosp.netcdf3,
|
||||
ucar.nc2.iosp.netcdf4,
|
||||
ucar.nc2.iosp.nexrad2,
|
||||
ucar.nc2.iosp.nids,
|
||||
ucar.nc2.iosp.noaa,
|
||||
ucar.nc2.iosp.nowrad,
|
||||
ucar.nc2.iosp.shapefile,
|
||||
ucar.nc2.iosp.sigmet,
|
||||
ucar.nc2.iosp.uamiv,
|
||||
ucar.nc2.iosp.uf,
|
||||
ucar.nc2.jni.netcdf,
|
||||
ucar.nc2.ncml,
|
||||
ucar.nc2.rewrite,
|
||||
ucar.nc2.stream,
|
||||
ucar.nc2.thredds,
|
||||
ucar.nc2.time,
|
||||
ucar.nc2.units,
|
||||
ucar.nc2.util,
|
||||
ucar.nc2.util.cache,
|
||||
ucar.nc2.util.log,
|
||||
ucar.nc2.util.net,
|
||||
ucar.nc2.util.rc,
|
||||
ucar.nc2.util.reflect,
|
||||
ucar.nc2.util.xml,
|
||||
ucar.nc2.wmo,
|
||||
ucar.nc2.write
|
||||
Export-Package: com.sap.sailing.grib
|
||||
Require-Bundle: com.sap.sailing.domain.common,
|
||||
com.sap.sailing.domain,
|
||||
com.sap.sse.common
|
||||
com.sap.sse.common,
|
||||
com.sap.sse
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.io.IOException;
|
||||
|
||||
import com.sap.sailing.domain.common.Bounds;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
@@ -39,4 +40,6 @@ public interface GribWindField {
|
||||
* The time range covered by the underlying GRIB data.
|
||||
*/
|
||||
TimeRange getTimeRange();
|
||||
|
||||
Iterable<Wind> getAllWindFixes();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
package com.sap.sailing.grib;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Formatter;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.grib.impl.GribWindFieldFactoryImpl;
|
||||
|
||||
import ucar.nc2.ft.FeatureDataset;
|
||||
@@ -9,13 +17,18 @@ import ucar.nc2.ft.FeatureDataset;
|
||||
*
|
||||
* Example Usage:
|
||||
* <pre>
|
||||
* final Formatter errorLog = new Formatter(System.err);
|
||||
* final Formatter errorLog = createLogFormatter(logger, Level.INFO);
|
||||
* final ucar.nc2.util.CancelTask task = null;
|
||||
* final String location = "resources/globalMarineNetCroatia.grb.bz2"; // or could be some URI / URL from where to retrieve it
|
||||
* final FeatureDataset dataSet = FeatureDatasetFactoryManager.open(FeatureType.ANY, location, task, errorLog);
|
||||
* final GribWindField windField = GribWindFieldFactory.INSTANCE.createGribWindField(dataSet);
|
||||
* </pre>
|
||||
*
|
||||
* Or:
|
||||
* <pre>
|
||||
* final GribWindField windField = GribWindFieldFactory.INSTANCE.createGribWindField(new File("/this/is/the/path/to/my/grib/file.grb"));
|
||||
* </pre>
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
@@ -23,4 +36,41 @@ public interface GribWindFieldFactory {
|
||||
GribWindFieldFactory INSTANCE = new GribWindFieldFactoryImpl();
|
||||
|
||||
GribWindField createGribWindField(FeatureDataset... dataSet);
|
||||
|
||||
GribWindField createGribWindField(Iterable<String> locations) throws IOException;
|
||||
|
||||
GribWindField createGribWindField(Logger logger, Level level, Iterable<String> locations) throws IOException;
|
||||
|
||||
GribWindField createGribWindField(Formatter errorLog, Iterable<String> locations) throws IOException;
|
||||
|
||||
GribWindField createGribWindFieldFromFiles(Iterable<File> files) throws IOException;
|
||||
|
||||
GribWindField createGribWindFieldFromFiles(Formatter errorLog, Iterable<File> files) throws IOException;
|
||||
|
||||
GribWindField createGribWindFieldFromFiles(Logger logger, Level level, Iterable<File> files) throws IOException;
|
||||
|
||||
/**
|
||||
* Requires that the $TMP part of the file system can be accessed for writing because the files will temporarily be
|
||||
* stored there.
|
||||
*/
|
||||
GribWindField createGribWindFieldFromStreams(Map<InputStream, String> inputStreamsAndFilenames) throws IOException;
|
||||
|
||||
/**
|
||||
* Requires that the $TMP part of the file system can be accessed for writing because the files will temporarily be
|
||||
* stored there.
|
||||
*/
|
||||
GribWindField createGribWindFieldFromStreams(Formatter errorLog, Map<InputStream, String> inputStreamsAndFilenames) throws IOException;
|
||||
|
||||
/**
|
||||
* Requires that the $TMP part of the file system can be accessed for writing because the files will temporarily be
|
||||
* stored there.
|
||||
*/
|
||||
GribWindField createGribWindFieldFromStreams(Logger logger, Level level, Map<InputStream, String> inputStreamsAndFilenames) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a {@link Formatter} that writes its messages to the {@code logger} using the log {@code level} provided.
|
||||
* Use this method to create formatters for the {@code createGribWindField...} methods that require one.
|
||||
*/
|
||||
Formatter createLogFormatter(Logger logger, Level level);
|
||||
|
||||
}
|
||||
|
||||
+27
-2
@@ -1,8 +1,10 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@@ -227,12 +229,35 @@ public abstract class AbstractGribWindFieldImpl implements GribWindField {
|
||||
return getNearestNonNaNValueWithPosition(coordinateSystem, grid, timeIndex, zIndex, yIndex, xIndex, distance+1);
|
||||
}
|
||||
|
||||
private double getValue(GridDatatype grid, int timeIndex, int zIndex, final int x, final int y) throws IOException {
|
||||
protected double getValue(GridDatatype grid, int timeIndex, int zIndex, final int x, final int y) throws IOException {
|
||||
final Array arrayForTimePointBefore = grid.readDataSlice(timeIndex, zIndex, y, x);
|
||||
final double valueAsFloat = (double) arrayForTimePointBefore.getFloat(0);
|
||||
return valueAsFloat;
|
||||
}
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
static interface ValueForCoordinateProvider<T> {
|
||||
T getValue(int timeIndex, int x, int y, TimePoint timePoint, Position position);
|
||||
}
|
||||
|
||||
protected <T> Iterable<T> foreach(GridDatatype grid, ValueForCoordinateProvider<T> provider) {
|
||||
final List<T> result = new ArrayList<>();
|
||||
final GridCoordSystem coordinateSystem = grid.getCoordinateSystem();
|
||||
final int timeDimLength = grid.getTimeDimension().getLength();
|
||||
final int xDimLength = grid.getXDimension().getLength();
|
||||
final int yDimLength = grid.getYDimension().getLength();
|
||||
for (int t=0; t<timeDimLength; t++) {
|
||||
final TimePoint timePoint = toTimePoint(coordinateSystem.getTimeAxis1D().getCalendarDate(t));
|
||||
for (int x=0; x<xDimLength; x++) {
|
||||
for (int y=0; y<yDimLength; y++) {
|
||||
final Position position = toPosition(coordinateSystem.getLatLon(x, y));
|
||||
result.add(provider.getValue(t, x, y, timePoint, position));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Optional<String> getUnit(VariableDS variable) {
|
||||
final Optional<String> result;
|
||||
final ucar.nc2.Attribute attribute = variable.findAttribute("units");
|
||||
|
||||
+147
@@ -1,11 +1,76 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Formatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.grib.GribWindField;
|
||||
import com.sap.sailing.grib.GribWindFieldFactory;
|
||||
import com.sap.sse.common.util.MappingIterable;
|
||||
import com.sap.sse.util.LoggerAppender;
|
||||
|
||||
import ucar.nc2.constants.FeatureType;
|
||||
import ucar.nc2.ft.FeatureDataset;
|
||||
import ucar.nc2.ft.FeatureDatasetFactoryManager;
|
||||
|
||||
public class GribWindFieldFactoryImpl implements GribWindFieldFactory {
|
||||
private static final Logger logger = Logger.getLogger(GribWindFieldFactoryImpl.class.getName());
|
||||
private static final Level DEFAULT_ERROR_LOG_LEVEL = Level.INFO;
|
||||
|
||||
/**
|
||||
* Weak references inserted into {@link #filesToCleanWhenGribWindFieldNoLongerUsed} must be registered with this
|
||||
* queue. When such a registration happens, a {@link #fileSystemCleaner} thread must be started while holding this
|
||||
* object's monitor (synchronized). The thread is then responsible for observing the queue and cleaning the
|
||||
* directories. When, while holding the monitor, the {@link #filesToCleanWhenGribWindFieldNoLongerUsed} map becomes
|
||||
* empty, the thread terminates and sets the {@link #fileSystemCleaner} field to {@code null} so that a new thread
|
||||
* must be created the next time a reference is entered into the map.
|
||||
*/
|
||||
private final ReferenceQueue<GribWindField> referenceQueue = new ReferenceQueue<>();
|
||||
private Thread fileSystemCleaner;
|
||||
private final Map<WeakReference<GribWindField>, File> filesToCleanWhenGribWindFieldNoLongerUsed = new HashMap<>();
|
||||
|
||||
private void removeDirectoryWhenWindFieldNoLongerStronglyReferenced(GribWindField windField, File directoryToRemove) {
|
||||
assert directoryToRemove.isDirectory();
|
||||
synchronized (this) {
|
||||
final boolean needToStartThread = filesToCleanWhenGribWindFieldNoLongerUsed.isEmpty();
|
||||
filesToCleanWhenGribWindFieldNoLongerUsed.put(new WeakReference<GribWindField>(windField, referenceQueue), directoryToRemove);
|
||||
if (needToStartThread && fileSystemCleaner == null) {
|
||||
fileSystemCleaner = new Thread(()->{
|
||||
boolean finished = false;
|
||||
while (!finished) {
|
||||
try {
|
||||
final Reference<? extends GribWindField> ref = referenceQueue.remove();
|
||||
synchronized (GribWindFieldFactoryImpl.this) {
|
||||
filesToCleanWhenGribWindFieldNoLongerUsed.remove(ref);
|
||||
finished = filesToCleanWhenGribWindFieldNoLongerUsed.isEmpty();
|
||||
if (finished) {
|
||||
fileSystemCleaner = null;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.WARNING, "Interrupted while waiting for weak reference, giving up", e);
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
}, "GRIB directory cleaner");
|
||||
fileSystemCleaner.setDaemon(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindField(FeatureDataset... dataSets) {
|
||||
final GribWindField result;
|
||||
@@ -18,4 +83,86 @@ public class GribWindFieldFactoryImpl implements GribWindFieldFactory {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindField(Iterable<String> locations) throws IOException {
|
||||
return createGribWindField(logger, DEFAULT_ERROR_LOG_LEVEL, locations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindField(Logger logger, Level level, Iterable<String> locations) throws IOException {
|
||||
final Formatter errorLog = createLogFormatter(logger, level);
|
||||
return createGribWindField(errorLog, locations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindField(Formatter errorLog, Iterable<String> locations) throws IOException {
|
||||
final List<FeatureDataset> dataSets = new ArrayList<>();
|
||||
for (final String location : locations) {
|
||||
FeatureDataset dataSet = FeatureDatasetFactoryManager.open(FeatureType.ANY, location, /* task */ null, errorLog);
|
||||
dataSets.add(dataSet);
|
||||
}
|
||||
return createGribWindField(dataSets.toArray(new FeatureDataset[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindFieldFromFiles(Iterable<File> files) throws IOException {
|
||||
return createGribWindFieldFromFiles(logger, DEFAULT_ERROR_LOG_LEVEL, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindFieldFromFiles(Logger logger, Level level, Iterable<File> files)
|
||||
throws IOException {
|
||||
return createGribWindFieldFromFiles(createLogFormatter(logger, level), files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindFieldFromFiles(Formatter errorLog, Iterable<File> files) throws IOException {
|
||||
return createGribWindField(errorLog, new MappingIterable<>(files,
|
||||
f->{ try { return f.getCanonicalPath(); } catch (Exception e) { logger.log(Level.SEVERE, "Error obtaining GRIB file path", e); return null; } }));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindFieldFromStreams(Map<InputStream, String> streamsAndFilenames) throws IOException {
|
||||
return createGribWindFieldFromStreams(logger, DEFAULT_ERROR_LOG_LEVEL, streamsAndFilenames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindFieldFromStreams(Logger logger, Level level, Map<InputStream, String> streamsAndFilenames)
|
||||
throws IOException {
|
||||
return createGribWindFieldFromStreams(createLogFormatter(logger, level), streamsAndFilenames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GribWindField createGribWindFieldFromStreams(Formatter errorLog, Map<InputStream, String> streamsAndFilenames)
|
||||
throws IOException {
|
||||
final List<File> files = new ArrayList<>();
|
||||
for (final Entry<InputStream, String> e : streamsAndFilenames.entrySet()) {
|
||||
files.add(copyStreamToFile(e.getKey(), e.getValue()));
|
||||
}
|
||||
final GribWindField result = createGribWindFieldFromFiles(errorLog, files);
|
||||
for (final File file : files) {
|
||||
removeDirectoryWhenWindFieldNoLongerStronglyReferenced(result, file.getParentFile());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the contents of the input stream {@code s} into a temporary directory that is created solely for this
|
||||
* purpose. The {@code filename} will be used for the name of the file. The {@link File} object representing the
|
||||
* file written is returned so that it and its containing directory can be removed when the contents of the stream
|
||||
* are no longer needed.
|
||||
*/
|
||||
private File copyStreamToFile(InputStream s, String filename) throws IOException {
|
||||
Path tempDir = Files.createTempDirectory("gribcache");
|
||||
Path filePath = tempDir.resolve(filename);
|
||||
Files.copy(s, filePath);
|
||||
return filePath.toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Formatter createLogFormatter(Logger logger, Level level) {
|
||||
return new Formatter(new LoggerAppender(level, logger));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+61
-4
@@ -1,6 +1,11 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
@@ -17,6 +22,7 @@ import ucar.nc2.dt.grid.GridDataset;
|
||||
import ucar.nc2.ft.FeatureDataset;
|
||||
|
||||
public class SpeedAndDirectionWindField extends AbstractGribWindFieldImpl {
|
||||
private static final Logger logger = Logger.getLogger(SpeedAndDirectionWindField.class.getName());
|
||||
private static final int WIND_DIRECTION_PARAMETER_ID = 31;
|
||||
private static final int WIND_DIRECTION_GRIB2_DISCIPLINE = 0;
|
||||
private static final int WIND_DIRECTION_GRIB2_PARAMETER_CATEGORY = 2;
|
||||
@@ -63,16 +69,23 @@ public class SpeedAndDirectionWindField extends AbstractGribWindFieldImpl {
|
||||
final double confidence;
|
||||
if (directionComponentInDegreesTrue != null && speedComponentInMetersPerSecond != null) {
|
||||
confidence = getTimeConfidence(timePoint, directionComponentInDegreesTrue.getB());
|
||||
wind = new WindImpl(directionComponentInDegreesTrue.getC(), directionComponentInDegreesTrue.getB(),
|
||||
new MeterPerSecondSpeedWithDegreeBearingImpl(speedComponentInMetersPerSecond.getA(),
|
||||
// we're getting the "from" direction from the GRIB file and need to convert to "to" here
|
||||
new DegreeBearingImpl(directionComponentInDegreesTrue.getA()).reverse()));
|
||||
wind = createWindFixFromDirectionAndSpeed(directionComponentInDegreesTrue.getC(), directionComponentInDegreesTrue.getB(),
|
||||
speedComponentInMetersPerSecond.getA(),
|
||||
// we're getting the "from" direction from the GRIB file and need to convert to "to" here
|
||||
directionComponentInDegreesTrue.getA());
|
||||
} else {
|
||||
wind = null;
|
||||
confidence = 0;
|
||||
}
|
||||
return new WindWithConfidenceImpl<TimePoint>(wind, confidence*getBaseConfidence(), timePoint, /* useSpeed */ true);
|
||||
}
|
||||
|
||||
private Wind createWindFixFromDirectionAndSpeed(Position position, TimePoint timePoint, double speedInMetersPerSecond, double fromTrueDirectionInDeg) {
|
||||
return new WindImpl(position, timePoint,
|
||||
new MeterPerSecondSpeedWithDegreeBearingImpl(speedInMetersPerSecond,
|
||||
// we're getting the "from" direction from the GRIB file and need to convert to "to" here
|
||||
new DegreeBearingImpl(fromTrueDirectionInDeg).reverse()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the data set has a wind speed variable (GRIB parameter #32) and a wind direction variable
|
||||
@@ -82,4 +95,48 @@ public class SpeedAndDirectionWindField extends AbstractGribWindFieldImpl {
|
||||
return hasVariable(windDirectionVariableSpecification, dataSets) && hasVariable(windSpeedVariableSpecification, dataSets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Wind> getAllWindFixes() {
|
||||
final List<Wind> result = new ArrayList<>();
|
||||
for (final FeatureDataset dataSet : getDataSets()) {
|
||||
GridDatatype directionGrid = null;
|
||||
GridDatatype speedGrid = null;
|
||||
for (Iterator<GridDatatype> i=((GridDataset) dataSet).getGrids().iterator(); i.hasNext() && (directionGrid==null || speedGrid==null); ) {
|
||||
final GridDatatype grid = i.next();
|
||||
if (windDirectionVariableSpecification.matches(grid.getVariable())) {
|
||||
assert isDegreesTrue(getUnit(grid.getVariable()).get());
|
||||
directionGrid = grid;
|
||||
} else if (windSpeedVariableSpecification.matches(grid.getVariable())) {
|
||||
assert isMetersPerSecond(getUnit(grid.getVariable()).get());
|
||||
speedGrid = grid;
|
||||
}
|
||||
}
|
||||
if (directionGrid != null && speedGrid != null) {
|
||||
final GridDatatype finalDirectionGrid = directionGrid;
|
||||
final GridDatatype finalSpeedGrid = speedGrid;
|
||||
for (final Wind wind : foreach(directionGrid, (int timeIndex, int x, int y, TimePoint timePoint, Position position)->{
|
||||
try {
|
||||
final Wind wind;
|
||||
double speedInMetersPerSecond = getValue(finalSpeedGrid, timeIndex, /* zIndex */ 0, x, y);
|
||||
double trueDirectionFromInDeg = getValue(finalDirectionGrid, timeIndex, /* zIndex */ 0, x, y);
|
||||
if (!Double.isNaN(speedInMetersPerSecond) && !Double.isNaN(trueDirectionFromInDeg)) {
|
||||
wind = createWindFixFromDirectionAndSpeed(position, timePoint, speedInMetersPerSecond, trueDirectionFromInDeg);
|
||||
} else {
|
||||
wind = null;
|
||||
}
|
||||
return wind;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.INFO, "Exception trying to compute wind from speed and direction", e);
|
||||
return null;
|
||||
}
|
||||
})) {
|
||||
if (wind != null) {
|
||||
result.add(wind);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package com.sap.sailing.grib.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
@@ -17,6 +22,7 @@ import ucar.nc2.dt.grid.GridDataset;
|
||||
import ucar.nc2.ft.FeatureDataset;
|
||||
|
||||
public class UVWindField extends AbstractGribWindFieldImpl {
|
||||
private static final Logger logger = Logger.getLogger(UVWindField.class.getName());
|
||||
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;
|
||||
@@ -63,11 +69,8 @@ public class UVWindField extends AbstractGribWindFieldImpl {
|
||||
}
|
||||
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(),
|
||||
new MeterPerSecondSpeedWithDegreeBearingImpl(Math.sqrt(uComponentInMetersPerSecond.getA()*uComponentInMetersPerSecond.getA()+
|
||||
vComponentInMetersPerSecond.getA()*vComponentInMetersPerSecond.getA()),
|
||||
new RadianBearingImpl(atan2>0 ? atan2 : 2*Math.PI+atan2)));
|
||||
wind = createWindFixFromUAndV(uComponentInMetersPerSecond.getC(), uComponentInMetersPerSecond.getB(),
|
||||
uComponentInMetersPerSecond.getA(), vComponentInMetersPerSecond.getA());
|
||||
} else {
|
||||
wind = null;
|
||||
confidence = 0;
|
||||
@@ -82,4 +85,58 @@ public class UVWindField extends AbstractGribWindFieldImpl {
|
||||
public static boolean handles(FeatureDataset... dataSets) {
|
||||
return hasVariable(uComponentOfWindVariableSpecification, dataSets) && hasVariable(vComponentOfWindVariableSpecification, dataSets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Wind> getAllWindFixes() {
|
||||
final List<Wind> result = new ArrayList<>();
|
||||
for (final FeatureDataset dataSet : getDataSets()) {
|
||||
GridDatatype uGrid = null;
|
||||
GridDatatype vGrid = null;
|
||||
for (Iterator<GridDatatype> i=((GridDataset) dataSet).getGrids().iterator(); i.hasNext() && (uGrid==null || vGrid==null); ) {
|
||||
final GridDatatype grid = i.next();
|
||||
if (uComponentOfWindVariableSpecification.matches(grid.getVariable())) {
|
||||
assert isMetersPerSecond(getUnit(grid.getVariable()).get());
|
||||
uGrid = grid;
|
||||
} else if (vComponentOfWindVariableSpecification.matches(grid.getVariable())) {
|
||||
assert isMetersPerSecond(getUnit(grid.getVariable()).get());
|
||||
vGrid = grid;
|
||||
}
|
||||
}
|
||||
if (uGrid != null && vGrid != null) {
|
||||
final GridDatatype finalUGrid = uGrid;
|
||||
final GridDatatype finalVGrid = vGrid;
|
||||
for (final Wind wind : foreach(uGrid, (int timeIndex, int x, int y, TimePoint timePoint, Position position)->{
|
||||
try {
|
||||
final Wind wind;
|
||||
double uComponentInMetersPerSecond = getValue(finalUGrid, timeIndex, /* zIndex */ 0, x, y);
|
||||
double vComponentInMetersPerSecond = getValue(finalVGrid, timeIndex, /* zIndex */ 0, x, y);
|
||||
if (!Double.isNaN(uComponentInMetersPerSecond) && !Double.isNaN(vComponentInMetersPerSecond)) {
|
||||
wind = createWindFixFromUAndV(position, timePoint, uComponentInMetersPerSecond, vComponentInMetersPerSecond);
|
||||
} else {
|
||||
wind = null;
|
||||
}
|
||||
return wind;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.INFO, "Exception trying to compute wind from speed and direction", e);
|
||||
return null;
|
||||
}
|
||||
})) {
|
||||
if (wind != null) {
|
||||
result.add(wind);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Wind createWindFixFromUAndV(Position position, TimePoint timePoint, double uComponentInMetersPerSecond,
|
||||
double vComponentInMetersPerSecond) {
|
||||
final double atan2 = Math.atan2(uComponentInMetersPerSecond, vComponentInMetersPerSecond);
|
||||
return new WindImpl(position, timePoint,
|
||||
new MeterPerSecondSpeedWithDegreeBearingImpl(
|
||||
Math.sqrt(uComponentInMetersPerSecond * uComponentInMetersPerSecond
|
||||
+ vComponentInMetersPerSecond * vComponentInMetersPerSecond),
|
||||
new RadianBearingImpl(atan2 > 0 ? atan2 : 2 * Math.PI + atan2)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user