Introduction to Python scripting in GRASS GIS 7
Python code can be executed in GRASS GIS GUI in the Python tab in the Layer Manager. You can enter any Python code, we can, for example, do a simple calculation:import math
5 * math.sin(math.pi/2)
This is an quick way to run small code snippets and
test Python code you are currently working on.
You can find your own way of running Python code in GRASS GIS, but for now use the Simple Python Editor which comes with GRASS GIS 7.2 or newer or the interactive Python console in the Python tab where you should only paste one line at a time. For scripts written in an external editor such as Spyder, Atom, or Notepad++, you can use File > Launch script which will run your script in the Console tab and you will be able to run it again by using arrow up and Enter key as in case of other commands.
We are using North Carolina GRASS Location in this assignment. If you already have North Carolina GRASS GIS sample dataset, you don't have to download anything.
Python Scripting Library
The GRASS GIS 7 Python Scripting Library provides functions to call GRASS modules within scripts as subprocesses. The most often used functions include:run_command
: most often used with modules which output raster/vector data and where text output is not expectedread_command
: used when we are interested in text outputparse_command
: used with modules producing text output as key=value pairwrite_command
: for modules expecting text input from either standard input or file
Calling GRASS GIS modules
We start by importing GRASS GIS Python Scripting Library:import grass.script as gs
First, we set the computational extent and resolution to the raster layer elevation
.
gs.run_command('g.region', raster='elevation')
The run_command()
function is used most often.
Here, we apply the focal operation average
(r.neighbors) to smooth
the elevation raster layer.
gs.run_command('r.neighbors', input='elevation', output='elev_smoothed', method='average', flags='c')
Note that the syntax is similar to the
syntax used in the manuals, command line, or Bash;
one of the bigger differences is that the flags are specified in
a parameter instead of using dashes.
Calling GRASS GIS modules with textual input or output
Textual output from modules can be captured using theread_command()
function.
First we get the current computational region settings:
print(gs.read_command('g.region', flags='p'))
Then we get univariate raster statistics and we add a g
flag to what we would use in the command line.
print(gs.read_command('r.univar', map='elev_smoothed', flags='g'))
Certain modules can produce output in key-value format which is enabled
by the g
flag.
The parse_command()
function automatically parses this
output and returns a dictionary.
In the following example, we call g.proj to
get the projection parameters of the actual location.
gs.parse_command('g.proj', flags='g')
For comparison, below is the same example, but using the
read_command()
function.
print(gs.read_command('g.proj', flags='g'))
Certain modules require the text input be in a file or provided as
standard input. Using the write_command()
function we can
conveniently pass the string as the standard input to the module. Here,
we are creating a new vector with one point with the
v.in.ascii module. Note that the
stdin
parameter is not used as a module parameter,
but its content is passed as the standard input to the subprocess.
gs.write_command('v.in.ascii', input='-', stdin='%s|%s' % (635818.8, 221342.4), output='view_point')
Convenient wrapper functions
Some modules have wrapper functions to simplify the most common tasks. We can obtain the information about the vector layer which we just created with the v.info wrapper.gs.vector_info('view_point')
It is also possible to retrieve the raster layer history
(r.support) and layer information
(r.info) or to query
(r.what) raster layer pixel values.
The pixel (cell) values are obtained using the
raster_what()
function:
gs.raster_what('elevation', [[635818.8, 221342.4], [635710, 221330]])
As another example, the r.mapcalc wrapper for
raster algebra allows using a long expressions.
gs.mapcalc("elev_strip = if(elevation > 100 && elevation < 125, elevation, null())")
print(gs.read_command('r.univar', map='elev_strip', flags='g'))
The g.region wrapper is a convenient way to
retrieve the current region settings (i.e., computational region). It
returns a dictionary with values converted to appropriate types (floats
and ints).
region = gs.region()
print region
# cell area in map units (in projected Locations)
region['nsres'] * region['ewres']
We can list data stored in a GRASS GIS location with g.list wrappers.
With this function, the map layers are grouped by mapsets (in this
example, raster layers):
gs.list_grouped(['raster'])
Here is an example of a different g.list wrapper
which structures the output as list of pairs (name, mapset).
We also obtain current mapset with g.gisenv
wrapper.
current_mapset = gs.gisenv()['MAPSET']
gs.list_pairs('raster', mapset=current_mapset)
PyGRASS
Standard scripting with GRASS GIS modules in Python may seem cumbersome in certain cases namely when you have to do to things like iterating over vector features or raster rows/columns. PyGRASS library introduces several objects that allow to interact directly with the data, individual features and cells, using the underlying C API of GRASS GIS libraries.Working with vector data
Import the necessary classes:from grass.pygrass.vector import VectorTopo
from grass.pygrass.vector.geometry import Point
Create an instance of a new vector map:
points = VectorTopo('points')
Open the map in write mode:
points.open(mode='w')
Create some vector geometry features, like two points:
point1 = Point(635818.8, 221342.4)
point2 = Point(633627.7, 227050.2)
Add the above two points to the new vector map with:
points.write(point1)
points.write(point2)
Finally close the vector map:
points.close()
Now do the same thing using the context manager syntax provided by with,
and creating and setting also the attribute table:
# Define the columns of the new vector map
cols = [(u'cat', 'INTEGER PRIMARY KEY'),
(u'name', 'TEXT')]
with VectorTopo('points', mode='w', tab_cols=cols, overwrite=True) as points:
# save the point, category (identifier) and the attribute
points.write(point1, cat=1, attrs=('pub', ))
points.write(point2, cat=2, attrs=('restaurant', ))
# save the changes to the database
points.table.conn.commit()
Note: We don't need to close the vector map because it is already closed
by the context manager (used through the with
statement).
Reading an existing vector map:
with VectorTopo('points', mode='r') as points:
for point in points:
print(point, point.attrs['name'])
The above content is derived from workshop How to write a Python GRASS GIS 7 addon presented at FOSS4G Europe 2015. The relevant parts which contain more details are: