This document discusses geographic scripting in gvSIG using the Python programming language. It provides examples of building geometries like points, lines, polygons and multi-geometries programmatically using the gvsig module. It also demonstrates using predicates to check relationships between geometries like intersection and containment. Functions for performing geometric operations on layers like intersection, union, difference and buffer are also illustrated.
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
PART 4: GEOGRAPHIC SCRIPTING
1. GEOGRAPHIC SCRIPTING IN GVSIG
HALFWAY BETWEEN USER AND DEVELOPER
Geoinformation Research Group,
Department of Geography
University of Potsdam
21-25 November 2016
Andrea Antonello
PART 4: GEOGRAPHIC SCRIPTING
2. GEO-SCRIPTING IN GVSIG
In gvSIG geographic scripting is done using the Python programming
language syntax (the actual engine is called Jython).
As proposed when creating a new script, most geo-related operations are
done using the gvsig module. Also, a main method needs to be defined, for
the script to work:
import gvsig
def main(*args):
# do some scripting here
3. GEO-SCRIPTING IN GVSIG
gvSIG imports will be used in several ways throughout the course, mostly
because the way one uses imports depends on what he/she is writing...
and also in order to make you better understand how things work.
Let's see an example of two different ways to handle imports:
from gvsig import *
from gvsig.geom import *
def main(*args):
point = createPoint(D2, 30, 10)
print "Point: ", point
project = currentProject()
print "Project name: ", project.name
import gvsig
from gvsig import geom
def main(*args):
point = geom.createPoint(geom.D2, 30, 10)
print "Point: ", point
project = gvsig.currentProject()
print "Project name: ", project.name
4. BUILDING GEOMETRIES
Geometries can be built through the use of their constructors, which is
the usual way to create geometries programmatically:
# build 2D geometries by constructor
# simple geometries
# point
point = geom.createPoint(geom.D2, 30, 10)
print point
# line
line = geom.createLine(geom.D2, [(30,10), (10,30), (20,40), (40,40)])
print line.convertToWKT()
# but also using points already created
line = geom.createLine(geom.D2, [point, (10,30), (20,40), (40,40)])
print line.convertToWKT()
# polygon
polygon = geom.createPolygon(geom.D2, [[35,10],[10,20],[15,40],[45,45],[35,10]])
print polygon.convertToWKT()
5. BUILDING GEOMETRIES
The same applies to the multi-geometries:
# multi-geometries
# multipoint
p1 = geom.createPoint(geom.D2, 10,40)
p2 = geom.createPoint(geom.D2, 40,30)
p3 = geom.createPoint(geom.D2, 20,20)
p4 = geom.createPoint(geom.D2, 30,10)
multiPoint = geom.createMultiPoint(points=[p1, p2, p3, p4])
print multiPoint.convertToWKT()
# multiline
l1 = geom.createLine(geom.D2, [(10,10),(20,20),(10,40)])
l2 = geom.createLine(geom.D2, [(40,40),(30,30),(40,20),(30,10)])
multiline = geom.createMultiLine()
multiline.addCurve(l1)
multiline.addCurve(l2)
# for multipolygons the same approach as with lines can be used
6. BUILDING GEOMETRIES
Also the well known text (WKT) representation can be used:
# build geometries by wkt
# simple
g = geom.createGeometryFromWKT("POINT (30 10)")
print "POINT: " + g.convertToWKT()
g = geom.createGeometryFromWKT("LINESTRING (30 10, 10 30, 20 40, 40 40)")
print "LINESTRING: " + g.convertToWKT()
g = geom.createGeometryFromWKT("POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))")
print "POLYGON: " + g.convertToWKT()
g = geom.createGeometryFromWKT("POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10), " +
"(20 30, 35 35, 30 20, 20 30))")
print "POLYGON: " + g.convertToWKT()
g = geom.createGeometryFromWKT("MULTIPOINT (10 40, 40 30, 20 20, 30 10)")
# multi
print "MULTIPOINT: " + g.convertToWKT()
g = geom.createGeometryFromWKT("MULTILINESTRING ((10 10, 20 20, 10 40), " +
"(40 40, 30 30, 40 20, 30 10))")
print "MULTILINESTRING: " + g.convertToWKT()
g = geom.createGeometryFromWKT("MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), " +
"((15 5, 40 10, 10 20, 5 10, 15 5)))")
print "MULTIPOLYGON: " + g.convertToWKT()
7. BUILDING GEOMETRIES
If the geometries are 2D simplified constructors are available:
# for 2D geometries a simpler constructor can be used
# point
point = geom.createPoint2D(30, 10)
print point
# as opposed to a 4D point
point3DM = geom.createPoint(geom.D3M,30,10,1005,23)
print point3DM.convertToWKT()
# line
line = geom.createLine2D([(30,10), (10,30), (20,40), (40,40)])
print line.convertToWKT()
# polygon
polygon = geom.createPolygon2D([[35,10],[10,20],[15,40],[45,45],[35,10]])
print polygon.convertToWKT()
8. A TEST SET OF GEOMETRIES TO USE AS REFERENCE
To better explain the various functions and predicates we will start by
creating a set of geometries on which to apply the operations.
You are now able to create the following points, line and polygons:
0
5
0 5
g1
g5
g2
g3
g4
g6
9. BUILDING GEOMETRIES
In order to visualize intermediate results we will create a view document
(the map view) and make it active. Geometries will be loaded on top of it.
Let's create the example dataset, using wildcards for imports in order to
have a more readable code:
from gvsig import *
from gvsig.geom import *
def main(*args):
# build the example dataset
g1 = createGeometryFromWKT("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))")
g2 = createGeometryFromWKT("POLYGON ((5 0, 5 2, 7 2, 7 0, 5 0))")
g3 = createGeometryFromWKT("POINT (4 1)")
g4 = createGeometryFromWKT("POINT (5 4)")
g5 = createGeometryFromWKT("LINESTRING (1 0, 1 6)")
g6 = createGeometryFromWKT("POLYGON ((3 3, 3 6, 6 6, 6 3, 3 3))")
And now that it is build, how do we check if it is correct?
10. BUILDING GEOMETRIES
We can exploit the map view to check geometries:
# get the current active view
view = currentView()
# create an envelope containing our
# geometries
bbox = createEnvelope([-4,-4],[10,10])
# zoom to the envelope
view.centerView(bbox)
# get the view's graphics layer and
# draw the geometries
gr = view.getGraphicsLayer()
gr.clearAllGraphics()
c1 = getColorFromRGB(255, 0, 0, 128)
g1Sym = simplePolygonSymbol(c1)
idG1 = gr.addSymbol(g1Sym)
gr.addGraphic("g1", g1, idG1, "g1")
c2 = getColorFromRGB(255, 255, 0, 128)
g2Sym = simplePolygonSymbol(c2)
idG2 = gr.addSymbol(g2Sym)
gr.addGraphic("g2", g2, idG2, "g2")
c3 = getColorFromRGB(0, 255, 0, 128)
g3Sym = simplePointSymbol(c3)
g3Sym.setSize(10)
idG3 = gr.addSymbol(g3Sym)
gr.addGraphic("g3", g3, idG3, "g3")
c4 = getColorFromRGB(0, 255, 0, 128)
g4Sym = simplePointSymbol(c4)
g4Sym.setSize(10)
idG4 = gr.addSymbol(g4Sym)
gr.addGraphic("g4", g4, idG4, "g4")
c5 = getColorFromRGB(0, 255, 255, 128)
g5Sym = simpleLineSymbol(c5)
g5Sym.setLineWidth(3)
idG5 = gr.addSymbol(g5Sym)
gr.addGraphic("g5", g5, idG5, "g5")
c6 = getColorFromRGB(0, 0, 255, 128)
g6Sym = simplePolygonSymbol(c6)
idG6 = gr.addSymbol(g6Sym)
gr.addGraphic("g6", g6, idG6, "g6")
# refresh the view
view.getMapContext().invalidate()
11. BUILDING GEOMETRIES
The graphics layer is a layer that can be used to draw on top of the map
layers. If we have the countries layer loaded, and execute the script, we
should see some shapes appear near the equator (this is true only if the
view has been created with CRS EPSG:4326, which is the default for the
new map views) :
12. PREDICATES
INTERSECTS
Let's see which geometries intersect with g1 and print the result:
print g1.intersects(g2) # True
print g1.intersects(g3) # True
print g1.intersects(g4) # True
print g1.intersects(g5) # True
print g1.intersects(g6) # True
Note that geometries that touch (like g1 and g2) also intersect.
TOUCHES
Let's see which geometries touch with g1 and print the result:
print g1.touches(g2) # True
print g1.touches(g3) # False
print g1.touches(g4) # True
print g1.touches(g5) # False
print g1.touches(g6) # False
13. PREDICATES
CONTAINS
Let's see which geometries are contained in g1 and print the result:
print g1.contains(g2) # False
print g1.contains(g3) # True
print g1.contains(g4) # False
print g1.contains(g5) # False
print g1.contains(g6) # False
Mind that a point on the border is not contained, so only g3 is contained.
This can be solved through the covers predicate.
COVERS
print g1.covers(g2) # False
print g1.covers(g3) # True
print g1.covers(g4) # True
print g1.covers(g5) # False
print g1.covers(g6) # False
14. FUNCTIONS: INTERSECTION
Let's see which geometries are contained in g1 and print the result:
# the intersection of polygons returns a polygon
g1_inter_g6 = g1.intersection(g6)
print g1_inter_g6
# the intersection of touching polygons returns a line
print g1.intersection(g2)
# the intersection of a polygon with a point is a point
print g1.intersection(g3)
# the intersection of a polygon with a line is a point
print g1.intersection(g5)
# view the intersection of g1 and g6
gr.clearAllGraphics()
c = getColorFromRGB(255, 0, 0, 128)
gSym = simplePolygonSymbol(c)
idG = gr.addSymbol(gSym)
gr.addGraphic("g", g1, idG, "g")
c = getColorFromRGB(0, 0, 255, 128)
gSym = simplePolygonSymbol(c)
idG = gr.addSymbol(gSym)
gr.addGraphic("g", g6, idG, "g")
c = getColorFromRGB(0, 255, 0, 128)
gSym = simplePolygonSymbol(c)
idG = gr.addSymbol(gSym)
gr.addGraphic("g", g1_inter_g6, idG, "g")
0
5
0 5
g1
g5
g2
g3
g4
g6
15. FUNCTIONS: SYMDIFFERENCE
What is the resulting geometry of the symdifference (portions not shared)
of different geometry types?
# the symDifference of intersecting polygons returns a multipolygon
g1jts = g1.getJTS()
symDiff16 = g1jts.symDifference(g6.getJTS())
print symDiff16
# but the symDifference of touching polygons returns the polygons union
print g1jts.symDifference(g2.getJTS())
# the symDifference of a polygon with a contained point returns the original polygon
print g1jts.symDifference(g3.getJTS())
# the symDifference of a polygon with a line is a hybrid collection (line + polygon)
print g1jts.symDifference(g5.getJTS())
# to get back a gvsig geometry
multiPolygon2d = createGeometryFromWKT(symDiff16)
print multiPolygon2d
0
5
0 5
g1
g5
g2
g3
g4
g6
Note that the symDifference is not implemented
in gvSIG. In that case it is possible to access
directly the JTS geometries and use the function
on those. Afterwards through the WKT
definition they can be converted back.
16. FUNCTIONS: UNION
What is the resulting geometry of the union of different geometry types?
# the union of intersecting polygons returns a polygon
print g1.union(g6).convertToWKT()
# same as the union of touching polygons
print g1.union(g2).convertToWKT()
# the union of a polygon with a contained point returns the original polygon
print g1.union(g3).convertToWKT()
# the union of a polygon with a line doesn't result in a valid geometry
print g1.union(g5)
The following shows the union of
polygons g1 and g6:
0
5
0 5
g1
g5
g2
g3
g4
g6
17. FUNCTIONS: DIFFERENCE
The difference of geometries obviously depends on the calling object:
# this returns g1 minus the overlapping part of g6
print g1.difference(g6).convertToWKT()
# while this returns g6 minus the overlapping part of g1
print g6.difference(g1).convertToWKT()
# in the case of difference with lines, the result is the original polygon
# with additional points in the intersections
print g1.difference(g5).convertToWKT()
# the difference of polygon and point is the original polygon
print g1.difference(g3).convertToWKT()
The following shows the difference
of polygons g1 and g6:
0
5
0 5
g1
g5
g2
g3
g4
g6
18. JTS GEOMETRIES
Some functions are not supported in the gvSIG scripting API. But it is
possible to access the underlying JTS geometries.
JTS stands for , the most well known spatial
predicates and geometry processing library available in the open source
panorama.
This is how to get back and forth between gvSIG and JTS:
Java Topology Suite
# create a gvsig geometry
polygonGvsig = createGeometryFromWKT("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))")
# get the JTS geometry
polygonJTS = polygonGvsig.getJTS()
# do some advanced processing... for example get the centroid
centroidJTS = polygonJTS.getCentroid();
# print the gvsig geometry
print "gvSIG polygon WKT: " + polygonGvsig.convertToWKT()
# print the JTS geometry
print "JTS polygon WKT: %s" % polygonJTS
# get back to a gvsig geometry
centroidGvsig = createGeometryFromWKT(centroidJTS)
print "gvSIG centroid WKT: " + centroidGvsig.convertToWKT()
19. FUNCTIONS: BUFFER
Creating a buffer around a geometry always generates a polygon
geometry. The behavior can be tweaked, depending on the geometry type:
from com.vividsolutions.jts.operation.buffer import BufferParameters
g3jts = g3.getJTS()
g5jts = g5.getJTS()
# the buffer of a point
b1 = g3jts.buffer(1.0)
# the buffer of a point with few quandrant segments
b2 = g3jts.buffer(1.0, 1)
# round end cap style, few points
b3 = g5jts.buffer(1.0, 2, BufferParameters.CAP_ROUND)
# round end cap style, more points
b4 = g5jts.buffer(1.0, 10, BufferParameters.CAP_ROUND)
# square end cap style
b5 = g5jts.buffer(1.0, -1, BufferParameters.CAP_SQUARE)
# flat end cap style
b6 = g5jts.buffer(1.0, -1, BufferParameters.CAP_FLAT)
geoms = [b1, b2, b3, b4, b5, b6]
gr.clearAllGraphics()
c = getColorFromRGB(255, 0, 0, 80)
gSym = simplePolygonSymbol(c)
idG = gr.addSymbol(gSym)
for g in geoms:
gr.addGraphic("g", createGeometryFromWKT(g.toText()), idG, "g")
JTS gives more control
about buffering, it just
requires us to import the
BufferParameters module.
20. FUNCTIONS IN JTS: CONVEXHULL
Some functions are not available through the gvSIG API. We saw this
already in the buffer functions and symDifference. Another example is the
convex hull.
So let's try working the JTS way. First, we create the geometries as JTS
geometries:
# build the example dataset
g1 = createGeometryFromWKT("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))").getJTS()
g2 = createGeometryFromWKT("POLYGON ((5 0, 5 2, 7 2, 7 0, 5 0))").getJTS()
g3 = createGeometryFromWKT("POINT (4 1)").getJTS()
g4 = createGeometryFromWKT("POINT (5 4)").getJTS()
g5 = createGeometryFromWKT("LINESTRING (1 0, 1 6)").getJTS()
g6 = createGeometryFromWKT("POLYGON ((3 3, 3 6, 6 6, 6 3, 3 3))").getJTS()
geoms = [g1, g2, g3, g4, g5, g6]
21. FUNCTIONS IN JTS: CONVEXHULL
Once we have the geometries, we need to create a collection of
geometries, which will require us to import a few classes.
from com.vividsolutions.jts.geom import GeometryCollection, GeometryFactory
gc = GeometryCollection(geoms, GeometryFactory())
with a GeometryCollection it is very simple to create a convex hull and
visualize it:
convexHull = gc.convexHull()
gr = view.getGraphicsLayer()
gr.clearAllGraphics()
c = getColorFromRGB(255, 0, 0, 80)
gSym = simplePolygonSymbol(c)
idG = gr.addSymbol(gSym)
for g in geoms:
gr.addGraphic("g", createGeometryFromWKT(convexHull.toText()), idG, "g")
view.getMapContext().invalidate()
22. FUNCTIONS IN JTS: TRANSFORMATIONS
Also for operations of translation, scaling and rotation we can make use of
the JTS capabilities.
It all boils down to a single class, the AffineTransformation, which we can
import as:
from com.vividsolutions.jts.geom.util import AffineTransformation as AT
import math # used for conversion to radians
It has direct methods to access to the different transformations:
# create the jts polygon
square = createGeometryFromWKT("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))")
square = square.getJTS()
# this is how affine transformations are created
scaleAffineTransformation = AT.scaleInstance(4,4)
translationAffineTransformation = AT.translationInstance(2,2)
rotationAffineTransformation = AT.rotationInstance(math.radians(45))
shearAffineTransformation = AT.shearInstance(0.75, 0)
# this is how transformations are applied to geometries
scaledSquare = scaleAffineTransformation.transform(square)
print "Original: %s" % square
print "Scaled: %s" % scaledSquare
23. FUNCTIONS IN JTS: TRANSFORMATIONSLet's see how all the transformations work:
square = createGeometryFromWKT("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))").getJTS()
# scale the square by 4 times
squareLarge = AT.scaleInstance(4,4).transform(square)
# move it by x, y units
squareTranslate = AT.translationInstance(2,2).transform(square)
# move it and then rotate it by 45 degrees
squareTranslateRotate = AT.rotationInstance(math.radians(45)
).transform(squareTranslate)
# realize that the order of things are there for a reason
squareRotate = AT.rotationInstance(math.radians(45)).transform(square)
squareRotateTranslate = AT.translationInstance(2,2).transform(squareRotate)
# rotate around a defined center
squareTranslateRotateCenter = AT.rotationInstance(math.radians(45),
2.5, 2.5).transform(squareTranslate)
# shear the square
squareShear = AT.shearInstance(0.75,0).transform(square)
geoms = [square, squareLarge, squareTranslate,
squareTranslateRotate, squareRotateTranslate,
squareTranslateRotateCenter, squareShear]
view = currentView()
gr = view.getGraphicsLayer()
gr.clearAllGraphics()
c = getColorFromRGB(255, 0, 0, 80)
gSym = simplePolygonSymbol(c)
idG = gr.addSymbol(gSym)
for g in geoms:
gr.addGraphic("g", createGeometryFromWKT(g), idG, "g")
view.mapContext.invalidate()
24. PROJECTIONS
Geometries can be reprojected directly using the crs objects. These can
be created in several ways. The simplest is through the use of the EPSG
code:
from gvsig import *
from gvsig.geom import *
def main(*args):
# create the source and destination crs
# usign the EPSG codes
crs1 = getCRS('EPSG:4326')
crs2 = getCRS('EPSG:32632')
# get the transformation object
transform = crs1.getCT(crs2)
# reproject a point from 4326 to 32632
point = createPoint2D(11, 46)
print "Original point in lat/long: " + str(point)
point.reProject(transform)
print "and the reprojected point: " + str(point)
# get the WKT representation of the crs object
print crs2.getWKT()
25. PROJECTIONS
Sometimes it is necessary to modify the crs definition. If you load the data
in a GIS and they do not appear where they should, it might be a problem
of the false easting, but also the units might not be right.
PROJCS["WGS 84 / UTM zone 32N",
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
UNIT["degree", 0.017453292519943295],
AXIS["Longitude", EAST],
AXIS["Latitude", NORTH],
AUTHORITY["EPSG","4326"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["central_meridian", 9.0],
PARAMETER["latitude_of_origin", 0.0],
PARAMETER["scale_factor", 0.9996],
PARAMETER["false_easting", 500000.0], # sometimes a different false easting is used
PARAMETER["false_northing", 0.0],
UNIT["m", 1.0], # sometimes millimeters are used, with scale 0.001
AXIS["Easting", EAST],
AXIS["Northing", NORTH],
AUTHORITY["EPSG","32632"]]
26. PROJECTIONS
Converting from WKT to a crs is only an import statement away:
from org.gvsig.fmap.crs import CRSFactory
factory = CRSFactory.getCRSFactory()
prjWkt = """PROJCS["WGS 84 / UTM zone 32N",
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
UNIT["degree", 0.017453292519943295],
AXIS["Longitude", EAST],
AXIS["Latitude", NORTH],
AUTHORITY["EPSG","4326"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["central_meridian", 9.0],
PARAMETER["latitude_of_origin", 0.0],
PARAMETER["scale_factor", 0.9996],
PARAMETER["false_easting", 500000.0],
PARAMETER["false_northing", 0.0],
UNIT["m", 1.0],
AXIS["Easting", EAST],
AXIS["Northing", NORTH],
AUTHORITY["EPSG","32632"]]
"""
crs2 = factory.get("wkt", prjWkt)
27. CREATING THE FIRST SHAPEFILE
When we talk about "writing GIS stuff", we are usually talking about
creating a shapefile. Let's see the steps to create our first shapefile:
# place here the usual imports and main definition
# create the blueprint of the shapefile
schema = createFeatureType()
schema.append("name", "STRING", 20)
schema.append("GEOMETRY", "GEOMETRY")
schema.get("GEOMETRY").setGeometryType(POINT, D2)
# create the shapefile
shape = createShape(schema, CRS="EPSG:4326")
# edit the shapefile and add features
shape.edit()
point1 = createPoint2D(-122.42, 37.78)
shape.append(name='San Francisco', GEOMETRY=point1)
point2 = createPoint2D(-73.98, 40.47)
shape.append(name='New York', GEOMETRY=point2)
shape.commit()
# add the shapefile as layer to the view
currentView().addLayer(shape)
# print the path of the shapefile
print "path: ", shape.getDataStore().getFullName()
28. CREATING THE FIRST SHAPEFILE
Let's see a slightly more complex example:
# place here the usual imports and main definition
# create the blueprint of the shapefile
schema = createFeatureType()
schema.append("name", "STRING", 20)
schema.append("population", "INTEGER", 4)
schema.append("lat", "DOUBLE", 8)
schema.append("lon", "DOUBLE", 8)
schema.append("GEOMETRY", "GEOMETRY")
schema.get("GEOMETRY").setGeometryType(POINT, D2)
# create the shapefile
shape = createShape(schema, prefixname="complex", CRS="EPSG:4326")
# edit the shapefile and add features
shape.edit()
point = createPoint2D(-73.98, 40.47)
shape.append(name='New York',population=19040000,
lat=40.749979064, lon=-73.9800169288,
GEOMETRY=point)
shape.commit()
# add the shapefile as layer to the view
currentView().addLayer(shape)
29. CREATING THE FIRST SHAPEFILE
Once the scripts are run, you should see the following in the map view:
30. BUILD A VIEW AND LOAD DATALet's start from scratch and go through the whole process again.
1) imports
# the most used imports are...
from gvsig import *
from gvsig.geom import *
2) create the data blueprint
# define a working crs
epsg = "EPSG:4326"
# create the blueprint of the shapefile
schema = createFeatureType()
schema.append("name", "STRING", 20)
schema.append("GEOMETRY", "GEOMETRY")
schema.get("GEOMETRY").setGeometryType(POINT, D2)
# create the shapefile
shape = createShape(schema, prefixname="simple", CRS=epsg)
# edit the shapefile and add a feature
shape.edit()
x = -122.42
y = 37.78
point = createPoint2D(x, y)
shape.append(name='New York', GEOMETRY=point)
shape.commit()
3) create some data
31. BUILD A VIEW AND LOAD DATA
3) load an external shapefile into a layer
# first add some background shp
background = loadLayer("Shape",
shpFile="/home/hydrologis/data/natural_earth/ne_10m_admin_0_countries.shp",
CRS=epsg)
4) create a new map view with name and crs and load the layers into it
# zoom to the feature
boxDelta = 0.05
newview.centerView(createEnvelope([x-boxDelta,y-boxDelta],[x+boxDelta,y+boxDelta]))
5) zoom to the created feature
# create a new view and set its projection
newview = currentProject().createView("Example view")
newview.setProjection(getCRS(epsg))
newview.addLayer(background)
newview.addLayer(shape)
newview.showWindow()
32. INVESTIGATING A VECTOR LAYER
Let's see how accessing views and layers is done...
# define view and layer to investigate
viewName = "Example view"
layerName = "ne_10m_admin_0_countries"
# get the view and layers
view = currentProject().getView(viewName)
if view is None:
print "ERROR: view ", viewName, " is not available in the current project"
return
layers = view.getLayers()
# pick the right layer
layerToRead = None
print "Iterate through the layers: "
for layer in layers:
print "tLayer: ", layer.getName()
if layer.getName() == layerName:
layerToRead = layer
33. INVESTIGATING A VECTOR LAYER
...and what about the layer's schema content?
# investigate its schema
if layerToRead is not None:
schema = layerToRead.getSchema()
# show the attributes
attrSchema = schema.getAttrNames()
print "nnSchema attr: ", attrSchema
print "nnFields description"
for field in schema:
print " Name: ", field.getName(),
print " tDataTypeName: ", field.getDataTypeName(),
print " tPrecision: ", field.getPrecision(),
print " tSize: ", field.getSize()
if field.getDataTypeName() == 'Geometry':
geomType = field.getGeomType()
print " tGeom Name: ", geomType.getName()
print " tGeom FullName: ", geomType.getFullName()
print " tDimension: ", geomType.getDimension()
else:
print "ERROR: layer ", layerName, " is not available in view: ", viewName
34. INVESTIGATING A VECTOR LAYER
...and what about the data?
# define view and layer to investigate
viewName = "Example view"
layerName = "ne_10m_admin_0_countries"
# get the view
view = currentProject().getView(viewName)
# get the layer by its name
layer = view.getLayer(layerName)
# get the features set
features = layer.features()
print "Features available in layer: ", features.getSize()
# loop through the features
for feature in features:
print feature.getValues()
35. INVESTIGATING A VECTOR LAYER
How to access the feature attributes:
layer = currentView().getLayer("ne_10m_admin_0_countries")
features = layer.features()
for feature in features:
print "The country ", feature.NAME , " has ", feature.get("POP_EST"), " inhabitants."
It is possible to get only the features selected by the user with:
features = layer.getSelection()
36. INVESTIGATING A VECTOR LAYER
Features can be extracted from a layer by means of filters and sorted by a
given attribute.
Extract the population less than 1000 and major than 0, in ascending
order:
layer = currentView().getLayer("ne_10m_admin_0_countries")
features = layer.features("POP_EST < 1000 and POP_EST > 0", sortby="POP_EST", asc=True)
for feature in features:
print "The country ", feature.NAME , " has ", feature.get("POP_EST"), " inhabitants."
And here a simple way to get the country with largest population:
features = layer.features(sortby="POP_EST", asc=False)
for feature in features:
print "The country with most inhabitants is ", feature.NAME , " with ", feature.POP_EST
break
37. SELECT FEATURES IN A VECTOR LAYER
Features can be selected or deselected in a view/layer using the selection
object of the layer.
Example: select an print the countries with more than 1E9 inhabitants
# select features
selection = layer.getSelection()
selection.deselectAll()
features = layer.features("POP_EST > 1E9")
for feature in features:
selection.select(feature)
print "The country ", feature.NAME , " has ", feature.get("POP_EST"), " inhabitants."
selection.deselect(feature)
Single features can be removed from the selection through:
38. CREATE A COUNTRIES CENTROIDS LAYER
It is no rocket science to apply all we have seen until this point to create a
shapefile containing the centroids of the countries.
All you need to know is that the geometry has a method that extracts the
centroid for you: centroid
countriesLayer = currentView().getLayer("ne_10m_admin_0_countries")
shpPath = "/home/hydrologis/TMP/centroids.shp"
epsg = "EPSG:4326"
schema = createFeatureType()
schema.append("name", "STRING", 20)
schema.append("GEOMETRY", "GEOMETRY")
schema.get("GEOMETRY").setGeometryType(POINT, D2)
shape = createShape(schema, filename=shpPath, CRS=epsg)
shape.edit()
countries = countriesLayer.features()
for country in countries:
geometry = country.GEOMETRY
centroid = geometry.centroid()
shape.append(name=country.NAME, GEOMETRY=centroid)
shape.commit()
currentView().addLayer(shape)
39. STYLING YOUR LAYERS
Let's see how styling of the different feature/geometries types is done.
To do so, we first load a layer of each type: point, line, polygon
basePath = "/home/hydrologis/data/natural_earth/"
countries = basePath + "ne_10m_admin_0_countries.shp"
places = basePath + "ne_10m_populated_places.shp"
roads = basePath + "ne_10m_roads.shp"
# create a new view and set its projection
epsg = "EPSG:4326"
newview = currentProject().createView("Example view")
newview.setProjection(getCRS(epsg))
newview.showWindow()
countriesLayer = loadLayer("Shape", shpFile=countries, CRS=epsg)
placesLayer = loadLayer("Shape", shpFile=places, CRS=epsg)
roadsLayer = loadLayer("Shape", shpFile=roads, CRS=epsg)
newview.addLayer(countriesLayer)
newview.addLayer(roadsLayer)
newview.addLayer(placesLayer)
40. SIMPLE STYLING OF POINTS
Styling of shapes is done by creating a symbol and set it in the layer's
legend:
# get the layer's legend
pointsLegend = placesLayer.getLegend()
# create and modify the symbol
pointSymbol = simplePointSymbol()
pointSymbol.setSize(15);
pointSymbol.setColor(getColorFromRGB(0,0,255,128)); # blue
pointSymbol.setOutlined(True);
pointSymbol.setOutlineColor(getColorFromRGB(255,255,0)); # yellow
pointSymbol.setOutlineSize(1);
# circle = 0; square = 1; cross = 2; diamond = 3; X = 4; triangle = 5; star = 6; vertical line = 7;
pointSymbol.setStyle(5);
# set symbol to legend and legend to layer
pointsLegend.setDefaultSymbol(pointSymbol)
placesLayer.setLegend(pointsLegend)
# then zoom
x = 11
y = 46
boxDelta = 2
newview.centerView(createEnvelope([x-boxDelta,y-boxDelta],[x+boxDelta,y+boxDelta]))
41. SIMPLE STYLING OF LINES
Styling of lines is similar to that of points:
lineSymbol = simpleLineSymbol()
lineSymbol.setLineWidth(2);
lineSymbol.setLineColor(getColorFromRGB(128,128,128));
linesLegend = roadsLayer.getLegend()
linesLegend.setDefaultSymbol(lineSymbol)
roadsLayer.setLegend(linesLegend)
While styling of polygons is done in two steps: outline and fill
# first create the outline symbol and style it
polygonOutlineSymbol = simpleLineSymbol()
polygonOutlineSymbol.setLineWidth(2)
polygonOutlineSymbol.setLineColor(getColorFromRGB(255,0,0))
# then the fill
polygonFillSymbol = simplePolygonSymbol()
polygonFillSymbol.setOutline(polygonOutlineSymbol)
polygonFillSymbol.setFillColor(getColorFromRGB(255,0,0,70))
polygonLegend = countriesLayer.getLegend()
polygonLegend.setDefaultSymbol(polygonFillSymbol)
countriesLayer.setLegend(polygonLegend)
42. SIMPLE STYLING OF POLYGONS
Once run, the script should create a new view that looks like:
43. HOW TO REUSE CODE?
Often pieces of code are repeated for different variables and could be
reused. This can be easily done with functions. Functions are defined
using the "def" construct.
The following is a simple example that shows how one can use a function
to set the symbol in the layer's legend, using one line instead of repeating
three lines for each layer:
def setSymbolInLayer(symbol, layer):
legend = layer.getLegend()
legend.setDefaultSymbol(symbol)
layer.setLegend(legend)
44. SIMPLE STYLING OF POLYGONS
It is also possible to style using a color-ramp based on an attribute of the
layer. In that case it will be necessary to add one import:
from org.gvsig.symbology.fmap.mapcontext.rendering.legend.impl import VectorialIntervalLegend
Then, using the interval legend, it is quite straight forward:
# create an interval legend
intervalLegend = VectorialIntervalLegend(POLYGON)
intervalLegend.setStartColor(Color.gray)
intervalLegend.setEndColor(Color.red)
intervalLegend.setIntervalType(1) # natural = 1, quantile = 2
store = countriesLayer.getFeatureStore()
# calculate the intervals using the field name and the desired number of intervals
intervals = intervalLegend.calculateIntervals(store, "POP_EST", 5, POLYGON)
intervalLegend.setIntervals(intervals)
countriesLayer.setLegend(intervalLegend)
45. SIMPLE STYLING OF POLYGONS
...which the should look like a choropleth map:
46. <license>
This work is released under Creative Commons Attribution Share Alike (CC-BY-SA).
</license>
<sources>
Much of the knowledge needed to create this training material has been produced
by the sparkling knights of the
<a href="http:www.osgeo.org">Osgeo</a>,
<a href="http://tsusiatsoftware.net/">JTS</a>,
<a href="http://www.jgrasstools.org">JGrasstools</a> and
<a href="http:www.gvsig.org">gvSIG</a> communities.
Their websites are filled up with learning material that can be use to grow
knowledge beyond the boundaries of this set of tutorials.
Another essential source has been the Wikipedia project.
</sources>
<acknowledgments>
Particular thanks go to those friends that directly or indirectly helped out in
the creation and review of this series of handbooks.
Thanks to Antonio Falciano for proofreading the course and Oscar Martinez for the
documentation about gvSIG scripting.
</acknowledgments>
<footer>
This tutorial is brought to you by <a href="http:www.hydrologis.com">HydroloGIS</a>.
<footer>