GeoTools : FM on trunk proposal 2


Motivation for supporting a new feature model is described very in this proposal, lets call it proposal one. So why an alternate proposal? Proposal 1 is technical sound, and good enough to allow people to start working community schema support. However it is not good enough to make that community schema support core to other applications such as GeoServer. Without being adopted by the wider community it will not be maintained and become unsupported, as has happened with the previous complex features branch.

In particular, things that are lacking are:

Backwards Compatability

Proposal one is backwards compatable with the rest of the library because it is simply additive. Of course adding new classes in a module that is not depended on by any other modules will be backwards compatable.

What about client code? Can code that uses the old feature model use the new model without any changes? The answer is no. So saying that proposal one is backwards compatable is a stretch.

Transition Strategy

Proposal one allows for running both models in parallel leaving the descision of when to swtich up to client code and the rest of the library. The benefits of this transition strategy are:

  • very non-invasive
  • no work for people that dont care about the feature model

However there are some drawbacks:

  • leaves all the work to the end, which raises the difficulty in performing the transition

Risk to Clients

The new feature model is very different from the old model. Along with a new api, are the new semantics of the new model. These new semantics will undoubetbly lead to issues in client code transitioning to it as assumptions made with the old model are no longer valid in the new model. Without forcing the rest of the geotools library to use any of the new model, it will receive no qa, and flush out none of these issues before hand. This leaves a very large burden on the client to flush out all the problems in the new model while performing the transition.

Having two models around means supporting two models everywhere until everything decides to transition to the new model. This means either adding additional execution paths to existing code, or adding extension points in key places to abstract out the dependency on a particular feature model. Either alternative is potentially a lot of work.

Beyond the feature model are apis built on top of it. For example the Geotools DataStore api. By definition of proposal 1, these other apis will not support the new model. Which means a client must create an abstraction around these other apis. An example would be a catalog. For clients that dont use a catalog, and use these apis directly, this is a problem.


  1. Port builder classes from FM branch to trunk (tick)
  2. Port all feature type and feature construction code to new build api
  3. Port rest of feature model from FM branch
  4. Make geotools feature model classes extend geoapi interfaces
  5. Get library passing all test cases
  6. Deprecate geotools feature model interfaces


Much of the api for building features has been deprecated on geotools trunk. The TypeBuilder and FeatureBuilder interfaces developed on the fm branch are intended to be used as the alternative.

Building a Feature Type

//create a new feature type factory
FeatureTypeFactory typeFactory = FeatureTypeFactory.newInstance( "simpleFeatureTypeFactory" );

//set the name
typeFactory.setName( "simpleTypeName" );
typeFactory.setNamespace( new URI( "" ) );

//create the attributes
AttributeType pointPropertyAttributeType = AttributeTypeFactory.newAttributeType( "pointProperty", Point.class );
typeFactory.addType( pointPropertyAttributeType );
typeFactory.setDefaultGeometry( pointPropertyAttributeType );

