pktools  2.6.3
Processing Kernel for geospatial data
pklas2img.cc
1 /**********************************************************************
2 pklas2img.cc: Rasterize LAS/LAZ point clouds with filtering/compositing options
3 Copyright (C) 2008-2014 Pieter Kempeneers
4 
5 This file is part of pktools
6 
7 pktools 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 3 of the License, or
10 (at your option) any later version.
11 
12 pktools 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 You should have received a copy of the GNU General Public License
18 along with pktools. If not, see <http://www.gnu.org/licenses/>.
19 ***********************************************************************/
20 #include <iostream>
21 #include "Optionpk.h"
22 #include "imageclasses/ImgReaderGdal.h"
23 #include "imageclasses/ImgWriterGdal.h"
24 #include "imageclasses/ImgReaderOgr.h"
25 #include "lasclasses/FileReaderLas.h"
26 #include "algorithms/StatFactory.h"
27 #include "algorithms/Filter2d.h"
28 
29 using namespace std;
30 
31 int main(int argc,char **argv) {
32  Optionpk<string> input_opt("i", "input", "Input las file");
33  Optionpk<string> attribute_opt("n", "name", "names of the attribute to select: intensity, return, nreturn, z", "z");
34  // Optionpk<bool> disc_opt("circ", "circular", "circular disc kernel for dilation and erosion", false);
35  // Optionpk<double> maxSlope_opt("s", "maxSlope", "Maximum slope used for morphological filtering", 0.0);
36  // Optionpk<double> hThreshold_opt("ht", "maxHeight", "initial and maximum height threshold for progressive morphological filtering (e.g., -ht 0.2 -ht 2.5)", 0.2);
37  // Optionpk<short> maxIter_opt("maxit", "maxit", "Maximum number of iterations in post filter", 5);
38  Optionpk<unsigned short> returns_opt("ret", "ret", "number(s) of returns to include");
39  Optionpk<unsigned short> classes_opt("class", "class", "classes to keep: 0 (created, never classified), 1 (unclassified), 2 (ground), 3 (low vegetation), 4 (medium vegetation), 5 (high vegetation), 6 (building), 7 (low point, noise), 8 (model key-point), 9 (water), 10 (reserved), 11 (reserved), 12 (overlap)");
40  Optionpk<string> composite_opt("comp", "comp", "composite for multiple points in cell (min, max, median, mean, sum, first, last, profile (percentile height values), percentile, number (point density)). Last: overwrite cells with latest point", "last");
41  Optionpk<string> filter_opt("fir", "filter", "filter las points (first,last,single,multiple,all).", "all");
42  // Optionpk<string> postFilter_opt("pf", "pfilter", "post processing filter (etew_min,promorph (progressive morphological filter),bunting (adapted promorph),open,close,none).", "none");
43  // Optionpk<short> dimx_opt("dimx", "dimx", "Dimension X of postFilter", 3);
44  // Optionpk<short> dimy_opt("dimy", "dimy", "Dimension Y of postFilter", 3);
45  Optionpk<string> output_opt("o", "output", "Output image file");
46  Optionpk<string> projection_opt("a_srs", "a_srs", "assign the projection for the output file in epsg code, e.g., epsg:3035 for European LAEA projection");
47  Optionpk<double> ulx_opt("ulx", "ulx", "Upper left x value bounding box (in geocoordinates if georef is true). 0 is read from input file", 0.0);
48  Optionpk<double> uly_opt("uly", "uly", "Upper left y value bounding box (in geocoordinates if georef is true). 0 is read from input file", 0.0);
49  Optionpk<double> lrx_opt("lrx", "lrx", "Lower right x value bounding box (in geocoordinates if georef is true). 0 is read from input file", 0.0);
50  Optionpk<double> lry_opt("lry", "lry", "Lower right y value bounding box (in geocoordinates if georef is true). 0 is read from input file", 0.0);
51  Optionpk<string> otype_opt("ot", "otype", "Data type for output image ({Byte/Int16/UInt16/UInt32/Int32/Float32/Float64/CInt16/CInt32/CFloat32/CFloat64}). Empty string: inherit type from input image", "Byte");
52  Optionpk<string> oformat_opt("of", "oformat", "Output image format (see also gdal_translate). Empty string: inherit from input image", "GTiff");
53  Optionpk<double> dx_opt("dx", "dx", "Output resolution in x (in meter)", 1.0);
54  Optionpk<double> dy_opt("dy", "dy", "Output resolution in y (in meter)", 1.0);
55  Optionpk<short> nbin_opt("nbin", "nbin", "Number of percentile bins for calculating percentile height value profile (=number of output bands)", 10.0);
56  Optionpk<double> percentile_opt("perc","perc","Percentile value used for rule percentile",95);
57  Optionpk<short> nodata_opt("nodata", "nodata", "nodata value to put in image", 0);
58  Optionpk<string> option_opt("co", "co", "Creation option for output file. Multiple options can be specified.");
59  Optionpk<string> colorTable_opt("ct", "ct", "color table (file with 5 columns: id R G B ALFA (0: transparent, 255: solid)");
60  Optionpk<short> verbose_opt("v", "verbose", "verbose mode", 0,2);
61 
62  nbin_opt.setHide(1);
63  percentile_opt.setHide(1);
64  nodata_opt.setHide(1);
65  option_opt.setHide(1);
66  colorTable_opt.setHide(1);
67 
68  bool doProcess;//stop process when program was invoked with help option (-h --help)
69  try{
70  doProcess=input_opt.retrieveOption(argc,argv);
71  attribute_opt.retrieveOption(argc,argv);
72  returns_opt.retrieveOption(argc,argv);
73  classes_opt.retrieveOption(argc,argv);
74  composite_opt.retrieveOption(argc,argv);
75  filter_opt.retrieveOption(argc,argv);
76  output_opt.retrieveOption(argc,argv);
77  projection_opt.retrieveOption(argc,argv);
78  ulx_opt.retrieveOption(argc,argv);
79  uly_opt.retrieveOption(argc,argv);
80  lrx_opt.retrieveOption(argc,argv);
81  lry_opt.retrieveOption(argc,argv);
82  otype_opt.retrieveOption(argc,argv);
83  oformat_opt.retrieveOption(argc,argv);
84  dx_opt.retrieveOption(argc,argv);
85  dy_opt.retrieveOption(argc,argv);
86  nbin_opt.retrieveOption(argc,argv);
87  percentile_opt.retrieveOption(argc,argv);
88  nodata_opt.retrieveOption(argc,argv);
89  option_opt.retrieveOption(argc,argv);
90  colorTable_opt.retrieveOption(argc,argv);
91  verbose_opt.retrieveOption(argc,argv);
92  }
93  catch(string predefinedString){
94  std::cout << predefinedString << std::endl;
95  exit(0);
96  }
97 
98  if(!doProcess){
99  cout << endl;
100  cout << "pklas2img -i lasfile -o output" << endl;
101  cout << endl;
102  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
103  exit(0);//help was invoked, stop processing
104  }
105  //todo: is this needed?
106  GDALAllRegister();
107 
108  double dfComplete=0.0;
109  const char* pszMessage;
110  void* pProgressArg=NULL;
111  GDALProgressFunc pfnProgress=GDALTermProgress;
112  double progress=0;
113 
114  Vector2d<vector<double> > inputData;//row,col,point
115 
116 
117  ImgReaderGdal maskReader;
118  ImgWriterGdal outputWriter;
119  GDALDataType theType=GDT_Unknown;
120  if(verbose_opt[0])
121  cout << "possible output data types: ";
122  for(int iType = 0; iType < GDT_TypeCount; ++iType){
123  if(verbose_opt[0])
124  cout << " " << GDALGetDataTypeName((GDALDataType)iType);
125  if( GDALGetDataTypeName((GDALDataType)iType) != NULL
126  && EQUAL(GDALGetDataTypeName((GDALDataType)iType),
127  otype_opt[0].c_str()))
128  theType=(GDALDataType) iType;
129  }
130  if(verbose_opt[0]){
131  if(theType==GDT_Unknown)
132  cout << "Unknown output pixel type: " << otype_opt[0] << endl;
133  else
134  cout << "Output pixel type: " << GDALGetDataTypeName(theType) << endl;
135  }
136 
137  double maxLRX=0;
138  double maxULY=0;
139  double minULX=0;
140  double minLRY=0;
141 
142  unsigned long int totalPoints=0;
143  unsigned long int nPoints=0;
144  unsigned long int ipoint=0;
145  for(int iinput=0;iinput<input_opt.size();++iinput){
146  // assert(input_opt[iinput].find(".las")!=string::npos);
147  FileReaderLas lasReader;
148  try{
149  lasReader.open(input_opt[iinput]);
150  }
151  catch(string errorString){
152  cerr << errorString << endl;
153  exit(1);
154  }
155  catch(...){
156  cerr << "Error opening input " << input_opt[iinput] << endl;
157  exit(2);
158  }
159  nPoints=lasReader.getPointCount();
160  totalPoints+=nPoints;
161 
162  if(ulx_opt[0]>=lrx_opt[0]||uly_opt[0]<=lry_opt[0]){
163  double ulx,uly,lrx,lry;
164  lasReader.getExtent(ulx,uly,lrx,lry);
165  lrx+=dx_opt[0];//pixel coordinates are referenced to upper left corner (las coordinates are centres)
166  lry-=dy_opt[0];//pixel coordinates are referenced to upper left corner (las coordinates are centres)
167  if(ulx>=lrx){
168  ulx=ulx-dx_opt[0]/2.0;
169  lrx=ulx+dx_opt[0]/2.0;
170  }
171  if(uly<=lry){
172  uly=lry+dy_opt[0]/2.0;
173  lry=lry-dy_opt[0]/2.0;
174  }
175  if(maxLRX>minULX){
176  maxLRX=(lrx>maxLRX)?lrx:maxLRX;
177  maxULY=(uly>maxULY)?uly:maxULY;
178  minULX=(ulx<minULX)?ulx:minULX;
179  minLRY=(lry<minLRY)?lry:minLRY;
180  }
181  else{//initialize
182  maxLRX=lrx;
183  maxULY=uly;
184  minULX=ulx;
185  minLRY=lry;
186  }
187  }
188  else{
189  maxLRX=lrx_opt[0];
190  maxULY=uly_opt[0];
191  minULX=ulx_opt[0];
192  minLRY=lry_opt[0];
193  }
194  lasReader.close();
195  }
196  if(verbose_opt[0]){
197  std::cout << setprecision(12) << "--ulx=" << minULX << " --uly=" << maxULY << " --lrx=" << maxLRX << " --lry=" << minLRY << std::endl;
198  std::cout << "total number of points before filtering: " << totalPoints << std::endl;
199  std::cout << "filter set to " << filter_opt[0] << std::endl;
200  // std::cout << "postFilter set to " << postFilter_opt[0] << std::endl;
201  }
202  int ncol=ceil(maxLRX-minULX)/dx_opt[0];//number of columns in outputGrid
203  int nrow=ceil(maxULY-minLRY)/dy_opt[0];//number of rows in outputGrid
204  //todo: multiple bands
205  int nband=(composite_opt[0]=="profile")? nbin_opt[0] : 1;
206  if(!output_opt.size()){
207  cerr << "Error: no output file defined" << endl;
208  exit(1);
209  }
210  if(verbose_opt[0])
211  cout << "opening output file " << output_opt[0] << endl;
212  outputWriter.open(output_opt[0],ncol,nrow,nband,theType,oformat_opt[0],option_opt);
213  outputWriter.GDALSetNoDataValue(nodata_opt[0]);
214  //set projection
215  double gt[6];
216  gt[0]=minULX;
217  gt[1]=dx_opt[0];
218  gt[2]=0;
219  gt[3]=maxULY;
220  gt[4]=0;
221  gt[5]=-dy_opt[0];
222  outputWriter.setGeoTransform(gt);
223  if(projection_opt.size()){
224  string projectionString=outputWriter.setProjectionProj4(projection_opt[0]);
225  if(verbose_opt[0])
226  cout << "projection: " << projectionString << endl;
227  }
228  if(!outputWriter.isGeoRef())
229  cout << "Warning: output image " << output_opt[0] << " is not georeferenced!" << endl;
230  if(colorTable_opt.size())
231  outputWriter.setColorTable(colorTable_opt[0]);
232 
233  inputData.clear();
234  inputData.resize(nrow,ncol);
235  Vector2d<double> outputData(nrow,ncol);
236  for(int irow=0;irow<nrow;++irow)
237  for(int icol=0;icol<ncol;++icol)
238  outputData[irow][icol]=0;
239 
240  cout << "Reading " << input_opt.size() << " point cloud files" << endl;
241  pfnProgress(progress,pszMessage,pProgressArg);
242  for(int iinput=0;iinput<input_opt.size();++iinput){
243  FileReaderLas lasReader;
244  try{
245  lasReader.open(input_opt[iinput]);
246  }
247  catch(string errorString){
248  cout << errorString << endl;
249  exit(1);
250  }
251  if(verbose_opt[0]){
252  if(lasReader.isCompressed())
253  cout << "Reading compressed point cloud " << input_opt[iinput]<< endl;
254  else
255  cout << "Reading uncompressed point cloud " << input_opt[iinput] << endl;
256  }
257  //set bounding filter
258  // lasReader.addBoundsFilter(minULX,maxULY,maxLRX,minLRY);
259  //set returns filter
260  if(returns_opt.size())
261  lasReader.addReturnsFilter(returns_opt);
262  if(classes_opt.size())
263  lasReader.addClassFilter(classes_opt);
264  lasReader.setFilters();
265 
266  if(attribute_opt[0]!="z"){
267  vector<boost::uint16_t> returnsVector;
268  vector<string>::iterator ait=attribute_opt.begin();
269  while(ait!=attribute_opt.end()){
270  if(*ait=="intensity"){
271  if(verbose_opt[0])
272  std::cout << "writing intensity" << std::endl;
273  ++ait;
274  }
275  else if(*ait=="return"){
276  if(verbose_opt[0])
277  std::cout << "writing return number" << std::endl;
278  ++ait;
279  }
280  else if(*ait=="nreturn"){
281  if(verbose_opt[0])
282  std::cout << "writing number of returns" << std::endl;
283  ++ait;
284  }
285  else
286  attribute_opt.erase(ait);
287  }
288  }
289  liblas::Point thePoint(&(lasReader.getHeader()));
290  while(lasReader.readNextPoint(thePoint)){
291  progress=static_cast<float>(ipoint)/totalPoints;
292  pfnProgress(progress,pszMessage,pProgressArg);
293  if(verbose_opt[0]>1)
294  cout << "reading point " << ipoint << endl;
295  if(thePoint.GetX()<minULX||thePoint.GetX()>=maxLRX||thePoint.GetY()>=maxULY||thePoint.GetY()<minLRY)
296  continue;
297  if((filter_opt[0]=="single")&&(thePoint.GetNumberOfReturns()!=1))
298  continue;
299  if((filter_opt[0]=="multiple")&&(thePoint.GetNumberOfReturns()<2))
300  continue;
301  if((filter_opt[0]=="last")&&(thePoint.GetReturnNumber()!=thePoint.GetNumberOfReturns()))
302  continue;
303  if((filter_opt[0]=="first")&&(thePoint.GetReturnNumber()!=1))
304  continue;
305  double dcol,drow;
306  outputWriter.geo2image(thePoint.GetX(),thePoint.GetY(),dcol,drow);
307  int icol=static_cast<int>(dcol);
308  int irow=static_cast<int>(drow);
309  if(irow<0||irow>=nrow){
310  // //test
311  // cout << "Error: thePoint.GetX(),thePoint.GetY(),dcol,drow" << thePoint.GetX() << ", " << thePoint.GetY() << ", " << dcol << ", " << drow << endl;
312  continue;
313  }
314  if(icol<0||icol>=ncol){
315  // //test
316  // cout << "Error: thePoint.GetX(),thePoint.GetY(),dcol,drow" << thePoint.GetX() << ", " << thePoint.GetY() << ", " << dcol << ", " << drow << endl;
317  continue;
318  }
319  assert(irow>=0);
320  assert(irow<nrow);
321  assert(icol>=0);
322  assert(icol<ncol);
323  if(composite_opt[0]=="number")
324  outputData[irow][icol]+=1;
325  else if(attribute_opt[0]=="z")
326  inputData[irow][icol].push_back(thePoint.GetZ());
327  else if(attribute_opt[0]=="intensity")
328  inputData[irow][icol].push_back(thePoint.GetIntensity());
329  else if(attribute_opt[0]=="return")
330  inputData[irow][icol].push_back(thePoint.GetReturnNumber());
331  else if(attribute_opt[0]=="nreturn")
332  inputData[irow][icol].push_back(thePoint.GetNumberOfReturns());
333  else{
334  std::string errorString="attribute not supported";
335  throw(errorString);
336  }
337  ++ipoint;
338  }
339  if(verbose_opt[0])
340  std::cout << "number of points: " << ipoint << std::endl;
341  lasReader.close();
342  }
343  progress=1;
344  pfnProgress(progress,pszMessage,pProgressArg);
345 
346  std::cout << "processing LiDAR points" << std::endl;
347  progress=0;
348  pfnProgress(progress,pszMessage,pProgressArg);
350  //fill inputData in outputData
351  // if(composite_opt[0]=="profile"){
352  // assert(postFilter_opt[0]=="none");
353  // for(int iband=0;iband<nband;++iband)
354  // outputProfile[iband].resize(nrow,ncol);
355  // }
356  for(int irow=0;irow<nrow;++irow){
357  if(composite_opt[0]=="number")
358  continue;//outputData already set
359  Vector2d<double> outputProfile(nband,ncol);
360  for(int icol=0;icol<ncol;++icol){
361  std::vector<double> profile;
362  if(!inputData[irow][icol].size())
363  outputData[irow][icol]=(static_cast<double>((nodata_opt[0])));
364  else{
366  if(composite_opt[0]=="min")
367  outputData[irow][icol]=stat.mymin(inputData[irow][icol]);
368  else if(composite_opt[0]=="max")
369  outputData[irow][icol]=stat.mymax(inputData[irow][icol]);
370  else if(composite_opt[0]=="median")
371  outputData[irow][icol]=stat.median(inputData[irow][icol]);
372  else if(composite_opt[0]=="percentile")
373  outputData[irow][icol]=stat.percentile(inputData[irow][icol],inputData[irow][icol].begin(),inputData[irow][icol].end(),percentile_opt[0]);
374  else if(composite_opt[0]=="mean")
375  outputData[irow][icol]=stat.mean(inputData[irow][icol]);
376  else if(composite_opt[0]=="sum")
377  outputData[irow][icol]=stat.sum(inputData[irow][icol]);
378  else if(composite_opt[0]=="first")
379  outputData[irow][icol]=inputData[irow][icol][0];
380  else if(composite_opt[0]=="last")
381  outputData[irow][icol]=inputData[irow][icol].back();
382  else if(composite_opt[0]=="profile"){
383  if(inputData[irow][icol].size()<2){
384  for(int iband=0;iband<nband;++iband)
385  outputProfile[iband][icol]=static_cast<double>(nodata_opt[0]);
386  continue;
387  }
388  double min=0;
389  double max=0;
390  stat.minmax(inputData[irow][icol],inputData[irow][icol].begin(),inputData[irow][icol].end(),min,max);
391  if(verbose_opt[0])
392  std::cout << "min,max: " << min << "," << max << std::endl;
393  if(max>min){
394  stat.percentiles(inputData[irow][icol],inputData[irow][icol].begin(),inputData[irow][icol].end(),profile,nband,min,max);
395  assert(profile.size()==nband);
396  for(int iband=0;iband<nband;++iband)
397  outputProfile[iband][icol]=profile[iband];
398  }
399  else{
400  for(int iband=0;iband<nband;++iband)
401  outputProfile[iband][icol]=max;
402  }
403  }
404  else{
405  std::cout << "Error: composite_opt " << composite_opt[0] << " not supported" << std::endl;
406  exit(2);
407  }
408  }
409  }
410  if(composite_opt[0]=="profile"){
411  for(int iband=0;iband<nband;++iband){
412  // assert(outputProfile[iband].size()==outputWriter.nrOfRow());
413  assert(outputProfile[iband].size()==outputWriter.nrOfCol());
414  try{
415  outputWriter.writeData(outputProfile[iband],GDT_Float64,irow,iband);
416  }
417  catch(std::string errorString){
418  cout << errorString << endl;
419  exit(1);
420  }
421  }
422  }
423  progress=static_cast<float>(irow)/outputWriter.nrOfRow();
424  pfnProgress(progress,pszMessage,pProgressArg);
425  }
426  progress=1;
427  pfnProgress(progress,pszMessage,pProgressArg);
428  inputData.clear();//clean up memory
429  //apply post filter
430  // std::cout << "Applying post processing filter: " << postFilter_opt[0] << std::endl;
431  // if(postFilter_opt[0]=="etew_min"){
432  // if(composite_opt[0]!="min")
433  // std::cout << "Warning: composite option is not set to min!" << std::endl;
434  // //Elevation Threshold with Expand Window (ETEW) Filter (p.73 frmo Airborne LIDAR Data Processing and Analysis Tools ALDPAT 1.0)
435  // //first iteration is performed assuming only minima are selected using options -fir all -comp min
436  // unsigned long int nchange=1;
437  // //increase cells and thresholds until no points from the previous iteration are discarded.
438  // int dimx=dimx_opt[0];
439  // int dimy=dimy_opt[0];
440  // filter2d::Filter2d morphFilter;
441  // // morphFilter.setNoValue(0);
442  // Vector2d<float> currentOutput=outputData;
443  // int iteration=1;
444  // while(nchange&&iteration<=maxIter_opt[0]){
445  // double hThreshold=maxSlope_opt[0]*dimx;
446  // Vector2d<float> newOutput;
447  // nchange=morphFilter.morphology(currentOutput,newOutput,"erode",dimx,dimy,disc_opt[0],hThreshold);
448  // currentOutput=newOutput;
449  // dimx+=2;//change from theory: originally double cellCize
450  // dimy+=2;//change from theory: originally double cellCize
451  // std::cout << "iteration " << iteration << ": " << nchange << " pixels changed" << std::endl;
452  // ++iteration;
453  // }
454  // outputData=currentOutput;
455  // }
456  // else if(postFilter_opt[0]=="promorph"||postFilter_opt[0]=="bunting"){
457  // if(composite_opt[0]!="min")
458  // std::cout << "Warning: composite option is not set to min!" << std::endl;
459  // assert(hThreshold_opt.size()>1);
460  // //Progressive morphological filter tgrs2003_zhang vol41 pp 872-882
461  // //first iteration is performed assuming only minima are selected using options -fir all -comp min
462  // //increase cells and thresholds until no points from the previous iteration are discarded.
463  // int dimx=dimx_opt[0];
464  // int dimy=dimy_opt[0];
465  // filter2d::Filter2d theFilter;
466  // // theFilter.setNoValue(0);
467  // Vector2d<float> currentOutput=outputData;
468  // double hThreshold=hThreshold_opt[0];
469  // int iteration=1;
470  // while(iteration<=maxIter_opt[0]){
471  // std::cout << "iteration " << iteration << " with window size " << dimx << " and dh_max: " << hThreshold << std::endl;
472  // Vector2d<float> newOutput;
473  // try{
474  // theFilter.morphology(outputData,currentOutput,"erode",dimx,dimy,disc_opt[0],hThreshold);
475  // theFilter.morphology(currentOutput,outputData,"dilate",dimx,dimy,disc_opt[0],hThreshold);
476  // if(postFilter_opt[0]=="bunting"){
477  // theFilter.doit(outputData,currentOutput,"median",dimx,dimy,1,disc_opt[0]);
478  // outputData=currentOutput;
479  // }
480  // }
481  // catch(std::string errorString){
482  // cout << errorString << endl;
483  // exit(1);
484  // }
485  // int newdimx=(dimx==1)? 3: 2*(dimx-1)+1;
486  // int newdimy=(dimx==1)? 3: 2*(dimy-1)+1;//from PE&RS vol 71 pp313-324
487  // hThreshold=hThreshold_opt[0]+maxSlope_opt[0]*(newdimx-dimx)*dx_opt[0];
488  // dimx=newdimx;
489  // dimy=newdimy;
490  // if(hThreshold>hThreshold_opt[1])
491  // hThreshold=hThreshold_opt[1];
492  // ++iteration;
493  // }
494  // outputData=currentOutput;
495  // }
496  // else if(postFilter_opt[0]=="open"){
497  // if(composite_opt[0]!="min")
498  // std::cout << "Warning: composite option is not set to min!" << std::endl;
499  // filter2d::Filter2d morphFilter;
500  // // morphFilter.setNoValue(0);
501  // Vector2d<float> filterInput=outputData;
502  // try{
503  // morphFilter.morphology(outputData,filterInput,"erode",dimx_opt[0],dimy_opt[0],disc_opt[0],maxSlope_opt[0]);
504  // morphFilter.morphology(filterInput,outputData,"dilate",dimx_opt[0],dimy_opt[0],disc_opt[0],maxSlope_opt[0]);
505  // }
506  // catch(std::string errorString){
507  // cout << errorString << endl;
508  // exit(1);
509  // }
510  // }
511  // else if(postFilter_opt[0]=="close"){
512  // if(composite_opt[0]!="max")
513  // std::cout << "Warning: composite option is not set to max!" << std::endl;
514  // filter2d::Filter2d morphFilter;
515  // // morphFilter.setNoValue(0);
516  // Vector2d<float> filterInput=outputData;
517  // try{
518  // morphFilter.morphology(outputData,filterInput,"dilate",dimx_opt[0],dimy_opt[0],disc_opt[0],maxSlope_opt[0]);
519  // morphFilter.morphology(filterInput,outputData,"erode",dimx_opt[0],dimy_opt[0],disc_opt[0],maxSlope_opt[0]);
520  // }
521  // catch(std::string errorString){
522  // cout << errorString << endl;
523  // exit(1);
524  // }
525  // }
526  if(composite_opt[0]!="profile"){
527  //write output file
528  std::cout << "writing output raster file" << std::endl;
529  progress=0;
530  pfnProgress(progress,pszMessage,pProgressArg);
531  for(int irow=0;irow<nrow;++irow){
532  try{
533  assert(outputData.size()==outputWriter.nrOfRow());
534  assert(outputData[0].size()==outputWriter.nrOfCol());
535  outputWriter.writeData(outputData[irow],GDT_Float64,irow,0);
536  }
537  catch(std::string errorString){
538  cout << errorString << endl;
539  exit(1);
540  }
541  progress=static_cast<float>(irow)/outputWriter.nrOfRow();
542  pfnProgress(progress,pszMessage,pProgressArg);
543  }
544  }
545  progress=1;
546  pfnProgress(progress,pszMessage,pProgressArg);
547  if(verbose_opt[0])
548  std::cout << "closing lasReader" << std::endl;
549  outputWriter.close();
550 }