plateau.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /****************************************************************************
  2. *
  3. * MODULE: r.terraflow
  4. *
  5. * COPYRIGHT (C) 2007 Laura Toma
  6. *
  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.
  11. *
  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.
  16. *
  17. *****************************************************************************/
  18. #include <grass/iostream/ami.h> /* for queue */
  19. #include "plateau.h"
  20. #include "common.h"
  21. #include "streamutils.h"
  22. #include "sortutils.h"
  23. #include "types.h"
  24. #include "ccforest.h"
  25. #include "3scan.h"
  26. #define PLAT_DEBUG if(0)
  27. /* ********************************************************************** */
  28. /* ********************************************************************** */
  29. /* this is a function object to create the direction and plateau
  30. * streams given a window */
  31. class detectPlateaus {
  32. private:
  33. AMI_STREAM<direction_type> *dirStream;
  34. AMI_STREAM<plateauType> *platStream;
  35. AMI_STREAM<ElevationWindow > *winStream;
  36. queue<direction_type> *dirQueue;
  37. queue<plateauType> *platQueue;
  38. ccforest<cclabel_type> colTree;
  39. plateauType *getPlateauForward(dimension_type i, dimension_type j,
  40. dimension_type nr, dimension_type n);
  41. direction_type *getDirectionForward(dimension_type i, dimension_type j,
  42. dimension_type nr, dimension_type n);
  43. const dimension_type nrows;
  44. const dimension_type ncols;
  45. const elevation_type nodata_value;
  46. public:
  47. detectPlateaus(const dimension_type gnrows,const dimension_type gncols,
  48. const elevation_type gnodata_value,
  49. AMI_STREAM<direction_type>* dirstr,
  50. AMI_STREAM<ElevationWindow > *winstr);
  51. ~detectPlateaus();
  52. void processWindow(dimension_type row, dimension_type col,
  53. elevation_type *a,
  54. elevation_type *b,
  55. elevation_type *c);
  56. void generatePlateaus(AMI_STREAM<elevation_type> &elstr);
  57. void removeDuplicates();
  58. void relabelPlateaus();
  59. void generateStats(AMI_STREAM<plateauStats> *statStr);
  60. AMI_STREAM<plateauType> *getPlateaus() { return platStream; }
  61. };
  62. /* ********************************************************************** */
  63. detectPlateaus::detectPlateaus(const dimension_type gnrows,
  64. const dimension_type gncols,
  65. const elevation_type gnodata_value,
  66. AMI_STREAM<direction_type>* gdirstr,
  67. AMI_STREAM<ElevationWindow > *gwinstr):
  68. dirStream(gdirstr), winStream(gwinstr),
  69. nrows(gnrows), ncols(gncols), nodata_value(gnodata_value) {
  70. platStream = new AMI_STREAM<plateauType>();
  71. }
  72. detectPlateaus::~detectPlateaus() {
  73. /*delete platStream; */ /* user must delete */
  74. }
  75. /* ********************************************************************** */
  76. /* return a pointer to three plateauType structures, starting at
  77. location i,j. caller should check valid field in returned
  78. structs. */
  79. plateauType *
  80. detectPlateaus::getPlateauForward(dimension_type i, dimension_type j,
  81. dimension_type nr, dimension_type nc) {
  82. bool ok;
  83. static plateauType ptarr[3]; /* return value */
  84. plateauType pt;
  85. ok = platQueue->peek(0, &pt);
  86. while(ok && (pt.i < i || (pt.i==i && pt.j<j))) {
  87. platQueue->dequeue(&pt); /* past needing this, so remove */
  88. ok = platQueue->peek(0, &pt);
  89. }
  90. if(ok && pt.i == i && pt.j == j) {
  91. platQueue->dequeue(&pt); /* found it, so remove */
  92. ptarr[0] = pt;
  93. } else {
  94. ptarr[0].invalidate();
  95. }
  96. /* locate next two, if possible */
  97. for(int kk=0,k=1; k<3; k++) {
  98. ok = platQueue->peek(kk, &pt);
  99. if(ok && pt.i == i && pt.j == j+k) {
  100. ptarr[k] = pt;
  101. kk++; /* found something, so need to peek further forward */
  102. } else {
  103. ptarr[k].invalidate();
  104. }
  105. }
  106. #if(0)
  107. cout << "request at " << i << "," << j << " returns: " <<
  108. ptarr[0] << ptarr[1] << ptarr[2] << endl;
  109. platQueue->peek(0, &pt);
  110. cout << "queue length = " << platQueue->length()
  111. << "; head=" << pt << endl;
  112. #endif
  113. return ptarr;
  114. }
  115. /* ********************************************************************** */
  116. /* should be called for each element in the grid */
  117. direction_type *
  118. detectPlateaus::getDirectionForward(dimension_type i, dimension_type j,
  119. dimension_type nr, dimension_type nc) {
  120. static direction_type dirarr[3]; /* return value */
  121. dirarr[0] = 0;
  122. dirarr[1] = 0;
  123. dirarr[2] = 0;
  124. assert(i<nr-1);
  125. assert(nc>3);
  126. if(i>=0) {
  127. if(!(i==0 && j==-1)) dirQueue->dequeue(dirarr);
  128. if(j == -1) dirarr[0] = 0;
  129. if(j+1 < nc) dirQueue->peek(0, dirarr+1);
  130. if(j+2 < nc) dirQueue->peek(1, dirarr+2);
  131. }
  132. #if(0)
  133. cout << "\t\t\trequest at " << i << "," << j << " returns: " <<
  134. dirarr[0] << " " << dirarr[1] << " " << dirarr[2] << "\t";
  135. direction_type dir;
  136. dirQueue->peek(0, &dir);
  137. cout << "queue length = " << dirQueue->length()
  138. << "; head=" << dir << endl;
  139. #endif
  140. return dirarr;
  141. }
  142. /* ********************************************************************** */
  143. void
  144. detectPlateaus::processWindow(dimension_type row, dimension_type col,
  145. elevation_type *a,
  146. elevation_type *b,
  147. elevation_type *c) {
  148. AMI_err ae;
  149. static plateauType prevPlat; /* cell on left (auto-initialized) */
  150. direction_type dir;
  151. assert(row>=0);
  152. assert(col>=0);
  153. /* create window and write out */
  154. ElevationWindow win(a, b, c);
  155. /* compute direction; pits should have been filled */
  156. dir = encodeDirection(win, nrows, ncols, row, col);
  157. dirQueue->enqueue(dir); /* write dir to dir stream */
  158. ae = dirStream->write_item(dir); /* save to file for later use */
  159. assert(ae == AMI_ERROR_NO_ERROR);
  160. /* must always read to keep in sync */
  161. direction_type *dirarr =
  162. getDirectionForward(row-1, col-1, nrows, ncols);
  163. /* if(dir == DIRECTION_UNDEF) return; */
  164. if(is_nodata(win.get())) { /* if nodata, get outa here */
  165. prevPlat.cclabel = LABEL_UNDEF;
  166. return;
  167. }
  168. if(col == 0) prevPlat.cclabel = LABEL_UNDEF; /* no left cell */
  169. /* now check for continuing plateaus */
  170. plateauType *ptarr =
  171. getPlateauForward(row-1, col-1, nrows, ncols);
  172. cclabel_type crtlabel=LABEL_UNDEF;
  173. for(int i=0; i<4; i++) {
  174. if(win.get(i) != win.get()) continue; /* only interesting if same elev */
  175. /* determine label for cell */
  176. cclabel_type label = LABEL_UNDEF;
  177. if(i<3) {
  178. if(ptarr[i].valid) label = ptarr[i].cclabel;
  179. } else {
  180. if(prevPlat.valid) label = prevPlat.cclabel;
  181. }
  182. /* check for collisions */
  183. if(label != LABEL_UNDEF) {
  184. if (crtlabel == LABEL_UNDEF) {
  185. crtlabel = label;
  186. } else if(crtlabel != label) { /* collision!! */
  187. /* pick smaller label */
  188. if(crtlabel<label) {
  189. colTree.insert(crtlabel, label);
  190. } else {
  191. colTree.insert(label, crtlabel);
  192. crtlabel = label;
  193. }
  194. }
  195. }
  196. }
  197. /* assign label if required */
  198. if(crtlabel == LABEL_UNDEF) {
  199. /* if we have a direction, we're done. we are not part of a known
  200. plateau. if we are part(neighbor) of a plateau to be identified
  201. later, our neighbor will write us later. */
  202. if(dir > 0) {
  203. prevPlat = plateauType(row, col, dir);
  204. PLAT_DEBUG cout << "skipping " << prevPlat << endl;
  205. return;
  206. }
  207. crtlabel = labelFactory::getNewLabel();
  208. }
  209. /* check boundaries that are also part of plateau (but didnt know it) */
  210. for(int i=0; i<4; i++) {
  211. direction_type ndir(0);
  212. if(win.get(i) != win.get()) continue; /* only interesting if same elev */
  213. /* determine direction for cell */
  214. if(i<3) {
  215. ndir = dirarr[i];
  216. } else {
  217. if(prevPlat.valid) ndir = prevPlat.dir;
  218. }
  219. /* check for boundaries */
  220. if(ndir > 0) { /* has direction */
  221. plateauType nbor;
  222. if(i<3) {
  223. nbor = plateauType(row-1, col+i-1, ndir, crtlabel);
  224. } else {
  225. nbor = plateauType(row, col-1, ndir, crtlabel);
  226. }
  227. if((nbor.i >= 0) & (nbor.j >= 0)) { /* make sure nbor on grid;
  228. this can happen because
  229. we pad the grid with
  230. nodata */
  231. PLAT_DEBUG cout << "neighbor insert " << nbor << endl;
  232. ae = platStream->write_item(nbor);
  233. assert(ae == AMI_ERROR_NO_ERROR);
  234. }
  235. }
  236. } /* for i */
  237. /* write this plateau point to the plateau stream */
  238. plateauType pt;
  239. prevPlat = pt = plateauType(row, col, dir, crtlabel);
  240. platQueue->enqueue(pt);
  241. PLAT_DEBUG cout << "inserting " << pt << endl;
  242. platStream->write_item(pt); /* save to file for later use */
  243. }
  244. /* ********************************************************************** */
  245. void
  246. detectPlateaus::generatePlateaus(AMI_STREAM<elevation_type> &elstr) {
  247. dirQueue = new queue<direction_type>();
  248. platQueue = new queue<plateauType>();
  249. /* scan3(elstr, hdr, hdr.get_nodata(), *this); */
  250. // if (opt->verbose) STRACE("starting memscan");
  251. memoryScan(elstr, nrows, ncols, nodata_value, *this);
  252. //if (opt->verbose) STRACE("memscan done");
  253. delete dirQueue;
  254. delete platQueue;
  255. }
  256. /* ********************************************************************** */
  257. class duplicateFixer {
  258. ccforest<cclabel_type> *colTree;
  259. public:
  260. duplicateFixer(ccforest<cclabel_type> *p) : colTree(p) {};
  261. int compare(const plateauType &a, const plateauType &b) {
  262. int c = ijCmpPlateauType::compare(a,b);
  263. if(c==0 && (a.cclabel != b.cclabel)) { /* collision */
  264. if(a.cclabel<b.cclabel) {
  265. colTree->insert(a.cclabel, b.cclabel);
  266. } else {
  267. colTree->insert(b.cclabel, a.cclabel);
  268. }
  269. }
  270. return c;
  271. }
  272. };
  273. /* ********************************************************************** */
  274. /* take out plateau elements that were generated multiple times */
  275. void
  276. detectPlateaus::removeDuplicates() {
  277. PLAT_DEBUG cout << "sort plateauStream (by ij): ";
  278. sort(&platStream, ijCmpPlateauType());
  279. ::removeDuplicatesEx(&platStream, duplicateFixer(&colTree));
  280. }
  281. /* ********************************************************************** */
  282. /* collapse labels; remove nodata regions */
  283. void
  284. detectPlateaus::relabelPlateaus() {
  285. AMI_err ae;
  286. plateauType *pt;
  287. AMI_STREAM<plateauType> *sortedInStr;
  288. PLAT_DEBUG cout << "sort plateauStream (by label): ";
  289. sortedInStr = sort(platStream, labelCmpPlateauType());
  290. delete platStream;
  291. platStream = new AMI_STREAM<plateauType>;
  292. sortedInStr->seek(0);
  293. /*
  294. cout << "EDGESTREAM:" << endl; colTree.printEdgeStream();
  295. cout << "ROOTSTREAM:" << endl; colTree.printRootStream();
  296. cout << "RELABELING:" << endl;
  297. */
  298. while((ae = sortedInStr->read_item(&pt)) == AMI_ERROR_NO_ERROR) {
  299. cclabel_type root = colTree.findNextRoot(pt->cclabel);
  300. assert(root <= pt->cclabel);
  301. assert(root >= LABEL_START);
  302. pt->cclabel = root;
  303. ae = platStream->write_item(*pt);
  304. assert(ae == AMI_ERROR_NO_ERROR);
  305. /* cout << *pt << endl; */
  306. }
  307. delete sortedInStr;
  308. }
  309. /* ********************************************************************** */
  310. void
  311. detectPlateaus::generateStats(AMI_STREAM<plateauStats> *statStr) {
  312. AMI_err ae;
  313. plateauType *pt;
  314. /* sort by label */
  315. AMI_STREAM<plateauType> *sortedStream;
  316. PLAT_DEBUG cout << "sort plateauStream (by label): ";
  317. sortedStream = sort(platStream, labelCmpPlateauType());
  318. delete platStream;
  319. plateauStats labelStats = plateauStats();
  320. sortedStream->seek(0);
  321. while((ae = sortedStream->read_item(&pt)) == AMI_ERROR_NO_ERROR) {
  322. if(pt->cclabel != labelStats.label) {
  323. if(labelStats.label != LABEL_UNDEF) {
  324. ae = statStr->write_item(labelStats);
  325. assert(ae == AMI_ERROR_NO_ERROR);
  326. }
  327. labelStats = plateauStats(pt->cclabel);
  328. }
  329. labelStats.add(*pt);
  330. }
  331. ae = statStr->write_item(labelStats);
  332. assert(ae == AMI_ERROR_NO_ERROR);
  333. platStream = sortedStream;
  334. }
  335. /* ********************************************************************** */
  336. /* ********************************************************************** */
  337. AMI_STREAM<plateauType> *
  338. findPlateaus(AMI_STREAM<elevation_type> *elstr,
  339. const dimension_type nrows, const dimension_type ncols,
  340. const elevation_type nodata_value,
  341. AMI_STREAM<ElevationWindow > *winstr,
  342. AMI_STREAM<direction_type> *dirStr,
  343. AMI_STREAM<plateauStats> *statStr) {
  344. Rtimer rt;
  345. labelFactory::reset();
  346. /* find plateaus */
  347. rt_start(rt);
  348. if (stats) {
  349. stats->comment("----------", opt->verbose);
  350. stats->comment("finding flat areas (plateaus and depressions)");
  351. }
  352. detectPlateaus md(nrows, ncols,nodata_value, dirStr, winstr);
  353. md.generatePlateaus(*elstr);
  354. rt_stop(rt);
  355. if (stats) {
  356. stats->recordTime("findPlateaus::generate plateaus", rt);
  357. stats->recordLength("plateaus", md.getPlateaus());
  358. }
  359. rt_start(rt);
  360. if (stats)
  361. stats->comment("removing duplicate plateaus", opt->verbose);
  362. md.removeDuplicates(); /* get rid of duplicates of same plateau point */
  363. rt_stop(rt);
  364. if (stats) {
  365. stats->recordTime("findPlateaus::removing duplicates", rt);
  366. stats->recordLength("plateaus", md.getPlateaus());
  367. }
  368. #if(0)
  369. { /* XXX */
  370. AMI_STREAM<plateauType> *tmp = sort(md.getPlateaus(), ijCmpPlateauType());
  371. printStream2Grid(tmp, nrows, ncols,
  372. "label0.asc", plateauType::printLabel);
  373. delete tmp;
  374. }
  375. #endif
  376. rt_start(rt);
  377. if (stats)
  378. stats->comment("relabeling plateaus", opt->verbose);
  379. md.relabelPlateaus(); /* re-assign labels (combine connected plateaus) */
  380. rt_stop(rt);
  381. if (stats) {
  382. stats->recordTime("findPlateaus::relabeling", rt);
  383. stats->recordLength("plateaus", md.getPlateaus());
  384. }
  385. rt_start(rt);
  386. if (stats)
  387. stats->comment("generating plateau statistics", opt->verbose);
  388. md.generateStats(statStr);
  389. rt_stop(rt);
  390. if (stats) {
  391. stats->recordTime("findPlateaus::generating stats", rt);
  392. stats->recordLength("plateaus", md.getPlateaus());
  393. }
  394. dirStr->seek(0);
  395. return md.getPlateaus();
  396. }
  397. /* ********************************************************************** */