Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions vector/v.overlay/area_area.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static int cmp_int(const void *a, const void *b)
int area_area(struct Map_info *In, int *field, struct Map_info *Tmp,
struct Map_info *Out, struct field_info *Fi, dbDriver *driver,
int operator, int *ofield, ATTRIBUTES *attr, struct ilist *BList,
double snap)
double snap, double area_minsize)
{
int ret, input, line, nlines, area, nareas;
int in_centr, out_cat;
Expand Down Expand Up @@ -172,10 +172,19 @@ int area_area(struct Map_info *In, int *field, struct Map_info *Tmp,
Vect_remove_bridges(Tmp, NULL, NULL, NULL);
}

G_set_verbose(0);
Vect_build_partial(Tmp, GV_BUILD_NONE);
Vect_build_partial(Tmp, GV_BUILD_BASE);
G_set_verbose(verbose);
if (area_minsize > 0) {
Vect_build_partial(Tmp, GV_BUILD_CENTROIDS);
G_message(_("Removing areas smaller than %g sqm..."), area_minsize);
Vect_remove_small_areas(Tmp, area_minsize, NULL, NULL);
Vect_build_partial(Tmp, GV_BUILD_BASE);
}
else {
G_set_verbose(0);
Vect_build_partial(Tmp, GV_BUILD_NONE);
Vect_build_partial(Tmp, GV_BUILD_BASE);
G_set_verbose(verbose);
}

G_message(_("Merging lines..."));
Vect_merge_lines(Tmp, GV_BOUNDARY, NULL, NULL);

Expand Down
2 changes: 1 addition & 1 deletion vector/v.overlay/local.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ATTR *find_attr(ATTRIBUTES *attributes, int cat);
int area_area(struct Map_info *In, int *field, struct Map_info *Tmp,
struct Map_info *Out, struct field_info *Fi, dbDriver *driver,
int operator, int * ofield, ATTRIBUTES *attr, struct ilist *BList,
double snap_thresh);
double snap_thresh, double area_minsize);
int line_area(struct Map_info *In, int *field, struct Map_info *Tmp,
struct Map_info *Out, struct field_info *Fi, dbDriver *driver,
int operator, int * ofield, ATTRIBUTES *attr,
Expand Down
16 changes: 12 additions & 4 deletions vector/v.overlay/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* OGR support by Martin Landa <landa.martin gmail.com>
* Markus Metz
* PURPOSE:
* COPYRIGHT: (C) 2003-2016 by the GRASS Development Team
* COPYRIGHT: (C) 2003-2026 by the GRASS Development Team
*
* This program is free software under the GNU General
* Public License (>=v2). Read the file COPYING that
Expand All @@ -36,10 +36,10 @@ int main(int argc, char *argv[])
{
int i, j, input, line, nlines, operator;
int type[2], field[2], ofield[3];
double snap_thresh;
double snap_thresh, area_minsize;
struct GModule *module;
struct Option *in_opt[2], *out_opt, *type_opt[2], *field_opt[2],
*ofield_opt, *operator_opt, *snap_opt;
*ofield_opt, *operator_opt, *snap_opt, *minsize_opt;
struct Flag *table_flag;
struct Map_info In[2], Out, Tmp;
struct line_pnts *Points, *Points2;
Expand Down Expand Up @@ -142,6 +142,13 @@ int main(int argc, char *argv[])
snap_opt->type = TYPE_DOUBLE;
snap_opt->answer = "1e-8";

minsize_opt = G_define_option();
minsize_opt->key = "minsize";
minsize_opt->label = _("Minimum size of areas to keep in square meters");
minsize_opt->description = _("Enable with minsize > 0");
minsize_opt->type = TYPE_DOUBLE;
minsize_opt->answer = "0";

table_flag = G_define_standard_flag(G_FLG_V_TABLE);
table_flag->guisection = _("Attributes");

Expand Down Expand Up @@ -220,6 +227,7 @@ int main(int argc, char *argv[])
operator_opt->answer);

snap_thresh = atof(snap_opt->answer);
area_minsize = atof(minsize_opt->answer);

Points = Vect_new_line_struct();
Points2 = Vect_new_line_struct();
Expand Down Expand Up @@ -635,7 +643,7 @@ int main(int argc, char *argv[])
/* AREA x AREA */
if (type[0] == GV_AREA) {
area_area(In, field, &Tmp, &Out, Fi, driver, operator, ofield, attr,
BList, snap_thresh);
BList, snap_thresh, area_minsize);
}
else { /* LINE x AREA */
line_area(In, field, &Tmp, &Out, Fi, driver, operator, ofield, attr,
Expand Down
128 changes: 128 additions & 0 deletions vector/v.overlay/tests/test_v_overlay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import pytest
import grass.script as gs

# run in a GRASS session with nc_spm_full_v2beta1


class TestVOverlay:
"""Test v.overlay output against expected output"""

# create test data
@pytest.fixture(scope="class", autouse=True)
Comment on lines +10 to +11
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is autouse a pattern we want to use, or we prefer being explicit of the side effects?

def create_testdata(self):
# set up
gs.run_command(
"v.extract",
input="boundary_county",
output="boundary_county_extract1",
where="NAME in ('CURRITUCK')",
)
gs.run_command(
"v.extract",
input="boundary_county",
output="boundary_county_extract2",
where="NAME in ('CAMDEN')",
)
# modify extract 1
gs.run_command(
"v.buffer",
input="boundary_county_extract1",
output="boundary_county_extract1_buffer_out",
type="area",
distance=2,
)
gs.run_command(
"v.buffer",
input="boundary_county_extract1_buffer_out",
output="boundary_county_extract1_buffer_in",
type="area",
distance=-2,
)

# run the tests
yield

# clean up test data regardless of test success/failure
gs.run_command(
"g.remove", type="vector", flags="f", pattern="boundary_county_extract*"
)
Comment on lines +7 to +48
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the grass temporary session/project created?

There is no default data available by default (that could be affected destructively by a low-quality test), unlike gunittest, that these tests already assume a project with certain maps are available and loaded (which end up being integration tests because of that).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation! Indeed I assumed that a default GRASS session with the NC data is already active. So how can I make use of the NC data in a pytest test for a simple fast test?

Alternatively, I would extract the test data from the NC data and add them to the GRASS source code, but that seems wrong.


def test_voverlay_nocleaning(self):
"""
Overlay two input vectors and compare the output to the expected output.
The output will have many very small areas.
This test would fail in GRASS84 with a too small number of centroids.
Since GRASS84, calculation for centroids has been improved for
edge cases like very small areas."""
gs.run_command(
"v.overlay",
ainput="boundary_county_extract1_buffer_in",
atype="area",
binput="boundary_county_extract2",
btype="area",
output="boundary_county_extract_overlay_nocleaning",
operator="or",
snap=-1,
quiet=True,
)

reference = {
"nodes": 1050,
"primitives": 1802,
"points": 0,
"lines": 0,
"boundaries": 1572,
"centroids": 230,
"areas": 529,
"islands": 7,
}

observed = gs.vector_info_topo(
map="boundary_county_extract_overlay_nocleaning",
)

# compare results
for key in list(reference):
# all integers, simple comparison is ok
assert reference[key] == observed[key], (
f"Difference in {key}, expected {reference[key]}, got {observed[key]}"
)

def test_voverlay_withcleaning(self):
"""Overlay two input vectors and compare the output to the expected output.
The output will have many very small areas.
This is a test for the cleaning options snap and minsize"""
gs.run_command(
"v.overlay",
ainput="boundary_county_extract1_buffer_in",
atype="area",
binput="boundary_county_extract2",
btype="area",
output="boundary_county_extract_overlay_withcleaning",
operator="or",
snap=1.0e-6,
minsize=0.00001,
quiet=True,
)

reference = {
"nodes": 1232,
"primitives": 2164,
"points": 0,
"lines": 0,
"boundaries": 1847,
"centroids": 317,
"areas": 622,
"islands": 7,
}

observed = gs.vector_info_topo(
map="boundary_county_extract_overlay_withcleaning",
)

# compare results
for key in list(reference):
# all integers, simple comparison is ok
assert reference[key] == observed[key], (
f"Difference in {key}, expected {reference[key]}, got {observed[key]}"
)
10 changes: 9 additions & 1 deletion vector/v.overlay/v.overlay.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,16 @@ <h2>NOTES</h2>
the output layer with the third number.

<p>
If <b>atype</b>=auto is given than <em>v.overlay</em> determines
If <b>atype</b>=auto is given then <em>v.overlay</em> determines the
feature type for <b>ainput</b> from the first found feature.
<p>
When overlaying two vectors with areas, very small areas can occur in the
output. This can happen when e.g. one vector is a slightly modified
version of the other vector (buffered or simplified). These very small
areas can be removed by setting <b>minsize</b> to some value larger 0.
The value is interpreted as square meters. In order to remove only noise
from slightly mismatching boundaries, the value of <b>minsize</b> should be
small, e.g. in the range 0.0001 to 1.

<!-- This is outdated
<p><div class="code"><pre>
Expand Down
12 changes: 10 additions & 2 deletions vector/v.overlay/v.overlay.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,16 @@ If the *third* number of the **olayer** option is greater than 0, then
the categories of **binput** in layer *blayer* are transferred to the
output layer with the third number.

If **atype**=auto is given than *v.overlay* determines feature type for
**ainput** from the first found feature.
If **atype**=auto is given then *v.overlay* determines the feature type
for **ainput** from the first found feature.

When overlaying two vectors with areas, very small areas can occur in the
output. This can happen when e.g. one vector is a slightly modified
version of the other vector (buffered or simplified). These very small
areas can be removed by setting **minsize** to some value larger 0.
The value is interpreted as square meters. In order to remove only noise
from slightly mismatching boundaries, the value of **minsize** should be
small, e.g. in the range 0.0001 to 1.

## EXAMPLES

Expand Down
Loading