Added by jgarnett, last edited by jgarnett on Sep 22, 2008  (view change)

Labels

 
(None)

A FeatureCollection is a collection of Features similar to a JDBC ResultSet. Please note that while this concept sounds similar to a Java 5 Collection<Feature> the crucial difference is the requirement to close each FeatureIterator after use in order to prevent memory and connection leaks.

Overview of Feature Collection

The FeatureCollection interface has some additional methods so you can review the FeatureType of the members and so on.

The interface looks something like this:

interface FeatureCollection extends Collection<Feature> implements Feature {
   Iterator<Feature> iterator();
   close( Iterator iterator );
   ...
}

FeatureCollection of SimpleFeature

We are going to focus on FeatureCollection<SimpleFeatureType,SimpleFeature that adds the following constraints:

  • does not support mixed content, members must be all be of the same FeatureType
  • does not support any additional attributes (on the collection itself)

With these restrictions the following methods make sense:

AttributeType/*SimpleFeatureCollectionType*/ getType();
SimpleFeatureCollectionType getFeatureCollectionType();
SimpleFeatureType getMemberType();

SimpleFeatureCollection is used through out GeoTools at the moment, we will make use of the more generic FeatureCollection interface when we have some more talented sources of data.

Handle FeatureCollection as a Persisted Collection

A FeatureCollection is not an in memory snapshot of your data (as you might expect), we work with the assumption that GIS data is larger than you can fit into memory. Most implementations of FeatureCollection provide a memory footprint close to zero and each time you access the data will be loaded as you use it.

Please note that you should not treat a FeatureCollection as a normal in memory Java collection - these are heavyweight objects and we must ask you to close any iterators you open.

FeatureIterator iterator = featureCollection.features();
try {
     while( iterator.hasNext() ){
           Feature feature = iterator.next();
           ...
     }
}
finally {
     iterator.close();
}

For more details please see our [09 FeatureCollection Iterator] page.

Creating a new FeatureCollection

To create a new FeatureCollection please use the FeatureCollections utility class:

SimpleFeatureCollection collection = FeatureCollections.newCollection();

You can also create your collection with an "id", which will can be used as a handle to tell your collections appart.

SimpleFeatureCollection collection = FeatureCollections.newCollection("internal");

Adding Content to your FeatureCollection

You can create new features and add them to this FeatureCollection as needed:

SimpleFeatureCollection collection = FeatureCollections.newCollection("internal");
        
FeatureType type = DataUtilities.createType("location","geom:Point,name:String");        
collection.add( type.create( new Object[]{ point1, "name1"} ) );
collection.add( type.create( new Object[]{ point2, "name2"} ) )

Acquiring a FeatureCollection from a DataStore

The most common thing to do is grab a FeatureCollection from a file or service. For the background on DataStore please visit the 10 Data module.

File file = new File("example.shp");
Map map = new HashMap();
map.put( "url", file.toURL() );
DataStore dataStore = DataStoreFinder.getDataStore( Map map );

FeatureSource featureSource = dataStore.getFeatureSource( typeName );
FeatureCollection collection = featureSource.getFeatures();

Please be aware that this is not a copy - the FeatureCollection above should be considered to be the same thing as the "example.shp". Changes made to the collection will be written out to the shapefile.

How to load a FeatureCollection into Memory

If you would like to work with an in memory copy, you will need to explicitly take the following step:

FeatureCollection collection = myFeatureSource.getFeatures();
featureCollection memory = DataUtilities.collection( collection );

Using Query to produce a specific FeatureCollection

Using a Query to order your Attributes

Occasionally you will want to specify the exact order in which your attributes are presented to you, or even leave some attributes out altogether.

DefaultQuery query = new DefaultQuery( typeName, filter);
query.setPropertyNames( "geom", "name" );
FeatureCollection sorted = source.getFeatures(query);

Please note that the resulting FeatureCollection.getSchema() will not match FeatureSource.getFeatureType(), since the attributes will now be limited to (and in the order) specified.

Using a Query to Sort a FeatureCollection

DefaultQuery query = new DefaultQuery( typeName, filter);
SortBy sort = filterFactory.sort( sortField, SortOrder.DESCENDING);
query.setSortBy( new SortBy[] { sort } );

FeatureCollection sorted = source.getFeatures(query);

Using Feature Collection to Access Features

A FeatureCollection similar to Java Collection<Feature>; this means that an Iterator is available for you to to access the contents.

