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

ucar.nc2.ft.point.FlattenedDatasetPointCollection Maven / Gradle / Ivy

The newest version!
package ucar.nc2.ft.point;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import ucar.nc2.ft.DsgFeatureCollection;
import ucar.nc2.ft.FeatureDatasetPoint;
import ucar.nc2.ft.PointFeature;
import ucar.nc2.ft.PointFeatureCC;
import ucar.nc2.ft.PointFeatureCCC;
import ucar.nc2.ft.PointFeatureCollection;
import ucar.nc2.ft.PointFeatureIterator;
import ucar.nc2.time.CalendarDateUnit;
import ucar.nc2.util.IOIterator;

/**
 * An aggregate collection of all the PointFeatures in a dataset formed by flattening the nested structures within.
 * This class's {@link #getPointFeatureIterator iterator} returns features in default order, with maximum read
 * efficiency as the goal.
 *
 * @author cwardgar
 * @since 2014/10/08
 */
public class FlattenedDatasetPointCollection extends PointCollectionImpl {
  private final FeatureDatasetPoint fdPoint;

  /**
   * Constructs a FlattenedDatasetPointCollection.
   *
   * @param fdPoint a point dataset.
   * @throws IllegalArgumentException if any of the feature collections in the dataset are not of type
   *         {@code PointFeatureCollection} or {@code NestedPointFeatureCollection}.
   */
  public FlattenedDatasetPointCollection(FeatureDatasetPoint fdPoint) throws IllegalArgumentException {
    super(fdPoint.getLocation(), CalendarDateUnit.unixDateUnit, null); // Default dateUnit and altUnits.
    this.fdPoint = fdPoint;

    List featCols = fdPoint.getPointFeatureCollectionList();

    if (!featCols.isEmpty()) {
      DsgFeatureCollection firstFeatCol = featCols.get(0);

      // Replace this.dateUnit, this.altUnits, and this.extras with "typical" values from firstFeatCol.
      // We can't be certain that those values are representative of ALL collections in the dataset, but it's
      // a decent bet because in practice, firstFeatCol is so often the ONLY collection.
      copyFieldsFrom(firstFeatCol);
    }
  }

  private void copyFieldsFrom(DsgFeatureCollection featCol) {
    this.timeUnit = featCol.getTimeUnit();
    this.altUnits = featCol.getAltUnits();
  }

  @Override
  public PointFeatureIterator getPointFeatureIterator() {
    return new FlattenedDatasetPointIterator(fdPoint);
  }


  protected class FlattenedDatasetPointIterator extends PointIteratorAbstract {
    private final Iterator dsgFeatColIter;

    private PointFeatureIterator pfIter;
    private IOIterator pfcIter;
    private IOIterator pfccIter;

    private boolean finished; // set to "true" when close() is called.

    public FlattenedDatasetPointIterator(FeatureDatasetPoint fdPoint) {
      this.dsgFeatColIter = fdPoint.getPointFeatureCollectionList().iterator();
      setCalculateBounds(FlattenedDatasetPointCollection.this.getInfo());
    }

