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

ucar.nc2.ft2.simpgeometry.CFLine Maven / Gradle / Ivy

The newest version!
package ucar.nc2.ft2.simpgeometry;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ucar.ma2.Array;
import ucar.ma2.IndexIterator;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Variable;
import ucar.nc2.constants.CF;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.ft2.simpgeometry.exception.InvalidDataseriesException;

/**
 * A CF 1.8 compliant Line
 * for use with Simple Geometries. Can also
 * represent Multilines.
 * 
 * @author [email protected]
 *
 */
public class CFLine implements Line {

  private static final Logger cfl = LoggerFactory.getLogger(CFLine.class);
  private List points; // a list of the constitutent points of the Line, connected in ascending order as in the
                              // CF convention
  private Line next; // if non-null, next refers to the next line part of a multi-line
  private Line prev; // if non-null, prev refers to the previous line part of a multi-line
  private Array data; // data associated with the line

  /**
   * Get the geometry the data is associated with.
   * 
   */
  public GeometryType getGeometryType() {
    return GeometryType.LINE;
  }

  /**
   * Add a point to the end of the line.
   *
   */
  public void addPoint(double x, double y) {
    Point ptPrev = null;

    if (!points.isEmpty()) {
      ptPrev = points.get(points.size() - 1);
    }

    this.points.add(new CFPoint(x, y, ptPrev, null, null));

  }

  /**
   * Returns the list of points which make up this line
   * 
   * @return points - the collection of points that make up this line
   */
  public List getPoints() {
    return points;
  }

  /**
   * Get the data associated with this line
   * 
   * @return data
   */
  public Array getData() {
    return data;
  }

  /**
   * If part of a multiline, returns the next line within that line
   * if it is present.
   * 
   * @return next line if present, null if not
   */
  public Line getNext() {
    return next;
  }

  /**
   * If part of a multiline, returns the previous line within that line
   * if it is present
   * 
   * @return previous line if present, null if not
   */
  public Line getPrev() {
    return prev;
  }

  /**
   * Set the data associated with this Line
   * 
   * @param data - array of data to set to
   */
  public void setData(Array data) {
    this.data = data;
  }

  /**
   * Sets the previous line which makes up the multiline which this line is a part of.
   * If prev is a CFLine, automatically connect the other line to this line as well.
   * 
   */
  public void setNext(Line next) {
    if (next instanceof CFLine) {
      setNext((CFLine) next);
    }

    else
      this.next = next;
  }

  /**
   * Sets the next line which makes up the multiline which this line is a part of.
   * Automatically connects the other line to this line as well.
   */
  protected void setNext(CFLine next) {
    this.next = next;

    if (next != null) {
      next.setPrevOnce(this);
    }
  }

  private void setNextOnce(CFLine next) {
    this.next = next;
  }


  /**
   * Sets the previous line which makes up the multiline which this line is a part of.
   * If prev is a CFLine, automatically connect the other line to this line as well.
   */
  public void setPrev(Line prev) {
    if (prev instanceof CFLine) {
      setPrev((CFLine) prev);
    }

    else
      this.prev = prev;
  }

  /**
   * Sets the previous line which makes up the multiline which this line is a part of.
   * Automatically connect the other line to this line as well.
   */
  protected void setPrev(CFLine prev) {
    this.prev = prev;

    if (prev != null) {
      prev.setNextOnce(this);
    }
  }

  private void setPrevOnce(CFLine prev) {
    this.prev = prev;
  }