However you will need to close your iterator after use; so that any resources (such as database connections) are returned.

There are comparisons with several other ways of reading data on this page; you can choose the approach that suites you. FeatureVisitor is something I like; it involves less lines of code on my part but it "gobbles" all the error messages. FeatureReader makes all the error messages visible but involves a lot of try/catch code. When working on Java 1.4 code you may want to use FeatureIterator rather than Iterator.

Using Iterator

CoordinateReferenceSystem crs = features.getMemberType().getCRS();
        BoundingBox bounds = new ReferencedEnvelope( crs );
        
        Iterator<SimpleFeature> iterator = features.iterator();
        try {
            while( iterator.hasNext()){
                SimpleFeature feature = iterator.next();
                bounds.include( feature.getBounds() );
            }
        }
        finally{
            features.close( iterator );
        }

Working with Invalid Data

Currently GeoTools follows a "fail first" policy; that is if the data does not exactly meet the requirements of the FeatureType a RuntimeException will be thrown.

However often you may in want to just "skip" the troubled Feature and carry on; very few dataset's are perfect.

FeatureCollection featureCollection = featureSource.getFeatures(filter);
Iterator iterator = null;
int count;
int problems;
try {
     for( iterator = features.iteator(); iterator.hasNext(); count++){
           try {
               Feature feature = (Feature) iterator.next();
               ...
           }
           catch( RuntimeException dataProblem ){
               problems++;
               lastProblem = dataProblem;               
           }
     }
}
finally {
     featureCollection.close( iterator );
}
if( problems == 0 ){
   System.out.println("Was able to read "+count+" features.");
else {
   System.out.println("Read "+count + "features, with "+problems+" failures");

We hope to change this policy; allowing you to work with your data as it exists (invalid or not).

Use of FeatureVisitor (Recommended)

FeatureVisitor lets you accomplish the same thing as Working with Invalid Data above, with less try/catch/finally boilerplate code.

CoordinateReferenceSystem crs = features.getMemberType().getCRS();
final BoundingBox bounds = new ReferencedEnvelope( crs );
        
features.accepts( new AbstractFeatureVisitor(){
    public void visit( Feature feature ) {
        bounds.include( feature.getBounds() );
    }            
}, new NullProgressListener() );

You do not have to worry about exceptions, open or closing iterators and as an added bonus this may even be faster (depending on the number of cores you have available).

Use of FeatureIterator

When working with Java 1.4 (and GeoTools 2.4) you do not have access to Java 5 and Iterator<SimpleFeature>. You can use a FeatureIterator to remove the casting to Feature, you will also find it has a less clunky close() operation.

CoordinateReferenceSystem crs = features.getMemberType().getCRS();
        Envelope bounds = new Envelope();
        
        FeatureIterator features = collection.features();
        try {
            while( features.hasNext()){
                Feature feature = features.next();
                bounds.expandToInclude( feature.getBounds() );
            }
        }
        finally{
            features.close();
        }

In Java 5 FeatureCollection iterator() also returns a FeatureIterator with a close method (in case you care?)

Comparison with using DataStore and FeatureReader

FeatureReader is a "low level" version of Iterator that is willing to throw IOExceptions, it is a little bit more difficult to use but you may find the extra level of detail worth it.

FeatureReader reader = null;
try {
     reader = dataStore.getFeatureReader( typeName, filter, Transaction.AUTO_COMMIT );
     while( reader.hasNext() ){
          try {
              Feature feature = reader.next();
          }
          catch( IllegalArgumentException badData ){
              // skipping this feature since it has invalid data
          }
          catch( IOException unexpected ){
              unexpected.printStackTrace();
              break; // after an IOException the reader is "broken"
          }
     }
}
catch( IOException couldNotConnect){
     couldNotConnect.printStackTrace();
}
finally {
     if( reader != null ) reader.close();
}

About FeatureCollection

What does a FeatureCollection Represent

FeatureCollection is also formal construct as far as the standards people are concerned - and they have a few thoughts you may not of expected:

  • A FeatureCollection is created to represent something (often a spatial relationship such as "everything in the following bounding box"), but the motivation could be temporal (all the features in a given timer period), or simply aggregation (my feature collection "route" is made up of the following seven roads).
  • A FeatureCollection has a bounds
  • A FeatureCollection is itself a Feature; this example may make sense when you think of the "route" example above - you may want to name the route and so on
  • A FeatureCollection has a FeatureCollectionType; this follows from the idea of it being a Feature (but is worth stating again)