Difference between revisions of "GRASS and Python"

From GRASS-Wiki
Jump to: navigation, search
(Python extensions for GRASS GIS)
(References: +GRASS GIS and the Python geospatial modules (Shapely, PySAL,...))
 
(206 intermediate revisions by 26 users not shown)
Line 1: Line 1:
==Python SIGs==
+
''This page discusses usage 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 [http://grass.osgeo.org/grass76/manuals/libpython/ Python API for GRASS GIS 7] documentation.''
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 extensions for GRASS GIS==
+
Python is a programming language which is more powerful than shell scripting but easier and more forgiving than C.
=== wxPython GUI development for GRASS ===
+
The Python script can contain simple module description definitions which will be processed with {{cmd|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:
'''[http://www.public.asu.edu/~cmbarton/files/grass_wxpython/ Link to wxPython prototype GUI for GRASS 6]'''
 
  
'''Update 00:50, 9 August 2006 (CEST) '''New version will run directly from GRASS prompt with new startup script. Now put gism.py and associated files into $GISBASE/etc/gmwxp and add a new script (gm.wxp) to the $GISBASE/scripts folder. Then simply type gm.wxp& from the GRASS command prompt to get the new prototype wxPython GUI for GRASS. See instructions in today's package update. [[User:Cmbarton|Cmbarton]]
+
<source lang="python">
 +
import grass.script as gscript
 +
</source>
  
'''Update 23:59, 7 August 2006 (CEST) '''New version with nearly complete raster menu by Yann Chemin. [[User:Cmbarton|Cmbarton]]
+
The related files are at $GISBASE/etc/python/grass/script/*.py. See below for more details.
  
'''Update 19:02, 7 August 2006 (CEST)'''I just uploaded a new version of gism.py and related files. I'll keep an archive and so am changing the link to go to the folder rather than the file itself so I won't have to keep changing the link. This update includes the following changes:
+
''Note: For code which needs access to the power of C, you can access the GRASS C library functions via [[GRASS and Python#Python Ctypes Interface|the Python "ctypes" interface]].''
*The biggest change in wrapping in grassgui.py. This provides autogenerated wxPython GUI dialogs for all grass commands.
 
This can now be called from the command console (just type in the command name with no arguments as you do now) or the
 
prototype menus. I updated grassgui.py to current wxPython syntax (mostly) and added methods to make it callable from
 
other modules.
 
*Methods added to gism.py and grassgui.py that automatically restart them with pythonw for the Mac
 
--[[User:Cmbarton|Cmbarton]] 19:02, 7 August 2006 (CEST)
 
  
Initial development has begun on the next generation UI for GRASS. wxPython is the leading contender for a platform in which to continue development of the GRASS GUI. wxPython is a Python implementation of the well known wxWidgets (formerly wxWindows) interface development platform for C++. An initial demonstration was developed by Jachym Cepicky to show the possibility of controling GRASS via wxPython. A second generation prototype has been completed, building on Jachym's work. It is available for testing and further development at link above.
+
=== Integrated Python shell ===
--[[User:Cmbarton|Cmbarton]] 07:15, 4 August 2006 (CEST)
 
  
===Python-SWIG-GRASS interface===
+
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.
There is a prototype GRASS-SWIG interface available (thanks to Sajith VK), find it in GRASS 6-CVS: swig/python/
 
  
Background: [http://www.swig.org SWIG] (Simplified Wrapper and Interface Generator) is:
+
[[Image:wxgui-pyshell.png|center|400px|Embedded interactive Python Shell in wxGUI Layer Manager]]
  
* A compiler that turns ANSI C/C++ declarations into scripting language interfaces.
+
=== Integrated Python editor ===
* Completely automated (produces a fully working Python extension module).
 
* Language neutral. SWIG can also target Tcl, Perl, Guile, MATLAB, etc...
 
* Attempts to eliminate the tedium of writing extension modules.
 
  
'''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 [http://freegis.org/cgi-bin/viewcvs.cgi/grass6/gui/wxpython/ GRASS 6 wxPython interface] for inspiration. Important is to auto-generate the GRASS-Python class at compile time with a Python script.
+
The [[wxGUI]] Layer Manager in GRASS GIS 7.2.0+ comes with a "Simple Python Editor" which enables users to author Python scripts directly in GRASS GIS GUI. User can also run the script easily in GRASS GIS environment with all the dependencies loaded. The editor comes with several examples, templates, and links to documentation.
  
=== Python-GRASS add-ons ===
+
[[File:Simple python editor v buffer.png|300px|thumb|right|Python interactive shell (console) and a simple Python editor are a powerful option how to interact with GRASS GIS as well as extent its functionality]]
  
Stand-alone addons:
+
=== External Python editors (IDE) ===
  
# Jáchym Čepický's G-ps.map, a GUI to typeset printable maps with ps.map (http://les-ejk.cz/?cat=gpsmap)
+
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]].
# 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://les-ejk.cz/?cat=pywps)
 
  
=== Using Grass gui.tcl in python ===
+
==== The correct editor settings for Python indentation ====
  
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.
+
Be sure to use only white spaces and no tabs to indent Python code!
  
  import Tkinter
+
See: https://trac.osgeo.org/grass/wiki/Submitting/Python#Editorsettingsfor4-spaceindentation
import os
+
 
 +
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.<br>
 +
  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 [https://trac.osgeo.org/grass/wiki/Submitting/Python Submitting Python].
 +
 
 +
=== Creating Python scripts that call GRASS functionality from outside ===
 +
 
 +
For calling GRASS functionality from outside, see the [https://grass.osgeo.org/grass76/manuals/libpython/script.html#module-script.setup 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 ([[:File:Wxgui-pyshell.png|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/grass76) and the bin.<YOUR_ARCH>/grass76 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 [https://grass.osgeo.org/grass76/manuals/libpython/script_intro.html GRASS GIS Python scripting with script package].
 +
 
 +
For more details, see
 +
* [[Module command line parser]]
 +
* [[GRASS Python Scripting Library#Parsing the options and flags]]
 +
 
 +
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 [https://grass.osgeo.org/grass71/manuals/parser_standard_options.html 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 dialogs 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 ===
 +
 
 +
* See [[GRASS 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 '''[[Python/pygrass|pygrass]]'''
 +
 
 +
'''Example 1''':
 +
<source lang="python">
 +
from grass.pygrass.modules import Module
 +
 
 +
slope_aspect = Module("r.slope.aspect")
 +
slope_aspect(elevation='elevation', slope='slp', aspect='asp',
 +
              format='degrees', overwrite=True)
 +
</source>
 +
 
 +
'''Example 2''', using "shortcuts":
 +
<source lang="python">
 +
#!/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")
 +
</source>
 +
 
 +
 
 +
'''Example 3''', using "shortcuts" and external raster format support:
 +
<source lang="python">
 +
#!/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
 +
</source>
 +
 
 +
'''Example 4''':
 +
<source lang="python">
 +
#!/usr/bin/env python
 
   
 
   
# Startup (once):
+
# Example for pyGRASS usage - vector API
+
 
tk = Tkinter.Tk()
+
from grass.pygrass.modules.shortcuts import general as g
tk.eval ("wm withdraw .")
+
from grass.pygrass.vector import VectorTopo
tk.eval ("source $env(GISBASE)/etc/gui.tcl")
+
from grass.pygrass.modules import Module
# Here you could do various things to change what the gui does
+
 
# See gui.tcl and README.GUI
+
vectmap = 'myzipcodes_wake'
+
 
# Make a gui (per dialog)
+
g.message("Importing SHAPE file ...")
# This sets up a window for the command.
+
ogrimport = Module('v.in.ogr')
# This can be different to integrate with tkinter:
+
ogrimport('/home/neteler/gis_data/zipcodes_wake.shp', output=vectmap)
tk.eval ('set path ".dialog$dlg"')
+
 
tk.eval ('toplevel .dialog$dlg')
+
g.message("Assessing vector topology...")
# Load the code for this command:
+
zipcodes = VectorTopo(vectmap)
fd = os.popen ("d.vect --tcltk")
+
 
gui = fd.read()
+
# Open the map with topology:
# Run it
+
zipcodes.open()
tk.eval(gui)
+
 
dlg = tk.eval('set dlg') # This is used later to get and set
+
# query number of topological features
   
+
areas  = zipcodes.number_of("areas")
# Get the current command in the gui we just made:
+
islands = zipcodes.number_of("islands")
currentcommand = tk.eval ("dialog_get_command " + dlg)
+
print 'Map: <' + vectmap + '> with %d areas and %d islands' % (areas, islands)
+
 
# Set the command in the dialog we just made:
+
dblink = zipcodes.dblinks[0]
tk.eval ("dialog_set_command " + dlg + " {d.vect map=roads}")
+
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()
 +
</source>
 +
 
 +
* For more examples, see '''[[Python/pygrass|pygrass]]'''
 +
 
 +
=== Python Ctypes Interface ===
 +
 
 +
This interface allows calling GRASS library functions from Python scripts. See [[Python Ctypes Examples]] for details.
 +
 
 +
Examples:
 +
 
 +
* GRASS 7: [http://trac.osgeo.org/grass/browser/grass/trunk/doc/python/raster_example_ctypes.py raster], [http://trac.osgeo.org/grass/browser/grass/trunk/doc/python/vector_example_ctypes.py vector] example
 +
 
 +
* Latest and greatest: GRASS 7 Python {{src|scripts}}
 +
 
 +
* <strike>[[PythonSwigExamples|More complex examples]]</strike> <<-- TODO: update to Ctypes
 +
 
 +
Sample script for GRASS 6 raster access (use within GRASS, Spearfish session):
 +
<source lang="python">
 +
#!/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)
 +
</source>
 +
 
 +
Sample script for vector access (use within GRASS, Spearfish session):
 +
 
 +
<source lang="python">
 +
#!/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)
 +
</source>
 +
 
 +
=== Using ipython ===
 +
 
 +
Using [http://ipython.org/ 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:
 +
 
 +
<source lang="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
 +
</source>
 +
 
 +
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 ==
 
== Links ==
  
* GRASS-SWIG interface http://mpa.itc.it/markus/grass61progman/swig/
+
=== General guides ===
* SWIG http://www.swig.org/
+
 
* SIP (C/C++ bindings generator) http://directory.fsf.org/all/Python-SIP.html
+
* [http://en.wikibooks.org/wiki/Python_Programming/ Wikibook Python Programming]
 +
* [http://www.poromenos.org/tutorials/python Quick Python tutorial] for programmers of other languages
 +
*: [http://wiki.python.org/moin/BeginnersGuide/Programmers More Python tutorials] for programmers
 +
* [http://www.python.org/dev/peps/pep-0008/ Python programming style guide]
 +
* [http://wiki.python.org/moin/PythonEditors Python Editors]
 +
 
 +
=== Programming ===
 +
 
 +
* Python and GRASS:
 +
** Library interfaces: [GRASS Python Scripting Library http://grass.osgeo.org/programming7/pythonlib.html]
 +
** Graphical user interface (GIU): [GRASS wxPython-based GUI http://grass.osgeo.org/programming7/wxpythonlib.html]
 +
** PyWPS, GRASS-Web Processing Service: [[WPS]]
 +
 
 +
* Python and OSGeo:
 +
** [http://wiki.osgeo.org/wiki/OSGeo_Python_Library OSGeo Python Library]
 +
 
 
* Python and GDAL/OGR:
 
* Python and GDAL/OGR:
 
** [http://mapserver.gis.umn.edu/community/conferences/MUM3/workshop/python Open Source Python GIS Hacks Mum'03]
 
** [http://mapserver.gis.umn.edu/community/conferences/MUM3/workshop/python Open Source Python GIS Hacks Mum'03]
Line 86: Line 440:
 
** http://zcologia.com/news/categorylist_html?cat_id=8
 
** http://zcologia.com/news/categorylist_html?cat_id=8
 
** http://www.perrygeo.net/wordpress/?p=4
 
** http://www.perrygeo.net/wordpress/?p=4
 +
 
* Python bindings to PROJ:
 
* Python bindings to PROJ:
 
** http://www.cdc.noaa.gov/people/jeffrey.s.whitaker/python/pyproj.html
 
** http://www.cdc.noaa.gov/people/jeffrey.s.whitaker/python/pyproj.html
 +
 +
* Python and GIS:
 +
** [http://gispython.org/ Open Source GIS-Python Laboratory]
 +
 +
* Python and Statistics:
 +
** [http://rpy.sourceforge.net/ RPy] - Python interface to the R-statistics programming language
 +
 +
* Bindings:
 +
** SIP (C/C++ bindings generator) http://directory.fsf.org/all/Python-SIP.html
 +
** [http://www.cython.org/ Cython] - C-Extensions for Python (compile where speed is needed)
 +
 
* Other external projects
 
* Other external projects
** Scientific Python: http://www.scipy.org/
+
** [http://www.scipy.org Scientific Python]
 +
** [http://wiki.python.org/moin/NumericAndScientific Numeric and Scientific]
 +
** [http://w3.pppl.gov/~hammett/comp/python/python.html Info on Python for Scientific Applications]
 +
 
 +
=== Presentations ===
 +
 
 +
* http://www.slideshare.net/search/slideshow?q=pygrass
 +
 
 +
From FOSS4G2006 (initial historical steps):
 +
* [http://www.foss4g2006.org/materialDisplay.py?contribId=136&amp;sessionId=48&amp;materialId=slides&amp;confId=1 A Python sweeps in the GRASS] - A. Frigeri 2006
 +
* [http://www.foss4g2006.org/materialDisplay.py?contribId=67&amp;sessionId=48&amp;materialId=slides&amp;confId=1 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. ([http://dx.doi.org/10.3390/ijgi2010201 DOI] | [http://www.mdpi.com/2220-9964/2/1/201/pdf PDF])
 +
 
 +
* GRASS GIS and the Python geospatial modules (Shapely, PySAL,...)
 +
** See http://osgeo-org.1560.x6.nabble.com/GRASS-and-the-Python-geospatial-modules-Shapely-PySAL-td4985075.html
  
[[Category:Development]]
+
{{Python}}
[[Category:Linking to other languages]]
 

Latest revision as of 09:12, 15 November 2019

This page discusses usage 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.

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.

Embedded interactive Python Shell in wxGUI Layer Manager

Integrated Python editor

The wxGUI Layer Manager in GRASS GIS 7.2.0+ comes with a "Simple Python Editor" which enables users to author Python scripts directly in GRASS GIS GUI. User can also run the script easily in GRASS GIS environment with all the dependencies loaded. The editor comes with several examples, templates, and links to documentation.

Python interactive shell (console) and a simple Python editor are a powerful option how to interact with GRASS GIS as well as extent its functionality

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/grass76) and the bin.<YOUR_ARCH>/grass76 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 dialogs 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()

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 6 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

3rd party Python software connected to GRASS GIS

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

Programming

  • Python and Statistics:
    • RPy - Python interface to the R-statistics programming language

Presentations

From FOSS4G2006 (initial historical steps):

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)