//
// NetcdfGrids.java
//

/*

The software in this file is Copyright(C) 2017 by Tom Whittaker.
It is designed to be used with the VisAD system for interactive
analysis and visualization of numerical data.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License in file NOTICE for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package visad.jmet;

import java.io.File;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.Vector;

import ucar.netcdf.Attribute;
import ucar.netcdf.AttributeSet;
import ucar.netcdf.DimensionIterator;
import ucar.netcdf.DimensionSet;
import ucar.netcdf.NetcdfFile;
import ucar.netcdf.Variable;
import ucar.netcdf.VariableIterator;
import visad.CoordinateSystem;
import visad.Data;
import visad.DateTime;
import visad.FieldImpl;
import visad.FlatField;
import visad.FunctionType;
import visad.Gridded1DDoubleSet;
import visad.Integer2DSet;
import visad.Real;
import visad.RealTupleType;
import visad.RealType;
import visad.Set;
import visad.Tuple;
import visad.Unit;
import visad.data.units.Parser;

/**
 * Reads data from netCDF NCEP model grids one parameter at a time
 * instead of the whole file (see Plain).  This is pretty focused on
 * delivering grids that are renderable in 3D.
 *
 * @author Tom Whittaker
 */
public class NetcdfGrids {
  File filename;
  NetcdfFile nc = null;
  double[] pressureLevels;
  double[] valtime;
  double[][] times;
  Set time_set;
  int[] timeIndex;
  DateTime[] validDateTime;
  int num_levels, num_records, xval, yval;
  RealType x,y,level,time_type,pres;
  RealTupleType grid_domain;
  Integer2DSet dom_set;
  CoordinateSystem gridCoord;
  int gridNumber, gridTypeCode;

  /** set up to read grids from a netCDF file
  *
  * @param filename is the name of the netCDF file to read from
  *
  */


  public NetcdfGrids(String filename) {
    this(new File(filename) );
  }

