nodata.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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>
  19. #include "nodata.h"
  20. #include "common.h"
  21. #include "sortutils.h"
  22. #include "streamutils.h"
  23. #include "ccforest.h"
  24. #include "3scan.h"
  25. /* #include "plateau.h" */
  26. #include "genericWindow.h"
  27. /* globals
  28. extern statsRecorder *stats;
  29. extern userOptions *opt;
  30. extern struct Cell_head *region;
  31. extern dimension_type nrows, ncols;
  32. */
  33. #define NODATA_DEBUG if(0)
  34. /* ********************************************************************** */
  35. elevation_type nodataType::ELEVATION_BOUNDARY;
  36. elevation_type nodataType::ELEVATION_NODATA;
  37. /* ********************************************************************** */
  38. /* int */
  39. /* is_nodata(elevation_type el) { */
  40. /* return (el == nodataType::ELEVATION_BOUNDARY || */
  41. /* el == nodataType::ELEVATION_NODATA); */
  42. /* } */
  43. int
  44. is_nodata(short x) {
  45. return (x == nodataType::ELEVATION_BOUNDARY ||
  46. x == nodataType::ELEVATION_NODATA);
  47. }
  48. int
  49. is_nodata(int x) {
  50. return (x == nodataType::ELEVATION_BOUNDARY ||
  51. x == nodataType::ELEVATION_NODATA);
  52. }
  53. int
  54. is_nodata(float x) {
  55. return (x == nodataType::ELEVATION_BOUNDARY ||
  56. x == nodataType::ELEVATION_NODATA);
  57. }
  58. int
  59. is_void(elevation_type el) {
  60. return (el == nodataType::ELEVATION_NODATA);
  61. }
  62. /* ********************************************************************** */
  63. class detectEdgeNodata {
  64. private:
  65. AMI_STREAM<nodataType> *nodataStream;
  66. AMI_STREAM<elevation_type> *elevStream;
  67. queue<nodataType> *nodataQueue;
  68. ccforest<cclabel_type> colTree;
  69. nodataType *getNodataForward(dimension_type i, dimension_type j,
  70. dimension_type nr, dimension_type n);
  71. const dimension_type nr, nc;
  72. const elevation_type nodata;
  73. public:
  74. detectEdgeNodata(const dimension_type nrows, const dimension_type nc,
  75. const elevation_type nodata);
  76. ~detectEdgeNodata();
  77. void processWindow(dimension_type row, dimension_type col,
  78. elevation_type &point,
  79. elevation_type *a,
  80. elevation_type *b,
  81. elevation_type *c);
  82. void generateNodata(AMI_STREAM<elevation_type> &elstr);
  83. void relabelNodata();
  84. AMI_STREAM<elevation_type> *merge();
  85. AMI_STREAM<nodataType> *getNodata() { return nodataStream; }
  86. };
  87. /* ********************************************************************** */
  88. detectEdgeNodata::detectEdgeNodata(const dimension_type nrows,
  89. const dimension_type ncols,
  90. const elevation_type gnodata)
  91. : nr(nrows), nc(ncols), nodata(gnodata) {
  92. nodataStream = new AMI_STREAM<nodataType>();
  93. elevStream = new AMI_STREAM<elevation_type>();
  94. }
  95. /* ********************************************************************** */
  96. detectEdgeNodata::~detectEdgeNodata() {
  97. delete nodataStream;
  98. delete elevStream;
  99. }
  100. /* ********************************************************************** */
  101. /* return a pointer to three plateauType structures, starting at
  102. location i,j. caller should check valid field in returned
  103. structs. */
  104. nodataType *
  105. detectEdgeNodata::getNodataForward(dimension_type i, dimension_type j,
  106. dimension_type nr, dimension_type nc) {
  107. bool ok;
  108. static nodataType ptarr[3]; /* return value */
  109. nodataType pt;
  110. ok = nodataQueue->peek(0, &pt);
  111. while(ok && (pt.i < i || (pt.i==i && pt.j<j))) {
  112. nodataQueue->dequeue(&pt); /* past needing this, so remove */
  113. ok = nodataQueue->peek(0, &pt);
  114. }
  115. if(ok && pt.i == i && pt.j == j) {
  116. nodataQueue->dequeue(&pt); /* found it, so remove */
  117. ptarr[0] = pt;
  118. } else {
  119. ptarr[0].invalidate();
  120. }
  121. /* locate next two, if possible */
  122. for(int kk=0,k=1; k<3; k++) {
  123. ok = nodataQueue->peek(kk, &pt);
  124. if(ok && pt.i == i && pt.j == j+k) {
  125. ptarr[k] = pt;
  126. kk++; /* found something, so need to peek further forward */
  127. } else {
  128. ptarr[k].invalidate();
  129. }
  130. }
  131. #if(0)
  132. cout << "request at " << i << "," << j << " returns: " <<
  133. ptarr[0] << ptarr[1] << ptarr[2] << endl;
  134. nodataQueue->peek(0, &pt);
  135. cout << "queue length = " << nodataQueue->length()
  136. << "; head=" << pt << endl;
  137. #endif
  138. return ptarr;
  139. }
  140. /* ********************************************************************** */
  141. void
  142. detectEdgeNodata::processWindow(dimension_type row, dimension_type col,
  143. elevation_type &point,
  144. elevation_type *a,
  145. elevation_type *b,
  146. elevation_type *c) {
  147. AMI_err ae;
  148. static nodataType prevCell; /* cell on left (gets initialized) */
  149. assert(row>=0);
  150. assert(col>=0);
  151. /* create window and write out */
  152. ElevationWindow win(a, b, c);
  153. fillPit(win); /* fill pit in window */
  154. ae = elevStream->write_item(win.get());
  155. assert(ae == AMI_ERROR_NO_ERROR);
  156. /* only interested in nodata in this pass */
  157. if(win.get() != nodata) {
  158. prevCell.label = LABEL_UNDEF;
  159. return;
  160. }
  161. if(col == 0) prevCell.label = LABEL_UNDEF; /* no left cell */
  162. /* now check for continuing plateaus */
  163. nodataType *ptarr =
  164. getNodataForward(row-1, col-1, nr, nc);
  165. /* make sure we use boundary label if appropriate */
  166. cclabel_type crtlabel;
  167. crtlabel = (IS_BOUNDARY(row,col,nr, nc) ? LABEL_BOUNDARY : LABEL_UNDEF);
  168. for(int i=0; i<4; i++) {
  169. if(win.get(i) != win.get()) continue; /* only interesting if same elev */
  170. /* determine label for cell */
  171. cclabel_type label = LABEL_UNDEF;
  172. if(i<3) {
  173. if(ptarr[i].valid) label = ptarr[i].label;
  174. } else {
  175. if(prevCell.valid) label = prevCell.label;
  176. }
  177. /* check for collisions */
  178. if(label != LABEL_UNDEF) {
  179. if (crtlabel == LABEL_UNDEF) {
  180. crtlabel = label;
  181. } else if(crtlabel != label) { /* collision!! */
  182. /* pick smaller label, but prefer nodata */
  183. if(crtlabel==LABEL_BOUNDARY || crtlabel<label) {
  184. colTree.insert(crtlabel, label);
  185. } else {
  186. colTree.insert(label, crtlabel);
  187. crtlabel = label;
  188. }
  189. }
  190. }
  191. }
  192. /* assign label if required */
  193. if(crtlabel == LABEL_UNDEF) {
  194. crtlabel = labelFactory::getNewLabel();
  195. }
  196. /* write this plateau point to the plateau stream */
  197. nodataType pt;
  198. prevCell = pt = nodataType(row, col, crtlabel);
  199. nodataQueue->enqueue(pt);
  200. /* NODATA_DEBUG *stats << "inserting " << pt << endl; */
  201. nodataStream->write_item(pt); /* save to file for later use */
  202. }
  203. /* ********************************************************************** */
  204. void
  205. detectEdgeNodata::generateNodata(AMI_STREAM<elevation_type> &elstr) {
  206. nodataQueue = new queue<nodataType>();
  207. scan3(elstr, nr, nc, nodata, *this);
  208. delete nodataQueue;
  209. }
  210. /* ********************************************************************** */
  211. /* collapse labels; remove nodata regions */
  212. void
  213. detectEdgeNodata::relabelNodata() {
  214. AMI_err ae;
  215. nodataType *pt;
  216. /* sort by label */
  217. if (stats)
  218. NODATA_DEBUG *stats << "sort nodataStream (by nodata label): ";
  219. AMI_STREAM<nodataType> *sortedInStream;
  220. sortedInStream = sort(nodataStream, labelCmpNodataType());
  221. delete nodataStream;
  222. nodataStream = new AMI_STREAM<nodataType>();
  223. while((ae = sortedInStream->read_item(&pt)) == AMI_ERROR_NO_ERROR) {
  224. cclabel_type root = colTree.findNextRoot(pt->label);
  225. assert(root <= pt->label);
  226. pt->label = root;
  227. ae = nodataStream->write_item(*pt);
  228. assert(ae == AMI_ERROR_NO_ERROR);
  229. }
  230. delete sortedInStream;
  231. }
  232. /* ********************************************************************** */
  233. AMI_STREAM<elevation_type> *
  234. detectEdgeNodata::merge() {
  235. if (stats)
  236. NODATA_DEBUG *stats << "sort nodataStream (by ij): ";
  237. /*
  238. AMI_STREAM<nodataType> *sortedNodataStream;
  239. sortedNodataStream = sort(nodataStream, ijCmpNodataType());
  240. delete nodataStream;
  241. nodataStream=sortedNodataStream;
  242. */
  243. sort(&nodataStream, ijCmpNodataType());
  244. //note: nodataStream gets deleted and replaced with the sorted stream
  245. AMI_STREAM<elevation_type> *mergeStr;
  246. mergeStr = mergeStream2Grid(elevStream, nrows, ncols,
  247. nodataStream, nodataType2elevation_type());
  248. return mergeStr;
  249. }
  250. /* ********************************************************************** */
  251. /* ********************************************************************** */
  252. AMI_STREAM<elevation_type> *
  253. classifyNodata(AMI_STREAM<elevation_type> *elstr) {
  254. Rtimer rt;
  255. rt_start(rt);
  256. if (stats)
  257. stats->comment("finding nodata", opt->verbose);
  258. detectEdgeNodata md(nrows, ncols, nodataType::ELEVATION_NODATA);
  259. md.generateNodata(*elstr);
  260. if (stats)
  261. *stats << "nodata stream length = " << md.getNodata()->stream_len() << endl;
  262. {
  263. char * foo;
  264. md.getNodata()->name(&foo);
  265. if (stats)
  266. *stats << "nodata stream name: " << foo << endl;
  267. }
  268. rt_stop(rt);
  269. if (stats)
  270. stats->recordTime("classifyNodata::generate nodata", rt);
  271. rt_start(rt);
  272. if (stats)
  273. stats->comment("relabeling nodata", opt->verbose);
  274. md.relabelNodata(); /* re-assign labels (combine connected plateaus) */
  275. rt_stop(rt);
  276. if (stats)
  277. stats->recordTime("classifyNodata::relabeling", rt);
  278. rt_start(rt);
  279. if (stats)
  280. stats->comment("merging relabeled grid", opt->verbose);
  281. AMI_STREAM<elevation_type> *mergeStr;
  282. mergeStr = md.merge();
  283. rt_stop(rt);
  284. if (stats)
  285. stats->recordTime("classifyNodata::merge", rt);
  286. mergeStr->seek(0);
  287. return mergeStr;
  288. }
  289. /* ********************************************************************** */