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