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