Skip to content

Commit

Permalink
new addon v.what.rast.label
Browse files Browse the repository at this point in the history
Add-on to uploads raster values and labels at positions of vector points to the vector attribute tab
  • Loading branch information
ecodiv committed Jan 8, 2022
1 parent b7a36eb commit 2886a5d
Show file tree
Hide file tree
Showing 3 changed files with 285 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/vector/v.what.rast.label/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
MODULE_TOPDIR = ../..

PGM = v.what.rast.label

include $(MODULE_TOPDIR)/include/Make/Script.make

default: script
37 changes: 37 additions & 0 deletions src/vector/v.what.rast.label/v.what.rast.label.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<h2>DESCRIPTION</h2>

<em>v.what.rast.label</em> retrieves raster values and labels from one
or more given raster maps for each point stored in the input vector map.
The name of the columns with the raster values and labels are
respectively<i>ID_name-of-raster-map</i> and <i>name-of-raster-map</i>.

<p>Optionally, the user can define raster layers without labels. In that case
only raster values are uploaded to a column with the name
<i>name-of-raster-map</i>.

<p>Except the columns with raster values, the attribute table of the
output vector point map can optionally include columns with coordinates.

<p>The user can opt to include the attribute columns of the input vector layer
in the output. In that case, the columns with the raster values and labels will
appear after the columns from the input vector layer.

<h2>NOTES</h2>

Points and centroid with shared category number cannot be processed. To solved
this, unique categories may be added with v.category in a separate layer. See
<i>v.what.rast</i> for details.

<p>If you only want to uploads raster values at positions of vector points to the
attribute table of that vector layer, use <i>v.what.rast</i> instead.

<h2>SEE ALSO</h2>

<em>
<a href="v.what.rast">v.what.rast</a>,
<a href="https://grass.osgeo.org/grass78/manuals/addons/v.what.rast.multi.html">
r.what.rast.multi</a>
</em>

<h2>AUTHORS</h2>
<em>Paulo van Breugel</em>
241 changes: 241 additions & 0 deletions src/vector/v.what.rast.label/v.what.rast.label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
#!/usr/bin/env python

##############################################################################
#
# MODULE: v.what.rast.label
# AUTHOR(S): Paulo van Breugel <paulo at ecodiv dot earth>
# PURPOSE: Upload raster values and labels at positions of vector points
# to the vector attribute table.
#
# COPYRIGHT: (C) 2016-2022 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
# for details.
##############################################################################

#%module
#% description: Uploads raster values and labels to vector point layer
#% keyword: vector
#% keyword: sampling
#% keyword: raster
#% keyword: position
#% keyword: querying
#% keyword: attribute table
#% keyword: surface information
#%end

#%option G_OPT_V_MAP
#% key: vector
#% description: Name vector points map for which to add raster values & labels
#% guisection: Input
#% required: yes
#%end

#%option G_OPT_R_INPUTS
#% key: raster
#% description: Name of raster map(s) with labels to be queried
#% guisection: Input
#% required: yes
#% multiple: yes
#%end

#%option G_OPT_R_INPUTS
#% key: raster2
#% description: Name of raster map(s) without labels to be queried
#% guisection: Input
#% required: no
#% multiple: yes
#%end

#%option G_OPT_V_OUTPUT
#% description: Name of output point layer
#% key_desc: name
#% guisection: Input
#% required: yes
#%end

#%flag
#% key: o
#% description: Include columns of input vector map
#%end

#%flag
#% key: c
#% description: Include point coordinates
#%end
# import libraries
import os
import sys
import uuid
import atexit
from subprocess import PIPE
import textwrap
import grass.script as gs
from grass.pygrass.modules import Module

# create set to store names of temporary maps to be deleted upon exit
clean_layers = []


def create_unique_name(name):
"""Generate a tmp name which contains prefix
Store the name in the global list.
"""
return name + str(uuid.uuid4().hex)


def tmpname(prefix):
"""Generate a tmp name which contains prefix
Store the name in the global list.
"""
tmpf = create_unique_name(prefix)
clean_layers.append(tmpf)
return tmpf


