Skip to content

Commit ebf2de1

Browse files
authored
r.geomorphon: Add two more comparison modes (OSGeo#1096)
The geomorphon computation includes a procedure done for each of the eight cardinal directions. The procedure traces through the elevation data to find the angles of zenith and nadir line of sight and to decide if the cardinal point is above (1), below (-1) or level (0) with the central point. The existing nadir/zenith comparison defaults to 0 when zenith and nadir angles are exactly equal and at least one angle is over its threshold. For example, this would be the case if the zenith point was 20 raster cells away at height +20m and the nadir point was 1 raster cell away at height -1m. This is much more likely to happen when the elevation data is low-resolution. Specifically, at 30m raster resolution and 1m vertical resolution this happened every 1 out of 25 cases on average. In that edge case the computed distance to the cardinal point remains 0, which results in incorrect derived cartesian coordinates and related results, most notably azimuth and elongation. Besides that, the comparison does not specifically check that when it comes to a 1 or -1 result, the corresponding angle is above its threshold. Address these issues by introducing a new "comparison" command-line option, in which "anglev1" stands for the exact existing behaviour and "anglev2" and "anglev2_distance" enable more sophisticated comparison methods.
1 parent 37dc05e commit ebf2de1

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed

raster/r.geomorphon/local_proto.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ GLOBAL double search_distance, flat_distance;
8282
GLOBAL double flat_threshold, flat_threshold_height;
8383
GLOBAL struct Cell_head window;
8484
GLOBAL int cell_step;
85+
/* Zenith/nadir comparison modes. */
86+
GLOBAL enum
87+
{
88+
ANGLEV1,
89+
ANGLEV2,
90+
ANGLEV2_DISTANCE
91+
} compmode;
8592

8693
/* memory */
8794
int open_map(MAPS * rast);

raster/r.geomorphon/main.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ int main(int argc, char **argv)
8484
*par_skip_radius,
8585
*par_flat_threshold,
8686
*par_flat_distance,
87+
*par_comparison,
8788
*par_multi_prefix, *par_multi_step, *par_multi_start;
8889
struct Flag *flag_units, *flag_extended;
8990

@@ -151,6 +152,15 @@ int main(int argc, char **argv)
151152
par_flat_distance->description =
152153
_("Flatness distance, zero for none");
153154

155+
par_comparison = G_define_option();
156+
par_comparison->key = "comparison";
157+
par_comparison->type = TYPE_STRING;
158+
par_comparison->options = "anglev1,anglev2,anglev2_distance";
159+
par_comparison->answer = "anglev1";
160+
par_comparison->required = NO;
161+
par_comparison->description =
162+
_("Comparison mode for zenith/nadir line-of-sight search");
163+
154164
par_multi_prefix = G_define_option();
155165
par_multi_prefix->key = "prefix";
156166
par_multi_prefix->type = TYPE_STRING;
@@ -193,6 +203,14 @@ int main(int argc, char **argv)
193203
double ns_resolution;
194204

195205
multires = (par_multi_prefix->answer) ? 1 : 0;
206+
if (!strcmp(par_comparison->answer, "anglev1"))
207+
compmode = ANGLEV1;
208+
else if (!strcmp(par_comparison->answer, "anglev2"))
209+
compmode = ANGLEV2;
210+
else if (!strcmp(par_comparison->answer, "anglev2_distance"))
211+
compmode = ANGLEV2_DISTANCE;
212+
else
213+
G_fatal_error(_("Failed parsing <%s>"), par_comparison->answer);
196214
for (i = o_forms; i < o_size; ++i) /* check for outputs */
197215
if (opt_output[i]->answer) {
198216
if (G_legal_filename(opt_output[i]->answer) < 0)

raster/r.geomorphon/pattern.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,65 @@
77
static int nextr[NUM_DIRS] = { -1, -1, -1, 0, 1, 1, 1, 0 };
88
static int nextc[NUM_DIRS] = { 1, 0, -1, -1, -1, 0, 1, 1 };
99

10+
/*
11+
* A more thorough comparison using a few factors of different priority
12+
* similar to the BGP best path selection algorithm. When the distances are
13+
* equal, it becomes an improved version of the original r.geomorphon
14+
* comparison (which is different from the original paper), in that it applies
15+
* each threshold to its respective angle and does not default to 0 when there
16+
* is a tie. Each angle must be non-negative.
17+
*/
18+
static int compare_multi(const double nadir_angle, const double zenith_angle,
19+
const double nadir_threshold,
20+
const double zenith_threshold,
21+
const double nadir_distance,
22+
const double zenith_distance)
23+
{
24+
const unsigned char
25+
nadir_over = nadir_angle > nadir_threshold,
26+
zenith_over = zenith_angle > zenith_threshold;
27+
28+
/*
29+
* If neither angle exceeds its threshold, consider the elevation profile
30+
* flat enough.
31+
*/
32+
if (!nadir_over && !zenith_over)
33+
return 0;
34+
/*
35+
* If exactly one angle exceeds its threshold, that angle represents the
36+
* elevation profile.
37+
*/
38+
if (!nadir_over && zenith_over)
39+
return 1;
40+
if (nadir_over && !zenith_over)
41+
return -1;
42+
/*
43+
* If both angles exceed their thresholds, the greater angle (if any)
44+
* represents the elevation profile better.
45+
*/
46+
if (nadir_angle < zenith_angle)
47+
return 1;
48+
if (nadir_angle > zenith_angle)
49+
return -1;
50+
/*
51+
* Here each angle is above its threshold and the angles are exactly equal
52+
* (which happens quite often when the elevation values are integer rather
53+
* than real). Consider the angle computed over the greater distance to
54+
* represent the elevation profile better since it is based on a greater
55+
* number of cells.
56+
*/
57+
if (nadir_distance < zenith_distance)
58+
return 1;
59+
if (nadir_distance > zenith_distance)
60+
return -1;
61+
/*
62+
* If there is still a tie, 0 would not be a valid result because both
63+
* angles exceed their thresholds hence the elevation profile definitely
64+
* is not flat. Resolve this with a preferred constant value.
65+
*/
66+
return 1;
67+
}
68+
1069
int calc_pattern(PATTERN * pattern, int row, int cur_row, int col)
1170
{
1271
/* calculate parameters of geomorphons and store it in the struct pattern */
@@ -121,6 +180,48 @@ int calc_pattern(PATTERN * pattern, int row, int cur_row, int col)
121180
if (nadir_angle < -nadir_threshold)
122181
pattern->negatives += i;
123182

183+
if (compmode != ANGLEV1) {
184+
/*
185+
* One of the differences from ANGLEV1 is that even if there is a
186+
* tie, the second switch block will eventually use one of the
187+
* distances instead of 0 to set the cardinal point distance.
188+
*/
189+
switch (compmode) {
190+
case ANGLEV2:
191+
pattern->pattern[i] =
192+
compare_multi(fabs(nadir_angle), fabs(zenith_angle),
193+
nadir_threshold, zenith_threshold, 0, 0);
194+
break;
195+
case ANGLEV2_DISTANCE:
196+
pattern->pattern[i] =
197+
compare_multi(fabs(nadir_angle), fabs(zenith_angle),
198+
nadir_threshold, zenith_threshold,
199+
nadir_distance, zenith_distance);
200+
break;
201+
default:
202+
G_fatal_error(_("Internal error in %s()"), __func__);
203+
}
204+
205+
switch (pattern->pattern[i]) {
206+
case 1:
207+
pattern->elevation[i] = zenith_height;
208+
pattern->distance[i] = zenith_distance;
209+
pattern->num_positives++;
210+
break;
211+
case -1:
212+
pattern->elevation[i] = nadir_height;
213+
pattern->distance[i] = nadir_distance;
214+
pattern->num_negatives++;
215+
break;
216+
case 0:
217+
pattern->distance[i] = search_distance;
218+
break;
219+
}
220+
221+
continue;
222+
} /* if (compmode != ANGLEV1) */
223+
224+
/* ANGLEV1 */
124225
if (fabs(zenith_angle) > zenith_threshold ||
125226
fabs(nadir_angle) > nadir_threshold) {
126227
if (fabs(nadir_angle) < fabs(zenith_angle)) {
@@ -135,6 +236,10 @@ int calc_pattern(PATTERN * pattern, int row, int cur_row, int col)
135236
pattern->distance[i] = nadir_distance;
136237
pattern->num_negatives++;
137238
}
239+
/*
240+
* If the angles are exactly equal, the cardinal direction search
241+
* results are the values set at the beginning of the for() loop.
242+
*/
138243
}
139244
else {
140245
pattern->distance[i] = search_distance;

raster/r.geomorphon/r.geomorphon.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ <h2>OPTIONS</h2>
6767
<DD>The difference (in degrees) between zenith and nadir line-of-sight which indicate flat direction. If higher threshold produce more flat maps. If resolution of the map is low (more than 1 km per cell) threshold should be very small (much smaller than 1 degree) because on such distance 1 degree of difference means several meters of high difference.</DD>
6868
<DT><b>dist</b></DT>
6969
<DD>>Flat distance. This is additional parameter defining the distance above which the threshold starts to decrease to avoid problems with pseudo-flat line-of-sights if real elevation difference appears on the distance where its value is higher (TO BE CORRECTED).</DD>
70+
<DT><b>comparison</b></DT>
71+
<DD>Comparison mode for zenith/nadir line-of-sight search. "anglev1" is
72+
the original r.geomorphon comparison mode. "anglev2" is an improved
73+
mode, which better handles angle thresholds and zenith/nadir angles
74+
that are exactly equal. "anglev2_distance" in addition to that takes
75+
the zenith/nadir distances into account when the angles are exactly
76+
equal.</DD>
7077
<DT><b>forms</b></DT>
7178
<DD>Returns geomorphic map with 10 most popular terrestrial forms. Legend for forms, its definition by the number of <em>+</em> and <em>-</em> and its idealized visualisation are presented at the image.
7279
<center>

0 commit comments

Comments
 (0)