package nl.deltares.hydrotel;
import nl.wldelft.util.Box;
import nl.wldelft.util.FastDateFormat;
import nl.wldelft.util.FileUtils;
import nl.wldelft.util.LongArrayUtils;
import nl.wldelft.util.StringArrayUtils;
import nl.wldelft.util.TextUtils;
import nl.wldelft.util.TimeUnit;
import nl.wldelft.util.io.LineReader;
import nl.wldelft.util.io.LineWriter;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import ucar.ma2.Array;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import ucar.nc2.units.DateUnit;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
public class HydrotelPreAdapter {
private static final Logger log = Logger.getLogger(HydrotelPreAdapter.class);
private static final String PROPERTIES = "properties";
private static final String SORTIES_HYDROTEL = "sortiesHydrotel";
private static final String SAUVEGARDE_ETAT_FIN_SIMULATION = "sauvegardeEtatFinSimulation";
private static final String PR = "pr";
private static final String STATION_ID = "station_id";
private final File runFile;
private Path runPath = null;
private NetcdfFile netcdfRunFile = null;
private String logLevel = "INFO";
private static final String LOG_LEVEL = "log_level";
private static final String WORK_DIR = "work_dir";
private static final String END_TIME = "end_time";
private static final String START_TIME = "start_time";
private static final String INPUT_NETCDF_FILES_VARIABLE_NAME = "input_netcdf_files";
private File workDirFile = null;
private String[] inputTimeSeriesFiles = null;
private float[] temperatureAdditif1D = null;
private float[] precipitationMultiplicatif1D = null;
private float[] precipitationAdditif1D = null;
private long[] correctionTimes = null;
private File simulationFolder = null;
private static final FastDateFormat YYYYMMDDHH = FastDateFormat.getInstance("yyyy/MM/dd HH", TimeZone.getTimeZone("EST"), Locale.CANADA, null);
private static final FastDateFormat YYYY_MM_DDHH = FastDateFormat.getInstance("yyyy-MM-dd HH", TimeZone.getTimeZone("EST"), Locale.CANADA, null);
private static final FastDateFormat DDMMYYYYKK = FastDateFormat.getInstance("dd/MM/yyyy kk", TimeZone.getTimeZone("EST"), Locale.CANADA, null);
private double startDateTime = 0.0;
private double endDateTime = 0.0;
private boolean saveStatesAtEnd = false;
private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = new DecimalFormatSymbols(Locale.CANADA);
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0000",DECIMAL_FORMAT_SYMBOLS);
private HydrotelPreAdapter(File runFile) {
this.runFile = runFile;
}
public static void main(String[] args) throws Exception {
if (args.length != 1)
throw new IllegalArgumentException("Specify either one argument that is the path to the run file, or 4 arguments that are 'work dir', input file, output file and 'log HZ reeks'.");
File runFile = new File(args[0]);
if (!runFile.exists()) throw new Exception("Can not find run file specified as argument " + runFile);
HydrotelPreAdapter adapter = new HydrotelPreAdapter(runFile);
try {
adapter.run();
if (log.isInfoEnabled()) log.info("HydrotelAdapter run finished without exception");
} catch (Exception e) {
log.error(e.getMessage(), e);
throw e;
} finally {
LogManager.shutdown();
}
}
private void run() throws Exception {
try {
runPath = runFile.getParentFile().toPath();
String absolutePath = runFile.getAbsolutePath();
netcdfRunFile = NetcdfFile.open(absolutePath);
readRunInfoFile();
} finally {
netcdfRunFile.close();
}
convertTimeSeries();
editSimulationFile();
}
private void editSimulationFile() throws IOException {
File simulationCsv = new File(simulationFolder, "simulation.csv");
String[] simulationLines = FileUtils.readAllLines(simulationCsv);
double dayAfterStartDatewriteStateTime = startDateTime + TimeUnit.DAY_MILLISgetWriteStateTime();
for (int i = 1; i < simulationLines.length; i++) {
String line = simulationLines[i];
String[] splitLines = line.split(";");
if (splitLines.length == 0) continue;
String key = splitLines[0];
if (splitLines.length == 1) splitLines = new String[]{key, null};
switch (key) {
case "DATE DEBUT":
splitLines[1] = YYYY_MM_DDHH.format(startDateTime);
break;
case "DATE FIN":
splitLines[1] = YYYY_MM_DDHH.format(endDateTime);
break;
case "ECRITURE ETAT FONTE NEIGE":
case "ECRITURE ETAT BILAN VERTICAL":
case "ECRITURE ETAT RUISSELEMENT SURFACE":
case "ECRITURE ETAT ACHEMINEMENT RIVIERE":
splitLines[1] = YYYY_MM_DDHH.format(dayAfterStartDatewriteStateTime);
break;
default:
continue;
}
simulationLines[i] = TextUtils.join(splitLines, ';');
}
FileUtils.deleteIfExists(simulationCsv);
FileUtils.writeText(simulationCsv, TextUtils.join(simulationLines, '\n'));
}
private double getWriteStateTime() {
if (saveStatesAtEnd) return endDateTime;
return startDateTime + TimeUnit.DAY_MILLIS;
}
private void readRunInfoFile() throws Exception {
logLevel = netcdfRunFile.findVariable(LOG_LEVEL).readScalarString();
getWorkDir();
configureLogging();
if (log.isDebugEnabled()) log.debug("HydrotelAdapter started");
inputTimeSeriesFiles = getFilePaths(INPUT_NETCDF_FILES_VARIABLE_NAME);
simulationFolder = new File(workDirFile, "simulation/simulation");
readStartEndTime();
handleProperties();
}
private void readStartEndTime() throws Exception {
Variable startTimeVar = netcdfRunFile.findVariable(START_TIME);
double relativeStartDateTime = startTimeVar.readScalarDouble();
double relativeEndDateTime = netcdfRunFile.findVariable(END_TIME).readScalarDouble();
Attribute units = startTimeVar.findAttribute("units");
String tunitsString = units.getStringValue();
if (log.isInfoEnabled()) log.info("INFO: Start time: " + relativeStartDateTime + ' ' + tunitsString);
if (log.isInfoEnabled()) log.info("INFO: End time: " + relativeEndDateTime + ' ' + tunitsString);
DateUnit referenceUnit = new DateUnit(units.getStringValue());
long currentReferenceTime = referenceUnit.getDateOrigin().getTime();
ucar.nc2.units.TimeUnit timeUnit = referenceUnit.getTimeUnit();
startDateTime = timeUnit.getValueInSeconds(relativeStartDateTime) * 1000 + currentReferenceTime;
endDateTime = timeUnit.getValueInSeconds(relativeEndDateTime) * 1000 + currentReferenceTime;
}
private void getWorkDir() throws IOException {
String relativeWorkDirString = String.valueOf((char[]) netcdfRunFile.findVariable(WORK_DIR).read().copyToNDJavaArray()).trim();
String workDirString = getWorkDirString(relativeWorkDirString);
workDirFile = new File(workDirString);
if (log.isInfoEnabled()) log.info("Work dir: " + workDirString);
if (!workDirFile.exists()) throw new RuntimeException("Work dir not found: " + workDirString);
}
private String getWorkDirString(String relativeWorkDirString) {
if (TextUtils.equals(relativeWorkDirString, ".")) return runPath.toString();
return runPath.resolve(new File(relativeWorkDirString).toPath()).toString();
}
private void handleProperties() throws Exception {
Variable var = netcdfRunFile.findVariable(PROPERTIES);
String sortiesHydrotel = getPropertyStringValue(var, SORTIES_HYDROTEL);
convertOutputSettings(sortiesHydrotel);
String saveStateAtEndStringValue = getPropertyStringValue(var, SAUVEGARDE_ETAT_FIN_SIMULATION);
if (saveStateAtEndStringValue != null) saveStatesAtEnd = Boolean.valueOf(saveStateAtEndStringValue);
}
private void convertOutputSettings(String sortiesHydrotel) throws IOException {
boolean debitAvalOnly = false;
String[] outputValuesHydrotel = new String[0];
if (sortiesHydrotel == null) {
debitAvalOnly = true;
} else {
outputValuesHydrotel = sortiesHydrotel.split(";");
if (outputValuesHydrotel.length == 1 && outputValuesHydrotel[0].equals("DEBIT_AVAL")) {
debitAvalOnly = true;
}
}
File outputCsv = new File(simulationFolder, "output.csv");
if (debitAvalOnly) {
File exutoireCsv = new File(simulationFolder, "output_exutoire.csv");
FileUtils.copy(exutoireCsv, outputCsv);
} else {
File bassinCsv = new File(simulationFolder, "output_bassin.csv");
String[] bassinLines = FileUtils.readAllLines(bassinCsv);
for (int i = 1; i < bassinLines.length; i++) {
String line = bassinLines[i];
String[] splitLines = line.split(";");
if (StringArrayUtils.contains(outputValuesHydrotel, splitLines[0])) {
splitLines[1] = "1";
}
bassinLines[i] = TextUtils.join(splitLines, ';');
}
FileUtils.writeText(outputCsv, TextUtils.join(bassinLines, '\n'));
}
}
private String[] getFilePaths(String variableName) throws IOException {
Variable inputVar = netcdfRunFile.findVariable(variableName);
Array inputArray = inputVar.read();
Object inputFilesNDArrayObject = inputArray.copyToNDJavaArray();
char[][] inputFilesChar = (char[][]) inputFilesNDArrayObject;
List<String> inputFilesList = new ArrayList<>();
for (char[] chArray : inputFilesChar) {
File inputFileRelativePath = new File(String.valueOf(chArray).trim());
String absoluteFilePath = runPath.resolve(inputFileRelativePath.toPath()).toString();
inputFilesList.add(absoluteFilePath);
}
String[] inputFiles = new String[inputFilesList.size()];
inputFilesList.toArray(inputFiles);
return inputFiles;
}
private static List<String> getStringsFromNetcdfVariable(NetcdfFile netcdfFile, String variableName) throws IOException {
Variable inputVar = netcdfFile.findVariable(variableName);
Array inputArray = inputVar.read();
Object inputFilesNDArrayObject = inputArray.copyToNDJavaArray();
char[][] inputFilesChar = (char[][]) inputFilesNDArrayObject;
List<String> stationIds = new ArrayList<>();
for (char[] chArray : inputFilesChar) {
String stationId = String.valueOf(chArray).trim();
stationIds.add(stationId);
}
return stationIds;
}
private void configureLogging() {
File logFile = new File(workDirFile, "hydrotel_pre_adapter.log");
if (logFile.exists()) {
//noinspection ResultOfMethodCallIgnored
logFile.delete();
}
Properties props = new Properties();
props.setProperty("log4j.rootLogger", logLevel + ", hydrotel");
props.setProperty("log4j.appender.hydrotel.File", logFile.getAbsolutePath());
props.setProperty("log4j.appender.hydrotel", "org.apache.log4j.RollingFileAppender");
props.setProperty("log4j.appender.hydrotel.layout", "org.apache.log4j.PatternLayout");
props.setProperty("log4j.appender.hydrotel.layout.ConversionPattern", "%p - %m \n");
LogManager.resetConfiguration();
PropertyConfigurator.configure(props);
if (log.isDebugEnabled()) log.debug("Debug log level enabled");
if (log.isInfoEnabled()) log.info("Info log level enabled");
}
private void convertTimeSeries() throws Exception {
String meteoFile = null;
for (String inputTimeSeriesFile : inputTimeSeriesFiles) {
if (inputTimeSeriesFile.endsWith("meteo.nc")) {
meteoFile = inputTimeSeriesFile;
} else if (inputTimeSeriesFile.endsWith("corrections.nc")) {
convertCorrections(inputTimeSeriesFile);
} else if (inputTimeSeriesFile.endsWith("debit.nc")) {
convertHydro(inputTimeSeriesFile);
}
}
convertMeteo(meteoFile);
}
private void convertHydro(String inputTimeSeriesFile) throws IOException {
File hydroDir = new File(workDirFile, "hydro");
File stationsSth = new File(hydroDir, "station.sth");
List<String> requiredStations = getRequiredStations(stationsSth);
NetcdfFile netcdfFile = null;
long[] times;
float[][] floats;
List<String> stationIds;
try {
netcdfFile = NetcdfFile.open(inputTimeSeriesFile);
Variable timeVar = netcdfFile.findVariable("time");
times = readTimes(timeVar);
boolean useExtraEnsembleDimension = useExtraEnsembleDimension(netcdfFile);
floats = read2DArray(netcdfFile, "Q_obs", useExtraEnsembleDimension);
stationIds = getStringsFromNetcdfVariable(netcdfFile, STATION_ID);
} finally {
if (netcdfFile != null) netcdfFile.close();
}
writeHydroFiles(hydroDir, stationsSth, requiredStations, times, floats, stationIds, netcdfFile);
}
private static void writeHydroFiles(File hydroDir, File stationsSth, List<String> requiredStations, long[] times, float[][] floats, List<String> stationIds, NetcdfFile netcdfFile) throws IOException {
for (int i = 0; i < stationIds.size(); i++) {
String id = stationIds.get(i);
if (!requiredStations.contains(id)) {
log.warn("Data exported for station " + id + " but not present in " + stationsSth);
continue;
}
requiredStations.remove(id);
writeHydroStationFile(hydroDir, times, floats, i, id);
}
// Log errors for stations not removed in loop above
for (String requiredStation : requiredStations) {
log.error("No data for station " + requiredStation + " found in " + netcdfFile.getLocation());
}
}
private static void writeHydroStationFile(File hydroDir, long[] times, float[][] floats, int i, String id) throws IOException {
File hydroStationFile = new File(hydroDir, id + ".hyd");
try (LineWriter lineWriter = new LineWriter(hydroStationFile, Charset.defaultCharset())) {
lineWriter.writeLine("1 3");
lineWriter.writeLine("");
for (int j = 0; j < times.length; j++) {
String dateTime = DDMMYYYYKK.format(times[j]);
String value = TextUtils.padLeft(DECIMAL_FORMAT.format(floats[j][i]), 8);
lineWriter.writeLine(dateTime + ' ' + value);
}
}
}
private static List<String> getRequiredStations(File stationsSth) throws IOException {
List<String> requiredStations = new ArrayList<>();
try (LineReader lineReader = new LineReader(stationsSth, Charset.defaultCharset())) {
lineReader.skipLines(3);
String line = lineReader.readLine();
while (line != null) {
String[] columns = line.split(" ");
requiredStations.add(columns[0]);
line = lineReader.readLine();
}
}
return requiredStations;
}
private void convertCorrections(String inputTimeSeriesFile) throws IOException {
NetcdfFile netcdfFile = null;
try {
netcdfFile = NetcdfFile.open(inputTimeSeriesFile);
Variable timeVar = netcdfFile.findVariable("time");
correctionTimes = readTimes(timeVar);
useExtraEnsembleDimension(netcdfFile);
float[] eenAdditif1D = read1DArrayFloat(netcdfFile, "eenAdditif");
float[] reserveEauSolMultiplicatif1D = read1DArrayFloat(netcdfFile, "reserveEauSolMultiplicatif");
File correctionCsv = new File(simulationFolder, "correction.csv");
String correctionText = FileUtils.readText(correctionCsv);
File correctionTmp = new File(simulationFolder, "correction_tmp.csv");
try (LineWriter lineWriter = new LineWriter(correctionTmp, Charset.defaultCharset())) {
lineWriter.write(correctionText);
lineWriter.writeLine("");
writeEenAdditiveCorrection(eenAdditif1D, lineWriter, DECIMAL_FORMAT);
writeReserveEauSolMultiplicatifCorrection(reserveEauSolMultiplicatif1D, lineWriter, DECIMAL_FORMAT);
}
FileUtils.deleteIfExists(correctionCsv);
FileUtils.copy(correctionTmp, correctionCsv);
FileUtils.deleteIfExists(correctionTmp);
precipitationAdditif1D = read1DArrayFloat(netcdfFile, "precipitationAdditif");
precipitationMultiplicatif1D = read1DArrayFloat(netcdfFile, "precipitationMultiplicatif");
temperatureAdditif1D = read1DArrayFloat(netcdfFile, "temperatureAdditif");
} finally {
if (netcdfFile != null) netcdfFile.close();
}
}
private void writeEenAdditiveCorrection(float[] values, LineWriter lineWriter, DecimalFormat decimalFormat) throws IOException {
String[] strings = new String[8];
for (int k = 0; k < correctionTimes.length; k++) {
float value = values[k];
if (value == 0.0f) continue;
String dateTimeString = YYYYMMDDHH.format(correctionTimes[k]);
if (Float.isNaN(value)) throw new RuntimeException("NaN value found for eenAdditif at " + dateTimeString);
strings[0] = "1";
strings[1] = dateTimeString;
strings[2] = dateTimeString;
strings[3] = "5";
strings[4] = decimalFormat.format(value);
strings[5] = "1";
strings[6] = "0";
strings[7] = "tous";
String line = TextUtils.join(strings, ';');
lineWriter.writeLine(line);
}
}
private void writeReserveEauSolMultiplicatifCorrection(float[] values, LineWriter lineWriter, DecimalFormat decimalFormat) throws IOException {
String[] strings = new String[8];
for (int k = 0; k < correctionTimes.length; k++) {
float value = values[k];
if (value == 1.0f) continue;
String dateTimeString = YYYYMMDDHH.format(correctionTimes[k]);
if (Float.isNaN(value))
throw new RuntimeException("NaN value found for reserveEauSolMultiplicatif at " + dateTimeString);
strings[0] = "1";
strings[1] = dateTimeString;
strings[2] = dateTimeString;
strings[3] = "4";
strings[4] = "0";
strings[5] = decimalFormat.format(value);
strings[6] = "0";
strings[7] = "tous";
String line = TextUtils.join(strings, ';');
lineWriter.writeLine(line);
}
}
private static double[] read1DArrayDouble(NetcdfFile netcdfFile, String varName) throws IOException {
Variable eenAdditif = netcdfFile.findVariable(varName);
Array eenAdditifArray = eenAdditif.read();
return (double[]) eenAdditifArray.copyTo1DJavaArray();
}
private static float[] read1DArrayFloat(NetcdfFile netcdfFile, String varName) throws IOException {
Variable variable = netcdfFile.findVariable(varName);
Array array = variable.read();
return (float[]) array.copyTo1DJavaArray();
}
private void convertMeteo(String inputTimeSeriesFile) throws IOException {
File meteoDir = new File(workDirFile, "meteo");
Map<Box<Float, Float>, String> requiredXYCoords = getRequiredCoords(meteoDir);
NetcdfFile netcdfFile = null;
try {
netcdfFile = NetcdfFile.open(inputTimeSeriesFile);
double[] x = read1DArrayDouble(netcdfFile, "x");
double[] y = read1DArrayDouble(netcdfFile, "y");
boolean useExtraEnsembleDimension = useExtraEnsembleDimension(netcdfFile);
Variable timeVar = netcdfFile.findVariable("time");
long[] times = readTimes(timeVar);
//noinspection ResultOfMethodCallIgnored
meteoDir.mkdirs();
float[][][] prFloats = read3DArray(netcdfFile, PR, useExtraEnsembleDimension);
float[][][] tasMinFloats = read3DArray(netcdfFile, "tasmin", useExtraEnsembleDimension);
float[][][] tasMaxFloats = read3DArray(netcdfFile, "tasmax", useExtraEnsembleDimension);
writeRequiredMeteoFiles(meteoDir, requiredXYCoords, x, y, times, DDMMYYYYKK, prFloats, tasMinFloats, tasMaxFloats);
} finally {
if (netcdfFile != null) netcdfFile.close();
}
}
private static boolean useExtraEnsembleDimension(NetcdfFile netcdfFile) {
Dimension realization = netcdfFile.findDimension("realization");
if (realization == null) return false;
if (realization.getLength() > 1) throw new RuntimeException("Multiple ensembles at once not supported");
return true;
}
private void writeRequiredMeteoFiles(File meteoDir, Map<Box<Float, Float>, String> requiredXYCoords, double[] x, double[] y, long[] times, FastDateFormat dateFormat, float[][][] prFloats, float[][][] tasMinFloats, float[][][] tasMaxFloats) throws IOException {
for (int i = 0; i < x.length; i++) {
float xCoord = (float) x[i];
for (int j = 0; j < y.length; j++) {
float yCoord = (float) y[j];
Box<Float, Float> foundCoords = new Box<>(xCoord, yCoord);
String fileName = requiredXYCoords.get(foundCoords);
if (fileName == null) continue;
File meteoFile = new File(meteoDir, fileName + ".met");
writeMeteoFile(times, dateFormat, prFloats, tasMinFloats, tasMaxFloats, i, j, meteoFile);
}
}
}
private void writeMeteoFile(long[] times, FastDateFormat dateFormat, float[][][] prFloats, float[][][] tasMinFloats, float[][][] tasMaxFloats, int i, int j, File meteoFile) throws IOException {
try (LineWriter lineWriter = new LineWriter(meteoFile, Charset.defaultCharset())) {
lineWriter.writeLine("1 3");
String[] strings = new String[5];
for (int k = 0; k < times.length; k++) {
assert correctionTimes[k] == times[k];
String[] dateTimeStrings = dateFormat.format(times[k]).split(" ");
strings[0] = dateTimeStrings[0];
strings[1] = dateTimeStrings[1];
strings[2] = TextUtils.padLeft(String.valueOf(tasMaxFloats[k][j][i] + temperatureAdditif1D[k]), 5);
strings[3] = TextUtils.padLeft(String.valueOf(tasMinFloats[k][j][i] + temperatureAdditif1D[k]), 5);
strings[4] = String.valueOf(prFloats[k][j][i] * precipitationMultiplicatif1D[k] + precipitationAdditif1D[k]);
String line = TextUtils.join(strings, '_').replaceAll("_", " ");
lineWriter.writeLine(line);
}
}
}
private static Map<Box<Float, Float>, String> getRequiredCoords(File meteoDir) throws IOException {
File stationStm = new File(meteoDir, "station.stm");
Map<Box<Float, Float>, String> requiredXYCoords = new HashMap<>();
try (LineReader lineReader = new LineReader(stationStm, Charset.defaultCharset())) {
lineReader.skipLines(3);
String line = lineReader.readLine();
while (line != null) {
String[] columns = line.split(" ");
float x = Float.valueOf(columns[1]);
float y = Float.valueOf(columns[2]);
Box<Float, Float> coordBox = new Box<>(x, y);
requiredXYCoords.put(coordBox, columns[0]);
line = lineReader.readLine();
}
}
return requiredXYCoords;
}
private static float[][][] read3DArray(NetcdfFile netcdfFile, String varName, boolean useExtraEnsembleDimension) throws IOException {
Variable variable = netcdfFile.findVariable(varName);
Array values = variable.read();
if (!useExtraEnsembleDimension) return (float[][][]) values.copyToNDJavaArray();
int[] shape = variable.getShape();
if (shape.length == 3) {
log.warn("Not all variables contain an ensemble dimension");
return (float[][][]) values.copyToNDJavaArray();
}
float[][][][] floats4D = (float[][][][]) values.copyToNDJavaArray();
float[][][] floats3D = new float[floats4D.length][][];
for (int i = 0; i < floats4D.length; i++) {
floats3D[i] = floats4D[i][0];
}
return floats3D;
}
@SuppressWarnings("SameParameterValue")
private static float[][] read2DArray(NetcdfFile netcdfFile, String varName, boolean useExtraEnsembleDimension) throws IOException {
Variable variable = netcdfFile.findVariable(varName);
Array values = variable.read();
if (!useExtraEnsembleDimension) return (float[][]) values.copyToNDJavaArray();
int[] shape = variable.getShape();
if (shape.length == 2) {
log.warn("Not all variables contain an ensemble dimension");
return (float[][]) values.copyToNDJavaArray();
}
float[][][] floats3D = (float[][][]) values.copyToNDJavaArray();
float[][] floats2D = new float[floats3D.length][];
for (int i = 0; i < floats2D.length; i++) {
floats2D[i] = floats3D[i][0];
}
return floats2D;
}
private static String getPropertyStringValue(Variable propertiesVariable, String propertyName) throws Exception {
if (propertiesVariable == null) return null;
Attribute attribute = propertiesVariable.findAttribute(propertyName);
if (attribute == null) return null;
String value = attribute.getStringValue();
if (value == null || value.trim().isEmpty()) {
throw new Exception("Property '" + propertyName + "' in netcdf run file is empty or is not of type String.");
}
String trimmedValue = value.trim();
if (log.isDebugEnabled())
log.debug("Read property " + propertyName + " with value '" + trimmedValue + "' from netcdf run file.");
return trimmedValue;
}
private static long[] readTimes(Variable timeVariable) throws IOException {
if (timeVariable == null) return LongArrayUtils.EMPTY_ARRAY;
//read times.
Array timeArray = timeVariable.read();
double[] times = (double[]) timeArray.get1DJavaArray(Double.class);
//convert times.
long[] convertedTimes = new long[times.length];
DateUnit dateUnit = readTimeUnit(timeVariable);
if (dateUnit == null)
throw new IOException("Invalid date time unit string has been coded in the file: '" + timeVariable.getUnitsString() + "'. Unit string should be for example: 'seconds since 2012-01-30 00:00:00'");
for (int i = 0; i < times.length; i++) {
Date date = dateUnit.makeDate(times[i]);
if (date == null) throw new RuntimeException("Invalid time the file: '" + times[i]);
convertedTimes[i] = date.getTime();
}
return convertedTimes;
}
private static DateUnit readTimeUnit(Variable timeVariable) {
try {
String unitString = timeVariable.getUnitsString();
//if present, replace . by : in the timeZone specification in the unitString,
//e.g. change "days since 2011-09-19 06:0:0.0 0.00" to "days since 2011-09-19 06:0:0.0 0:00".
//This is a workaround implemented for FEWS-6544.
String[] parts = unitString.split("\\s+");
if (parts.length > 0 && parts[parts.length - 1].matches(".?\\d{1,2}\\.\\d{2}")) {
//if a timeZone specification is present, then it is always the last part, see
//http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.5/cf-conventions.html#time-coordinate
parts[parts.length - 1] = parts[parts.length - 1].replaceFirst("\\.", ":");
StringBuilder buffer = new StringBuilder(parts[0]);
for (int n = 1; n < parts.length; n++) {
buffer.append(' ').append(parts[n]);
}
unitString = buffer.toString();
}
return new DateUnit(unitString);
} catch (Exception e) {
//if the given variable does not have a unit of time.
return null;
}
}
}
|