GeoScript Spatial Capabilities for Scripting Languages Justin Deolivera and Jared Erickson.

Post on 26-Dec-2015

226 views 3 download

Transcript of GeoScript Spatial Capabilities for Scripting Languages Justin Deolivera and Jared Erickson.

GeoScriptSpatial Capabilities for Scripting Languages

Justin Deolivera and Jared Erickson

Scripting Platform for JVM Languages

Similar API Respect languages differences    Python GeoScript should be PythonicMake easy things easy, Make hard things possible

Groovy

• Groovy o Dynamic languageo Easy for Java programmers to learn o Closures, DSLso REPL, GUI Consoleo Compiles to Java Byte Codeo 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.5o Full access to Java libraries

• http://geoscript.org/py• https://github.com/jdeolive/geoscript-py

• Scalao Combine functional and object-oriented

programmingo Statically typed???o REPLo Compiles to Java bytecodeo Full access to Java libraries

• http://geoscript.org/scala/• https://github.com/dwins/geoscript.scala/

Scala

On the shoulders of giants...

On the shoulders of giants...

• Java Topology Suite (JTS)o Geometry API and Algorithms

• GeoToolso DataStoreso Features o Coverages/Rasters o Referencingo Renderering

• Java Tribe Base librarieso GeoServero uDigo Geomajas

GeoScript Modules

GeoScript Modules

• Geometry• Projection• Feature• Layer• Workspace• Style• Renderer

Geometry

• Convenient constructors• I/O

o WKT/WKBo JSONo GML

• Plotting• Transforms

Geometry

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

Constructors

Geometry

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

Input/Output

Geometry

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

Plotting

Geometry

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

Transforms

Projection

• Parse/encode WKT• Full GeoTools EPSG

database• Re-projection

Projection

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

Constructors

Projection

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

Reprojection

WGS 84 UTM Albers

Data Access

• Read and Write Layers• Query Layers using CQL• I/O 

o GeoJSONo GML

Data Access

>>> from geoscript.workspace import PostGIS>>> from geoscript import geom

>>> pg = PostGIS('spearfish')>>> pg.layers()['archsites', 'bugsites', 'railroads', 'restricted', 'roads', 'streams']>>> l = pg['bugsites']>>> l.schemabugsites [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)])

Workspaces

Data Access

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

Layers

Data Access

>>> 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.id0>>> square['name']'square'>>> square.geomPOLYGON ((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'})

Features

Data Access

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

Input/Output

Styling and Rendering

• Taming SLDo Symbolizerso Scale dependenceo Thematics

Styling

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

Stroke

Styling

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

Fill

Styling

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

Shape

Styling

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

Icon

Styling

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

Labels

Styling

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

Scale

Styling

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

Theming

Centroidsimport 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.cursorwhile(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 Diagramimport 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.voronoiDiagramdiagramLayer.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.Layerimport 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 = 0String 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

@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 thisGraffiti.start()    

Graffiti Micro Web Framework

Geometry Web Services 

Geometry Web ServicesOpen 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 Serverimport com.sun.grizzly.http.embed.GrizzlyWebServerimport com.sun.grizzly.http.servlet.ServletAdapterimport 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.Mapimport geoscript.style.*import geoscript.layer.Shapefileimport 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 = boundsresponse.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

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) returnif (opt.h || !opt.d) cli.usage()else println geoscript.geom.Geometry.fromWKT(System.in.text).buffer(opt.d as double).wkt

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) returnif (opt.h) cli.usage()else println geoscript.geom.Geometry.fromWKT(System.in.text).bounds.geometry.wkt

geom_buffer.groovy

geom_envelope.groovy

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!