GRASS and Python
This page discusses usage of GRASS GIS and Python in a general way. If you just want to write a script to run in GRASS GIS, go to GRASS Python Scripting Library wiki page or Python API for GRASS GIS 7 documentation.
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. The library for "scripting" is "grass.script", typically used as:
import grass.script as gscript
The related files are at $GISBASE/etc/python/grass/script/*.py. See below for more details.
Note: For code which needs access to the power of C, you can access the GRASS C library functions via the Python "ctypes" interface.
How to write a Python GRASS GIS 7 addon
The tutorial "How to write a Python GRASS GIS 7 addon" is available here: https://github.com/wenzeslaus/python-grass-addon
Integrated Python shell
The wxGUI Layer Manager in GRASS GIS comes with a "Python Shell" which enables users to type and execute Python commands directly in wxGUI environment.
Integrated Python editor
The wxGUI Layer Manager in GRASS GIS 7 comes with a "Simple Python Editor" which enables users to author Python scripts directly in the GRASS GIS GUI. Users can also run the script easily in the GRASS GIS environment with all the dependencies loaded. The editor comes with several examples, templates, and links to documentation.
External Python editors (IDE)
Besides the wxGUI Python shell and script editor, also advanced editors like Spyder (The Scientific PYthon Development EnviRonment) can be used in connection with GRASS GIS. For details, see Tools for Python programming.
The correct editor settings for Python indentation
Be sure to use only white spaces and no tabs to indent Python code!
See: https://trac.osgeo.org/grass/wiki/Submitting/Python#Editorsettingsfor4-spaceindentation
Editor settings for 4-space indentation
- Geany editor:
- Edit > Preferences > Editor > Intentation tab > Type: Spaces
- GNU Emacs:
- python-mode default
- spyder ...
- pycharm ...
Using the GRASS Python Scripting Library
You can run Python scripts easily in a GRASS session.
To write these scripts,
- check the code in lib/python/ which provides grass.script in order to support GRASS scripts written in Python.
See the GRASS Python Scripting Library for notes and examples.
- The scripts/ directory of GRASS contains a series of examples actually provided to the end users.
For the desired Python code style, have a look at Submitting Python.
Creating Python scripts that call GRASS functionality from outside
For calling GRASS functionality from outside, see the documentation and also Working with GRASS without starting it explicitly.
Note: This is a more advanced use case of using GRASS' functionality from outside via Python. Commonly, a user will run GRASS Python script from inside a GRASS session, i.e. either from the command line or from the Python shell embedded in the wxGUI (screenshot).
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).
Testing and installing Python extensions
Debugging
Make sure the script is executable:
chmod +x /path/to/my.extension.py
During development, a Python script can be debugged using the Python Debugger (pdb):
python -m pdb /path/to/my.extension.py input=my_input_layer output=my_output_layer option=value -f
Installation
Once you're happy with your script, you can put it in the scripts/ folder of your GRASS install. To do so, first create a directory named after your extension, then create a Makefile for it, and a HTML man page:
cd /path/to/grass_src/ cd scripts ls # it is useful to check out the existing scripts and their structure mkdir my.extension cd my.extension cp path/to/my.extension.py . touch my.extension.html touch Makefile
Next step is to edit the Makefile. It is a very simple text file, the only thing to check is to put the right extension name (WITHOUT the .py file extension) after PGM:
MODULE_TOPDIR = ../.. PGM = my.extension include $(MODULE_TOPDIR)/include/Make/Script.make default: script
The HTML file would be generated automatically. If you want to add more precisions in it, you can do it (just make sure you start at DESCRIPTION. See existing scripts.)
In the /path/to/grass_src/scripts folder there is also a Makefile. Before building your new folder name should be added to the SUBDIRS list in this file to be built correctly.
You can then run "make" within the my.extension folder. Running "make" in the extension directory places the resulting files in the staging directory (path/to/grass_src/dist.<YOUR_ARCH>/). If you're running GRASS from the staging directory (/path/to/grass_src/bin.<YOUR_ARCH>/grass7), subsequent commands will used the updated files.
# in your extension directory (/path/to/grass_src/scripts/my.extension/) make # Starting GRASS from the staging directory /path/to/grass_src/bin.<YOUR_ARCH>/grass7 my.extension help
You can also run "make install" from the top level directory of your GRASS install (say /usr/local/src/grass_trunk/). Running "make install" from the top level just copies the whole of the dist.<YOUR_ARCH>/ directory to the installation directory (e.g. /usr/local/grass78) and the bin.<YOUR_ARCH>/grass78 bin file to the bin directory (e.g. /usr/local/bin), and fixes any embedded paths in scripts and configuration files.
cd /path/to/grass_src make install # Starting GRASS as usual would work and show your extension available grass7 my.extension help
Using the parser to get GUI and standardized CLI
Read the documentation for GRASS GIS Python scripting with script package.
For more details, see
Be sure to use the standardized options such as G_OPT_R_INPUT or G_OPT_V_OUTPUT if possible. See the overview table of parser standard options in the documentation.
Using a version of Python different from the default installation
Sometimes you don't want to use the default Python version installed on your system. You can control the interpreter used for building by overriding the PYTHON make variable, e.g.
make PYTHON=/path/to/python ...
For controlling the interpreter used at run time:
- On Windows, Python scripts are invoked via %GRASS_PYTHON%, so changing that environment variable will change the interpreter.
- On Unix, GRASS_PYTHON is only used for components which use wxPython, e.g. wxGUI or the parameter dialogues which are displayed when modules are run without arguments.
Scripts rely upon the "#!/usr/bin/env python" line. You can control the interpreter used by scripts by creating a directory containing a symlink named "python" which refers to your preferred version of the interpreter, and placing that directory at the front of $PATH.
Python extensions in GRASS GIS
Python Scripting Library
pyGRASS Library
pyGRASS is an object oriented Python Application Programming Interface (API) for GRASS GIS. pyGRASS wants to improve the integration between GRASS and Python, render the use of Python under GRASS more consistent with the language itself and make the GRASS scripting and programming activity easier and more natural to the final users. Some simple examples below.
For more details, see pygrass
Example 1:
from grass.pygrass.modules import Module
slope_aspect = Module("r.slope.aspect")
slope_aspect(elevation='elevation', slope='slp', aspect='asp',
format='degrees', overwrite=True)
Example 2, using "shortcuts":
#!/usr/bin/env python
# simple example for pyGRASS usage: raster processing via modules approach
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster as r
g.message("Filter elevation map by a threshold...")
# set computational region
input = 'elevation'
g.region(raster=input)
# hardcoded:
# r.mapcalc('elev_100m = if(elevation > 100, elevation, null())', overwrite = True)
# with variables
output = 'elev_100m'
thresh = 100.0
r.mapcalc("%s = if(%s > %d, %s, null())" % (output, input, thresh, input), overwrite = True)
r.colors(map=output, color="elevation")
Example 3, using "shortcuts" and external raster format support:
#!/usr/bin/env python
# simple example for pyGRASS usage: raster processing via Modules approach
# Read GeoTIFF directly, write out GeoTIFF directly
import os
import tempfile
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster as r
from grass.pygrass.modules import Module
# use alias name for GRASS GIS commands with more than one dot in name
r.external_out = Module('r.external.out')
# get the raster elevation map into GRASS without import
geotiff = 'elevation.tif' # with path
curr_raster='myraster'
r.external(input=geotiff, output=curr_raster)
# define output directory for files resulting from GRASS calculation:
gisdb = os.path.join(tempfile.gettempdir(), 'gisoutput')
try:
os.stat(gisdb)
except:
os.mkdir(gisdb)
g.message("Output will be written to %s" % gisdb)
# define where to store the resulting files and format
r.external_out(directory=gisdb, format='GTiff')
# do the GRASS GIS job
g.message("Filter elevation map by a threshold...")
# set computational region
g.region(raster=curr_raster)
# generate GeoTIFF
output = 'elev_100m.tif'
thresh = 100.0
r.mapcalc("%s = if(%s > %d, %s, null())" % (output, curr_raster, thresh, curr_raster), overwrite = True)
r.colors(map=output, color="elevation")
# cease GDAL mode, back to GRASS data writing mode
r.external_out(flags = 'r')
# the resulting GeoTIFF file is in the gisdb directory
Example 4:
#!/usr/bin/env python
# Example for pyGRASS usage - vector API
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.vector import VectorTopo
from grass.pygrass.modules import Module
vectmap = 'myzipcodes_wake'
g.message("Importing SHAPE file ...")
ogrimport = Module('v.in.ogr')
ogrimport('/home/neteler/gis_data/zipcodes_wake.shp', output=vectmap)
g.message("Assessing vector topology...")
zipcodes = VectorTopo(vectmap)
# Open the map with topology:
zipcodes.open()
# query number of topological features
areas = zipcodes.number_of("areas")
islands = zipcodes.number_of("islands")
print 'Map: <' + vectmap + '> with %d areas and %d islands' % (areas, islands)
dblink = zipcodes.dblinks[0]
print 'DB name:'
print dblink.database
table = dblink.table()
print 'Column names:'
print table.columns.names()
print 'Column types:'
print table.columns.types()
zipcodes.close()
- For more examples, see pygrass
Python Ctypes Interface
This interface allows calling GRASS library functions from Python scripts. See Python Ctypes Examples for details.
Examples:
- Latest and greatest: GRASS 7 Python scripts
Sample script for GRASS raster access (use within GRASS, Spearfish session):
#!/usr/bin/env python
## TODO: update example to Ctypes
import os, sys
from grass.lib import grass
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
import os, sys
from grass.lib import grass
from grass.lib import vector as grassvect
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)
Using ipython
Using ipython is great due to the TAB completion. Don't remember the function name or curious about what's offered? Hit TAB (maybe twice for a list).
Here an example how to connect to C functions directly from Python:
grass76
# ... in the GRASS GIS terminal
ipython
In [1]: from grass.lib import gis
In [2]: gis.<TAB>
Display all 729 possibilities? (y or n)
gis.ARRAY gis.G_fatal_longjmp gis.G_set_key_value
gis.ArgumentError gis.G_file_name gis.G_set_keywords
gis.Array gis.G_file_name_misc gis.G_set_ls_exclude_filter
...
gis.G_fatal_error gis.G_set_gisrc_mode gis.wstring_at
In [2]: gis.G_message("Hello world")
Hello world
A full ipython workshop is available here: https://github.com/wenzeslaus/python-grass-addon
wxPython GUI development
- See the wxGUI wiki page
3rd party Python software connected to GRASS GIS
- PyWPS (http://pywps.org/)
- QGIS Processing plugin
FAQ
- Q: Error message "execl() failed: Permission denied" - what to do?
- A: Be sure that the execute bit of the script is set.
Links
General guides
- Wikibook Python Programming
- Quick Python tutorial for programmers of other languages
- More Python tutorials for programmers
- Python programming style guide
- Python Editors
Programming
- Python and GRASS:
- Library interfaces: [GRASS Python Scripting Library https://grass.osgeo.org/grass78/manuals/libpython/]
- Graphical user interface (GUI): [GRASS wxPython-based GUI https://grass.osgeo.org/grass-stable/manuals/wxGUI.html]
- PyWPS, GRASS-Web Processing Service: WPS
- 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 (initial historical steps):
- A Python sweeps in the GRASS - A. Frigeri 2006
- GRASS goes web: PyWPS - J. Cepicky 2006 (see also WPS)
References
- Zambelli, P., Gebbert, S., Ciolli, M., 2013. Pygrass: An Object Oriented Python Application Programming Interface (API) for Geographic Resources Analysis Support System (GRASS) Geographic Information System (GIS). ISPRS International Journal of Geo-Information 2, 201–219. (DOI | PDF)
- GRASS GIS and the Python geospatial modules (Shapely, PySAL,...)