/*
 * Copyright 1998, University Corporation for Atmospheric Research
 * All Rights Reserved.
 * See file LICENSE for copying and redistribution conditions.
 *
 * $Id: DefaultView.java,v 1.9 2004-11-19 23:31:08 donm Exp $
 */

package visad.data.netcdf.in;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import ucar.netcdf.Dimension;
import ucar.netcdf.Netcdf;
import ucar.netcdf.Variable;
import ucar.netcdf.VariableIterator;
import visad.data.netcdf.QuantityDB;
import visad.CoordinateSystem;
import visad.ErrorEstimate;
import visad.Gridded1DSet;
import visad.GriddedSet;
import visad.Integer1DSet;
import visad.IntegerNDSet;
import visad.Linear1DSet;
import visad.LinearLatLonSet;
import visad.LinearNDSet;
import visad.LinearSet;
import visad.MathType;
import visad.RealTupleType;
import visad.RealType;
import visad.SampledSet;
import visad.SetType;
import visad.TextType;
import visad.TypeException;
import visad.Unit;
import visad.VisADException;

/**
 * Provides support for the default view of a netCDF dataset as documented
 * in the netCDF User's Guide.
 *
 * @author Steven R. Emmerson
 * @version $Revision: 1.9 $ $Date: 2004-11-19 23:31:08 $
 */
