32
nvironmental ngineering ydroloGIS HydroloGIS S.r.l. - Via Siemens, 19 - 39100 Bolzano www.hydrologis.com Java Open Source GIS Development From the building blocks to extending an existing GIS application. Geoinformation Research Group, Department of Geography University of Potsdam August 2011 Part 2: Introduction of the main Geo-Objects Tutor: Andrea Antonello

Opensource gis development - part 2

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

Page 1: Opensource gis development - part 2

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

Page 2: Opensource gis development - part 2

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

Page 3: Opensource gis development - part 2

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.

Page 4: Opensource gis development - part 2

Geometry (JTS)

The JTS library provides the following set of spatial data types:

Point

MultiPoint

LineString MultiLineString

LinearRingPolygon MultiPolygon

GeometryCollection

Page 5: Opensource gis development - part 2

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);

Page 6: Opensource gis development - part 2

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);

Page 7: Opensource gis development - part 2

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.

Page 8: Opensource gis development - part 2

(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();

Page 9: Opensource gis development - part 2

(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();

Page 10: Opensource gis development - part 2

(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).

Page 11: Opensource gis development - part 2

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."

Page 12: Opensource gis development - part 2

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

Page 13: Opensource gis development - part 2

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)

Page 14: Opensource gis development - part 2

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.

Page 15: Opensource gis development - part 2

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.

Page 16: Opensource gis development - part 2

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

Page 17: Opensource gis development - part 2

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);

Page 18: Opensource gis development - part 2

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

Page 19: Opensource gis development - part 2

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);

Page 20: Opensource gis development - part 2

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();

Page 21: Opensource gis development - part 2

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();

Page 22: Opensource gis development - part 2

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

Page 23: Opensource gis development - part 2

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}// };

Page 24: Opensource gis development - part 2

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);

Page 25: Opensource gis development - part 2

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(); }

Page 26: Opensource gis development - part 2

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.

Page 27: Opensource gis development - part 2

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");

Page 28: Opensource gis development - part 2

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);

Page 29: Opensource gis development - part 2

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);

Page 30: Opensource gis development - part 2

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.

Page 31: Opensource gis development - part 2

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.

Page 32: Opensource gis development - part 2

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.