Skip to content

GRASS GIS Scripting with Python

GRASS GIS can be automated and extended using Python. This page demonstrates how to write a complete geospatial analysis workflow using GRASS GIS commands in Python using grass.script and pygrass.


Python Script: Import Raster Data

Import Raster files

import os
import sys
import subprocess
import grass.script as gs
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster as r
from grass.pygrass.modules.shortcuts import display as d
from grass.pygrass.modules.shortcuts import vector as v
from grass.pygrass.gis import *
import grass.script as grass
import grass.script.setup as gsetup
import re




# Main function
def main(gisdb, location, mapset): 

    os.environ['GISDBASE'] = gisdb
    os.environ['LOCATION_NAME'] = location

    # Check if mapset exists; if not, create it
    mapset_path = os.path.join(gisdb, location, mapset)
    if not os.path.exists(mapset_path):
        print(f"Mapset '{mapset}' does not exist. Creating new mapset...")
        # Create the new mapset
        gs.run_command('g.mapset', flags='c', mapset=mapset, location=location, dbase=GISDBASE)
    else:
        print(f"Mapset '{mapset}' already exists.")

    # Initialize GRASS session
    gsetup.init(gisdb, location, mapset)
    print(f"GRASS GIS session initialized in {gisdb}/{location}/{mapset}")


    input_folder = "/Volumes/ExternalSSD/eqipa_data/pcp_imd_monthly"
    for file in os.listdir(input_folder):
        if file.endswith(".tif"):
            full_path = os.path.join(input_folder, file)
            name = os.path.splitext(file)[0]  
            print(f"Importing {file} as {name}")
            gs.run_command('r.import', input=full_path, output=name)




if __name__ == '__main__':
    GISDBASE = "/Volumes/ExternalSSD/eqipa_data/grassdata"
    LOCATION_NAME = "eqipa"
    MAPSET = "data_monthly"                 

    # Call the main function
    main(GISDBASE, LOCATION_NAME, MAPSET)

Python Script: Resampling of Raster Maps

Resampling

import os
import sys
import subprocess
import grass.script as gs
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster as r
from grass.pygrass.modules.shortcuts import display as d
from grass.pygrass.modules.shortcuts import vector as v
from grass.pygrass.gis import *
import grass.script as grass
import grass.script.setup as gsetup
import re




# Main function
def main(gisdb, location, mapset): 

    shapefile = 'IndiaBoundary.geojson' 

    os.environ['GISDBASE'] = gisdb
    os.environ['LOCATION_NAME'] = location

    # Check if mapset exists; if not, create it
    mapset_path = os.path.join(gisdb, location, mapset)
    if not os.path.exists(mapset_path):
        print(f"Mapset '{mapset}' does not exist. Creating new mapset...")
        # Create the new mapset
        gs.run_command('g.mapset', flags='c', mapset=mapset, location=location, dbase=GISDBASE)
    else:
        print(f"Mapset '{mapset}' already exists.")

    # Initialize GRASS session
    gsetup.init(gisdb, location, mapset)
    print(f"GRASS GIS session initialized in {gisdb}/{location}/{mapset}")



    vector_name = os.path.splitext(os.path.basename(shapefile))[0]


    # Import shapefile
    gs.run_command('v.import', input=shapefile, output=vector_name, overwrite=True)

    # Set region
    gs.run_command('g.region', vector=vector_name, res=0.00292)

    start_yr = '2023'
    end_yr = '2024'



    for year in range(int(start_yr), int(end_yr) + 1):
        for month in range(1,13):
            input_raster = f"imd_pcp_m_{year}_{month:02d}"
            resampled_raster = f"imd_pcp_resamp_m_{year}_{month:02d}"

            # This resampling only change the pixel size, will not apply any interpolation or will not change pixel value
            gs.run_command(
                'r.resample',
                input=input_raster,
                output=resampled_raster,
                overwrite=True
            )





if __name__ == '__main__':
    GISDBASE = "/Volumes/ExternalSSD/eqipa_data/grassdata"
    LOCATION_NAME = "eqipa"
    MAPSET = "data_monthly"                 

    # Call the main function
    main(GISDBASE, LOCATION_NAME, MAPSET)

Python Script: Aggregate Monthly Maps to Annual

Monthly to Annual Maps

