From 92c46148bd1bb5829fa4fb5972d83aeb0456afac Mon Sep 17 00:00:00 2001 From: Paulo van Breugel Date: Mon, 12 Feb 2024 11:29:25 +0100 Subject: [PATCH] v.maxent.swd: update manual page, added option to thin point layers and bugfixes (#1022) - Included an example - Added option to thin species presence and background point layers. - Bug fixes Co-authored-by: Markus Neteler --- src/vector/v.maxent.swd/v.maxent.swd.html | 168 +++++++++++++++------- src/vector/v.maxent.swd/v.maxent.swd.py | 99 +++++++++---- 2 files changed, 188 insertions(+), 79 deletions(-) diff --git a/src/vector/v.maxent.swd/v.maxent.swd.html b/src/vector/v.maxent.swd/v.maxent.swd.html index 6d952d94db..5eccdd8bc8 100644 --- a/src/vector/v.maxent.swd/v.maxent.swd.html +++ b/src/vector/v.maxent.swd/v.maxent.swd.html @@ -1,97 +1,159 @@

DESCRIPTION

-v.maxent.swd Produce a set of SWD files as input to MaxEnt -3.3.3k+, based on a point vector layer(s) of species distribution(s), a -point layer with background points and user-defined raster layers. The -user can also let the function create the background point file. -(respecting region and mask, if set). +The v.maxent.swd takes one or more point vector layers with +the location of species presence locations (parameter: species), +and a set of raster layers representing relevant environmental +variables (parameter: evp_maps). For all point locations, it +reads in the values of the environmental raster layers. The resulting +point layers(s) are combined in one layer and this is exported as a SWD +file that can be used as input for MaxEnd 3.4 or higher.

-The SWD file format is a simple CSV-like file format as described -in Elith et al. 2011. The first three fields provide the species name, -x-coordinate and y-coordinate, while subsequent fields contain the -values of the user-selected environmental parameters. Note that the -coordinates are not used by the MaxEnt program if all environmental -data is given in the SWD format file. - -

-The user can provide one or more species point files, which will be -combined in one SWD file. The map names of both the species point -layers and the environmental parameters can be replaced by alias names, -which will be used by MaxEnt. +The user can also provide a point layer with background points +(parameter: bgp). Alternatively, a user-defined number of +background points can be generated automatically, respecting the +computational region and MASK. In either case, for all point locations, +the function reads in the values of the environmental raster layers. +The resulting point layer is exported as a SWD file.

If alias names are used, a CSV file (alias_file) can be created with alias names in the first column and map names in the second column, -separated by comma, without header. This can be used as input in -r.maxent.lambdas (but note that this addon does not seem to work with -outputs from Maxent 3.4+). +separated by comma, without a header.

NOTES

-The SWD output files are simple comma delimited text -files (.csv) and can therefore be easily read in in for example R -and subsequently used in other models / functions. +The map names of both the species point layers and the environmental +parameters can be replaced by alias names, which will be used by +MaxEnt.

-Maxent expects the raster cells to be perfectly square. I.e., the n-s -and e-w resolution need to be the same. v.maxent.swd will fail if this -is not the case. If this happens, you should change the resolution. +The SWD file format is a simple comma-delimited text files. The first +three fields provide the species name, x-coordinate and y-coordinate, +while subsequent fields contain the values of the user-selected +environmental parameters. The files can be easily read in for example, +R and subsequently used in other models / functions.

-Following the grass gis convention, the resolution of an exported -raster is determined by the region settings. So make sure to set the -resolution of the region so that the n-s and e-w resolution match. To -accomplish this, you can use (replaced the *** for the desired -resolution): +Maxent expects the n-s and e-w resolution to be the same. Following the +grass gis convention, the resolution of an exported raster is +determined by the region settings. So make sure to set the resolution +of the region so that the n-s and e-w resolution match. To accomplish +this, you can use (replaced the *** for the desired resolution):

 g.region -a res=***
 

-Alternatively, you can set the -e flag. This will run g.region +Alternatively, you can set the -e flag. This will run g.region for you, adjusting the resolution so both the ns and ew resolutionn match the smallest of the two, using nearest neighbor resampling.

-This addon is a vector based alternative to r.out.maxent_swd. +This addon is a vector-based alternative to r.out.maxent_swd. It can be more efficient with sparse data points. The main difference is that with this addon you can have more than one sample point per -raster cell. points per raster cell. If this is not desired, use -r.out.maxent_swd. Another difference is the option to export the -predictor raster layers to a user-defined folder. This can be used in -Maxent, Maxnet addon for R or other software. +raster cell. But note that you can use the -t flag to thin the +point layer so that there is never more than 1 point per raster cell. +Another difference is the option to export the predictor raster layers +to a user-defined folder. This can be used in Maxent, Maxnet addon for +R or other software. + +

EXAMPLES

+ +The examples below use a dataset that you can download from +here. It includes vector point layer with +observation locations of the pale-throated sloth (Bradypus +tridactylus) from GBIF, a number of bioclim +raster layers from WorldClim, +the IUCN +RedList range map of the species, and a boundary layer of the South +American countries from NaturalEarth. + +

+The zip file contains a GRASS +location. Unzip it and put it in a GRASS GIS database. Next, open +GRASS GIS and go to the mapset southamerica. Download the zip +file, and unzip it in a GRASS GIS database. + +

+
+v.maxent.swd -t species=Bradypus_tridactylus \
+ evp_maps=bio02,bio03@southamerica,bio08,bio09,bio13,bio15,bio17 \
+ evp_cat=sa_eco_l2 alias_cat=landuse nbgp=10000 \
+ bgr_output=maxentinput/bgrd_swd.csv \
+ species_output=maxentinput/spec_swd.csv \
+ export_rasters=maxentinput/envlayers
+
+
+ +

+The output is a folder maxentinput with the SWD files +bgrd_swd.csv and spec_swd.csv and the accompanying proj files. The +latter provide information about the CRS, which might be useful if you +want to import the point layers in another software tools. In addition, +the example code creates the raster layers of the environmental layes +in ascii format in the folder envlayers. + +

+The created data layers can be used as input for Maxent. +Alternatively, you can use it as input for the r.maxent.train +addon, which provides a convenient wrapper for the Maxent +software.

SEE ALSO

REFERENCES

AUTHOR

Paulo van Breugel, paulo at ecodiv.earth + +

+HAS green academy University of Applied Sciences
+Innovative +Biomonitoring research group
+Climate-robust +Landscapes research group + diff --git a/src/vector/v.maxent.swd/v.maxent.swd.py b/src/vector/v.maxent.swd/v.maxent.swd.py index 5d7ecda1f9..5a6dd1c26b 100755 --- a/src/vector/v.maxent.swd/v.maxent.swd.py +++ b/src/vector/v.maxent.swd/v.maxent.swd.py @@ -6,7 +6,7 @@ # MODULE: v.maxent.swd # AUTHOR(S): Paulo van Breugel # PURPOSE: Produce a set of text file (SWD file) which can be used as -# input to MaxEnt 3.3.3. It may also provide the input data +# input to MaxEnt 3.4+. It may also provide the input data # presence and absence/background for other modeling tools # in e.g. R # @@ -36,12 +36,12 @@ # # REQUIREMENTS: # - -# %Module -# % description: Export raster values at given point locations as text file in SWD format for input in Maxent +# %module +# % description: Export raster values at given point locations as text file in SWD format for input in Maxent. In addition, the addon can export the environmental raster layers as ascii files. # % keyword: vector # % keyword: export # % keyword: Maxent -# %End +# %end # %option # % key: species @@ -106,7 +106,6 @@ # % type: string # % description: Number or percentage of background points # % key_desc: number -# % answer : 10000 # % required: no # % guisection: point data # %end @@ -121,6 +120,21 @@ # % guisection: point data # %end +# %flag +# % key: t +# % label: Thin species and background points +# % description: Select this flag if you want to limit the species and background points to maximum one point per raster cell. Note that this is already the case for the background points with the nbgp option. +# % guisection: output +# %end + +# %option G_OPT_F_OUTPUT +# % key: species_output +# % description: Species SWD file +# % required : no +# % multiple: no +# % guisection: output +# %end + # %option G_OPT_F_OUTPUT # % key: bgr_output # % description: Background SWD file @@ -145,14 +159,6 @@ # %requires: bgr_output,nbgp,bgp # %end -# %option G_OPT_F_OUTPUT -# % key: species_output -# % description: Species SWD file -# % required : no -# % multiple: no -# % guisection: output -# %end - # %rules # %collective: species_output,species # %end @@ -219,6 +225,7 @@ import atexit import sys import uuid +import pathlib import grass.script as gs @@ -253,6 +260,7 @@ def cleanup(): def CreateFileName(outputfile): + """Create temporary file name""" flname = outputfile k = 0 while os.path.isfile(flname): @@ -265,6 +273,15 @@ def CreateFileName(outputfile): return flname +def thin_points(layer, newname): + """ + Thin point layer, reducing to density to maximum one per raster layer + """ + tmprast = create_temporary_name("thin") + gs.run_command("v.to.rast", input=layer, type="point", output=tmprast, use="value") + gs.run_command("r.to.vect", input=tmprast, output=newname, type="point") + + def main(options, flags): """Check if X and Y resolution is equal""" regioninfo = gs.parse_command("g.region", flags="g") @@ -291,6 +308,7 @@ def main(options, flags): evp = evp.split(",") evpn = options["alias_names"] bgrout = options["bgr_output"] + bkgr_file_extension = pathlib.Path(bgrout).suffix if os.path.isfile(bgrout): bgrout2 = CreateFileName(bgrout) gs.message( @@ -363,15 +381,31 @@ def main(options, flags): exporturl = os.path.join( options["export_rasters"], f"{evpn[idx]}.{raster_extension}" ) - gs.run_command( - "r.out.gdal", - input=name, - output=exporturl, - format=raster_format, - nodata=int(options["nodata"]), - flags="c", - quiet=True, - ) + laytype = gs.raster_info(name)["datatype"] + if laytype == "CELL" and ( + int(options["nodata"]) < 0 or int(options["nodata"]) > 255 + ): + gs.run_command( + "r.out.gdal", + input=name, + output=exporturl, + format=raster_format, + nodata=int(options["nodata"]), + flags="c", + type="Int32", + quiet=True, + ) + else: + gs.run_command( + "r.out.gdal", + input=name, + output=exporturl, + format=raster_format, + nodata=options["nodata"], + flags="c", + quiet=True, + ) + if len(evpc) > 0: for idx, name in enumerate(evpc): exporturl = os.path.join( @@ -396,7 +430,10 @@ def main(options, flags): bgpname = create_temporary_name("bgp") gs.message(_("Creating SWD file with background points")) if bool(options["bgp"]): - gs.run_command("g.copy", vector=[options["bgp"], bgpname], quiet=True) + if bool(flags["t"]): + thin_points(options["bgp"], bgpname) + else: + gs.run_command("g.copy", vector=[options["bgp"], bgpname], quiet=True) else: gs.run_command( "r.random", @@ -453,7 +490,10 @@ def main(options, flags): file=bgrout, quiet=True, ) - prjout = bgrout.replace("csv", "prj") + if bkgr_file_extension == "": + prjout = f"{bgrout}.prj" + else: + prjout = bgrout.replace(bkgr_file_extension, "prj") proj_string = gs.read_command("g.proj", flags="fe").strip() with open(prjout, "w") as outfile: outfile.write(proj_string) @@ -480,13 +520,17 @@ def main(options, flags): ) ) specout = options["species_output"] + spec_file_extension = pathlib.Path(specout).suffix # Write for each species a temp swd file for i in range(len(specs)): # Upload environmental values for point locations to attribute table bgrtmp = os.path.join(bgrdir, "prespoints{}".format(i)) specname = create_temporary_name("sp") - gs.run_command("g.copy", vector=[specs[i], specname], quiet=True) + if bool(flags["t"]): + thin_points(specs[i], specname) + else: + gs.run_command("g.copy", vector=[specs[i], specname], quiet=True) for j in range(len(env_vars)): gs.run_command( "v.what.rast", @@ -544,7 +588,10 @@ def main(options, flags): for fname in filenames: with open(fname) as infile: outfile.write(infile.read().rstrip() + "\n") - prjout = specout.replace("csv", "prj") + if spec_file_extension == "": + prjout = f"{specout}.prj" + else: + prjout = specout.replace(spec_file_extension, "prj") proj_string = gs.read_command("g.proj", flags="fe").strip() with open(prjout, "w") as outfile: outfile.write(proj_string)