Skip to content

Commit

Permalink
r.recode.attr: added option to set delimiter (#1250)
Browse files Browse the repository at this point in the history
* r.recode.attr: added option to set delimiter

- Option added to set delimiter.
- Added example to the help page.
- Removed dead link to non-existing blog post.

* add grass gis development team to copyright line
  • Loading branch information
ecodiv authored Nov 26, 2024
1 parent 8827e50 commit 0429182
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 59 deletions.
130 changes: 97 additions & 33 deletions src/raster/r.recode.attr/r.recode.attr.html
Original file line number Diff line number Diff line change
@@ -1,48 +1,112 @@

<h2>DESCRIPTION</h2>

The <em>r.reclass.attr</em> plugin let you reclass/recode a raster
layer based on values in a csv table. The csv file should include at
least two columns. The first column should correspond to (part of)
the raster values. The other columns should hold the
reclassification values.

<p>For each column in the csv (comma separated text) file (except
the first one) a new map will be created, replacing the raster
values corresponding to the first column with those in the second
(3rd, 4th, etc) column.

<p>The user can define the names of the output map(s). If only one
output name is provided and the rules file contains more than two
columns, the function will create output names by appending for each
recode layer the corresponding column name to the output name
provided by the user.

<h2>NOTES</h2>
The script uses r.recode to allow the user to reclass integer values
to floating point values. However, like <a href="https://grass.osgeo.org/grass-stable/manuals/r.reclass.html">r.reclass</a>
you can only convert number one to one (and not like <a href="https://grass.osgeo.org/grass-stable/manuals/r.recode.html">r.recode</a>
ranges of values to one value or a new range of values)

<h2>TODO</h2>
<ul>
<li>Use r.reclass instead of r.recode when possible (if input
raster is integer and all reclass values are integers)</li>
<li>Allow text files with different types of separators (now only
comma separated files)</li>
</ul>
The <em>r.recode.attr</em> plugin let you reclass/recode a raster layer
based on values specified in a csv table.The module requires the first
row of the CSV file to contain column headers. The table must include
at least two columns: The first column corresponds to the raster values
(or a subset of them). The remaining columns contain the
reclassification values, which can be either integers or floating-point
numbers.

<p>
For each column in the csv file (except the first one) new raster map
will be created, replacing the raster values corresponding to the first
column with those in the second (3rd, 4th, etc) column.

<p>
Users can define custom names for the output map(s). If only one output
name is provided and the CSV file contains more than two columns, the
module will automatically generate output names by appending the column
names to the provided base name.

<h2>EXAMPLES</h2>

<p>See <a href="https://pvanb.wordpress.com/2014/12/13/recode-your-raster-file-in-grass-gis-using-a-csv-file/">this blog post</a> for an example.
The example uses the basic North Caroline dataset. You can download it
from (<a href="https://grass.osgeo.org/download/data/">here</a>).
Alternatively, you can install in directly from within GRASS using the
"Download sample project" option in the Data panel.

Inspect the categories of the <i>landuse</i> raster layer.

<p>
<div class="code">
<pre>
r.category map=landuse@PERMANENT
</pre>
</div>

<p>
Based on the categories of the <i>landuse</i> layer, create a CSV file
<i>reclass.csv</i>. This table assigns a friction value and a
suitability value to each attribute.

<p>
<div class="code">
<pre>
cat &lt;&lt;EOL &gt; reclass.csv
rasterID,friction,suitability
1,0.9,0
2,0.7,0.2
3,0.6,0.4
4,0.2,0.5
5,0.1,0.9
6,1,0
7,0.8,0
EOL
</pre>
</div>

<p>
Use the <em>r.recode.attr</em> addon to generate two new raster layers,
one for friction and another for suitability. Specify a base name for
the output maps. Ensure that the <b>separator</b> matches the delimiter
used in your CSV file.

<p>
<div class="code">
<pre>
r.recode.attr input=landuse output=map rules=reclass.csv separator=comma
</pre>
</div>

<p>
Note that the names of the two maps are constructed based on the
provided base name 'map' + name of the name of the column. Create for
both layers created above a color table.

<p>
<div class="code">
<pre>
r.colors map=map_friction color=oranges
r.colors map=map_suitability color=greens
</pre>
</div>

<p>
The original land use map and the derived friction and suitability maps are shown in the figure below.

<p>
<div style="text-align: center; margin: 10px;">
<a href="r_recode_attr_01.png">
<img src="r_recode_attr_01.png" width="600" height="269" border="0"
alt="Example output maps of r.recode.attr"></a><br>
<i>Figure 1: The A) friction and B) suitability maps,
based on scores assigned to each land use category
of the landuse map.
</i>
</div>