  /**
   * Given a dataset, variable, and index, automatically populates this Line and
   * returns it. If not found, returns null.
   * 
   * @param dataset which the variable is a part of
   * @param var the variable which has a geometry attribute
   * @param index of the line within the variable
   * @return return a line
   */
  public Line setupLine(NetcdfDataset dataset, Variable var, int index) {
    this.points.clear();
    Array xPts;
    Array yPts;
    Variable nodeCounts;
    Variable partNodeCounts = null;

    List axes = dataset.getCoordinateAxes();
    CoordinateAxis x = null;
    CoordinateAxis y = null;

    String[] nodeCoords = var.attributes().findAttributeString(CF.NODE_COORDINATES, "").split(" ");

    // Look for x and y
    for (CoordinateAxis ax : axes) {

      if (ax.getFullName().equals(nodeCoords[0]))
        x = ax;
      if (ax.getFullName().equals(nodeCoords[1]))
        y = ax;
    }

    // Affirm node counts
    String node_c_str = var.findAttributeString(CF.NODE_COUNT, "");

    if (!node_c_str.equals("")) {
      nodeCounts = dataset.findVariable(node_c_str);
    }

    else
      return null;

    // Affirm part node counts
    String pNodeCoStr = var.findAttributeString(CF.PART_NODE_COUNT, "");

    if (!pNodeCoStr.equals("")) {
      partNodeCounts = dataset.findVariable(pNodeCoStr);
    }

    SimpleGeometryIndexFinder indexFinder = new SimpleGeometryIndexFinder(nodeCounts);

    // Get beginning and ending indicies for this polygon
    int lower = indexFinder.getBeginning(index);
    int upper = indexFinder.getEnd(index);


    try {

      xPts = x.read(lower + ":" + upper).reduce();
      yPts = y.read(lower + ":" + upper).reduce();

      IndexIterator itrX = xPts.getIndexIterator();
      IndexIterator itrY = yPts.getIndexIterator();

      // No multipolygons just read in the whole thing
      if (partNodeCounts == null) {

        this.next = null;
        this.prev = null;

        // x and y should have the same shape, will add some handling on this
        while (itrX.hasNext()) {
          this.addPoint(itrX.getDoubleNext(), itrY.getDoubleNext());
        }

        switch (var.getRank()) {

          case 2:
            this.setData(var.read(CFSimpleGeometryHelper.getSubsetString(var, index)).reduce());
            break;

          case 1:
            this.setData(var.read("" + index));
            break;

          default:
            throw new InvalidDataseriesException(InvalidDataseriesException.RANK_MISMATCH); // currently do not support
                                                                                            // anything but dataseries
                                                                                            // and scalar associations

        }
      }

      // If there are multipolygons then take the upper and lower of it and divy it up
      else {

        Line tail = this;
        Array pnc = partNodeCounts.read();
        IndexIterator pncItr = pnc.getIndexIterator();

        // In part node count search for the right index to begin looking for "part node counts"
        int pncInd = 0;
        int pncEnd = 0;
        while (pncEnd < lower) {
          pncEnd += pncItr.getIntNext();
          pncInd++;
        }

        // Now the index is found, use part node count and the index to find each part node count of each individual
        // part
        while (lower < upper) {

          int smaller = pnc.getInt(pncInd);

          while (smaller > 0) {
            tail.addPoint(itrX.getDoubleNext(), itrY.getDoubleNext());
            smaller--;
          }

          // Set data of each
          switch (var.getRank()) {

            case 2:
              tail.setData(var.read(CFSimpleGeometryHelper.getSubsetString(var, index)).reduce());
              break;

            case 1:
              tail.setData(var.read("" + index));
              break;

            default:
              throw new InvalidDataseriesException(InvalidDataseriesException.RANK_MISMATCH); // currently do not
                                                                                              // support anything but
                                                                                              // dataseries and scalar
                                                                                              // associations

          }

          lower += tail.getPoints().size();
          pncInd++;
          tail.setNext(new CFLine());
          tail = tail.getNext();
        }

        // Clean up
        tail = tail.getPrev();
        if (tail != null)
          tail.setNext(null);
      }
    }

    catch (IOException | InvalidRangeException | InvalidDataseriesException e) {
      cfl.error(e.getMessage());
      return null;
    }

    return this;
  }

  /**
   * Gets the upper bounding box coordinate on the line.
   * 
   * @return double array = (x, y)
   */
  public double[] getBBUpper() {
    double[] bbUpper = new double[2];

    List ptList = this.getPoints();
    if (ptList.isEmpty())
      return null;
    bbUpper[0] = ptList.get(0).getY();
    bbUpper[1] = ptList.get(0).getY();

    for (Point pt : this.getPoints()) {
      if (bbUpper[0] < pt.getX()) {
        bbUpper[0] = pt.getX();
      }

      if (bbUpper[1] < pt.getY()) {
        bbUpper[1] = pt.getY();
      }
    }

    // Got maximum points, add some padding.
    bbUpper[0] += 10;
    bbUpper[1] += 10;

    return bbUpper;
  }

  /**
   * Gets the lower bounding box coordinate on the line.
   * 
   * @return double array = (x, y)
   */
  public double[] getBBLower() {
    double[] bbLower = new double[2];

    List ptList = this.getPoints();
    if (ptList.isEmpty())
      return null;
    bbLower[0] = ptList.get(0).getY();
    bbLower[1] = ptList.get(0).getY();

    for (Point pt : this.getPoints()) {
      if (bbLower[0] > pt.getX()) {
        bbLower[0] = pt.getX();
      }

      if (bbLower[1] > pt.getY()) {
        bbLower[1] = pt.getY();
      }
    }

    // Got minimum points, add some padding.
    bbLower[0] -= 10;
    bbLower[1] -= 10;

    return bbLower;
  }

  /**
   * Constructs an "empty" line with no members using an ArrayList to implement the point list.
   * 
   */
  public CFLine() {
    this.points = new ArrayList<>();
    this.next = null;
    this.prev = null;
    this.data = null;
  }

  /**
   * From a given list of points, construct a line
   * 
   * @param newPt The list of points which will constitute the new line
   */
  public CFLine(List newPt) {
    this.points = newPt;
    this.next = null;
    this.data = null;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy