/*
 * Copyright 1998, University Corporation for Atmospheric Research
 * All Rights Reserved.
 * See file LICENSE for copying and redistribution conditions.
 *
 * $Id: View.java,v 1.9 2006-02-13 22:30:08 curtis Exp $
 */

package visad.data.netcdf.in;

import java.io.IOException;
import java.util.Map;
import java.util.WeakHashMap;
import ucar.netcdf.Attribute;
import ucar.netcdf.Dimension;
import ucar.netcdf.DimensionIterator;
import ucar.netcdf.Netcdf;
import ucar.netcdf.Variable;
import ucar.netcdf.VariableIterator;
import visad.CoordinateSystem;
import visad.data.in.ArithProg;
import visad.data.netcdf.Quantity;
import visad.data.netcdf.QuantityDB;
import visad.data.units.Parser;
import visad.DoubleSet;
import visad.ErrorEstimate;
import visad.FloatSet;
import visad.Gridded1DDoubleSet;
import visad.Gridded1DSet;
import visad.Integer1DSet;
import visad.Linear1DSet;
import visad.RealType;
import visad.ScalarType;
import visad.SI;
import visad.SimpleSet;
import visad.TextType;
import visad.TypeException;
import visad.Unit;
import visad.VisADException;

/**
 * A convention-dependent view of a netCDF dataset.
 *
 * @author Steven R. Emmerson
 * @version $Revision: 1.9 $ $Date: 2006-02-13 22:30:08 $
 */
public abstract class View
{
    /**
     * The netCDF dataset that is being viewed through these conventions.
     */
    private final Netcdf     netcdf;

    /**
     * The quantity database to use to map netCDF variables to VisAD
     * Quantity-s.
     */
    private final QuantityDB quantityDB;

    /**
     * Flag for whether this View handles char variables as Text
     */
    private final boolean charToText;

    /*
     * Performance caches:
     */
    private final Map        varToRealType;
    private final Map        varToTextType;
    private final Map        varToUnit;
    private final Map        dimToSet;
    private final Map        dimToRealType;

    /*
     * Well-known quantities for comparison purposes:
     */
    private final Quantity   longitude;
    private final Quantity   latitude;

    /**
     * Something for creating unique names.
     */
    private static int       nameCount;

    /** Names of outer dimensions to be factored out
    *
    */

    private java.util.Set factorNameSet = null;

    /**
     * Constructs from a netCDF dataset.
     *
     * @param netcdf                The netCDF dataset.
     * @param quantityDB            The quantity database to use to map netCDF
     *                              variables to VisAD Quantity-s.
     * @throws NullPointerException if either argument is null.
     */
    protected View(Netcdf netcdf, QuantityDB quantityDB)
    {
        this(netcdf, quantityDB, false);
    }

    /**
     * Constructs from a netCDF dataset.
     *
     * @param netcdf                The netCDF dataset.
     * @param quantityDB            The quantity database to use to map netCDF
     *                              variables to VisAD Quantity-s.
     * @param charToText            Specifies whether the View should map char
     *                              variables to VisAD Text objects
     * @throws NullPointerException if either argument is null.
     */
    protected View(Netcdf netcdf, QuantityDB quantityDB, boolean charToText)
    {
        this.netcdf = netcdf;
        this.quantityDB = quantityDB;
        this.charToText = charToText;
        varToUnit = new WeakHashMap();
        dimToSet = new WeakHashMap();
        varToRealType = new WeakHashMap();
        varToTextType = new WeakHashMap();
        dimToRealType = new WeakHashMap();
        longitude = quantityDB.get("longitude");
        latitude = quantityDB.get("latitude");
    }

    /**
     * Returns a view of a netCDF dataset.  The exact view returned depends
     * on the netCDF dataset.
     *
     * @param netcdf            The netCDF dataset.
     * @param db		        A quantity database to be used to map netCDF
     *                          variables to VisAD {@link Quantity}s.
     * @return                  A view of the dataset.
     */
    public static View getInstance(Netcdf netcdf, QuantityDB db)
    {
        return getInstance(netcdf, db, false);
    }