import os
import sys
import subprocess
import grass.script as gs
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster as r
from grass.pygrass.modules.shortcuts import display as d
from grass.pygrass.modules.shortcuts import vector as v
from grass.pygrass.gis import *
import grass.script as grass
import grass.script.setup as gsetup
import re




# Main function
def main(gisdb, location, mapset): 

    shapefile = 'IndiaBoundary.geojson' 

    os.environ['GISDBASE'] = gisdb
    os.environ['LOCATION_NAME'] = location

    # Check if mapset exists; if not, create it
    mapset_path = os.path.join(gisdb, location, mapset)
    if not os.path.exists(mapset_path):
        print(f"Mapset '{mapset}' does not exist. Creating new mapset...")
        # Create the new mapset
        gs.run_command('g.mapset', flags='c', mapset=mapset, location=location, dbase=GISDBASE)
    else:
        print(f"Mapset '{mapset}' already exists.")

    # Initialize GRASS session
    gsetup.init(gisdb, location, mapset)
    print(f"GRASS GIS session initialized in {gisdb}/{location}/{mapset}")



    # Argi year: June - May
    start_month='6'
    end_month='5'

    start_yr = '2023'
    end_yr = '2023'

    agri_yr_timerange = []

    for year in range(int(start_yr), int(end_yr) + 1):
        months_range = []

        if int(start_month) > int(end_month):
            for month in range(int(start_month), 13):
                months_range.append(f"{year}_{month:02d}")
            for month in range(1, int(end_month) + 1):
                months_range.append(f"{year + 1}_{month:02d}")
        else:
            for month in range(int(start_month), int(end_month) + 1):
                months_range.append(f"{year}_{month:02d}")
        agri_yr_timerange.append(months_range)

        print('agri_yr_timerange',agri_yr_timerange)

        timerange = range(int(start_yr), int(end_yr) + 1)
        years = list(timerange)

        # Create strings like "2022_2023"
        years_str = [f"{y}_{y + 1}" for y in years]

        print("years_str")
        print(years_str)

    # Add other mapsets to the current session
    gs.run_command('g.mapsets', mapset="data_monthly", operation="add")
    vector_name = os.path.splitext(os.path.basename(shapefile))[0]


    # Import shapefile
    gs.run_command('v.import', input=shapefile, output=vector_name, overwrite=True)

    # Set region
    gs.run_command('g.region', vector=vector_name, res=0.00292)


    for y in agri_yr_timerange:
        yr = int(y[0].split("_")[0]) 
        yr_string = f'{yr}_{yr + 1}' 
        print(yr_string)

        eta_maps_list = [f"wapor_eta_m_{i}" for i in y]
        eta_out_map_name = f'wapor_eta_a_{yr_string}'

        tbp_maps_list = [f"wapor_tbp_m_{i}" for i in y]
        tbp_out_map_name = f'wapor_tbp_a_{yr_string}'

        pcp_maps_list = [f"imd_pcp_resam_m_{i}" for i in y]
        pcp_out_map_name = f'imd_pcp_resamp_a_{yr_string}'

        gs.run_command('r.series', input=eta_maps_list, output=eta_out_map_name, method='sum', overwrite=True)
        gs.run_command('r.series', input=tbp_maps_list, output=tbp_out_map_name, method='sum', overwrite=True)
        gs.run_command('r.series', input=pcp_maps_list, output=pcp_out_map_name, method='sum', overwrite=True)




if __name__ == '__main__':
    GISDBASE = "/Volumes/ExternalSSD/eqipa_data/grassdata"
    LOCATION_NAME = "eqipa"
    MAPSET = "data_annual"                 

    # Call the main function
    main(GISDBASE, LOCATION_NAME, MAPSET)

Python Script: Raster Calculation and Masking

Raster Calculation

import os
import sys
import subprocess
import grass.script as gs
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster as r
from grass.pygrass.modules.shortcuts import display as d
from grass.pygrass.modules.shortcuts import vector as v
from grass.pygrass.gis import *
import grass.script as grass
import grass.script.setup as gsetup
import re




