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

ucar.nc2.ft2.coverage.adapter.DtCoverageAdapter 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.AttributeContainer;
import ucar.nc2.AttributeContainerHelper;
import ucar.nc2.Dimension;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.dataset.*;
import ucar.nc2.ft2.coverage.*;
import ucar.nc2.util.Misc;
import ucar.unidata.util.Parameter;

import java.io.IOException;
import java.util.*;

/**
 * Adapt a DtCoverageDataset to a ucar.nc2.ft2.coverage.CoverageCollection
 * This is how we read non-GRIB files into a Coverage, using the NetcdfDataset/CoordinateSystem.
 * Forked from ucar.nc2.dt.GeoGridDataset.
 *
 * @author caron
 * @since 5/1/2015
 */
public class DtCoverageAdapter implements CoverageReader, CoordAxisReader {

  public static FeatureDatasetCoverage factory(DtCoverageDataset proxy, Formatter errlog) {
    DtCoverageAdapter reader = new DtCoverageAdapter(proxy);

    AttributeContainerHelper atts = new AttributeContainerHelper(proxy.getName());
    atts.addAll(proxy.getGlobalAttributes());

    List pgrids = new ArrayList<>();
    for (DtCoverage dtGrid : proxy.getGrids())
      pgrids.add(makeCoverage(dtGrid, reader));

    Set transformNames = new HashSet<>(); // unique transforms
    List transforms = new ArrayList<>();
    for (DtCoverageDataset.Gridset gset : proxy.getGridsets()) {
      DtCoverageCS gcs = gset.getGeoCoordSystem();
      for (ucar.nc2.dataset.CoordinateTransform ct : gcs.getCoordTransforms())
        if (!transformNames.contains(ct.getName())) {
          transforms.add(makeTransform(ct));
          transformNames.add(ct.getName());
        }
    }

    List covAxes = new ArrayList<>();

    Map covAxesMap = new HashMap<>(); // unique axes (by name)
    for (DtCoverageDataset.Gridset gset : proxy.getGridsets()) {
      DtCoverageCS gcs = gset.getGeoCoordSystem();
      for (ucar.nc2.dataset.CoordinateAxis axis : gcs.getCoordAxes()) {
        if (null == covAxesMap.get(axis.getFullName())) {
          ucar.nc2.util.Optional opt = makeCoordAxis(proxy.getCoverageType(), axis, reader);
          if (!opt.isPresent()) {
            errlog.format("%s%n", opt.getErrorMessage());
            continue;
          }
          CoverageCoordAxis covAxis = opt.get();
          covAxes.add(covAxis);
          covAxesMap.put(covAxis.getName(), covAxis);
        }
      }
      // pcoordSys.add(makeCoordSys(gset.getGeoCoordSystem(), allAxisNameSet));
    }

    // add dimension axis: needed for swath, others?
    List tmpList = new ArrayList<>(covAxes);
    for (CoverageCoordAxis covAxis : tmpList) {
      if (covAxis.getDependsOn() == null) continue;
      for (String dep : covAxis.getDependsOnList()) {
        if (covAxesMap.get(dep) == null) {
          // assume its an "index" coord var from a dimension
          Dimension dim = proxy.findDimension( dep);
          CoverageCoordAxis dimAxis = makeCoordAxisFromDimension( dim);
          covAxes.add(dimAxis);
          covAxesMap.put(dimAxis.getName(), dimAxis);
        }
      }
    }

    // now make coordsys, so we can include dimension axes
    List pcoordSys = new ArrayList<>();
    for (DtCoverageDataset.Gridset gset : proxy.getGridsets()) {
      pcoordSys.add(makeCoordSys(gset.getGeoCoordSystem(), covAxesMap));
    }

    CoverageCollection cd = new CoverageCollection(proxy.getName(), proxy.getCoverageType(), atts,
            null, null, proxy.getCalendarDateRange(),
            pcoordSys, transforms, covAxes, pgrids, reader);

    return new FeatureDatasetCoverage(reader.getLocation(), reader, cd);
  }

  private static Coverage makeCoverage(DtCoverage dt, DtCoverageAdapter reader) {
    return new Coverage(dt.getName(), dt.getDataType(), dt.getAttributes(), dt.getCoordinateSystem().getName(), dt.getUnitsString(), dt.getDescription(), reader, dt);
  }

