GRASS

From PyWPS
Jump to: navigation, search

Contents

Working with GRASS

PyWPS has native GRASS GIS support. First the pywps.cfg should be properly configured (see: Configuration).

If you want to use GRASS GIS commands in your process, you can indicate the GRASS location or just use a temporary one. If you want to use GRASS GIS commands in your process, and there is no GRASS Location to be used, you have to set grassLocation=True in the process definition:

WPSProcess.__init__(self, identifier = "foo",
 ...
 grassLocation = True)

This will create a Temporary GRASS location using a XY reference coordinate system, that will be deleted when the process execute has been finished. You can also work in an existing GRASS Location, then just set only the location name. The gisdbase should be set in the configuration file:

WPSProcess.__init__(self, identifier = "foo",
 ...
 grassLocation = "spearfish60")

The GRASS command line is accessed using the cmd() method utility from WPSProcess class. This method accepts an array with command and options and will run them in the GRASS command line, so inside the execute method we could have:

 self.cmd(["r.los","in=elevation.dem","out=los","coord=1000,1000"])
 self.cmd(["v.net.path","network","afcol=forward","abcol=backward","out=mypath",'''nlayer=1","1 9 12"'''])
 self.cmd(["d.mon","start=PNG"],stdout=False)

Note: It is also possible to send the commands and parameters as a single string, but it's not guaranteed that it will work.

If the cmd() is assigned to a variable then the variable will contain the sysout generated by grass, for example:

region = self.cmd(["g.region","-g"]

A complete GRASS process in PyWPS will have the following steps: 1) Import from PyWPS into GRASS (v.in.ogr or r.in.gdal) 2) If raster the region should set region 3) Do the work 4) export back to PyWPS (v.out.ogr or r.out.gdal)

For example a complete WPS process:


def execute(self):
        """Execute process.

        Each command will be executed and output values will be set
        """

         # run some command from the command line
         self.cmd("g.region -d")

         # set status value
         self.status.set("Importing data",20)

         #Import data into GRASS using v.in.ogr
         self.cmd("v.in.ogr dsn=%s output=data" %\
                 (self.getInputValue('data')))

         #Doing work
         self.status.set("Buffering",50)
         self.cmd("v.buffer input=data output=data_buff buffer=%s scale=1.0 tolerance=0.01" %\
                 (self.getInputValue('width')))

         self.status.set("Exporting data",90)
         
         #exporting
         self.cmd("v.out.ogr type=area format=GML input=data_buff dsn=out.xml olayer=path.xml")

         self.bufferOut.setValue("out.xml")
         self.textOut.setValue("hallo, world")
         return

Another import/export possibility is the r.external/v.external, the following example transforms a GeoTiff image into a PNG:

from pywps.Process import WPSProcess

class Geotiff2PNG(WPSProcess):

    def __init__(self):

        ##
        # Process initialization
        WPSProcess.__init__(self,
            identifier = "geotiff2png",
            title="Geotiff to PNG conversion",
            version = "1.0",
            grassLocation = True,
            storeSupported = True,
            statusSupported = True)
      
        
        self.inputImage=self.addComplexInput(identifier='input',maxmegabites=100,title="input image",minOccurs=1,maxOccurs=1,formats = [{'mimeType': 'image/tiff'}, {'mimeType': 'image/geotiff'}, {'mimeType': 'application/geotiff'}, {'mimeType': 'application/x-geotiff'}])
        self.outputImage=self.addComplexOutput(identifier="output",title="output image",formats=[{'mimeType':'image/png'}])
       
        
    def execute(self):
        self.cmd(["r.in.gdal","input=%s" % self.inputImage.getValue(),'output=inputImage','-o'])
        #r.gdal may import things as RBG bands, if so they need to regroupped
        try:
            self.cmd(['g.region',"rast=inputImage"])
        except:
            self.cmd(['g.region',"rast=inputImage.green"])
            self.cmd(["r.composite","red=inputImage.red","green=inputImage.green","blue=inputImage.blue","output=inputImage"])
        self.cmd(["r.out.png","input=inputImage","output=./tmp.png"])
        self.outputImage.setValue("./tmp.png")      

Using GRASS-Python interface

Since GRASS 6.4, there's a Python interface. There is a swig interface and another GRASS Module-Python interface. They are both described in GRASS Wiki . For the swig interface, have a look in the GRASS-Wiki - it is perfectly documented.

To use the GRASS-python interface its enough to import it in the process script as follows:

from grass.script import core as grass
from pywps.Process import WPSProcess

class Process(WPSProcess):
     def __init__(self):
         WPSProcess.__init__(self,
            identifier="grassprocess",
            title="GRASS Process")

     def execute(self):
       ret=grass.run_command("d.his",
                  h_map="drap_map",
                  i_map="relief_map",
                  brighten=0)
       return

GRASS packages can be accessed using the standard python procedure, for example a request to the mapcalc functionality:

grass.mapcalc("MASK=if(($cloudResampName < 0.01000),1,null())",
 cloudResampName = cloudResampName)


The cmd() util

The cmd() method can be used to run any bash commands or access other system thru bash but such IT'S NOT ADVISABLE, any access to external program should be done using Python packages already prepared to access the desired system. Any way here is an example of ls bash command:

listFiles=self.cmd(["ls","-l"])


GRASS Notes

GRASS GIS is a major problem for "newbies" since it deals with path configurations and env. variables.

PyWPS will try to use the following path syntax, to use/create a GRASS location:

$GISDBASE/$LOCATION_NAME/$MAPSET

The $GISDBASE and $LOCATION_NAME are stored in the pywps.cfg or indicated in the Process class. Note that pywps.cfg will have PRECEDENCE.

