QGIS API Documentation  2.12.0-Lyon
qgsrasterfilewriter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterfilewriter.cpp
3  ---------------------
4  begin : July 2012
5  copyright : (C) 2012 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include <typeinfo>
16 
17 #include "qgsrasterfilewriter.h"
18 #include "qgsproviderregistry.h"
19 #include "qgsrasterinterface.h"
20 #include "qgsrasteriterator.h"
21 #include "qgsrasterlayer.h"
22 #include "qgsrasterprojector.h"
23 
24 #include <QCoreApplication>
25 #include <QProgressDialog>
26 #include <QTextStream>
27 #include <QMessageBox>
28 
30  mMode( Raw ), mOutputUrl( outputUrl ), mOutputProviderKey( "gdal" ), mOutputFormat( "GTiff" ),
31  mTiledMode( false ), mMaxTileWidth( 500 ), mMaxTileHeight( 500 ),
32  mBuildPyramidsFlag( QgsRaster::PyramidsFlagNo ),
33  mPyramidsFormat( QgsRaster::PyramidsGTiff ),
34  mProgressDialog( 0 ), mPipe( 0 ), mInput( 0 )
35 {
36 
37 }
38 
39 QgsRasterFileWriter::QgsRasterFileWriter()
40  : mMode( Raw )
41  , mOutputProviderKey( "gdal" )
42  , mOutputFormat( "GTiff" )
43  , mTiledMode( false )
44  , mMaxTileWidth( 500 )
45  , mMaxTileHeight( 500 )
46  , mBuildPyramidsFlag( QgsRaster::PyramidsFlagNo )
47  , mPyramidsFormat( QgsRaster::PyramidsGTiff )
48  , mProgressDialog( 0 )
49  , mPipe( 0 )
50  , mInput( 0 )
51 {
52 
53 }
54 
56 {
57 
58 }
59 
61  const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
62 {
63  QgsDebugMsg( "Entered" );
64 
65  if ( !pipe )
66  {
67  return SourceProviderError;
68  }
69  mPipe = pipe;
70 
71  //const QgsRasterInterface* iface = iter->input();
72  const QgsRasterInterface* iface = pipe->last();
73  if ( !iface )
74  {
75  return SourceProviderError;
76  }
77  mInput = iface;
78 
79  if ( QgsRasterBlock::typeIsColor( iface->dataType( 1 ) ) )
80  {
81  mMode = Image;
82  }
83  else
84  {
85  mMode = Raw;
86  }
87 
88  QgsDebugMsg( QString( "reading from %1" ).arg( typeid( *iface ).name() ) );
89 
90  if ( !iface->srcInput() )
91  {
92  QgsDebugMsg( "iface->srcInput() == 0" );
93  return SourceProviderError;
94  }
95 #ifdef QGISDEBUG
96  const QgsRasterInterface &srcInput = *iface->srcInput();
97  QgsDebugMsg( QString( "srcInput = %1" ).arg( typeid( srcInput ).name() ) );
98 #endif
99 
100  mProgressDialog = progressDialog;
101 
102  QgsRasterIterator iter( pipe->last() );
103 
104  //create directory for output files
105  if ( mTiledMode )
106  {
107  QFileInfo fileInfo( mOutputUrl );
108  if ( !fileInfo.exists() )
109  {
110  QDir dir = fileInfo.dir();
111  if ( !dir.mkdir( fileInfo.fileName() ) )
112  {
113  QgsDebugMsg( "Cannot create output VRT directory " + fileInfo.fileName() + " in " + dir.absolutePath() );
114  return CreateDatasourceError;
115  }
116  }
117  }
118 
119  if ( mMode == Image )
120  {
121  WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, progressDialog );
122  mProgressDialog = 0;
123  return e;
124  }
125  else
126  {
127  mProgressDialog = 0;
128  WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, progressDialog );
129  return e;
130  }
131 }
132 
133 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe* pipe, QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
134  const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
135 {
136  QgsDebugMsg( "Entered" );
137  if ( !iter )
138  {
139  return SourceProviderError;
140  }
141 
142  const QgsRasterInterface* iface = pipe->last();
143  if ( !iface )
144  {
145  return SourceProviderError;
146  }
147 
148  QgsRasterDataProvider* srcProvider = const_cast<QgsRasterDataProvider*>( dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() ) );
149  if ( !srcProvider )
150  {
151  QgsDebugMsg( "Cannot get source data provider" );
152  return SourceProviderError;
153  }
154 
155  iter->setMaximumTileWidth( mMaxTileWidth );
156  iter->setMaximumTileHeight( mMaxTileHeight );
157 
158  int nBands = iface->bandCount();
159  if ( nBands < 1 )
160  {
161  return SourceProviderError;
162  }
163 
164 
165  //check if all the bands have the same data type size, otherwise we cannot write it to the provider
166  //(at least not with the current interface)
167  int dataTypeSize = QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) );
168  for ( int i = 2; i <= nBands; ++i )
169  {
170  if ( QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) ) != dataTypeSize )
171  {
172  return DestProviderError;
173  }
174  }
175 
176  // Output data type - source data type is preferred but it may happen that we need
177  // to set 'no data' value (which was not set on source data) if output extent
178  // is larger than source extent (with or without reprojection) and there is no 'free'
179  // (not used) value available
180  QList<bool> destHasNoDataValueList;
181  QList<double> destNoDataValueList;
182  QList<QGis::DataType> destDataTypeList;
183  for ( int bandNo = 1; bandNo <= nBands; bandNo++ )
184  {
185  QgsRasterNuller *nuller = pipe->nuller();
186 
187  bool srcHasNoDataValue = srcProvider->srcHasNoDataValue( bandNo );
188  bool destHasNoDataValue = false;
189  double destNoDataValue = std::numeric_limits<double>::quiet_NaN();
190  QGis::DataType destDataType = srcProvider->srcDataType( bandNo );
191  // TODO: verify what happens/should happen if srcNoDataValue is disabled by setUseSrcNoDataValue
192  QgsDebugMsg( QString( "srcHasNoDataValue = %1 srcNoDataValue = %2" ).arg( srcHasNoDataValue ).arg( srcProvider->srcNoDataValue( bandNo ) ) );
193  if ( srcHasNoDataValue )
194  {
195 
196  // If source has no data value, it is used by provider
197  destNoDataValue = srcProvider->srcNoDataValue( bandNo );
198  destHasNoDataValue = true;
199  }
200  else if ( nuller && nuller->noData( bandNo ).size() > 0 )
201  {
202  // Use one user defined no data value
203  destNoDataValue = nuller->noData( bandNo ).value( 0 ).min();
204  destHasNoDataValue = true;
205  }
206  else
207  {
208  // Verify if we realy need no data value, i.e.
209  QgsRectangle srcExtent = outputExtent;
210  QgsRasterProjector *projector = pipe->projector();
211  if ( projector && projector->destCrs() != projector->srcCrs() )
212  {
213  QgsCoordinateTransform ct( projector->destCrs(), projector->srcCrs() );
214  srcExtent = ct.transformBoundingBox( outputExtent );
215  }
216  if ( !srcProvider->extent().contains( srcExtent ) )
217  {
218  // Destination extent is larger than source extent, we need destination no data values
219  // Get src sample statistics (estimation from sample)
220  QgsRasterBandStats stats = srcProvider->bandStatistics( bandNo, QgsRasterBandStats::Min | QgsRasterBandStats::Max, srcExtent, 250000 );
221 
222  // Test if we have free (not used) values
223  double typeMinValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) );
224  double typeMaxValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) );
225  if ( stats.minimumValue > typeMinValue )
226  {
227  destNoDataValue = typeMinValue;
228  }
229  else if ( stats.maximumValue < typeMaxValue )
230  {
231  destNoDataValue = typeMaxValue;
232  }
233  else
234  {
235  // We have to use wider type
236  destDataType = QgsRasterBlock::typeWithNoDataValue( destDataType, &destNoDataValue );
237  }
238  destHasNoDataValue = true;
239  }
240  }
241 
242  if ( nuller && destHasNoDataValue )
243  {
244  nuller->setOutputNoDataValue( bandNo, destNoDataValue );
245  }
246 
247  QgsDebugMsg( QString( "bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( destDataType ).arg( destHasNoDataValue ).arg( destNoDataValue ) );
248  destDataTypeList.append( destDataType );
249  destHasNoDataValueList.append( destHasNoDataValue );
250  destNoDataValueList.append( destNoDataValue );
251  }
252 
253  QGis::DataType destDataType = destDataTypeList.value( 0 );
254  // Currently write API supports one output type for dataset only -> find the widest
255  for ( int i = 1; i < nBands; i++ )
256  {
257  if ( destDataTypeList.value( i ) > destDataType )
258  {
259  destDataType = destDataTypeList.value( i );
260  // no data value may be left per band (for future)
261  }
262  }
263 
264  //create destProvider for whole dataset here
265  QgsRasterDataProvider* destProvider = 0;
266  double pixelSize;
267  double geoTransform[6];
268  globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
269 
270  // initOutput() returns 0 in tile mode!
271  destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList );
272 
273  WriterError error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
274 
275  if ( error == NoDataConflict )
276  {
277  // The value used for no data was found in source data, we must use wider data type
278  if ( destProvider ) // no tiles
279  {
280  destProvider->remove();
281  delete destProvider;
282  destProvider = 0;
283  }
284  else // VRT
285  {
286  // TODO: remove created VRT
287  }
288 
289  // But we don't know which band -> wider all
290  for ( int i = 0; i < nBands; i++ )
291  {
292  double destNoDataValue;
293  QGis::DataType destDataType = QgsRasterBlock::typeWithNoDataValue( destDataTypeList.value( i ), &destNoDataValue );
294  destDataTypeList.replace( i, destDataType );
295  destNoDataValueList.replace( i, destNoDataValue );
296  }
297  destDataType = destDataTypeList.value( 0 );
298 
299  // Try again
300  destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList );
301  error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
302  }
303 
304  if ( destProvider )
305  delete destProvider;
306 
307  return error;
308 }
309 
310 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
311  const QgsRasterPipe* pipe,
312  QgsRasterIterator* iter,
313  int nCols, int nRows,
314  const QgsRectangle& outputExtent,
315  const QgsCoordinateReferenceSystem& crs,
316  QGis::DataType destDataType,
317  const QList<bool>& destHasNoDataValueList,
318  const QList<double>& destNoDataValueList,
319  QgsRasterDataProvider* destProvider,
320  QProgressDialog* progressDialog )
321 {
322  Q_UNUSED( pipe );
323  Q_UNUSED( destHasNoDataValueList );
324  QgsDebugMsg( "Entered" );
325 
326  const QgsRasterInterface* iface = iter->input();
327  const QgsRasterDataProvider *srcProvider = dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() );
328  int nBands = iface->bandCount();
329  QgsDebugMsg( QString( "nBands = %1" ).arg( nBands ) );
330 
331  //Get output map units per pixel
332  int iterLeft = 0;
333  int iterTop = 0;
334  int iterCols = 0;
335  int iterRows = 0;
336 
337  QList<QgsRasterBlock*> blockList;
338  blockList.reserve( nBands );
339  for ( int i = 1; i <= nBands; ++i )
340  {
341  iter->startRasterRead( i, nCols, nRows, outputExtent );
342  blockList.push_back( 0 );
343  if ( destProvider ) // no tiles
344  {
345  destProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
346  }
347  }
348 
349  int nParts = 0;
350  int fileIndex = 0;
351  if ( progressDialog )
352  {
353  int nPartsX = nCols / iter->maximumTileWidth() + 1;
354  int nPartsY = nRows / iter->maximumTileHeight() + 1;
355  nParts = nPartsX * nPartsY;
356  progressDialog->setMaximum( nParts );
357  progressDialog->show();
358  progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
359  }
360 
361  // hmm why is there a for(;;) here ..
362  // not good coding practice IMHO, it might be better to use [ for() and break ] or [ while (test) ]
363  for ( ;; )
364  {
365  for ( int i = 1; i <= nBands; ++i )
366  {
367  if ( !iter->readNextRasterPart( i, iterCols, iterRows, &( blockList[i - 1] ), iterLeft, iterTop ) )
368  {
369  // No more parts, create VRT and return
370  if ( mTiledMode )
371  {
372  QString vrtFilePath( mOutputUrl + "/" + vrtFileName() );
373  writeVRT( vrtFilePath );
374  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
375  {
376  buildPyramids( vrtFilePath );
377  }
378  }
379  else
380  {
381  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
382  {
383  buildPyramids( mOutputUrl );
384  }
385  }
386 
387  QgsDebugMsg( "Done" );
388  return NoError; //reached last tile, bail out
389  }
390  // TODO: verify if NoDataConflict happened, to do that we need the whole pipe or nuller interface
391  }
392 
393  if ( progressDialog && fileIndex < ( nParts - 1 ) )
394  {
395  progressDialog->setValue( fileIndex + 1 );
396  progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
397  QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
398  if ( progressDialog->wasCanceled() )
399  {
400  for ( int i = 0; i < nBands; ++i )
401  {
402  delete blockList[i];
403  }
404  break;
405  }
406  }
407 
408  // It may happen that internal data type (dataType) is wider than destDataType
409  QList<QgsRasterBlock*> destBlockList;
410  for ( int i = 1; i <= nBands; ++i )
411  {
412  if ( srcProvider && srcProvider->dataType( i ) == destDataType )
413  {
414  destBlockList.push_back( blockList[i-1] );
415  }
416  else
417  {
418  // TODO: this conversion should go to QgsRasterDataProvider::write with additional input data type param
419  blockList[i-1]->convert( destDataType );
420  destBlockList.push_back( blockList[i-1] );
421  }
422  blockList[i-1] = 0;
423  }
424 
425  if ( mTiledMode ) //write to file
426  {
427  QgsRasterDataProvider* partDestProvider = createPartProvider( outputExtent,
428  nCols, iterCols, iterRows,
429  iterLeft, iterTop, mOutputUrl,
430  fileIndex, nBands, destDataType, crs );
431 
432  if ( partDestProvider )
433  {
434  //write data to output file. todo: loop over the data list
435  for ( int i = 1; i <= nBands; ++i )
436  {
437  partDestProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
438  partDestProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, 0, 0 );
439  delete destBlockList[i - 1];
440  addToVRT( partFileName( fileIndex ), i, iterCols, iterRows, iterLeft, iterTop );
441  }
442  delete partDestProvider;
443  }
444  }
445  else if ( destProvider )
446  {
447  //loop over data
448  for ( int i = 1; i <= nBands; ++i )
449  {
450  destProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, iterLeft, iterTop );
451  delete destBlockList[i - 1];
452  }
453  }
454  ++fileIndex;
455  }
456 
457  QgsDebugMsg( "Done" );
458  return NoError;
459 }
460 
461 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
462  const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
463 {
464  QgsDebugMsg( "Entered" );
465  if ( !iter )
466  {
467  return SourceProviderError;
468  }
469 
470  const QgsRasterInterface* iface = iter->input();
471  if ( !iface )
472  return SourceProviderError;
473 
474  QGis::DataType inputDataType = iface->dataType( 1 );
475  if ( inputDataType != QGis::ARGB32 && inputDataType != QGis::ARGB32_Premultiplied )
476  {
477  return SourceProviderError;
478  }
479 
480  iter->setMaximumTileWidth( mMaxTileWidth );
481  iter->setMaximumTileHeight( mMaxTileHeight );
482 
483  void* redData = qgsMalloc( mMaxTileWidth * mMaxTileHeight );
484  void* greenData = qgsMalloc( mMaxTileWidth * mMaxTileHeight );
485  void* blueData = qgsMalloc( mMaxTileWidth * mMaxTileHeight );
486  void* alphaData = qgsMalloc( mMaxTileWidth * mMaxTileHeight );
487  QgsRectangle mapRect;
488  int iterLeft = 0, iterTop = 0, iterCols = 0, iterRows = 0;
489  int fileIndex = 0;
490 
491  //create destProvider for whole dataset here
492  QgsRasterDataProvider* destProvider = 0;
493  double pixelSize;
494  double geoTransform[6];
495  globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
496 
497  destProvider = initOutput( nCols, nRows, crs, geoTransform, 4, QGis::Byte );
498 
499  iter->startRasterRead( 1, nCols, nRows, outputExtent );
500 
501  int nParts = 0;
502  if ( progressDialog )
503  {
504  int nPartsX = nCols / iter->maximumTileWidth() + 1;
505  int nPartsY = nRows / iter->maximumTileHeight() + 1;
506  nParts = nPartsX * nPartsY;
507  progressDialog->setMaximum( nParts );
508  progressDialog->show();
509  progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
510  }
511 
512  QgsRasterBlock *inputBlock = 0;
513  while ( iter->readNextRasterPart( 1, iterCols, iterRows, &inputBlock, iterLeft, iterTop ) )
514  {
515  if ( !inputBlock )
516  {
517  continue;
518  }
519 
520  if ( progressDialog && fileIndex < ( nParts - 1 ) )
521  {
522  progressDialog->setValue( fileIndex + 1 );
523  progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
524  QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
525  if ( progressDialog->wasCanceled() )
526  {
527  delete inputBlock;
528  break;
529  }
530  }
531 
532  //fill into red/green/blue/alpha channels
533  qgssize nPixels = ( qgssize )iterCols * iterRows;
534  // TODO: should be char not int? we are then copying 1 byte
535  int red = 0;
536  int green = 0;
537  int blue = 0;
538  int alpha = 255;
539  for ( qgssize i = 0; i < nPixels; ++i )
540  {
541  QRgb c = inputBlock->color( i );
542  alpha = qAlpha( c );
543  red = qRed( c ); green = qGreen( c ); blue = qBlue( c );
544 
545  if ( inputDataType == QGis::ARGB32_Premultiplied )
546  {
547  double a = alpha / 255.;
548  QgsDebugMsgLevel( QString( "red = %1 green = %2 blue = %3 alpha = %4 p = %5 a = %6" ).arg( red ).arg( green ).arg( blue ).arg( alpha ).arg(( int )c, 0, 16 ).arg( a ), 5 );
549  red /= a;
550  green /= a;
551  blue /= a;
552  }
553  memcpy(( char* )redData + i, &red, 1 );
554  memcpy(( char* )greenData + i, &green, 1 );
555  memcpy(( char* )blueData + i, &blue, 1 );
556  memcpy(( char* )alphaData + i, &alpha, 1 );
557  }
558  delete inputBlock;
559 
560  //create output file
561  if ( mTiledMode )
562  {
563  //delete destProvider;
564  QgsRasterDataProvider* partDestProvider = createPartProvider( outputExtent,
565  nCols, iterCols, iterRows,
566  iterLeft, iterTop, mOutputUrl, fileIndex,
567  4, QGis::Byte, crs );
568 
569  if ( partDestProvider )
570  {
571  //write data to output file
572  partDestProvider->write( redData, 1, iterCols, iterRows, 0, 0 );
573  partDestProvider->write( greenData, 2, iterCols, iterRows, 0, 0 );
574  partDestProvider->write( blueData, 3, iterCols, iterRows, 0, 0 );
575  partDestProvider->write( alphaData, 4, iterCols, iterRows, 0, 0 );
576 
577  addToVRT( partFileName( fileIndex ), 1, iterCols, iterRows, iterLeft, iterTop );
578  addToVRT( partFileName( fileIndex ), 2, iterCols, iterRows, iterLeft, iterTop );
579  addToVRT( partFileName( fileIndex ), 3, iterCols, iterRows, iterLeft, iterTop );
580  addToVRT( partFileName( fileIndex ), 4, iterCols, iterRows, iterLeft, iterTop );
581  delete partDestProvider;
582  }
583  }
584  else if ( destProvider )
585  {
586  destProvider->write( redData, 1, iterCols, iterRows, iterLeft, iterTop );
587  destProvider->write( greenData, 2, iterCols, iterRows, iterLeft, iterTop );
588  destProvider->write( blueData, 3, iterCols, iterRows, iterLeft, iterTop );
589  destProvider->write( alphaData, 4, iterCols, iterRows, iterLeft, iterTop );
590  }
591 
592  ++fileIndex;
593  }
594 
595  if ( destProvider )
596  delete destProvider;
597 
598  qgsFree( redData ); qgsFree( greenData ); qgsFree( blueData ); qgsFree( alphaData );
599 
600  if ( progressDialog )
601  {
602  progressDialog->setValue( progressDialog->maximum() );
603  }
604 
605  if ( mTiledMode )
606  {
607  QString vrtFilePath( mOutputUrl + "/" + vrtFileName() );
608  writeVRT( vrtFilePath );
609  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
610  {
611  buildPyramids( vrtFilePath );
612  }
613  }
614  else
615  {
616  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
617  {
618  buildPyramids( mOutputUrl );
619  }
620  }
621  return NoError;
622 }
623 
624 void QgsRasterFileWriter::addToVRT( const QString& filename, int band, int xSize, int ySize, int xOffset, int yOffset )
625 {
626  QDomElement bandElem = mVRTBands.value( band - 1 );
627 
628  QDomElement simpleSourceElem = mVRTDocument.createElement( "SimpleSource" );
629 
630  //SourceFilename
631  QDomElement sourceFilenameElem = mVRTDocument.createElement( "SourceFilename" );
632  sourceFilenameElem.setAttribute( "relativeToVRT", "1" );
633  QDomText sourceFilenameText = mVRTDocument.createTextNode( filename );
634  sourceFilenameElem.appendChild( sourceFilenameText );
635  simpleSourceElem.appendChild( sourceFilenameElem );
636 
637  //SourceBand
638  QDomElement sourceBandElem = mVRTDocument.createElement( "SourceBand" );
639  QDomText sourceBandText = mVRTDocument.createTextNode( QString::number( band ) );
640  sourceBandElem.appendChild( sourceBandText );
641  simpleSourceElem.appendChild( sourceBandElem );
642 
643  //SourceProperties
644  QDomElement sourcePropertiesElem = mVRTDocument.createElement( "SourceProperties" );
645  sourcePropertiesElem.setAttribute( "RasterXSize", xSize );
646  sourcePropertiesElem.setAttribute( "RasterYSize", ySize );
647  sourcePropertiesElem.setAttribute( "BlockXSize", xSize );
648  sourcePropertiesElem.setAttribute( "BlockYSize", ySize );
649  sourcePropertiesElem.setAttribute( "DataType", "Byte" );
650  simpleSourceElem.appendChild( sourcePropertiesElem );
651 
652  //SrcRect
653  QDomElement srcRectElem = mVRTDocument.createElement( "SrcRect" );
654  srcRectElem.setAttribute( "xOff", "0" );
655  srcRectElem.setAttribute( "yOff", "0" );
656  srcRectElem.setAttribute( "xSize", xSize );
657  srcRectElem.setAttribute( "ySize", ySize );
658  simpleSourceElem.appendChild( srcRectElem );
659 
660  //DstRect
661  QDomElement dstRectElem = mVRTDocument.createElement( "DstRect" );
662  dstRectElem.setAttribute( "xOff", xOffset );
663  dstRectElem.setAttribute( "yOff", yOffset );
664  dstRectElem.setAttribute( "xSize", xSize );
665  dstRectElem.setAttribute( "ySize", ySize );
666  simpleSourceElem.appendChild( dstRectElem );
667 
668  bandElem.appendChild( simpleSourceElem );
669 }
670 
671 #if 0
672 void QgsRasterFileWriter::buildPyramids( const QString& filename )
673 {
674  GDALDatasetH dataSet;
675  GDALAllRegister();
676  dataSet = GDALOpen( filename.toLocal8Bit().data(), GA_Update );
677  if ( !dataSet )
678  {
679  return;
680  }
681 
682  //2,4,8,16,32,64
683  int overviewList[6];
684  overviewList[0] = 2;
685  overviewList[1] = 4;
686  overviewList[2] = 8;
687  overviewList[3] = 16;
688  overviewList[4] = 32;
689  overviewList[5] = 64;
690 
691 #if 0
692  if ( mProgressDialog )
693  {
694  mProgressDialog->setLabelText( QObject::tr( "Building Pyramids..." ) );
695  mProgressDialog->setValue( 0 );
696  mProgressDialog->setWindowModality( Qt::WindowModal );
697  mProgressDialog->show();
698  }
699 #endif
700  GDALBuildOverviews( dataSet, "AVERAGE", 6, overviewList, 0, 0, /*pyramidsProgress*/ 0, /*mProgressDialog*/ 0 );
701 }
702 #endif
703 
704 void QgsRasterFileWriter::buildPyramids( const QString& filename )
705 {
706  QgsDebugMsg( "filename = " + filename );
707  // open new dataProvider so we can build pyramids with it
708  QgsRasterDataProvider* destProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( mOutputProviderKey, filename );
709  if ( !destProvider )
710  {
711  return;
712  }
713 
714  // TODO progress report
715  // TODO test mTiledMode - not tested b/c segfault at line # 289
716  // connect( provider, SIGNAL( progressUpdate( int ) ), mPyramidProgress, SLOT( setValue( int ) ) );
717  QList< QgsRasterPyramid> myPyramidList;
718  if ( ! mPyramidsList.isEmpty() )
719  myPyramidList = destProvider->buildPyramidList( mPyramidsList );
720  for ( int myCounterInt = 0; myCounterInt < myPyramidList.count(); myCounterInt++ )
721  {
722  myPyramidList[myCounterInt].build = true;
723  }
724 
725  QgsDebugMsg( QString( "building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ) );
726  // QApplication::setOverrideCursor( Qt::WaitCursor );
727  QString res = destProvider->buildPyramids( myPyramidList, mPyramidsResampling,
728  mPyramidsFormat, mPyramidsConfigOptions );
729  // QApplication::restoreOverrideCursor();
730 
731  // TODO put this in provider or elsewhere
732  if ( !res.isNull() )
733  {
734  QString title, message;
735  if ( res == "ERROR_WRITE_ACCESS" )
736  {
737  title = QObject::tr( "Building pyramids failed - write access denied" );
738  message = QObject::tr( "Write access denied. Adjust the file permissions and try again." );
739  }
740  else if ( res == "ERROR_WRITE_FORMAT" )
741  {
742  title = QObject::tr( "Building pyramids failed." );
743  message = QObject::tr( "The file was not writable. Some formats do not "
744  "support pyramid overviews. Consult the GDAL documentation if in doubt." );
745  }
746  else if ( res == "FAILED_NOT_SUPPORTED" )
747  {
748  title = QObject::tr( "Building pyramids failed." );
749  message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
750  }
751  else if ( res == "ERROR_JPEG_COMPRESSION" )
752  {
753  title = QObject::tr( "Building pyramids failed." );
754  message = QObject::tr( "Building internal pyramid overviews is not supported on raster layers with JPEG compression and your current libtiff library." );
755  }
756  else if ( res == "ERROR_VIRTUAL" )
757  {
758  title = QObject::tr( "Building pyramids failed." );
759  message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
760  }
761  QMessageBox::warning( 0, title, message );
762  QgsDebugMsg( res + " - " + message );
763  }
764  delete destProvider;
765 }
766 
767 #if 0
768 int QgsRasterFileWriter::pyramidsProgress( double dfComplete, const char *pszMessage, void* pData )
769 {
770  Q_UNUSED( pszMessage );
771  GDALTermProgress( dfComplete, 0, 0 );
772  QProgressDialog* progressDialog = static_cast<QProgressDialog*>( pData );
773  if ( pData && progressDialog->wasCanceled() )
774  {
775  return 0;
776  }
777 
778  if ( pData )
779  {
780  progressDialog->setRange( 0, 100 );
781  progressDialog->setValue( dfComplete * 100 );
782  }
783  return 1;
784 }
785 #endif
786 
787 void QgsRasterFileWriter::createVRT( int xSize, int ySize, const QgsCoordinateReferenceSystem& crs, double* geoTransform, QGis::DataType type, const QList<bool>& destHasNoDataValueList, const QList<double>& destNoDataValueList )
788 {
789  mVRTDocument.clear();
790  QDomElement VRTDatasetElem = mVRTDocument.createElement( "VRTDataset" );
791 
792  //xsize / ysize
793  VRTDatasetElem.setAttribute( "rasterXSize", xSize );
794  VRTDatasetElem.setAttribute( "rasterYSize", ySize );
795  mVRTDocument.appendChild( VRTDatasetElem );
796 
797  //CRS
798  QDomElement SRSElem = mVRTDocument.createElement( "SRS" );
799  QDomText crsText = mVRTDocument.createTextNode( crs.toWkt() );
800  SRSElem.appendChild( crsText );
801  VRTDatasetElem.appendChild( SRSElem );
802 
803  //geotransform
804  if ( geoTransform )
805  {
806  QDomElement geoTransformElem = mVRTDocument.createElement( "GeoTransform" );
807  QString geoTransformString = QString::number( geoTransform[0] ) + ", " + QString::number( geoTransform[1] ) + ", " + QString::number( geoTransform[2] ) +
808  ", " + QString::number( geoTransform[3] ) + ", " + QString::number( geoTransform[4] ) + ", " + QString::number( geoTransform[5] );
809  QDomText geoTransformText = mVRTDocument.createTextNode( geoTransformString );
810  geoTransformElem.appendChild( geoTransformText );
811  VRTDatasetElem.appendChild( geoTransformElem );
812  }
813 
814  int nBands;
815  if ( mMode == Raw )
816  {
817  nBands = mInput->bandCount();
818  }
819  else
820  {
821  nBands = 4;
822  }
823 
824  QStringList colorInterp;
825  colorInterp << "Red" << "Green" << "Blue" << "Alpha";
826 
828  dataTypes.insert( QGis::Byte, "Byte" );
829  dataTypes.insert( QGis::UInt16, "UInt16" );
830  dataTypes.insert( QGis::Int16, "Int16" );
831  dataTypes.insert( QGis::UInt32, "Int32" );
832  dataTypes.insert( QGis::Float32, "Float32" );
833  dataTypes.insert( QGis::Float64, "Float64" );
834  dataTypes.insert( QGis::CInt16, "CInt16" );
835  dataTypes.insert( QGis::CInt32, "CInt32" );
836  dataTypes.insert( QGis::CFloat32, "CFloat32" );
837  dataTypes.insert( QGis::CFloat64, "CFloat64" );
838 
839  for ( int i = 1; i <= nBands; i++ )
840  {
841  QDomElement VRTBand = mVRTDocument.createElement( "VRTRasterBand" );
842 
843  VRTBand.setAttribute( "band", QString::number( i ) );
844  QString dataType = dataTypes.value( type );
845  VRTBand.setAttribute( "dataType", dataType );
846 
847  if ( mMode == Image )
848  {
849  VRTBand.setAttribute( "dataType", "Byte" );
850  QDomElement colorInterpElement = mVRTDocument.createElement( "ColorInterp" );
851  QDomText interpText = mVRTDocument.createTextNode( colorInterp.value( i - 1 ) );
852  colorInterpElement.appendChild( interpText );
853  VRTBand.appendChild( colorInterpElement );
854  }
855 
856  if ( !destHasNoDataValueList.isEmpty() && destHasNoDataValueList.value( i - 1 ) )
857  {
858  VRTBand.setAttribute( "NoDataValue", QString::number( destNoDataValueList.value( i - 1 ) ) );
859  }
860 
861  mVRTBands.append( VRTBand );
862  VRTDatasetElem.appendChild( VRTBand );
863  }
864 }
865 
866 bool QgsRasterFileWriter::writeVRT( const QString& file )
867 {
868  QFile outputFile( file );
869  if ( ! outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
870  {
871  return false;
872  }
873 
874  QTextStream outStream( &outputFile );
875  mVRTDocument.save( outStream, 2 );
876  return true;
877 }
878 
879 QgsRasterDataProvider* QgsRasterFileWriter::createPartProvider( const QgsRectangle& extent, int nCols, int iterCols,
880  int iterRows, int iterLeft, int iterTop, const QString& outputUrl, int fileIndex, int nBands, QGis::DataType type,
881  const QgsCoordinateReferenceSystem& crs )
882 {
883  double mup = extent.width() / nCols;
884  double mapLeft = extent.xMinimum() + iterLeft * mup;
885  double mapRight = mapLeft + mup * iterCols;
886  double mapTop = extent.yMaximum() - iterTop * mup;
887  double mapBottom = mapTop - iterRows * mup;
888  QgsRectangle mapRect( mapLeft, mapBottom, mapRight, mapTop );
889 
890  QString outputFile = outputUrl + "/" + partFileName( fileIndex );
891 
892  //geotransform
893  double geoTransform[6];
894  geoTransform[0] = mapRect.xMinimum();
895  geoTransform[1] = mup;
896  geoTransform[2] = 0.0;
897  geoTransform[3] = mapRect.yMaximum();
898  geoTransform[4] = 0.0;
899  geoTransform[5] = -mup;
900 
901  // perhaps we need a separate createOptions for tiles ?
902 
903  QgsRasterDataProvider* destProvider = QgsRasterDataProvider::create( mOutputProviderKey, outputFile, mOutputFormat, nBands, type, iterCols, iterRows, geoTransform, crs, mCreateOptions );
904 
905  // TODO: return provider and report error
906  return destProvider;
907 }
908 
909 QgsRasterDataProvider* QgsRasterFileWriter::initOutput( int nCols, int nRows, const QgsCoordinateReferenceSystem& crs,
910  double* geoTransform, int nBands, QGis::DataType type,
911  const QList<bool>& destHasNoDataValueList, const QList<double>& destNoDataValueList )
912 {
913  if ( mTiledMode )
914  {
915  createVRT( nCols, nRows, crs, geoTransform, type, destHasNoDataValueList, destNoDataValueList );
916  return 0;
917  }
918  else
919  {
920 #if 0
921  // TODO enable "use existing", has no effect for now, because using Create() in gdal provider
922  // should this belong in provider? should also test that source provider is gdal
923  if ( mBuildPyramidsFlag == -4 && mOutputProviderKey == "gdal" && mOutputFormat.toLower() == "gtiff" )
924  mCreateOptions << "COPY_SRC_OVERVIEWS=YES";
925 #endif
926 
927  QgsRasterDataProvider* destProvider = QgsRasterDataProvider::create( mOutputProviderKey, mOutputUrl, mOutputFormat, nBands, type, nCols, nRows, geoTransform, crs, mCreateOptions );
928 
929  if ( !destProvider )
930  {
931  QgsDebugMsg( "No provider created" );
932  }
933 
934  return destProvider;
935  }
936 }
937 
938 void QgsRasterFileWriter::globalOutputParameters( const QgsRectangle& extent, int nCols, int& nRows,
939  double* geoTransform, double& pixelSize )
940 {
941  pixelSize = extent.width() / nCols;
942 
943  //calculate nRows automatically for providers without exact resolution
944  if ( nRows < 0 )
945  {
946  nRows = ( double )nCols / extent.width() * extent.height() + 0.5;
947  }
948  geoTransform[0] = extent.xMinimum();
949  geoTransform[1] = pixelSize;
950  geoTransform[2] = 0.0;
951  geoTransform[3] = extent.yMaximum();
952  geoTransform[4] = 0.0;
953  geoTransform[5] = -( extent.height() / nRows );
954 }
955 
956 QString QgsRasterFileWriter::partFileName( int fileIndex )
957 {
958  // .tif for now
959  QFileInfo outputInfo( mOutputUrl );
960  return QString( "%1.%2.tif" ).arg( outputInfo.fileName() ).arg( fileIndex );
961 }
962 
963 QString QgsRasterFileWriter::vrtFileName()
964 {
965  QFileInfo outputInfo( mOutputUrl );
966  return QString( "%1.vrt" ).arg( outputInfo.fileName() );
967 }
virtual int bandCount() const =0
Get number of bands.
Sixty four bit floating point (double)
Definition: qgis.h:131
Sixteen bit signed integer (qint16)
Definition: qgis.h:127
static QgsProviderRegistry * instance(const QString &pluginPath=QString::null)
Means of accessing canonical single instance.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
QgsRasterFileWriter(const QString &outputUrl)
Complex Float64.
Definition: qgis.h:135
Base class for processing modules.
Definition: qgsrasterpipe.h:41
QgsRasterNuller * nuller() const
void * qgsMalloc(size_t size)
Allocates size bytes and returns a pointer to the allocated memory.
Definition: qgis.cpp:253
Iterator for sequentially processing raster cells.
static QgsRasterDataProvider * create(const QString &providerKey, const QString &uri, const QString &format, int nBands, QGis::DataType type, int width, int height, double *geoTransform, const QgsCoordinateReferenceSystem &crs, const QStringList &createOptions=QStringList())
Creates a new dataset with mDataSourceURI.
QDomNode appendChild(const QDomNode &newChild)
void startRasterRead(int bandNumber, int nCols, int nRows, const QgsRectangle &extent)
Start reading of raster band.
void setMaximum(int maximum)
void push_back(const T &value)
Eight bit unsigned integer (quint8)
Definition: qgis.h:125
void setWindowModality(Qt::WindowModality windowModality)
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:196
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:126
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void reserve(int alloc)
void setLabelText(const QString &text)
bool contains(const QgsRectangle &rect) const
return true when rectangle contains other rectangle
virtual double srcNoDataValue(int bandNo) const
Value representing no data value.
virtual const QgsRasterInterface * srcInput() const
Get source / raw input, the first in pipe, usually provider.
Raster pipe that deals with null values.
QString toWkt() const
A helper to get an wkt representation of this srs.
double maximumValue
The maximum cell value in the raster band.
QgsRasterInterface * last() const
Definition: qgsrasterpipe.h:86
static double maximumValuePossible(QGis::DataType)
Helper function that returns the maximum possible value for a GDAL data type.
virtual bool setNoDataValue(int bandNo, double noDataValue)
Set no data value on created dataset.
QString tr(const char *sourceText, const char *disambiguation, int n)
int size() const
bool isNull() const
T value(int i) const
int maximumTileWidth() const
virtual QgsRasterBandStats bandStatistics(int theBandNo, int theStats=QgsRasterBandStats::All, const QgsRectangle &theExtent=QgsRectangle(), int theSampleSize=0)
Get band statistics.
void setValue(int progress)
static QGis::DataType typeWithNoDataValue(QGis::DataType dataType, double *noDataValue)
For given data type returns wider type and sets no data value.
QString number(int n, int base)
int count(const T &value) const
The RasterBandStats struct is a container for statistics about a single raster band.
void processEvents(QFlags< QEventLoop::ProcessEventsFlag > flags)
void append(const T &value)
Thirty two bit floating point (float)
Definition: qgis.h:130
WriterError writeRaster(const QgsRasterPipe *pipe, int nCols, int nRows, QgsRectangle outputExtent, const QgsCoordinateReferenceSystem &crs, QProgressDialog *p=0)
Write raster file.
Raster data container.
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
virtual QGis::DataType srcDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual QList< QgsRasterPyramid > buildPyramidList(QList< int > overviewList=QList< int >())
Accessor for ths raster layers pyramid list.
QString fileName() const
void setAttribute(const QString &name, const QString &value)
bool isEmpty() const
static bool typeIsColor(QGis::DataType type)
Returns true if data type is color.
int maximumTileHeight() const
static int typeSize(int dataType)
QDir dir() const
DataType
Raster data types.
Definition: qgis.h:122
QgsRasterProjector * projector() const
QgsCoordinateReferenceSystem destCrs() const
Get destination CRS.
virtual QGis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
bool readNextRasterPart(int bandNumber, int &nCols, int &nRows, QgsRasterBlock **block, int &topLeftCol, int &topLeftRow)
Fetches next part of raster data, caller takes ownership of the block and caller should delete the bl...
void * GDALDatasetH
void setRange(int minimum, int maximum)
Base class for processing filters like renderers, reprojector, resampler etc.
QDomText createTextNode(const QString &value)
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:375
QString toLower() const
QByteArray toLocal8Bit() const
bool exists() const
void setOutputNoDataValue(int bandNo, double noData)
Set output no data value.
Complex Int16.
Definition: qgis.h:132
virtual QgsRectangle extent() override=0
Get the extent of the data source.
void setMaximumTileWidth(int w)
virtual QGis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
Raster namespace.
Definition: qgsraster.h:28
void clear()
void setMaximumTileHeight(int h)
void save(QTextStream &str, int indent) const
QgsCoordinateReferenceSystem srcCrs() const
Get source CRS.
QgsRasterRangeList noData(int bandNo) const
QString absolutePath() const
virtual bool remove()
Remove dataset.
bool mkdir(const QString &dirName) const
Class for storing a coordinate reference system (CRS)
Class for doing transforms between two map coordinate systems.
char * data()
double minimumValue
The minimum cell value in the raster band.
virtual QString buildPyramids(const QList< QgsRasterPyramid > &thePyramidList, const QString &theResamplingMethod="NEAREST", QgsRaster::RasterPyramidsFormat theFormat=QgsRaster::PyramidsGTiff, const QStringList &theConfigOptions=QStringList())
Create pyramid overviews.
StandardButton warning(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
iterator insert(const Key &key, const T &value)
void show()
virtual bool write(void *data, int band, int width, int height, int xOffset, int yOffset)
Writes into the provider datasource.
Complex Int32.
Definition: qgis.h:133
virtual bool srcHasNoDataValue(int bandNo) const
Return true if source band has no data value.
QRgb color(int row, int column) const
Read a single color.
QDomElement createElement(const QString &tagName)
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:137
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:206
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:139
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:191
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition: qgis.cpp:283
const QgsRasterInterface * input() const
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:128
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:211
Complex Float32.
Definition: qgis.h:134
const T value(const Key &key) const
Base class for raster data providers.
void replace(int i, const T &value)