GeoTools : org.geotools.data interface scratchpad

org.geotools.data Interfaces

The e-mails discussing the various data interfaces (DataStore, FeatureCollection, etc) get pretty lengthy. Hopefully this page can serve as a summary of what the interfaces might look like with the most current suggestions.

Note that Java generic syntax is used below in several places (i.e. List<QName>). This is used for clarity of writing, but may not actually be used in the "real" interfaces.

(Note: This was started by Chris Dillard in hopes that someone with a little more time would come along and finish filling things in...)
(Comments inline from James Macgill, marked JRM)

DataStore
interface DataStore {
    // Gets a list of all the names of the types held in this DataStore.
    List<QName> getTypeNames();

    // Gets all features of the given type
    FeatureCollection getFeatures(QName type);

    // Gets all features of the given type that pass some Filter
    FeatureCollection getFeatures(QName type, Filter f);

    // Gets features of the given type that pass some query.
    // This allows for retrieving only a subset of the attributes (and
    // may allow for reprojection)
    FeatureCollection getFeatures(QName type, Query q);// should/could the type be part of the 
                                                       // query (JRM)

    // Shortcut for getting the FeatureCollection then calling setTransaction(t)
    FeatureCollection getFeatures(QName type, Query q, Transaction t);
}
Feature
interface Feature {
    FeatureType getFeatureType();
    FeatureCollection getParent(); //Back pointer is v.important. (JRM)
    // getBounds() can return null if Feature has no geometry
    Envelope getBounds();
    String getID();

    Object getAttribute(String path) throws IllegalAttributeException;
    Object getAttribute(int index) throws IndexOutOfBoundsException;
    void setAttribute(String path, Object value) throws IllegalAttributeException;
    void setAttribute(int index, Object value) throws IndexOutOfBoundsException;
}
FeatureCollection
interface FeatureCollection extends Feature, Collection {
    // Accessor methods, from Collection
    // Will always return FeatureIterator, shown below.
    Iterator<Feature> iterator();
// DZ - add a throws OperationNotSupported?
// CSD - Under what circumstances could these operations not be supported?
// DZ - the toArray() function may not be supported for large dataset. Also optionally returning null works.
    Object [] toArray();
    Object [] toArray(Object [] buffer);

    // Size methods, from Collection
    boolean isEmpty();
    // May return -1 if size is not known (streaming)
    int size();

    // Feature containment checking, from Collection
    boolean contains(Object o);
    boolean containsAll(Collection c);

    // Modification methods from Collection.  All may throw UnsupportedOperationException
    // This is in line with the Collections 'Unmodifiable' approach. (JRM)
    boolean add(Object o);
    boolean addAll(Collection c);
    void clear();
    boolean remove(Object o);
    boolean removeAll(Collection c);
    boolean retainAll(Collection c);

    // Do we need a close() operation?
    void close() throws IOException; // I don't think so, I don't see how a FeatureCollection
                                     // could ever be open or closed, it just is.  the class
                                     // returned by iterattor() would have to handle this. (JRM)
// DZ - hmm, very useful for many datastores, we can't rely on dispose to be executed in a 
// DZ - timely manner which could cause connection issues
// CSD - It seems FeatureCollection represents the result of a specific query, so you will
// CSD - need a close() operation to clean up.

    // If the add() methods are to work, we'll need a transaction somehow:
    void setTransaction(Transaction t);
    Transaction getTransaction();
    // The above is true iif modifications to a FeatureCollection are imidatly reflected in the 
    // orignating DataStore, this may or may not be the case.  Needs some thought. (JRM)

    // Listener methods
    public void addFeatureListener(FeatureListener fl);
    public void removeFeatureListener(FeatureListener fl);
}
FeatureType
interface FeatureType {
    // Include all the same stuff that's in FeatureType now.
    // Also include the following:

    // Returns true if Features of this type can be cast to FeatureCollection.
    boolean isCollection();

    // If isCollection() returns true, this method returns a non-empty
    // list of FeatureTypes that indicates what types of Features can be
    // elements of the collection.
    List<FeatureType> getChildTypes();
}
QName
interface QName {
    // Get the namespace for your feature.
    public URI getNamespaceURI();
    // Get the name (without namespace or prefix)
    public String getLocalName();

    // This can return null if not known or data source doesn't care.
    // Note that this only applies when the features are encoded as XML.
    public String getPreferredPrefix();
}

As an example, suppose we have some feature xml that looks like this:

<xyz:MajorRoads xmlns:xyz="http://www.foo.com/data/majorroads">
  ...
</xyz:MajorRoads>

For a QName describing this Feature, getNamespaceURI() would return "http://www.foo.com/data/majorroads", getLocalName() would return "MajorRoads", and getPreferredPrefix() could return "xyz".

FeatureIterator

It seems like we need some way of calling "close" on an iterator. Relying on garbage collection (and implementing finalize()) is a bad idea.

CSD - Do we want to extend ListIterator? I suspect not... Although nothing prevents a given DataStore from returning one if it can easily seek.

interface FeatureIterator extends Iterator {
    // For clarity, here are the methods from Iterator:
    boolean hasNext();
    Object next();
    void remove(); // Throws UnsuppOpException

    // And here's the reason for this interface to exist:
    public void close() throws IOException;
}
FeatureListener
interface FeatureListener {
    public void featuresAdded(FeatureEvent e);
    public void featuresRemoved(FeatureEvent e);
    public void featuresChanged(FeatureEvent e);
}
FeatureEvent
class FeatureEvent extends java.util.EventObject {
    // IDs of the features that were affected
    private List<String> fids;

    public FeatureEvent(FeatureCollection source, List<String> fids) {
        super(source);
    }

    public List<String> getFeatureIDs() {
        return fids;
    }

    // Synonym for EventObject.getSource(), but does the cast for you
    public FeatureCollection getFeatureCollection() {
        return (FeatureCollection) getSource();
    }
}

Usage

Examples of using the interfaces in common situations.

Reading features from a datastore
public class Test {
    public static void main(String [] args) {
        DataStore ds = ... ;

        FeatureCollection fc = null;
        FeatureIterator fi = null;

        try {
            fc = ds.getFeatures(new QName("Roads"));

            fi = (FeatureIterator) fc.iterator();
            while (fi.hasNext()) {
                Feature f = (Feature) fi.next();
                doStuffWithFeature(f);
            }
        }
        finally {
            if (fi != null) fi.close();
            if (fc != null) fc.close();
        }
    }
}
Writing features to a new datastore
Appending features to an existing datastore
etc.