GeoScript

advertisement
GeoScript
Spatial Capabilities for Scripting Languages
Justin Deolivera and Jared Erickson
Scripting Platform for JVM
Languages
Similar API
Respect languages differences
Python GeoScript should be Pythonic
Make easy things easy, Make hard things possible
Groovy
• Groovy
o Dynamic language
o Easy for Java programmers to learn
o Closures, DSLs
o REPL, GUI Console
o Compiles to Java Byte Code
o Full access to Java libraries
• http://geoscript.org/groovy
• https://github.com/jericks/geoscript-groovy
JavaScript
Python
• Jython
o Java implementation of Python
o Jython 2.5 = CPython 2.5
o Full access to Java libraries
• http://geoscript.org/py
• https://github.com/jdeolive/geoscript-py
Scala
• Scala
o Combine functional and object-oriented
programming
o Statically typed???
o REPL
o Compiles to Java bytecode
o Full access to Java libraries
• http://geoscript.org/scala/
• https://github.com/dwins/geoscript.scala/
On the shoulders of giants...
On the shoulders of giants...
• Java Topology Suite (JTS)
o Geometry API and Algorithms
• GeoTools
o DataStores
o Features
o Coverages/Rasters
o Referencing
o Renderering
• Java Tribe Base libraries
o GeoServer
o uDig
o Geomajas
GeoScript Modules
GeoScript Modules
•
•
•
•
•
•
•
Geometry
Projection
Feature
Layer
Workspace
Style
Renderer
Geometry
• Convenient constructors
• I/O
o WKT/WKB
o JSON
o GML
• Plotting
• Transforms
Geometry
Constructors
>>> from geoscript import geom
>>> geom.Point(30, 10)
POINT(30 10)
>>> geom.LineString((30,10), (10,20), (20,40), (40,40), (30,10))
LINESTRING (30 10, 10 20, 20 40, 40 40, 30 10)
>>> geom.Polygon([(35,10), (10,20), (15,40), (45,45), (35,10)], [(20,30), (35,35), (30,20),
(20,30)])
POLYGON ((35 10, 10 20, ... 30 20, 20 30))
>>> geom.MultiLineString([(10,10), (20,20), (10,40)],
[(40,40), (30,30), (40,20), (30,10)])
MULTILINESTRING ((10 10, 20 20, ... 40 20, 30 10))
Geometry
Input/Output
>>> from geoscript import geom
>>> point = geom.Point(30, 10)
>>> geom.writeGML(point)
<gml:Point xmlns:gml="http://www.opengis.net/gml">
<gml:coord>
<gml:X>30.0</gml:X>
<gml:Y>10.0</gml:Y>
</gml:coord>
</gml:Point>
>>> geom.readJSON('{"type":"Point","coordinates":[30,10]}')
POINT(30 10)
>>> geom.writeWKB(point)
array('b', [0, 0, 0, 0, 1, 64, 62, ... 64, 36, 0, 0, 0, 0, 0, 0])
Geometry
Plotting
>>> from geoscript.render import plot
>>> from geoscript import geom
>>> poly = geom.Polygon([(35,10), (10,20), (15,40), (45,45), (35,10)], [(20,30), (35,35),
(30,20), (20,30)])
>>> plot(poly)
Geometry
Transforms
>>> from geoscript.geom import Point, transform
>>> from geoscript.render import draw
>>> c = geom.Point(0, 0).buffer(10)
>>> d = transform(c, dx=5) # translate
>>> draw([c,d])
>>> e,f = transform(c, shx=0.5), transform(c, shx=-0.5) # shear
>>> g = transform(c, sx=0.7) # scale
>>> draw([e,f,g])
Projection
• Parse/encode WKT
• Full GeoTools EPSG
database
• Re-projection
Projection
Constructors
>>> from geoscript.proj import Projection
>>> Projection('EPSG:4326')
GEOGCS["WGS 84",
DATUM["World Geodetic System 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["Geodetic longitude", EAST],
AXIS["Geodetic latitude", NORTH],
AUTHORITY["EPSG","4326"]]
>>> p = Projection('GEOGCS[..."Degree",0.017453292519943295]]')
>>> p.id
'EPSG:4326'
Projection
Reprojection
>>> from geoscript import geom, render
>>> from geoscript.proj import Projection
>>> p = Projection('epsg:4326')
>>> p.transform((-111, 45.7), 'epsg:26912')
(500000.0, 5060716.313515949)
>>> g = geom.Point(0, 0).buffer(4)
>>> g = reduce(lambda x,y:x.union(y),[geom.transform(g,dx=x,dy=y)
for x,y in [(3,0),(0,3),(-3,0),(0,-3)]])
>>> render.draw(g)
>>> render.draw(p.transform(g, 'epsg:26912'))
>>> render.draw(p.transform(g, 'epsg:3005'))
WGS 84
UTM
Albers
Data Access
• Read and Write Layers
• Query Layers using CQL
• I/O
o GeoJSON
o GML
Data Access
Workspaces
>>> from geoscript.workspace import PostGIS
>>> from geoscript import geom
>>> pg = PostGIS('spearfish')
>>> pg.layers()
['archsites', 'bugsites', 'railroads', 'restricted', 'roads', 'streams']
>>> l = pg['bugsites']
>>> l.schema
bugsites [cat: long, str1: str, the_geom: Point]
>>> l = Layer('foo')
>>> l.add([geom.Point(0,0)])
>>> l = pg.add(l)
>>> l = pg.create('bar', [('geom',geom.Point),('name',str)])
Data Access
Layers
>>> from geoscript.layer import Shapefile
>>> shp = Shapefile('states.shp')
>>> shp.count()
49
>>> shp.count('CARPOOL/PERSONS > 0.06')
26
>>> shp.bounds()
(-124.731422, 24.955967, -66.969849, 49.371735, EPSG:4326)
>>> shp.bounds("STATE_NAME = 'Colorado'")
(-109.055199, 36.988972, -102.036758, 41.00341, EPSG:4326)
>>> for f in shp.features():
... print '%s has %f people' % (f['STATE_NAME'],f['PERSONS'])
>>> for f in shp.features('CARPOOL/PERSONS > 0.06'):
... print '%s likes to carpool' % f['STATE_NAME']
Data Access
Features
>>> from geoscript.feature import Schema, Feature
>>> from geoscript.geom import *
>>> schema = Schema('shapes', [('geom',Polygon), ('name',str)])
>>> square = Feature([LineString((0,0),(1,1)).bounds().toPolygon(),
'square'], 0, schema)
>>> square.id
0
>>> square['name']
'square'
>>> square.geom
POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))
>>> square.geom = LineString((0,0),(2,1)).bounds().toPolygon()
>>> square['name'] = 'rectangle'
>>> circle = Feature({'geom':Point(0,0).buffer(1),'name':'circle'})
Data Access
Input/Output
>>> from geoscript.feature import *
>>> from geoscript.geom import Polygon
>>> triangle = Feature({'geom':Polygon([(0,0),(1,1),(2,0),(0,0)])})
>>> writeGML(triangle)
<gsf:feature fid="fid--272f9bd3_1322c34ca2d_-8000" xmlns:gml="http://www.opengis.net/gml"
xmlns:gsf="http://geoscript.org/feature">
<gsf:geom>
<gml:Polygon>...</gml:Polygon>
</gsf:geom>
</gsf:feature>
>>> writeGML(triangle, version=3.2)
...
>>> writeJSON(triangle)
{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[0.0,0.0],[1,1],[2,0.0],[0.0,0.0]]]},"properties":{},"id":
"fid--272f9bd3_1322c34ca2d_-8000"}
>>> f = readJSON('{"type":"Feature", "geometry":{"type":"Point", "coordinates":[1.0,
2.0]}}')
feature.fid--709b2d80_1322c512876_-8000 {geometry: POINT (1 2)}
Styling and Rendering
• Taming SLD
o Symbolizers
o Scale dependence
o Thematics
Styling
Stroke
>>> from geoscript.style import Stroke
>>> from geoscript.render import draw
>>> from geoscript.geom import *
>>> line = LineString((0,0), (1,1))
>>> draw(line, Stroke('#000000', width=2)) # normal
>>> draw(line, Stroke('black', width=2, dash=[5,5])) # dash
>>> draw(line, Stroke((0,0,0), width=2).hatch('vertline')) # hatch
Styling
Fill
>>> from geoscript.style import Fill, Stroke
>>> from geoscript.render import draw
>>> from geoscript.geom import *
>>> poly = Point(0,0).buffer(1)
>>> draw(poly, Fill('gray')) # normal
>>> draw(poly, Fill('gray', opacity=0.5)) # transparent
>>> draw(poly, Fill('gray').hatch('backslash')) # hatch
>>> draw(poly, Stroke('red',2) + Fill().hatch('times'))
Styling
Shape
>>> from geoscript.style import Shape
>>> from geoscript.render import draw
>>> from geoscript.geom import *
>>> mpoint = MultiPoint((0,0), (0,1), (1,1), (1,0), (0.5,0.5))
>>> draw(mpoint, Shape('black', 10))
>>> draw(mpoint, Shape('yellow', 20, 'star'))
>>> draw(mpoint, Shape('red', 20, 'x'))
Styling
Icon
>>> from geoscript.style import Icon
>>> from geoscript.render import draw
>>> from geoscript.geom import *
>>> mpoint = MultiPoint((0,0), (0,1), (1,1), (1,0), (0.5,0.5))
>>> draw(mpoint, Icon('hospital.png'))
>>> draw(mpoint, Icon('rainy.svg'))
Styling
Labels
>>> from geoscript.style import Label, Stroke, Fill, Shape
>>> from geoscript.feature import Feature
>>> from geoscript.render import draw
>>> from geoscript.geom import *
>>> font = 'bold 16px Arial'
>>> f = Feature({'geom': Point(0,0), 'name': 'foo'})
>>> draw(f, Shape() + Label('name', font).point(displace=(20,0)))
>>> f = Feature({'geom': LineString((0,0), (1,1)), 'name': 'bar'})
>>> draw(f, Stroke() + Label('name', font).linear(offset=10))
>>> f = Feature({'geom': Point(0,0).buffer(1), 'name': 'baz'})
>>> draw(f, Fill() + Label('name', font).halo('white', 2))
Styling
Scale
>>> from geoscript.style Shape, Icon, Label
>>> from geoscript.render import draw
>>> from geoscript.feature import Feature
>>> from geoscript.geom import *
>>> school = Feature({'loc': Point(0,0), 'name': 'J.L. Crowe'})
>>> style = Shape('#004d96', 5).range(min=3000)
>>> style += Icon('school20.png').range(max=3000, min=1500)
>>> style += Icon('school40.png').range(max=1500)
>>> bounds = Bounds(-100, -100, 100, 100, 'epsg:404000')
>>> draw(school, style, bounds)
>>> draw(school, style, bounds.scale(0.5))
>>> draw(school, style, bounds.scale(0.1))
Styling
Theming
>>> from geoscript.style Stroke, Fill, Label
>>> from geoscript.render import draw
>>> from geoscript.layer import Shapefile
>>> states = Shapefile('states.shp')
>>> style = Stroke() + Label('STATE_ABBR', '14 "Times New Roman"')
>>> style += Fill('#4DFF4D', 0.7).where('PERSONS < 2000000')
>>> style += Fill('#FF4D4D', 0.7).where('PERSONS BETWEEN 2000000 AND 4000000')
>>> style += Fill('#4D4DFF', 0.7).where('PERSONS > 4000000')
>>> draw(states, style)
Centroids
import geoscript.layer.*
import geoscript.feature.*
import geoscript.geom.*
Shapefile shp = new Shapefile('states.shp')
Schema schema = shp.schema.changeGeometryType('Point',
'states_centroids')
Layer centroidLayer = shp.workspace.create(schema)
Cursor cursor = shp.cursor
while(cursor.hasNext()) {
Feature f = cursor.next()
Map attributes = [:]
f.attributes.each{k,v ->
if (v instanceof Geometry) {
attributes[k] = v.centroid
}
else {
attributes[k] = v
}
}
Feature feature = schema.feature(attributes, f.id)
centroidLayer.add(feature)
}
cursor.close()
Voronoi Diagram
import geoscript.layer.*
import geoscript.feature.*
import geoscript.geom.*
Shapefile shp = new Shapefile('states.shp')
Schema schema = new
Schema('states_voronoi',
[['the_geom','MultiPolygon','EPSG:4326']])
Layer diagramLayer = shp.workspace.create(schema)
List geoms = shp.features.collect{f->
f.geom.centroid
}
GeometryCollection geomCol = new GeometryCollection(geoms)
Geometry voronoiGeom = geomCol.voronoiDiagram
diagramLayer.add(schema.feature([voronoiGeom]))
Shapefiles to PostGIS
import geoscript.workspace.*
import geoscript.layer.*
def dir = new Directory("/Users/jericks/Downloads/wash")
println("Shapefiles: ${dir.layers}")
def postgis = new PostGIS('postgres','localhost','5432','public','postgres', 'postgres')
println("PostGIS Layers: ${postgis.layers}")
dir.layers.each{name->
def layer = dir.get(name)
println("Adding ${layer.name}...")
postgis.add(layer)
}
USGS Earth Quakes
Read RSS Feed to a Shapefile
import geoscript.geom.*
import geoscript.feature.*
import geoscript.layer.Layer
import geoscript.workspace.Directory
Schema s = new Schema('earthquakes'[['the_geom', 'Point',
'EPSG:4326'], ['title','String'], ['date', 'java.util.Date'], ['elevation', 'Double']])
Directory dir = new Directory('.')
Layer layer = dir.create(s)
def url = "http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml"
def rss = new XmlParser().parse(url)
int c = 0
String dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
rss.entry.each{e ->
def title = e.title.text()
def date = Date.parse(dateFormat, e.updated.text())
def coordinate = e."georss:point".text().split(" ")
double x = coordinate[1] as Double
double y = coordinate[0] as Double
def point = new Point(x,y)
def elev = e."georss:elev".text() as Double
Feature f = s.feature(['title':title,'date':date,
'elevation': elev, 'the_geom': point],"earthquake_${c}")
layer.add(f)
c++
}
Web Applications
Graffiti Micro Web Framework
@GrabResolver(name="graffiti", root="http://simple-dm.googlecode.com/svn/repository")
@Grab("com.goodercode:graffiti:1.0-SNAPSHOT")
import graffiti.*
import geoscript.geom.Geometry
@Get("/buffer")
def buffer() {
Geometry.fromWKT(params.geom).buffer(params.distance as double).wkt
}
@Get("/centroid")
def centroid() {
Geometry.fromWKT(params.geom).centroid.wkt
}
@Get("/convexHull")
def convexHull() {
Geometry.fromWKT(params.geom).convexHull.wkt
}
Graffiti.root 'graffiti'
Graffiti.serve this
Graffiti.start()
Geometry Web Services
Geometry Web Services
Open Layers
function centroid() {
var features = vectorLayer.features;
if (features.length == 0) {
alert("Please add some features!");
} else {
OpenLayers.loadURL('centroid', {
geom: wktFormat.write(features)
},
this,
function(request) {
var wkt = request.responseText;
var features = wktFormat.read(wkt);
if (features) vectorLayer.addFeatures(features);
},
function() {
alert("Error calculating centroids!");
}
);
}
}
WMS Server
import com.sun.grizzly.http.embed.GrizzlyWebServer
import com.sun.grizzly.http.servlet.ServletAdapter
import groovy.servlet.GroovySerlvet
@Grab(group='com.sun.grizzly',module='grizzly-servlet-webserver', version='1.9.10')
def start() {
def server = new GrizzlyWebServer(8080, "web")
def servlet = new ServletAdapter()
servlet.contextPath = "/geoscript"
servlet.servletInstance = new GroovyServlet()
server.addGrizzlyAdapter(servlet, ["/geoscript"] as String[])
server.start()
}
start()
WMS Server...
import geoscript.map.Map
import geoscript.style.*
import geoscript.layer.Shapefile
import geoscript.geom.Bounds
def file = new File("states.shp")
def shp = new Shapefile(file)
shp.style = new Fill("steelblue") + new Stroke("wheat", 0.1)
def map = new Map(
width: 256,
height: 256,
layers: [shp],
proj: shp.proj,
fixAspectRatio: false
)
def bbox = request.getParameter("BBOX").split(",")
def bounds = new Bounds(bbox[0] as double, bbox[1] as double, bbox[2] as double,
bbox[3] as double)
map.bounds = bounds
response.contentType = "image/png"
map.render(response.outputStream)
map.close()
Geometry Command line
echo "POINT (1 1)" | geoscript-groovy geom_buffer.groovy -d 10 | geoscript-groovy
geom_envelope.groovy
geom_buffer.groovy
def cli = new CliBuilder(usage: 'geoscript-groovy geom_buffer.groovy -d')
cli.d(longOpt: 'distance', 'buffer distance', args:1)
cli.h(longOpt: 'help', 'Show usage information and quit')
def opt = cli.parse(args)
if(!opt) return
if (opt.h || !opt.d) cli.usage()
else println geoscript.geom.Geometry.fromWKT(System.in.text).buffer(opt.d as
double).wkt
geom_envelope.groovy
def cli = new CliBuilder(usage: 'geoscript-groovy geom_envelope.groovy')
cli.h(longOpt: 'help', 'Show usage information and quit')
def opt = cli.parse(args)
if(!opt) return
if (opt.h) cli.usage()
else println geoscript.geom.Geometry.fromWKT(System.in.text).bounds.geometry.wkt
Road Map
•
•
•
•
Rendering Module
Raster Module
WPS/GeoServer plugin(s)
Map Printing
Resources
Web Site
http://geoscript.org
Google Group
http://groups.google.com/group/geoscript
Blog
http://geoscriptblog.blogspot.com
GitHub
https://github.com/jdeolive/geoscript-py
https://github.com/tschaub/geoscript-js
https://github.com/dwins/geoscript.scala
https://github.com/jericks/geoscript-groovy
Thank you!
Download