Skip to content

Commit 00ad768

Browse files
committed
Fix highest peaks being filtered from bathymetry.
We were assuming that prominence=elevation for the highest peak in a tree. When the peak's elevation is negative, as in bathymetry, this causes the prominence of the highest peak to be negative, which means the highest peak will always be filtered out.
1 parent 21fbbe0 commit 00ad768

8 files changed

+68
-15
lines changed

code/compute_parents.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ int main(int argc, char **argv) {
112112

113113
// Build island tree to get prominence values
114114
IslandTree *islandTree = new IslandTree(*divideTree);
115-
islandTree->build();
115+
islandTree->build(false); // TODO: Flag for bathymetry?
116116

117117
// Build line parent tree
118118
LineTree *lineTree = new LineTree(*divideTree);

code/island_tree.cpp

+27-5
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ IslandTree::IslandTree(const DivideTree &divideTree) :
3636
mDivideTree(divideTree) {
3737
}
3838

39-
void IslandTree::build() {
39+
void IslandTree::build(bool isBathymetry) {
4040
// Initialize topology
4141
mNodes.resize(mDivideTree.nodes().size());
4242
int index = 1; // Peaks are 1-indexed; put in a dummy node 0
@@ -54,7 +54,7 @@ void IslandTree::build() {
5454
uninvertPeaks();
5555
uninvertSaddles();
5656

57-
computeProminences();
57+
computeProminences(isBathymetry);
5858
}
5959

6060
// Sort peaks so that parent is always higher elevation.
@@ -152,7 +152,7 @@ void IslandTree::uninvertSaddle(int nodeId) {
152152
}
153153
}
154154

155-
void IslandTree::computeProminences() {
155+
void IslandTree::computeProminences(bool isBathymetry) {
156156
for (int i = 1; i < (int) mNodes.size(); ++i) {
157157
Elevation elev = getPeak(i).elevation;
158158
int childNodeId = i;
@@ -177,8 +177,17 @@ void IslandTree::computeProminences() {
177177
}
178178

179179
if (parentNodeId == Node::Null) {
180-
// This is the highest point in the tree
181-
mNodes[i].prominence = elev;
180+
// This is the highest point in the tree. In "normal" circumstances where
181+
// all the elevations are nonnegative, we can safely define the prominence as equal
182+
// to the elevation. But for bathymetry the prominence is not so clear. We
183+
// use the elevation of the minimum saddle as "sea level". This should agree
184+
// with the traditional definition (prominence = elevation) for regular terrain,
185+
// and for bathymetry, it will guarantee that the highest point has the
186+
// highest prominence, which matches intuition.
187+
//
188+
// Other definitions of the prominence of the highest point are possible,
189+
// for example, height above the minimum sample value.
190+
mNodes[i].prominence = elev - getSeaLevelValue(isBathymetry);
182191
} else {
183192
// Prominence = peak elevation - key col elevation
184193
int saddlePeakId = mNodes[childNodeId].saddlePeakId;
@@ -189,6 +198,19 @@ void IslandTree::computeProminences() {
189198
}
190199
}
191200

201+
Elevation IslandTree::getSeaLevelValue(bool isBathymetry) {
202+
// If this is bathymetry, find the minimum saddle elevation in the tree.
203+
if (isBathymetry && !mNodes.empty()) {
204+
Elevation elevation = std::numeric_limits<Elevation>::max();
205+
for (auto const &saddle : mDivideTree.saddles()) {
206+
elevation = std::min(elevation, saddle.elevation);
207+
}
208+
return elevation;
209+
}
210+
211+
return 0;
212+
}
213+
192214
const Peak &IslandTree::getPeak(int peakId) const {
193215
return mDivideTree.peaks()[peakId - 1]; // 1-indexed
194216
}

code/island_tree.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class IslandTree {
4949

5050
explicit IslandTree(const DivideTree &divideTree);
5151

52-
void build();
52+
// If isBathymetry is true, don't assume sea level = 0.
53+
void build(bool isBathymetry);
5354

5455
bool writeToFile(const std::string &filename) const;
5556

@@ -66,11 +67,15 @@ class IslandTree {
6667
// Sort peaks by increasing divide tree saddle elevation
6768
void uninvertSaddles();
6869

69-
void computeProminences();
70+
void computeProminences(bool isBathymetry);
7071

7172
void uninvertPeak(int nodeId);
7273
void uninvertSaddle(int nodeId);
7374

75+
// Return the elevation of "sea level", i.e. the elevation used as the
76+
// base of the highest peak in the tree.
77+
Elevation getSeaLevelValue(bool isBathymetry);
78+
7479
// Indices start at 1; use these helper functions to deal with offset.
7580
const Peak &getPeak(int peakId) const;
7681
const Saddle &getSaddle(int saddleId) const;

code/merge_divide_trees.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ static void usage() {
4848
printf(" in same units as divide tree, default = 100\n");
4949
printf(" -s scale_factor Scale output elevations by this (e.g. meters to feet), default = 1\n");
5050
printf(" -t num_threads Number of threads to use, default = 1\n");
51+
printf(" -b Input data is bathymetric (do not use sea level)\n");
5152
exit(1);
5253
}
5354

@@ -83,6 +84,7 @@ int main(int argc, char **argv) {
8384
Elevation minProminence = 100;
8485
bool finalize = false;
8586
bool flipElevations = false;
87+
bool bathymetry = false;
8688
int numThreads = 1;
8789
float elevationScale = 1.0f;
8890

@@ -96,12 +98,16 @@ int main(int argc, char **argv) {
9698
{"v", required_argument, nullptr, 0},
9799
{nullptr, 0, 0, 0},
98100
};
99-
while ((ch = getopt_long(argc, argv, "afm:s:t:", long_options, nullptr)) != -1) {
101+
while ((ch = getopt_long(argc, argv, "abfm:s:t:", long_options, nullptr)) != -1) {
100102
switch (ch) {
101103
case 'a':
102104
flipElevations = true;
103105
break;
104-
106+
107+
case 'b':
108+
bathymetry = true;
109+
break;
110+
105111
case 'f':
106112
finalize = true;
107113
break;
@@ -183,7 +189,7 @@ int main(int argc, char **argv) {
183189
VLOG(1) << "Building prominence island tree";
184190

185191
IslandTree *unprunedIslandTree = new IslandTree(*divideTree);
186-
unprunedIslandTree->build();
192+
unprunedIslandTree->build(bathymetry);
187193

188194
if (finalize) {
189195
divideTree->deleteRunoffs(); // Does not affect island tree
@@ -211,7 +217,7 @@ int main(int argc, char **argv) {
211217

212218
// Build new island tree on pruned divide tree to get final prominence values
213219
IslandTree *prunedIslandTree = new IslandTree(*divideTree);
214-
prunedIslandTree->build();
220+
prunedIslandTree->build(bathymetry);
215221

216222
// Write final prominence value table
217223
string filename = outputFilename + ".txt";

code/prominence.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ static void usage() {
6262
printf(" -t num_threads Number of threads, default = 1\n");
6363
printf(" -z UTM zone (if input data is in UTM)\n");
6464
printf(" -a Compute anti-prominence instead of prominence\n");
65+
printf(" -b Input DEM is bathymetric (do not use sea level)\n");
6566
printf(" --kml Generate KML output of divide tree\n");
6667
exit(1);
6768
}
@@ -80,6 +81,7 @@ int main(int argc, char **argv) {
8081
int ch;
8182
string str;
8283
bool antiprominence = false;
84+
bool bathymetry = false;
8385
int writeKml = false;
8486
int utmZone = NO_UTM_ZONE;
8587

@@ -91,12 +93,16 @@ int main(int argc, char **argv) {
9193
{"kml", no_argument, &writeKml, 1},
9294
{nullptr, 0, 0, 0},
9395
};
94-
while ((ch = getopt_long(argc, argv, "af:i:k:m:o:t:z:", long_options, nullptr)) != -1) {
96+
while ((ch = getopt_long(argc, argv, "abf:i:k:m:o:t:z:", long_options, nullptr)) != -1) {
9597
switch (ch) {
9698
case 'a':
9799
antiprominence = true;
98100
break;
99-
101+
102+
case 'b':
103+
bathymetry = true;
104+
break;
105+
100106
case 'f': {
101107
auto format = FileFormat::fromName(optarg);
102108
if (format == nullptr) {
@@ -183,6 +189,7 @@ int main(int argc, char **argv) {
183189

184190
// Don't write out unpruned divide tree--it's too large and slow
185191
ProminenceOptions options = {output_directory, minProminence, false, antiprominence,
192+
bathymetry,
186193
static_cast<bool>(writeKml)};
187194

188195
// Caching doesn't do anything for our calculation and the tiles are huge

code/prominence_task.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ bool ProminenceTask::run(double lat, double lng, const CoordinateSystem &coordin
8686
VLOG(1) << "Pruning divide tree to " << mOptions.minProminence << " prominence";
8787

8888
IslandTree islandTree(*divideTree);
89-
islandTree.build();
89+
islandTree.build(mOptions.bathymetry);
9090

9191
divideTree->prune(mOptions.minProminence, islandTree);
9292

code/prominence_task.h

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ struct ProminenceOptions {
4343
// or anti-prominence, which is the "prominence" of low points.
4444
bool antiprominence;
4545

46+
// Data is bathymetry; do not assume sea level = 0 for prom calculations.
47+
bool bathymetry;
48+
4649
// Write KML of pruned divide tree?
4750
bool writeKml;
4851
};

scripts/run_prominence.py

+10
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ def main():
219219
help='Size of reprojected tiles to generate')
220220
parser.add_argument('--samples_per_tile', type=int, default=10000,
221221
help='Number of samples per edge of a reprojected tile')
222+
parser.add_argument('--bathymetry', action=argparse.BooleanOptionalAction,
223+
help='Is the input DEM bathymetry?')
222224
args = parser.parse_args()
223225

224226
gdal.UseExceptions()
@@ -304,9 +306,13 @@ def main():
304306
# Run prominence and merge_divide_trees
305307
print("Running prominence")
306308
prominence_binary = os.path.join(args.binary_dir, "prominence")
309+
extra_args = ""
310+
if args.bathymetry:
311+
extra_args += " -b"
307312
prom_command = f"{prominence_binary} --v=1 -f CUSTOM-{args.degrees_per_tile}-{args.samples_per_tile}" + \
308313
f" -i {args.tile_dir} -o {args.output_dir}" + \
309314
f" -t {args.threads} -m {args.min_prominence}" + \
315+
extra_args + \
310316
f" -- {ymin} {ymax} {xmin} {xmax}"
311317
run_command(prom_command)
312318

@@ -317,8 +323,12 @@ def main():
317323
units_multiplier = 1
318324
if args.output_units == "feet":
319325
units_multiplier = 3.28084
326+
extra_args = ""
327+
if args.bathymetry:
328+
extra_args += " -b"
320329
merge_command = f"{merge_binary} --v=1 -f" + \
321330
f" -t {args.threads} -m {args.min_prominence} -s {units_multiplier}" + \
331+
extra_args + \
322332
f" {merge_output} {merge_inputs}"
323333
run_command(merge_command)
324334

0 commit comments

Comments
 (0)