Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v.rast.move: Move points by values from raster map #877

Merged
merged 10 commits into from
Jul 19, 2023
7 changes: 7 additions & 0 deletions src/vector/v.rast.move/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
MODULE_TOPDIR = ../..

PGM = v.rast.move

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

default: script
112 changes: 112 additions & 0 deletions src/vector/v.rast.move/examples.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Graphics for Description of r.series\n",
"\n",
"Requires _pngquant_, _optipng_ and ImageMagic _mogrify_."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"from IPython.display import Image\n",
"\n",
"import grass.script as gs\n",
"import grass.jupyter as gj"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"gj.init(\"~/grassdata/nc_basic_spm_grass7/user1\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!g.region vector=roadsmajor res=100\n",
"!r.mapcalc expression=\"a = 1000 * sin(row())\"\n",
"!r.mapcalc expression=\"b = 0\"\n",
"!v.rast.move input=roadsmajor output=roads_moved x_raster=a y_raster=b"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!g.region vector=roadsmajor,roads_moved\n",
"!r.colors map=a color=difference --q\n",
"!v.patch input=roadsmajor,roads_moved output=merged --q\n",
"!v.buffer input=merged output=buffer distance=1000 --q"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plot = gj.Map(use_region=True, width=700)\n",
"plot.d_background(color=\"white\")\n",
"plot.d_rast(map=\"a\")\n",
"plot.d_vect(map=\"buffer\", color=\"white\", fill_color=\"white\", flags=\"s\")\n",
"plot.d_vect(map=\"roadsmajor\", color=\"blue\", width=5, legend_label=\"Original\")\n",
"plot.d_vect(map=\"roads_moved\", color=\"red\", width=5, legend_label=\"Shifted\")\n",
"plot.d_legend_vect(flags=\"b\", at=(60,10))\n",
"plot.d_legend(raster=\"a\", at=\"8,35,85,90\", flags=\"b\", title=\"X shift\")\n",
"plot.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"filename = \"v_rast_move.png\"\n",
"plot.save(filename)\n",
"!mogrify -trim {filename}\n",
"!pngquant --ext \".png\" -f {filename}\n",
"!optipng -o7 {filename}\n",
"Image(filename)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
88 changes: 88 additions & 0 deletions src/vector/v.rast.move/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Setup dataset for v.rast.move test"""

from types import SimpleNamespace

import pytest

import grass.script as gs

LINES = """\
VERTI:
L 9 1
0.0984456 0.61658031
0.15025907 0.68264249
0.2642487 0.76943005
0.39119171 0.79792746
0.52202073 0.80181347
0.62953368 0.78367876
0.68134715 0.73056995
0.71243523 0.64637306
0.73445596 0.54663212
1 1
L 8 1
0.09455959 0.40544041
0.31217617 0.40673575
0.61917098 0.40932642
0.75518135 0.41580311
0.86658031 0.41580311
0.86528497 0.35621762
0.87305699 0.20595855
0.87823834 0.08290155
1 2
"""

MIX = """\
VERTI:
L 9 1
0.0984456 0.61658031
0.15025907 0.68264249
0.2642487 0.76943005
0.39119171 0.79792746
0.52202073 0.80181347
0.62953368 0.78367876
0.68134715 0.73056995
0.71243523 0.64637306
0.73445596 0.54663212
1 1
P 1 1
0.09455000 0.40540000
1 3
"""


@pytest.fixture(scope="module")
def line_dataset(tmp_path_factory):
"""Create a session and fill mapset with data"""
tmp_path = tmp_path_factory.mktemp("line_dataset")
location = "test"
lines_name = "lines"
mix_name = "mix"
gs.core._create_location_xy(tmp_path, location) # pylint: disable=protected-access
with gs.setup.init(tmp_path / location):
gs.write_command(
"v.in.ascii", input="-", output=lines_name, stdin=LINES, format="standard"
)
gs.run_command("g.region", vector=lines_name, grow=0.1, res=0.01, flags="a")

x_name = "x"
y_name = "y"
x_value = 2.0
y_value = 5.3
nulls_name = "null_only"
gs.mapcalc(f"{x_name} = {x_value}")
gs.mapcalc(f"{y_name} = {y_value}")
gs.mapcalc(f"{nulls_name} = 0.0 + null()")

gs.write_command(
"v.in.ascii", input="-", output=mix_name, stdin=MIX, format="standard"
)

yield SimpleNamespace(
name=lines_name,
x=x_name,
y=y_name,
x_value=x_value,
y_value=y_value,
nulls=nulls_name,
mix_name=mix_name,
)
88 changes: 88 additions & 0 deletions src/vector/v.rast.move/tests/test_v_rast_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Test v.rast.move"""

import pytest

import grass.script as gs


def test_displacement_result(line_dataset):
"""Check geometry of the result"""
result = "result"
gs.run_command(
"v.rast.move",
input=line_dataset.name,
x_raster=line_dataset.x,
y_raster=line_dataset.y,
output=result,
)
old_metadata = gs.vector_info(line_dataset.name)
metadata = gs.vector_info(result)

