123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- /****************************************************************************
- *
- * 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 <stdlib.h>
- #include <ctype.h>
- #include <time.h>
- #include <string>
- #include "fill.h"
- #include "common.h"
- #include "water.h"
- #include "sortutils.h"
- #include "streamutils.h"
- #include "filldepr.h"
- #include "grid.h"
- /* globals in common.H
- extern statsRecorder *stats; stats file
- extern userOptions *opt; command-line options
- extern struct Cell_head *region; header of the region
- extern dimension_type nrows, ncols;
- */
- #define FILL_DEBUG if(0)
- #define FILL_SAVEALL if(0)
- /* defined in this module */
- void recordWatersheds(AMI_STREAM<labelElevType> *labeledWater);
- long assignDirections(AMI_STREAM<plateauStats> *statstr,
- AMI_STREAM<plateauType> *platstr,
- AMI_STREAM<waterType> *waterstr);
- void assignFinalDirections(AMI_STREAM<plateauStats> *statstr,
- AMI_STREAM<plateauType> *platstr,
- AMI_STREAM<waterType> *waterstr);
- template<class T1, class T2, class T3, class T4, class FUN>
- void mergeStreamGridGrid(AMI_STREAM<T1> *grid1,
- AMI_STREAM<T2> *grid2,
- dimension_type rows, dimension_type cols,
- AMI_STREAM<T3> *str,
- FUN fo,
- AMI_STREAM<T4> *outStream);
- void merge2waterBase(AMI_STREAM<waterType> *unsortedWaterStr,
- AMI_STREAM<direction_type> *dirStr,
- AMI_STREAM<elevation_type> *elStr,
- AMI_STREAM<waterWindowBaseType> *merge);
- AMI_STREAM<waterGridType>*
- merge2waterGrid(AMI_STREAM<waterType> *unsortedWaterStr,
- AMI_STREAM<direction_type> *dirStr,
- AMI_STREAM<elevation_type> *elStr);
- AMI_STREAM<boundaryType> *
- findBoundariesMain(AMI_STREAM<labelElevType> *labeledWater);
-
- /* ********************************************************************** */
- /* some helper classes */
- /* ********************************************************************** */
- class printElevation {
- public:
- char *operator()(const elevation_type &p) {
- static char buf[20];
- sprintf(buf, "%.1f", (float)p);
- return buf;
- }
- };
- class printDirection {
- public:
- char *operator()(const direction_type &p) {
- static char buf[20];
- sprintf(buf, "%3d", p);
- return buf;
- }
- char *operator()(const waterWindowBaseType &p) {
- static char buf[20];
- sprintf(buf, "%3d", p.dir);
- return buf;
- }
- #if(0)
- char *operator()(const waterWindowBaseType &p) {
- static char buf[3];
- buf[0] = directionSymbol(p.dir);
- buf[1] = '\0';
- return buf;
- }
- #endif
- };
- class printLabel {
- public:
- char *operator()(const labelElevType&p) {
- static char buf[8];
- sprintf(buf, CCLABEL_FMT, p.getLabel());
- return buf;
- }
- char *operator()(const waterGridType &p) {
- static char buf[8];
- sprintf(buf, CCLABEL_FMT, p.getLabel());
- return buf;
- }
- char *operator()(const waterType &p) {
- static char buf[8];
- sprintf(buf, CCLABEL_FMT, p.getLabel());
- return buf;
- }
- };
- class printDepth {
- public:
- char *operator()(const waterGridType &p) {
- static char buf[3];
- sprintf(buf, "%1u", p.depth);
- return buf;
- }
- };
- char *
- verbosedir(std::string s) {
- static char buf[BUFSIZ];
- sprintf(buf, "dump/%s", s.c_str());
- return buf;
- }
- /* ---------------------------------------------------------------------- */
- /* fill the terrain (if necessary) and compute flow direction stream;
- elstr must exist and contain grid data before call, filledstr and
- dirstr are created; elstr is deleted and replaced with the
- classified elstr, which has boundary nodata distinguished from
- inner nodata */
- AMI_STREAM<waterWindowBaseType>*
- computeFlowDirections(AMI_STREAM<elevation_type>*& elstr,
- AMI_STREAM<elevation_type>*& filledstr,
- AMI_STREAM<direction_type>*& dirstr,
- AMI_STREAM<labelElevType> *& labeledWater) {
- Rtimer rt, rtTotal;
- AMI_STREAM<elevation_type> *elstr_reclass=NULL;
- AMI_STREAM<ElevationWindow > *winstr=NULL;
- AMI_STREAM<plateauStats> *statstr=NULL;
- AMI_STREAM<plateauType> *platstr=NULL;
- AMI_STREAM<waterType> *waterstr=NULL;
- AMI_STREAM<waterGridType> *mergedWaterStr=NULL;
- AMI_STREAM<boundaryType> *boundaryStr=NULL;
- AMI_STREAM<waterWindowType> *waterWindows=NULL;
-
- rt_start(rtTotal);
- assert(elstr && filledstr == NULL && dirstr == NULL && labeledWater == NULL);
- if (stats) {
- stats->comment("------------------------------");
- stats->comment("COMPUTING FLOW DIRECTIONS");
-
- /* adjust nodata -- boundary nodata distinguished from inner
- nodata */
- stats->comment("classifying nodata (inner & boundary)");
- }
-
- elstr_reclass = classifyNodata(elstr);
- delete elstr;
- elstr = elstr_reclass;
-
- /* ---------------------------------------------------------------------- */
- /* find the plateaus. */
- /* ---------------------------------------------------------------------- */
- if (stats) {
- stats->comment("----------", opt->verbose);
- stats->comment("assigning preliminary directions");
- }
-
- rt_start(rt);
- dirstr = new AMI_STREAM<direction_type>;
- winstr = new AMI_STREAM<ElevationWindow>();
- statstr = new AMI_STREAM<plateauStats>;
-
- platstr = findPlateaus(elstr, nrows, ncols, nodataType::ELEVATION_NODATA,
- winstr, dirstr, statstr);
-
- delete winstr; /* not used; not made */
- rt_stop(rt);
-
- if (stats) {
- stats->recordTime("findingPlateaus", rt);
- stats->recordLength("plateaus", platstr);
- stats->recordLength("plateau stats", statstr);
- }
- FILL_SAVEALL {
- /* printStream(*stats, statstr); */
- FILL_DEBUG cout << "sort plateauStr (by ij): ";
- AMI_STREAM<plateauType> *tmp = sort(platstr, ijCmpPlateauType());
- printStream2Grid(tmp, nrows, ncols,
- verbosedir("label1.asc"), plateauType::printLabel);
- delete tmp;
- }
-
- /* ---------------------------------------------------------------------- */
- /* assign labels and directions & BFS ordering. depressions have
- labels, but no direction information.
- */
- /* ---------------------------------------------------------------------- */
- rt_start(rt);
- waterstr = new AMI_STREAM<waterType>();
- assignDirections(statstr, platstr, waterstr);
- delete platstr;
- delete statstr;
- rt_stop(rt);
- if (stats) {
- stats->recordTime("assigning directions", rt);
- *stats << "maxWatershedCount=" << labelFactory::getLabelCount() << endl;
- }
-
- rt_start(rt);
- mergedWaterStr = merge2waterGrid(waterstr, dirstr, elstr);
- delete dirstr;
- delete waterstr;
- rt_stop(rt);
- if (stats) {
- stats->recordTime("merging", rt);
- stats->recordLength("mergedWaterStr", mergedWaterStr);
- }
- /* ---------------------------------------------------------------------- */
- /* watershed analysis */
- /* IN: mergedWaterStr, labelFactory::... */
- /* ---------------------------------------------------------------------- */
- if (stats) {
- stats->comment("--------------", opt->verbose);
- stats->comment("generating watersheds and watershed graph");
- }
- rt_start(rt);
- waterWindows = new AMI_STREAM<waterWindowType>();
- createWaterWindows(mergedWaterStr, nrows, ncols, waterWindows);
- delete mergedWaterStr;
- rt_stop(rt);
- if (stats) {
- stats->recordTime("creating windows", rt);
- stats->recordLength("waterWindows", waterWindows);
- }
- /* ---------------------------------------------------------------------- */
- rt_start(rt);
- labeledWater = new AMI_STREAM<labelElevType>();
- boundaryStr = new AMI_STREAM<boundaryType>();
- generateWatersheds(&waterWindows, nrows, ncols, labeledWater, boundaryStr);
- /* do we need to make boundaries here?? */
- delete waterWindows;
- /* cout << "bogus boundary length = " << boundaryStr->stream_len() << endl;*/
- assert(boundaryStr->stream_len() == 0);
- delete boundaryStr;
-
- assert(labeledWater->stream_len() == nrows * ncols);
- rt_stop(rt);
- if (stats)
- stats->recordTime("generating watersheds", rt);
-
- /* ---------------------------------------------------------------------- */
- /* find boundaries */
- /* ---------------------------------------------------------------------- */
- FILL_DEBUG cerr << "sort labeledWater (by ij):";
- sort(&labeledWater, ijCmpLabelElevType());
- #ifdef SAVE_ASCII
- cerr << "saving WATERSHED grid as watershed_grid\n";
- printStream2Grid(labeledWater, nrows, ncols,
- "watershed.asc", labelElevType::printLabel);
- #endif
- boundaryStr = findBoundariesMain(labeledWater);
-
-
- /* ---------------------------------------------------------------------- */
- /* filling */
- /* valid streams are: boundaryStr, labeledWater */
- /* ---------------------------------------------------------------------- */
- rt_start(rt);
- elevation_type *raise;
- /*find the raise elevations */
-
- FILL_DEBUG cerr << "sort boundaryStr (by elev): ";
- sort(&boundaryStr, elevCmpBoundaryType());
- raise = fill_depression(boundaryStr, labelFactory::getLabelCount());
- delete boundaryStr;
- rt_stop(rt);
- if (stats)
- stats->recordTime("filling depressions", rt);
-
- /*fill the terrain*/
- rt_start(rt);
- filledstr = new AMI_STREAM<elevation_type>();
- commit_fill(labeledWater, raise, labelFactory::getLabelCount(), filledstr);
- assert(filledstr->stream_len() == nrows * ncols);
- delete [] raise;
- rt_stop(rt);
- if (stats) {
- stats->recordTime("updating filled grid", rt);
- stats->recordLength("filledstr", filledstr);
- /* ---------------------------------------------------------------------- */
- /* find plateaus again and reassign directions */
- /* ---------------------------------------------------------------------- */
- stats->comment("------------------------------");
- stats->comment("REASSIGNING DIRECTIONS");
- }
- rt_start(rt);
- winstr = NULL;
- dirstr = new AMI_STREAM<direction_type>();
- statstr = new AMI_STREAM<plateauStats>();
- platstr = findPlateaus(filledstr, nrows, ncols, nodataType::ELEVATION_NODATA,
- winstr, dirstr, statstr);
- rt_stop(rt);
- if (stats) {
- stats->recordTime("findingPlateaus2", rt);
- stats->recordLength("final plateaus", platstr);
- stats->recordLength("final plateau stats", statstr);
- }
- FILL_SAVEALL {
- FILL_DEBUG cout << "sort plateauStr (by ij): ";
- AMI_STREAM<plateauType> *tmp = sort(platstr, ijCmpPlateauType());
- printStream2Grid(tmp, nrows, ncols,
- verbosedir("plateaus.asc"), plateauType::printLabel);
- delete tmp;
- }
- /* assign final directions */
- rt_start(rt);
- waterstr = new AMI_STREAM<waterType>();
- long dc = assignDirections(statstr, platstr, waterstr);
- if(dc && stats) {
- *stats << "WARNING: " << dc << " depressions (islands) detected\n";
- }
- delete platstr;
- delete statstr;
- rt_stop(rt);
- if (stats)
- stats->recordTime("final directions", rt);
-
- /* merge */
- rt_start(rt);
- AMI_STREAM<waterWindowBaseType> *flowStream;
- /*STREAM_TO_OPTION(flowStream, "flowStream");*/
- char path[BUFSIZ];
- char* base_dir = getenv(STREAM_TMPDIR);
- assert(base_dir);
- sprintf(path, "%s/flowStream", base_dir);
- flowStream = new AMI_STREAM<waterWindowBaseType>(path);
- /*flowStream->persist(PERSIST_PERSISTENT); */
- if (stats)
- stats->comment("creating flowStream: ", flowStream->sprint());
- merge2waterBase(waterstr, dirstr, filledstr, flowStream);
- delete waterstr;
- rt_stop(rt);
- if (stats)
- stats->recordTime("merge water,dir,elev to flow", rt);
- rt_stop(rtTotal);
-
- #ifdef SAVE_ASCII
- /*write grids as ascii file */
- printGridStream(filledstr, nrows, ncols,
- "filled_elev.asc", printElevation());
- printGridStream(flowStream, nrows, ncols,
- "direction.asc", printDirection());
- #endif
-
- if (stats) {
- stats->recordTime("Total compute flow direction running time", rtTotal);
- stats->comment("compute flow directions done.");
- }
-
- return flowStream;
- }
- /* ---------------------------------------------------------------------- */
- void
- recordWatersheds(AMI_STREAM<labelElevType> *labeledWater) {
- AMI_err ae;
- long labelCount = 0;
- AMI_STREAM<labelElevType> *tmp;
- if (stats)
- *stats << "--- watershed stats" << endl;
- FILL_DEBUG cout << "sort labeledWater (by wat label): ";
- tmp = sort(labeledWater, labelCmpLabelElevType());
- labelElevType *p;
- cclabel_type prev(LABEL_UNDEF);
- tmp->seek(0);
- while((ae = tmp->read_item(&p)) == AMI_ERROR_NO_ERROR) {
- if(p->getLabel() != prev) {
- labelCount++;
- prev = p->getLabel();
- }
- }
- assert(ae == AMI_ERROR_END_OF_STREAM);
- if (stats) {
- *stats << "watershed count = " << labelCount << endl;
- *stats << "---" << endl;
- stats->flush();
- }
- delete tmp;
- }
- /* ********************************************************************** */
- /* assign directions to plateaus that have sinks;
- * reassign labels to depressions (don't drain out).
- * all plateaus are written out to the waterstr. */
- long
- assignDirections(AMI_STREAM<plateauStats> *statstr,
- AMI_STREAM<plateauType> *platstr,
- AMI_STREAM<waterType> *waterstr) {
- size_t fmem;
- AMI_err ae;
- plateauStats *ps;
- if (stats) {
- stats->comment("----------", opt->verbose);
- stats->comment("assigning directions on plateaus");
- }
-
- labelFactory::reset(); /* we are relabeling now */
-
- statstr->seek(0);
- platstr->seek(0);
- fmem = getAvailableMemory();
- long depressionCount=0;
- long spillCount=0;
- while((ae = statstr->read_item(&ps)) == AMI_ERROR_NO_ERROR) {
- if(ps->size*sizeof(gridElement) > fmem) {
- cerr << "WARNING: grid larger than memory (ignored)" << endl;
- }
- assert(ps->label != LABEL_NODATA);
- if(ps->hasSpill) {
- spillCount++;
- grid *platGrid = new grid(ps->iMin, ps->jMin, ps->iMax, ps->jMax,
- ps->size, ps->label);
- platGrid->load(*platstr);
- platGrid->assignDirections(opt->d8 ? 1 : 0);
- platGrid->save(*waterstr); /* this doesn't save labels */
- delete platGrid;
- } else {
- /* depression - just give contiguous labels only */
- depressionCount++;
- cclabel_type label = labelFactory::getNewLabel();
- for(int i=0; i<ps->size; i++) {
- plateauType *pt;
- platstr->read_item(&pt);
- pt->cclabel = label; /* assign new label */
- waterType wt(*pt); /* write it out */
- ae = waterstr->write_item(wt);
- assert(ae == AMI_ERROR_NO_ERROR);
- }
- }
- }
- if (stats) {
- *stats << "depression count = " << depressionCount << endl;
- *stats << "spill count = " << spillCount << endl;
- }
- return depressionCount;
- }
- /* ********************************************************************** */
- /* assign directions to plateaus that have sinks;
- * check that there are no depressions.
- */
- void
- assignFinalDirections(AMI_STREAM<plateauStats> *statstr,
- AMI_STREAM<plateauType> *platstr,
- AMI_STREAM<waterType> *waterstr) {
- AMI_err ae;
- plateauStats *ps;
- if (stats)
- stats->comment("assigning final directions");
- statstr->seek(0);
- platstr->seek(0);
- while((ae = statstr->read_item(&ps)) == AMI_ERROR_NO_ERROR) {
-
- if(ps->hasSpill) {
- grid *platGrid = new grid(ps->iMin, ps->jMin, ps->iMax, ps->jMax,
- ps->size, ps->label);
- platGrid->load(*platstr);
- platGrid->assignDirections(opt->d8 ? 1 : 0);
- platGrid->save(*waterstr); /* this doesn't save labels */
- delete platGrid;
- } else {
- /* could be legitimate */
- cerr << "WARNING: depression detected: " << *ps << endl;
- continue;
- }
- }
- };
- /* ********************************************************************** */
- class directionElevationMerger {
- public:
- waterGridType operator()(elevation_type el, direction_type dir,
- waterType p) {
- /* check that no (boundary) nodata values got in here */
- assert(el != nodataType::ELEVATION_BOUNDARY);
- assert(!is_nodata(el)); /* p should be a valid grid cell */
- return waterGridType(el, p.getDirection(), p.getLabel(), p.getDepth());
- }
- waterGridType operator()(elevation_type el, direction_type dir) {
- waterGridType wg(el, dir);
- if(el == nodataType::ELEVATION_BOUNDARY) { /* hack XXX (approved) */
- wg.setLabel(LABEL_BOUNDARY);
- }
- /* nodata => boundary or undef */
- assert(!is_nodata(el) ||
- (wg.getLabel()==LABEL_BOUNDARY || wg.getLabel()==LABEL_UNDEF));
- return wg;
- }
- };
- /* ---------------------------------------------------------------------- */
- AMI_STREAM<waterGridType> *
- merge2waterGrid(AMI_STREAM<waterType> *unsortedWaterStr,
- AMI_STREAM<direction_type> *dirStr,
- AMI_STREAM<elevation_type> *elStr) {
- AMI_STREAM<waterType> *waterStr;
-
-
- FILL_DEBUG cout << "sort waterStr (by ij): ";
- waterStr = sort(unsortedWaterStr, ijCmpWaterType());
- FILL_SAVEALL printStream2Grid(waterStr, nrows, ncols,
- verbosedir("platlabels.asc"), printLabel());
-
- AMI_STREAM<waterGridType> *mergedWaterStr = new AMI_STREAM<waterGridType>();
- mergeStreamGridGrid(elStr, dirStr,
- nrows, ncols,
- waterStr,
- directionElevationMerger(),
- mergedWaterStr);
- delete waterStr;
- FILL_SAVEALL printGridStream(mergedWaterStr, nrows, ncols,
- verbosedir("mergedlabels.asc"), printLabel());
- assert(mergedWaterStr->stream_len());
- return mergedWaterStr;
- }
- /* ---------------------------------------------------------------------- */
- void
- merge2waterBase(AMI_STREAM<waterType> *unsortedWaterStr,
- AMI_STREAM<direction_type> *dirStr,
- AMI_STREAM<elevation_type> *elStr,
- AMI_STREAM<waterWindowBaseType> *merge) {
- AMI_STREAM<waterType> *waterStr;
- FILL_DEBUG cout << "sort waterStr (by ij): ";
- waterStr = sort(unsortedWaterStr, ijCmpWaterType());
- mergeStreamGridGrid(elStr, dirStr,
- nrows, ncols,
- waterStr,
- directionElevationMerger(),
- merge);
- delete waterStr;
- }
- /* ---------------------------------------------------------------------- */
- /*
- * merge 2 grids and a stream together to form a new grid.
- * str should be sorted in ij order
- */
- template<class T1, class T2, class T3, class T4, class FUN>
- void
- mergeStreamGridGrid(AMI_STREAM<T1> *grid1,
- AMI_STREAM<T2> *grid2,
- dimension_type rows, dimension_type cols,
- AMI_STREAM<T3> *str,
- FUN fo,
- AMI_STREAM<T4> *outStream) {
- T1 *t1p;
- T2 *t2p;
- T3 *t3p;
- AMI_err aeUpd, ae;
-
- grid1->seek(0);
- grid2->seek(0);
- str->seek(0);
- aeUpd = str->read_item(&t3p);
- assert(aeUpd == AMI_ERROR_NO_ERROR || aeUpd == AMI_ERROR_END_OF_STREAM);
- for(dimension_type row = 0; row < rows; row++) {
- for(dimension_type col = 0; col < cols; col++) {
- ae = grid1->read_item(&t1p);
- assert(ae == AMI_ERROR_NO_ERROR);
- ae = grid2->read_item(&t2p);
- assert(ae == AMI_ERROR_NO_ERROR);
-
- T4 t4;
- if(aeUpd == AMI_ERROR_NO_ERROR && t3p->i == row && t3p->j == col) {
- /* cell present in stream */
- t4 = fo(*t1p, *t2p, *t3p);
- aeUpd = str->read_item(&t3p);
- assert(aeUpd == AMI_ERROR_NO_ERROR ||
- aeUpd == AMI_ERROR_END_OF_STREAM);
- } else {
- /* not in stream */
- t4 = fo(*t1p, *t2p);
- }
- ae = outStream->write_item(t4);
- assert(ae == AMI_ERROR_NO_ERROR);
- }
- /*assert(outStream->stream_len() == (row+1) * cols); */
- }
- assert(outStream->stream_len() == rows * cols);
- return;
- }
- /* ---------------------------------------------------------------------- */
- /* make boundaryStr from labeledWater */
- AMI_STREAM<boundaryType> *
- findBoundariesMain(AMI_STREAM<labelElevType> *labeledWater) {
- AMI_STREAM<boundaryType> *boundaryStr;
- Rtimer rt;
-
- rt_start(rt);
- boundaryStr = new AMI_STREAM<boundaryType>();
- FILL_SAVEALL printGridStream(labeledWater, nrows, ncols,
- verbosedir("labels.asc"), printLabel());
-
- findBoundaries(labeledWater, nrows, ncols, boundaryStr);
- if (stats)
- stats->recordLength("all boundaries", boundaryStr);
-
- FILL_SAVEALL {
- FILL_DEBUG cout << "sort boundaryStr (by ij): ";
- sort(&boundaryStr, ijCmpBoundaryType());
- removeDuplicatesEx(&boundaryStr, ijCmpBoundaryType());
- printStream2Grid(boundaryStr, nrows, ncols,
- verbosedir("boundary.asc"), boundaryType::print);
- }
- FILL_DEBUG cout << "sort boundaryStr (by wat label): ";
- sort(&boundaryStr, waterCmpBoundaryType());
- removeDuplicatesEx(&boundaryStr, boundaryCmpBoundaryType());
-
- rt_stop(rt);
- if (stats) {
- stats->recordTime("generating boundaries", rt);
- stats->recordLength("boundary stream", boundaryStr);
- }
-
- /*if(GETOPT("veryfillverbose")) printStream(cout, boundaryStr);
- SAVE_ON_OPTION(boundaryStr, "saveBoundaryStream");
- */
- return boundaryStr;
- }
|