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:
- FirstProject.java (Live Source Code)
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
- You will need to kick these dependencies into your IDE with another:
C:java\geotools-example>mvn eclipse:eclipse
- 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.
- download geotools source, and maven 2.0.8
- cd into the module you want to use (example shapefile)
- 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.
For added performance you can install these two projects as Java extensions into your JRE. |
Example Code
The example code is available from:
- http://svn.geotools.org/trunk/demo/example/src/main/java/org/geotools/demo/FirstProject.java
- included in the demo directory when you download geotools
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.
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>
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(); } } }