1
/****************************************************************************
5
* COPYRIGHT (C) 2007 Laura Toma
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
*****************************************************************************/
24
#include <grass/iostream/ami.h>
29
#include "streamutils.h"
30
#include "sortutils.h"
33
#define WATER_DEBUG if(0)
38
labelElevType::printLabel(const labelElevType &p) {
40
sprintf(buf, CCLABEL_FMT, p.label);
49
/* smaller elevation, depth is smaller priority */
51
fillPriority::compare(const fillPriority &a, const fillPriority &b) {
52
if(a.el < b.el) return -1;
53
if(a.el > b.el) return 1;
55
if(a.depth < b.depth) return -1;
56
if(a.depth > b.depth) return 1;
58
if(a.i < b.i) return -1;
59
if(a.i > b.i) return 1;
61
if(a.j < b.j) return -1;
62
if(a.j > b.j) return 1;
68
fillPriority::qscompare(const void *a, const void *b) {
69
fillPriority *x = (fillPriority*)a;
70
fillPriority *y = (fillPriority*)b;
71
return compare(*x, *y);
75
operator<(const fillPriority &a, const fillPriority &b) {
76
if(a.el < b.el) return 1;
77
if(a.el > b.el) return 0;
79
if(a.depth < b.depth) return 1;
80
if(a.depth > b.depth) return 0;
82
if(a.i < b.i) return 1;
83
if(a.i > b.i) return 0;
85
if(a.j < b.j) return 1;
86
if(a.j > b.j) return 0;
92
operator<=(const fillPriority &a, const fillPriority &b) {
93
if(a.el < b.el) return 1;
94
if(a.el > b.el) return 0;
96
if(a.depth < b.depth) return 1;
97
if(a.depth > b.depth) return 0;
99
if(a.i < b.i) return 1;
100
if(a.i > b.i) return 0;
102
if(a.j < b.j) return 1;
103
if(a.j > b.j) return 0;
109
operator>(const fillPriority &a, const fillPriority &b) {
110
if(a.el < b.el) return 0;
111
if(a.el > b.el) return 1;
113
if(a.depth < b.depth) return 0;
114
if(a.depth > b.depth) return 1;
116
if(a.i < b.i) return 0;
117
if(a.i > b.i) return 1;
119
if(a.j < b.j) return 0;
120
if(a.j > b.j) return 1;
127
operator==(const fillPriority &a, const fillPriority &b) {
128
return (a.el == b.el)
129
&& (a.depth == b.depth)
136
operator!=(const fillPriority &a, const fillPriority &b) {
137
return (a.el != b.el)
138
|| (a.depth != b.depth)
145
operator << (ostream& s, const fillPriority &p) {
146
return s << "[fillPriority el=" << p.el
147
<< ", d=" << p.depth << ", "
154
/* ********************************************************************** */
158
operator << (ostream& s, const labelElevType &p) {
159
return s << (ijBaseType)p << " "
160
<< "el=" << p.el << ", "
164
/* ********************************************************************** */
167
waterType::printLabel(const waterType &p) {
169
sprintf(buf, CCLABEL_FMT, p.label);
173
/* ********************************************************************** */
177
boundaryType::print(const boundaryType &p) {
191
operator << (ostream& s, const boundaryType &p) {
192
return s << "[boundaryType "
193
<< (labelElevType)p << ", "
199
/* ********************************************************************** */
200
/* ********************************************************************** */
202
class waterWindower {
204
AMI_STREAM<waterWindowType> *waterWindows;
206
waterWindower(AMI_STREAM<waterWindowType> *str) :
207
waterWindows(str) {};
208
void processWindow(dimension_type i, dimension_type j,
209
waterGridType &point,
210
waterWindowBaseType *a,
211
waterWindowBaseType *b,
212
waterWindowBaseType *c);
216
waterWindower::processWindow(dimension_type i, dimension_type j,
217
waterGridType &point,
218
waterWindowBaseType *a,
219
waterWindowBaseType *b,
220
waterWindowBaseType *c) {
221
waterWindowType win = waterWindowType(i, j, point.getLabel(), a, b, c);
222
AMI_err ae = waterWindows->write_item(win);
223
assert(ae == AMI_ERROR_NO_ERROR);
227
createWaterWindows(AMI_STREAM<waterGridType> *mergedWaterStr,
228
const dimension_type nrows, const dimension_type ncols,
229
AMI_STREAM<waterWindowType> *waterWindows) {
230
stats->comment("creating windows", opt->verbose);
231
waterWindower winfo(waterWindows);
232
waterWindowBaseType nodata;
233
assert(mergedWaterStr->stream_len() > 0);
234
stats->comment("warning: using slower scan", opt->verbose);
235
scan3(*mergedWaterStr, nrows, ncols, nodata, winfo);
239
/* ********************************************************************** */
240
/* ********************************************************************** */
244
* push labels to upslope neighbors
247
generateWatersheds(AMI_STREAM<waterWindowType> **waterWindows,
248
const dimension_type nrows, const dimension_type ncols,
249
AMI_STREAM<labelElevType> *labeledWater,
250
AMI_STREAM<boundaryType> *boundaryStr) {
252
waterWindowType *winp, prevWin;
253
assert(prevWin.getDepth() == DEPTH_INITIAL);
254
EMPQueueAdaptive<fillPLabel, fillPriority> *pq;
256
stats->comment("generateWatersheds", opt->verbose);
258
assert((*waterWindows)->stream_len() == (nrows * ncols));
260
WATER_DEBUG cout << "sort waterWindowsStream (by priority): ";
261
sort(waterWindows, priorityCmpWaterWindowType());
263
pq = new EMPQueueAdaptive<fillPLabel, fillPriority>();
265
/* if(GETOPT("alwaysUseExternalPQ")) { */
266
/* pq->makeExternal(); */
268
/* if(GETOPT("useDebugPQ")) { */
269
/* pq->makeExternalDebug(); */
272
stats->comment("starting generate watersheds main loop", opt->verbose);
274
assert((*waterWindows)->stream_len() == (nrows * ncols));
275
/* not really in a grid, so row, col are not valid (but count correct) */
276
for(dimension_type row=0; row<nrows; row++) {
277
for(dimension_type col=0; col<ncols; col++) {
278
ae = (*waterWindows)->read_item(&winp);
279
assert(ae == AMI_ERROR_NO_ERROR);
281
/* make sure it's sorted; prevWin default constructor should be ok */
282
assert(winp->getPriority() > prevWin.getPriority());
285
XXX winp->sanityCheck();
286
/* cout << "--- PROC: " << *winp << endl; */
287
/* get my label(s) */
288
fillPLabel plabel; /* from the PQ */
290
cclabel_type label = winp->getLabel();
293
/* check to see if things are coming out of the pq in
294
order. just peek at the next one */
296
XXX winp->sanityCheck();
298
/* XXX pq->verify(); */
299
XXX winp->sanityCheck();
300
assert(pq->is_empty() || winp->getPriority() <= tmp.getPriority());
303
while(pq->min(plabel) &&
304
((prio=plabel.getPriority()) == winp->getPriority())) {
305
/* XXX pq->verify(); */
306
XXX winp->sanityCheck();
307
pq->extract_min(plabel);
308
/* XXX pq->verify(); */
309
XXX winp->sanityCheck();
310
if(label == LABEL_UNDEF) label = plabel.getLabel();
312
/* no label! assign a new one */
313
if((label==LABEL_UNDEF) && (!is_nodata(winp->getElevation()))) {
316
/* check to see if things are coming out of the pq in
317
order. just peek at the next one */
319
XXX winp->sanityCheck();
321
/* XXX pq->verify(); */
322
XXX winp->sanityCheck();
323
assert(pq->is_empty() || winp->getPriority() <= tmp.getPriority());
326
if(IS_BOUNDARY(winp->i,winp->j,nrows, ncols)) { /* edge of grid */
327
assert(!is_nodata(winp->getElevation()));
328
label = LABEL_BOUNDARY; /* reserved for watersheds draining
331
label = labelFactory::getNewLabel();
334
winp->setLabel(label);
336
/* push label to 'upslope' neighbors. let's assume that the
337
* edges cause no problems, since they have no directions... */
338
if(label != LABEL_UNDEF) {
340
for(int i=-1; i<2; i++) {
341
for(int j=-1; j<2; j++) {
342
assert(k==linear(i,j));
343
if(!is_nodata(winp->getElevation(k))
344
&& winp->drainsFrom(i,j)) { /* points to me */
346
prio = fillPriority(winp->getElevation(k),
348
winp->i + i, winp->j + j);
350
/* dont insert if preceeds us */
351
if(winp->getPriority() < prio) {
352
fillPLabel plabel(prio, label);
354
} else { /* trying to send a label backward */
355
cerr << "WARNING: time travel attempted" << endl;
356
cerr << "inst priority is " << prio << endl;
357
cerr << "source is " << *winp << "; prio="
358
<< winp->getPriority() << endl;
362
fillPLabel plabel(prio, label);
371
/* write myself to output */
372
ae = labeledWater->write_item(winp->getCenter());
373
assert(ae == AMI_ERROR_NO_ERROR);
377
assert(pq->is_empty());
380
stats->comment("done with generate watersheds", opt->verbose);
385
/* ********************************************************************** */
388
class boundaryDetector {
390
const dimension_type nrows, ncols;
391
AMI_STREAM<boundaryType> *boundaryStr;
392
void processPair(labelElevType &pt,
393
dimension_type i, dimension_type j, labelElevType &n);
395
boundaryDetector(AMI_STREAM<boundaryType> *str,
396
const dimension_type gnrows, const dimension_type gncols)
397
: nrows(gnrows), ncols(gncols), boundaryStr(str) {};
399
void processWindow(dimension_type i, dimension_type j,
400
labelElevType &point,
412
boundaryDetector::processPair(labelElevType &pt,
413
dimension_type i, dimension_type j,
415
if(n.getLabel() != LABEL_UNDEF && pt.getLabel() != n.getLabel()) {
416
boundaryType bt(pt, mymax(pt.getElevation(), n.getElevation()),
418
AMI_err ae = boundaryStr->write_item(bt);
419
assert(ae == AMI_ERROR_NO_ERROR);
420
} else if(IS_BOUNDARY(i,j,nrows, ncols) && pt.getLabel() != LABEL_BOUNDARY) {
421
/* this part makes sure that regions on the grid edge
422
are considered 'boundary' */
423
boundaryType bt(pt, LABEL_BOUNDARY);
424
AMI_err ae = boundaryStr->write_item(bt);
425
assert(ae == AMI_ERROR_NO_ERROR);
430
boundaryDetector::processWindow(dimension_type i, dimension_type j,
431
labelElevType &point,
435
if(point.getLabel() == LABEL_UNDEF) return;
437
/* don't use the nodata as the boundary. */
438
/* if(point.getLabel() == LABEL_NODATA) return; */
439
assert(point.getLabel() != LABEL_NODATA);
441
for(int k=0; k<3; k++) {
442
processPair(point, i, j, a[k]);
443
processPair(point, i, j, b[k]);
444
processPair(point, i, j, c[k]);
446
/* processPair(point, i, j, b[0]); */
450
/* ********************************************************************** */
453
findBoundaries(AMI_STREAM<labelElevType> *labeledWater,
454
const dimension_type nrows, const dimension_type ncols,
455
AMI_STREAM<boundaryType> *boundaryStr) {
456
stats->comment("creating windows", opt->verbose);
457
boundaryDetector det(boundaryStr, nrows, ncols);
458
/* cerr << "WARNING: using scan3 instead of scan2" << endl; */
459
scan3(*labeledWater, nrows, ncols, labelElevType(), det);
462
assert(LABEL_BOUNDARY < LABEL_NODATA);
463
boundaryType bt(-1, -1, ELEVATION_MIN, LABEL_BOUNDARY, LABEL_NODATA);
464
AMI_err ae = boundaryStr->write_item(bt);
465
assert(ae == AMI_ERROR_NO_ERROR);
470
/* ********************************************************************** */
473
compressedWaterWindowBaseType::computeDelta(waterWindowBaseType *center,
475
waterWindowBaseType *p) const{
476
if(center->el != p->el) {
477
assert(p->depth == 1 || center->el > p->el);
480
if(index > 7) return 0; /* we store our depth elsewhere */
482
int d = p->depth - center->depth + 1;
486
cerr << "whoops - assertion failure" << endl;
487
cerr << "center = " << *center << endl;
488
cerr << "p = " << *p << endl;
489
cerr << "this = " << *this << endl;
496
compressedWaterWindowBaseType::compressedWaterWindowBaseType(dimension_type gi,
498
waterWindowBaseType *a,
499
waterWindowBaseType *b,
500
waterWindowBaseType *c)
501
: ijBaseType(gi, gj) {
503
for(int i=0; i<3; i++) {
509
for(int i=0; i<3; i++) {
510
const direction_type mask_a[] = {2, 4, 8};
511
const direction_type mask_b[] = {1, 0, 16};
512
const direction_type mask_c[] = {128, 64, 32};
513
points.setBit(i, a[i].dir & mask_a[i]);
514
points.setBit(norm(i+3), b[i].dir & mask_b[i]);
515
points.setBit(i+5, c[i].dir & mask_c[i]);
521
/* nodata is not processed. */
522
if(is_nodata(b[1].el)) {
526
for(int i=0; i<3; i++) {
527
depth_delta |= computeDelta(b+1, norm(-1,i-1), a+i);
528
depth_delta |= computeDelta(b+1, norm(0,i-1), b+i);
529
depth_delta |= computeDelta(b+1, norm(1,i-1), c+i);
534
compressedWaterWindowBaseType::getPriority() const {
535
return fillPriority(getElevation(), getDepth(), i, j);
540
compressedWaterWindowBaseType::getDepth(int k) const {
541
if(getElevation() != getElevation(k)) return DEPTH_INITIAL;
542
return depth + ((depth_delta >> (norm(k)*2)) & 0x3) - 1;
547
compressedWaterWindowBaseType::sanityCheck() {
550
assert(depth >= DEPTH_INITIAL);
555
operator<<(ostream& s, const compressedWaterWindowBaseType &p) {
556
return s << "[compressedWaterWindowBaseType "
558
<< " " << directionSymbol(p.getDirection())
559
<< " e=" << p.getElevation()
560
<< " d =" << p.getDepth() << "]";
563
/* ********************************************************************** */
566
compressedWaterWindowType::getCenter() const {
567
return labelElevType(i, j, getElevation(), label);
571
compressedWaterWindowType::sanityCheck() {
572
assert(label >= LABEL_UNDEF);
573
compressedWaterWindowBaseType::sanityCheck();
578
operator<<(ostream& s, const compressedWaterWindowType &p) {
579
return s << "[compressedWaterWindowType "
581
<< " " << directionSymbol(p.getDirection())
582
<< " e=" << p.getElevation()
583
<< " d=" << p.getDepth()