/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2012, 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.feature.collection;
import java.io.IOException;
import java.util.Collection;
import org.geotools.data.DataUtilities;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.store.FilteringFeatureCollection;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.util.NullProgressListener;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.sort.SortBy;
import org.opengis.geometry.BoundingBox;
/**
* Implement a feature collection just based on provision of a {@link FeatureIterator}.
* <p>
* This implementation asks you to implement:
* <ul>
* <li>{@link #features()}</li>
* <li>
* This is the direct decentent of the origional {@link AbstractFeatureCollection}
* and represents the easiest way to package your content as a FeatureCollection.
* <p>
* As this class provides no optimization, it is strongly recommended that you implement
* the following methods (which require a whole collection traversal):
* <ul>
* <li>{@link #size()}</li>
* <li>{@link #getBounds()</li>
* </ul>
*
* @author Jody Garnett (LISAsoft)
* @source $URL$
*/
public abstract class BaseFeatureCollection<T extends FeatureType, F extends Feature> implements FeatureCollection<T,F> {
/**
* id used when serialized to gml
*/
protected String id;
protected T schema;
protected BaseFeatureCollection(){
this( null, null );
}
protected BaseFeatureCollection(T schema) {
this( schema, null );
}
protected BaseFeatureCollection(T schema, String id) {
this.id = id == null ? "featureCollection" : id;
this.schema = schema;
}
public String getID() {
return id;
}
public T getSchema() {
return schema;
}
//
// FeatureCollection - Feature Access
//
/**
* Subclasses required to implement this method to traverse FeatureCollection contents.
* <p>
* Note that {@link FeatureIterator<F>#close()} is available to clean up
* after any resource use required during traversal.
*/
public abstract FeatureIterator<F> features();
/**
* Returns <tt>true</tt> if this collection contains the specified element. <tt></tt>.
* <p>
*
* This implementation iterates over the elements in the collection, checking each element in turn for equality with the specified element.
*
* @param o object to be checked for containment in this collection.
* @return <tt>true</tt> if this collection contains the specified element.
*/
public boolean contains(Object o) {
FeatureIterator<F> e = features();
try {
if (o == null) {
while (e.hasNext()){
if (e.next() == null){
return true;
}
}
} else {
while (e.hasNext()){
if (o.equals(e.next())){
return true;
}
}
}
return false;
} finally {
e.close();
}
}
/**
* Returns <tt>true</tt> if this collection contains all of the elements in the specified collection.
* <p>
*
* @param c collection to be checked for containment in this collection.
* @return <tt>true</tt> if this collection contains all of the elements in the specified collection.
* @throws NullPointerException if the specified collection is null.
*
* @see #contains(Object)
*/
public boolean containsAll(Collection<?> c) {
FeatureIterator<F> e = features();
try {
while (e.hasNext()){
Feature feature = e.next();
if (!c.contains(feature)){
return false;
}
}
return true;
} finally {
e.close();
}
}
/**
* @return <tt>true</tt> if this collection contains no elements.
*/
public boolean isEmpty() {
FeatureIterator<F> iterator = features();
try {
return !iterator.hasNext();
} finally {
iterator.close();
}
}
/**
* Array of all the elements.
*
* @return an array containing all of the elements in this collection.
*/
public Object[] toArray() {
Object[] result = new Object[size()];
FeatureIterator<F> e = null;
try {
e = features();
for (int i = 0; e.hasNext(); i++)
result[i] = e.next();
return result;
} finally {
e.close();
}
}
@SuppressWarnings("unchecked")
public <O> O[] toArray(O[] a) {
int size = size();
if (a.length < size) {
a = (O[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
}
FeatureIterator<F> it = features();
try {
Object[] result = a;
for (int i = 0; i < size; i++)
result[i] = it.next();
if (a.length > size)
a[size] = null;
return a;
} finally {
it.close();
}
}
public void accepts(org.opengis.feature.FeatureVisitor visitor,
org.opengis.util.ProgressListener progress) throws IOException {
DataUtilities.visit(this, visitor, progress);
}
//
// Feature Collections API
//
/**
* Convenience implementation that just wraps this collection into a
* {@link FilteringFeatureCollection}. Subclasses might want to override this in case the filter
* can be cascaded to their data sources.
*
* @param filter
* @return
*/
public FeatureCollection<T,F> subCollection(Filter filter) {
if (filter == Filter.INCLUDE) {
return this;
}
return new FilteringFeatureCollection<T, F>(this, filter);
}
/**
* Obtained sorted contents, only implemented for SimpleFeature at present.
* <p>
* This method only supports SimpleFeature at present, consider use of FeatureSource.features( Query ).
*
* @param order Sort order
* @return FeatureCollection sorted in the indicated order
*/
@SuppressWarnings("unchecked")
public FeatureCollection<T,F> sort(SortBy order) {
if (getSchema() instanceof SimpleFeatureType) {
// go for the most efficient way if possible, otherwise rely on pure in memory
// sorting...
SimpleFeatureCollection simple = DataUtilities.simple((FeatureCollection<SimpleFeatureType, SimpleFeature>) this);
return (FeatureCollection<T, F>) new SortedSimpleFeatureCollection(simple,new SortBy[] { order });
} else {
// hmm... we don't even have a basic non simple collection... need to implement one
// before going here
throw new UnsupportedOperationException("Cannot sort on complex features at the moment");
}
}
/**
* Returns the number of elements in this collection.
*
* @return Number of items, or Interger.MAX_VALUE
*/
public int size(){
int count = 0;
FeatureIterator<F> it = features();
try {
while( it.hasNext() ){
@SuppressWarnings("unused")
Feature feature = it.next();
count++;
}
} finally {
it.close();
}
return count;
}
/**
* Full collection traversal to obtain bounds of FeatureCollection.
* Subclasees are strong encouraged to override this expensive method
* (even if just to implement caching).
*/
public ReferencedEnvelope getBounds(){
ReferencedEnvelope bounds = null;
FeatureIterator<F> it = features();
try {
while( it.hasNext() ){
Feature feature = it.next();
BoundingBox bbox = feature.getBounds();
if( bbox != null ){
if( bounds == null ){
bounds = new ReferencedEnvelope( bbox );
}
else {
bounds.include( bbox );
}
}
}
} finally {
it.close();
}
return bounds;
}
}