Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.
Code Block
titleJava class example adapter for netcdf gridded time series
package netcdfexample.reading;

import nl.wldelft.util.TextUtils;
import ucar.ma2.Array;
import ucar.ma2.ArrayDouble;
import ucar.ma2.ArrayFloat;
import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.NetcdfFileWriteable;
import ucar.nc2.Variable;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.units.DateUnit;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class NetcdfGridAdapter {

    public static final String AIRPRESSURE_FORECAST = "airpressure_forecast";
    public static final String LON = "lon";
    public static final String LAT = "lat";
    public static final String X = "x";
    public static final String Y = "y";
    public static final String TIME = "time";

    public static void main(String[] args) throws Exception {

        if (args.length != 1)
            throw new Exception("Specify only the netcdf run file as argument");

        File runFile = new File(args[0]);
        if (!runFile.exists())
            throw new Exception("Can not find run file specified as argument " + runFile);

        //Reading netcdf run file
        Path runPath = runFile.getParentFile().toPath();
        NetcdfDataset netcdfRunFileDataset = new NetcdfDataset(NetcdfDataset.openFile(runFile.getAbsolutePath(), null));

        Variable startTimeVar = netcdfRunFileDataset.findVariable("start_time");
        //Start and end time can be used for model specific settings
        double startDateTime = startTimeVar.readScalarDouble();
        double endDateTime = netcdfRunFileDataset.findVariable("end_time").readScalarDouble();
        Attribute units = startTimeVar.findAttribute("units");
        String tunitsString = units.getStringValue();
        DateUnit referenceUnit = new DateUnit(tunitsString);
        //The time of date origin can be used to determine the real time values
        long currentReferenceTime = referenceUnit.getDateOrigin().getTime();
        //The variable work_dir specifies the working directory relative to the directory of the run file
        String relativeWorkDirString = netcdfRunFileDataset.findVariable("work_dir").readScalarString().trim();
        String workDir;
        //A point "." means the work dir is the same as the directory of the run file
        if (TextUtils.equals(relativeWorkDirString, ".")) {
            workDir = runPath.toString();
        } else {
            workDir = runPath.resolve(new File(relativeWorkDirString).toPath()).toString();

        //Reading variable that specifies input netcdf files
        Variable inputNetcdfVar = netcdfRunFileDataset.findVariable("input_netcdf_files");
        Array inputNetcdfArray =;
        Object inputTimeSeriesFilesNDArrayObject = inputNetcdfArray.copyToNDJavaArray();
        char[][] inputTimeSeriesFilesChar = (char[][]) inputTimeSeriesFilesNDArrayObject;

        //Converting 2 dimensional char array to list of strings
        List<String> inputTimeSeriesFilesList = new ArrayList<>();
        for (char[] chArray : inputTimeSeriesFilesChar) {
        //Done reading netcdf run file

        //Processing input files
        for (String inputFilePath : inputTimeSeriesFilesList) {
            File inputFileRelativePath = new File(inputFilePath);
            String absoluteFilePath = runPath.resolve(inputFileRelativePath.toPath()).toString();
            File inputNetcdfFile = new File(absoluteFilePath);
            //Should not happen often in practice because run file only specifies input netcdf files that are actually exported
            //But could happen when running adapter with older run info file
            if (!inputNetcdfFile.exists())
                throw new RuntimeException("Input file does not exist: " + absoluteFilePath);
            processAirPressure2DimensionalGrid(absoluteFilePath, workDir, tunitsString);

    //Example how to read gridded data from netcdf file exported by FEWS
    public static void processAirPressure2DimensionalGrid(String absoluteFilePath, String workDir, String tunitsString) throws Exception {
        NetcdfFile netcdfDatasetInputFile = new NetcdfDataset(NetcdfDataset.openFile(absoluteFilePath, null));

        try {
            //Reading air pressure variable
            Variable airPressureVariable = netcdfDatasetInputFile.findVariable(AIRPRESSURE_FORECAST);
            if (airPressureVariable == null)
                throw new Exception("Air pressure variable '" + AIRPRESSURE_FORECAST + "' not found in");
            //Reading time variable
            Dimension timeDimension = airPressureVariable.getDimension(0);
            Variable timeVariable = netcdfDatasetInputFile.findVariable(timeDimension.getName());
            long[] convertedTimeArray = readTimes(timeVariable);
            //Reading latitude variable
            Variable latitudeVariable = netcdfDatasetInputFile.findVariable(LAT);
            Array latitudeArray =;
            double[][] latitudeValues = (double[][]) latitudeArray.copyToNDJavaArray();
            //Reading longitude variable
            Variable longitudeVariable = netcdfDatasetInputFile.findVariable(LON);
            Array longitudeArray =;
            double[][] longitudeValues = (double[][]) longitudeArray.copyToNDJavaArray();
            //Reading x variable
            Variable xVariable = netcdfDatasetInputFile.findVariable(X);
            Array xArray =;
            double[] xValues = (double[]) xArray.copyTo1DJavaArray();
            //Reading y variable
            Variable yVariable = netcdfDatasetInputFile.findVariable(Y);
            Array yArray =;
            double[] yValues = (double[]) yArray.copyTo1DJavaArray();
            //Reading air pressure values
            Array airPressureArray =;
            float[][][] floatValues = (float[][][]) airPressureArray.copyToNDJavaArray();

            File outputNetcdfFile = new File(workDir, "");
            NetcdfFileWriteable gridNetcdf = NetcdfFileWriteable.createNew(outputNetcdfFile.getPath(), false);
            try {
                writeNetcdfGrid(gridNetcdf, floatValues, tunitsString, convertedTimeArray, latitudeValues, longitudeValues, xValues, yValues);
            } finally {
        } finally {

    //Example how to write netcdf for FEWS
    private static void writeNetcdfGrid(NetcdfFileWriteable netcdfFile, float[][][] floatValues, String timeUnitsString, long[] convertedTimeArray, double[][] latitudeValues, double[][] longitudeValues, double[] xValues, double[] yValues) throws IOException, InvalidRangeException {

        Dimension timeDimension = netcdfFile.addDimension(TIME, floatValues.length);
        Dimension yDimension = netcdfFile.addDimension(Y, floatValues[0].length);
        Dimension xDimension = netcdfFile.addDimension(X, floatValues[0][0].length);

        ArrayList<Dimension> timeDimensions = new ArrayList<>();
        addNetcdfVariable(netcdfFile, timeDimensions, TIME, DataType.DOUBLE, TIME, TIME, timeUnitsString, "T");

        ArrayList<Dimension> xDimensions = new ArrayList<>();
        addNetcdfVariable(netcdfFile, xDimensions, X, DataType.DOUBLE, "projection_x_coordinate", "x coordinate according to CH1903", "degrees_east", "X");

        ArrayList<Dimension> yDimensions = new ArrayList<>();
        addNetcdfVariable(netcdfFile, yDimensions, Y, DataType.DOUBLE, "projection_y_coordinate", "y coordinate according to CH1903", "degrees_north", "Y");

        ArrayList<Dimension> latLonDimensions = new ArrayList<>();
        addNetcdfVariable(netcdfFile, latLonDimensions, LAT, DataType.DOUBLE, "latitude", "latitude", "degrees_north", "Y");
        addNetcdfVariable(netcdfFile, latLonDimensions, LON, DataType.DOUBLE, "longitude", "longitude", "degrees_north", "Y");

        ArrayList<Dimension> airPressureDimensions = new ArrayList<>();

        addNetcdfVariable(netcdfFile, airPressureDimensions, AIRPRESSURE_FORECAST, DataType.FLOAT, null, AIRPRESSURE_FORECAST, "hPa", null, -999, "y x");

        //First define all variable and dimensions, then create the netcdf, after creation values can be written to variables

        //Write values to variable
        writeTime(netcdfFile, convertedTimeArray);
        write1DValues(netcdfFile, xValues, X);
        write1DValues(netcdfFile, yValues, Y);
        write2DValues(netcdfFile, latitudeValues, LAT);
        write2DValues(netcdfFile, longitudeValues, LON);
        writeAirPressure(netcdfFile, floatValues);

    private static void addNetcdfVariable(NetcdfFileWriteable netcdfFile, ArrayList<Dimension> dimensions, String variableName, DataType dataType, String standardName, String longName, String units, String axis) {
        netcdfFile.addVariable(variableName, dataType, dimensions);
        netcdfFile.addVariableAttribute(variableName, "standard_name", standardName);
        netcdfFile.addVariableAttribute(variableName, "long_name", longName);
        netcdfFile.addVariableAttribute(variableName, "units", units);
        netcdfFile.addVariableAttribute(variableName, "axis", axis);

    private static void addNetcdfVariable(NetcdfFileWriteable netcdfFile, ArrayList<Dimension> dimensions, String variableName, DataType dataType, String standardName, String longName, String units, String axis, int fillValue, String coordinates) {
        netcdfFile.addVariable(variableName, dataType, dimensions);
        if (standardName != null) netcdfFile.addVariableAttribute(variableName, "standard_name", standardName);
        if (longName != null) netcdfFile.addVariableAttribute(variableName, "long_name", longName);
        if (units != null) netcdfFile.addVariableAttribute(variableName, "units", units);
        if (axis != null) netcdfFile.addVariableAttribute(variableName, "axis", axis);
        if (fillValue != Integer.MIN_VALUE) netcdfFile.addVariableAttribute(variableName, "_FillValue", fillValue);
        if (coordinates != null) netcdfFile.addVariableAttribute(variableName, "coordinates", coordinates);

    private static void writeAirPressure(NetcdfFileWriteable netcdfFile, float[][][] floatValues) throws IOException, InvalidRangeException {
        ArrayFloat.D3 airPressureArray = new ArrayFloat.D3(floatValues.length, floatValues[0].length, floatValues[0][0].length);
        for (int i = 0; i < floatValues.length; i++) {
            for (int j = 0; j < floatValues[0].length; j++) {
                for (int k = 0; k < floatValues[0][0].length; k++) {
                    airPressureArray.set(i, j, k, floatValues[i][j][k]);
        netcdfFile.write(AIRPRESSURE_FORECAST, airPressureArray);

    private static void write2DValues(NetcdfFileWriteable netcdfFile, double[][] latValues, String variableName) throws IOException, InvalidRangeException {
        ArrayDouble.D2 latArray = new ArrayDouble.D2(latValues.length, latValues[0].length);
        for (int i = 0; i < latValues.length; i++) {
            for (int j = 0; j < latValues[0].length; j++) {
                latArray.set(i, j, latValues[i][j]);
        netcdfFile.write(variableName, latArray);

    private static void writeTime(NetcdfFileWriteable netcdfFile, long[] convertedTimeArray) throws IOException, InvalidRangeException {
        ArrayDouble.D1 timeArray = new ArrayDouble.D1(convertedTimeArray.length);
        for (int i = 0; i < convertedTimeArray.length; i++) {
            timeArray.set(i, convertedTimeArray[i]);
        netcdfFile.write(TIME, timeArray);

    private static void write1DValues(NetcdfFileWriteable netcdfFile, double[] yValues, String variableName) throws IOException, InvalidRangeException {
        ArrayDouble.D1 yArray = new ArrayDouble.D1(yValues.length);
        for (int i = 0; i < yValues.length; i++) {
            yArray.set(i, yValues[i]);
        netcdfFile.write(variableName, yArray);

    private static long[] readTimes(Variable timeVariable) throws IOException {
        if (timeVariable == null) throw new RuntimeException("Time variable not present");

        //read times.
        Array timeArray =;
        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) {
                convertedTimes[i] = date.getTime();
            } else {//if date is null.
                convertedTimes[i] = 0;
        return convertedTimes;

    public 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
                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;