<h2>SEE ALSO</h2>

<em>
<a href="https://grass.osgeo.org/grass-stable/manuals/r.reclass.html">r.reclass</a>,
<a href="https://grass.osgeo.org/grass-stable/manuals/r.recode.html">r.recode</a>
</em>

<h2>AUTHOR</h2>

Paulo van Breugel, paulo at ecodiv.org
Paulo van Breugel, <a href="https://ecodiv.earth">https://ecodiv.earth</a>, HAS green academy University of Applied Sciences, <a href="https://www.has.nl/en/research/professorships/innovative-bio-monitoring-professorship/">Innovative
Biomonitoring research group</a>, <a href="https://www.has.nl/en/research/professorships/climate-robust-landscapes-professorship/">Climate-robust
Landscapes research group</a>

67 changes: 41 additions & 26 deletions src/raster/r.recode.attr/r.recode.attr.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env python

#!/usr/bin/env python3

########################################################################
#
Expand All @@ -8,9 +7,8 @@
# PURPOSE: Recode raster to one or more new layers using an
# attribute table (csv file) as input
#
# COPYRIGHT: (C) 2015 Paulo van Breugel
# https://ecodiv.earth
# https://pvanb.wordpress.com/
# COPYRIGHT: (C) 2015-2024 by Paulo van Breugel
# and the GRASS Development Team
#
# This program is free software under the GNU General Public
# License (>=v2). Read the file COPYING that comes with GRASS
Expand All @@ -19,7 +17,7 @@
########################################################################
#
# %Module
# % description: Recode raster using attribute table (csv file) as input.
# % description: Recode raster based on the values in one or more columns in a csv file.
# % keyword: raster
# % keyword: recode
# %End
Expand Down Expand Up @@ -50,6 +48,9 @@
# % required: yes
# %end

# %option G_OPT_F_SEP
# %end

# %flag:
# % key: a
# % description: Align the current region to the input raster map
Expand All @@ -60,7 +61,7 @@
import sys
import numpy as np
import tempfile
import grass.script as grass
import grass.script as gs

# for Python 3 compatibility
try:
Expand All @@ -69,25 +70,44 @@
xrange = range


# main function
def main():

# check if GISBASE is set
if "GISBASE" not in os.environ:
# return an error advice
grass.fatal("You must be in GRASS GIS to run this program")
def get_delimiter(separator_option):
"""
Function to replace the description of the delimiter with the actual character
"""
separator_mapping = {
"comma": ",",
"pipe": "|",
"space": " ",
"tab": "\t",
"newline": "\n",
}
if separator_option in list(separator_mapping.keys()):
return separator_mapping.get(separator_option, None)
else:
return separator_option


def main(options, flags):
"""
Recodes a raster layer based on the values in one or more columns
in a supplied csv file.
"""

# input raster map and parameters
inputmap = options["input"]
outBase = options["output"]
rules = options["rules"]
separator = get_delimiter(options["separator"])
outNames = outBase.split(",")
lengthNames = len(outNames)
flag_a = flags["a"]

# Get attribute data
myData = np.genfromtxt(rules, delimiter=",", skip_header=1)
nmsData = np.genfromtxt(rules, delimiter=",", names=True)
try:
myData = np.genfromtxt(rules, delimiter=separator, skip_header=1)
except Exception as e:
gs.fatal(_("Error loading data with delimiter '{}': {}".format(separator, e)))
nmsData = np.genfromtxt(rules, delimiter=separator, names=True)
dimData = myData.shape
nmsData = nmsData.dtype.names

Expand All @@ -105,24 +125,19 @@ def main():
else:
nmOutput = outNames[0] + "_" + nmsData[y]

cf = grass.find_file(
name=nmOutput, element="cell", mapset=grass.gisenv()["MAPSET"]
)
cf = gs.find_file(name=nmOutput, element="cell", mapset=gs.gisenv()["MAPSET"])
if cf["fullname"] != "":
grass.fatal("The layer " + nmOutput + " already exist in this mapset")
gs.fatal("The layer " + nmOutput + " already exist in this mapset")

if flag_a:
grass.run_command(
gs.run_command(
"r.recode", input=inputmap, output=nmOutput, rules=tmpname, flags="a"
)
else:
grass.run_command(
"r.recode", input=inputmap, output=nmOutput, rules=tmpname
)
gs.run_command("r.recode", input=inputmap, output=nmOutput, rules=tmpname)
os.close(fd1)
os.remove(tmpname)


if __name__ == "__main__":
options, flags = grass.parser()
sys.exit(main())
sys.exit(main(*gs.parser()))
Binary file added src/raster/r.recode.attr/r_recode_attr_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 0429182

Please sign in to comment.