Procházet zdrojové kódy

r.random: Add -s flag, remove -i flag, redo interface (#1054)

A new flag -s is added similarly to r.mapcalc where either seed or -s
is required, i.e., user needs to explicitly ask for a generated random seed
or provide a fixed one. Store seed to history (esp. useful when generated).
The long type used for the seed is not portable, but that's what is used in the library function.

The -i flag for (only) printing cell counts is removed. The warning about that
possibly happening was in the documentation. r.univar added as a primary suggested
way of obtaining that info. The flag just makes the interface more complicated
and another module can be used instead of running this module twice.

This also changes -z flag to -n and -d to -z. Flag z is more common for adding Z coordinate than obsolete association
where zero is now null. -d was used for Z coordinate, so -z now has different
meaning than before. The flag letter is replaced by n and what was Z coordinate (d)
can now be z.

The original sections were Required, Optional, and Points with points
having only one entry. Now all options and flags are split between
Input and Output with Optional having only the standard long flags.

This also updates r.random interface so that it takes advantage of parser features namely
the option dependencies.

Use current terminology in documentation (raster map, but not raster map layer,
no sites, but vector points or vector point map). Restructure doc and add sections.
Tell what is the role of the raster (input/cover).
Vaclav Petras před 3 roky
rodič
revize
3b89e47404

+ 0 - 24
raster/r.random/README

@@ -1,24 +0,0 @@
-/*********************************************************************
- * GRASS program to create a raster map with non-zero data
- *         in random locations.
- * Locations will be within non-zero data in the input layer.
- *        (unless -z option specified)
- * If it necessary to restrict the locations to only some categories
- *    of the input map, a reclass map should be constructed first
- *    and used as input to this program.
- *
- * Number of random cells can be a fixed number or a percentage of cells.
- *
- * Resulting raster map consists of original cell values at the selected
- *         random locations and zero elsewhere.
- *
- * A sitefile may optionally be created.
- *
- * Usage:
- *  r.random [-iqz] input=name [raster_output=name] [sites_output=name] n=n[%]
- *
- *    -i mean report number of total cells and null cells
- *    -q means run quietly
- *    -z means generate random locations for zero-data as well
- *
- *********************************************************************/

+ 1 - 1
raster/r.random/local_proto.h

@@ -48,7 +48,7 @@ void get_stats(struct rr_state *);
 int execute_random(struct rr_state *);
 
 /* support.c */
-int make_support(struct rr_state *, int, double);
+int make_support(struct rr_state *, int, double, long);
 
 #endif /* __LOCAL_PROTO_H__ */
 

+ 45 - 43
raster/r.random/main.c

@@ -8,11 +8,15 @@
  *                 Eric G. Miller
  *               Modified for GRASS 6.x and updates
  *                 Brad Douglas
+ *               Cover map support
+ *                 Markus Neteler
+ *               Modified interface for GRASS 8.x
+ *                 Vaclav Petras
  *
  * PURPOSE:      Generate a vector or raster map of random points
  *               selected from an input map
  *
- * COPYRIGHT:    (C) 2003-2007 by the GRASS Development Team
+ * COPYRIGHT:    (C) 2003-2020 by the GRASS Development Team
  *
  *               This program is free software under the GNU General Public
  *               License (>=v2). Read the file COPYING that comes with GRASS
@@ -43,7 +47,7 @@ int main(int argc, char *argv[])
     } parm;
     struct
     {
-	struct Flag *zero, *info, *z_geometry, *notopol_flag;
+	struct Flag *gen_seed, *zero, *z_geometry, *notopol_flag;
     } flag;
 
     G_gisinit(argv[0]);
@@ -55,55 +59,75 @@ int main(int argc, char *argv[])
     G_add_keyword(_("random"));
     G_add_keyword(_("level1"));
 
+    module->label =
+	_("Creates randomly placed raster cells or vector points");
     module->description =
-	_("Creates a raster map layer and vector point map "
-	  "containing randomly located points.");
+	_("Creates a raster map and vector point map "
+	  "containing randomly located cells and points.");
 
     parm.input = G_define_standard_option(G_OPT_R_INPUT);
     parm.input->description = _("Name of input raster map");