for item in ["lines", "nodes"]:
assert old_metadata[item] == metadata[item]
assert metadata["north"] < line_dataset.y_value + 1
assert metadata["south"] > line_dataset.y_value
assert metadata["west"] > line_dataset.x_value
assert metadata["east"] < line_dataset.x_value + 1


@pytest.mark.parametrize("nulls", ["zeros", "warning", "error"])
def test_null_options_accepted(line_dataset, nulls):
"""Check that all values for the nulls option are accepted"""
result = f"result_nulls_{nulls}"
gs.run_command(
"v.rast.move",
input=line_dataset.name,
x_raster=line_dataset.x,
y_raster=line_dataset.y,
output=result,
nulls=nulls,
)


def test_nulls_as_zeros(line_dataset):
"""Check that transforming nulls to zeros works"""
result = "result_zeros"
gs.run_command(
"v.rast.move",
input=line_dataset.name,
x_raster=line_dataset.nulls,
y_raster=line_dataset.nulls,
output=result,
nulls="zeros",
)
old_metadata = gs.vector_info(line_dataset.name)
metadata = gs.vector_info(result)

for item in ["lines", "nodes", "north", "south", "east", "west"]:
assert old_metadata[item] == metadata[item], item


def test_nulls_fail(line_dataset):
"""Check that an error is generated for nulls"""
result = "result_fails"
with pytest.raises(gs.CalledModuleError):
gs.run_command(
"v.rast.move",
input=line_dataset.name,
x_raster=line_dataset.nulls,
y_raster=line_dataset.nulls,
output=result,
nulls="error",
)


def test_non_lines_ignored(line_dataset):
"""Check geometry of the result"""
result = "result_mix"
gs.run_command(
"v.rast.move",
input=line_dataset.mix_name,
x_raster=line_dataset.x,
y_raster=line_dataset.y,
output=result,
)
metadata = gs.vector_info(result)
assert metadata["lines"] == 1
assert metadata["points"] == 0
assert metadata["nodes"] == 2
93 changes: 93 additions & 0 deletions src/vector/v.rast.move/v.rast.move.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<h2>DESCRIPTION</h2>

<em>v.rast.move</em> takes values from raster maps and adds them to X and Y
coordinates of features in a vector map vertex by vertex. Works on lines
only, other features are ignored and not included in the result.

Null values in rasters are turned into zeros by default and a warning is
generated. This behavior can be modified by the <b>nulls</b> option to
either silence the warning with explicit <code>nulls="zeros"</code>
or the warning can be turned into an error with <code>nulls="error"</code>.

The rasters are loaded based on the computational region, so the most
advantageous use of resources is to set the computational region to
match the vector. To avoid issues with vector coordinates at the border
of the computational region, it is best to also grow the region one cell
on each side. Vector features outside of the computational region always
result in an error being reported (regardless of the <b>nulls</b> option),
but the rasters can have any extent as along as the computational region
is set to match the vector.

<h2>NOTES</h2>

Unlike <em>v.perturb</em> which moves points randomly, <em>v.rast.move</em>
works on vertices of lines and uses same value for all vertices at a given
cell. Unlike v.transform used with raster values in attribute columns,
<em>v.rast.move</em> operates on individual vertices in the line, not on the
whole line (attributes are associated with features, not their vertices).

<h2>EXAMPLES</h2>

<h3>Shift in X direction</h3>

<!-- The original code is in the notebook. -->

This example uses the North Carolina sample dataset.

Set the computational region to match the vector map and use
100-meter resolution.

<div class="code"><pre>
g.region vector=roadsmajor res=100
</pre></div>

Generate rasters for a shift in X direction (one raster is a wave, the other is zero):

<div class="code"><pre>
g.region vector=roadsmajor res=100
r.mapcalc expression="a = 1000 * sin(row())"
r.mapcalc expression="b = 0"
</pre></div>

Use the rasters to move the vector:

<div class="code"><pre>
v.rast.move input=roadsmajor output=roads_moved x_raster=a y_raster=b
</pre></div>

<div align="center" style="margin: 10px">
<a href="v_rast_move.png">
<img src="v_rast_move.png" width="600" height="600" alt="Two roads networks" border="0">
</a><br>
<i>
Figure: Original (blue) and shifted (red) road network and the X shift
values in diverging blue-white-red colors (red shift right, blue shift
left, white no shift)
</i>
</div>

<h2>SEE ALSO</h2>

<ul>
<li>
<em><a href="v.transform.html">v.transform</a></em>
for changing coordinates for the whole vector map or
feature by feature based on the attributes,
</li>
<li>
<em><a href="v.perturb.html">v.perturb</a></em>
for randomly changing point positions by small amounts,
</li>
<li>
<em><a href="r.mapcalc.html">r.mapcalc</a></em>
for generating or adjusting the raster maps,
</li>
<li>
<em><a href="g.region.html">g.region</a></em>
to set the computational region before the computation.
</li>
</ul>

<h2>AUTHOR</h2>

Vaclav Petras, <a href="http://geospatial.ncsu.edu/">NCSU Center for Geospatial Analytics, GeoForAll Lab</a>
Loading
Loading