typeFactory.addType( AttributeTypeFactory.newAttributeType( "lineProperty", LineString.class ) );
typeFactory.addType( AttributeTypeFactory.newAttributeType( "intProperty", Integer.class ) );
typeFactory.addType( AttributeTypeFactory.newAttributeType( "stringProperty", String.class ) );
typeFactory.addType( AttributeTypeFactory.newAttributeType( "dateProperty, Date.class ) );

//build the type
FeatureType featureType = typeFactory.getFeatureType();
//create the builder
SimpleTypeBuilder typeBuilder = new SimpleTypeBuilder( CommonFactoryFinder.getSimpleFeatureFactory(null) );

//set the name
typeBuilder.setName( "simpleFeatureType" );
typeBuilder.setNamespace( "" );

//add the attribute types
typeBuilder.attribute( "pointProperty", Point.class );
typeBuilder.attribute( "lineProperty", LineString.class );
typeBuilder.attribute( "intProperty", Integer.class );
typeBuilder.attribute( "stringProperty", String.class );
typeBuilder.attribute( "dateProperty, Date.class );

//set the default geometry
typeBuilder.setGeometryName( "pointProperty" );

//set the crs
typeBuilder.setCRS( CRS.decode( "4326" ) );

//build the type
FeatureType featureType = typeBuilder.feature();

Builing a Feature

//already have a feature type around from somewhere...
FeatureType featureType = ....

//add the attributes
Object[] attributes = new Object[5];
attributes[0] =  new Point(..);
attributes[1] =  new LineString(..);
attributes[2] =  new Integer(..);
attributes[3] =  new String(..);
attributes[4] =  new Date(..);

Feature feature = featureType.create( attributes, "fid" );
//already have a feature type around from somewhere...
FeatureType featureType = ....

//create the builder
SimpleFeautreBuilder builder = new SimpleFeatureBuilder( new SimpleFeatureFactoryImpl() );

//set the type
builder.setType( featureType );

//add the attributes
builder.add( new Point(..) );
builder.add( new LineString(..) );
builder.add( new Integer(..) );
builder.add( new String(..) );
builder.add( new Date(..) );

//build the feature
Feature feature = "fid" );

Feature and FeatureType Construction

Much of the code to construct feature types and features can be isolated to the following methods:

  • AttributeTypeFactory.newAttributeType() ( 666 errors )
  • DataUtilities.createType()
  • FeatureTypes.newFeatureType()
  • FeatureType.create() ( 160 errors )

Porting these methods to the new builder apis will handle most cases.

New Feature Model

An implementation of the new feature model has been created on the fm branch. It must be ported to geotools trunk before the old feature model can be made to extend it. The details of doing so are outlined here.

Extending the New Model

In order to maintain backwards compatability ( for the most part ) with the rest of the library, the old feature model will be made to extend the new one. The new feature model contains a "simple" component which maps closley to the old model. So what this boils down to is having the old model extend the simple components of the new model. More specifically:

  • AttributeType made an extension of AttributeDescriptor
  • FeatureType made an extension of SimpleFeatureType
  • Feature made an extension of SimpleFeature

(warning) denotes a naming conflict between a method in the old model and a method in the new model


In the new model, the old idea of an AttributeType has been split into two concepts, namley AttributeDescriptor and AttributeType. The former will be extended by the old AttributeType. The following shows the mappings of the old methods to methods in the new api.



String getName()



Class getType()



Filter getRestriction()


boolean isNillable()


int getMinOccurs()


int getMaxOccurs()


Implementation Details

  • getName() renamed to name()
  • getType() renamed to binding()
  • AttributeType extends AttributeDescriptor
  • DefaultAttributeType extends AttributeDescriptorImpl
  • Remove unused AttributeType implementations:
    • ListAttributeType
    • SetAttributeType
    • ChoiceAttributeType
    • NestedAttributeType
    • FeatureAttributeType (question)
  • Handle other implementations of AttributeType:
    • ArcSDEAttributeType


The old FeatureType interfaces maps to the SimpleFeature interface in the new model. The interface mappings are:



URI getNamespace()


String getTypeName()


boolean isAbstract()


FeatureType[] getAnscestors()

AttributeType getSuper()

GeometryAttributeType getDefaultGeometry()

AttributeDescriptor getDefaultGeometry()


int getAttributeCount()


AttributeType getAttributeType( int i )

(AttributeDescriptor) attributes().get( i )

AttributeType getAttributeType( String xpath )

(AttributeDescriptor) attributes().get( i )

int find(AttributeType type)


int find(String attName)


Implementation Details

  • getDefaultGeometry() renamed to ?
  • FeatureType extends SimpleFeatureType
  • DefaultFeatureType extends SimpleFeatureTypeImpl
  • Handle other FeatureType implementations
    • WFSFeatureType
    • VPFFeatureType


  • FeatureCollectionIteration compares FeatureType to AttributeType in type hierachy


As FeautreType maps to SimpleFeatureType, Feature maps to SimpleFeature. The interface mappings are:



FeatureType getFeatureType()


String getID()


Object[] getAttributes()

getAttribute( String xpath )

get( xpath )

getAttribute( int index )

get( index )

setAttribute( int index, Object value )

set( index, value )

setAttribute( String xPath, Object value )

set( xPath value )

int getNumberOfAttributes()


Geometry getDefaultGeometry()



void setDefaultGeometry(Geometry geom)

defaultGeometry( geom )

Envelope getBounds()

BoundingBox getBounds()


Implementation Details

  • getDefaultGeometry() renamed to ?
  • getBounds() renamed to bounds()
  • Feature extends SimpleFeature
  • DefaultFeature extends SimpleFeatureImpl
  • Update FeatureCollection implementations ? ( there is discussion about making FeatureCollection composed of a feature and a collection instead of an extension of both )


  • FeatureDocument


At this point, the library should fully compile. Then it becomes a game of getting modules to pass tests and work through the issues that arise due to the new internal model.


At this point, the library should fully compile. Then it becomes a game of getting modules to pass tests and work through the issues that arise due to the new internal model.