+    parm.input->guisection = _("Input");
 
     parm.cover = G_define_standard_option(G_OPT_R_INPUT);
     parm.cover->key = "cover";
     parm.cover->required = NO;
     parm.cover->description = _("Name of cover raster map");
+    parm.cover->guisection = _("Input");
 
     parm.npoints = G_define_option();
     parm.npoints->key = "npoints";
     parm.npoints->key_desc = "number[%]";
     parm.npoints->type = TYPE_STRING;
     parm.npoints->required = YES;
-    parm.npoints->description = _("The number of points to allocate");
+    parm.npoints->label = _("The number of points (or cells) to generate");
+    parm.npoints->description =
+        _("The number of vector points or raster cells to generate,"
+          " possibly as a percentage of number of cells");
+    parm.npoints->guisection = _("Output");
 
     parm.raster = G_define_standard_option(G_OPT_R_OUTPUT);
     parm.raster->required = NO;
     parm.raster->key = "raster";
+    parm.raster->guisection = _("Output");
 
     parm.sites = G_define_standard_option(G_OPT_V_OUTPUT);
     parm.sites->required = NO;
     parm.sites->key = "vector";
+    parm.sites->guisection = _("Output");
 
     parm.seed = G_define_option();
     parm.seed->key = "seed";
     parm.seed->type = TYPE_INTEGER;
     parm.seed->required = NO;
     parm.seed->description = _("Seed for rand() function");
+    parm.seed->guisection = _("Input");
 
-    flag.zero = G_define_flag();
-    flag.zero->key = 'z';
-    flag.zero->description = _("Generate points also for NULL category");
+    flag.gen_seed = G_define_flag();
+    flag.gen_seed->key = 's';
+    flag.gen_seed->description = _("Generate random seed (result is non-deterministic)");
+    flag.gen_seed->guisection = _("Input");
 
-    flag.info = G_define_flag();
-    flag.info->key = 'i';
-    flag.info->description =
-	_("Report information about input raster and exit");
+    flag.zero = G_define_flag();
+    flag.zero->key = 'n';
+    flag.zero->description = _("Generate points also for NULL cells");
+    flag.zero->guisection = _("Output");
 
     flag.z_geometry = G_define_flag();
-    flag.z_geometry->key = 'd';
-    flag.z_geometry->description = _("Generate vector points as 3D points");
+    flag.z_geometry->key = 'z';
+    flag.z_geometry->label = _("Generate vector points as 3D points");
+    flag.z_geometry->description = _("Input raster values will be used for Z coordinates");
+    flag.z_geometry->guisection = _("Output");
 
     flag.notopol_flag = G_define_standard_flag(G_FLG_V_TOPO);
-    flag.notopol_flag->description = _("Do not build topology in points mode");
-    flag.notopol_flag->guisection = _("Points");
+    flag.notopol_flag->description = _("Do not build topology for vector points");
+    flag.notopol_flag->guisection = _("Output");
+
+    /* Either explicit seed or explicitly generate seed, but not both. */
+    G_option_exclusive(parm.seed, flag.gen_seed, NULL);
+    G_option_required(parm.seed, flag.gen_seed, NULL);
+    /* At least one of outputs is required. */
+    G_option_required(parm.raster, parm.sites, NULL);
 
     if (G_parser(argc, argv) != 0)
 	exit(EXIT_FAILURE);
@@ -124,32 +148,6 @@ int main(int argc, char *argv[])
     myState.z_geometry = flag.z_geometry->answer;
     myState.notopol = flag.notopol_flag->answer;
 
-    /* If they only want info we ignore the rest */
-    get_stats(&myState);
-
-    if (flag.info->answer) {
-#ifdef HAVE_LONG_LONG_INT
-	G_message("Raster:      %s\n"
-		  "Cover:       %s\n"
-		  "Cell Count:  %llu\n"
-		  "Null Cells:  %llu\n\n",
-		  myState.inraster, myState.inrcover,
-		  myState.nCells, myState.nNulls);
-#else
-	G_message("Raster:      %s\n"
-		  "Cover:       %s\n"
-		  "Cell Count:  %lu\n"
-		  "Null Cells:  %lu\n\n",
-		  myState.inraster, myState.inrcover,
-		  myState.nCells, myState.nNulls);
-#endif
-	exit(EXIT_SUCCESS);
-    }
-
-    if (!(parm.raster->answer || parm.sites->answer))
-	G_fatal_error(_("Note: one (or both) of %s and %s must be specified"),
-		      parm.raster->key, parm.sites->key);
-
     /* look for n[%] */
     percent = has_percent(parm.npoints->answer);
     if (percent) {
@@ -171,6 +169,10 @@ int main(int argc, char *argv[])
 	}
     }
 