public class DefaultView
    extends     View
{
    private final DimsToSet dimsToSet;

    /**
     * Constructs from a netCDF dataset and a quantity database.
     *
     * @param netcdf            The netCDF dataset.
     * @param quantityDB        The quantity database to use to map netCDF
     *                          variables to VisAD Quantity-s.
     */
    public DefaultView(Netcdf netcdf, QuantityDB quantityDB)
    {
        this(netcdf, quantityDB, false);
    }

    /**
     * Constructs from a netCDF dataset and a quantity database.
     *
     * @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
     */
    public DefaultView(Netcdf netcdf, QuantityDB quantityDB, boolean charToText)
    {
        super(netcdf, quantityDB, charToText);
        dimsToSet = new DimsToSet();
    }

    /**
     * 

Indicates if a given variable should be ignored during iteration.

* *

This implementation returns the value of {@link * #isCoordinateVariable(Variable)}.

* * @return true if and only if the variable * should be ignored. */ protected boolean isIgnorable(Variable var) { return isCoordinateVariable(var); } /** * Returns the domain of a netCDF variable. * * @param var A netCDF variable. * @return The domain of the given 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 Domain getDomain(Variable var) throws TypeException, IOException { return new DefaultDomain(var); } /** *

Returns the VisAD domain set corresponding to an array of netCDF * dimension in netCDF order (outermost dimension first).

* *

This implementation supports 1-dimensional coordinate variables, * 1-dimensional longitude, and 2-dimensional latitude/longitude domains * and assumes that each netCDF dimension is independent of all others. * This implementation uses {@link #getDomainSet(Dimension)}, {@link * #getDomainSet(Dimension)}, and {@link #getDomainType(Dimension[])}.

* * @param dims A netCDF domain. Must be in netCDF order * (outer dimension first). * @return The VisAD domain set corresponding to * dims. * @throws VisADException Couldn't create necessary VisAD object. * @exception IOException if a netCDF read-error occurs. */ protected SampledSet getDomainSet(Dimension[] dims) throws IOException, VisADException { if (dims.length == 0) return getDomainSet(dims[0]); /* * This implementation caches earlier results because this operation * is potentially expensive and multiple netCDF variables can have the * same domain. */ GriddedSet set; synchronized(dimsToSet) { set = dimsToSet.get(dims); if (set == null) { Gridded1DSet[] sets = new Gridded1DSet[dims.length]; int j = dims.length; for (int i = 0; i < dims.length; ++i) sets[--j] = getDomainSet(dims[i]); // reverse order boolean allInteger1DSets = true; for (int i = 0; allInteger1DSets && i < sets.length; ++i) allInteger1DSets = sets[i] instanceof Integer1DSet; MathType type = getDomainType(dims); if (allInteger1DSets) { set = (GriddedSet)getIntegerSet(sets, type); } else { boolean allLinear1DSets = true; for (int i = 0; allLinear1DSets && i < sets.length; ++i) allLinear1DSets = sets[i] instanceof Linear1DSet; if (allLinear1DSets) { set = (GriddedSet)getLinearSet(sets, type); } else { set = getGriddedSet(sets, type); } } dimsToSet.put(dims, set); } } return set; } /** * Returns an {@link IntegerSet} corresponding to the product of one or more * {@link Integer1DSet}s. * * @param sets The {@link Integer1DSet}s to be multiplied * together. * @param type The {@link MathType} for the result. * @return The {@link IntegerSet} corresponding to the * input. * @throws VisADException if a VisAD object couldn't be created. */ private static GriddedSet getIntegerSet(Gridded1DSet[] sets, MathType type) throws VisADException { int rank = sets.length; int[] lengths = new int[rank]; for (int idim = 0; idim < rank; ++idim) lengths[idim] = ((Integer1DSet)sets[idim]).getLength(0); // TODO: add CoordinateSystem argument return IntegerNDSet.create(type, lengths, (CoordinateSystem)null, (Unit[])null, (ErrorEstimate[])null); } /** *

Returns a {@link LinearSet} corresponding to the product of one or * more {@link Linear1DSet}s.

* *

This implementation uses {@link #isLongitude(RealType)} and {@link * #isLatitude(RealType)}. * * @param sets The {@link Linear1DSet}s to be multiplied * together. * @param type The {@link MathType} for the result. * NB: The units in the sets needn't be the * same as the units in type. * @return The {@link LinearSet} corresponding to the * input. * @throws VisADException if a VisAD object couldn't be created. */ private LinearSet getLinearSet(Gridded1DSet[] sets, MathType type) throws VisADException { LinearSet set = null; int rank = sets.length; double[] firsts = new double[rank]; double[] lasts = new double[rank]; int[] lengths = new int[rank]; Unit[] units = new Unit[rank]; for (int idim = 0; idim < rank; ++idim) { Linear1DSet linear1DSet = (Linear1DSet)sets[idim]; firsts[idim] = linear1DSet.getFirst(); lengths[idim] = linear1DSet.getLength(0); lasts[idim] = linear1DSet.getLast(); units[idim] = linear1DSet.getSetUnits()[0]; } // TODO: add CoordinateSystem argument if (rank == 2) { RealType[] types = ((RealTupleType)type).getRealComponents(); if ((isLongitude(types[0]) && isLatitude(types[1])) || (isLongitude(types[1]) && isLatitude(types[0]))) { set = new LinearLatLonSet(type, firsts[0], lasts[0], lengths[0], firsts[1], lasts[1], lengths[1], (CoordinateSystem)null, units, (ErrorEstimate[])null); } } if (set == null) { set = LinearNDSet.create(type, firsts, lasts, lengths, (CoordinateSystem)null, units, (ErrorEstimate[])null); } return set; } /** * Returns a {@link GriddedSet} corresponding to one or more {@link * Gridded1DSet}s. * * @param sets The {@link Gridded1DSet}s to be multiplied * together. * @param type The {@link MathType} for the result. * NB: The units in the sets needn't be the * same as the units in type. * @return The {@link GriddedSet} corresponding to the * input. * @throws VisADException if a VisAD object couldn't be created. * @throws IOException if a netCDF read-error occurs. */ private static GriddedSet getGriddedSet(Gridded1DSet[] sets, MathType type) throws VisADException, IOException { int rank = sets.length; // Handle the case where we only have one set. Save us some work // and keep the integrity of a Gridded1DDoubleSet for Time if (rank == 1 && sets[0].getType().equals(new SetType(type))) { return sets[0]; } int[] lengths = new int[rank]; float[][] values = new float[rank][]; int ntotal = 1; for (int idim = 0; idim < rank; ++idim) { lengths[idim] = sets[idim].getLength(0); ntotal *= lengths[idim]; } int step = 1; int laststep = 1; for (int idim = 0; idim < rank; ++idim) { float[] vals = sets[idim].getSamples(false)[0]; values[idim] = new float[ntotal]; step *= lengths[idim]; for (int i=0; iReturns the VisAD {@link MathType} corresponding to an array of netCDF * dimensions. If the array has zero length, then null is * returned.

* *

This implementation uses {@link #getRealType(Dimension)}.

* * @param dims netCDF dimensions in netCDF order (outermost * dimension first). * @return The type of the domain corresponding to * dims. RETURN_VALUE is * null, a RealType, * or a RealTupleType if * dims.length is 0, 1, or greater * than 1, respectively. * @throws VisADException Couldn't create necessary VisAD object. */ protected MathType getDomainType(Dimension[] dims) throws VisADException { MathType type; int rank = dims.length; if (rank == 0) { type = null; // means scalar domain } else if ( rank == 1) { type = (MathType)getRealType(dims[0]); } else { RealType[] types = new RealType[dims.length]; int j = dims.length; for (int i = 0; i < dims.length; ++i) types[--j] = getRealType(dims[i]); // reverse order type = new RealTupleType(types); } return type; } /** * Iterates over the virtual VisAD data objects in a netCDF dataset. */ final class DefaultDataIterator extends VirtualDataIterator { /** * The netCDF variable iterator. */ private final VariableIterator varIter; /** * Constructs from nothing. */ DefaultDataIterator() { super(DefaultView.this); varIter = DefaultView.this.getNetcdf().iterator(); } /** * Returns a clone of the next virtual VisAD data object. * *

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

* * @return A clone 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. */ 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 ignorable variables 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)); } return null; // no more data } } /** * The default domain of a netCDF variable. A default domain comprises * the variable's netCDF dimensions. */ private final class DefaultDomain extends Domain { /** * Outermost dimension first; at least one element. */ private final Dimension[] dims; private volatile int hashCode; private volatile SampledSet domainSet; /** * @throws IllegalArgumentException if the rank of the variable is 0. */ DefaultDomain(Variable var) throws TypeException { super(var); dims = getDimensions(var); } /** * 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 read error occurs. * @throws VisADException if a VisAD object can't be created. */ protected VirtualField getVirtualField(VirtualTuple range) throws VisADException, IOException { VirtualField field; int rank = dims.length; if (rank == 2 && range.getType() instanceof TextType) { // char field = VirtualField.newVirtualField( DefaultView.this.getDomainSet(dims[0]), range); } else if (rank == 1 || !DefaultView.this.isTime(dims[0])) { field = VirtualField.newVirtualField( DefaultView.this.getDomainSet(dims), range); } else { Dimension[] innerDims = new Dimension[rank-1]; System.arraycopy(dims, 1, innerDims, 0, innerDims.length); field = VirtualField.newVirtualField( DefaultView.this.getDomainSet(dims[0]), new VirtualTuple( VirtualField.newVirtualField( DefaultView.this.getDomainSet(innerDims), range))); } return field; } public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof DefaultDomain)) return false; return Arrays.equals(dims, ((DefaultDomain)obj).dims); } public int hashCode() { int hash = hashCode; if (hash == 0) { hash = 1; for (int i = 0; i < dims.length; i++) hash = hash*31 + dims[i].hashCode(); hashCode = hash; } return hash; } } /** * Cache of netCDF dimensions and their corresponding VisAD domain sets. */ private static class DimsToSet { private Map map; DimsToSet() { map = Collections.synchronizedMap(new WeakHashMap()); } void put(Dimension[] dims, GriddedSet set) { if (dims.length == 1) map.put(dims[0], set); else map.put(new DimArray(dims), set); } GriddedSet get(Dimension[] dims) { return dims.length == 1 ? (GriddedSet)map.get(dims[0]) : (GriddedSet)map.get(new DimArray(dims)); } private static class DimArray { private Dimension[] dims; private volatile int hashCode; DimArray(Dimension[] dims) { this.dims = (Dimension[])dims.clone(); // defensive copy } public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof DimArray)) return false; return Arrays.equals(dims, ((DimArray)obj).dims); } public int hashCode() { int hash = hashCode; if (hash == 0) { hash = 1; for (int i = 0; i < dims.length; i++) hash = hash*31 + dims[i].hashCode(); hashCode = hash; } return hash; } } } }

Ads help maintain this website.