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