+    /* Compute stats only after we know parameters are OK, but before
+       we need to check the provided number of points. */
+    get_stats(&myState);
+
     count = (myState.use_nulls) ? myState.nCells :
 	myState.nCells - myState.nNulls;
 
@@ -214,7 +216,7 @@ int main(int argc, char *argv[])
     execute_random(&myState);
 
     if (myState.outraster)
-	make_support(&myState, percent, percentage);
+	make_support(&myState, percent, percentage, seed_value);
 
     return EXIT_SUCCESS;
 }

+ 107 - 54
raster/r.random/r.random.html

@@ -1,33 +1,65 @@
 <h2>DESCRIPTION</h2>
-<p>The program <em>r.random</em> allows the user to create a
-raster map layer and/or a vector points map containing 
+
+<p>
+The module <em>r.random</em> creates a raster map with values in random places.
+Alternatively, it creates random vector points at these places.
+Number of random cells or points can be a fixed number or a percentage of cells
+from the input.
+By default, generated cells or points will be subset of non-NULL cells of the input.
+Resulting raster map consists of original cell values at the selected
+random locations and NULL (no data) values elsewhere.
+
+<h3>Placement of cells and points</h3>
+
+<p>
+The module allows the user to create a
+raster map and/or a vector points map containing
 coordinates of points whose locations have been randomly
-determined.  The program locates these randomly generated
-vector points (sites) within the current geographic region and mask (if
-any), on non-NULL category value data areas within a
-user-specified raster map layer. If the user sets the
-<b>-z</b> flag, points will be randomly generated across all
+determined. The module places these randomly generated
+vector points within the current computational region and raster mask (if
+any), on non-NULL raster cells in a
+user-specified raster map. If the user sets the
+<b>-n</b> flag, points will be randomly generated across all
 cells (even those with NULL values).
+Cells in the resulting raster overlap with the cells of the input raster
+based on the current computational region.
+Points in the resulting vector map are placed in cell centers of these cells.
+
+<h3>Number of cells and points</h3>
 
-<p>The category values and
-corresponding category names already associated with the
-random point locations in the <em>input</em> map layer are
-assigned to these points in the <em>raster_output</em> map
-layer. If the <b>-z</b> is specified, then a unique entry
+<p>The user may specify the quantity of random locations to be
+generated either as a <em>positive integer</em> (e.g., 10),
+or as a <em>percentage of the raster map's cells</em>
+(e.g., 10%, or 3.05%).  The number of cells considered for
+the percentage reflects whether or not the <b>-n</b> flag
+was given. Options are 0-100; fractions of percent may be
+stated as decimals (e.g., 66.67%, or 0.05%).
+
+<h3>Values</h3>
+
+<p>The cell values and
+corresponding category names (if present) associated with the
+random point locations in the <em>input</em> map are
+assigned to the newly generated cells in the <em>raster</em> map.
+If the <b>-n</b> is specified, then a unique entry
 is made for the value used where the <em>input</em> was NULL.
 This value is at least 1 less than the smallest value in the
 <em>input</em> raster and is given a medium gray color.
 
-<h2>NOTES</h2>
-<p>If a <em>cover</em> raster map is specified and the <em>cover</em> map
+<p>
+If a <em>cover</em> raster map is specified, values are taken
+from the <em>cover</em> raster map instead of the <em>input</em> raster map.
+If a <em>cover</em> raster map is specified and the <em>cover</em> map
 contains NULL (no data) values, these points are suppressed in the
-resulting <em>vector_output</em> or <em>raster_output</em> map.
+resulting <em>vector</em> or <em>raster</em> map.
+
+<h3>Vector output</h3>
 