For example a GRASS installed from source code will have the following configuration in pywps.cfg:

[grass]
path=/usr/local/grass-6.4.0RC6/bin:/usr/local/grass-6.4.0RC6/scripts
addonPath=
version=6.2.1
gui=text
gisbase=/usr/local/grass-6.4.0RC6
ldLibraryPath=/usr/local/grass-6.4.0RC6/lib
gisdbase=/users/grassgis

Therefore the $GISDBASE will be /users/grassgis.

If the process constructor has the following grass location:

grassLocation = "spearfish60"

Then PyWPS will be working in directory: /users/grassgis/spearfish60

The spearfish60 is a location that was previously created and has already a specific projection and location, aside from the the standard MAPSET created by default. So what will pyWPS do ?!

When the process is run it will create a temporary mapset that will be be deleted when the process finishes, so when a process is running the files/data will be saved in something like:

/users/grassgis/spearfish60/pywps54ghdft

Notice that the $MAPSET is set/created by pyWPS.

This $GISDBASE/$LOCATION_NAME/$MAPSET path will be used by any GRASS tool called inside the execute() method.

The GRASS variables are set in the pywps.cfg, which are then passed to pyWPS as environment variables, it has been reported by some users that sometimes the setting of the variables will not work, therefore they have to be set by "hand". This could be done in the wrapper script like this:

export GISBASE="/usr/local/grass-6.4.0RC6"
export PATH="$PATH:$GISBASE/bin:$GISBASE/scripts"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$GISBASE/etc/python"
export GISDBASE="/users/grassgis"

Any GRASS variable can be set using the bash for for more information on GRASS variable's please check: http://grass.osgeo.org/grass64/manuals/html64_user/variables.html

Final note, in some documentation we present the following example:

WPSProcess.__init__(self,
 identifier = "foo",
 ...
 grassLocation = "/foo/bar/grassdata/spearfish60")

This is outdated AND WILL NOT WORK, grassLocation doesn't support full $GISDBASE/$LOCATION_NAME setting the process's class

v.in.out 'No such file or directory' Error

Users constantly report the following error:

<ows:ExceptionText>
Failed to execute WPS process [gml2svg]: Could not perform command 
[['v.in.ogr', 'dsn=./pywpsInputmD7Jng', 'output=inputImage', '-o', '-t']]: 
[Errno 2] No such file or directory
</ows:ExceptionText>

v.in.ogr complains that it can't find the PyWPS input content. This error is caused by a lack of a GRASS location, if a process will run GRASS commands it's mandatory that: grassLocation=True (or path to location), grassLocation=False will non create a GRASS location and v.in.ogr will produce the File not found error

GRASS-WPS-Bridge

Currently a new project pretends to build an easy bridge between GRASS and PyWPS,52 North and ZOO WPS. Project is still in alfa stage and requires the GRASS's SVN version but some initial tests show promising results.

http://code.google.com/p/wps-grass-bridge/


--Wikiadmin 16:10, 10 January 2011 (UTC)

Setting Coordinate System information

For processes where the grassLocation parameter is simply set to True PyWPS creates a temporary GRASS Location at run time for each process instance (whenever an execute request is accepted). This GRASS location is identified by the process identifier (PID) and resides in the temporary folder indicated in the config file (/tmp by default). PyWPS creates two mapsets inside this location, the PERMANENT mapset and a working mapset also identified by the PID, where all GRASS commands issued from within the process are executed.

By default PyWPS creates this Location with a bare cartesian plane (X/Y projection) without any coordinate system information. This approach is sufficient for most cases, but it has two drawbacks:

  • spatial outputs are returned without coordinate system information;
  • GRASS commands that require a coordinate system (e.g. r.sun) cannot be executed.

If the user needs a process able to operate on different coordinate systems and keep this information in resulting datasets or if it needs to run GRASS commands that require a coordinate system a different approach must be tried.

GRASS allows the user to change the coordinate system of a location, but this must be made within the PERMANENT mapset. Fortunately, PyWPS creates a fully working GRASS location and such approach can be made at runtime within the process. It takes only two steps:

  • shift from the working mapset to the PERMANENT mapset;
  • apply the coordinate system information from an input dataset to the location.

This simple process exemplifies:

from pywps.Process import WPSProcess

class Process(WPSProcess):
	def __init__(self):
		WPSProcess.__init__(self,
			identifier = "grassProj",
			title="Grass buffer",
			version = "0.1",
			storeSupported = True,
			statusSupported = True,
			grassLocation = True,
			abstract="Tests the setting of coordinate system for GRASS at run time.")

		self.input = self.addComplexInput(
			identifier = "input",
			title = "A raster map in GeoTiff format.",
			formats = [{'mimeType':'image/tiff'}])

		self.output = self.addComplexOutput(
			identifier="output",
			title="A resulting raster map",
			formats = [{'mimeType':'image/tiff'}],
			useMapscript=False,
			asReference=True)


	def execute(self):
	
		# Shift to PERMANENT mapset
		self.cmd(["g.mapset", "mapset=PERMANENT"])

		# Set coordinate system from input
		self.cmd(["g.proj", "-c", "georef=%s" % (self.input.getValue())])

		# Inport data
		self.cmd(["r.in.gdal", "-o", "out=input", "in=%s" % (self.input.getValue())])

		# Do a dumb calculation
		self.cmd(["r.mapcalc","output=input+1"])
	
		# Expor result
		self.cmd(["r.out.gdal", "in=output", "type=Float32", "output=./output.tif"])
		self.output.setValue("./output.tif")

		return

In processes that have no spatial inputs, but a spatial output that must have coordinate system information, similar results can be achieved by using the epsg argument to the GRASS g.proj command.