All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.nc2.ft2.coverage.adapter.DtCoverage Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.nc2.ft2.coverage.adapter;

import ucar.ma2.*;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.constants.AxisType;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.dataset.VariableDS;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.util.Format;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * fork ucar.nc2.dt.grid.GeoGrid for adaption of GridCoverage.
 * Minimalist, does not do make a "logical GeoGrid subset"
 * LOOK maybe can only be used for GRID ?
 *
 * @author caron
 * @since 5/26/2015
 */
public class DtCoverage implements IsMissingEvaluator {
  static private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DtCoverage.class);
  static private final boolean debugArrayShape = false;

  private final DtCoverageDataset dataset;
  private final DtCoverageCS gcs;
  private final VariableDS vs;
  private int xDimOrgIndex = -1, yDimOrgIndex = -1, zDimOrgIndex = -1, tDimOrgIndex = -1, eDimOrgIndex = -1, rtDimOrgIndex = -1;
  private int xDimNewIndex = -1, yDimNewIndex = -1, zDimNewIndex = -1, tDimNewIndex = -1, eDimNewIndex = -1, rtDimNewIndex = -1;
  private final List mydims;

  /**
   * Constructor.
   *
   * @param dataset belongs to this dataset
   * @param dsvar   wraps this Variable
   * @param gcs     has this grid coordinate system
   */
  public DtCoverage(DtCoverageDataset dataset, DtCoverageCS gcs, VariableDS dsvar) {
    this.dataset = dataset;
    this.vs = dsvar;
    this.gcs = gcs;

    CoordinateAxis xaxis = gcs.getXHorizAxis();
    if (xaxis instanceof CoordinateAxis1D) {
      xDimOrgIndex = findDimension(xaxis.getDimension(0));
      yDimOrgIndex = findDimension(gcs.getYHorizAxis().getDimension(0));

    } else { // 2D case
      //coverity[COPY_PASTE_ERROR]
      yDimOrgIndex = findDimension(gcs.getXHorizAxis().getDimension(0));
      xDimOrgIndex = findDimension(gcs.getXHorizAxis().getDimension(1));
    }

    if (gcs.getVerticalAxis() != null) zDimOrgIndex = findDimension(gcs.getVerticalAxis().getDimension(0));
    if (gcs.getTimeAxis() != null) {
      if (gcs.getTimeAxis().getRank() == 1)
        tDimOrgIndex = findDimension(gcs.getTimeAxis().getDimension(0));
      else
        tDimOrgIndex = findDimension(gcs.getTimeAxis().getDimension(1));
      // deal with swath where time not independent dimension LOOK rewrite ??
      if ((tDimOrgIndex == yDimOrgIndex) || (tDimOrgIndex == xDimOrgIndex)) {
        tDimOrgIndex = -1;
      }
    }
    if (gcs.getEnsembleAxis() != null) eDimOrgIndex = findDimension(gcs.getEnsembleAxis().getDimension(0));
    if (gcs.getRunTimeAxis() != null) rtDimOrgIndex = findDimension(gcs.getRunTimeAxis().getDimension(0));

    // construct canonical dimension list
    int count = 0;
    this.mydims = new ArrayList<>();
    if ((rtDimOrgIndex >= 0) && (rtDimOrgIndex != tDimOrgIndex)) {
      mydims.add(dsvar.getDimension(rtDimOrgIndex));
      rtDimNewIndex = count++;
    }
    if (eDimOrgIndex >= 0) {
      mydims.add(dsvar.getDimension(eDimOrgIndex));
      eDimNewIndex = count++;
    }
    if (tDimOrgIndex >= 0) {
      Dimension tdim = dsvar.getDimension(tDimOrgIndex);
      mydims.add(tdim);
      tDimNewIndex = count++;
    }
    if (zDimOrgIndex >= 0) {
      mydims.add(dsvar.getDimension(zDimOrgIndex));
      zDimNewIndex = count++;
    }
    if (yDimOrgIndex >= 0) {
      mydims.add(dsvar.getDimension(yDimOrgIndex));
      yDimNewIndex = count++;
    }
    if (xDimOrgIndex >= 0) {
      mydims.add(dsvar.getDimension(xDimOrgIndex));
      xDimNewIndex = count;
    }
  }

  private int findDimension(Dimension want) {
    java.util.List dims = vs.getDimensions();
    for (int i = 0; i < dims.size(); i++) {
      Dimension d = (Dimension) dims.get(i);
      if (d.equals(want))
        return i;
    }
    return -1;
  }

  /**
   * Returns an ArrayList containing the dimensions used by this geoGrid.
   * The dimension are put into canonical order: (rt, e, t, z, y, x). Note that the z and t dimensions are optional.
   * If the Horizontal axes are 2D, the x and y dimensions are arbitrarily chosen to be
   * gcs.getXHorizAxis().getDimension(1), gcs.getXHorizAxis().getDimension(0), respectively.
   *
   * @return List with objects of type Dimension, in canonical order.
   */
  public java.util.List getDimensions() {
    return new ArrayList<>(mydims);
  }

  /**
   * get the ith dimension
   *
   * @param i : which dimension
   * @return ith Dimension
   */
  public Dimension getDimension(int i) {
    if ((i < 0) || (i >= mydims.size())) return null;
    return mydims.get(i);
  }

  /**
   * get the time Dimension, if it exists
   */
  public Dimension getTimeDimension() {
    return tDimNewIndex < 0 ? null : getDimension(tDimNewIndex);
  }

  /**
   * get the z Dimension, if it exists
   */
  public Dimension getZDimension() {
    return zDimNewIndex < 0 ? null : getDimension(zDimNewIndex);
  }

  /**
   * get the y Dimension, if it exists
   */
  public Dimension getYDimension() {
    return yDimNewIndex < 0 ? null : getDimension(yDimNewIndex);
  }

  /**
   * get the x Dimension, if it exists
   */
  public Dimension getXDimension() {
    return xDimNewIndex < 0 ? null : getDimension(xDimNewIndex);
  }

  /**
   * get the ensemble Dimension, if it exists
   */
  public Dimension getEnsembleDimension() {
    return eDimNewIndex < 0 ? null : getDimension(eDimNewIndex);
  }

  /**
   * get the run time Dimension, if it exists
   */
  public Dimension getRunTimeDimension() {
    return rtDimNewIndex < 0 ? null : getDimension(rtDimNewIndex);
  }

  /**
   * get the time Dimension index in the geogrid (canonical order), or -1 if none
   */
  public int getTimeDimensionIndex() {
    return tDimNewIndex;
  }

  /**
   * get the z Dimension index in the geogrid (canonical order), or -1 if none
   */
  public int getZDimensionIndex() {
    return zDimNewIndex;
  }

  /**
   * get the y Dimension index in the geogrid (canonical order)
   */
  public int getYDimensionIndex() {
    return yDimNewIndex;
  }

  /**
   * get the x Dimension index in the geogrid (canonical order)
   */
  public int getXDimensionIndex() {
    return xDimNewIndex;
  }

  /**
   * get the ensemble Dimension index in the geogrid (canonical order)
   */
  public int getEnsembleDimensionIndex() {
    return eDimNewIndex;
  }

  /**
   * get the runtime Dimension index in the geogrid (canonical order)
   */
  public int getRunTimeDimensionIndex() {
    return rtDimNewIndex;
  }

  /**
   * Convenience function; lookup Attribute by name.
   *
   * @param name the name of the attribute
   * @return the attribute, or null if not found
   */
  public Attribute findAttributeIgnoreCase(String name) {
    return vs.findAttributeIgnoreCase(name);
  }

  /**
   * Convenience function; lookup Attribute value by name. Must be String valued
   *
   * @param attName      name of the attribute
   * @param defaultValue if not found, use this as the default
   * @return Attribute string value, or default if not found.
   */
  public String findAttValueIgnoreCase(String attName, String defaultValue) {
    return dataset.getNetcdfDataset().findAttValueIgnoreCase(vs, attName, defaultValue);
  }

  // implementation of GeoGrid interface

  /**
   * get the rank
   */
  public int getRank() {
    return mydims.size();
  }

  /**
   * get the shape
   */
  public int[] getShape() {
    int[] shape = new int[mydims.size()];
    for (int i = 0; i < mydims.size(); i++) {
      Dimension d = mydims.get(i);
      shape[i] = d.getLength();
    }
    return shape;
  }

  /**
   * get the data type
   */
  public DataType getDataType() {
    return vs.getDataType();
  }

  public List getAttributes() {
    return vs.getAttributes();
  }

  public VariableDS getVariable() {
    return vs;
  }

  public String getFullName() {
    return vs.getFullName();
  }

  public String getName() {
    return vs.getFullName();
  }

  public String getShortName() {
    return vs.getShortName();
  }

  /**
   * get the GeoGridCoordSys for this GeoGrid.
   */
  public DtCoverageCS getCoordinateSystem() {
    return gcs;
  }

  /**
   * get the Projection.
   */
  public ProjectionImpl getProjection() {
    return gcs.getProjection();
  }

  /**
   * get the standardized description, or null if none.
   */
  public String getDescription() {
    return vs.getDescription();
  }

  /**
   * get the unit as a string
   */
  public String getUnitsString() {
    String units = vs.getUnitsString();
    return (units == null) ? "" : units;
  }


  //public ucar.unidata.geoloc.ProjectionImpl getProjection() { return gcs.getProjection(); }

  /**
   * true if there may be missing data, see VariableDS.hasMissing()
   */
  public boolean hasMissingData() {
    return vs.hasMissing();
  }

  /**
   * if val is missing data, see VariableDS.isMissingData()
   */
  public boolean isMissingData(double val) {
    return vs.isMissing(val);
  }

  public boolean hasMissing() {
    return vs.hasMissing();
  }

  public boolean isMissing(double val) {
    return vs.isMissing(val);
  }

  /**
   * Convert (in place) all values in the given array that are considered
   * as "missing" to Float.NaN, according to isMissingData(val).
   *
   * @param values input array
   * @return input array, with missing values converted to NaNs.
   */
  public float[] setMissingToNaN(float[] values) {
    if (!vs.hasMissing()) return values;
    final int length = values.length;
    for (int i = 0; i < length; i++) {
      double value = values[i];
      if (vs.isMissing(value))
        values[i] = Float.NaN;
    }
    return values;
  }

  /**
   * Get the minimum and the maximum data value of the previously read Array,
   * skipping missing values as defined by isMissingData(double val).
   *
   * @param a Array to get min/max values
   * @return both min and max value.
   */
  public MAMath.MinMax getMinMaxSkipMissingData(Array a) {
    return MAMath.getMinMaxSkipMissingData(a, this);
  }

  /**
   * This reads an arbitrary data slice, returning the data in
   * canonical order (t-z-y-x). If any dimension does not exist, ignore it.
   *
   * @param t if < 0, get all of time dim; if valid index, fix slice to that value.
   * @param z if < 0, get all of z dim; if valid index, fix slice to that value.
   * @param y if < 0, get all of y dim; if valid index, fix slice to that value.
   * @param x if < 0, get all of x dim; if valid index, fix slice to that value.
   * @return data[t, z, y, x], eliminating missing or fixed dimension.
   */
  public Array readDataSlice(int t, int z, int y, int x) throws java.io.IOException {
    return readDataSlice(0, 0, t, z, y, x);
  }

  /**
   * This reads an arbitrary data slice, returning the data in
   * canonical order (rt-e-t-z-y-x). If any dimension does not exist, ignore it.
   *
   * @param rt if < 0, get all of runtime dim; if valid index, fix slice to that value.
   * @param e  if < 0, get all of ensemble dim; if valid index, fix slice to that value.
   * @param t  if < 0, get all of time dim; if valid index, fix slice to that value.
   * @param z  if < 0, get all of z dim; if valid index, fix slice to that value.
   * @param y  if < 0, get all of y dim; if valid index, fix slice to that value.
   * @param x  if < 0, get all of x dim; if valid index, fix slice to that value.
   *
   * @return data[rt, e, t, z, y, x], eliminating missing or fixed dimension.
   */
  public Array readDataSlice(int rt, int e, int t, int z, int y, int x) throws java.io.IOException {

    int rank = vs.getRank();
    int[] start = new int[rank];
    int[] shape = new int[rank];
    for (int i = 0; i < rank; i++) {
      start[i] = 0;
      shape[i] = 1;
    }
    Dimension xdim = getXDimension();
    Dimension ydim = getYDimension();
    Dimension zdim = getZDimension();
    Dimension tdim = getTimeDimension();
    Dimension edim = getEnsembleDimension();
    Dimension rtdim = getRunTimeDimension();

    // construct the shape of the data volume to be read
    if (rtdim != null) {
      if ((rt >= 0) && (rt < rtdim.getLength()))
        start[rtDimOrgIndex] = rt;  // fix rt
      else {
        shape[rtDimOrgIndex] = rtdim.getLength(); // all of rt
      }
    }

    if (edim != null) {
      if ((e >= 0) && (e < edim.getLength()))
        start[eDimOrgIndex] = e;  // fix e
      else {
        shape[eDimOrgIndex] = edim.getLength(); // all of e
      }
    }

    if (tdim != null) {
      if ((t >= 0) && (t < tdim.getLength()))
        start[tDimOrgIndex] = t;  // fix t
      else {
        shape[tDimOrgIndex] = tdim.getLength(); // all of t
      }
    }

    if (zdim != null) {
      if ((z >= 0) && (z < zdim.getLength()))
        start[zDimOrgIndex] = z;  // fix z
      else {
        shape[zDimOrgIndex] = zdim.getLength(); // all of z
      }
    }

    if (ydim != null) {
      if ((y >= 0) && (y < ydim.getLength()))
        start[yDimOrgIndex] = y;  // fix y
      else {
        shape[yDimOrgIndex] = ydim.getLength(); // all of y
      }
    }

    if (xdim != null) {
      if ((x >= 0) && (x < xdim.getLength())) // all of x
        start[xDimOrgIndex] = x;  // fix x
      else {
        shape[xDimOrgIndex] = xdim.getLength(); // all of x
      }
    }

    if (debugArrayShape) {
      System.out.println("read shape from org variable = ");
      for (int i = 0; i < rank; i++)
        System.out.println("   start = " + start[i] + " shape = " + shape[i] + " name = " + vs.getDimension(i).getShortName());
    }

    // read it
    Array dataVolume;
    try {
      dataVolume = vs.read(start, shape);
    } catch (Exception ex) {
      log.error("GeoGrid.getdataSlice() on variable " + vs.getFullName() + " dataset " + dataset.getLocation(), ex);
      throw new java.io.IOException(ex);
    }

    // permute to canonical order if needed; order rt,e,t,z,y,x
    int[] permuteIndex = calcPermuteIndex();
    if (permuteIndex != null)
      dataVolume = dataVolume.permute(permuteIndex);

    // eliminate fixed dimensions, but not all dimensions of length 1.
    int count = 0;
    if (rtdim != null) {
      if (rt >= 0) dataVolume = dataVolume.reduce(count);
      else count++;
    }
    if (edim != null) {
      if (e >= 0) dataVolume = dataVolume.reduce(count);
      else count++;
    }
    if (tdim != null) {
      if (t >= 0) dataVolume = dataVolume.reduce(count);
      else count++;
    }
    if (zdim != null) {
      if (z >= 0) dataVolume = dataVolume.reduce(count);
      else count++;
    }
    if (ydim != null) {
      if (y >= 0) dataVolume = dataVolume.reduce(count);
      else count++;
    }
    if (xdim != null) {
      if (x >= 0) dataVolume = dataVolume.reduce(count);
    }

    return dataVolume;
  }

  /**
   * This reads an arbitrary data section, returning the data in
   * canonical order (rt-e-t-z-y-x). If any dimension does not exist, ignore it.
   *
   * @param subset - each Range must be named by the axisType that its used for. order not important
   *
   * @return data[rt, e, t, z, y, x], eliminating missing dimensions. length=1 not eliminated
   */
  public Array readDataSection(Section subset, boolean canonicalOrder) throws InvalidRangeException, IOException {

    // get the ranges list in the order of the variable; a null range means "all" to vs.read()
    Range[] varRange = new Range[getRank()];
    for (Range r : subset.getRanges()) {
      AxisType type = AxisType.valueOf(r.getName());
      switch (type) {
        case Lon:
        case GeoX:
          varRange[xDimOrgIndex] = r;
          break;
        case Lat:
        case GeoY:
          varRange[yDimOrgIndex] = r;
          break;
        case Height:
        case Pressure:
        case GeoZ:
          varRange[zDimOrgIndex] = r;
          break;
        case Time:
          varRange[tDimOrgIndex] = r;
          break;
        case RunTime:
          varRange[rtDimOrgIndex] = r;
          break;
        case Ensemble:
          varRange[eDimOrgIndex] = r;
          break;
      }
    }

    // read it
    Array dataVolume = vs.read(Arrays.asList(varRange));

    // permute to canonical order if needed; order rt,e,t,z,y,x
    if (canonicalOrder) {
      int[] permuteIndex = calcPermuteIndex();
      if (permuteIndex != null)
        dataVolume = dataVolume.permute(permuteIndex);
    }

    return dataVolume;
  }

  private int[] calcPermuteIndex() throws java.io.IOException {
    Dimension xdim = getXDimension();
    Dimension ydim = getYDimension();
    Dimension zdim = getZDimension();
    Dimension tdim = getTimeDimension();
    Dimension edim = getEnsembleDimension();
    Dimension rtdim = getRunTimeDimension();

    // LOOK: the real problem is the lack of named dimensions in the Array object
    // figure out correct permutation for canonical ordering for permute
    List oldDims = vs.getDimensions();
    int[] permuteIndex = new int[vs.getRank()];
    int count = 0;
    if (oldDims.contains(rtdim)) permuteIndex[count++] = oldDims.indexOf(rtdim);
    if (oldDims.contains(edim)) permuteIndex[count++] = oldDims.indexOf(edim);
    if (oldDims.contains(tdim)) permuteIndex[count++] = oldDims.indexOf(tdim);
    if (oldDims.contains(zdim)) permuteIndex[count++] = oldDims.indexOf(zdim);
    if (oldDims.contains(ydim)) permuteIndex[count++] = oldDims.indexOf(ydim);
    if (oldDims.contains(xdim)) permuteIndex[count] = oldDims.indexOf(xdim);

    if (debugArrayShape) {
      System.out.println("oldDims = ");
      for (Dimension oldDim : oldDims) System.out.println("   oldDim = " + oldDim.getShortName());
      System.out.println("permute dims = ");
      for (int aPermuteIndex : permuteIndex) System.out.println("   oldDim index = " + aPermuteIndex);
    }

    // check to see if we need to permute
    boolean needPermute = false;
    for (int i = 0; i < permuteIndex.length; i++) {
      if (i != permuteIndex[i]) needPermute = true;
    }

    return needPermute ? permuteIndex : null;
  }

  /////////////////////////////////////////////////////////////////////////////////

  /**
   * Instances which have same name and coordinate system are equal.
   */
  public boolean equals(Object oo) {
    if (this == oo) return true;
    if (!(oo instanceof DtCoverage))
      return false;

    DtCoverage d = (DtCoverage) oo;
    if (!getFullName().equals(d.getFullName())) return false;
    return getCoordinateSystem().equals(d.getCoordinateSystem());
  }

  /**
   * Override Object.hashCode() to be consistent with equals.
   */
  public int hashCode() {
    if (hashCode == 0) {
      int result = 17;
      // result = 37*result + dataset.getName().hashCode();
      result = 37 * result + getFullName().hashCode();
      result = 37 * result + getCoordinateSystem().hashCode();
      hashCode = result;
    }
    return hashCode;
  }

  private int hashCode = 0; // Bloch, item 8

  /**
   * string representation
   */
  public String toString() {
    return getFullName();
  }

  /**
   * nicely formatted information
   */
  public String getInfo() {
    StringBuilder buf = new StringBuilder(200);
    buf.setLength(0);
    buf.append(getFullName());
    Format.tab(buf, 30, true);
    buf.append(getUnitsString());
    Format.tab(buf, 60, true);
    buf.append(hasMissingData());
    Format.tab(buf, 66, true);
    buf.append(getDescription());
    return buf.toString();
  }

  public int compareTo(DtCoverage g) {
    return getFullName().compareTo(g.getFullName());
  }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy