Added by jgarnett, last edited by jgarnett on Aug 29, 2008  (view change)

Labels

 
(None)

The most common feedback we get on the GeoTools library is this "But I just want to Read a Shapefile". If you made it through the previous tutorial (03 First Project) - then you are going to have an easier time of this page.

References:

Getting some Spatial Data

Now that we have tried out maven, we can get down to working with some real spatial data. The shapefile format used by ESRI products is in very common use, if you do not already have a shapefile please download "world_borders.zip" and "world_borders.prj" from the following location:

You can find some more sample data here:

After you have found some sample data please please make sure to unzip the archive into the individual files shp, dbf, and shx files. The prj file is used to describe the projection of the data and is very useful if you want to draw or perform analysis.

Dependencies

In the previous step 03 First Project we made a simple pom.xml file that depdended on gt-main.

<dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-main</artifactId>
      <version>2.5-RC1</version>
    </dependency>

If you have not done these steps already please return to the 03 First Project page.

Adding the Shape and EPSG-WKT Plugins to your Project

We are going to start by adding two plugins to GeoTools toolkit. Plugins are used to add functionality to the core library.

Here are the plugins we will be using to to read a shapefile.

  • gt2-shape - Is used to reads file.shp, file.dbf, file.shx etc...
  • gt2-epsg-hsql - Is used to read file.prj

You can add these plugins by editing your pom.xml dependency section:

<dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-shapefile</artifactId>
      <version>2.5-RC1</version>
    </dependency>
    <dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-epsg-hsql</artifactId>
      <version>2.5-RC1</version>
    </dependency>

Once again please use make use of the correct "version" for the GeoTools you wish to work with.

Update your IDE Project FIles

  1. You will need to kick these dependencies into your IDE with another:
    C:java\geotools-example>mvn eclipse:eclipse
    
  2. Hit refresh in Eclipse

We will set up the rest of this page with opening a simple shapefile.

Here are the Jars you will needed

In this section we have added a dependency on gt-main, gt-shapefile and gt-epsg-hsql - here is what that actually looks like.

Please note that GeoTools does not offer a standalone shape file reader - we seriously need all the jars above order to do this "simple" task. We could get by with less - but only by ignoring information (such as that "prj" file).

Where did all these GeoTools jars come from

GeoTools is divided up into a series of modules, plugins and extentions. For the back ground information on how GeoTools slots together please read the 02 Meet the GeoTools Library page.

Look at the maven reports review individual modules:

Where did all these other jars come from

GeoTools makes use of a lot of third party jars. We really do want to stick to working on spatial code. Following our don't invent here policy we turn to the experts to handle things such as logging, working with java beans. and so on.

If you get a chance please use maven to sort out these dependencies.

  1. download geotools source, and maven 2.0.8
  2. cd into the module you want to use (example shapefile)
  3. type the following: mvn dependency:tree
    C:\java\geotools\trunk\modules\plugin\shapefile>mvn dependency:tree
    [dependency:tree]
     org.geotools:gt2-shapefile:jar:2.5-RC1
     +- junit:junit:jar:3.8.1:test
     +- javax.media:jai_core:jar:1.1.3:provided
     +- org.geotools:gt2-main:jar:2.5-M3:compile
     |  +- org.geotools:gt2-api:jar:2.5-M3:compile
     |  +- com.vividsolutions:jts:jar:1.9:compile
     |  +- edu.oswego:concurrent:jar:1.3.4:compile
     |  \- commons-beanutils:commons-beanutils:jar:1.7.0:compile
     |     \- commons-logging:commons-logging:jar:1.1.1:compile (version 
     managed from 1.0.3)
     +- org.geotools:gt2-sample-data:jar:2.5-M3:test
     +- org.geotools:gt2-referencing:jar:2.5-M3:compile
     |  +- java3d:vecmath:jar:1.3.1:compile
     |  +- commons-pool:commons-pool:jar:1.3:compile
     |  \- org.geotools:gt2-metadata:jar:2.5-M3:compile
     |     +- org.opengis:geoapi:jar:2.2-M3:compile
     |     \- javax.units:jsr108:jar:0.01:compile
     +- jdom:jdom:jar:1.0:compile
     +- velocity:velocity:jar:1.4:compile
     |  \- velocity:velocity-dep:jar:1.4:runtime
     \- org.geotools:gt2-epsg-hsql:jar:2.5-M3:test
        \- hsqldb:hsqldb:jar:1.8.0.7:test
    

Astute readers will notice the use of Java Advanced Imaging and a Java3D jar in the above list. These jars can optionally make use of native code if you have installed the appropriate "Java Runtime Extension" into your JRE.

  • vecmath - from the Java3D project
  • jai_core - from the Java Advanced Imaging Project

For added performance you can install these two projects as Java extensions into your JRE.

Example Code

The example code is available from:

I have cut and pasted the above code into this wiki for reference. Please make use of the above links to be sure you have an example that matches the version of GeoTools you are using.

Application

Please create the file FirstProject.java and copy and paste in the following code:

package org.geotools.demo;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureSource;
import org.geotools.factory.GeoTools;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import com.vividsolutions.jts.geom.Geometry;

public class FirstProject {
	
public static void main(String[] args) throws Exception {
    System.out.println("Welcome to GeoTools:" + GeoTools.getVersion());
    
    File file = getShapeFile(args);
    try {
    	// Connection parameters
        Map<String,Serializable>
        	connectParameters = new HashMap<String,Serializable>();
        
        connectParameters.put("url", file.toURI().toURL());
        connectParameters.put("create spatial index", true );
        DataStore dataStore = DataStoreFinder.getDataStore(connectParameters);
        
        // we are now connected
        String[] typeNames = dataStore.getTypeNames();
        String typeName = typeNames[0];

        System.out.println("Reading content " + typeName);

        FeatureSource<SimpleFeatureType, SimpleFeature> featureSource;
        FeatureCollection<SimpleFeatureType, SimpleFeature> collection;
        FeatureIterator<SimpleFeature> iterator;
        
        featureSource = dataStore.getFeatureSource(typeName);
        collection = featureSource.getFeatures();
        iterator = collection.features();
        
        double totalLength=0.0;
        try {
            while (iterator.hasNext()) {
                SimpleFeature feature = iterator.next();
                
                Geometry geometry = (Geometry) feature.getDefaultGeometry();
                totalLength += geometry.getLength();
            }
        }
        finally {
        	if( iterator != null ){
        		// YOU MUST CLOSE THE ITERATOR!
                iterator.close();	
        	}                
        }
        System.out.println("Total Length " + totalLength);

    } catch (Exception ex) {
        ex.printStackTrace();
        System.exit(1);
    }
    System.exit(0);
}

private static File promptShapeFile(String[] args)
		throws FileNotFoundException {
	File file;
	if (args.length == 0) {
		JFileChooser chooser = new JFileChooser();
		chooser.setDialogTitle("Open Shapefile for Reprojection");
		chooser.setFileFilter(new FileFilter() {
			public boolean accept(File f) {
				return f.isDirectory() || f.getPath().endsWith("shp")
						|| f.getPath().endsWith("SHP");
			}

			public String getDescription() {
				return "Shapefiles";
			}
		});
		int returnVal = chooser.showOpenDialog(null);

		if (returnVal != JFileChooser.APPROVE_OPTION) {
			System.exit(0);
		}
		file = chooser.getSelectedFile();

		System.out.println("You chose to open this file: " + file.getName());
	} else {
		file = new File(args[0]);
	}
	if (!file.exists()) {
		throw new FileNotFoundException(file.getAbsolutePath());
	}
	return file;
}

}

Running your Application

There are several ways to run this application easily:

  • You can run your application in your IDE
  • Or from the command line:
    C:\java\geotools-example>mvn exec:java -Dexec.mainClass="org.geotools.demo.example.FirstProject"
  • To run from the command line and pass in a file as an argument use:
    C:\java\geotools-example>mvn exec:java -Dexec.mainClass="org.geotools.demo.example.FirstProject" -Dexec.args="C:\data\world_borders.shp"

Questions

What Does ShapefileDataStore do

Here is how this all fits together:

  • ShapefileDataStore will read your file into a FeatureCollection
  • FeatureCollection has a number of Features
  • Each Feature has a Geometry (a JTS Geometry object)
  • Each Feature has a number of attributes (String, Integers, etc...)
  • The FeatureCollection has a schema which tells you what the String, Integers, etc mean
  • There is a CoordinateReferenceSystem to tell you what the Coordinates mean - so if you want to draw the shapefile you can tell where in the world the coordinates go.

Can the program read files that are several MB in size

Yes the shapefile reading code actually does not read anything until you open up an iterator(); and then it only keeps the file open as you call next(), .. hasNext(), ... next() ... etc...

The approach used is to "stream" the content into your application as you read; it does NOT load it into memory allowing you to work with massive files. GIS data is almost always big; so this approach is needed.

If you have database experience you may wish to think of a FeatureCollection as a prepared statement, and iterator() as executing the query.

I had to add a bunch of import statements to the top of App.java, and a try/catch block, to get it to actually compile and run.  Here's the complete program:

package org.geotools.demo.example;

import java.io.File;
import java.util.Map;
import java.util.HashMap;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;

import org.geotools.data.FeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.Feature;
import com.vividsolutions.jts.geom.Geometry;

import org.geotools.factory.GeoTools;

public class App
{
    public static void main( String[] args )
    {
        System.out.println( "Hello GeoTools: " + GeoTools.getVersion() );

        try {

            if( args.length != 1 ) System.exit(1);
            File file = new File( args[0] );
            if( !file.exists() ) System.exit(1);

            Map connect = new HashMap();
            connect.put( "url", file.toURL() );

            DataStore dataStore = DataStoreFinder.getDataStore( connect );
            String[] typeNames = dataStore.getTypeNames ();
            String typeName = typeNames[0];


            System.out.println( "Reading content "+ typeName );

            FeatureSource featureSource = dataStore.getFeatureSource( typeName );
            FeatureCollection collection = featureSource.getFeatures();
            FeatureIterator iterator = collection.features();
            int length = 0;
            try {
                while( iterator.hasNext() ){
                    Feature feature = iterator.next();
                    Geometry geometry = feature.getDefaultGeometry();
                    length += geometry.getLength();
                }
            }
            finally {
                iterator.close();
            }
            System.out.println( "Total length "+length );

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}


For compatibility with 2.5-M3, quite a few statements have to be changed. Here is what worked for me.

connect.put("url", file.toURI().toURL());
...
double boundingArea = 0.0;
...
GeometryAttribute geometryAttribute = feature.getDefaultGeometryProperty();
BoundingBox boundingBox = geometryAttribute.getBounds();
boundingArea += boundingBox.getHeight() * boundingBox.getWidth();
...
System.out.println("Total bounding area " + boundingArea);

Also the dependency should be

<dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-shapefile</artifactId>
      <version>2.5-M3</version>
</dependency>
<dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-epsg-wkt</artifactId>
      <version>2.5-M3</version>
</dependency>
Posted by Tara Athan at Aug 27, 2008 17:51 Updated by Tara Athan