Opened 7 years ago

Last modified 5 years ago

#3227 new enhancement

g.gui.rlisetup: possibility to execute in command line ?

Reported by: tgrippa Owned by: grass-dev@…
Priority: normal Milestone: 8.0.0
Component: Raster Version: svn-trunk
Keywords: Cc:
CPU: Unspecified Platform: All

Description

I would like to ask about the possibility to use g.gui.rlisetup in command line (maybe a conversion of the gui in a module?). Indeed, while all other r.li modules could be passed in command line (interesting for process automation purposes) it is a pity that the only 'bottleneck' is the configuration file creation.

Thanks

Change History (8)

comment:1 by lucadelu, 7 years ago

It should be done, but it could not have all the capabilities of the gui module. Right now you can look at r.diversity code to see an example to create a configuration file on the fly

comment:2 by tgrippa, 7 years ago

Thanks a lot for your answer. I will see after the Christmas break if this "on the fly" configuration could match our needs.

comment:3 by neteler, 6 years ago

Milestone: 7.0.67.0.7

comment:4 by martinl, 5 years ago

Still relevant?

in reply to:  4 ; comment:5 by tgrippa, 5 years ago

Replying to martinl:

Still relevant?

Not relevant anymore. Finally, we bypass g.gui.rlisetup with a specific script in Python to create the setup files.

in reply to:  5 comment:6 by veroandreo, 5 years ago

Replying to tgrippa:

Replying to martinl:

Still relevant?

Not relevant anymore. Finally, we bypass g.gui.rlisetup with a specific script in Python to create the setup files.

Could you share how you did it? This is indeed interesting

comment:7 by tgrippa, 5 years ago

Here is the function. It allows to create setup file for landscape polygons. If there are several base rasters (the categorical raster on which to compute landscape metrics using r.li), it allows for the creation of multiple setup file.

I'm sure this function could be improved but at least has the merit of existing ;)

##### Function that create the r.li configuration file for a list of landcover raster.
### It enable to create in one function as many configuration file as the number of raster provided in 'listoflandcoverraster'.
### It could be use only in case study with a several landcover raster and only one landscape unit layer.
### So, the landscape unit layer if fixed and there are the landcover raster which change. 
# 'listoflandcoverraster' wait for a list with the name (string) of landcover rasters.
# 'landscape_polygons' wait for the name (string) of the vector layer containing the polygons to be used as landscape units.
# 'masklayerhardcopy' wait for a boolean value (True/False) depending if the user want to create hard copy of the landscape units mask layers or not.
# 'returnlistpath' wait for a boolean value (True/False) according to the fact that a list containing the path to the configuration files is desired.
# 'ncores' wait for a integer corresponding to the number of desired cores to be used for parallelization.

# Import GRASS lib
import grass.script as gscript

# Import libraries for multiprocessing 
import multiprocessing
from multiprocessing import Pool
from functools import partial 

# Function that copy the landscape unit raster masks on a new layer with name corresponding to the current 'landcover_raster'
def copy_landscapeunitmasks(current_landcover_raster,base_landcover_raster,landscape_polygons,landscapeunit_bbox,cat):
    ### Copy the landscape units mask for the current 'cat'
    # Define the name of the current "current_landscapeunit_rast" layer
    current_landscapeunit_rast=current_landcover_raster.split("@")[0]+"_"+landscape_polygons.split("@")[0]+"_"+str(cat)          
    base_landscapeunit_rast=base_landcover_raster.split("@")[0]+"_"+landscape_polygons.split("@")[0]+"_"+str(cat)          
    # Copy the the landscape unit created for the first landcover map in order to match the name of the current landcover map
    gscript.run_command('g.copy', overwrite=True, quiet=True, raster=(base_landscapeunit_rast,current_landscapeunit_rast))
    # Add the line to the text variable
    text="MASKEDOVERLAYAREA "+current_landscapeunit_rast+"|"+landscapeunit_bbox[cat]
    return text

