pktools  2.6.4
Processing Kernel for geospatial data
pkreclass.cc
1 /**********************************************************************
2 pkreclass.cc: program to replace pixel values in raster image
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 <assert.h>
21 #include <map>
22 #include "base/Optionpk.h"
23 #include "imageclasses/ImgReaderOgr.h"
24 #include "imageclasses/ImgWriterOgr.h"
25 #include "imageclasses/ImgReaderGdal.h"
26 #include "imageclasses/ImgWriterGdal.h"
27 
28 /******************************************************************************/
66 using namespace std;
67 
68 int main(int argc, char *argv[])
69 {
70  Optionpk<string> input_opt("i", "input", "Input image");
71  Optionpk<string> mask_opt("m", "mask", "Mask image(s)");
72  Optionpk<string> output_opt("o", "output", "Output mask file");
73  Optionpk<unsigned short> masknodata_opt("msknodata", "msknodata", "Mask value(s) where image has nodata. Use one value for each mask, or multiple values for a single mask.", 1);
74  Optionpk<int> nodata_opt("nodata", "nodata", "nodata value to put in image if not valid (0)", 0);
75  Optionpk<string> colorTable_opt("ct", "ct", "color table (file with 5 columns: id R G B ALFA (0: transparent, 255: solid)");
76  Optionpk<unsigned short> band_opt("b", "band", "band index(es) to replace (other bands are copied to output)", 0);
77  Optionpk<string> type_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", "");
78  Optionpk<string> code_opt("code", "code", "Recode text file (2 colums: from to)");
79  Optionpk<string> class_opt("c", "class", "list of classes to reclass (in combination with reclass option)");
80  Optionpk<string> reclass_opt("r", "reclass", "list of recoded classes (in combination with class option)");
81  Optionpk<string> fieldname_opt("n", "fname", "field name of the shape file to be replaced", "label");
82  Optionpk<string> option_opt("co", "co", "Creation option for output file. Multiple options can be specified.");
83  Optionpk<string> description_opt("d", "description", "Set image description");
84  Optionpk<short> verbose_opt("v", "verbose", "verbose", 0);
85 
86  bool doProcess;//stop process when program was invoked with help option (-h --help)
87  try{
88  doProcess=input_opt.retrieveOption(argc,argv);
89  mask_opt.retrieveOption(argc,argv);
90  masknodata_opt.retrieveOption(argc,argv);
91  nodata_opt.retrieveOption(argc,argv);
92  code_opt.retrieveOption(argc,argv);
93  class_opt.retrieveOption(argc,argv);
94  reclass_opt.retrieveOption(argc,argv);
95  colorTable_opt.retrieveOption(argc,argv);
96  output_opt.retrieveOption(argc,argv);
97  type_opt.retrieveOption(argc,argv);
98  band_opt.retrieveOption(argc,argv);
99  fieldname_opt.retrieveOption(argc,argv);
100  option_opt.retrieveOption(argc,argv);
101  description_opt.retrieveOption(argc,argv);
102  verbose_opt.retrieveOption(argc,argv);
103  }
104  catch(string predefinedString){
105  std::cout << predefinedString << std::endl;
106  exit(0);
107  }
108  if(!doProcess){
109  cout << endl;
110  cout << "Usage: pkreclass -i input [-c from -r to]* -o output" << endl;
111  cout << endl;
112  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
113  exit(0);//help was invoked, stop processing
114  }
115 
116  if(input_opt.empty()){
117  std::cerr << "No input file provided (use option -i). Use --help for help information" << std::endl;
118  exit(0);
119  }
120  if(output_opt.empty()){
121  std::cerr << "No output file provided (use option -o). Use --help for help information" << std::endl;
122  exit(0);
123  }
124 
125  // vector<short> bandVector;
126  // for(int iband=0;iband<band_opt.size();++iband)
127  // bandVector.push_back(band_opt[iband]);
128  map<string,string> codemapString;//map with codes: codemapString[theKey(from)]=theValue(to)
129  map<double,double> codemap;//map with codes: codemap[theKey(from)]=theValue(to)
130  if(code_opt.size()){
131  if(verbose_opt[0])
132  cout << "opening code text file " << code_opt[0] << endl;
133  ifstream codefile;
134  codefile.open(code_opt[0].c_str());
135  string theKey;
136  string theValue;
137  while(codefile>>theKey){
138  codefile >> theValue;
139  codemapString[theKey]=theValue;
140  codemap[string2type<double>(theKey)]=string2type<double>(theValue);
141  }
142  codefile.close();
143  }
144  else{//use combination of class_opt and reclass_opt
145  assert(class_opt.size()==reclass_opt.size());
146  for(int iclass=0;iclass<class_opt.size();++iclass){
147  codemapString[class_opt[iclass]]=reclass_opt[iclass];
148  codemap[string2type<double>(class_opt[iclass])]=string2type<double>(reclass_opt[iclass]);
149  }
150  }
151  assert(codemapString.size());
152  assert(codemap.size());
153  //if verbose true, print the codes to screen
154  if(verbose_opt[0]){
155  map<string,string>::iterator mit;
156  cout << codemapString.size() << " codes used: " << endl;
157  for(mit=codemapString.begin();mit!=codemapString.end();++mit)
158  cout << (*mit).first << " " << (*mit).second << endl;
159  }
160  bool refIsRaster=false;
161  ImgReaderOgr ogrReader;
162  try{
163  ogrReader.open(input_opt[0]);
164  }
165  catch(string errorString){
166  refIsRaster=true;
167  }
168  // if(input_opt[0].find(".shp")!=string::npos){//shape file
169  if(!refIsRaster){
170  if(verbose_opt[0])
171  cout << "opening " << input_opt[0] << " for reading " << endl;
172  // ImgReaderOgr ogrReader(input_opt[0]);
173  if(verbose_opt[0])
174  cout << "opening " << output_opt[0] << " for writing " << endl;
175  ImgWriterOgr ogrWriter(output_opt[0],ogrReader);
176  if(verbose_opt[0])
177  cout << "copied layer from " << input_opt[0] << endl << flush;
178  OGRFeatureDefn *poFDefn = ogrWriter.getLayer()->GetLayerDefn();
179  //start reading features from the layer
180  if(verbose_opt[0])
181  cout << "reset reading" << endl;
182  ogrReader.getLayer()->ResetReading();
183  unsigned long int ifeature=0;
184  if(verbose_opt[0])
185  cout << "going through features" << endl << flush;
186  while(true){
187 // while( (poFeature = ogrWriter.getLayer()->GetNextFeature()) != NULL ){
188  OGRFeature *poFeature;
189  poFeature=ogrReader.getLayer()->GetNextFeature();
190  if(poFeature== NULL)
191  break;
192  OGRFeatureDefn *poFDefn = ogrWriter.getLayer()->GetLayerDefn();
193  string featurename;
194  for(int iField=0;iField<poFDefn->GetFieldCount();++iField){
195  OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
196  string fieldname=poFieldDefn->GetNameRef();
197  if(fieldname==fieldname_opt[0]){
198  string fromClass=poFeature->GetFieldAsString(iField);
199  string toClass=fromClass;
200  if(codemapString.find(fromClass)!=codemapString.end())
201  toClass=codemapString[fromClass];
202  poFeature->SetField(iField,toClass.c_str());
203  if(verbose_opt[0])
204  cout << "feature " << ifeature << ": " << fromClass << "->" << poFeature->GetFieldAsInteger(iField) << endl << flush;
205 // cout << "feature " << ifeature << ": " << fromClass << "->" << toClass << endl << flush;
206  }
207  }
208  //do not forget to actually write feature to file!!!
209  ogrWriter.createFeature(poFeature);
210  OGRFeature::DestroyFeature( poFeature );
211  ++ifeature;
212  }
213  if(verbose_opt[0])
214  cout << "replaced " << ifeature << " features" << endl;
215  ogrReader.close();
216  ogrWriter.close();
217  }
218  else{//image file
219  ImgReaderGdal inputReader;
220  vector<ImgReaderGdal> maskReader(mask_opt.size());
221  ImgWriterGdal outputWriter;
222  if(verbose_opt[0])
223  cout << "opening input image file " << input_opt[0] << endl;
224  inputReader.open(input_opt[0]);
225  for(int imask=0;imask<mask_opt.size();++imask){
226  if(verbose_opt[0])
227  cout << "opening mask image file " << mask_opt[imask] << endl;
228  maskReader[imask].open(mask_opt[imask]);
229  }
230  if(verbose_opt[0]){
231  cout << "opening output image file " << output_opt[0] << endl;
232  cout << "data type: " << type_opt[0] << endl;
233  }
234  //create output image with user defined data type
235  GDALDataType theType=GDT_Unknown;
236  if(verbose_opt[0])
237  cout << "possible output data types: ";
238  for(int iType = 0; iType < GDT_TypeCount; ++iType){
239  if(verbose_opt[0])
240  cout << " " << GDALGetDataTypeName((GDALDataType)iType);
241  if( GDALGetDataTypeName((GDALDataType)iType) != NULL
242  && EQUAL(GDALGetDataTypeName((GDALDataType)iType),
243  type_opt[0].c_str()))
244  theType=(GDALDataType) iType;
245  }
246  if(theType==GDT_Unknown)
247  theType=inputReader.getDataType();
248  if(verbose_opt[0])
249  cout << endl << "Output pixel type: " << GDALGetDataTypeName(theType) << endl;
250  if(option_opt.findSubstring("INTERLEAVE=")==option_opt.end()){
251  string theInterleave="INTERLEAVE=";
252  theInterleave+=inputReader.getInterleave();
253  option_opt.push_back(theInterleave);
254  }
255  outputWriter.open(output_opt[0],inputReader.nrOfCol(),inputReader.nrOfRow(),inputReader.nrOfBand(),theType,inputReader.getImageType(),option_opt);
256  for(int iband=0;iband<inputReader.nrOfBand();++iband)
257  outputWriter.GDALSetNoDataValue(nodata_opt[0],iband);
258  if(description_opt.size())
259  outputWriter.setImageDescription(description_opt[0]);
260 
261  if(colorTable_opt.size()){
262  if(colorTable_opt[0]!="none")
263  outputWriter.setColorTable(colorTable_opt[0]);
264  }
265  else if (inputReader.getColorTable()!=NULL)//copy colorTable from input image
266  outputWriter.setColorTable(inputReader.getColorTable());
267 
268  //if input image is georeferenced, copy projection info to output image
269  if(inputReader.isGeoRef()){
270  for(int imask=0;imask<mask_opt.size();++imask)
271  assert(maskReader[imask].isGeoRef());
272  }
273  outputWriter.copyGeoTransform(inputReader);
274  outputWriter.setProjection(inputReader.getProjection());
275  double ulx,uly,lrx,lry;
276  inputReader.getBoundingBox(ulx,uly,lrx,lry);
277  outputWriter.copyGeoTransform(inputReader);
278  assert(nodata_opt.size()==masknodata_opt.size());
279  if(verbose_opt[0]&&mask_opt.size()){
280  for(int iv=0;iv<masknodata_opt.size();++iv)
281  cout << masknodata_opt[iv] << "->" << nodata_opt[iv] << endl;
282  }
283 
284  assert(outputWriter.nrOfCol()==inputReader.nrOfCol());
285  // Vector2d<int> lineInput(inputReader.nrOfBand(),inputReader.nrOfCol());
286  Vector2d<double> lineInput(inputReader.nrOfBand(),inputReader.nrOfCol());
287  Vector2d<short> lineMask(mask_opt.size());
288  for(int imask=0;imask<mask_opt.size();++imask)
289  lineMask[imask].resize(maskReader[imask].nrOfCol());
290  Vector2d<double> lineOutput(outputWriter.nrOfBand(),outputWriter.nrOfCol());
291  int irow=0;
292  int icol=0;
293  const char* pszMessage;
294  void* pProgressArg=NULL;
295  GDALProgressFunc pfnProgress=GDALTermProgress;
296  double progress=0;
297  pfnProgress(progress,pszMessage,pProgressArg);
298  double oldRowMask=-1;
299  for(irow=0;irow<inputReader.nrOfRow();++irow){
300  //read line in lineInput buffer
301  for(int iband=0;iband<inputReader.nrOfBand();++iband){
302  try{
303  // inputReader.readData(lineInput[iband],GDT_Int32,irow,iband);
304  inputReader.readData(lineInput[iband],GDT_Float64,irow,iband);
305  }
306  catch(string errorstring){
307  cerr << errorstring << endl;
308  exit(1);
309  }
310  }
311  double x,y;//geo coordinates
312  double colMask,rowMask;//image coordinates in mask image
313  for(icol=0;icol<inputReader.nrOfCol();++icol){
314  bool masked=false;
315  if(mask_opt.size()>1){//multiple masks
316  for(int imask=0;imask<mask_opt.size();++imask){
317  inputReader.image2geo(icol,irow,x,y);
318  maskReader[imask].geo2image(x,y,colMask,rowMask);
319  if(static_cast<int>(rowMask)!=static_cast<int>(oldRowMask)){
320  assert(rowMask>=0&&rowMask<maskReader[imask].nrOfRow());
321  try{
322  maskReader[imask].readData(lineMask[imask],GDT_Int16,static_cast<int>(rowMask));
323  }
324  catch(string errorstring){
325  cerr << errorstring << endl;
326  exit(1);
327  }
328  oldRowMask=rowMask;
329  }
330  short ivalue=0;
331  if(mask_opt.size()==masknodata_opt.size())//one invalid value for each mask
332  ivalue=masknodata_opt[imask];
333  else//use same invalid value for each mask
334  ivalue=masknodata_opt[0];
335  if(lineMask[imask][colMask]==ivalue){
336  for(int iband=0;iband<inputReader.nrOfBand();++iband)
337  lineInput[iband][icol]=nodata_opt[imask];
338  masked=true;
339  break;
340  }
341  }
342  }
343  else if(mask_opt.size()){//potentially more invalid values for single mask
344  inputReader.image2geo(icol,irow,x,y);
345  maskReader[0].geo2image(x,y,colMask,rowMask);
346  if(static_cast<int>(rowMask)!=static_cast<int>(oldRowMask)){
347  assert(rowMask>=0&&rowMask<maskReader[0].nrOfRow());
348  try{
349  maskReader[0].readData(lineMask[0],GDT_Int16,static_cast<int>(rowMask));
350  }
351  catch(string errorstring){
352  cerr << errorstring << endl;
353  exit(1);
354  }
355  oldRowMask=rowMask;
356  }
357  for(int ivalue=0;ivalue<masknodata_opt.size();++ivalue){
358  assert(masknodata_opt.size()==nodata_opt.size());
359  if(lineMask[0][colMask]==masknodata_opt[ivalue]){
360  for(int iband=0;iband<inputReader.nrOfBand();++iband)
361  lineInput[iband][icol]=nodata_opt[ivalue];
362  masked=true;
363  break;
364  }
365  }
366  }
367  for(int iband=0;iband<lineOutput.size();++iband){
368  lineOutput[iband][icol]=lineInput[iband][icol];
369  if(find(band_opt.begin(),band_opt.end(),iband)!=band_opt.end()){
370  if(!masked && codemap.find(lineInput[iband][icol])!=codemap.end()){
371  double toValue=codemap[lineInput[iband][icol]];
372  lineOutput[iband][icol]=toValue;
373  }
374  }
375  }
376  }
377  //write buffer lineOutput to output file
378  try{
379  for(int iband=0;iband<outputWriter.nrOfBand();++iband)
380  outputWriter.writeData(lineOutput[iband],GDT_Float64,irow,iband);
381  }
382  catch(string errorstring){
383  cerr << errorstring << endl;
384  exit(1);
385  }
386  //progress bar
387  progress=static_cast<float>((irow+1.0)/outputWriter.nrOfRow());
388  pfnProgress(progress,pszMessage,pProgressArg);
389  }
390  inputReader.close();
391  outputWriter.close();
392  }
393 }