def cleanup():
"""Remove temporary maps specified in the global list"""
list_temporary_layers = list(reversed(clean_layers))
for temporary_layer in list_temporary_layers:
ffile = gs.find_file(
name=temporary_layer, element="vector", mapset=gs.gisenv()["MAPSET"]
)
if ffile["file"]:
gs.run_command(
"g.remove", flags="f", type="vector", name=temporary_layer, quiet=True
)


def main(options, flags):

# Variables
raster_cat = options["raster"]
raster_cat = raster_cat.split(",")
raster_cat_names = [z.split("@")[0] for z in raster_cat]
raster_cat_names = [x.lower() for x in raster_cat_names]
raster_cont = options["raster2"]
raster_cont = raster_cont.split(",")
raster_cont_names = [z.split("@")[0] for z in raster_cont]
raster_cont_names = [x.lower() for x in raster_cont_names]
input_vector = options["vector"]
output_vector = options["output"]

if flags["o"]:
tmp_layer = tmpname("v_what_rastlabel")
Module("g.copy", vector=[input_vector, output_vector], quiet=True)
else:
tmp_layer = output_vector
# Create vector with column names
base_column_names = ["x double precision, y double precision, label integer"]
for i, raster_name in enumerate(raster_cat):
data_types = gs.parse_command("r.info", flags="g", map=raster_name, quiet=True)[
"datatype"
]
if data_types == "CELL":
base_column_names.append(
"{0}_ID integer, {0} varchar(255)".format(raster_cat_names[i])
)
else:
base_column_names.append(
"ID_{0} double precision, {0} varchar(255)".format(raster_cat_names[i])
)
column_names = ",".join(base_column_names)

# Get raster points of raster layers with labels
# Export point map to text file first and use that as input in r.what
point_to_ascii = Module(
"v.out.ascii",
input=input_vector,
format="point",
separator="space",
precision=12,
stdout_=PIPE,
).outputs.stdout
raster_cats = Module(
"r.what", flags="f", map=raster_cat, stdin_=point_to_ascii, stdout_=PIPE
).outputs.stdout
ascii_to_point = raster_cats.replace("|*|", "||")
Module(
"v.in.ascii",
input="-",
stdin_=ascii_to_point,
output=tmp_layer,
columns=column_names,
separator="pipe",
format="point",
x=1,
y=2,
quiet=True,
)

# In- or exclude coordinates
if not flags["c"]:
Module("v.db.dropcolumn", map=tmp_layer, columns=["x", "y"])
# Get raster points of raster layers without labels (optional)
if options["raster2"]:
for j, raster_cont_values in enumerate(raster_cont):
Module(
"v.what.rast",
map=tmp_layer,
raster=raster_cont_values,
column=raster_cont_names[j],
quiet=True,
)
# Join table of original layer (and drop label columns)

if flags["o"]:

cols = Module("db.columns", table=tmp_layer, stdout_=PIPE).outputs.stdout.split(
"\n"
)
cols.pop()
del cols[:1]

sqlstat = "CREATE INDEX {0}_label ON {0} (label);".format(tmp_layer)
Module("db.execute", sql=sqlstat)
Module(
"v.db.join",
map=output_vector,
column="cat",
other_table=tmp_layer,
other_column="label",
subset_columns=cols,
)
# Remove label column
Module("v.db.dropcolumn", map=output_vector, columns=["label"])

# Write metadata
list_with_options = dict((k, v) for k, v in options.items() if v)
list_with_flags = dict((k, v) for k, v in flags.items() if v)
history_options = " ".join(
"{!s}={!r}".format(k, v) for (k, v) in list_with_options.items()
)
history_flags = " ".join(
"{!s}={!r}".format(k, v) for (k, v) in list_with_flags.items()
)
history_command = "r.what.rastlabel \n{0} \n{1}".format(
history_flags, history_options
)

history_command = textwrap.fill(
history_command,
width=40,
break_long_words=False,
break_on_hyphens=True,
subsequent_indent=" " * 13,
)

Module(
"v.support",
map=output_vector,
comment="created with v.what.rast.label",
cmdhist=history_command,
flags=["r", "h"],
quiet=True,
)


if __name__ == "__main__":
atexit.register(cleanup)
sys.exit(main(*gs.parser()))

0 comments on commit 2886a5d

Please sign in to comment.