# Function that create the r.li configuration file for the base landcover raster and then for all the binary rasters
def create_rli_configfile(listoflandcoverraster,landscape_polygons,
                          masklayerhardcopy=False,returnlistpath=True,ncores=2):
    # Check if 'listoflandcoverraster' is not empty
    if len(listoflandcoverraster)==0:
        sys.exit("The list of landcover raster is empty and should contain at least one raster name")
    # Check if rasters provided in 'listoflandcoverraster' exists to avoid error in mutliprocessing 
    for cur_rast in listoflandcoverraster:
        try:
            mpset=cur_rast.split("@")[1]
        except:
            mpset=""
        if cur_rast.split("@")[0] not in [x[0] for x in gscript.list_pairs(type='raster',mapset=mpset)]:
            sys.exit('Raster <%s> not found' %cur_rast)
    # Check if rasters provided in 'listoflandcoverraster' have the same extend and spatial resolution 
    raster={}
    for x, rast in enumerate(raster_list):
        raster[x]=gscript.raster_info(rast)
    key_list=raster.keys()
    for x in key_list[1:]:
        for info in ('north','south','east','west','ewres','nsres'):
            if not raster[0][info]==raster[x][info]:
                sys.exit("Some raster provided in the list have different spatial resolution or extend, please check")    
    # Get the version of GRASS GIS 
    version=grass.version()['version'].split('.')[0]
    # Define the folder to save the r.li configuration files
    if sys.platform=="win32":
        rli_dir=os.path.join(os.environ['APPDATA'],"GRASS"+version,"r.li")
    else: 
        rli_dir=os.path.join(os.environ['HOME'],".grass"+version,"r.li")
    if not os.path.exists(rli_dir):
        os.makedirs(rli_dir)
    ## Create an ordered list with the 'cat' value of landscape units to be processed.
    list_cat=[int(x) for x in gscript.parse_command('v.db.select', quiet=True, 
                                                        map=landscape_polygons, column='cat', flags='c')]
    list_cat.sort()
    # Declare a empty dictionnary which will contains the north, south, east, west values for each landscape unit
    landscapeunit_bbox={}
    # Declare a empty list which will contain the path of the configation files created
    listpath=[]
    # Declare a empty string variable which will contains the core part of the r.li configuration file
    maskedoverlayarea_1=""
    # Duplicate 'listoflandcoverraster' in a new variable called 'tmp_list'
    tmp_list=list(listoflandcoverraster)
    # Set the current landcover raster as the first of the list
    base_landcover_raster=tmp_list.pop(0) #The pop function return the first item of the list and delete it from the list at the same time
    # Loop trough the landscape units
    for cat in list_cat:
        # Extract the current landscape unit polygon as temporary vector
        tmp_vect="tmp_"+base_landcover_raster.split("@")[0]+"_"+landscape_polygons.split("@")[0]+"_"+str(cat)
        gscript.run_command('v.extract', overwrite=True, quiet=True, 
                            input=landscape_polygons, cats=cat, output=tmp_vect)
        # Set region to match the extent of the current landscape polygon, with resolution and alignement matching the landcover raster
        gscript.run_command('g.region', vector=tmp_vect, align=base_landcover_raster)
        # Rasterize the landscape unit polygon
        landscapeunit_rast=tmp_vect[4:]
        gscript.run_command('v.to.rast', overwrite=True, quiet=True, input=tmp_vect, output=landscapeunit_rast, use='cat', memory='3000')
        # Remove temporary vector
        gscript.run_command('g.remove', quiet=True, flags="f", type='vector', name=tmp_vect)
        # Set the region to match the raster landscape unit extent and save the region info in a dictionary
        region_info=gscript.parse_command('g.region', raster=landscapeunit_rast, flags='g')
        n=str(round(float(region_info['n']),5)) #the config file need 5 decimal for north and south
        s=str(round(float(region_info['s']),5))
        e=str(round(float(region_info['e']),6)) #the config file need 6 decimal for east and west
        w=str(round(float(region_info['w']),6))
        # Save the coordinates of the bbox in the dictionary (n,s,e,w)
        landscapeunit_bbox[cat]=n+"|"+s+"|"+e+"|"+w
        # Add the line to the maskedoverlayarea_1 variable
        maskedoverlayarea_1+="MASKEDOVERLAYAREA "+landscapeunit_rast+"|"+landscapeunit_bbox[cat]+"\n"

    # Compile the content of the r.li configuration file
    config_file_content="SAMPLINGFRAME 0|0|1|1\n"
    config_file_content+=maskedoverlayarea_1
    config_file_content+="RASTERMAP "+base_landcover_raster+"\n"
    config_file_content+="VECTORMAP "+landscape_polygons+"\n"

    # Create a new file and save the content
    configfilename=base_landcover_raster.split("@")[0]+"_"+landscape_polygons.split("@")[0]
    path=os.path.join(rli_dir,configfilename)
    listpath.append(path)
    f=open(path, 'w')
    f.write(config_file_content)
    f.close()
    
    # Continue creation of r.li configuration file and landscape unit raster the rest of the landcover raster provided
    while len(tmp_list)>0:
        # Initialize 'maskedoverlayarea_2' variable as an empty string
        maskedoverlayarea_2=""
        # Set the current landcover raster as the first of the list
        current_landcover_raster=tmp_list.pop(0) #The pop function return the first item of the list and delete it from the list at the same time
        if masklayerhardcopy: # If the user asked for hard copy of the landscape units mask layers
            # Copy all the landscape units masks for the current landcover raster
            p=Pool(ncores) #Create a pool of processes and launch them using 'map' function
            func=partial(copy_landscapeunitmasks,current_landcover_raster,base_landcover_raster,landscape_polygons,landscapeunit_bbox) # Set fixed argument of the function
            maskedoverlayarea_2=p.map(func,list_cat) # Launch the processes for as many items in the list and get the ordered results using map function
            p.close()
            p.join()
            # Compile the content of the r.li configuration file
            config_file_content="SAMPLINGFRAME 0|0|1|1\n"
            config_file_content+="\n".join(maskedoverlayarea_2)+"\n"
            config_file_content+="RASTERMAP "+current_landcover_raster+"\n"
            config_file_content+="VECTORMAP "+landscape_polygons+"\n"
        else: # If the user not asked for hard copy
            # Compile the content of the r.li configuration file
            config_file_content="SAMPLINGFRAME 0|0|1|1\n"
            config_file_content+=maskedoverlayarea_1  # If user do not asked for hard copy, the mask layers are the same than for the first configuration file
            config_file_content+="RASTERMAP "+current_landcover_raster+"\n"  # But the name of the RASTERMAP should be the one of the current landcover raster
            config_file_content+="VECTORMAP "+landscape_polygons+"\n"
        # Create a new file and save the content
        configfilename=current_landcover_raster.split("@")[0]+"_"+landscape_polygons.split("@")[0]
        path=os.path.join(rli_dir,configfilename)
        listpath.append(path)
        f=open(path, 'w')
        f.write(config_file_content)
        f.close()
    
    # Return a list of path of configuration files creates if option actived
    if returnlistpath:
        return list_cat,listpath
    else:
        return list_cat

comment:8 by lucadelu, 5 years ago

Milestone: 7.0.78.0.0
Version: unspecifiedsvn-trunk
Note: See TracTickets for help on using tickets.