  private static CoverageCoordSys makeCoordSys(DtCoverageCS dt, Map covAxesMap) {
    List transformNames = new ArrayList<>();
    for (CoordinateTransform ct : dt.getCoordTransforms())
      transformNames.add(ct.getName());

    Set covAxes = new HashSet<>();
    for (CoordinateAxis axis : dt.getCoordAxes()) {
      CoverageCoordAxis covAxis = covAxesMap.get(axis.getFullName());
      if (covAxis == null) continue; // some may have been rejected
      covAxes.add(covAxis);
      for (String dep : covAxis.getDependsOnList()) { // add in dependencies
        CoverageCoordAxis depAxis = covAxesMap.get(dep);
        if (depAxis != null)
          covAxes.add(depAxis);
      }
    }

    // make list of names - sorted by axis type
    List covAxesList = new ArrayList<>(covAxes);
    Collections.sort(covAxesList);
    List covAxesNames = new ArrayList<>();
    for (CoverageCoordAxis axis : covAxesList) covAxesNames.add(axis.getName());

    return new CoverageCoordSys(dt.getName(), covAxesNames, transformNames, dt.getCoverageType());
  }

  private static CoverageTransform makeTransform(ucar.nc2.dataset.CoordinateTransform dt) {
    AttributeContainerHelper atts = new AttributeContainerHelper(dt.getName());
    for (Parameter p : dt.getParameters())
      atts.addAttribute(new Attribute(p));
    return new CoverageTransform(dt.getName(), atts, dt.getTransformType() == TransformType.Projection);
  }

  private static CoverageCoordAxis makeCoordAxisFromDimension(Dimension dim) {
    CoverageCoordAxisBuilder builder = new CoverageCoordAxisBuilder();
    builder.name = dim.getFullName();
    builder.dataType = DataType.INT;
    builder.axisType = AxisType.Dimension;
    builder.dependenceType = CoverageCoordAxis.DependenceType.dimension;
    builder.spacing = CoverageCoordAxis.Spacing.regularPoint;
    builder.ncoords = dim.getLength();
    builder.startValue = 0;
    builder.endValue = dim.getLength()-1;
    builder.resolution = 1;

    return new CoverageCoordAxis1D(builder);
  }

