package org.geotools.caching.grid.featurecache.readers; import java.io.IOException; import java.util.HashSet; import java.util.NoSuchElementException; import org.geotools.caching.spatialindex.SpatialIndex; import org.geotools.caching.util.CacheUtil; import org.geotools.data.FeatureReader; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.filter.Filter; import com.vividsolutions.jts.geom.Envelope; /** * This feature reader reads features from two sources. The results * of either are optionally cached. * <p> * All features are read from the feature readers and cached (if requested) however * only those features which pass the postFilter filter are * returned. * </p> * * @author Emily * @since 1.2.0 * * * * @source $URL$ */ public class CombiningCachingFeatureReader implements FeatureReader<SimpleFeatureType, SimpleFeature>{ private FeatureReader<SimpleFeatureType, SimpleFeature> r1 = null; private FeatureReader<SimpleFeatureType, SimpleFeature> r2 = null; private boolean cache1 = false; //if feature reader1 results are to be cached private boolean cache2 = false; //if feature reader 2 results are to be cached private SpatialIndex index = null; //cache location private Filter postFilter = null; //filter to apply to features as they are read from readers private SimpleFeature next = null; //the next feature to return on a call to next(); private HashSet<String> collectedFeatureIds; //need this for now because the "grid" may store the same feature multiple times per node; we only want to get each feature once /** * Creates a new feature reader that combines the results from two feature * reads and optionally caches the results. * * * All features are read from the feature readers and cached (if requested) * however only those features which pass the postFilter filter are * returned. </p> * * @param reader1 feature reader 1 * @param reader2 feature reader 2 * @param cache1 if results from feature reader 1 should be cached * @param cache2 if results from feature reader 2 should be cached * @param cache cache * @param postFilter the filter to be applied as feature read */ public CombiningCachingFeatureReader(FeatureReader<SimpleFeatureType, SimpleFeature> reader1, FeatureReader<SimpleFeatureType, SimpleFeature> reader2, boolean cache1, boolean cache2, SpatialIndex cache, Filter postFilter) throws IOException{ this.r1 = reader1; this.r2 = reader2; this.cache1 = cache1; this.cache2 = cache2; this.index = cache; this.postFilter = postFilter; this.collectedFeatureIds = new HashSet<String>(); init(); } /** * Closes the feature reader */ public void close() throws IOException { r1.close(); r2.close(); next = null; } /** * Get type associated with reader. * <p>This function returns the type from feature reader 1. Assumes that * both feature readers have the same type.</p> */ public SimpleFeatureType getFeatureType() { return (SimpleFeatureType)r1.getFeatureType(); } /** * @returns true if there are more features to read */ public boolean hasNext() throws IOException { return next != null; } /** * This function finds the first element in the collection */ private void init() throws IOException{ next = findNext(); } /** * Finds the next feature to return in the given collection. * <p>This function is responsible for caching the * features as it processes them. All features are cached * however only features which pass the postFilter * are returned. * </p> * <p>The function iterators through all the features * in the first feature reader; then moves on to the features in the second * feature reader. * </p> * * @return the next feature in the collection */ private SimpleFeature findNext() throws IOException { while (r1.hasNext()) { SimpleFeature sf = (SimpleFeature) r1.next(); if (cache1) { this.index.insertData(sf, CacheUtil.convert((Envelope) sf.getBounds())); } if (postFilter.evaluate(sf) && !collectedFeatureIds.contains(sf.getID())) { return sf; } } while (r2.hasNext()) { SimpleFeature sf = (SimpleFeature) r2.next(); if (cache2) { this.index.insertData(sf, CacheUtil.convert((Envelope) sf.getBounds())); } if (postFilter.evaluate(sf) && !collectedFeatureIds.contains(sf.getID())) { return sf; } } return null; } /** * Returns the next element in the collection. */ public SimpleFeature next() throws IOException, IllegalArgumentException, NoSuchElementException { SimpleFeature current = next; collectedFeatureIds.add(current.getID()); next = findNext(); if (current == null){ throw new NoSuchElementException("Both of the readers of been closed."); } return current; } }