Opensource gis development - part 3


Published on

Thrid part of the Course "Java Open Source GIS Development - From the building blocks to extending an existing GIS application." held at the University of Potsdam in August 2011

Published in: Education
1 Like
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Opensource gis development - part 3

  1. 1. Java Open Source GIS DevelopmentFrom the building blocks to extending an existing GIS application. Geoinformation Research Group, Department of Geography University of Potsdam August 2011 Part 3: Vector data Tutor: Andrea Antonello ydroloGIS nvironmental ngineeringHydroloGIS S.r.l. - Via Siemens, 19 - 39100 Bolzano
  2. 2. Vector basicsThe first thing people approaching GIS development want to do is tovisualize the world boundaries map with main cities from a shapefile. In thefirst part of this course we tried the quickstart mode, now we want to havemore control over the data we visualize and also want to write filtered data toa new shapefile.The parts we still need to learn are: • read the features from the shapefile • write features to a shapefile • view the result in a simple viewer (or did we learn that already?)
  3. 3. How to read data from a shapefileA shapefile can be read through the appropriate data store. TheDataStoreFinder becomes handy to help in the case one doesnt knowthe class to use: // create a parameter map Map<String, URL> paramMap = Collections.singletonMap("url", shapeFileUrl); // the datastore finder supplies the appropriate store for the passed url DataStore store = DataStoreFinder.getDataStore(paramMap); // get the data source to extract the features SimpleFeatureSource featureSource = store.getFeatureSource(store.getTypeNames()[0]); SimpleFeatureCollection featureCollection = featureSource.getFeatures();The same could have been achieved by using directly the shapefiledatastore: store = new ShapefileDataStore(shapeFile.toURI().toURL());We already learned how to handle vector data.
  4. 4. How to write data to a shapefileWriting data to a shapefile requires the creation of a compatible datastore.Right now we only want to write down the data read before, which meansthat we can use the same blueprint of the data (SimpleFeatureType): ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory(); ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params); SimpleFeatureType schema = featureCollection.getSchema(); newDataStore.createSchema(schema);The data are then written to file using a FeatureStore and a Transaction: Transaction transaction = new DefaultTransaction("create"); String typeName = schema.getTypeName(); SimpleFeatureSource outFeatureSource = newDataStore.getFeatureSource(typeName); if (outFeatureSource instanceof SimpleFeatureStore) { SimpleFeatureStore featureStore = (SimpleFeatureStore) outFeatureSource; featureStore.setTransaction(transaction); try { featureStore.addFeatures(featureCollection); transaction.commit(); } catch (Exception problem) { problem.printStackTrace(); transaction.rollback(); } finally { transaction.close(); } }
  5. 5. How to view a shapefileTo check if the file was properly created, we can use the simple map viewerthat GeoTools provides: store = DataStoreFinder.getDataStore(params); featureSource = store.getFeatureSource(store.getTypeNames()[0]); MapContext map = new MapContext(); map.setTitle("Check"); Style style = SLD.createSimpleStyle(featureSource.getSchema()); Layer layer = new FeatureLayer(featureSource, style); map.addLayer(layer); JMapFrame.showMap(map);You should see something like:
  6. 6. You might argue that it is boring to read and write the original data. It is agood point, which is why we will instead apply a filter in order to create a newfile containing only those countries that have an estimated population majorthan 100 millions. Need a hint? Here it is, apply it where appropriate: Filter filter = ECQL.toFilter("POP_EST > 100000000");And it should look like the following map:
  7. 7. How to write a shapefile from scratchWhen creating data from scratch, the first step is the design of its structure.Once the design is solid, it can be programmatically described through thefeaturetype.Lets assume we want to create a lat/long grid to overlay on the worldcountries map. We want it to look like the following map:
  8. 8. The grid we will create has a simple structure. We basically need a set oflines and an attribute for every line that represents its name: SimpleFeatureTypeBuilder ftBuilder = new SimpleFeatureTypeBuilder(); ftBuilder.setName("grid"); ftBuilder.setCRS(DefaultGeographicCRS.WGS84); ftBuilder.add("the_geom", LineString.class); ftBuilder.add("name", String.class); SimpleFeatureType ft = ftBuilder.buildFeatureType();Next we create a hashmap containing as key the name of the line and asvalue the line itself: HashMap<String, LineString> name2LinesMap = new HashMap<String, LineString>(); // create lon lines for( int lon = -180; lon <= 180; lon = lon + 10 ) { Coordinate start = new Coordinate(lon, -90); Coordinate stop = new Coordinate(lon, 90); LineString line = geometryFactory.createLineString(new Coordinate[]{start, stop}); name2LinesMap.put("lon: " + lon, line); }The same hase to be done for the latitude lines.
  9. 9. Using the blueprint we created before, we now create the collection offeatures of the grid: SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft); SimpleFeatureCollection newCollection = FeatureCollections.newCollection(); Set<Entry<String, LineString>> name2LineEntrySet = name2LinesMap.entrySet(); for( Entry<String, LineString> name2Line : name2LineEntrySet ) { Object[] values = new Object[]{name2Line.getValue(), name2Line.getKey()}; builder.addAll(values); SimpleFeature feature = builder.buildFeature(null); newCollection.add(feature); }It is left as an exercise to write this feature collection to a shapefile andvisualize it.
  10. 10. A quick exercise before we start with geometriesRead a CSV file containing geographic points and transform it to a shapefile.The data to use are the postal code data that can be found on theGeoNames Download Server.The attribute table has to contain: • a name of the point (field 2) • the country code (field 9) • the elevation (field 16)Also filter out only those points within a distance of about 1 degree fromPotsdam.
  11. 11. Geometry basicsHandling vector data is about two things: • geometry • attributesWe already learned how to create attributes and query/filter them.Now, we will dedicate some time to the essence of the spatial data: thegeometry.In Java, GIS geometry is synonymous of Java Topology Suite (JTS), the APIfor geometric operations.In the following sections we will explore both JTS functions (buffer, convexhull, intersection, symdifference, union) and predicates (contains, covers,overlaps, intersects).
  12. 12. A set of geometries to use as referenceTo better explain the various functions and predicates we will start bycreating a set of geometries on which to apply the operations.You are now able to create the following points, line and polygons: 5 g6 g1 g4 g5 g2 g3 0 0 5
  13. 13. The JTS testbuilderWhen testing code that handles geometries, it would be handy to see whatwe are doing. If we test an intersection between two geometries, we are ableto print out the text representation of any of those geometries, but thatdoesnt really help, we need a visual feedback.The JTS suite supplies a tool called testbuilder, which is able to visualize thetext representation of the geometries, as well as perform every operation wewill see in the next section.To install the testbuilder, go to the JTS download area, get the latest versionof JTS and uncompress it somewhere on your disk. Inside the extractedfolder you will find a file named bin/ (.bat forwindows). Execute it with: sh bin/
  14. 14. Try to print to standard output the geometries created before through: System.out.println(geometryName);and paste the result in the testbuilder. This will help a lot throughout thefollowing exercises.
  15. 15. Predicates IntersectsLets see which geometries intersect with g1 and print the result: System.out.println(g1.intersects(g2)); // true System.out.println(g1.intersects(g3)); // true System.out.println(g1.intersects(g4)); // true System.out.println(g1.intersects(g5)); // true System.out.println(g1.intersects(g6)); // trueNote that geometries that touch (like g1 and g2) also intersect. TouchesLets test which geometries touch g1 and print the result: System.out.println(g1.touches(g2)); // true System.out.println(g1.touches(g3)); // false System.out.println(g1.touches(g4)); // true System.out.println(g1.touches(g5)); // false System.out.println(g1.touches(g6)); // false
  16. 16. ContainsLets test which geometries are contained by g1 and print the result: System.out.println(g1.contains(g2)); // false System.out.println(g1.contains(g3)); // true System.out.println(g1.contains(g4)); // false System.out.println(g1.contains(g5)); // false System.out.println(g1.contains(g6)); // falseMind that a point on the border is not contained, so only g3 is contained. Thiscan be solved through the covers predicate. Covers System.out.println(g1.covers(g2)); // false System.out.println(g1.covers(g3)); // true System.out.println(g1.covers(g4)); // true System.out.println(g1.covers(g5)); // false System.out.println(g1.covers(g6)); // falseAs you can see, now also g4 is covered.
  17. 17. Functions IntersectionWhat is the resulting geometry of the intersection of different geometrytypes? // the intersection of polygons returns a polygon System.out.println(g1.intersection(g6)); // but the intersection of touching polygons returns a line System.out.println(g1.intersection(g2)); // the intersection of a polygon with a point is a point System.out.println(g1.intersection(g3)); // the intersection of a polygon with a line is a point System.out.println(g1.intersection(g5));The following shows the intersection of polygons g1 and g6: 5 g6 g1 g4 g5 g2 g3 0 0 5
  18. 18. SymdifferenceWhat is the resulting geometry of the symdifference of different geometrytypes? // the symDifference of intersecting polygons returns a multipolygon System.out.println(g1.symDifference(g6)); // but the symDifference of touching polygons returns the polygons union System.out.println(g1.symDifference(g2)); // the symDifference of a polygon with a contained point returns the original polygon System.out.println(g1.symDifference(g3)); // the symDifference of a polygon with a line is a hybrid collection (line + polygon) System.out.println(g1.symDifference(g5));The following shows the symdifference of polygons g1 and g6: 5 g6 g1 g4 g5 g2 g3 0 0 5
  19. 19. UnionWhat is the resulting geometry of the union of different geometry types? // the union of intersecting polygons returns a polygon System.out.println(g1.union(g6)); // same as the union of touching polygons System.out.println(g1.union(g2)); // the union of a polygon with a contained point is a point returns the original polygon System.out.println(g1.union(g3)); // the union of a polygon with a line is a hybrid collection (line + polygon) System.out.println(g1.union(g5));The following shows the union of polygons g1 and g6: 5 g6 g1 g4 g5 g2 g3 0 0 5
  20. 20. DifferenceThe difference of geometries obviously depends on the calling object: // this returns g1 minus the overlapping part of g6 System.out.println(g1.difference(g6)); // while this returns g6 minus the overlapping part of g1 System.out.println(g6.difference(g1)); // in the case of difference with lines, the result is the original polygon // with additional points in the intersections System.out.println(g1.difference(g2)); // the difference of polygon and point is the original polygon System.out.println(g1.difference(g3));The following shows the difference of polygons g1 and g6: 5 g6 g1 g4 g5 g2 g3 0 0 5
  21. 21. BufferCreating a buffer around a geometry always generates a polygon geometry.The behaviour can be tweaked, depending on the geometry type: // the buffer of a point System.out.println(g3.buffer(1.0)); // the buffer of a point with few quandrant segments System.out.println(g3.buffer(1.0, 1)); // round end cap style, few points System.out.println(g5.buffer(1.0, 2, BufferParameters.CAP_ROUND)); // round end cap style, more points System.out.println(g5.buffer(1.0, 10, BufferParameters.CAP_ROUND)); // flat end cap style System.out.println(g5.buffer(1.0, -1, BufferParameters.CAP_FLAT));
  22. 22. Convex HullTo test the convext hull operation, first lets create a geometry collectioncontaining the line and all the polygons: GeometryCollection geometryCollection = new GeometryCollection(new Geometry[]{g1, g2, g5, g6}, new GeometryFactory());Since the GeometryCollection extends Geometry, we can apply on it theconvex hull operation the same way we would do with any geometry: System.out.println(geometryCollection.convexHull());
  23. 23. Frequently used operations and tricks Find the shortest line between two geometriesThe DistanceOp class gives the possibility to find the nearest points of twogeometries, that given, creating the line is easy: Coordinate[] nearestPoints = DistanceOp.nearestPoints(g2, g5); LineString shortestLine = geometryFactory.createLineString(nearestPoints); Offset lineTo create an offset line, the single sided buffer operation can be used: BufferParameters params = new BufferParameters(); params.setSingleSided(true); // positive buffer is left Geometry offsetBuffer = BufferOp.bufferOp(g5, 1, params);Exercise: how can we extract the offset line from the single sided buffer?
  24. 24. Perpendicular lineA perpendicular line can be created with the help of the LineSegment. Ithas the ability to calculate points along the segment, even with offset.Create the LineSegment based on the line coordinates: Coordinate[] coordinates = g5.getCoordinates(); LineSegment lineSegment = new LineSegment(coordinates[0], coordinates[1]);We will create a perpendicular line of the same length of the original line. Wetherefore compute the points at the left and right side of the original linethrough an offset starting from the middle point of the segment (0.5). Theobtained points are then used as start and endpoint of the perpendicular line: double length = lineSegment.getLength(); Coordinate leftPoint = lineSegment.pointAlongOffset(0.5, length / 2.0); Coordinate rightPoint = lineSegment.pointAlongOffset(0.5, -length / 2.0); LineString perpendicularLine = geometryFactory.createLineString(new Coordinate[]{leftPoint, rightPoint});
  25. 25. Optimizing operations with prepared geometriesTo gain performance in repeating operations, the class PreparedGeometry issupplied together with a factory to actually prepare any geometry. Operationslike contains or covers can execute more than 100 times faster than theoriginal Geometry methods: PreparedGeometry preparedPolygon = PreparedGeometryFactory.prepare(g1); boolean contains = preparedPolygon.contains(g3);
  26. 26. Exercises• create train tracks from the WKT line: LINESTRING (50 380, 90 210, 180 160, 240 40, 240 40) (hint: LengthIndexedLine is useful).• extract the offset line from a single sided buffer.
  27. 27. Geometry/Attributes ExercisesFor the following exercises use the countries layer (admin countries fromNatural Earth) and the main cities layer (populated places). • find the smallest and largest country • calculate, using the country layer, how many people live inside every UTM zone (assuming an uniform distribution of population) • calculate, using the country and city layer, the percentage of the people of each country living in the main cities.
  28. 28. This work is released under Creative Commons Attribution ShareAlike (CC-BY-SA)Much of the knowledge needed to create this training material hasbeen produced by the sparkling knights of the GeoTools, JTS anduDig community. Another essential source has been the Wikipediacommunity effort.Particular thanks go to those friends that directly or indirectly helpedout in the creation and review of this developers handbook: JodyGarnett from the uDig/GeoTools community and the TANTO team.This tutorial was written with the support of the GeoinformationResearch Group of the University of Potsdam and HydroloGIS.