/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2010, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.data.collection;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.collection.AbstractFeatureCollection;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureVisitor;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.sort.SortBy;
import org.opengis.geometry.BoundingBox;
/**
* FeatureCollection implementation wrapping around a java.util.List.
* <p>
* This implementation wraps around a java.util.List and is suitable
* for quickly getting something on screen.
* <p>
* Usage notes:
* <ul>
* <li>This implementation does not use a spatial index, please do not expect spatial operations to be fast.
* <li>FeatureCollections are not allowed to have duplicates
* </ul>
* <p>
* This implementation is intended to quickly wrap up a list of features and get them on screen; as such
* it respects various hints about the copying of internal content as provided by the renderer.
*
* @see Hints#FEATURE_DETACHED
* @author Oliver Gottwald
* @author Jody
*
* @source $URL: http://svn.osgeo.org/geotools/trunk/modules/library/main/src/main/java/org/geotools/data/collection/ListFeatureCollection.java $
*/
@SuppressWarnings("unchecked")
public class ListFeatureCollection extends AbstractFeatureCollection {
/** wrapped list of features containing the contents */
private List<SimpleFeature> list;
/** Cached bounds */
private ReferencedEnvelope bounds = null;
/**
* Create a ListFeatureCollection for the provided schema
* An ArrayList is used internally.
* @param schema
*/
public ListFeatureCollection(SimpleFeatureType schema) {
this(schema, new ArrayList<SimpleFeature>());
}
/**
* Create a ListFeatureCollection around the provided list. The contents
* of the list should all be of the provided schema for this to make sense.
* Please keep in mind the feature collection control, no two Features in the list
* should have the same feature id, and you should not insert the same feature more
* then once.
* <p>
* The provided list is directly used for storage, most feature collection
* operations just use a simple iterator so there is no performance advantaged
* to be gained over using an ArrayList vs a LinkedList (other then for the size()
* method of course).
*
* @param schema
* @param list
*/
public ListFeatureCollection(SimpleFeatureType schema, List<SimpleFeature> list ){
super(schema);
this.list = list;
}
/**
* Create a ListFeatureCollection around the provided list. The contents
* of the list should all be of the provided schema for this to make sense.
* Please keep in mind the feature collection control, no two Features in the list
* should have the same feature id, and you should not insert the same feature more
* then once.
* <p>
* The provided list is directly used for storage, most feature collection
* operations just use a simple iterator so there is no performance advantaged
* to be gained over using an ArrayList vs a LinkedList (other then for the size()
* method of course).
*
* @param schema
* @param list
*/
public ListFeatureCollection(SimpleFeatureCollection copy ) throws IOException {
this( copy.getSchema() );
addAll(copy );
}
@Override
public int size() {
return list.size();
}
@Override
protected Iterator openIterator() {
Iterator it = list.iterator();
return it;
}
@Override
protected void closeIterator(Iterator close) {
// nothing to do there
}
@Override
public boolean add(SimpleFeature f) {
//maintain the bounds
BoundingBox boundingBox = f.getBounds();
if (bounds == null){
bounds = new ReferencedEnvelope(
boundingBox.getMinX(), boundingBox.getMaxX(),
boundingBox.getMinY(), boundingBox.getMaxY(),
schema.getCoordinateReferenceSystem());
} else {
bounds.expandToInclude(boundingBox.getMinX(), boundingBox.getMinY());
bounds.expandToInclude(boundingBox.getMaxX(), boundingBox.getMaxY());
}
return list.add(f);
}
@Override
public void clear() {
// maintain the bounds
bounds = null;
super.clear();
}
@Override
public SimpleFeatureIterator features() {
return new ListFeatureIterator(list);
}
@Override
public synchronized ReferencedEnvelope getBounds() {
if( bounds == null ){
bounds = calculateBounds();
}
return bounds;
}
/**
* Calculate bounds from features
* @return
*/
private ReferencedEnvelope calculateBounds() {
ReferencedEnvelope extent = new ReferencedEnvelope();
for( SimpleFeature feature : list ){
if( feature == null ) continue;
ReferencedEnvelope bbox = ReferencedEnvelope.reference( feature.getBounds() );
if( bbox == null || bbox.isEmpty() || bbox.isNull() ) continue;
extent.expandToInclude( bbox );
}
return extent;
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
/**
* SimpleFeatureIterator that will use collection close method.
* @author Jody
*/
private class ListFeatureIterator implements SimpleFeatureIterator {
private Iterator<SimpleFeature> iter;
public ListFeatureIterator(List<SimpleFeature> features) {
iter = features.iterator();
}
public void close() {
if( iter instanceof FeatureIterator){
((FeatureIterator)iter).close();
}
}
public boolean hasNext() {
return iter.hasNext();
}
public SimpleFeature next() throws NoSuchElementException {
return iter.next();
}
}
@Override
public SimpleFeatureCollection subCollection(Filter filter) {
CollectionFeatureSource temp = new CollectionFeatureSource( this );
return temp.getFeatures(filter);
}
@Override
public SimpleFeatureCollection sort(SortBy order) {
Query subQuery = new Query( getSchema().getTypeName() );
subQuery.setSortBy( new SortBy[]{ order } );
CollectionFeatureSource temp = new CollectionFeatureSource( this );
return temp.getFeatures(subQuery);
}
}