-<p>The <em>vector_output</em> file created by <em>r.random</em>
+<p>The <em>vector</em> file created by <em>r.random</em>
 contains vector points that represent the <em>center points</em> of the
 randomly generated cells.  A <em>value</em> attribute contains the cell value
 of the <em>input</em> raster (or the assigned value
-when <b>-z</b> is used). <br>
+when <b>-n</b> is used).
 If a <em>cover</em> map is additionally specified, a second
 column  <em>covervalue</em> is populated with raster values from
 the <em>cover</em> map.
@@ -36,35 +68,41 @@ the <em>cover</em> map.
 topology to minimize the required resources. This is suitable input
 to <em>v.surf.rst</em> and other vector modules.
 
-<p>The user may specify the quantity of random locations to be
-generated either as a <em>positive integer</em> (e.g., 10),
-or as a <em>percentage of the raster map layer's cells</em> 
-(e.g., 10%, or 3.05%).  The number of cells considered for 
-the percentage reflects whether or not the <b>-z</b> flag
-was given. Options are 0-100; percentages less than
-one percent may be stated as decimals.
-
-<p>Flag <b>-i</b> prints the raster map's name and location, 
-the total number of cells under the current region settings, and
-the number of NULL valued cells under the current region settings.
-Then module exits without doing anything.  Useful for deciding on the number
-of sites to have <em>r.random</em> create.
-<b>WARNING:</b> this feature may be removed in future. Use 
-<a href="g.region.html">g.region</a> and 
-<a href="r.report.html">r.report</a>
-instead.
+<h2>NOTES</h2>
+
+To decide on the number
+of points <em>r.random</em> will create,
+use <em><a href="r.univar.html">r.univar</a></em>,
+<em><a href="g.region.html">g.region</a></em>, or
+<em><a href="r.report.html">r.report</a></em>.
+<em>r.univar</em> is the fastest way to obtain number of
+non-NULL cells and NULL cells in a raster map
+given the current computational region and raster mask:
+
+<div class="code"><pre>
+r.univar map=inputmap
+</pre></div>
+
+The text output contains total number of null and non-null cells
+(called <code>cells</code> in the machine-readable shell script style output),
+total null cells (<code>null_cells</code>),
+and number of non-null cells (<code>n</code>).
+
+Alternatively, you can use the following to examine the computational
+region and the raster map:
+
 <div class="code"><pre>
 g.region -p
 r.report map=inputmap units=c null="*" nsteps=1
 </pre></div>
 
-<p>To create random vector point locations within some, but not all, 
-non-zero categories of the input raster map layer, 
-the user must first create a reclassified raster map layer 
-of the original raster map layer (e.g., using the GRASS 
-program <em><a href="r.reclass.html">r.reclass</a></em>) 
-that contains only the desired categories, 
-and then use the reclassed raster map layer as input to <em>r.random</em>.
+<p>To create random vector point locations within some, but not all,
+categories of a integer input raster map (aka CELL raster map),
+the user must first create a reclassified raster map
+of the original raster map (e.g., using the GRASS
+module <em><a href="r.reclass.html">r.reclass</a></em>)
+that contains only the desired categories,
+and then use the reclassed raster map as input to <em>r.random</em>.
 
 <h2>EXAMPLES</h2>
 
@@ -88,7 +126,7 @@ landuse map, result stored in 3D vector map:
 
 <div class="code"><pre>
 g.region raster=elevation -p
-r.random -d elevation cover=landclass96 vector=luserand3d n=100
+r.random -z elevation cover=landclass96 vector=luserand3d n=100
 
 # data output (value: elevation, covervalue: landuse class):
 v.db.select luserand3d
@@ -100,26 +138,41 @@ cat|value|covervalue
 ...
 </pre></div>
 
-<h2>KNOWN ISSUES</h2>
-
-It's not possible to use the <b>-i</b> flag without specifying the
-<b>npoints</b> parameter.
-
 
 <h2>SEE ALSO</h2>
 
