123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- /****************************************************************************
- *
- * MODULE: r.terraflow
- *
- * COPYRIGHT (C) 2007 Laura Toma
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *****************************************************************************/
- #include <assert.h>
- #include <iostream>
- #include <grass/iostream/ami.h>
- #include "3scan.h"
- #include "water.h"
- #include "streamutils.h"
- #include "sortutils.h"
- #define WATER_DEBUG if(0)
- #define XXX if(0)
- char *
- labelElevType::printLabel(const labelElevType &p) {
- static char buf[8];
- sprintf(buf, CCLABEL_FMT, p.label);
- return buf;
- }
- /* smaller elevation, depth is smaller priority */
- int
- fillPriority::compare(const fillPriority &a, const fillPriority &b) {
- if(a.el < b.el) return -1;
- if(a.el > b.el) return 1;
-
- if(a.depth < b.depth) return -1;
- if(a.depth > b.depth) return 1;
-
- if(a.i < b.i) return -1;
- if(a.i > b.i) return 1;
-
- if(a.j < b.j) return -1;
- if(a.j > b.j) return 1;
-
- return 0;
- }
- int
- fillPriority::qscompare(const void *a, const void *b) {
- fillPriority *x = (fillPriority*)a;
- fillPriority *y = (fillPriority*)b;
- return compare(*x, *y);
- }
- int
- operator<(const fillPriority &a, const fillPriority &b) {
- if(a.el < b.el) return 1;
- if(a.el > b.el) return 0;
-
- if(a.depth < b.depth) return 1;
- if(a.depth > b.depth) return 0;
-
- if(a.i < b.i) return 1;
- if(a.i > b.i) return 0;
-
- if(a.j < b.j) return 1;
- if(a.j > b.j) return 0;
-
- return 0;
- }
- int
- operator<=(const fillPriority &a, const fillPriority &b) {
- if(a.el < b.el) return 1;
- if(a.el > b.el) return 0;
-
- if(a.depth < b.depth) return 1;
- if(a.depth > b.depth) return 0;
-
- if(a.i < b.i) return 1;
- if(a.i > b.i) return 0;
-
- if(a.j < b.j) return 1;
- if(a.j > b.j) return 0;
-
- return 1;
- }
- int
- operator>(const fillPriority &a, const fillPriority &b) {
- if(a.el < b.el) return 0;
- if(a.el > b.el) return 1;
-
- if(a.depth < b.depth) return 0;
- if(a.depth > b.depth) return 1;
-
- if(a.i < b.i) return 0;
- if(a.i > b.i) return 1;
-
- if(a.j < b.j) return 0;
- if(a.j > b.j) return 1;
-
- return 0;
- }
- int
- operator==(const fillPriority &a, const fillPriority &b) {
- return (a.el == b.el)
- && (a.depth == b.depth)
- && (a.i == b.i)
- && (a.j == b.j);
- }
- int
- operator!=(const fillPriority &a, const fillPriority &b) {
- return (a.el != b.el)
- || (a.depth != b.depth)
- || (a.i != b.i)
- || (a.j != b.j);
- }
- ostream&
- operator << (ostream& s, const fillPriority &p) {
- return s << "[fillPriority el=" << p.el
- << ", d=" << p.depth << ", "
- << p.i << ","
- << p.j << "]";
- }
- /* ********************************************************************** */
- ostream&
- operator << (ostream& s, const labelElevType &p) {
- return s << (ijBaseType)p << " "
- << "el=" << p.el << ", "
- << p.label;
- }
- /* ********************************************************************** */
- char *
- waterType::printLabel(const waterType &p) {
- static char buf[8];
- sprintf(buf, CCLABEL_FMT, p.label);
- return buf;
- }
- /* ********************************************************************** */
- char *
- boundaryType::print(const boundaryType &p) {
- static char buf[4];
- if(p.isValid()) {
- buf[0] = '1';
- } else {
- buf[0] = '0';
- }
- buf[1] = '\0';
-
- return buf;
- }
- ostream&
- operator << (ostream& s, const boundaryType &p) {
- return s << "[boundaryType "
- << (labelElevType)p << ", "
- << p.label2 << "]";
- }
- /* ********************************************************************** */
- /* ********************************************************************** */
- class waterWindower {
- private:
- AMI_STREAM<waterWindowType> *waterWindows;
- public:
- waterWindower(AMI_STREAM<waterWindowType> *str) :
- waterWindows(str) {};
- void processWindow(dimension_type i, dimension_type j,
- waterGridType &point,
- waterWindowBaseType *a,
- waterWindowBaseType *b,
- waterWindowBaseType *c);
- };
- void
- waterWindower::processWindow(dimension_type i, dimension_type j,
- waterGridType &point,
- waterWindowBaseType *a,
- waterWindowBaseType *b,
- waterWindowBaseType *c) {
- waterWindowType win = waterWindowType(i, j, point.getLabel(), a, b, c);
- AMI_err ae = waterWindows->write_item(win);
- assert(ae == AMI_ERROR_NO_ERROR);
- }
- void
- createWaterWindows(AMI_STREAM<waterGridType> *mergedWaterStr,
- const dimension_type nrows, const dimension_type ncols,
- AMI_STREAM<waterWindowType> *waterWindows) {
- if (stats)
- stats->comment("creating windows", opt->verbose);
- waterWindower winfo(waterWindows);
- waterWindowBaseType nodata;
- assert(mergedWaterStr->stream_len() > 0);
- if (stats)
- stats->comment("warning: using slower scan", opt->verbose);
- scan3(*mergedWaterStr, nrows, ncols, nodata, winfo);
- }
- /* ********************************************************************** */
- /* ********************************************************************** */
- /*
- * push labels to upslope neighbors
- */
- void
- generateWatersheds(AMI_STREAM<waterWindowType> **waterWindows,
- const dimension_type nrows, const dimension_type ncols,
- AMI_STREAM<labelElevType> *labeledWater,
- AMI_STREAM<boundaryType> *boundaryStr) {
- AMI_err ae;
- waterWindowType *winp, prevWin;
- assert(prevWin.getDepth() == DEPTH_INITIAL);
- EMPQueueAdaptive<fillPLabel, fillPriority> *pq;
- if (stats)
- stats->comment("generateWatersheds", opt->verbose);
- assert((*waterWindows)->stream_len() == ((off_t)nrows * ncols));
- WATER_DEBUG cout << "sort waterWindowsStream (by priority): ";
- sort(waterWindows, priorityCmpWaterWindowType());
-
- pq = new EMPQueueAdaptive<fillPLabel, fillPriority>();
-
- /* if(GETOPT("alwaysUseExternalPQ")) { */
- /* pq->makeExternal(); */
- /* } */
- /* if(GETOPT("useDebugPQ")) { */
- /* pq->makeExternalDebug(); */
- /* } */
-
- if (stats)
- stats->comment("starting generate watersheds main loop", opt->verbose);
-
- assert((*waterWindows)->stream_len() == ((off_t)nrows * ncols));
- /* not really in a grid, so row, col are not valid (but count correct) */
- for(dimension_type row=0; row<nrows; row++) {
- for(dimension_type col=0; col<ncols; col++) {
- ae = (*waterWindows)->read_item(&winp);
- assert(ae == AMI_ERROR_NO_ERROR);
-
- /* make sure it's sorted; prevWin default constructor should be ok */
- assert(winp->getPriority() > prevWin.getPriority());
- prevWin = *winp;
-
- XXX winp->sanityCheck();
- /* cout << "--- PROC: " << *winp << endl; */
- /* get my label(s) */
- fillPLabel plabel; /* from the PQ */
- fillPriority prio;
- cclabel_type label = winp->getLabel();
- #ifndef NDEBUG
- {
- /* check to see if things are coming out of the pq in
- order. just peek at the next one */
- fillPLabel tmp;
- XXX winp->sanityCheck();
- pq->min(tmp);
- /* XXX pq->verify(); */
- XXX winp->sanityCheck();
- assert(pq->is_empty() || winp->getPriority() <= tmp.getPriority());
- }
- #endif
- while(pq->min(plabel) &&
- ((prio=plabel.getPriority()) == winp->getPriority())) {
- /* XXX pq->verify(); */
- XXX winp->sanityCheck();
- pq->extract_min(plabel);
- /* XXX pq->verify(); */
- XXX winp->sanityCheck();
- if(label == LABEL_UNDEF) label = plabel.getLabel();
- }
- /* no label! assign a new one */
- if((label==LABEL_UNDEF) && (!is_nodata(winp->getElevation()))) {
- #ifndef NDEBUG
- {
- /* check to see if things are coming out of the pq in
- order. just peek at the next one */
- fillPLabel tmp;
- XXX winp->sanityCheck();
- pq->min(tmp);
- /* XXX pq->verify(); */
- XXX winp->sanityCheck();
- assert(pq->is_empty() || winp->getPriority() <= tmp.getPriority());
- }
- #endif
- if(IS_BOUNDARY(winp->i,winp->j,nrows, ncols)) { /* edge of grid */
- assert(!is_nodata(winp->getElevation()));
- label = LABEL_BOUNDARY; /* reserved for watersheds draining
- out of grid */
- } else {
- label = labelFactory::getNewLabel();
- }
- }
- winp->setLabel(label);
-
- /* push label to 'upslope' neighbors. let's assume that the
- * edges cause no problems, since they have no directions... */
- if(label != LABEL_UNDEF) {
- int k=0;
- for(int i=-1; i<2; i++) {
- for(int j=-1; j<2; j++) {
- assert(k==linear(i,j));
- if(!is_nodata(winp->getElevation(k))
- && winp->drainsFrom(i,j)) { /* points to me */
- assert(i || j);
- prio = fillPriority(winp->getElevation(k),
- winp->getDepth(k),
- winp->i + i, winp->j + j);
- #ifndef NDEBUG
- /* dont insert if precedes us */
- if(winp->getPriority() < prio) {
- fillPLabel plabel(prio, label);
- pq->insert(plabel);
- } else { /* trying to send a label backward */
- cerr << "WARNING: time travel attempted" << endl;
- cerr << "inst priority is " << prio << endl;
- cerr << "source is " << *winp << "; prio="
- << winp->getPriority() << endl;
- assert(0);
- }
- #else
- fillPLabel plabel(prio, label);
- pq->insert(plabel);
- #endif
- }
- k++;
- }
- }
- }
-
- /* write myself to output */
- ae = labeledWater->write_item(winp->getCenter());
- assert(ae == AMI_ERROR_NO_ERROR);
- }
- }
-
- assert(pq->is_empty());
- delete pq;
-
- if (stats)
- stats->comment("done with generate watersheds", opt->verbose);
- }
- /* ********************************************************************** */
- class boundaryDetector {
- private:
- const dimension_type nrows, ncols;
- AMI_STREAM<boundaryType> *boundaryStr;
- void processPair(labelElevType &pt,
- dimension_type i, dimension_type j, labelElevType &n);
- public:
- boundaryDetector(AMI_STREAM<boundaryType> *str,
- const dimension_type gnrows, const dimension_type gncols)
- : nrows(gnrows), ncols(gncols), boundaryStr(str) {};
-
- void processWindow(dimension_type i, dimension_type j,
- labelElevType &point,
- labelElevType *a,
- labelElevType *b,
- labelElevType *c);
- };
- template<class T>
- T mymax(T a, T b) {
- return (a>b?a:b);
- }
- void
- boundaryDetector::processPair(labelElevType &pt,
- dimension_type i, dimension_type j,
- labelElevType &n) {
- if(n.getLabel() != LABEL_UNDEF && pt.getLabel() != n.getLabel()) {
- boundaryType bt(pt, mymax(pt.getElevation(), n.getElevation()),
- n.getLabel());
- AMI_err ae = boundaryStr->write_item(bt);
- assert(ae == AMI_ERROR_NO_ERROR);
- } else if(IS_BOUNDARY(i,j,nrows, ncols) && pt.getLabel() != LABEL_BOUNDARY) {
- /* this part makes sure that regions on the grid edge
- are considered 'boundary' */
- boundaryType bt(pt, LABEL_BOUNDARY);
- AMI_err ae = boundaryStr->write_item(bt);
- assert(ae == AMI_ERROR_NO_ERROR);
- }
- }
- void
- boundaryDetector::processWindow(dimension_type i, dimension_type j,
- labelElevType &point,
- labelElevType *a,
- labelElevType *b,
- labelElevType *c) {
- if(point.getLabel() == LABEL_UNDEF) return;
- /* NODATA_FIX */
- /* don't use the nodata as the boundary. */
- /* if(point.getLabel() == LABEL_NODATA) return; */
- assert(point.getLabel() != LABEL_NODATA);
-
- for(int k=0; k<3; k++) {
- processPair(point, i, j, a[k]);
- processPair(point, i, j, b[k]);
- processPair(point, i, j, c[k]);
- }
- /* processPair(point, i, j, b[0]); */
- }
- /* ********************************************************************** */
- void
- findBoundaries(AMI_STREAM<labelElevType> *labeledWater,
- const dimension_type nrows, const dimension_type ncols,
- AMI_STREAM<boundaryType> *boundaryStr) {
- if (stats)
- stats->comment("creating windows", opt->verbose);
- boundaryDetector det(boundaryStr, nrows, ncols);
- /* cerr << "WARNING: using scan3 instead of scan2" << endl; */
- scan3(*labeledWater, nrows, ncols, labelElevType(), det);
-
- /* NODATA_FIX
- assert(LABEL_BOUNDARY < LABEL_NODATA);
- boundaryType bt(-1, -1, ELEVATION_MIN, LABEL_BOUNDARY, LABEL_NODATA);
- AMI_err ae = boundaryStr->write_item(bt);
- assert(ae == AMI_ERROR_NO_ERROR);
- */
- }
-
- /* ********************************************************************** */
- int
- compressedWaterWindowBaseType::computeDelta(waterWindowBaseType *center,
- int index,
- waterWindowBaseType *p) const{
- if(center->el != p->el) {
- assert(p->depth == 1 || center->el > p->el);
- return 0;
- }
- if(index > 7) return 0; /* we store our depth elsewhere */
-
- int d = p->depth - center->depth + 1;
- assert(d >= 0);
- #ifndef NDEBUG
- if(d>2) {
- cerr << "whoops - assertion failure" << endl;
- cerr << "center = " << *center << endl;
- cerr << "p = " << *p << endl;
- cerr << "this = " << *this << endl;
- }
- #endif
- assert(d <= 2);
- return d<<(2*index);
- }
- compressedWaterWindowBaseType::compressedWaterWindowBaseType(dimension_type gi,
- dimension_type gj,
- waterWindowBaseType *a,
- waterWindowBaseType *b,
- waterWindowBaseType *c)
- : ijBaseType(gi, gj) {
-
- for(int i=0; i<3; i++) {
- el[i] = a[i].el;
- el[i+3] = b[i].el;
- el[i+6] = c[i].el;
- }
-
- for(int i=0; i<3; i++) {
- const direction_type mask_a[] = {2, 4, 8};
- const direction_type mask_b[] = {1, 0, 16};
- const direction_type mask_c[] = {128, 64, 32};
- points.setBit(i, a[i].dir & mask_a[i]);
- points.setBit(norm(i+3), b[i].dir & mask_b[i]);
- points.setBit(i+5, c[i].dir & mask_c[i]);
- }
- dir = b[1].dir;
- depth = b[1].depth;
- depth_delta = 0;
-
- /* nodata is not processed. */
- if(is_nodata(b[1].el)) {
- return;
- }
-
- for(int i=0; i<3; i++) {
- depth_delta |= computeDelta(b+1, norm(-1,i-1), a+i);
- depth_delta |= computeDelta(b+1, norm(0,i-1), b+i);
- depth_delta |= computeDelta(b+1, norm(1,i-1), c+i);
- }
- }
- fillPriority
- compressedWaterWindowBaseType::getPriority() const {
- return fillPriority(getElevation(), getDepth(), i, j);
- }
- bfs_depth_type
- compressedWaterWindowBaseType::getDepth(int k) const {
- if(getElevation() != getElevation(k)) return DEPTH_INITIAL;
- return depth + ((depth_delta >> (norm(k)*2)) & 0x3) - 1;
- }
- void
- compressedWaterWindowBaseType::sanityCheck() {
- assert(i >= -1);
- assert(j >= -1);
- assert(depth >= DEPTH_INITIAL);
- }
-
- ostream&
- operator<<(ostream& s, const compressedWaterWindowBaseType &p) {
- return s << "[compressedWaterWindowBaseType "
- << p.i << "," << p.j
- << " " << directionSymbol(p.getDirection())
- << " e=" << p.getElevation()
- << " d =" << p.getDepth() << "]";
- }
- /* ********************************************************************** */
- labelElevType
- compressedWaterWindowType::getCenter() const {
- return labelElevType(i, j, getElevation(), label);
- }
- void
- compressedWaterWindowType::sanityCheck() {
- assert(label >= LABEL_UNDEF);
- compressedWaterWindowBaseType::sanityCheck();
- }
- ostream&
- operator<<(ostream& s, const compressedWaterWindowType &p) {
- return s << "[compressedWaterWindowType "
- << p.i << "," << p.j
- << " " << directionSymbol(p.getDirection())
- << " e=" << p.getElevation()
- << " d=" << p.getDepth()
- << " l=" << p.label;
- }
|