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
12 changes: 12 additions & 0 deletions vector/v.to.rast/dense_line.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ void plot_line_dense(double east1, double north1, double east2, double north2)
}
}

/* dense point plotting, alternative to G_plot_point()
* east, north are map coordinates.
* Needed because dense mode never calls G_setup_plot(), so the move and cont
* routines G_plot_point() uses are not set. The coordinate is truncated to the
* cell that contains it, matching what dense_line() does with its endpoints:
* setup_plot() frames the region by cell edges, unlike G_setup_plot(), which
* frames it by cell centres and therefore rounds. */
void plot_point_dense(double east, double north)
{
st->dot((int)X(G_adjust_easting(east, &st->window)), (int)Y(north));
}

/* dense line plotting, alternative to G_bresenham_line()
* x1, y1, x2, y2 are col, row numbers */
void dense_line(double x1, double y1, double x2, double y2,
Expand Down
14 changes: 9 additions & 5 deletions vector/v.to.rast/do_lines.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

/* function prototypes */
static int plot_line(double *, double *, int, int, int);
static int plot_points(double *, double *, int);
static int plot_points(double *, double *, int, int);
static double v2angle(double[2], double[2], double, double);
static double deg_angle(double, double, double, double);

Expand Down Expand Up @@ -131,7 +131,7 @@ int do_lines(struct Map_info *Map, struct line_pnts *Points,
count++;
}
else if (type & GV_POINTS) {
plot_points(Points->x, Points->y, Points->n_points);
plot_points(Points->x, Points->y, Points->n_points, dense);
count++;
}
}
Expand Down Expand Up @@ -199,11 +199,15 @@ static double deg_angle(double x0, double y0, double x1, double y1)
return (v_ang * 360.0 / M_2PI);
}

static int plot_points(double *x, double *y, int n)
static int plot_points(double *x, double *y, int n, int dense)
{
/* only plot the first point */
if (n > 0)
G_plot_point(*x, *y);
if (n > 0) {
if (dense)
plot_point_dense(*x, *y);
else
G_plot_point(*x, *y);
}

return 0;
}
1 change: 1 addition & 0 deletions vector/v.to.rast/local.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ int do_lines(struct Map_info *, struct line_pnts *, dbCatValArray *, int, int,
struct cat_list *, int, double, int, int, int *, int);

void plot_line_dense(double, double, double, double);
void plot_point_dense(double, double);
void setup_plot(double, double, double, double, int (*dot)(int, int));

/* raster.c */
Expand Down
33 changes: 33 additions & 0 deletions vector/v.to.rast/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import os

import pytest

import grass.script as gs
from grass.tools import Tools


@pytest.fixture
def xy_points_session(tmp_path):
"""Active session in an XY project holding a small point map"""
project = tmp_path / "xy_test"
gs.create_project(project)
with (
gs.setup.init(project, env=os.environ.copy()) as session,
Tools(session=session) as tools,
):
points = tmp_path / "points.txt"
# Deliberately off the cell boundaries: with a 1 unit resolution
# starting at 0, a whole-numbered coordinate falls on a cell edge,
# where rounding to the nearest cell and truncating to the containing
# cell agree. These do not, so they detect a half cell shift.
points.write_text("10.6|10.7|1\n20.2|20.3|2\n30.9|15.1|3\n")
tools.g_region(s=0, n=50, w=0, e=50, res=1)
tools.v_in_ascii(
input=str(points),
output="points",
separator="pipe",
x=1,
y=2,
cat=3,
)
yield session
31 changes: 31 additions & 0 deletions vector/v.to.rast/tests/v_to_rast_dense_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Tests for rasterizing points with the dense flag

Dense mode configures its own plotting routines and never calls G_setup_plot(),
but points were always plotted with G_plot_point(), which uses the move and cont
routines that only G_setup_plot() sets. Rasterizing a point map with -d
therefore called a null function pointer and the module crashed
(https://github.com/OSGeo/grass/issues/3105).
"""

from grass.tools import Tools


def test_dense_points_match_plain_points(xy_points_session):
"""Points rasterize into the same cells with and without the dense flag

The flag densifies lines, so for points there is nothing to densify and
both runs have to place every point in the same cell. The coordinates in
the fixture sit inside cells rather than on their edges, so a half cell
shift in either direction changes the row or the column and fails here.
"""
tools = Tools(session=xy_points_session)

tools.v_to_rast(input="points", output="plain", use="cat", type="point")
tools.v_to_rast(input="points", output="dense", use="cat", type="point", flags="d")

plain = tools.r_stats(input="plain", flags="gn").text.splitlines()
dense = tools.r_stats(input="dense", flags="gn").text.splitlines()

# east, north and category of the centre of the cell each point landed in
assert sorted(plain) == ["10.5 10.5 1", "20.5 20.5 2", "30.5 15.5 3"]
assert sorted(dense) == sorted(plain)
Loading