-<em>
-<a href="g.region.html">g.region</a>,
-<a href="r.reclass.html">r.reclass</a>,
-<a href="v.random.html">v.random</a>,
-<a href="v.surf.rst.html">v.surf.rst</a>
-</em>
+<ul>
+    <li>
+        <em><a href="g.region.html">g.region</a></em>
+        for setting the computational region and examining the total number of cells,
+    </li>
+    <li>
+        <em><a href="r.reclass.html">r.reclass</a></em>
+        for working only with subset of values in the raster map,
+    </li>
+    <li>
+        <em><a href="v.random.html">v.random</a></em>
+        for generating vector points without any involvement of raster data,
+    </li>
+    <li>
+        <em><a href="r.random.cells.html">r.random.cells</a></em>
+        for generating random cells with with spatial dependence (minimal distance),
+    </li>
+    <li>
+        <em><a href="r.surf.random.html">r.surf.random</a></em>
+        as an option for generating random cell values,
+    </li>
+    <li>
+        <em><a href="v.surf.rst.html">v.surf.rst</a></em>
+        as an option for creating a surface from sampled points.
+    </li>
+</ul>
 
 
 <h2>AUTHOR</h2>
 
 Dr. James Hinthorne,
-GIS Laboratory, 
+GIS Laboratory,
 Central Washington University
 <p>Modified for GRASS 5.0 by Eric G. Miller
 <p>Cover map support by Markus Neteler, 2007

+ 7 - 7
raster/r.random/support.c

@@ -5,7 +5,7 @@
 #include "local_proto.h"
 
 
-int make_support(struct rr_state *theState, int percent, double percentage)
+int make_support(struct rr_state *theState, int percent, double percentage, long seed_value)
 {
     char title[100];
     struct History hist;
@@ -39,21 +39,21 @@ int make_support(struct rr_state *theState, int percent, double percentage)
     /* write history for output raster */
     if (Rast_read_history(theState->outraster, G_mapset(), &hist) >= 0) {
 	Rast_short_history(theState->outraster, "raster", &hist);
-	Rast_format_history(&hist, HIST_DATSRC_1, "Based on map <%s>", inraster);
+	Rast_format_history(&hist, HIST_DATSRC_1, "Values based on map <%s>", inraster);
 	if (percent)
 	    Rast_format_history(
 		&hist, HIST_DATSRC_2,
-		"Random points over %.2f percent of the base map <%s>",
-		percentage, inraster);
+		"Random points for %.2f percent of input cells with random seed %ld",
+		percentage, seed_value);
 	else
 	    Rast_format_history(
 		&hist, HIST_DATSRC_2,
 #ifdef HAVE_LONG_LONG_INT
-		"%llu random points on the base map <%s>",
+		"%llu random points with random seed %ld",
 #else
-		"%lu random points on the base map <%s>",
+		"%lu random points with random seed %ld",
 #endif
-		theState->nRand, theState->inraster);
+		theState->nRand, seed_value);
 
 	Rast_command_history(&hist);
 	Rast_write_history(theState->outraster, &hist);

+ 23 - 7
raster/r.random/testsuite/test_r_random.py

