GRASS and Python
(for discussions on the new GRASS GUI, see here)
Python SIGs
Python Special Interest Groups are focused collaborative efforts to develop, improve, or maintain specific Python resources. Each SIG has a charter, a coordinator, a mailing list, and a directory on the Python website. SIG membership is informal, defined by subscription to the SIG's mailing list. Anyone can join a SIG, and participate in the development discussions via the SIG's mailing list. Below is the list of currently active Python SIGs, with links to their resources.
See more at http://www.python.org/community/sigs/
Writing Python scripts in GRASS
Python is a programming language which is more powerful than shell scripting but easier and more forgiving than C. The Python script can contain simple module description definitions which will be processed with g.parser, as shown in the example below. In this way with no extra coding a GUI can be built, inputs checked, and a skeleton help page can be generated automatically. In addition it adds links to the GRASS message translation system. For code which needs access to the power of C, you can access the GRASS C library functions via the SWIG interface.
- GRASS Python interface to library functions: http://download.osgeo.org/grass/grass6_progman/swig/
- GRASS Python scripting library: http://download.osgeo.org/grass/grass6_progman/pythonlib.html
Code style: Have a look at SUBMITTING_PYTHON.
Running external commands from Python
For information on running external commands from Python, see: http://docs.python.org/lib/module-subprocess.html
Avoid using the older os.* functions. Section 17.1.3 lists equivalents using the Popen() interface, which is more robust (particularly on Windows).
Examples
Display example
Example of Python script, which is processed by g.parser:
#!/usr/bin/env python
#
############################################################################
#
# MODULE: d.shadedmap
# AUTHOR(S): Unknown; updated to GRASS 5.7 by Michael Barton
# Converted to Python by Glynn Clements
# PURPOSE: Uses d.his to drape a color raster over a shaded relief map
# COPYRIGHT: (C) 2004,2008,2009 by the GRASS Development Team
#
# This program is free software under the GNU General Public
# License (>=v2). Read the file COPYING that comes with GRASS
# for details.
#
#############################################################################
#%Module
#% description: Drapes a color raster over a shaded relief map using d.his
#%End
#%option
#% key: reliefmap
#% type: string
#% gisprompt: old,cell,raster
#% description: Name of shaded relief or aspect map
#% required : yes
#%end
#%option
#% key: drapemap
#% type: string
#% gisprompt: old,cell,raster
#% description: Name of raster to drape over relief map
#% required : yes
#%end
#%option
#% key: brighten
#% type: integer
#% description: Percent to brighten
#% options: -99-99
#% answer: 0
#%end
import sys
from grass.script import core as grass
def main():
drape_map = options['drapemap']
relief_map = options['reliefmap']
brighten = options['brighten']
ret = grass.run_command("d.his", h_map = drape_map, i_map = relief_map, brighten = brighten)
sys.exit(ret)
if __name__ == "__main__":
options, flags = grass.parser()
main()
Example for embedding r.mapcalc (map algebra)
grass.mapcalc() accepts a template string followed by keyword arguments for the substitutions, e.g. (code snippets):
grass.mapcalc("${out} = ${rast1} + ${rast2}",
out = options['output'],
rast1 = options['raster1'],
rast2 = options['raster2'])
Best practice: first copy all of the options[] into separate variables at the beginning of main(), i.e.:
def main():
output = options['output']
raster1 = options['raster1']
raster2 = options['raster2']
...
grass.mapcalc("${out} = ${rast1} + ${rast2}",
out = output,
rast1 = raster1,
rast2 = raster2)
Python extensions for GRASS GIS
wxPython GUI development for GRASS
- See the GRASS GUI wiki page
GRASS Python library
Interfacing with NumPy
Glynn writes:
The grass.script.array module defines a class "array" which is a subclass of numpy.memmap with .read() and .write() methods to read/write the underlying file via r.out.bin/r.in.bin.
Example:
import grass.script.array as garray
a = garray.array()
a.read("elevation.dem")
b = garray.array()
b[...] = (a / 50).astype(int) * 50 # or whatever
b.write("elev.50m")
The size of the array is taken from the current region.
The main drawback of using numpy is that you're limited by available memory. Using a subclass of numpy.memmap lets you use files which may be much larger, but processing the entire array in one go is likely to produce in-memory results of a similar size.
One may also use the scipy matlab interface:
### SH: in GRASS ### r.out.mat input=elevation output=elev.mat
### PY ###
import scipy.io as sio
# load data
elev = sio.loadmat('elev.mat')
# retrive the actual array. the data set contains also the spatial reference
elev.get('map_data')
data = elev.get('map_data')
# a first simple plot
import pylab
pylab.plot(data)
pylab.show()
# the contour plot
pylab.contour(data)
# obviously data needs to ne reversed
import numpy as np
data_rev = data[::-1]
pylab.contour(data_rev)
# => this is a quick plot. basemap mapping may provide a nicer map!
#######
Python-SWIG-GRASS interface
There is a prototype GRASS-SWIG interface available (thanks to Sajith VK), find it in GRASS 6-CVS: swig/python/. Draft documentation is here. It now wraps both raster and vector data C functions plus the general GIS (G_*()) functions.
Background: SWIG (Simplified Wrapper and Interface Generator) is:
- A compiler that turns ANSI C/C++ declarations into scripting language interfaces.
- Completely automated (produces a fully working Python extension module).
- Language neutral. SWIG can also target Tcl, Perl, Guile, MATLAB (try PyLab+Matplotlib from python), etc...
- Attempts to eliminate the tedium of writing extension modules.
Python-SWIG examples
- Latest and greatest: [GRASS 7 Python scripts]
Sample script for GRASS 6 raster access (use within GRASS, Spearfish session):
#!/usr/bin/env python
import os, sys
from grass.lib import grass
if "GISBASE" not in os.environ:
print "You must be in GRASS GIS to run this program."
sys.exit(1)
if len(sys.argv)==2:
input = sys.argv[1]
else:
input = raw_input("Raster Map Name? ")
# initialize
grass.G_gisinit('')
# find map in search path
mapset = grass.G_find_cell2(input, '')
# determine the inputmap type (CELL/FCELL/DCELL) */
data_type = grass.G_raster_map_type(input, mapset)
infd = grass.G_open_cell_old(input, mapset)
inrast = grass.G_allocate_raster_buf(data_type)
rown = 0
while True:
myrow = grass.G_get_raster_row(infd, inrast, rown, data_type)
print rown, myrow[0:10]
rown += 1
if rown == 476:
break
grass.G_close_cell(inrast)
grass.G_free(cell)
Sample script for vector access (use within GRASS, Spearfish session):
#!/usr/bin/python
# run within GRASS Spearfish session
# run this before starting python to append module search path:
# export PYTHONPATH=/usr/src/grass70/swig/python
# check with "import sys; sys.path"
# or:
# sys.path.append("/usr/src/grass70/swig/python")
# FIXME: install the grass bindings in $GISBASE/lib/ ?
import os, sys
from grass.lib import grass
from grass.lib import vector as grassvect
if "GISBASE" not in os.environ:
print "You must be in GRASS GIS to run this program."
sys.exit(1)
if len(sys.argv)==2:
input = sys.argv[1]
else:
input = raw_input("Vector Map Name? ")
# initialize
grass.G_gisinit('')
# find map in search path
mapset = grass.G_find_vector2(input,'')
# define map structure
map = grassvect.Map_info()
# define open level (level 2: topology)
grassvect.Vect_set_open_level (2)
# open existing map
grassvect.Vect_open_old(map, input, mapset)
# query
print 'Vect map: ', input
print 'Vect is 3D: ', grassvect.Vect_is_3d (map)
print 'Vect DB links: ', grassvect.Vect_get_num_dblinks(map)
print 'Map Scale: 1:', grassvect.Vect_get_scale(map)
print 'Number of areas:', grassvect.Vect_get_num_areas(map)
# close map
grassvect.Vect_close(map)
TODO
- Implement modules support in a Python class using --interface-description and a Python-XML parser. This should be a generic class with module's name as parameter, returning back an object which describes the module (description, flags, parameters, status of not/required). See GRASS 6 wxPython interface for inspiration. Important is to auto-generate the GRASS-Python class at compile time with a Python script.
Python-GRASS add-ons
Stand-alone addons:
- Jáchym Čepický's G-ps.map, a GUI to typeset printable maps with ps.map (http://193.84.38.2/~jachym/index.py?cat=gpsmap)
- Jáchym Čepický's v.pydigit, a GUI to v.edit (http://les-ejk.cz/?cat=vpydigit)
- Jáchym Čepický's PyWPS, GRASS-Web Processing Service (http://pywps.wald.intevation.org)
Using GRASS gui.tcl in Python
Here is some example code to use the grass automatically generated guis in python code. This could (should) all be bundled up and abstracted away so that the implementation can be replaced later.
import Tkinter
import os
# Startup (once):
tk = Tkinter.Tk()
tk.eval ("wm withdraw .")
tk.eval ("source $env(GISBASE)/etc/gui.tcl")
# Here you could do various things to change what the gui does
# See gui.tcl and README.GUI
# Make a gui (per dialog)
# This sets up a window for the command.
# This can be different to integrate with tkinter:
tk.eval ('set path ".dialog$dlg"')
tk.eval ('toplevel .dialog$dlg')
# Load the code for this command:
fd = os.popen ("d.vect --tcltk")
gui = fd.read()
# Run it
tk.eval(gui)
dlg = tk.eval('set dlg') # This is used later to get and set
# Get the current command in the gui we just made:
currentcommand = tk.eval ("dialog_get_command " + dlg)
# Set the command in the dialog we just made:
tk.eval ("dialog_set_command " + dlg + " {d.vect map=roads}")
Links
General guides
- Wikibook Python Programming
- Quick Python tutorial for programmers of other languages
- More Python tutorials for programmers
Programming
- Python and GRASS:
- GRASS Python interface to library functions: http://download.osgeo.org/grass/grass6_progman/swig/ based on SWIG http://www.swig.org/
- GRASS Python scripting library: http://download.osgeo.org/grass/grass6_progman/pythonlib.html
- PyWPS, GRASS-Web Processing Service http://pywps.wald.intevation.org
- Python and OSGeo:
- Python and GDAL/OGR:
- Python bindings to PROJ:
- Python and GIS:
- Python and Statistics:
- RPy - Python interface to the R-statistics programming language
- Bindings:
- SIP (C/C++ bindings generator) http://directory.fsf.org/all/Python-SIP.html
- Cython - C-Extensions for Python (compile where speed is needed)
- Other external projects
Presentations
From FOSS4G2006:
- A Python sweeps in the GRASS - A. Frigeri 2006
- GRASS goes web: PyWPS - J. Cepicky 2006