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() throws IOException {
        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 = false;  // 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 - 2024 Weber Informatics LLC | Privacy Policy