@@ -57,7 +57,11 @@ class TestRasterTile(TestCase):
     def test_random_raster(self):
         """Testing r.random  runs successfully"""
         self.assertModule(
-            "r.random", input=self.input, npoints=self.npoints, raster=self.raster
+            "r.random",
+            input=self.input,
+            npoints=self.npoints,
+            raster=self.raster,
+            seed=1,
         )
         # check if random raster was created
         self.assertRasterExists(
@@ -67,7 +71,11 @@ class TestRasterTile(TestCase):
     def test_random_vector(self):
         """Testing r.random  runs successfully"""
         self.assertModule(
-            "r.random", input=self.input, npoints=self.npoints, vector=self.vector
+            "r.random",
+            input=self.input,
+            npoints=self.npoints,
+            vector=self.vector,
+            seed=1,
         )
         # check if random vector was created
         self.assertVectorExists(
@@ -84,6 +92,7 @@ class TestRasterTile(TestCase):
             input=self.input,
             npoints=self.npoints,
             raster=self.raster + "_null",
+            seed=1,
         )
         # check if random raster ( with NULL values )  was created
         self.assertRasterExists(
@@ -98,6 +107,7 @@ class TestRasterTile(TestCase):
             input=self.input,
             npoints=self.npoints,
             vector=self.vector + "_null",
+            seed=1,
         )
         # check if random vector ( with NULL values ) was created
         self.assertVectorExists(
@@ -114,6 +124,7 @@ class TestRasterTile(TestCase):
             input=self.input,
             npoints=self.npoints,
             raster=self.raster + "_without_topology",
+            seed=1,
         )
         # check if random raster ( without topology )  was created
         self.assertRasterExists(
@@ -129,6 +140,7 @@ class TestRasterTile(TestCase):
             input=self.input,
             npoints=self.npoints,
             vector=self.vector + "_without_topology",
+            seed=1,
         )
         # check if random vector ( without topology ) was created
         self.assertVectorExists(
@@ -140,28 +152,30 @@ class TestRasterTile(TestCase):
             vector=self.vector + "_without_topology", reference=topology
         )
 
-    def test_random_raster_flag_d(self):
+    def test_random_raster_flag_z(self):
         """Testing r.random  runs successfully"""
         self.assertModule(
             "r.random",
-            flags="d",
+            flags="z",
             input=self.input,
             npoints=self.npoints,
             raster=self.raster + "_3D",
+            seed=1,
         )
         # check if random raster ( 3D points )  was created
         self.assertRasterExists(
             self.raster, msg="landcover_1m_raster_random_3D not created"
         )
 
-    def test_vector_random_flag_d(self):
+    def test_vector_random_flag_z(self):
         """Testing r.random  runs successfully"""
         self.assertModule(
             "r.random",
-            flags="d",
+            flags="z",
             input=self.input,
             npoints=self.npoints,
             vector=self.vector + "_3D",
+            seed=1,
         )
         # check if random vector ( 3D points ) was created
         self.assertVectorExists(
@@ -178,6 +192,7 @@ class TestRasterTile(TestCase):
             npoints=self.npoints,
             cover="landcover_1m",
             raster=self.raster + "_cover_landcover_1m",
+            seed=1,
         )
         # check if random raster ( 3D points )  was created
         self.assertRasterExists(
@@ -185,7 +200,7 @@ class TestRasterTile(TestCase):
             msg="landcover_1m_raster_random_cover_landcover_1m was not created",
         )
 
-    def test_vector_random_flag_d(self):
+    def test_random_vector_cover(self):
         """Testing r.random  runs successfully"""
         self.assertModule(
             "r.random",
@@ -193,6 +208,7 @@ class TestRasterTile(TestCase):
             npoints=self.npoints,
             cover="landcover_1m",
             vector=self.vector + "_cover_landcover_1m",
+            seed=1,
         )
         # check if random vector ( 3D points ) was created
         self.assertVectorExists(

+ 9 - 13
raster/r.random/testsuite/testrandom.py

@@ -35,8 +35,8 @@ class Testrr(TestCase):
         self.runModule("g.remove", flags="f", type="vector", name=self.vector)
         self.runModule("g.remove", flags="f", type="raster", name=self.raster)
 
-    def test_flag_z(self):
-        """Testing flag z"""
+    def test_flag_n(self):
+        """Testing flag n"""
         string = """area_cat|count|sum
         1|0|null
         2|0|null
@@ -55,26 +55,22 @@ class Testrr(TestCase):
             cover=self.cover,
             npoints=100,
             vector=self.vector,
-            flags="z",
+            flags="n",
+            seed=1,
         )
         r_random.outputs.stdout = string
         self.assertLooksLike(reference=string, actual=r_random.outputs.stdout)
 
-    def test_flag_i(self):
-        """Testing flag i"""
-        self.assertModule(
-            "r.random", input=self.input, cover=self.cover, npoints=100, flags="i"
-        )
-
-    def test_flag_d(self):
-        """Testing flag d"""
+    def test_flag_z(self):
+        """Testing flag z"""
         self.assertModule(
             "r.random",
             input=self.input,
             cover=self.cover,
             npoints=100,
             vector=self.vector,
-            flags="d",
+            flags="z",
+            seed=1,
         )
         self.assertModule("v.info", map=self.vector, flags="t")
         topology = dict(points=100, lines=0, areas=0, map3d=1)
@@ -89,7 +85,7 @@ class Testrr(TestCase):
             npoints=36011,
             vector=self.vector,
             flags="b",
-            overwrite=True,
+            seed=1,
         )
         self.assertModule("v.info", map=self.vector, flags="t")
         topology = dict(points=36011, lines=0, areas=0)