    @Override
    public boolean hasNext() {
      try {
        // pfIterHasNext() will fail the first time hasNext() is called because no DsgFeatureCollection has
        // been loaded yet.
        while (!pfIterHasNext()) {
          if (!loadNextDsgFeatureCollection()) {
            close(); // May not be called otherwise if iter is being used in a for-each.
            return false;
          }
        }

        return true;
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    /**
     * Attempts to find a PointFeatureIterator in the currently-loaded DsgFeatureCollection that has another
     * available element (i.e. {@code hasNext() == true}). Such an iterator may already be loaded into
     * {@code pfIter}. If not, we'll have to look through {@code pfcIter} and/or {@code pfccIter} to find one.
     * 

* That iterator, if it's found, will be assigned to {@code pfIter} and this method will return {@code true}. * Otherwise, it'll return {@code false}, meaning that there are no more unread PointFeatures available in the * currently-loaded DsgFeatureCollection. * * @return {@code true} if {@code pfIter.hasNext()} will now return {@code true}. * @throws IOException if an I/O error occurs. */ private boolean pfIterHasNext() throws IOException { if (pfIter != null) { if (pfIter.hasNext()) { return true; } else { // We'll need to load a new PointFeatureIterator below. But first, close the old one. pfIter.close(); } } while (pfcIterHasNext()) { this.pfIter = pfcIter.next().getPointFeatureIterator(); if (pfIter.hasNext()) { return true; } // else: Iterator could be empty, in which case we proceed to the next loop iteration. } return false; } /** * Attempts to find a {@code IOIterator} in the currently-loaded DsgFeatureCollection * that has another available element (i.e. {@code hasNext() == true}). Such an iterator may already be loaded * into {@code pfcIter}. If not, we'll have to look through {@code pfccIter} to find one. *

* The iterator, if it's found, will be assigned to {@code pfcIter} and this method will return {@code true}. * Otherwise, it'll return {@code false}, meaning that there are no more unread PointFeatureCollection * iterators available in the currently-loaded DsgFeatureCollection * * @return {@code true} if {@code pfcIter.hasNext()} will now return {@code true}. * @throws IOException if an I/O error occurs. */ private boolean pfcIterHasNext() throws IOException { if (pfcIter != null && pfcIter.hasNext()) { return true; } while (pfccIter != null && pfccIter.hasNext()) { pfcIter = pfccIter.next().getCollectionIterator(); if (pfcIter.hasNext()) { return true; } // else: Iterator could be empty, in which case we proceed to the next loop iteration. } return false; } /** * Retrieves the next DsgFeatureCollection from {@code dsgFeatColIter} and assigns it to the appropriate data * member. The DsgFeatureCollections returned by {@link FeatureDatasetPoint#getPointFeatureCollectionList} will * be one of the following 3 subtypes: *

    *
  • {@link PointFeatureCollection}: will be assigned to {@code pfIter}
  • *
  • {@link PointFeatureCC}: will be assigned to {@code pfcIter}
  • *
  • {@link PointFeatureCCC}: will be assigned to {@code pfccIter}
  • *
* * @return {@code true} if the next DsgFeatureCollection was successfully loaded into the appropriate data * member, or {@code false} if no more remain. * @throws IOException if an I/O error occurs. */ private boolean loadNextDsgFeatureCollection() throws IOException { if (!dsgFeatColIter.hasNext()) { return false; } // Clear out any iterators belonging to the previous DsgFeatureCollection pfIter = null; pfcIter = null; pfccIter = null; DsgFeatureCollection dsgFeatCol = dsgFeatColIter.next(); if (dsgFeatCol instanceof PointFeatureCollection) { pfIter = ((PointFeatureCollection) dsgFeatCol).getPointFeatureIterator(); } else if (dsgFeatCol instanceof PointFeatureCC) { pfcIter = ((PointFeatureCC) dsgFeatCol).getCollectionIterator(); } else if (dsgFeatCol instanceof PointFeatureCCC) { pfccIter = ((PointFeatureCCC) dsgFeatCol).getCollectionIterator(); } else { throw new AssertionError("CAN'T HAPPEN: FeatureDatasetPoint.getPointFeatureCollectionList() " + "only contains PointFeatureCollection, PointFeatureCC, or PointFeatureCCC."); } return true; } @Override public PointFeature next() { if (pfIter == null) { // Could be null if hasNext() == false or wasn't called at all. return null; } else { PointFeature pointFeat = pfIter.next(); calcBounds(pointFeat); return pointFeat; } } @Override public void close() { if (finished) { return; } // If hasNext() was repeatedly called until it returned "false", all PointFeatureIterators should've // already been closed. However, this may be useful in exceptional circumstances. if (pfIter != null) { pfIter.close(); } finishCalcBounds(); finished = true; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy