Skip to content

Commit

Permalink
r.viewshed: limit viewshed horizontally by specifying two angles
Browse files Browse the repository at this point in the history
  • Loading branch information
petrasovaa committed Nov 7, 2019
1 parent 43fae79 commit 4b74af1
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 3 deletions.
15 changes: 14 additions & 1 deletion raster/r.viewshed/eventlist.cpp
Expand Up @@ -605,7 +605,20 @@ int is_point_outside_max_dist(Viewpoint vp, GridHeader hd,
return 0;
}


int is_point_inside_angle(Viewpoint vp,
dimensionType row, dimensionType col,
float minAngle, float maxAngle)
{
double ang;

ang = atan2(vp.row -row, col - vp.col) * 180 / M_PI;
if (ang < 0)
ang += 360;
/* all angles are in range 0-360) */
if (minAngle < maxAngle)
return minAngle <= ang && ang <= maxAngle;
return minAngle <= ang || ang <= maxAngle;
}

/* ------------------------------------------------------------
//note: this is expensive because distance is not stored in the event
Expand Down
5 changes: 4 additions & 1 deletion raster/r.viewshed/eventlist.h
Expand Up @@ -74,7 +74,10 @@ is_point_outside_max_dist(Viewpoint vp, GridHeader hd,
dimensionType row, dimensionType col,
float maxDist);


/*determines if the point at row,col is within min and max angle (in 0-360, 0 is east, CCW) */
int is_point_inside_angle(Viewpoint vp,
dimensionType row, dimensionType col,
float minAngle, float maxAngle);

/*sort the event list by the angle around the viewpoint) */
void sort_event_list(AMI_STREAM < AEvent > **eventList);
Expand Down
11 changes: 10 additions & 1 deletion raster/r.viewshed/grass.cpp
Expand Up @@ -309,6 +309,11 @@ init_event_list_in_memory(AEvent * eventList, char *rastName,
hd->nodata_value);
continue;
}
if (viewOptions.doDirection &&
!is_point_inside_angle(*vp, i, j, viewOptions.horizontal_angle_min,
viewOptions.horizontal_angle_max)) {
continue;
}

/* if point is outside maxDist, do NOT include it as an
event */
Expand Down Expand Up @@ -533,7 +538,11 @@ AMI_STREAM < AEvent > *init_event_list(char *rastName, Viewpoint * vp,
add_result_to_io_visibilitygrid(visgrid, &visCell);
continue;
}

