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 }
QList< QDomElement > mVRTBands
virtual int bandCount() const =0
Get number of bands.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
QgsRaster::RasterBuildPyramids mBuildPyramidsFlag
bool mTiledMode
False: Write one file, true: create a directory and add the files numbered.
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:111
Iterator for sequentially processing raster cells.
void startRasterRead(int bandNumber, int nCols, int nRows, const QgsRectangle &extent)
Start reading of raster band.
static QgsProviderRegistry * instance(QString pluginPath=QString::null)
means of accessing canonical single instance
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:185
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
bool setValue(int row, int column, double value)
Set value on position.
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.
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.
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, QStringList createOptions=QStringList())
Creates a new dataset with mDataSourceURI.
virtual bool setNoDataValue(int bandNo, double noDataValue)
Set no data value on created dataset.
const QgsRasterPipe * mPipe
bool writeVRT(const QString &file)
int maximumTileWidth() const
virtual QgsRasterBandStats bandStatistics(int theBandNo, int theStats=QgsRasterBandStats::All, const QgsRectangle &theExtent=QgsRectangle(), int theSampleSize=0)
Get band statistics.
static QGis::DataType typeWithNoDataValue(QGis::DataType dataType, double *noDataValue)
For given data type returns wider type and sets no data value.
QString partFileName(int fileIndex)
The RasterBandStats struct is a container for statistics about a single raster band.
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:37
virtual QList< QgsRasterPyramid > buildPyramidList(QList< int > overviewList=QList< int >())
Accessor for ths raster layers pyramid list.
virtual QGis::DataType srcDataType(int bandNo) const =0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual QgsRectangle extent()=0
Get the extent of the data source.
static bool typeIsColor(QGis::DataType type)
Returns true if data type is color.
void buildPyramids(const QString &filename)
virtual QGis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
QgsRaster::RasterPyramidsFormat mPyramidsFormat
int maximumTileHeight() const
static int typeSize(int dataType)
QgsRasterProjector * projector() const
QgsCoordinateReferenceSystem destCrs() const
Get destination CRS.
QgsRasterDataProvider * initOutput(int nCols, int nRows, const QgsCoordinateReferenceSystem &crs, double *geoTransform, int nBands, QGis::DataType type, QList< bool > destHasNoDataValueList=QList< bool >(), QList< double > destNoDataValueList=QList< double >())
Init VRT (for tiled mode) or create global output provider (single-file mode)
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...
Base class for processing filters like renderers, reprojector, resampler etc.
WriterError writeDataRaster(const QgsRasterPipe *pipe, QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &crs, QProgressDialog *progressDialog=0)
const QgsRasterInterface * mInput
QProgressDialog * mProgressDialog
void setOutputNoDataValue(int bandNo, double noData)
Set output no data value.
QString file
Definition: qgssvgcache.cpp:74
void setMaximumTileWidth(int w)
Raster namespace.
Definition: qgsraster.h:26
WriterError writeImageRaster(QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &crs, QProgressDialog *progressDialog=0)
void setMaximumTileHeight(int h)
QgsCoordinateReferenceSystem srcCrs() const
Get source CRS.
QgsRasterRangeList noData(int bandNo) const
virtual bool remove()
Returns the formats supported by create()
void createVRT(int xSize, int ySize, const QgsCoordinateReferenceSystem &crs, double *geoTransform, QGis::DataType type, QList< bool > destHasNoDataValueList, QList< double > destNoDataValueList)
Initialize vrt member variables.
Class for storing a coordinate reference system (CRS)
DataType
Raster data types.
Definition: qgis.h:163
Class for doing transforms between two map coordinate systems.
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.
virtual bool write(void *data, int band, int width, int height, int xOffset, int yOffset)
Writes into the provider datasource.
virtual bool srcHasNoDataValue(int bandNo) const
QRgb color(int row, int column) const
Read a single color.
void globalOutputParameters(const QgsRectangle &extent, int nCols, int &nRows, double *geoTransform, double &pixelSize)
Calculate nRows, geotransform and pixel size for output.
void addToVRT(const QString &filename, int band, int xSize, int ySize, int xOffset, int yOffset)
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:195
QgsRasterDataProvider * createPartProvider(const QgsRectangle &extent, int nCols, int iterCols, int iterRows, int iterLeft, int iterTop, const QString &outputUrl, int fileIndex, int nBands, QGis::DataType type, const QgsCoordinateReferenceSystem &crs)
Create provider and datasource for a part image (vrt mode)
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:180
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition: qgis.cpp:141
const QgsRasterInterface * input() const
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:200
QStringList mPyramidsConfigOptions
Base class for raster data providers.
#define tr(sourceText)