    /**
     * Returns a view of a netCDF dataset.  The exact view returned depends
     * on the netCDF dataset.
     *
     * @param netcdf            The netCDF dataset.
     * @param db		        A quantity database to be used to map netCDF
     *                          variables to VisAD {@link Quantity}s.
     * @param charToText        Specifies whether the View should map char
     *                          variables to VisAD Text objects
     * @return                  A view of the dataset.
     */
    public static View getInstance(
        Netcdf netcdf, QuantityDB db, boolean charToText)
    {
        View   view;
        String conventions = getConventionsString(netcdf);
        if (conventions == null)
        {
            view = new DefaultView(netcdf, db, charToText);
        }
        else
        {
            try
            {
                if (conventions.equals("CF-1.0"))
                    view = new CfView(netcdf, db, charToText);
                else if (conventions.equals("COARDS"))
                    view = new CfView(netcdf, db);
                else if (conventions.equals("COARDS/CF-1.0"))
                    view = new CfView(netcdf, db, charToText);
                else
                {
                    System.err.println(
                        "Unknown netCDF conventions attribute (" +
                        conventions + ").  Using default view...");
                    view = new DefaultView(netcdf, db, charToText);
                }
            }
            catch (IllegalArgumentException e)
            {
                System.err.println(
                    "netCDF dataset doesn't follow stated conventions (" +
                    conventions + "): " + e.getMessage() + 
                    "\nUsing default view...");
                view = new DefaultView(netcdf, db, charToText);
            }
        }
        return view;
    }

    /**
     * Does this View handle text.
     *
     * @return true if text is handled
     */
    public boolean isCharToText() {
       return charToText;
    }

    /**
     * Returns the underlying netCDF dataset.
     *
     * @return                  The netCDF dataset.
     */
    public Netcdf getNetcdf()
    {
        return netcdf;
    }