  public NetcdfGrids(File filename) {
    this.filename = filename;
    dom_set = null;
    gridCoord = null;

    try {
      nc = new NetcdfFile(filename, true);
      AttributeSet as = null;
      DimensionSet ds = null;

      // This block is just for me to look at the file...

/* *****************************************************************

      // global attributes
      as = nc.getAttributes();
      AttributeIterator ai = as.iterator();
      int num = as.size();
      System.out.println("number of attributes = "+num);
      while (ai.hasNext() ) {
        Attribute a = ai.next();
        System.out.println("Attribute "+a);
      }

      // dimensions
      ds = nc.getDimensions();
      DimensionIterator di = ds.iterator();
      int numDims = ds.size();
      while (di.hasNext() ) {
        ucar.netcdf.Dimension d = di.next();
        System.out.println("Dimension "+d.getName()+" has length="+
                d.getLength() );
      }

      // variables
      VariableIterator vi = nc.iterator();
      int numVars = nc.size();
      while (vi.hasNext() ) {

        Variable v = vi.next();
        System.out.println("Variable "+v.getName()+
              " has rank = "+v.getRank() );

          as = v.getAttributes() ;
          ai = as.iterator();
          num = as.size();
          System.out.println("   Number of attributes = "+num);
          while (ai.hasNext() ) {
            Attribute a = ai.next();
            System.out.println("   Attribute "+a);
          }

          di = v.getDimensionIterator();
          numDims = v.getRank();
          while (di.hasNext() ) {
            ucar.netcdf.Dimension d = di.next();
            System.out.println("   Dimension "+d.getName()+" has length = "+
                    d.getLength() );
          }

      }
************************************************************* */

      // get the pressure levels values

      int index[] = new int[1];
      Variable p = nc.get("level");
      if (p != null) {
        int temp[] = p.getLengths();
        num_levels = temp[0];
        pressureLevels = new double[num_levels];
        for (int i=0; i (x,y) -> values
      FunctionType withTime_type = new FunctionType(time_type, grid_type);

      // make a Tuple to hold the (level, withTime) values for each level
      tup = new Tuple[num_levels];

      int gridDim = dims[xDim] * dims[yDim];

      double grids[][] = new double[1][gridDim];
      Data griddata[] = new Data[2];

      if (levelDim != -1 && recordDim != -1) {
        for (int p=0; p range[p][1]) range[p][1] = value;
                  }

                  grids[0][i*dims[xDim] + j] = value;
                }
            }

            FlatField nffg = new FlatField(grid_type, dom_set);
            nffg.setSamples(grids, false);
            withTime.setSample(t, nffg);
          }

          griddata[0] = new visad.Real(pres, pressureLevels[p] );
          griddata[1] = withTime;
          tup[p] = new Tuple(griddata);
        }

      } else if (recordDim != -1) {
          range[0][0] = Double.MAX_VALUE;
          // WLH 2 May 2000
          // range[0][1] = Double.MIN_VALUE;
          range[0][1] = -Double.MAX_VALUE;

          FieldImpl withTime = new FieldImpl(withTime_type, time_set);

          for (int t=0; t range[0][1]) range[0][1] = value;
                  }
                  grids[0][i*dims[xDim] + j] = value;
                }
            }

            FlatField nffg = new FlatField(grid_type, dom_set);
            nffg.setSamples(grids, false);
            withTime.setSample(t, nffg);
          }

          griddata[0] = new visad.Real(pres, 1020. );
          griddata[1] = withTime;
          tup[0] = new Tuple(griddata);

      } else {
            for (int i=0; i range[0][1]) range[0][1] = value;
                  }
                  grids[0][i*dims[xDim] + j] = value;
                }
            }

          FlatField nffg = new FlatField(grid_type, dom_set);
          nffg.setSamples(grids, false);

          griddata[0] = new visad.Real(pres, 1020. );
          griddata[1] = nffg;
          tup[0] = new Tuple(griddata);
      }

    } catch (Exception ve) {ve.printStackTrace(); System.exit(1);}


    return tup;

  }

  /** Test routine: java NetcdfGrids 
  */

  public static void main(String args[]) {

    try {
    NetcdfGrids ng = new NetcdfGrids("97032712_eta.nc");
    RealType x = RealType.getRealType("x");
    RealType y = RealType.getRealType("y");
    RealType level = RealType.getRealType("level");
    RealType time_type = RealType.Time;
    RealType pres = RealType.getRealType("pres");
    RealType Z = RealType.getRealType("Z");
    int num_levels = ng.getNumberOfLevels();
    double [][] range = new double[num_levels][2];

    ng.setRealTypes(x,y,level,time_type,pres);
    Tuple d[] = ng.getGrids("Z", Z, range);
    System.out.println("range = "+range[0][0]+" - "+range[0][1]);

    } catch (Exception me) {me.printStackTrace(); System.exit(1); }

  }

  /** fetches grids are returns a Tuple [level][record] of FlatFields
  *
  * this was implemented to test the offset between mapping to
  * animator or just doing one at a time...
  *
  */

  public synchronized Tuple[][] getGridsWithTime(String name, RealType values,
  double[][] range) {

    // find the named variable
    Variable v = nc.get(name);
    if (v == null) {
      System.out.println("could not find "+v+" in netCDF file");
      System.exit(1);
    }

    // get long_name and units attributes
    Attribute long_name = v.getAttribute("long_name");
    Attribute units = v.getAttribute("units");
    Attribute fillvalue = v.getAttribute("_FillValue");

    // now get the dimensions, and look for "x", "y", "level" and "record"
    // these are the key dimension names
    // allow "lat" to be used for "y" and "lon" in place of "x"...sad

    DimensionIterator di = v.getDimensionIterator();
    int numDims = v.getRank();
    int[] dims = v.getLengths();

    String dimNames[] = new String[numDims];
    int xDim = -1;
    int yDim = -1;
    int levelDim = -1;
    int recordDim = -1;

    for (int i=0; i values

      RealType[] domain_components = {x,y};

      gridCoord = new GRIBCoordinateSystem(gridNumber);

      RealTupleType grid_domain =
          new RealTupleType(domain_components, gridCoord, null);

      dom_set = new Integer2DSet(grid_domain,
                                      dims[xDim], dims[yDim]);

      FunctionType grid_type = new FunctionType(grid_domain, values);

      // make a Tuple to hold the (level, withTime) values for each level

      tup = new Tuple[num_levels][num_records];

      int gridDim = dims[xDim] * dims[yDim];

      double grids[][] = new double[1][gridDim];
      Data griddata[] = new Data[2];

      if (levelDim != -1 && recordDim != -1) {
        for (int p=0; p 

Ads help maintain this website.