if (viewOptions.doDirection &&
!is_point_inside_angle(*vp, i, j, viewOptions.horizontal_angle_min,
viewOptions.horizontal_angle_max)) {
continue;
}
/* if point is outside maxDist, do NOT include it as an
event */
if (is_point_outside_max_dist
Expand Down
21 changes: 21 additions & 0 deletions raster/r.viewshed/main.cpp
Expand Up @@ -151,6 +151,8 @@ int main(int argc, char *argv[])
viewOptions.doCurv = FALSE;
viewOptions.doRefr = FALSE;
viewOptions.refr_coef = 1.0/7.0;
viewOptions.horizontal_angle_min = 0;
viewOptions.horizontal_angle_max = 360;

parse_args(argc, argv, &vpRow, &vpCol, &viewOptions, &memSizeBytes,
&region);
Expand Down Expand Up @@ -530,6 +532,19 @@ parse_args(int argc, char *argv[], int *vpRow, int *vpCol,
maxDistOpt->answer = infdist;
maxDistOpt->guisection = _("Settings");

/* angle range */
struct Option *direction;

direction = G_define_option();
direction->key = "direction_range";
direction->type = TYPE_DOUBLE;
direction->required = NO;
direction->key_desc = "min,max";
direction->options = "0-360";
direction->description =
_("Minimum and maximum horizontal angle limiting viewshed (0 is East, counterclockwise)");
direction->guisection = _("Settings");

/* atmospheric refraction coeff. 1/7 for visual, 0.325 for radio waves, ... */
/* in future we might calculate this based on the physics, for now we
just fudge by the 1/7th approximation.
Expand Down Expand Up @@ -608,6 +623,12 @@ parse_args(int argc, char *argv[], int *vpRow, int *vpCol,
if (viewOptions->maxDist < 0 && viewOptions->maxDist != INFINITY_DISTANCE) {
G_fatal_error(_("A negative max distance value is not allowed"));
}
viewOptions->doDirection = 0;
if (direction->answer) {
viewOptions->horizontal_angle_min = atof(direction->answers[0]);
viewOptions->horizontal_angle_max = atof(direction->answers[1]);
viewOptions->doDirection = 1;
}

viewOptions->doCurv = curvature->answer;
viewOptions->doRefr = refractionFlag->answer;
Expand Down
6 changes: 6 additions & 0 deletions raster/r.viewshed/r.viewshed.html
Expand Up @@ -83,6 +83,12 @@ <h2>NOTES</h2>
visibility using option <b>max_distance</b>. The value entered is in the
same units as the cell size of the raster.

<p>
The user can limit view horizontally by specifying a minimum and maximum directions
using option <b>direction_range</b>. The angles are in degrees, CCW, East is 0.
The angles should be between 0 and 360, e.g. direction_range=0,180 (north view),
or direction_range=270,90 (east view).

<p>
Main memory usage: By default <em>r.viewshed</em> assumes it has
500MB of main memory, and sets up its internal data structures so that
Expand Down
10 changes: 10 additions & 0 deletions raster/r.viewshed/testsuite/test_r_viewshed.py
Expand Up @@ -60,6 +60,16 @@ def test_limits_extreme_value(self):
self.assertRasterMinMax(map=self.viewshed, refmin=0, refmax=180,
msg="Viewing angle above the ground must be between 0 and 180 deg")

def test_direction(self):
"""Test direction range
"""
obs_elev = '1.75'
self.assertModule('r.viewshed', input='elevation',
coordinates=(634720,216180), output=self.viewshed,
observer_elevation=obs_elev, direction_range=[270, 90])
minmax = 'null_cells=1998900\nmin=74.90344\nmax=180'
self.assertRasterFitsUnivar(raster=self.viewshed, reference=minmax, precision=1e-5)


class TestViewshedAgainstReference(TestCase):
"""
Expand Down
10 changes: 10 additions & 0 deletions raster/r.viewshed/viewshed.cpp
Expand Up @@ -282,6 +282,11 @@ MemoryVisibilityGrid *viewshed_in_memory(char *inputfname, GridHeader * hd,
if (!is_nodata(visgrid->grid->hd, data[1][i]) &&
!is_point_outside_max_dist(*vp, *hd, sn.row, sn.col,
viewOptions.maxDist)) {
if (viewOptions.doDirection &&
!is_point_inside_angle(*vp, sn.row, sn.col,
viewOptions.horizontal_angle_min,
viewOptions.horizontal_angle_max))
continue;
/*calculate Distance to VP and Gradient, store them into sn */
/* need either 3 elevation values or
* 3 gradients calculated from 3 elevation values */
Expand Down Expand Up @@ -527,6 +532,11 @@ IOVisibilityGrid *viewshed_external(char *inputfname, GridHeader * hd,
if (!is_nodata(visgrid->hd, data[1][i]) &&
!is_point_outside_max_dist(*vp, *hd, sn.row, sn.col,
viewOptions.maxDist)) {
if (viewOptions.doDirection &&
!is_point_inside_angle(*vp, sn.row, sn.col,
viewOptions.horizontal_angle_min,
viewOptions.horizontal_angle_max))
continue;
/*calculate Distance to VP and Gradient, store them into sn */
/* need either 3 elevation values or
* 3 gradients calculated from 3 elevation values */
Expand Down
5 changes: 5 additions & 0 deletions raster/r.viewshed/visibility.h
Expand Up @@ -110,6 +110,11 @@ typedef struct viewOptions_
/* points that are farther than this distance from the viewpoint are
not visible */

float horizontal_angle_min;
float horizontal_angle_max;
int doDirection;
/* exclude points outside of the angle range */

OutputMode outputMode;
/* The mode the viewshed is output;
- in angle mode, the values recorded are {NODATA, INVISIBLE, angle}
Expand Down

0 comments on commit 4b74af1

Please sign in to comment.