    /**
     * 

Returns the value of the global "Conventions" attribute. If the * attribute doesn't exist or is invalid, then null is * returned. If the attribute exists but is not string-valued, then an * error message is printed to {@link System#err} and null is * returned.

* * @param netcdf The netCDF dataset. * @return The value of the attribute. */ protected static String getConventionsString(Netcdf netcdf) { Attribute attr = netcdf.getAttribute("Conventions"); try { return attr != null ? attr.getStringValue() : null; } catch (ClassCastException e) { System.err.println("The \"Conventions\" attribute (" + attr + ") isn't a string"); return null; } } /** * Returns the named netCDF variable. Returns null if the * variable doesn't exist. * * @param name The name of the netCDF variable. * @throws NullPointerException if the name is null. * @return The named netCDF variable or * null. */ protected Variable getVariable(String name) { return netcdf.get(name); } /** * Indicates if the netCDF variable with a given name is numeric. * * @param name The name of the netCDF variable. * @return true if and only if the * variable exists has numeric values. */ protected boolean isNumeric(String name) { Variable var = netcdf.get(name); if (var == null) return false; return isNumeric(var); } /** * Indicates if the given netCDF variable is numeric. * * @param var The netCDF variable. * @return true if and only if the * variable is numeric. * @throws NullPointerException if the argument is null. */ protected boolean isNumeric(Variable var) { return !var.getComponentType().equals(char.class); } /** * Indicates if a netCDF dimension represents longitude. This method uses * {@link #getRealType(Variable)} and {@link #isLongitude(RealType)}. * * @param var A netCDF dimension. * @return true if an only if dim * represents longitude. * @throws VisADException Couldn't create necessary VisAD object. */ protected boolean isLongitude(Variable var) throws VisADException { RealType type = getRealType(var); return type != null && type.equals(longitude); } /** * Indicates if a VisAD {@link RealType} represents longitude. * * @param type A VisAD {@link RealType}. May be * null. * @return true if an only if the VisAD * {@link RealType} represents longitude. */ protected boolean isLongitude(RealType type) { return type != null && type.equals(longitude); } /** * Indicates if a VisAD {@link RealType} represents latitude. * * @param type A VisAD {@link RealType}. May be * null. * @return true if an only if the VisAD * {@link RealType} represents latitude. */ protected boolean isLatitude(RealType type) { return type != null && type.equals(latitude); } /** *

Returns the VisAD {@link visad.MathType} of the domain corresponding to a * netCDF dimension.

* *

This implementation supports coordinate variables and uses {@link * #getCoordinateVariable(Dimension)} and {@link #getRealType(Variable)}. *

* * @param dim A netCDF dimension. * @return The VisAD MathType of the domain corresponding * to dim. Won't be null. * @throws TypeException if a corresponding {@link visad.RealType} needed * to be created but couldn't. */ protected RealType getRealType(Dimension dim) throws TypeException { RealType type; Variable var = getCoordinateVariable(dim); if (var != null) { type = getRealType(var); } else { synchronized(dimToRealType) { type = (RealType)dimToRealType.get(dim); if (type == null) { String name = dim.getName(); type = quantityDB.get(name); if (type == null) { type = RealType.getRealType(name); } if (type == null) { throw new TypeException( "Couldn't create RealType for " + dim.getName()); } dimToRealType.put(dim, type); } } } return type; } /** *

Returns the VisAD {@link RealType} of a netCDF variable.

* *

This implementation returns the value of {@link * #getRealTypeFromLongName(Variable)} if that is non-null; * otherwise, the value of {@link #getRealTypeFromName(Variable)} is * returned.

* * @param var The netCDF variable. * @return The corresponding VisAD RealType. * @throws NullPointerException if the argument is null. * @throws TypeException if a corresponding {@link RealType} needed * to be created but couldn't. */ protected RealType getRealType(Variable var) throws TypeException { if (var == null) throw new NullPointerException(); RealType type; /* * To improve performance, this method caches results. */ synchronized(varToRealType) { type = (RealType)varToRealType.get(var); if (type == null) { type = getRealTypeFromLongName(var); if (type == null) type = getRealTypeFromName(var); varToRealType.put(var, type); // cache result } } return type; } /** * Returns the VisAD RealType corresponding to the long_name * attribute of a netCDF variable. If the unit attribute of the variable * is incompatible with the unit of the long_name attribute, * then a new RealType is created whose default unit is that of the * attribute.

* *

This implementation first checks if the variable has a * long_name attribute via {@link #getLongName(Variable)}, * if it doesn't, then null is returned; otherwise, the * long name is used to query the quantity database. If the quantity * database doesn't contain a match, then null is returned; * otherwise, the variable's unit attribute -- obtained via {@link * #getUnitFromAttribute(Variable)} -- is checked. If the unit attribute * doesn't exist, then the {@link RealType} is returned; otherwise, the unit * attribute is compared against the default unit of the {@link RealType}. * If the two are convertible, then the {@link RealType} is returned; * otherwise, an attempt is made to create a new {@link RealType} with a * slightly different name than the variable's but with the same unit as the * variable's and that {@link RealType} is returned.

* * @param var The netCDF variable. * @return The corresponding VisAD RealType or * null if no corresponding type * was found or could be created. */ protected RealType getRealTypeFromLongName(Variable var) { RealType type; String name = getLongName(var); if (name == null) { type = null; } else { type = quantityDB.get(name); if (type != null) { Unit unit = getUnitFromAttribute(var); if (!Unit.canConvert(unit, type.getDefaultUnit())) { String newName = newName(var); System.err.println( "The unit attribute (" + unit + ") " + "of variable \"" + var.getName() + "\" " + "is incompatible with the unit (" + type.getDefaultUnit() + ") " + "of the quantity referenced by the long_name attribute " + "(" + name + "). " + "Attempting to create new quantity \"" + newName + "\"."); type = RealType.getRealType(newName, unit); } } } return type; } /** *

Returns the VisAD {@link RealType} corresponding to the name of a * netCDF variable. null is never returned.

* *

This implementation first obtains the variable's unit via {@link * #getUnitFromAttribute(Variable)}. It then queries the quantity database * for a match to the variable's name. If a match is found, then variable's * unit is checked. If the unit is null, then the {@link * RealType} from the database is returned; otherwise, the unit is checked * against the default unit of the obtained {@link RealType}. If the two * units are convertible, then the {@link RealType} is returned; otherwise, * a new {@link RealType} is created that has a slightly different name than * the variable's but with the variable's unit and that {@link RealType} * is returned. If the quantity database doesn't contain a match, then * the variable's unit is checked. If it's null, then * the value of {@link RealType#getRealType(String)} -- when given the * variable's name -- is returned; otherwise, the return value of {@link * RealType#getRealType(String, Unit)} -- when invoked with the variable's * name and unit -- is checked. If it's non-null, then that * {@link RealType} is returned; otherwise, a new {@link RealType} is * created that has a slightly different name than the variable's but with * the variable's unit and that {@link RealType} is returned.

* * @param var The netCDF variable. * @return The corresponding VisAD RealType. * @throws TypeException if a corresponding {@link RealType} needed * to be created but couldn't. */ protected RealType getRealTypeFromName(Variable var) throws TypeException { String name = var.getName(); Unit unit = getUnitFromAttribute(var); RealType type = quantityDB.get(name); if (type != null) { if (!Unit.canConvert(unit, type.getDefaultUnit())) type = newQuantity(var, unit, type.getDefaultUnit()); } else { if (unit == null) { type = RealType.getRealType(name); } else { type = RealType.getRealType(name, unit); if (type == null) { type = newQuantity( var, unit, RealType.getRealTypeByName(name).getDefaultUnit()); } } } return type; } private RealType newQuantity(Variable var, Unit wantUnit, Unit haveUnit) throws TypeException { String newName = newName(var); System.err.println( "The unit attribute (" + wantUnit + ") " + "of variable \"" + var.getName() + "\" " + "is incompatible with the unit (" + haveUnit + ") of the RealType of the same name. " + "Attempting to create new RealType \"" + newName + "\"."); RealType type = RealType.getRealType(newName, wantUnit); if (type == null) throw new TypeException(newName); return type; } /** * Returns a name for a given variable that is slightly different that the * variable's name and is guarenteed not to have been returned before. * * @param var The netCDF variable. * @return A new and unique name based on the * variable. */ protected String newName(Variable var) { return var.getName() + "_" + nameCount++; // getUnitFromAttribute(var).toString().replace(' ', '_') // .replace('.', '_'); } /** *

Gets the type of the values of a netCDF variable.

* *

This implementation returns the value of #getRealType(Variable)} * or {@link #getTextType(Variable)} -- depending on the value of {@link * #isNumeric(Variable)}.

* * @param var A netCDF variable. * @throws TypeException if a corresponding {@link RealType} needed * to be created but couldn't. * @throws VisADException if a VisAD object can't be created. */ protected ScalarType getScalarType(Variable var) throws TypeException, VisADException { return isNumeric(var) ? (ScalarType)getRealType(var) : (ScalarType)getTextType(var); } /** * Return the VisAD TextType of a netCDF variable. * * @param var The netCDF variable. * @return The VisAD TextType of var. * @throws VisADException if a VisAD object couldn't be created. * @throws IllegalArgumentException * if the netCDF variable is not textual. */ protected TextType getTextType(Variable var) throws VisADException { if (var == null) throw new NullPointerException(); if (isNumeric(var)) throw new IllegalArgumentException(var.toString()); TextType type; /* * To improve performance, this method caches results. */ synchronized(varToTextType) { type = (TextType)varToTextType.get(var); if (type == null) { type = TextType.getTextType(var.getName()); varToTextType.put(var, type); // cache result } } return type; } /** * Gets the representational set for the values of a netCDF variable. If * the variable isn't numeric, then null is returned. * *

This implementation uses {@link #getRealType(Variable)}, {@link * #getVetter(Variable)}, and {@link #getUnitFromAttribute(Variable)}.

* * @param var A netCDF variable. * @return The VisAD representational set for the values of * the variable or null. * @throws TypeException if a corresponding {@link RealType} needed * to be created but couldn't. * @throws VisADException Couldn't create necessary VisAD object. */ protected SimpleSet getRangeSet(Variable var) throws TypeException, VisADException { SimpleSet set; Class cl = var.getComponentType(); if (cl.equals(char.class)) { set = null; } else { RealType type = getRealType(var); if (cl.equals(byte.class)) { set = new Linear1DSet(type, Byte.MIN_VALUE+1, Byte.MAX_VALUE, Byte.MAX_VALUE - Byte.MIN_VALUE); } else if (cl.equals(short.class)) { set = new Linear1DSet(type, Short.MIN_VALUE+1, Short.MAX_VALUE, Short.MAX_VALUE - Short.MIN_VALUE); } else if (cl.equals(int.class)) { /* * The following is complicated due to the fact that the last * argument to the Linear1DSet() constructor: * * Linear1DSet(MathType type, double start, double stop, * int length) * * is an "int" -- and the number of Java "int" values cannot * be represented by a Java "int". */ Vetter vetter = getVetter(var); long minValid = (long)vetter.minValid(); long maxValid = (long)vetter.maxValid(); long length = maxValid - minValid + 1; set = length <= Integer.MAX_VALUE ? (SimpleSet)(new Linear1DSet(type, minValid, maxValid, (int)length)) : (SimpleSet)(new FloatSet(type, (CoordinateSystem)null, new Unit[] { getUnitFromAttribute(var)})); } else if (cl.equals(float.class)) { set = new FloatSet(type, (CoordinateSystem)null, new Unit[] {getUnitFromAttribute(var)}); } else { set = (SimpleSet)new DoubleSet(type, (CoordinateSystem)null, new Unit[] {getUnitFromAttribute(var)}); } } return set; } /** *

Returns a string-valued global attribute. If the attribute doesn't * exist or is invalid, then null is returned. If the * attribute exists but is not string-valued, then an error message is * printed to {@link System#err} and null is returned.

* *

This implementation uses {@link #getAttributeString(Variable, String)}. *

* * @param name The name of the attribute. * @return The string value of the attribute or * null. */ protected String getAttributeString(String name) { return getAttributeString((Variable)null, name); } /** * Returns a string-valued global attribute or a netCDF variable attribute. * If the attribute doesn't exist or is invalid, then null is * returned. If the attribute exists but is not string-valued, then an * error message is printed to {@link System#err} and null is * returned. * * @param var A netCDF variable or null to * indicate a global attribute. * @param name The name of the attribute. * @return The string value of the attribute or * null. */ protected String getAttributeString(Variable var, String name) { Attribute attr = var == null ? netcdf.getAttribute(name) : var.getAttribute(name); try { return attr != null ? attr.getStringValue() : null; } catch (ClassCastException e) { System.err.println( "Non-string attribute: " + var.getName() + ":" + name); return null; } } /** *

Returns the long name of a netCDF variable according to the variable's * long_name attribute. If the attribute doesn't exist, then * null is returned.

* *

This method uses {@link #getAttributeString(Variable, String)}.

* * @param var A netCDF variable. * @return The long name of var or * null. * @throws ClassCastException * if the attribute exists but its value isn't a * String. */ protected String getLongName(Variable var) { return getAttributeString(var, "long_name"); } /** *

Returns the string value of the unit attribute of a netCDF variable. * Returns null if the unit attribute is missing or * invalid.

* *

This method uses {@link #getAttributeString(Variable, String)} -- * first with the name "units" and then with the name "unit".

* * @param var A netCDF variable. * @return The unit of the values of var or * null. */ protected String getUnitString(Variable var) { String str = getAttributeString(var, "units"); if (str == null) str = getAttributeString(var, "unit"); return str; } /** *

Returns the unit of a netCDF variable according to the variable's unit * attribute. Returns null if the unit attribute is missing * or invalid. If a unit specification exists but can't be decoded, then * a warning message is printed to {@link System#err}.

* *

This method uses {@link #getUnitString(Variable)}.

* * @param var A netCDF variable. * @return The unit of the values of var or * null. */ protected Unit getUnitFromAttribute(Variable var) { Unit unit; /* * This method caches results to improve performance. */ synchronized(varToUnit) { /* * The following two lines exist because the unit map is a * WeakHashMap and may contain null values. */ unit = (Unit)varToUnit.get(var); if (!varToUnit.containsKey(var)) { String spec = getUnitString(var); if (spec != null) { try { unit = Parser.parse(spec); } catch (Exception e) { System.err.println( "Couldn't decode unit attribute (" + spec + ")" + " of variable \"" + var.getName() + "\": " + e.getMessage()); } } varToUnit.put(var, unit); // cache result } } return unit; } /** * Returns a value-vetter for a netCDF variable. * * @param var A netCDF variable. * @return A value-vetter for the variable. */ protected Vetter getVetter(Variable var) { return new Vetter(var); } /** *

Returns the VisAD {@link Gridded1DSet} corresponding to a netCDF * dimension.

* *

This implementation supports coordinate variables, longitude, * and the discovery of an arithmetic progression. It uses {@link * #isLongitude(Variable)}, {@link #getRealType(Dimension)}, and {@link * #getUnitFromAttribute(Variable)}.

* * @param dim A netCDF dimension. * @return The VisAD {@link visad.GriddedSet} corresponding to * the dimension. * @throws VisADException if a VisAD object couldn't be created. * @throws IOException if a netCDF read-error occurs. * @throws ClassCastException * if the dimension has a coordinate variable of * improper type. */ protected Gridded1DSet getDomainSet(Dimension dim) throws VisADException, IOException { /* * This implementation caches earlier results because this operation is * potentially expensive and may be invoked many times for any given * dimension. */ Gridded1DSet set = (Gridded1DSet)dimToSet.get(dim); if (set == null) { Variable coordVar = getCoordinateVariable(dim); if (coordVar == null) { // TODO: add CoordinateSystem argument set = new Integer1DSet(getRealType(dim), dim.getLength()); } else { ArithProg ap = isLongitude(coordVar) ? new visad.data.in.LonArithProg() : new visad.data.in.ArithProg(); Class varType = coordVar.getComponentType(); boolean isDouble = varType.equals(double.class); Object coordValues; if (isDouble) { coordValues = coordVar.toArray(); ap.accumulate((double[])coordValues); } else if (varType.equals(float.class)) { coordValues = coordVar.toArray(); ap.accumulate((float[])coordValues); } else { int length = 1; { int[] lengths = coordVar.getLengths(); for (int i = 0; i < lengths.length; i++) length *= lengths[i]; } float[] floatVals = new float[length]; if (varType.equals(int.class)) { int[] values = (int[])coordVar.toArray(); for (int i = 0; i < values.length; i++) floatVals[i] = values[i]; } else if (varType.equals(short.class)) { short[] values = (short[])coordVar.toArray(); for (int i = 0; i < values.length; i++) floatVals[i] = values[i]; } else { byte[] values = (byte[])coordVar.toArray(); for (int i = 0; i < values.length; i++) floatVals[i] = values[i]; } ap.accumulate(floatVals); coordValues = floatVals; } if (ap.isConsistent()) { /* * The coordinate-variable is an arithmetic progression. */ // TODO: add CoordinateSystem argument set = new Linear1DSet( getRealType(dim), ap.getFirst(), ap.getLast(), (int)ap.getNumber(), (CoordinateSystem)null, new Unit[] {getUnitFromAttribute(coordVar)}, (ErrorEstimate[])null); } else { /* * The coordinate-variable is not an arithmetic progression. */ // TODO: add CoordinateSystem argument set = isDouble ? (Gridded1DSet)new Gridded1DDoubleSet( getRealType(dim), new double[][] {(double[])coordValues}, dim.getLength(), (CoordinateSystem)null, new Unit[] {getUnitFromAttribute(coordVar)}, (ErrorEstimate[])null) : new Gridded1DSet( getRealType(dim), new float[][] {(float[])coordValues}, dim.getLength(), (CoordinateSystem)null, new Unit[] {getUnitFromAttribute(coordVar)}, (ErrorEstimate[])null); } } dimToSet.put(dim, set); } return set; } /** *

Returns the netCDF coordinate variable associated with a netCDF * dimension. If no such variable exists, then null is * returned.

* *

This implementation uses {@link #isNumeric(Variable)}.

* * @param dim A netCDF dimension. * @return The netCDF coordinate variable associated * with the dimension or null * if there is no coordinate variable. */ protected Variable getCoordinateVariable(Dimension dim) { Variable var = netcdf.get(dim.getName()); if (var != null && !(var.getRank() == 1 && isNumeric(var))) var = null; return var; } /**

Defines the names of domain components to factor out. * This only works if this names correspond to the outermost * dimension. The list of names may be changed after calling * this.

* *

The Set should contain only String(s).

* *

Typically, a TreeSet will be used. For example:

* TreeSet ts = new TreeSet(); * ts.add("myParameter"); * view.setOuterDimensionNameSet(ts); * * @param nameSet A Set containing the names (as Strings) of * the dimensions to factor out. * */ public void setOuterDimensionNameSet(java.util.Set nameSet) { factorNameSet = nameSet; } /** *

Returns the factorName object * * @return The Set of factorNames. */ public java.util.Set getOuterDimensionNameSet() { return factorNameSet; } /** *

Indicates if a netCDF dimension represents time.

* *

This implementation supports coordinate variables and uses {@link * #getRealType(Dimension)}.

* *

If setOuterDimensionNameSet() has been called, this list * of names will also logically be considered factorable.

* * @param dim A netCDF dimension. * @return true if and only if the dimension * represents time. * @throws VisADException Couldn't create necessary VisAD object. * @throws IOException I/O failure. */ protected boolean isTime(Dimension dim) throws VisADException, IOException { RealType rt = getRealType(dim); if (factorNameSet != null) { if (factorNameSet.contains(rt.getName()) ) { return true; } } return (isTime(rt.getDefaultUnit()) ); } /** * Indicates if a unit is a unit of time. * * @param unit A unit. * @return true if and only if the unit * is a unit of time. */ protected boolean isTime(Unit unit) { return unit != null && SI.second.isConvertible(unit.getAbsoluteUnit()); } /** * Returns the netCDF dimensions of a netCDF variable. * * @param var A netCDF variable. * @return The dimensions of var in * netCDF order. */ protected Dimension[] getDimensions(Variable var) { int rank = var.getRank(); Dimension[] dims = new Dimension[rank]; DimensionIterator iter = var.getDimensionIterator(); for (int i = 0; i < rank; ++i) dims[i] = iter.next(); return dims; } /** *

Indicates if a netCDF variable is a coordinate variable (i.e. has only * one netCDF dimension and that dimension has the same name).

* *

This implementation uses {@link #isNumeric(Variable)}.

* * @param var A netCDF variable. * @return true if and only if * var is a coordinate variable. */ protected boolean isCoordinateVariable(Variable var) { if (var.getRank() != 1 || !isNumeric(var)) return false; return getDimensions(var)[0].getName().equals(var.getName()); } /** * Returns an iterator over the virtual VisAD data objects of this view. * * @return An iterator over the virtual VisAD data objects * of the view. */ public VirtualDataIterator getVirtualDataIterator() { return new DataIterator(); } /** *

Indicates if a given variable should be ignored by the {@link * VirtualDataIterator} during iteration over the virtual VisAD data objects * in the netCDF dataset.

* * @return true if and only if the variable * should be ignored. */ protected abstract boolean isIgnorable(Variable var); /** * Returns the domain of a netCDF variable. * * @param var A netCDF variable. * @return The domain of the netCDF variable. * @throws IllegalArgumentException * if the rank of the variable is zero. * @throws TypeException if a {@link RealType} needed to be created but * couldn't. * @throws IOException if a netCDF read-error occurs. */ protected abstract Domain getDomain(Variable var) throws TypeException, IOException; /** *

Returns the virtual VisAD data object corresponding to a named netCDF * variable.

* *

This implementation uses {@link #getData(Variable)}.

* * @param name The name of the netCDF variable. * @return The corresponding virtual VisAD data object. * @throws NullPointerException if the argument is null. * @throws IllegalArgumentException * if the netCDF variable doesn't exist. * @throws TypeException if a {@link RealType} needed to be created * but couldn't. * @throws VisADException if a VisAD object couldn't be created. * @throws IOException if a netCDF read-error occurs. */ public VirtualData getData(String name) throws TypeException, VisADException, IOException { if (name == null) throw new NullPointerException(); return getData(netcdf.get(name)); } /** *

Returns the virtual VisAD data object corresponding to a netCDF * variable.

* *

This implementation uses {@link #getRealType(Variable)}, {@link * #getRangeSet(Variable)}, {@link #getUnitFromAttribute(Variable)}, {@link * #getVetter(Variable)}, and {@link #getDomain(Variable)}.

* * @param var The netCDF variable. * @return The corresponding virtual VisAD data object. * @throws NullPointerException if the argument is null. * @throws TypeException if a {@link RealType} needed to be created * but couldn't. * @throws VisADException if a VisAD object couldn't be created. * @throws IOException if a netCDF read-error occurs. */ protected VirtualData getData(Variable var) throws TypeException, VisADException, IOException { VirtualScalar scalar = (isNumeric(var) == true) ? (VirtualScalar) new VirtualReal(getRealType(var), var, getRangeSet(var), getUnitFromAttribute(var), getVetter(var)) : (VirtualScalar) new VirtualText(getTextType(var), var); return (var.getRank() == 0 || (!isNumeric(var) && var.getRank() == 1)) ? (VirtualData)scalar : getDomain(var).getVirtualField( new VirtualTuple(scalar)); } /** * Iterates over the virtual VisAD data objects of this view. */ protected class DataIterator extends VirtualDataIterator { /** * The netCDF variable iterator. */ private final VariableIterator varIter; /** * Constructs from nothing. */ DataIterator() { super(View.this); varIter = View.this.getNetcdf().iterator(); } /** *

Returns a copy of the next virtual VisAD data object.

* *

This implementation uses {@link #isCharToText()}, * {@link #isNumeric(Variable)}, {@link #isIgnorable(Variable)}, * and {@link #getData(Variable)}.

* * @return A copy of the next virtual VisAD data * object or null if there is * no more data. * @throws TypeException if a {@link ScalarType} needed * to be created but couldn't. * @throws VisADException Couldn't create necessary VisAD object. * @throws IOException if a netCDF read-error occurs. */ protected VirtualData getData() throws TypeException, VisADException, IOException { while (varIter.hasNext()) { Variable var = varIter.next(); // handle text only if charToText == true and rank <= 2 if (!isNumeric(var) && (!isCharToText() || var.getRank() > 2)) continue; // TODO: support arrays of text (Tuple?) if (isIgnorable(var)) continue; // ignore what's ignorable return View.this.getData(var); } return null; // no more data } } /** * The convention-dependent domain of a netCDF variable. */ protected abstract class Domain { /** * Constructs from a netCDF variable. * * @throws NullPointerException if the argument is * null. * @throws IllegalArgumentException if the rank of the variable is 0. */ protected Domain(Variable var) { if (var.getRank() == 0) throw new IllegalArgumentException(var.toString()); } /** * Returns a {@link VirtualField} corresponding to this domain and * a given range. * * @param range The range for the {@link VirtualField}. * @throws NullPointerException if the argument is null. * @throws IOException if a netCDF read-error occurs. * @throws VisADException if a VisAD object can't be created. */ protected abstract VirtualField getVirtualField(VirtualTuple range) throws VisADException, IOException; /** * Indicates if this instance equals an object. * * @param obj The object to be compared against. * @return true if and only if this * instance equals the object. */ public abstract boolean equals(Object obj); /** * Returns the hash code of this instance. * * @return The hash code of this instance. */ public abstract int hashCode(); } }

Ads help maintain this website.