  private static ucar.nc2.util.Optional makeCoordAxis(FeatureType ftype, ucar.nc2.dataset.CoordinateAxis dtCoordAxis, DtCoverageAdapter reader) {
    String name = dtCoordAxis.getFullName();
    DataType dataType = dtCoordAxis.getDataType();
    AxisType axisType = dtCoordAxis.getAxisType();
    String units = dtCoordAxis.getUnitsString();
    String description = dtCoordAxis.getDescription();
    AttributeContainer atts = dtCoordAxis.getAttributeContainer();

    if (axisType == null)
      return ucar.nc2.util.Optional.empty("Coordinate " + name + " has no axisType");

    CoverageCoordAxis.DependenceType dependenceType;
    String dependsOn = null;
    if (dtCoordAxis.isIndependentCoordinate())
      dependenceType = CoverageCoordAxis.DependenceType.independent;

    else if (dtCoordAxis.isScalar())
      dependenceType = CoverageCoordAxis.DependenceType.scalar;

    else if (dtCoordAxis instanceof CoordinateAxis2D) {
      dependenceType = CoverageCoordAxis.DependenceType.twoD;
      Formatter f = new Formatter();
      for (Dimension d : dtCoordAxis.getDimensions())  // LOOK axes may not exist
        f.format("%s ", d.getFullName());
      dependsOn = f.toString().trim();

    } else {
      /* Dimension d0 = dtCoordAxis.getDimension(0);
      if (dtCoordAxis.getRank() == 1 && null == gcs.findCoordAxis(d0.getShortName())) { // use it as independent
        dependenceType = CoverageCoordAxis.DependenceType.independent;

      } else { */
      dependenceType = CoverageCoordAxis.DependenceType.dependent;
      Formatter f = new Formatter();
      for (Dimension d : dtCoordAxis.getDimensions()) // LOOK axes may not exist
        f.format("%s ", d.getFullName());
      dependsOn = f.toString().trim();
      // }
    }

    int ncoords = (int) dtCoordAxis.getSize();
    CoverageCoordAxis.Spacing spacing;
    double startValue = 0.0;
    double endValue = 0.0;
    double resolution = 0.0;
    double[] values;

    // 1D case
    if (dtCoordAxis instanceof CoordinateAxis1D) {
      CoordinateAxis1D axis1D = (CoordinateAxis1D) dtCoordAxis;
      // Fix discontinuities in longitude axis. These occur when the axis crosses the date line.
      axis1D.correctLongitudeWrap();

      startValue = axis1D.getCoordValue(0);
      endValue = axis1D.getCoordValue((int) dtCoordAxis.getSize() - 1);

      if (axis1D.isRegular() || axis1D.isScalar()) {
        spacing = CoverageCoordAxis.Spacing.regularPoint;
        values = null;
        resolution = (ncoords == 1) ? 0.0 : (endValue - startValue) / (ncoords - 1);

      } else if (!dtCoordAxis.isInterval()) {
        spacing = CoverageCoordAxis.Spacing.irregularPoint;
        values = axis1D.getCoordValues();
        resolution = (ncoords == 1) ? 0.0 : (endValue - startValue) / (ncoords - 1);

      } else if (dtCoordAxis.isContiguous()) {
        spacing = CoverageCoordAxis.Spacing.contiguousInterval;
        values = axis1D.getCoordEdges();
        resolution = (endValue - startValue) / ncoords;

      } else {
        spacing = CoverageCoordAxis.Spacing.discontiguousInterval;
        values = new double[2 * ncoords];
        double[] bounds1 = axis1D.getBound1();
        double[] bounds2 = axis1D.getBound2();
        int count = 0;
        for (int i = 0; i < ncoords; i++) {
          values[count++] = bounds1[i];
          values[count++] = bounds2[i];
        }
        resolution = (endValue - startValue) / ncoords;
      }

      CoverageCoordAxisBuilder builder = new CoverageCoordAxisBuilder();
      builder.name = name;
      builder.units = units;
      builder.description = description;
      builder.dataType = dataType;
      builder.axisType = axisType;
      builder.attributes = atts;
      builder.dependenceType = dependenceType;
      builder.setDependsOn(dependsOn);
      builder.spacing = spacing;
      builder.ncoords = ncoords;
      builder.startValue = startValue;
      builder.endValue = endValue;
      builder.resolution = resolution;
      builder.values = values;
      builder.reader = reader;
      builder.isSubset = false;

      if (axisType == AxisType.TimeOffset)
        return ucar.nc2.util.Optional.of(new TimeOffsetAxis(builder));
      else
        return ucar.nc2.util.Optional.of(new CoverageCoordAxis1D(builder));
    }

    // TwoD case
    if (!(dtCoordAxis instanceof CoordinateAxis2D))
      throw new IllegalStateException("Dont know what to do with axis " + dtCoordAxis);

    CoordinateAxis2D axis2D = (CoordinateAxis2D) dtCoordAxis;

    if (!dtCoordAxis.isInterval()) {
      spacing = CoverageCoordAxis.Spacing.irregularPoint;
      values = axis2D.getCoordValues();

    } else if (dtCoordAxis.isContiguous()) {
      spacing = CoverageCoordAxis.Spacing.contiguousInterval;
      ArrayDouble.D3 bounds = axis2D.getCoordBoundsArray();
      if (bounds == null) throw new IllegalStateException("No bounds array");
      int[] shape = bounds.getShape();
      int count = 0;
      values = new double[ncoords + 1];
      for (int i = 0; i < shape[0]; i++) {
        for (int j = 0; j < shape[1]; j++) {
          values[count++] = bounds.get(i, j, 0);
        }
      }
      values[count] = bounds.get(shape[0] - 1, shape[1] - 1, 1); // last edge

    } else {
      spacing = CoverageCoordAxis.Spacing.discontiguousInterval;
      ArrayDouble.D3 bounds = axis2D.getCoordBoundsArray();
      if (bounds == null) throw new IllegalStateException("No bounds array");
      int[] shape = bounds.getShape();
      int count = 0;
      values = new double[2 * ncoords];
      for (int i = 0; i < shape[0]; i++) {
        for (int j = 0; j < shape[1]; j++) {
          values[count++] = bounds.get(i, j, 0);
          values[count++] = bounds.get(i, j, 1);
        }
      }
    }

    CoverageCoordAxisBuilder builder = new CoverageCoordAxisBuilder();
    builder.name = name;
    builder.units = units;
    builder.description = description;
    builder.dataType = dataType;
    builder.axisType = axisType;
    builder.attributes = dtCoordAxis.getAttributeContainer();
    builder.dependenceType = dependenceType;
    builder.setDependsOn(dependsOn);
    builder.spacing = spacing;
    builder.ncoords = ncoords;
    builder.startValue = startValue;
    builder.endValue = endValue;
    builder.resolution = resolution;
    builder.values = values;
    builder.reader = reader;
    builder.isSubset = false;
    builder.shape = dtCoordAxis.getShape();

    // Fmrc Time
    if (axisType == AxisType.Time) {
      if (ftype == FeatureType.FMRC) {
        builder.setDependsOn(dtCoordAxis.getDimension(0).getFullName());  // only the first dimension
        return ucar.nc2.util.Optional.of(new TimeAxis2DFmrc(builder));

      } else if (ftype == FeatureType.SWATH) {
        return ucar.nc2.util.Optional.of(new TimeAxis2DSwath(builder));
      }
    }

    // 2D Lat Lon
    if (axisType == AxisType.Lat || axisType == AxisType.Lon) {
      return ucar.nc2.util.Optional.of(new LatLonAxis2D(builder));
    }

    return ucar.nc2.util.Optional.empty("Dont know what to do with axis " + dtCoordAxis);
  }

  ////////////////////////
  private final DtCoverageDataset proxy;

  private DtCoverageAdapter(DtCoverageDataset proxy) {
    this.proxy = proxy;
  }


