瀏覽代碼

r.viewshed: limit viewshed horizontally by specifying two angles

Anna Petrasova 5 年之前
父節點
當前提交
4b74af13fd

+ 14 - 1
raster/r.viewshed/eventlist.cpp

@@ -605,7 +605,20 @@ int is_point_outside_max_dist(Viewpoint vp, GridHeader hd,
     return 0;
     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
    //note: this is expensive because distance is not stored in the event

+ 4 - 1
raster/r.viewshed/eventlist.h

@@ -74,7 +74,10 @@ is_point_outside_max_dist(Viewpoint vp, GridHeader hd,
 			  dimensionType row, dimensionType col,
 			  dimensionType row, dimensionType col,
 			  float maxDist);
 			  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) */
 /*sort the event list by the angle around the viewpoint) */
 void sort_event_list(AMI_STREAM < AEvent > **eventList);
 void sort_event_list(AMI_STREAM < AEvent > **eventList);

+ 10 - 1
raster/r.viewshed/grass.cpp

@@ -309,6 +309,11 @@ init_event_list_in_memory(AEvent * eventList, char *rastName,
 						   hd->nodata_value);
 						   hd->nodata_value);
 		continue;
 		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
 	    /* if point is outside maxDist, do NOT include it as an
 	       event */
 	       event */
@@ -533,7 +538,11 @@ AMI_STREAM < AEvent > *init_event_list(char *rastName, Viewpoint * vp,
 		add_result_to_io_visibilitygrid(visgrid, &visCell);
 		add_result_to_io_visibilitygrid(visgrid, &visCell);
 		continue;
 		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
 	    /* if point is outside maxDist, do NOT include it as an
 	       event */
 	       event */
 	    if (is_point_outside_max_dist
 	    if (is_point_outside_max_dist

+ 21 - 0
raster/r.viewshed/main.cpp

@@ -151,6 +151,8 @@ int main(int argc, char *argv[])
     viewOptions.doCurv = FALSE;
     viewOptions.doCurv = FALSE;
     viewOptions.doRefr = FALSE;
     viewOptions.doRefr = FALSE;
     viewOptions.refr_coef = 1.0/7.0;
     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,
     parse_args(argc, argv, &vpRow, &vpCol, &viewOptions, &memSizeBytes,
 	       &region);
 	       &region);
@@ -530,6 +532,19 @@ parse_args(int argc, char *argv[], int *vpRow, int *vpCol,
     maxDistOpt->answer = infdist;
     maxDistOpt->answer = infdist;
     maxDistOpt->guisection = _("Settings");
     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, ... */
     /* 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
     /* in future we might calculate this based on the physics, for now we
        just fudge by the 1/7th approximation.
        just fudge by the 1/7th approximation.
@@ -608,6 +623,12 @@ parse_args(int argc, char *argv[], int *vpRow, int *vpCol,
     if (viewOptions->maxDist < 0 && viewOptions->maxDist != INFINITY_DISTANCE) {
     if (viewOptions->maxDist < 0 && viewOptions->maxDist != INFINITY_DISTANCE) {
 	G_fatal_error(_("A negative max distance value is not allowed"));
 	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->doCurv = curvature->answer;
     viewOptions->doRefr = refractionFlag->answer;
     viewOptions->doRefr = refractionFlag->answer;

+ 6 - 0
raster/r.viewshed/r.viewshed.html

@@ -84,6 +84,12 @@ visibility using option <b>max_distance</b>.  The value entered is in the
 same units as the cell size of the raster.
 same units as the cell size of the raster.
 
 
 <p>
 <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
 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
 500MB of main memory, and sets up its internal data structures so that
 it does not require more than this amount of RAM.  The user can set
 it does not require more than this amount of RAM.  The user can set

+ 10 - 0
raster/r.viewshed/testsuite/test_r_viewshed.py

@@ -60,6 +60,16 @@ class TestViewshed(TestCase):
         self.assertRasterMinMax(map=self.viewshed, refmin=0, refmax=180,
         self.assertRasterMinMax(map=self.viewshed, refmin=0, refmax=180,
             msg="Viewing angle above the ground must be between 0 and 180 deg")
             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):
 class TestViewshedAgainstReference(TestCase):
     """
     """

+ 10 - 0
raster/r.viewshed/viewshed.cpp

@@ -282,6 +282,11 @@ MemoryVisibilityGrid *viewshed_in_memory(char *inputfname, GridHeader * hd,
 	if (!is_nodata(visgrid->grid->hd, data[1][i]) &&
 	if (!is_nodata(visgrid->grid->hd, data[1][i]) &&
 	    !is_point_outside_max_dist(*vp, *hd, sn.row, sn.col,
 	    !is_point_outside_max_dist(*vp, *hd, sn.row, sn.col,
 				       viewOptions.maxDist)) {
 				       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 */
 	    /*calculate Distance to VP and Gradient, store them into sn */
 	    /* need either 3 elevation values or 
 	    /* need either 3 elevation values or 
 	     * 3 gradients calculated from 3 elevation values */
 	     * 3 gradients calculated from 3 elevation values */
@@ -527,6 +532,11 @@ IOVisibilityGrid *viewshed_external(char *inputfname, GridHeader * hd,
 	if (!is_nodata(visgrid->hd, data[1][i]) &&
 	if (!is_nodata(visgrid->hd, data[1][i]) &&
 	    !is_point_outside_max_dist(*vp, *hd, sn.row, sn.col,
 	    !is_point_outside_max_dist(*vp, *hd, sn.row, sn.col,
 				       viewOptions.maxDist)) {
 				       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 */
 	    /*calculate Distance to VP and Gradient, store them into sn */
 	    /* need either 3 elevation values or 
 	    /* need either 3 elevation values or 
 	     * 3 gradients calculated from 3 elevation values */
 	     * 3 gradients calculated from 3 elevation values */

+ 5 - 0
raster/r.viewshed/visibility.h

@@ -110,6 +110,11 @@ typedef struct viewOptions_
     /* points that are farther than this distance from the viewpoint are
     /* points that are farther than this distance from the viewpoint are
        not visible  */
        not visible  */
 
 
+    float horizontal_angle_min;
+    float horizontal_angle_max;
+    int doDirection;
+    /* exclude points outside of the angle range  */
+
     OutputMode outputMode;
     OutputMode outputMode;
     /* The mode the viewshed is output; 
     /* The mode the viewshed is output; 
        - in angle mode, the values recorded are   {NODATA, INVISIBLE, angle}
        - in angle mode, the values recorded are   {NODATA, INVISIBLE, angle}