# Main function
def main(gisdb, location, mapset): 

    shapefile = 'IndiaBoundary.geojson' 

    os.environ['GISDBASE'] = gisdb
    os.environ['LOCATION_NAME'] = location

    # Check if mapset exists; if not, create it
    mapset_path = os.path.join(gisdb, location, mapset)
    if not os.path.exists(mapset_path):
        print(f"Mapset '{mapset}' does not exist. Creating new mapset...")
        # Create the new mapset
        gs.run_command('g.mapset', flags='c', mapset=mapset, location=location, dbase=GISDBASE)
    else:
        print(f"Mapset '{mapset}' already exists.")

    # Initialize GRASS session
    gsetup.init(gisdb, location, mapset)
    print(f"GRASS GIS session initialized in {gisdb}/{location}/{mapset}")



    vector_name = os.path.splitext(os.path.basename(shapefile))[0]


    # Import shapefile
    gs.run_command('v.import', input=shapefile, output=vector_name, overwrite=True)

    g.mapsets(mapset="nrsc_lulc", operation="add")


    # Set region
    gs.run_command('g.region', vector=vector_name, res=0.00292)

    start_yr = '2023'
    end_yr = '2023'


    timerange = range(int(start_yr), int(end_yr) + 1)
    years = list(timerange)

    years_str = [f"{y}_{y + 1}" for y in years]

    print("years_str")
    print(years_str)


    for y in years_str:
        # Apply raster mask
        # gs.run_command('r.mask', raster=f'LULC_250k_{y}', maskcats='2 3 4 5 7', overwrite=True)
        gs.run_command('r.mask', raster=f'LULC_250k_2022_2023', maskcats='2 3 4 5 7', overwrite=True)

        # Perform map calculations
        gs.mapcalc(f"wapor_eta_a_cropland_{y} = wapor_eta_a_{y}", overwrite=True)
        gs.mapcalc(f"wapor_tbp_a_cropland_{y} = wapor_tbp_a_{y}", overwrite=True)
        gs.mapcalc(f"wapor_bwp_a_{y} = wapor_tbp_a_{y} / (wapor_eta_a_{y} * 10)",overwrite=True)
        gs.run_command('r.mask', flags='r')


if __name__ == '__main__':
    GISDBASE = "/Volumes/ExternalSSD/eqipa_data/grassdata"
    LOCATION_NAME = "eqipa"
    MAPSET = "data_annual"                 

    # Call the main function
    main(GISDBASE, LOCATION_NAME, MAPSET)

Python Script: Export GeoTIFF's

Export GeoTIFF's

import os
import sys
import subprocess
import grass.script as gs
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster as r
from grass.pygrass.modules.shortcuts import display as d
from grass.pygrass.modules.shortcuts import vector as v
from grass.pygrass.gis import *
import grass.script as grass
import grass.script.setup as gsetup
import re




# Main function
def main(gisdb, location, mapset): 


    os.environ['GISDBASE'] = gisdb
    os.environ['LOCATION_NAME'] = location

    # Check if mapset exists; if not, create it
    mapset_path = os.path.join(gisdb, location, mapset)
    if not os.path.exists(mapset_path):
        print(f"Mapset '{mapset}' does not exist. Creating new mapset...")
        # Create the new mapset
        gs.run_command('g.mapset', flags='c', mapset=mapset, location=location, dbase=GISDBASE)
    else:
        print(f"Mapset '{mapset}' already exists.")

    # Initialize GRASS session
    gsetup.init(gisdb, location, mapset)
    print(f"GRASS GIS session initialized in {gisdb}/{location}/{mapset}")



    # Directory to export rasters
    export_dir = "/Volumes/ExternalSSD/eqipa_data/pcp_resamp"
    if not os.path.exists(export_dir):
        os.makedirs(export_dir)

    # create list of all rasters to export
    raster_names=[]
    start_yr = '2023'
    end_yr = '2023'

    for year in range(int(start_yr), int(end_yr) + 1):
        for month in range(1, 13):
            file_name = f"imd_pcp_resamp_m_{year}_{month:02d}"
            raster_names.append(file_name)



    # raster_names=gs.list_grouped(type=['raster'], pattern=f'imd_pcp_resamp_m_*')['data_monthly']



    # Export rasters using r.out.gdal
    for raster in raster_names:
        output_tif = f"{export_dir}/{raster}.tif"
        gs.run_command(
            'r.out.gdal',
            input=raster,
            output=output_tif,
            format='GTiff',
            # createopt="COMPRESS=LZW",
            overwrite=True
        )
        print(f"Raster {raster} exported to {output_tif}")


if __name__ == '__main__':
    GISDBASE = "/Volumes/ExternalSSD/eqipa_data/grassdata"
    LOCATION_NAME = "eqipa"
    MAPSET = "data_monthly"                 

    # Call the main function
    main(GISDBASE, LOCATION_NAME, MAPSET)