GeoTools : Features

In this brief tutorial we will see how vector data is handled in memory by the GeoTools library. Since the geometric component of the vector data is based on the JTS library, familiarity with JTS classes is required. If you don't know what JTS is and want to read introductory material about it first, please refer to:

Feature Definitions

Features represent the geographic data you may want to manage or analyse with a GIS: they may be houses, roads, streams, surficial geology, lakes, states and so on.

Every Feature is made up of a geometry and attributes: surely you will recognize that Features are designed to be the basic data types for a vector oriented GIS application.

In fact, at present there is a trick to embed a grid coverage into a Feature, but for the moment let's forget about that.

Please refer to the diagram at the bottom of this page during this short tutorial to gain an insight about the classes and interfaces we are going to review.

Each Feature is composed of an array of objects that represents its attributes. The following rules hold:

  • each Feature has at least one geometry, which may be null.
  • each Feature may have more than one Geometry, but at least one is the default geometry which is used, for example, when rendering the Feature if no other specification is made in the symbolizer.
  • each attribute can really be anything, which means it can also be another Feature. This way you can model containment.

For a more detailed explaination, please see the Feature javadoc.

Each Feature is described by a FeatureType, exactly as each java object is described by a Class object.
The FeatureType holds a set of AttributeTypes that describe the feature attributes, in particular, their name, type and validity rules.
As you can see, the FeatureType class also models multiple inheritance, so you can declare feature types that are subtypes (descendant) or simpler feature types.

Among the attribute types there is a special one, the GeometryAttributeType, which describes geometric attributes and holds a reference to the CoordinateReferenceSystem and GeometryFactory. The coordinate reference system is of paramount importance in GIS applications, since it is the only way to associate the numerical value of a Coordinate to a point on the Earth's surface.
Unfortunately, at the moment the DataStores don't have good support for coordinate reference systems, but hopefully this situation will improve over the next few months.

Features class diagram

The following class diagram shows all of the classes you should be aware of when working with Features.

Creating Features and Feature types

As you can see in the diagram, features, feature types and attribute types are just interfaces.

This means that you cannot instantiate them directly, but also that you can provide your own specialized versions that wrap legacy objects you may have in your own application.

You can create a Feature by hand with the following steps:

  • create an array of attribute types, describing the attributes in your feature type, using the
    AttributeTypeFactory static methods;
  • create a FeatureType out of the attribute types using the FeatureTypeFactory static methods;
  • create the set of attribute values that describe your specific feature;
  • create the Feature object by using the FeatureType as a factory.

The following code snippet illustrates this process:

//AttributeType geom = AttributeTypeFactory.newAttributeType("the_geom", Point.class);
AttributeType geom = AttributeTypeFactory.newAttributeType("the_geom", LineString.class);
AttributeType roadWidth = AttributeTypeFactory.newAttributeType("width", Float.class);
FeatureType ftRoad = FeatureTypeFactory.newFeatureType(new AttributeType[] {geom, roadWidth}, "road");
WKTReader wktReader = new WKTReader();
//Point geometry = (Point)"POINT (" + lat + " " + lon + ")");
LineString geometry = (LineString)"LINESTRING (0 0, 10 10)");
Float width = new Float(10);
Feature theRoad = ftRoad.create(new Object[] {geometry, width}, "myRoad");

Of course, you won't usually be involved in feature creation (that's the work of DataStores) but it's nice to know how to do it by hand, to have a better understanding of how things work.

Features are usually accessed from DataStores in a streaming way. Should you need to handle a set of Features in memory, have a look at the FeatureCollection class, which is a specialized Set designed to hold features and provides a convenient FeatureIterator that doesn't require you to use casts when calling the next() method.


Features.gif (image/gif)