Here I'll show how I implemented the WMS using the Pyramid framework in a single file.
Below is the core function of the WMS, it is basically a wrapper for the d.mon module.
First of all the GRASS Python modules are imported and a new GRASS environment is started. After setting the current region from the bounding box parameters in the WMS request, a new Cairo monitor is started with d.mon. The output is written to a temporary file which will be returned.
def _grass_wms(layers=[], bbox=[], width=256, height=256): # Path to GRASS installation gisbase = "/usr/local/grass-7.0.svn" # Set the environment variable os.environ["GISBASE"] = gisbase # Append the GRASS Python directory to the path sys.path.append("%s/etc/python" % gisbase) # Import the GRASS scripts from grass.script import core as grass from grass.script import setup as gsetup # Open the mapset gsetup.init(gisbase, "/home/adrian/Data/GISBASE7", "Landsat_128047", "adrian") vector_layers = grass.list_strings("vect") raster_layers = grass.list_strings("rast") grass.run_command("g.region", w=bbox[0], s=bbox[1], e=bbox[2], n=bbox[3]) # Create a temp file tempfile = grass.tempfile() filename = "%s.png" % tempfile grass.run_command("d.mon", start="cairo", width=width, height=height, output=filename) for layer in layers: if layer in raster_layers: grass.run_command("d.rast", map=layer, quiet=1) elif layer in vector_layers: grass.run_command("d.vect", map=layer, quiet=1, fcolor="0:0:255", color=None) grass.run_command("d.mon", stop="cairo") return filename
Above method is called by a Pyramid view that gets the layers, bounding box and image width and height parameters from the request and returns the image:
@view_config(route_name="wms") def wms_view(request): layers = request.params.get("LAYERS", "").split(",") bbox = request.params.get("BBOX", "").split(",") width = request.params.get("WIDTH") height = request.params.get("HEIGHT") try: filename = _grass_wms(layers=layers, bbox=bbox, width=width, height=height) f = open(filename, "r+") return Response(body=f.read(), content_type="image/png") except: pass
Furthermore I added a super simple index page which uses OpenLayers to show the WMS layer:
@view_config(route_name="index") def index_view(request): body = """ <html xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns="http://www.w3.org/1999/xhtml"> <head> <title>GRASS GIS Web Map Service</title> <script src="http://openlayers.org/api/OpenLayers.js" type="text/javascript"></script> <script type="text/javascript"> var map, wms_layer; function init(){ var map = new OpenLayers.Map("map-div",{ projection: "EPSG:32648", maxExtent: new OpenLayers.Bounds(235080, 2132280, 257100, 2147400), numZoomLevels: 6, }); var wms_layer = new OpenLayers.Layer.WMS("GRASS WMS", '/wms', { layers: "namngum5_patch,namngum5_bw,namngum5_smooth" },{ singleTile: true, }); map.addLayer(wms_layer); map.zoomToMaxExtent(); } </script> </head> <body onload="init()"> <div id="map-div" style="height: 600px; width: 800px;"> </div> </body> </html>""" return Response(body=body, content_type="text/html", status=200)
Last but not least it needs some Pyramid / WSGI code to get the server running:
def main(global_config, ** settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(settings=settings) config.add_static_view('static', 'static', cache_max_age=3600) config.add_route("index", '/') config.add_route("wms", '/wms') config.scan() return config.make_wsgi_app()
Since this is a very quick implementation, there are some unsolved issues:
- Reprojection on the fly is not supported, i.e. a WMS request must be in the same projection as the GRASS location
- Pyramid requests are not thread-safe i.e. if there are several requests setting a different region in GRASS the monitor output gets mixed up
- probably others as well
Last but not least two screenshots to illustrate the Web Map Service:
The index page shows the WMS layer |
The same location and layers in GRASS GIS |
No comments:
Post a Comment