  //////////////////////////////////////////////////////////////////////////////////////
  // CoverageReader

  @Override
  public void close() throws IOException {
    proxy.close();
  }

  @Override
  public String getLocation() {
    return proxy.getLocation();
  }

  @Override
  public GeoReferencedArray readData(Coverage coverage, SubsetParams params, boolean canonicalOrder) throws IOException, InvalidRangeException {
    DtCoverage grid = (DtCoverage) coverage.getUserObject();
    CoverageCoordSys orgCoordSys = coverage.getCoordSys();
    ucar.nc2.util.Optional opt = orgCoordSys.subset(params);
    if (!opt.isPresent())
      throw new InvalidRangeException(opt.getErrorMessage());

    CoverageCoordSys subsetCoordSys = opt.get();
    List rangeIters = subsetCoordSys.getRanges();
    List ranges = new ArrayList<>();

    boolean hasComposite = false;
    for (RangeIterator ri : rangeIters) {
      if (ri instanceof RangeComposite) hasComposite = true;
      else ranges.add((Range) ri);
    }

    if (!hasComposite) {
      Array data = grid.readDataSection(new Section(ranges), canonicalOrder);
      return new GeoReferencedArray(coverage.getName(), coverage.getDataType(), data, subsetCoordSys);
    }

    // Could use an Array composite here, if we had one
    Array result = Array.factory(coverage.getDataType(), subsetCoordSys.getShape());
    System.out.printf(" read %s result shape=%s%n", coverage.getName(), Misc.showInts(result.getShape()));
    int[] origin = new int[result.getRank()]; // all zeroes

    ranges.add(null); // make room for last
    int n = rangeIters.size();
    RangeComposite comp = (RangeComposite) rangeIters.get(n - 1);
    for (RangeIterator ri : comp.getRanges()) {
      // read the data
      ranges.set(n - 1, (Range) ri.setName(comp.getName())); // add last
      Section want = new Section(ranges);
      System.out.printf("  composite read section=%s%n", want);
      Array data = grid.readDataSection(want, canonicalOrder);

      //where does it go?
      Section dataSection = new Section(data.getShape());
      Section sectionInResult = dataSection.shiftOrigin(origin);
      System.out.printf("  sectionInResult=%s%n", sectionInResult);

      // copy it there
      IndexIterator resultIter = result.getRangeIterator(sectionInResult.getRanges());
      IndexIterator dataIter = data.getIndexIterator();
      while (dataIter.hasNext())
        resultIter.setDoubleNext(dataIter.getDoubleNext()); // look converting to double

      // get ready for next
      origin[n - 1] += data.getShape()[n - 1]; // look assumes compose only in last dimension; could be generalized
    }

    return new GeoReferencedArray(coverage.getName(), coverage.getDataType(), result, subsetCoordSys);
  }


  //////////////////////////////////////////////////////////////////////////////////////
  // CoordAxisReader

  /*
 * regular: regularly spaced points or intervals (start, end, npts), edges halfway between coords
 * irregularPoint: irregular spaced points (values, npts), edges halfway between coords
 * contiguousInterval: irregular contiguous spaced intervals (values, npts), values are the edges, and there are npts+1, coord halfway between edges
 * discontinuousInterval: irregular discontiguous spaced intervals (values, npts), values are the edges, and there are 2*npts
 */

  /*
    The coordAxis describes the subset wanted.
    LOOK this just reads the entire set of values from the original...
   */
  @Override
  public double[] readCoordValues(CoverageCoordAxis coordAxis) throws IOException {
    ucar.nc2.dataset.CoordinateAxis dtCoordAxis = proxy.getNetcdfDataset().findCoordinateAxis(coordAxis.getName());
    if (dtCoordAxis == null) throw new IllegalStateException("Cants find Coordinate Axis " + coordAxis.getName());

    if (dtCoordAxis instanceof CoordinateAxis1D) {

      CoordinateAxis1D axis1D = (CoordinateAxis1D) dtCoordAxis;

      switch (coordAxis.getSpacing()) {
        case irregularPoint:
          return axis1D.getCoordValues();

        case contiguousInterval:
          return axis1D.getCoordEdges();

        case discontiguousInterval:
          int n = (int) dtCoordAxis.getSize();
          double[] result = new double[2 * n];
          double[] bounds1 = axis1D.getBound1();
          double[] bounds2 = axis1D.getBound2();
          int count = 0;
          for (int i = 0; i < n; i++) {
            result[count++] = bounds1[i];
            result[count++] = bounds2[i];
          }
          return result;
      }
    }

    // twoD case i guess
    Array data = dtCoordAxis.read();

    double[] result = new double[(int) data.getSize()];
    int count = 0;
    while (data.hasNext())
      result[count++] = data.nextDouble();

    return result;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy