Upload
andrea-antonello
View
1.047
Download
3
Tags:
Embed Size (px)
DESCRIPTION
Second 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
Citation preview
nvironmental ngineeringydroloGISHydroloGIS S.r.l. - Via Siemens, 19 - 39100 Bolzano www.hydrologis.com
Java Open Source GISDevelopment
From the building blocks to extendingan existing GIS application.
Geoinformation Research Group, Department of GeographyUniversity of Potsdam
August 2011
Part 2: Introduction of the main Geo-ObjectsTutor: Andrea Antonello
A gentle introduction to the main geo Objects
This part aims to give the student, even if not a fully fledged developer, a
good usage-knowledge about the main geo-objects, leaving aside all the
architectural design and deep and dark computer science part. All in all the
aim is to get people to develop small algorithms in order to be able to extend
existing GIS frameworks such as uDig.
Some of the often used geo-objects that will be presented throughout this
part are:
Coordinate, GeometryFactory, Point, LineString, Polygon, Geometry,
CoordinateReferenceSystem, SimpleFeature, SimpleFeatureType, Simple-
FeatureCollection, GridCoverage2D, Filter
Two eclipse shortcuts before we start
Before we start right away with coding our first geo-objects, a short note
about shortcuts. Using shortcuts inside an IDE is extremely helpful. In eclipse
two shortcuts can save tons of time:
Ctrl^SPACE
This shortcut provides the context menu. Depending on the cursor position it
can for example complete variable or class names and supply a list of
possible methods on the current object.
Ctrl^1
This shortcut provides a quickfix menu, different depending on cursor
position. It can be used to rename variables, extract variable names, split
declarations and much more. It can even help you out to solve syntax errors.
Geometry (JTS)
The JTS library provides the following set of spatial data types:
Point
MultiPoint
LineString MultiLineString
LinearRingPolygon MultiPolygon
GeometryCollection
Coordinate
Coordinates are the building blocks of all spatial data types.
A Coordinate is a class that stores coordinates on the 2-dimensional
Cartesian plane. It has a simple constructor:
Coordinate coordinate2d = new Coordinate(11.0, 46.0);
It allows for a Z-coordinate, even if it is ignored for any operation:
Coordinate coordinate3d = new Coordinate(11.5, 46.0, 1200.0);
Which is why the only available operation on coordinates, the distance
calculation, returns 0.5 in the following case:
double distance = coordinate2d.distance(coordinate3d);
GeometryFactory
The GeometryFactory supplies a set of utility methods for building
Geometry objects. The following examples use it to build the main
geometries based on the main geographic building block, the Coordinate.
GeometryFactory factory = new GeometryFactory();
Create a Point:
Point point = factory.createPoint(coordinate1);
Create a LineString:
LineString line = factory.createLineString(new Coordinate[]{coordinate1, coordinate2});
Create a Polygon:
LinearRing ring = factory.createLinearRing(new Coordinate[]{coordinate1, coordinate2, coordinate3, coordinate1}); Polygon polygon = factory.createPolygon(ring, null);
Well Known Text (WKT)
The Well Known Text (WKT) representation of a geometry can be quite
handy, especially when creating testcases. It can be printed out simply by
invoking the toString() method of any geometry. A few examples:
• POINT (130 120)
• LINESTRING (50 380, 90 210, 180 160, 240 40, 240 40)
• POLYGON ((210 350, 230 310, 290 350, 290 350, 210 350))
The inverse, i.e. generating a Geometry object from WKT can be done
through the WKTReader:
String wktLine = "LINESTRING (50 380, 90 210, 180 160, 240 40, 240 40)"; WKTReader reader = new WKTReader(); Geometry readLine = reader.read(wktLine);
This representation of the geometries will help us to gain better
understanding of the different geometry types and operations.
(Multi)Point
A Point models a single Coordinate, a MultiPoint models a collection of
points.
A MultiPoint can be built through the factory:
MultiPoint multiPoint = factory.createMultiPoint(new Point[]{point1, point2, point3});
While the envelope of a point is the point itself, the envelope of a MultiPoint
geometry is the smalles envelop that contains all the points in the collection.
Geometry pointEnvelope = point1.getEnvelope(); Geometry multiPointEnvelope = multiPoint.getEnvelope();
(Multi)LineString
The line has a couple more properties than a point:
Geometry envelope = line.getEnvelope(); double length = line.getLength(); Point startPoint = line.getStartPoint(); Point endPoint = line.getEndPoint();
You can bundle lines in a collection:
MultiLineString multiLineString = factory.createMultiLineString(new LineString[]{line, line2});
Riddle: what is the area of a line?
double area = line.getArea();
And of a multiline?
double marea = multiLineString.getArea();
(Multi)Polygon
Polygons can also have holes, that are passed as second argument:
Polygon polygonWithHole = factory.createPolygon(linearRing, new LinearRing[]{hole});
The polygon has a couple more properties than a line:
Geometry envelope = polygon.getEnvelope(); double perimeter = polygon.getLength(); double area = polygon.getArea(); double areaWithHole = polygonWithHole.getArea(); Point centroid = polygon.getCentroid();
Geometry
The abstract class Geometry is the mother of all geometry classes.
The abstraction can be used for all geometric operations (for example
intersection and union).
Prj (GeoTools)
CoordinateReferenceSystem
WIKIPEDIA: "A spatial reference system (SRS) or coordinate reference
system (CRS) is a coordinate-based local, regional or global system used
to locate geographical entities. A spatial reference system defines a
specific map projection, as well as transformations between different
spatial reference systems. Spatial reference systems are defined by the
OGC's Simple feature access using well-known text, and support has been
implemented by several standards-based geographic information systems.
Spatial reference systems can be referred to using a SRID integer, including
EPSG codes defined by the International Association of Oil and Gas
Producers."
The Datum
The datum is a reference surface from which measurements are made
(Wikipedia).
Datums can be local, which are locally orientated ellissoid (no deviation on
the vertical, locally tangent), or global, which are used to cover the whole
globe and designed to support satellitar measurements.
local ellipsoid
global ellipsoid
geoid
Example Datums
Roma 40
local datum based on Hayford ellipsoid, with prime meridian on Monte
Mario
European Datum 50
local datum based on Hayford ellipsoid, tangent in Potsdam area,
with prime meridian in Greenwich. Used for UTM-ED50 CRS.
World Geodetic System WGS84
global datum with origin on the earth's mass center. Used for
example in the classic GPS CRS (EPSG:4326)
The Universal Transverse Mercator (UTM)
UTM maps the Earth with a transverse cylinder projection using 60 different
meridians, each of which is a standard "UTM Zone". By rotating the cylinder
in 60 steps (six degrees per step, about 800Km) UTM assures that all spots
on the globe will be within 3 degrees from the center of one of the 60
cylindrical projections.
Coordinate reprojection and transform, the (not so small) difference
Often reproject and transform are used the same way without much care.
There is a big difference though.
reproject
This is what we would call coordinate transform (CT). A CT can be
resolved in a well defined mathematical manner that doesn't lead to
precision loss (even if usually there is some minor due to data
precision and roundings).
transform
This is what we could call datum transform. Since datums are local
approximations of the geoid, transformations between datums are
based on statistical methods and lead most of the times to precision
loss.
GeoTools javadoc
• usually defined by a coordinate system (CS) and a datum
• captures the choice of values for the parameters that constitute the
degrees of freedom of the coordinate space
• since the choice is made either arbitrarily or by adopting values from
survey measurements, it leads to the large number of CRS in use around
the world
• is also the cause of the little understood fact that the latitude and longitude
of a point are not unique
• without the full specification of the CRS, coordinates are ambiguous at
best and meaningless at worst
CoordinateReferenceSystem (this time for real)
We now have the necessary (minimum) information to make use of CRS
inside our code and do some reprojecting. First let's create two CRS to work
with, one based on the Lat/long WGS84:
CoordinateReferenceSystem epsg4326Crs = CRS.decode("EPSG:4326");
and one based on WGS 84 / UTM zone 32N:
CoordinateReferenceSystem epsg32632Crs = CRS.decode("EPSG:32632");
To reproject coordinates first a math transform has to be created:
MathTransform transform = CRS.findMathTransform(epsg4326Crs, epsg32632Crs, lenient);
To actually reproject the JTS class supplies the method:
Coordinate newCoordinate = JTS.transform(coordinate, null, transform);
Vector (GeoTools)
SimpleFeature and SimpleFeatureType
In the course SimpleFeature will be used for Vector Data and represents
probably the most central object for GIS applications. The
SimpleFeatureType can be seen as the blueprint of the data. Vector data
are composed of a geometry part and an attribute table.
road1
road2
id name length
1
2
road1
road2
56.4
120.0
geometry attributes
SimpleFeatureType: the_geom: LineString id: Integer name: String length: Double
To create a feature, first the blueprint has to be created (feature type):
SimpleFeatureTypeBuilder featureTypeBuilder = new SimpleFeatureTypeBuilder(); featureTypeBuilder.setName("road"); featureTypeBuilder.setCRS(crs); featureTypeBuilder.add("the_geom", LineString.class); featureTypeBuilder.add("id", Integer.class); featureTypeBuilder.add("name", String.class); featureTypeBuilder.add("length", Double.class); SimpleFeatureType featureType = featureTypeBuilder.buildFeatureType();
Using the blueprint, any feature can be created by defining its contents:
// feature 1 SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType); int id = 1; String name = "road1"; double length = line1.getLength(); Object[] values = new Object[]{line1, id, name, length}; featureBuilder.addAll(values); SimpleFeature feature1 = featureBuilder.buildFeature(null); // feature 2 id = 2; name = "road2"; length = line2.getLength(); values = new Object[]{line2, id, name, length}; featureBuilder.addAll(values); SimpleFeature feature2 = featureBuilder.buildFeature(null);
How to extract the information once the features are packaged?
List<AttributeDescriptor> attributeDescriptors = featureType.getAttributeDescriptors(); for( AttributeDescriptor attributeDescriptor : attributeDescriptors ) { // get the name of the attribute from the descriptor String attributeName = attributeDescriptor.getLocalName(); // get the attribute value Object attribute = feature1.getAttribute(attributeName); // get the type of the attribute AttributeType attributeType = attributeDescriptor.getType(); Class< ? > binding = attributeType.getBinding(); // print a summary System.out.println(attributeName + " (type = " + binding.getSimpleName() + "): " + attribute); }
Being the geometric part really important, for the default geometry we have a
direct access:
Geometry geometry = (Geometry) feature1.getDefaultGeometry();
SimpleFeatureCollection
Features can be bundled into collections (which is how they are usually
served by readers and fed into writers):
SimpleFeatureCollection newCollection = FeatureCollections.newCollection(); newCollection.add(feature1); newCollection.add(feature2);
FeatureCollections are best accessed through an iterator, in order to assure
that they are not all read into memory:
SimpleFeatureIterator featureIterator = newCollection.features(); while( featureIterator.hasNext() ) { SimpleFeature feature = featureIterator.next(); // do something with it } // remember to close the handle! featureIterator.close();
Raster (GeoTools)
GridCoverage2D
A GridCoverage2D is what in the real world we usually call Raster or Grid,
i.e. a rectangular regular grid of pixels, each containing a value. The
following schema contains the main definitions we will use:
cols
rows
x res
y re
s
north
wes
t0,0
2,1
x (easting)
y (n
orth
ing)
grid space
world space
east
south
1200
1200
1200
1800
1170
1170
1800
1130
1130
1800
1130
1100
raster values
equator
Raster maps are not all that famous in GeoTools. People usually need them
to show nice ortophoto or similar imagery. This is probably the reason why
they have a quite tricky API. Building a GridCoverage2D object is quite a
pain.
In this course we will need to read and create raster data as for example
DTMs. To do so the JGrasstools API will be of help.
Let's start by defining some elevation data in a matrix:
double[][] elevationData = new double[][]{// {800, 900, 1000, 1000, 1200, 1250, 1300, 1350, 1450, 1500}, // {600, NaN, 750, 850, 860, 900, 1000, 1200, 1250, 1500}, // {500, 550, 700, 750, 800, 850, 900, 1000, 1100, 1500}, // {400, 410, 650, 700, 750, 800, 850, 490, 450, 1500}, // {450, 550, 430, 500, 600, 700, 800, 500, 450, 1500}, // {500, 600, 700, 750, 760, 770, 850, 1000, 1150, 1500}, // {600, 700, 750, 800, 780, 790, 1000, 1100, 1250, 1500}, // {800, 910, 980, 1001, 1150, 1200, 1250, 1300, 1450, 1500}// };
We now need to place them somewhere in the world, which means defining
boundaries and reference system:
double n = 5140020.0; double s = 5139780.0; double w = 1640650.0; double e = 1640950.0; double xRes = 30.0; double yRes = 30.0; CoordinateReferenceSystem crs = CRS.decode("EPSG:32632");
The JGrasstools API supplies a method to create a GridCoverage from the
data defined before:
RegionMap envelopeParams = new RegionMap(); envelopeParams.put(CoverageUtilities.NORTH, n); envelopeParams.put(CoverageUtilities.SOUTH, s); envelopeParams.put(CoverageUtilities.WEST, w); envelopeParams.put(CoverageUtilities.EAST, e); envelopeParams.put(CoverageUtilities.XRES, xRes); envelopeParams.put(CoverageUtilities.YRES, yRes); // build the coverage GridCoverage2D elevationRaster = CoverageUtilities.buildCoverage( // "elevation", elevationData, envelopeParams, crs, true);
To access data in a raster, we can either work in the world space (easting,
northing):
double[] value = elevationRaster.evaluate(new Point2D.Double(1640680, 5139820), (double[]) null);
or in the grid space (col, row):
value = elevationRaster.evaluate(new GridCoordinates2D(1, 6), (double[]) null);
It is possible to access the underlying image to iterate over it:
RenderedImage elevationRI = elevationRaster.getRenderedImage(); RandomIter iter = RandomIterFactory.create(elevationRI, null); for( int col = 0; col < elevationRI.getWidth(); col++ ) { for( int row = 0; row < elevationRI.getHeight(); row++ ) { double elev = iter.getSampleDouble(col, row, 0); System.out.print(elev + " "); } System.out.println(); }
Filters
A Filter defines a constraint that can be checked against an instance of an
object.
We will handle filters only with regard to vector data.
A filter can be seen as the WHERE clause of an SQL statement. It can apply
both to the alphanumeric values of an attribute table as well as to the
geometry.
One example could be: give me all the cities of Canada that count more than
10000 inhabitants.
CQL and ECQL
To help people in the construction of filters GeoTools supplies the class
ECQL.
Creating a filter that compares the string inside a field is therefore simple:
// name comparison filter Filter filter = ECQL.toFilter("name = 'road1'");
as is the comparison of numeric values:
// numeric comparison filter filter = ECQL.toFilter("length < 1.0");
one could also check the real length of a geometry, instead of the value in a
field:
// geometric filter filter = ECQL.toFilter("geomLength(the_geom) < 1.0");
it is possible to check if a field exists:
// field existence filter = ECQL.toFilter("name EXISTS");
regular expressions can be used to compare strings:
// like comparison filter = ECQL.toFilter("name LIKE 'roa%' AND length > 1.0");
and maybe the most used check, the bounding box:
// bounding box double w = 10.5; double e = 11.6; double s = 46.0; double n = 47.0; String filterString = "BBOX(the_geom, " + w + "," + s + "," + e + "," + n + ")"; filter = ECQL.toFilter(filterString);
The created filter can then for example be applied to a collection to extract a
subcollection based on the filter constraint:
SimpleFeatureCollection subCollection = collection.subCollection(filter);
Style
Style is that part that allows us to make maps look pretty and get the needed
symbolization and coloring of the contained data.
The OGC defines the Styled Layer Descriptor (SLD) as the standard to
style maps. GeoTools supplies an API to generate styles.
The programmatic approach to Style won't be handled in this course. To
create SLD styles, the style editor of uDig can be used.
Conclusions
In part 2 we played with the main objects used when doing GIS development.
We learned about the atomic entity, the Coordinate, how to build various
geometries through the GeometryFactory. We built Point, LineString and
Polygon geometries and now know that all their common behaviour can be
accessed through the Geometry class.
We then added the spatial context to the geometries by approaching the
CoordinateReferenceSystem. In the case of vectors, we created a blueprint
of the vector data as a SimpleFeatureType, we "populated" it creating the
actual SimpleFeature and bundled it to a SimpleFeatureCollection. We also
learned how to extract data from collections based on a Filter.
In the case of rasters we learned how to create a GridCoverage2D.
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 developer's 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.