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

ucar.nc2.ft2.coverage.CoverageCoordSys 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;

import ucar.ma2.RangeIterator;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.util.*;
import ucar.nc2.util.Optional;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.projection.LatLonProjection;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * A Coverage Coordinate System
 * Immutable after setImmutable is called
 *
 * @author caron
 * @since 7/11/2015
 */
public class CoverageCoordSys {

  public static String makeCoordSysName(List axisNames) {
    StringBuilder sb = new StringBuilder();
    for (String axisName : axisNames) {
      sb.append(axisName).append(' ');
    }
    return sb.deleteCharAt(sb.length() - 1).toString();  // Nuke trailing space.
  }

  //////////////////////////////////////////////////
  protected CoordSysContainer dataset;  // almost immutable, need to wire these in after the constructor
  private HorizCoordSys horizCoordSys;  // required
  private Time2DCoordSys time2DCoordSys;// optional
  private Map> dependentMap;
  private boolean isConstantForecast, immutable;

  private final String name;
  private final List axisNames;        // must be in order
  private final List transformNames;
  private final FeatureType type;

  public CoverageCoordSys(String name, List axisNames, List transformNames, FeatureType type) {
    this.name = name != null ? name : makeCoordSysName(axisNames);
    this.axisNames = axisNames;
    this.transformNames = transformNames;
    this.type = type;
  }

  // copy constructor
  public CoverageCoordSys(CoverageCoordSys from) {
    this.name = from.getName();
    this.axisNames = from.getAxisNames();
    this.transformNames = from.getTransformNames();
    this.type = from.getCoverageType();
  }

  public boolean isConstantForecast() {
    return isConstantForecast;
  }

  void setIsConstantForecast(boolean isConstantForecast) {
    if (immutable)
      throw new RuntimeException("Cant change CoverageCoordSys dataset once set immutable");
    this.isConstantForecast = isConstantForecast;
  }

  void setImmutable() {
    this.immutable = true;
  }

  public void setDataset(CoordSysContainer dataset) {
    if (immutable)
      throw new RuntimeException("Cant change CoverageCoordSys dataset once set immutable");
    this.dataset = dataset;

    // find dependent axes
    dependentMap = new HashMap<>();
    for (CoverageCoordAxis axis : getAxes()) {
      if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.dependent) {
        for (String indAxisName : axis.dependsOn) {
          CoverageCoordAxis independentAxis = dataset.findCoordAxis(indAxisName);
          if (independentAxis == null)
            throw new RuntimeException("Dependent axis " + axis.getName() + " depends on " + indAxisName + " not in Dataset");
          if (!axisNames.contains(indAxisName))
            throw new RuntimeException("Dependent axis " + axis.getName() + " depends on " + indAxisName + " not in CoordSys");

          List dependents = dependentMap.get(indAxisName);
          if (dependents == null) {
            dependents = new ArrayList<>();
            dependentMap.put(indAxisName, dependents);
          }
          dependents.add(axis);
        }
      }
    }

    // see if we need a Time2DCoordSys
    TimeAxis2DFmrc time2DAxis = null;
    TimeOffsetAxis timeOffsetAxis = null;
    CoverageCoordAxis1D runtimeAxis = null;

    for (CoverageCoordAxis axis : getAxes()) {
      if (axis.getAxisType() == AxisType.TimeOffset) {
        if (timeOffsetAxis != null)
          throw new RuntimeException("Cant have multiple TimeOffset Axes in a CoverageCoordSys");
        if (axis instanceof TimeOffsetAxis)
          timeOffsetAxis = (TimeOffsetAxis) axis;
      }
      if (axis.getAxisType() == AxisType.RunTime) {
        if (runtimeAxis != null)
          throw new RuntimeException("Cant have multiple RunTime axes in a CoverageCoordSys");
        runtimeAxis = (CoverageCoordAxis1D) axis;
      }
      if (axis instanceof TimeAxis2DFmrc) {
        if (time2DAxis != null)
          throw new RuntimeException("Cant have multiple TimeAxis2D axes in a CoverageCoordSys");
        time2DAxis = (TimeAxis2DFmrc) axis;
      }
    }

