Stay Connected with the Boundless Blog

Boundless GeoScript in Action Building a Web App

This is the second post of a three-part series dedicated to showing the versatility and functionality of GeoScript. 

TomIn Part One of this series we explored GeoScript basics, sliced and diced some solar intensity data, and created several visualizations.  We’ll now go a step further and start building a web application that relies on a Web Processing Service scripted in Python implementation of GeoScript.

Suppose you live in the US and find yourself feeling blue lately. You learn that you have a Vitamin D deficiency and decide to get as much sunlight as possible on your weekly Sunday drive. Your solution is to build a web app that suggests the sunniest possible drive inside your current state based on your  location and the number of stops you want to make. In this post we’ll learn how to build the back-end service that takes care of the computation. We will then build the front-end application in the upcoming Part Three.

Get your data into the database. First, import the solar and state data into your PostGIS instance and create two tables: ‘solar_dni_polygons’ and ‘usa_l48’ (we’ll assume that the importer named the geometry columns ‘geom’). Then open up a GeoScript shell and connect to PostGIS :

>>> from geoscript.workspace import PostGIS
>>> postgis_workspace = PostGIS('solar_db',’127.0.0.1’,'5432','public','boundless','password')

Now that we’re connected, let’s grab our layers and look inside :

>>> from geoscript.layer import Layer
>>> solar_layer = postgis_workspace.get('solar_dni_polygons')
>>> state_layer = postgis_workspace.get('usa_l48')
>>> solar_layer.count()
91439
>>> solar_layer.count()
(-161.0, 18.0, -66.9, 49.4, EPSG:4326)
>>> state_layer.count()
62
>>> state_layer.bounds()
(-179.14734, 17.6743956662, 179.77848, 71.389210465, EPSG:4269)

Script the processing logic from command line. Now let’s find the specific features we want based on the criteria described above. First we need to find all the features within a certain radius of where we are. For now, let’s assume that this is October and we are in Austin at POINT(-97.75 30.25). We’ll find our corresponding state by using a filter:

>>> current_state = state_layer.filter(‘CONTAINS(the_geom, POINT(-97.75 30.25))’)
>>> print current_state.first().get(‘state’)
Texas

Now that we’ve determined the state, let’s get the top sunniest spots inside it. We’ll use a cursor which accepts a “sort” parameter.  We’ll want to sort based on the value for the current month. For the sake of clarity let’s make a filter object and pass it into the cursor:

>>> from geoscript.filter import Filter
>>> geo_filter = Filter('WITHIN(the_geom,%s)'%current_state.first().geom)
>>> from geoscript.layer import Cursor
>>> solar_cursor = solar_layer.cursor(geo_filter,('OCT', 'DESC'))

Assuming we want to get the top 5:

>>> sunniest_features = solar_cursor.read(5)
>>> solar_cursor.close()
>>> print sunniest_features
[solar_dni_polygons.80668 {ID: 82691.0, GRIDCODE: 105453125.0, ANN_DNI: 7.4340143836, JAN: 6.9671129032, FEB: 7.2524672619, MAR: 7.8530887097, APR: 8.3245583333, MAY: 8.2876962366, JUN: 8.2920583333, JUL: 7.0886370968, AUG: 6.5005698925, SEP: 7.2412388889, OCT: 7.2913494624, NOV: 7.4192555556, DEC: 6.7222822581, the_geom: POLYGON ((-105.4 31.2, -105.5 31.2, -105.5 ... ...

Now that we have the 5 features we need, let’s make them into a multipoint:

>>> from geoscript.geom import MultiPoint
>>> result_list = []
>>> for f in sunniest_features:
...        c = f.geom.centroid
...        result_list.append([c.x,c.y])
...
>>> mp = MultiPoint(*result_list);
>>> print mp
MULTIPOINT ((-105.44999999999999 31.25), (-105.85 31.65), (-105.64999999999999 31.45), (-105.75 31.65), (-106.25 31.95))

Package your script as a WPS.  To create a WPS from your script, there are just a few little modifications that need to be made. First,  create a new text file called ‘solar_wps.py’, add all imports from your script and add the metadata section that will help the WPS expose the parameters in the capabilities document:

@process(
title = 'Sunny Road Trip',
description = 'This process will return the top sunniest locations within the origin state',
inputs = {
'origin': (Point, 'The place from which the road trip should start'),
'waypoints': (int, 'The number of stops that that trip should have, excluding the start and the last stop')
},
outputs = {
'roadtrip': (MultiPoint, 'The top n sunniest points')
}
)

Then, we just need to wrap our logic in a method called ‘run’ that accepts ‘origin’ and ‘waypoints’ parameters and returns the itinerary for our trip. The completed script looks like this.

Deploy your WPS to GeoServer.  To do this, find your GeoServer data directory and drop the file under ‘data_dir/scripts/wps’.  (The directly was created automatically when you installed the WPS extension.)

Test your WPS.  Open up the GeoServer Admin app in a browser window and pull up the WPS Request Builder under “Demos”. On the drop down we should see the script as “py:solar_wps”. After selecting it the page will update with form elements to provide input.  Enter the same values as our test scenario we seen a pop-up appear with the same results.
Screen Shot 2013-10-28 at 12.07.42 PM
We can also generate the XML request, coping that into a local file (“roadtrip-req.xml” for example), then invoke the web service from the command line using cURL:

curl -H ‘Content-type: xml’ -XPOST -d@’roadtrip-req.xml’ http://you-server:8080/geoserver/wps

Tom joined the team a few weeks ago this October. He works  directly with clients to engineer high-performance, scalable and redundant deployments of the Boundless software stack. Interested in joining our team? We’re hiring!