fill.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  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 <stdlib.h>
  19. #include <ctype.h>
  20. #include <time.h>
  21. #include <string>
  22. #include "fill.h"
  23. #include "common.h"
  24. #include "water.h"
  25. #include "sortutils.h"
  26. #include "streamutils.h"
  27. #include "filldepr.h"
  28. #include "grid.h"
  29. /* globals in common.H
  30. extern statsRecorder *stats; stats file
  31. extern userOptions *opt; command-line options
  32. extern struct Cell_head *region; header of the region
  33. extern dimension_type nrows, ncols;
  34. */
  35. #define FILL_DEBUG if(0)
  36. #define FILL_SAVEALL if(0)
  37. /* defined in this module */
  38. void recordWatersheds(AMI_STREAM<labelElevType> *labeledWater);
  39. long assignDirections(AMI_STREAM<plateauStats> *statstr,
  40. AMI_STREAM<plateauType> *platstr,
  41. AMI_STREAM<waterType> *waterstr);
  42. void assignFinalDirections(AMI_STREAM<plateauStats> *statstr,
  43. AMI_STREAM<plateauType> *platstr,
  44. AMI_STREAM<waterType> *waterstr);
  45. template<class T1, class T2, class T3, class T4, class FUN>
  46. void mergeStreamGridGrid(AMI_STREAM<T1> *grid1,
  47. AMI_STREAM<T2> *grid2,
  48. dimension_type rows, dimension_type cols,
  49. AMI_STREAM<T3> *str,
  50. FUN fo,
  51. AMI_STREAM<T4> *outStream);
  52. void merge2waterBase(AMI_STREAM<waterType> *unsortedWaterStr,
  53. AMI_STREAM<direction_type> *dirStr,
  54. AMI_STREAM<elevation_type> *elStr,
  55. AMI_STREAM<waterWindowBaseType> *merge);
  56. AMI_STREAM<waterGridType>*
  57. merge2waterGrid(AMI_STREAM<waterType> *unsortedWaterStr,
  58. AMI_STREAM<direction_type> *dirStr,
  59. AMI_STREAM<elevation_type> *elStr);
  60. AMI_STREAM<boundaryType> *
  61. findBoundariesMain(AMI_STREAM<labelElevType> *labeledWater);
  62. /* ********************************************************************** */
  63. /* some helper classes */
  64. /* ********************************************************************** */
  65. class printElevation {
  66. public:
  67. char *operator()(const elevation_type &p) {
  68. static char buf[20];
  69. sprintf(buf, "%.1f", (float)p);
  70. return buf;
  71. }
  72. };
  73. class printDirection {
  74. public:
  75. char *operator()(const direction_type &p) {
  76. static char buf[20];
  77. sprintf(buf, "%3d", p);
  78. return buf;
  79. }
  80. char *operator()(const waterWindowBaseType &p) {
  81. static char buf[20];
  82. sprintf(buf, "%3d", p.dir);
  83. return buf;
  84. }
  85. #if(0)
  86. char *operator()(const waterWindowBaseType &p) {
  87. static char buf[3];
  88. buf[0] = directionSymbol(p.dir);
  89. buf[1] = '\0';
  90. return buf;
  91. }
  92. #endif
  93. };
  94. class printLabel {
  95. public:
  96. char *operator()(const labelElevType&p) {
  97. static char buf[8];
  98. sprintf(buf, CCLABEL_FMT, p.getLabel());
  99. return buf;
  100. }
  101. char *operator()(const waterGridType &p) {
  102. static char buf[8];
  103. sprintf(buf, CCLABEL_FMT, p.getLabel());
  104. return buf;
  105. }
  106. char *operator()(const waterType &p) {
  107. static char buf[8];
  108. sprintf(buf, CCLABEL_FMT, p.getLabel());
  109. return buf;
  110. }
  111. };
  112. class printDepth {
  113. public:
  114. char *operator()(const waterGridType &p) {
  115. static char buf[3];
  116. sprintf(buf, "%1u", p.depth);
  117. return buf;
  118. }
  119. };
  120. char *
  121. verbosedir(std::string s) {
  122. static char buf[BUFSIZ];
  123. sprintf(buf, "dump/%s", s.c_str());
  124. return buf;
  125. }
  126. /* ---------------------------------------------------------------------- */
  127. /* fill the terrain (if necessary) and compute flow direction stream;
  128. elstr must exist and contain grid data before call, filledstr and
  129. dirstr are created; elstr is deleted and replaced with the
  130. classified elstr, which has boundary nodata distinguished from
  131. inner nodata */
  132. AMI_STREAM<waterWindowBaseType>*
  133. computeFlowDirections(AMI_STREAM<elevation_type>*& elstr,
  134. AMI_STREAM<elevation_type>*& filledstr,
  135. AMI_STREAM<direction_type>*& dirstr,
  136. AMI_STREAM<labelElevType> *& labeledWater) {
  137. Rtimer rt, rtTotal;
  138. AMI_STREAM<elevation_type> *elstr_reclass=NULL;
  139. AMI_STREAM<ElevationWindow > *winstr=NULL;
  140. AMI_STREAM<plateauStats> *statstr=NULL;
  141. AMI_STREAM<plateauType> *platstr=NULL;
  142. AMI_STREAM<waterType> *waterstr=NULL;
  143. AMI_STREAM<waterGridType> *mergedWaterStr=NULL;
  144. AMI_STREAM<boundaryType> *boundaryStr=NULL;
  145. AMI_STREAM<waterWindowType> *waterWindows=NULL;
  146. rt_start(rtTotal);
  147. assert(elstr && filledstr == NULL && dirstr == NULL && labeledWater == NULL);
  148. stats->comment("------------------------------");
  149. stats->comment("COMPUTING FLOW DIRECTIONS");
  150. /* adjust nodata -- boundary nodata distinguished from inner
  151. nodata */
  152. stats->comment("classifying nodata (inner & boundary)");
  153. elstr_reclass = classifyNodata(elstr);
  154. delete elstr;
  155. elstr = elstr_reclass;
  156. /* ---------------------------------------------------------------------- */
  157. /* find the plateaus. */
  158. /* ---------------------------------------------------------------------- */
  159. stats->comment("----------", opt->verbose);
  160. stats->comment("assigning preliminary directions");
  161. rt_start(rt);
  162. dirstr = new AMI_STREAM<direction_type>;
  163. winstr = new AMI_STREAM<ElevationWindow>();
  164. statstr = new AMI_STREAM<plateauStats>;
  165. platstr = findPlateaus(elstr, nrows, ncols, nodataType::ELEVATION_NODATA,
  166. winstr, dirstr, statstr);
  167. delete winstr; /* not used; not made */
  168. rt_stop(rt);
  169. stats->recordTime("findingPlateaus", rt);
  170. stats->recordLength("plateaus", platstr);
  171. stats->recordLength("plateau stats", statstr);
  172. FILL_SAVEALL {
  173. /* printStream(*stats, statstr); */
  174. FILL_DEBUG cout << "sort plateauStr (by ij): ";
  175. AMI_STREAM<plateauType> *tmp = sort(platstr, ijCmpPlateauType());
  176. printStream2Grid(tmp, nrows, ncols,
  177. verbosedir("label1.asc"), plateauType::printLabel);
  178. delete tmp;
  179. }
  180. /* ---------------------------------------------------------------------- */
  181. /* assign labels and directions & BFS ordering. depressions have
  182. labels, but no direction information.
  183. */
  184. /* ---------------------------------------------------------------------- */
  185. rt_start(rt);
  186. waterstr = new AMI_STREAM<waterType>();
  187. assignDirections(statstr, platstr, waterstr);
  188. delete platstr;
  189. delete statstr;
  190. rt_stop(rt);
  191. stats->recordTime("assigning directions", rt);
  192. *stats << "maxWatershedCount=" << labelFactory::getLabelCount() << endl;
  193. rt_start(rt);
  194. mergedWaterStr = merge2waterGrid(waterstr, dirstr, elstr);
  195. delete dirstr;
  196. delete waterstr;
  197. rt_stop(rt);
  198. stats->recordTime("merging", rt);
  199. stats->recordLength("mergedWaterStr", mergedWaterStr);
  200. /* ---------------------------------------------------------------------- */
  201. /* watershed analysis */
  202. /* IN: mergedWaterStr, labelFactory::... */
  203. /* ---------------------------------------------------------------------- */
  204. stats->comment("--------------", opt->verbose);
  205. stats->comment("generating watersheds and watershed graph");
  206. rt_start(rt);
  207. waterWindows = new AMI_STREAM<waterWindowType>();
  208. createWaterWindows(mergedWaterStr, nrows, ncols, waterWindows);
  209. delete mergedWaterStr;
  210. rt_stop(rt);
  211. stats->recordTime("creating windows", rt);
  212. stats->recordLength("waterWindows", waterWindows);
  213. /* ---------------------------------------------------------------------- */
  214. rt_start(rt);
  215. labeledWater = new AMI_STREAM<labelElevType>();
  216. boundaryStr = new AMI_STREAM<boundaryType>();
  217. generateWatersheds(&waterWindows, nrows, ncols, labeledWater, boundaryStr);
  218. /* do we need to make boundaries here?? */
  219. delete waterWindows;
  220. /* cout << "bogus boundary length = " << boundaryStr->stream_len() << endl;*/
  221. assert(boundaryStr->stream_len() == 0);
  222. delete boundaryStr;
  223. assert(labeledWater->stream_len() == nrows * ncols);
  224. rt_stop(rt);
  225. stats->recordTime("generating watersheds", rt);
  226. /* ---------------------------------------------------------------------- */
  227. /* find boundaries */
  228. /* ---------------------------------------------------------------------- */
  229. FILL_DEBUG cerr << "sort labeledWater (by ij):";
  230. sort(&labeledWater, ijCmpLabelElevType());
  231. #ifdef SAVE_ASCII
  232. cerr << "saving WATERSHED grid as watershed_grid\n";
  233. printStream2Grid(labeledWater, nrows, ncols,
  234. "watershed.asc", labelElevType::printLabel);
  235. #endif
  236. boundaryStr = findBoundariesMain(labeledWater);
  237. /* ---------------------------------------------------------------------- */
  238. /* filling */
  239. /* valid streams are: boundaryStr, labeledWater */
  240. /* ---------------------------------------------------------------------- */
  241. rt_start(rt);
  242. elevation_type *raise;
  243. /*find the raise elevations */
  244. FILL_DEBUG cerr << "sort boundaryStr (by elev): ";
  245. sort(&boundaryStr, elevCmpBoundaryType());
  246. raise = fill_depression(boundaryStr, labelFactory::getLabelCount());
  247. delete boundaryStr;
  248. rt_stop(rt);
  249. stats->recordTime("filling depressions", rt);
  250. /*fill the terrain*/
  251. rt_start(rt);
  252. filledstr = new AMI_STREAM<elevation_type>();
  253. commit_fill(labeledWater, raise, labelFactory::getLabelCount(), filledstr);
  254. assert(filledstr->stream_len() == nrows * ncols);
  255. delete [] raise;
  256. rt_stop(rt);
  257. stats->recordTime("updating filled grid", rt);
  258. stats->recordLength("filledstr", filledstr);
  259. /* ---------------------------------------------------------------------- */
  260. /* find plateaus again and reassign directions */
  261. /* ---------------------------------------------------------------------- */
  262. stats->comment("------------------------------");
  263. stats->comment("REASSIGNING DIRECTIONS");
  264. rt_start(rt);
  265. winstr = NULL;
  266. dirstr = new AMI_STREAM<direction_type>();
  267. statstr = new AMI_STREAM<plateauStats>();
  268. platstr = findPlateaus(filledstr, nrows, ncols, nodataType::ELEVATION_NODATA,
  269. winstr, dirstr, statstr);
  270. rt_stop(rt);
  271. stats->recordTime("findingPlateaus2", rt);
  272. stats->recordLength("final plateaus", platstr);
  273. stats->recordLength("final plateau stats", statstr);
  274. FILL_SAVEALL {
  275. FILL_DEBUG cout << "sort plateauStr (by ij): ";
  276. AMI_STREAM<plateauType> *tmp = sort(platstr, ijCmpPlateauType());
  277. printStream2Grid(tmp, nrows, ncols,
  278. verbosedir("plateaus.asc"), plateauType::printLabel);
  279. delete tmp;
  280. }
  281. /* assign final directions */
  282. rt_start(rt);
  283. waterstr = new AMI_STREAM<waterType>();
  284. long dc = assignDirections(statstr, platstr, waterstr);
  285. if(dc) {
  286. *stats << "WARNING: " << dc << " depressions (islands) detected\n";
  287. }
  288. delete platstr;
  289. delete statstr;
  290. rt_stop(rt);
  291. stats->recordTime("final directions", rt);
  292. /* merge */
  293. rt_start(rt);
  294. AMI_STREAM<waterWindowBaseType> *flowStream;
  295. /*STREAM_TO_OPTION(flowStream, "flowStream");*/
  296. char path[BUFSIZ];
  297. char* base_dir = getenv(STREAM_TMPDIR);
  298. assert(base_dir);
  299. sprintf(path, "%s/flowStream", base_dir);
  300. flowStream = new AMI_STREAM<waterWindowBaseType>(path);
  301. /*flowStream->persist(PERSIST_PERSISTENT); */
  302. stats->comment("creating flowStream: ", flowStream->sprint());
  303. merge2waterBase(waterstr, dirstr, filledstr, flowStream);
  304. delete waterstr;
  305. rt_stop(rt);
  306. stats->recordTime("merge water,dir,elev to flow", rt);
  307. rt_stop(rtTotal);
  308. #ifdef SAVE_ASCII
  309. /*write grids as ascii file */
  310. printGridStream(filledstr, nrows, ncols,
  311. "filled_elev.asc", printElevation());
  312. printGridStream(flowStream, nrows, ncols,
  313. "direction.asc", printDirection());
  314. #endif
  315. stats->recordTime("Total compute flow direction running time", rtTotal);
  316. stats->comment("compute flow directions done.");
  317. return flowStream;
  318. }
  319. /* ---------------------------------------------------------------------- */
  320. void
  321. recordWatersheds(AMI_STREAM<labelElevType> *labeledWater) {
  322. AMI_err ae;
  323. long labelCount = 0;
  324. AMI_STREAM<labelElevType> *tmp;
  325. *stats << "--- watershed stats" << endl;
  326. FILL_DEBUG cout << "sort labeledWater (by wat label): ";
  327. tmp = sort(labeledWater, labelCmpLabelElevType());
  328. labelElevType *p;
  329. cclabel_type prev(LABEL_UNDEF);
  330. tmp->seek(0);
  331. while((ae = tmp->read_item(&p)) == AMI_ERROR_NO_ERROR) {
  332. if(p->getLabel() != prev) {
  333. labelCount++;
  334. prev = p->getLabel();
  335. }
  336. }
  337. assert(ae == AMI_ERROR_END_OF_STREAM);
  338. *stats << "watershed count = " << labelCount << endl;
  339. *stats << "---" << endl;
  340. stats->flush();
  341. delete tmp;
  342. }
  343. /* ********************************************************************** */
  344. /* assign directions to plateaus that have sinks;
  345. * reassign labels to depressions (don't drain out).
  346. * all plateaus are written out to the waterstr. */
  347. long
  348. assignDirections(AMI_STREAM<plateauStats> *statstr,
  349. AMI_STREAM<plateauType> *platstr,
  350. AMI_STREAM<waterType> *waterstr) {
  351. size_t fmem;
  352. AMI_err ae;
  353. plateauStats *ps;
  354. stats->comment("----------", opt->verbose);
  355. stats->comment("assigning directions on plateaus");
  356. labelFactory::reset(); /* we are relabeling now */
  357. statstr->seek(0);
  358. platstr->seek(0);
  359. fmem = getAvailableMemory();
  360. long depressionCount=0;
  361. long spillCount=0;
  362. while((ae = statstr->read_item(&ps)) == AMI_ERROR_NO_ERROR) {
  363. if(ps->size*sizeof(gridElement) > fmem) {
  364. cerr << "WARNING: grid larger than memory (ignored)" << endl;
  365. }
  366. assert(ps->label != LABEL_NODATA);
  367. if(ps->hasSpill) {
  368. spillCount++;
  369. grid *platGrid = new grid(ps->iMin, ps->jMin, ps->iMax, ps->jMax,
  370. ps->size, ps->label);
  371. platGrid->load(*platstr);
  372. platGrid->assignDirections(opt->d8 ? 1 : 0);
  373. platGrid->save(*waterstr); /* this doesn't save labels */
  374. delete platGrid;
  375. } else {
  376. /* depression - just give contiguous labels only */
  377. depressionCount++;
  378. cclabel_type label = labelFactory::getNewLabel();
  379. for(int i=0; i<ps->size; i++) {
  380. plateauType *pt;
  381. platstr->read_item(&pt);
  382. pt->cclabel = label; /* assign new label */
  383. waterType wt(*pt); /* write it out */
  384. ae = waterstr->write_item(wt);
  385. assert(ae == AMI_ERROR_NO_ERROR);
  386. }
  387. }
  388. }
  389. *stats << "depression count = " << depressionCount << endl;
  390. *stats << "spill count = " << spillCount << endl;
  391. return depressionCount;
  392. }
  393. /* ********************************************************************** */
  394. /* assign directions to plateaus that have sinks;
  395. * check that there are no depressions.
  396. */
  397. void
  398. assignFinalDirections(AMI_STREAM<plateauStats> *statstr,
  399. AMI_STREAM<plateauType> *platstr,
  400. AMI_STREAM<waterType> *waterstr) {
  401. AMI_err ae;
  402. plateauStats *ps;
  403. stats->comment("assigning final directions");
  404. statstr->seek(0);
  405. platstr->seek(0);
  406. while((ae = statstr->read_item(&ps)) == AMI_ERROR_NO_ERROR) {
  407. if(ps->hasSpill) {
  408. grid *platGrid = new grid(ps->iMin, ps->jMin, ps->iMax, ps->jMax,
  409. ps->size, ps->label);
  410. platGrid->load(*platstr);
  411. platGrid->assignDirections(opt->d8 ? 1 : 0);
  412. platGrid->save(*waterstr); /* this doesn't save labels */
  413. delete platGrid;
  414. } else {
  415. /* could be legitimate */
  416. cerr << "WARNING: depression detected: " << *ps << endl;
  417. continue;
  418. }
  419. }
  420. };
  421. /* ********************************************************************** */
  422. class directionElevationMerger {
  423. public:
  424. waterGridType operator()(elevation_type el, direction_type dir,
  425. waterType p) {
  426. /* check that no (boundary) nodata values got in here */
  427. assert(el != nodataType::ELEVATION_BOUNDARY);
  428. assert(!is_nodata(el)); /* p should be a valid grid cell */
  429. return waterGridType(el, p.getDirection(), p.getLabel(), p.getDepth());
  430. }
  431. waterGridType operator()(elevation_type el, direction_type dir) {
  432. waterGridType wg(el, dir);
  433. if(el == nodataType::ELEVATION_BOUNDARY) { /* hack XXX (approved) */
  434. wg.setLabel(LABEL_BOUNDARY);
  435. }
  436. /* nodata => boundary or undef */
  437. assert(!is_nodata(el) ||
  438. (wg.getLabel()==LABEL_BOUNDARY || wg.getLabel()==LABEL_UNDEF));
  439. return wg;
  440. }
  441. };
  442. /* ---------------------------------------------------------------------- */
  443. AMI_STREAM<waterGridType> *
  444. merge2waterGrid(AMI_STREAM<waterType> *unsortedWaterStr,
  445. AMI_STREAM<direction_type> *dirStr,
  446. AMI_STREAM<elevation_type> *elStr) {
  447. AMI_STREAM<waterType> *waterStr;
  448. FILL_DEBUG cout << "sort waterStr (by ij): ";
  449. waterStr = sort(unsortedWaterStr, ijCmpWaterType());
  450. FILL_SAVEALL printStream2Grid(waterStr, nrows, ncols,
  451. verbosedir("platlabels.asc"), printLabel());
  452. AMI_STREAM<waterGridType> *mergedWaterStr = new AMI_STREAM<waterGridType>();
  453. mergeStreamGridGrid(elStr, dirStr,
  454. nrows, ncols,
  455. waterStr,
  456. directionElevationMerger(),
  457. mergedWaterStr);
  458. delete waterStr;
  459. FILL_SAVEALL printGridStream(mergedWaterStr, nrows, ncols,
  460. verbosedir("mergedlabels.asc"), printLabel());
  461. assert(mergedWaterStr->stream_len());
  462. return mergedWaterStr;
  463. }
  464. /* ---------------------------------------------------------------------- */
  465. void
  466. merge2waterBase(AMI_STREAM<waterType> *unsortedWaterStr,
  467. AMI_STREAM<direction_type> *dirStr,
  468. AMI_STREAM<elevation_type> *elStr,
  469. AMI_STREAM<waterWindowBaseType> *merge) {
  470. AMI_STREAM<waterType> *waterStr;
  471. FILL_DEBUG cout << "sort waterStr (by ij): ";
  472. waterStr = sort(unsortedWaterStr, ijCmpWaterType());
  473. mergeStreamGridGrid(elStr, dirStr,
  474. nrows, ncols,
  475. waterStr,
  476. directionElevationMerger(),
  477. merge);
  478. delete waterStr;
  479. }
  480. /* ---------------------------------------------------------------------- */
  481. /*
  482. * merge 2 grids and a stream together to form a new grid.
  483. * str should be sorted in ij order
  484. */
  485. template<class T1, class T2, class T3, class T4, class FUN>
  486. void
  487. mergeStreamGridGrid(AMI_STREAM<T1> *grid1,
  488. AMI_STREAM<T2> *grid2,
  489. dimension_type rows, dimension_type cols,
  490. AMI_STREAM<T3> *str,
  491. FUN fo,
  492. AMI_STREAM<T4> *outStream) {
  493. T1 *t1p;
  494. T2 *t2p;
  495. T3 *t3p;
  496. AMI_err aeUpd, ae;
  497. grid1->seek(0);
  498. grid2->seek(0);
  499. str->seek(0);
  500. aeUpd = str->read_item(&t3p);
  501. assert(aeUpd == AMI_ERROR_NO_ERROR || aeUpd == AMI_ERROR_END_OF_STREAM);
  502. for(dimension_type row = 0; row < rows; row++) {
  503. for(dimension_type col = 0; col < cols; col++) {
  504. ae = grid1->read_item(&t1p);
  505. assert(ae == AMI_ERROR_NO_ERROR);
  506. ae = grid2->read_item(&t2p);
  507. assert(ae == AMI_ERROR_NO_ERROR);
  508. T4 t4;
  509. if(aeUpd == AMI_ERROR_NO_ERROR && t3p->i == row && t3p->j == col) {
  510. /* cell present in stream */
  511. t4 = fo(*t1p, *t2p, *t3p);
  512. aeUpd = str->read_item(&t3p);
  513. assert(aeUpd == AMI_ERROR_NO_ERROR ||
  514. aeUpd == AMI_ERROR_END_OF_STREAM);
  515. } else {
  516. /* not in stream */
  517. t4 = fo(*t1p, *t2p);
  518. }
  519. ae = outStream->write_item(t4);
  520. assert(ae == AMI_ERROR_NO_ERROR);
  521. }
  522. /*assert(outStream->stream_len() == (row+1) * cols); */
  523. }
  524. assert(outStream->stream_len() == rows * cols);
  525. return;
  526. }
  527. /* ---------------------------------------------------------------------- */
  528. /* make boundaryStr from labeledWater */
  529. AMI_STREAM<boundaryType> *
  530. findBoundariesMain(AMI_STREAM<labelElevType> *labeledWater) {
  531. AMI_STREAM<boundaryType> *boundaryStr;
  532. Rtimer rt;
  533. rt_start(rt);
  534. boundaryStr = new AMI_STREAM<boundaryType>();
  535. FILL_SAVEALL printGridStream(labeledWater, nrows, ncols,
  536. verbosedir("labels.asc"), printLabel());
  537. findBoundaries(labeledWater, nrows, ncols, boundaryStr);
  538. stats->recordLength("all boundaries", boundaryStr);
  539. FILL_SAVEALL {
  540. FILL_DEBUG cout << "sort boundaryStr (by ij): ";
  541. sort(&boundaryStr, ijCmpBoundaryType());
  542. removeDuplicatesEx(&boundaryStr, ijCmpBoundaryType());
  543. printStream2Grid(boundaryStr, nrows, ncols,
  544. verbosedir("boundary.asc"), boundaryType::print);
  545. }
  546. FILL_DEBUG cout << "sort boundaryStr (by wat label): ";
  547. sort(&boundaryStr, waterCmpBoundaryType());
  548. removeDuplicatesEx(&boundaryStr, boundaryCmpBoundaryType());
  549. rt_stop(rt);
  550. stats->recordTime("generating boundaries", rt);
  551. stats->recordLength("boundary stream", boundaryStr);
  552. /*if(GETOPT("veryfillverbose")) printStream(cout, boundaryStr);
  553. SAVE_ON_OPTION(boundaryStr, "saveBoundaryStream");
  554. */
  555. return boundaryStr;
  556. }