    // LOOK would be better maybe to share time2DCoordSys across CoordSys
    if (timeOffsetAxis != null) {
      if (runtimeAxis == null)
        throw new RuntimeException("TimeOffset Axis must have a RunTime axis in a CoverageCoordSys");
      if (immutable && this.time2DCoordSys != null)
        throw new RuntimeException("Cant have multiple Time2DCoordSys in a CoverageCoordSys");
      time2DCoordSys = new Time2DOffsetCoordSys(runtimeAxis, timeOffsetAxis);
    }

    if (time2DAxis != null) {
      if (runtimeAxis == null)
        throw new RuntimeException("TimeAxis2D Axis must have a RunTime axis in a CoverageCoordSys");
      if (immutable && this.time2DCoordSys != null)
        throw new RuntimeException("Cant have multiple Time2DCoordSys in a CoverageCoordSys");
      time2DCoordSys = new Time2DCoordSys(runtimeAxis, time2DAxis);
    }
  }

  public void setHorizCoordSys(HorizCoordSys horizCoordSys) {
    if (immutable) throw new RuntimeException("Cant change CoverageCoordSys horizCoordSys once set immutable");
    this.horizCoordSys = horizCoordSys;
  }

  public HorizCoordSys makeHorizCoordSys() {
    CoverageCoordAxis xaxis = getAxis(AxisType.GeoX);
    CoverageCoordAxis yaxis = getAxis(AxisType.GeoY);
    CoverageCoordAxis lataxis = getAxis(AxisType.Lat);
    CoverageCoordAxis lonaxis = getAxis(AxisType.Lon);

    CoverageTransform hct = getHorizTransform();
    return HorizCoordSys.factory((CoverageCoordAxis1D) xaxis, (CoverageCoordAxis1D) yaxis, lataxis, lonaxis, hct);
  }

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

  public String getName() {
    return name;
  }

  public List getTransformNames() {
    return (transformNames != null) ? transformNames : new ArrayList<>();
  }

  public List getTransforms() {
    List result = new ArrayList<>();
    for (String name : getTransformNames()) {
      result.add(dataset.findCoordTransform(name));
    }
    return result;
  }

  public CoverageTransform getHorizTransform() {
    for (String name : getTransformNames()) {
      CoverageTransform ct = dataset.findCoordTransform(name);
      if (ct != null && ct.isHoriz()) return ct;
    }
    return null;
  }

  public HorizCoordSys getHorizCoordSys() {
    return horizCoordSys;
  }

  public FeatureType getCoverageType() {
    return type;
  }

  public List getAxisNames() {
    return axisNames;
  }

  @Override
  public String toString() {
    Formatter f = new Formatter();
    Indent indent = new Indent(2);
    toString(f, indent);
    return f.toString();
  }

  public void toString(Formatter f, Indent indent) {
    indent.incr();
    f.format("%s CoordSys '%s' type %s", indent, name, type);
    f.format(" has coordVars:");
    for (String v : axisNames)
      f.format("%s, ", v);
    f.format(" (shape=[%s])", Misc.showInts(getShape()));
    if (transformNames != null && !transformNames.isEmpty()) {
      f.format("; has transforms:");
      for (String t : transformNames)
        f.format("%s, ", t);
    }
    indent.decr();
  }

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

  public CoverageCoordAxis getXAxis() {
    CoverageCoordAxis result = getAxis(AxisType.GeoX);
    if (result == null)
      result = getAxis(AxisType.Lon);
    if (result == null)
      throw new IllegalArgumentException("Cant find X axis for coordsys " + getName());
    return result;
  }

  public CoverageCoordAxis getYAxis() {
    CoverageCoordAxis result = getAxis(AxisType.GeoY);
    if (result == null)
      result = getAxis(AxisType.Lat);
    if (result == null)
      throw new IllegalArgumentException("Cant find Y axis for coordsys " + getName());
    return result;
  }

  public CoverageCoordAxis getZAxis() {
    for (String axisName : getAxisNames()) {
      CoverageCoordAxis axis = dataset.findCoordAxis(axisName);
      if (axis == null)
        throw new IllegalStateException("Cant find axis with name "+axisName);
      if (axis.getAxisType() == AxisType.GeoZ || axis.getAxisType() == AxisType.Height || axis.getAxisType() == AxisType.Pressure)
        return axis;
    }
    return null;
  }

  public CoverageCoordAxis getTimeAxis() {
    CoverageCoordAxis result = getAxis(AxisType.Time);
    if (result == null)
      result = getAxis(AxisType.TimeOffset);
    return result;
  }

  public CoverageCoordAxis getAxis(AxisType type) {
    for (String axisName : getAxisNames()) {
      CoverageCoordAxis axis = dataset.findCoordAxis(axisName);
      if (axis == null)
        throw new IllegalStateException("Cant find axis with name "+axisName);
      else if (axis.getAxisType() == type) {
        return axis;
      }
    }
    return null;
  }

  public CoverageCoordAxis getAxis(String axisName) {
    return dataset.findCoordAxis(axisName);
  }

  public List getAxes() {
    List result = new ArrayList<>();
    for (String axisName : getAxisNames()) {
      CoverageCoordAxis axis = dataset.findCoordAxis(axisName);
      if (axis == null)
        throw new IllegalStateException("Cant find " + axisName);
      result.add(axis);
    }
    return result;
  }

  /**
   * Using independent axes only.
   * Note this depends on order of axes.
   */
  public int[] getShape() {
    int rank = 2; // always 2 horiz
    if (time2DCoordSys != null)
      rank += time2DCoordSys.getShape().length; // might have scalar runtime

    for (CoverageCoordAxis axis : getAxes()) {
      if (axis.getAxisType().isHoriz()) continue;
      if (isTime2D(axis)) continue;
      if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.independent) rank++;
    }

    int[] result = new int[rank];
    int count = 0;
    if (time2DCoordSys != null) {
      int[] timeShape = time2DCoordSys.getShape();
      System.arraycopy(timeShape, 0, result, count, timeShape.length);
      count += timeShape.length;
    }

    for (CoverageCoordAxis axis : getAxes()) {
      if (axis.getAxisType().isHoriz()) continue;
      if (isTime2D(axis)) continue;
      if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.independent)
        result[count++] = axis.getNcoords();
    }

    // the x,y shapes must be gotten from horizCoordSys
    for (RangeIterator ri : horizCoordSys.getRanges())
      result[count++] = ri.length();

    return result;
  }

  public List getRanges() {
    List result = new ArrayList<>();
    for (CoverageCoordAxis axis : getAxes()) {
      if (axis.getAxisType().isHoriz()) continue;
      if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.independent)
        result.add(axis.getRangeIterator());
    }

    result.addAll(horizCoordSys.getRanges()); // may be 2D
    return result;
  }


  public boolean isRegularSpatial() {
    CoverageCoordAxis xaxis = getXAxis();
    CoverageCoordAxis yaxis = getYAxis();
    if (xaxis == null || !(xaxis instanceof CoverageCoordAxis1D) || !xaxis.isRegular()) return false;
    if (yaxis == null || !(yaxis instanceof CoverageCoordAxis1D) || !yaxis.isRegular()) return false;
    return true;
  }

  public ProjectionImpl getProjection() {
    for (String ctName : getTransformNames()) {
      CoverageTransform ct = dataset.findCoordTransform(ctName);
      if (ct != null && ct.isHoriz())
        return ct.getProjection();
    }
    return new LatLonProjection();
  }

  private static class MyCoordSysContainer implements CoordSysContainer {
    public List axes;
    public List transforms;

    private MyCoordSysContainer(List axes, List transforms) {
      this.axes = axes;
      this.transforms = transforms;
    }

    @Override
    public CoverageTransform findCoordTransform(String transformName) {
      for (CoverageTransform ct : transforms)
        if (ct.getName().equalsIgnoreCase(transformName)) return ct;
      return null;
    }

    @Override
    public CoverageCoordAxis findCoordAxis(String axisName) {
      for (CoverageCoordAxis axis : axes) {
        if (axis.getName().equalsIgnoreCase(axisName)) return axis;
      }
      return null;
    }
  }

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

  public boolean isTime2D( CoverageCoordAxis axis) {
    if (time2DCoordSys == null) return false;
    if (axis instanceof TimeOffsetAxis) return true;
    if (axis instanceof TimeAxis2DFmrc) return true;
    return (axis.getAxisType() == AxisType.RunTime) && (axis.getDependenceType() != CoverageCoordAxis.DependenceType.dependent);
  }

  public Optional subset(SubsetParams params) {
    return subset(params, false, true);
  }

  public Optional subset(SubsetParams params, boolean makeCFcompliant, boolean finish) {
    Formatter errMessages = new Formatter();
    List subsetAxes = new ArrayList<>();
    for (CoverageCoordAxis axis : getAxes()) {
      if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.dependent) continue;
      if (axis.getAxisType().isHoriz()) continue;
      if (isTime2D(axis)) continue;

      ucar.nc2.util.Optional axiso = axis.subset(params);
      if (!axiso.isPresent())
        errMessages.format("%s: %s;%n", axis.getName(), axiso.getErrorMessage());
      else {
        CoverageCoordAxis1D subsetInd = (CoverageCoordAxis1D) axiso.get(); // independent always 1D
        subsetAxes.add(subsetInd);

        // subset any dependent axes
        for (CoverageCoordAxis dependent : getDependentAxes(subsetInd)) {
          Optional depo = dependent.subsetDependent(subsetInd);
          if (depo.isPresent()) subsetAxes.add(depo.get());
          else errMessages.format("%s;%n", depo.getErrorMessage());
        }
      }
    }

    AtomicBoolean isConstantForecast = new AtomicBoolean(false); // need a mutable boolean
    if (time2DCoordSys != null) {
      ucar.nc2.util.Optional> time2Do = time2DCoordSys.subset(params, isConstantForecast, makeCFcompliant);
      if (!time2Do.isPresent())
        errMessages.format("%s;%n", time2Do.getErrorMessage());
      else
        subsetAxes.addAll(time2Do.get());
    }

    Optional horizo = horizCoordSys.subset(params);
    if (!horizo.isPresent())
      errMessages.format("%s;%n", horizo.getErrorMessage());
    else {
      HorizCoordSys subsetHcs = horizo.get();
      subsetAxes.addAll(subsetHcs.getCoordAxes());
    }

    String errs = errMessages.toString();
    if (errs.length() > 0)
      return Optional.empty(errs);

    Collections.sort(subsetAxes);

    List names = new ArrayList<>();
    for (CoverageCoordAxis axis : subsetAxes)
      names.add(axis.getName());

    CoverageCoordSys resultCoordSys = new CoverageCoordSys(null, names, this.getTransformNames(), this.getCoverageType());
    MyCoordSysContainer fakeDataset = new MyCoordSysContainer(subsetAxes, getTransforms());
    resultCoordSys.setDataset(fakeDataset);
    resultCoordSys.setHorizCoordSys(resultCoordSys.makeHorizCoordSys());
    resultCoordSys.setIsConstantForecast(isConstantForecast.get());
    if (finish) resultCoordSys.setImmutable();

    return Optional.of(resultCoordSys);
  }

  public List getDependentAxes(CoverageCoordAxis indAxis) {
    List result = dependentMap.get(indAxis.getName());
    return (result == null) ? new ArrayList<>() : result;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy