46
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

PART 4: GEOGRAPHIC SCRIPTING

Embed Size (px)

Citation preview

Page 1: PART 4: GEOGRAPHIC SCRIPTING

GEOGRAPHIC SCRIPTING IN GVSIGHALFWAY BETWEEN USER AND DEVELOPER

Geoinformation Research Group,Department of Geography

University of Potsdam

21-25 November 2016

Andrea Antonello

PART 4: GEOGRAPHIC SCRIPTING

Page 2: PART 4: GEOGRAPHIC SCRIPTING

GEO-SCRIPTING IN GVSIGIn gvSIG geographic scripting is done using the Python programminglanguage syntax (the actual engine is called Jython).

As proposed when creating a new script, most geo-related operations aredone using the gvsig module. Also, a main method needs to be defined, forthe script to work:

import gvsig

def main(*args):

# do some scripting here

Page 3: PART 4: GEOGRAPHIC SCRIPTING

GEO-SCRIPTING IN GVSIGgvSIG imports will be used in several ways throughout the course, mostlybecause 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 gvsigfrom gvsig import geom

def main(*args):

point = geom.createPoint(geom.D2, 30, 10) print "Point: ", point

project = gvsig.currentProject() print "Project name: ", project.name

Page 4: PART 4: GEOGRAPHIC SCRIPTING

BUILDING GEOMETRIESGeometries can be built through the use of their constructors, which isthe 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()

Page 5: PART 4: GEOGRAPHIC SCRIPTING

BUILDING GEOMETRIESThe 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

Page 6: PART 4: GEOGRAPHIC SCRIPTING

BUILDING GEOMETRIESAlso 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()

Page 7: PART 4: GEOGRAPHIC SCRIPTING

BUILDING GEOMETRIESIf 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()

Page 8: PART 4: GEOGRAPHIC SCRIPTING

A TEST 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:

0

5

0 5

g1

g5g2

g3

g4

g6

Page 9: PART 4: GEOGRAPHIC SCRIPTING

BUILDING GEOMETRIESIn 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 tohave 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?

Page 10: PART 4: GEOGRAPHIC SCRIPTING

BUILDING GEOMETRIESWe 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()

Page 11: PART 4: GEOGRAPHIC SCRIPTING

BUILDING GEOMETRIESThe graphics layer is a layer that can be used to draw on top of the maplayers. If we have the countries layer loaded, and execute the script, weshould see some shapes appear near the equator (this is true only if theview has been created with CRS EPSG:4326, which is the default for thenew map views) :

Page 12: PART 4: GEOGRAPHIC SCRIPTING

PREDICATESINTERSECTS

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

Page 13: PART 4: GEOGRAPHIC SCRIPTING

PREDICATESCONTAINS

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

Page 14: PART 4: GEOGRAPHIC SCRIPTING

FUNCTIONS: INTERSECTIONLet'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

g5g2

g3

g4

g6

Page 15: PART 4: GEOGRAPHIC SCRIPTING

FUNCTIONS: SYMDIFFERENCEWhat 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

g5g2

g3

g4

g6

Note that the symDifference is not implementedin gvSIG. In that case it is possible to accessdirectly the JTS geometries and use the functionon those. Afterwards through the WKTdefinition they can be converted back.

Page 16: PART 4: GEOGRAPHIC SCRIPTING

FUNCTIONS: UNIONWhat 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 ofpolygons g1 and g6:

0

5

0 5

g1

g5g2

g3

g4

g6

Page 17: PART 4: GEOGRAPHIC SCRIPTING

FUNCTIONS: DIFFERENCEThe 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 differenceof polygons g1 and g6:

0

5

0 5

g1

g5g2

g3

g4

g6

Page 18: PART 4: GEOGRAPHIC SCRIPTING

JTS GEOMETRIESSome functions are not supported in the gvSIG scripting API. But it ispossible to access the underlying JTS geometries.

JTS stands for , the most well known spatialpredicates and geometry processing library available in the open sourcepanorama.

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

Page 19: PART 4: GEOGRAPHIC SCRIPTING

FUNCTIONS: BUFFERCreating a buffer around a geometry always generates a polygongeometry. 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 controlabout buffering, it justrequires us to import theBufferParameters module.

Page 20: PART 4: GEOGRAPHIC SCRIPTING

FUNCTIONS IN JTS: CONVEXHULLSome functions are not available through the gvSIG API. We saw thisalready in the buffer functions and symDifference. Another example is theconvex hull.

So let's try working the JTS way. First, we create the geometries as JTSgeometries:

# 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]

Page 21: PART 4: GEOGRAPHIC SCRIPTING

FUNCTIONS IN JTS: CONVEXHULLOnce we have the geometries, we need to create a collection ofgeometries, 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 andvisualize 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()

Page 22: PART 4: GEOGRAPHIC SCRIPTING

FUNCTIONS IN JTS: TRANSFORMATIONSAlso for operations of translation, scaling and rotation we can make use ofthe JTS capabilities.

It all boils down to a single class, the AffineTransformation, which we canimport as:

from com.vividsolutions.jts.geom.util import AffineTransformation as ATimport 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

Page 23: PART 4: GEOGRAPHIC SCRIPTING

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

Page 24: PART 4: GEOGRAPHIC SCRIPTING

PROJECTIONSGeometries can be reprojected directly using the crs objects. These canbe created in several ways. The simplest is through the use of the EPSGcode:

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

Page 25: PART 4: GEOGRAPHIC SCRIPTING

PROJECTIONSSometimes it is necessary to modify the crs definition. If you load the datain a GIS and they do not appear where they should, it might be a problemof 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"]]

Page 26: PART 4: GEOGRAPHIC SCRIPTING

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

Page 27: PART 4: GEOGRAPHIC SCRIPTING

CREATING THE FIRST SHAPEFILEWhen we talk about "writing GIS stuff", we are usually talking aboutcreating 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()

Page 28: PART 4: GEOGRAPHIC SCRIPTING

CREATING THE FIRST SHAPEFILELet'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)

Page 29: PART 4: GEOGRAPHIC SCRIPTING

CREATING THE FIRST SHAPEFILEOnce the scripts are run, you should see the following in the map view:

Page 30: PART 4: GEOGRAPHIC SCRIPTING

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

Page 31: PART 4: GEOGRAPHIC SCRIPTING

BUILD A VIEW AND LOAD DATA3) 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()

Page 32: PART 4: GEOGRAPHIC SCRIPTING

INVESTIGATING A VECTOR LAYERLet'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

Page 33: PART 4: GEOGRAPHIC SCRIPTING

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 "\n\nSchema attr: ", attrSchema print "\n\nFields 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

Page 34: PART 4: GEOGRAPHIC SCRIPTING

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

Page 35: PART 4: GEOGRAPHIC SCRIPTING

INVESTIGATING A VECTOR LAYERHow 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()

Page 36: PART 4: GEOGRAPHIC SCRIPTING

INVESTIGATING A VECTOR LAYERFeatures can be extracted from a layer by means of filters and sorted by agiven attribute.

Extract the population less than 1000 and major than 0, in ascendingorder:

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

Page 37: PART 4: GEOGRAPHIC SCRIPTING

SELECT FEATURES IN A VECTOR LAYERFeatures can be selected or deselected in a view/layer using the selectionobject 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:

Page 38: PART 4: GEOGRAPHIC SCRIPTING

CREATE A COUNTRIES CENTROIDS LAYERIt is no rocket science to apply all we have seen until this point to create ashapefile containing the centroids of the countries.All you need to know is that the geometry has a method that extracts thecentroid 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)

Page 39: PART 4: GEOGRAPHIC SCRIPTING

STYLING YOUR LAYERSLet'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)

Page 40: PART 4: GEOGRAPHIC SCRIPTING

SIMPLE STYLING OF POINTSStyling of shapes is done by creating a symbol and set it in the layer'slegend:

# 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]))

Page 41: PART 4: GEOGRAPHIC SCRIPTING

SIMPLE STYLING OF LINESStyling 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)

Page 42: PART 4: GEOGRAPHIC SCRIPTING

SIMPLE STYLING OF POLYGONSOnce run, the script should create a new view that looks like:

Page 43: PART 4: GEOGRAPHIC SCRIPTING

HOW TO REUSE CODE?Often pieces of code are repeated for different variables and could bereused. This can be easily done with functions. Functions are definedusing the "def" construct.

The following is a simple example that shows how one can use a functionto set the symbol in the layer's legend, using one line instead of repeatingthree lines for each layer:

def setSymbolInLayer(symbol, layer): legend = layer.getLegend() legend.setDefaultSymbol(symbol) layer.setLegend(legend)

Page 44: PART 4: GEOGRAPHIC SCRIPTING

SIMPLE STYLING OF POLYGONSIt is also possible to style using a color-ramp based on an attribute of thelayer. 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)

Page 45: PART 4: GEOGRAPHIC SCRIPTING

SIMPLE STYLING OF POLYGONS...which the should look like a choropleth map:

Page 46: PART 4: GEOGRAPHIC SCRIPTING

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