QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 "qgsgdalutils.h"
18 #include "qgsrasterfilewriter.h"
19 #include "qgscoordinatetransform.h"
20 #include "qgsproviderregistry.h"
21 #include "qgsrasterinterface.h"
22 #include "qgsrasteriterator.h"
23 #include "qgsrasterlayer.h"
24 #include "qgsrasterprojector.h"
25 #include "qgsrasterdataprovider.h"
26 #include "qgsrasternuller.h"
27 #include "qgsreadwritelocker.h"
28 
29 #include <QCoreApplication>
30 #include <QProgressDialog>
31 #include <QTextStream>
32 #include <QMessageBox>
33 #include <QRegularExpression>
34 
35 #include <cmath>
36 
37 #include <gdal.h>
38 #include <cpl_string.h>
39 #include <mutex>
40 
42 {
43  if ( mTiledMode )
44  return nullptr; // does not make sense with tiled mode
45 
46  double pixelSize;
47  double geoTransform[6];
48  globalOutputParameters( extent, width, height, geoTransform, pixelSize );
49 
50  return initOutput( width, height, crs, geoTransform, 1, dataType, QList<bool>(), QList<double>() );
51 }
52 
54 {
55  if ( mTiledMode )
56  return nullptr; // does not make sense with tiled mode
57 
58  double pixelSize;
59  double geoTransform[6];
60  globalOutputParameters( extent, width, height, geoTransform, pixelSize );
61 
62  return initOutput( width, height, crs, geoTransform, nBands, dataType, QList<bool>(), QList<double>() );
63 }
64 
65 QgsRasterFileWriter::QgsRasterFileWriter( const QString &outputUrl )
66  : mOutputUrl( outputUrl )
67 {
68 
69 }
70 
71 QgsRasterFileWriter::QgsRasterFileWriter()
72 {
73 
74 }
75 
76 
77 // Deprecated!
78 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRasterPipe *pipe, int nCols, int nRows, const QgsRectangle &outputExtent,
80 {
81  return writeRaster( pipe, nCols, nRows, outputExtent, crs, ( pipe && pipe->provider() ) ? pipe->provider()->transformContext() : QgsCoordinateTransformContext(), feedback );
82 }
83 
84 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRasterPipe *pipe, int nCols, int nRows, const QgsRectangle &outputExtent,
85  const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &transformContext,
86  QgsRasterBlockFeedback *feedback )
87 {
88  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
89 
90  if ( !pipe )
91  {
92  return SourceProviderError;
93  }
94  mPipe = pipe;
95 
96  //const QgsRasterInterface* iface = iter->input();
97  const QgsRasterInterface *iface = pipe->last();
98  if ( !iface )
99  {
100  return SourceProviderError;
101  }
102  mInput = iface;
103 
104  if ( QgsRasterBlock::typeIsColor( iface->dataType( 1 ) ) )
105  {
106  mMode = Image;
107  }
108  else
109  {
110  mMode = Raw;
111  }
112 
113  QgsDebugMsgLevel( QStringLiteral( "reading from %1" ).arg( typeid( *iface ).name() ), 4 );
114 
115  if ( !iface->sourceInput() )
116  {
117  QgsDebugMsg( QStringLiteral( "iface->srcInput() == 0" ) );
118  return SourceProviderError;
119  }
120 #ifdef QGISDEBUG
121  const QgsRasterInterface &srcInput = *iface->sourceInput();
122  QgsDebugMsgLevel( QStringLiteral( "srcInput = %1" ).arg( typeid( srcInput ).name() ), 4 );
123 #endif
124 
125  mFeedback = feedback;
126 
127  QgsRasterIterator iter( pipe->last() );
128 
129  //create directory for output files
130  if ( mTiledMode )
131  {
132  QFileInfo fileInfo( mOutputUrl );
133  if ( !fileInfo.exists() )
134  {
135  QDir dir = fileInfo.dir();
136  if ( !dir.mkdir( fileInfo.fileName() ) )
137  {
138  QgsDebugMsg( "Cannot create output VRT directory " + fileInfo.fileName() + " in " + dir.absolutePath() );
139  return CreateDatasourceError;
140  }
141  }
142  }
143 
144  // Remove pre-existing overview files to avoid using those with new raster
145  QFile pyramidFile( mOutputUrl + ( mTiledMode ? ".vrt.ovr" : ".ovr" ) );
146  if ( pyramidFile.exists() )
147  pyramidFile.remove();
148  pyramidFile.setFileName( mOutputUrl + ( mTiledMode ? ".vrt.rrd" : ".rrd" ) );
149  if ( pyramidFile.exists() )
150  pyramidFile.remove();
151 
152  if ( mMode == Image )
153  {
154  WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, feedback );
155  return e;
156  }
157  else
158  {
159  WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, transformContext, feedback );
160  return e;
161  }
162 }
163 
164 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe *pipe, QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,
165  const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &transformContext, QgsRasterBlockFeedback *feedback )
166 {
167  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
168  if ( !iter )
169  {
170  return SourceProviderError;
171  }
172 
173  const QgsRasterInterface *iface = pipe->last();
174  if ( !iface )
175  {
176  return SourceProviderError;
177  }
178 
179  QgsRasterDataProvider *srcProvider = const_cast<QgsRasterDataProvider *>( dynamic_cast<const QgsRasterDataProvider *>( iface->sourceInput() ) );
180  if ( !srcProvider )
181  {
182  QgsDebugMsg( QStringLiteral( "Cannot get source data provider" ) );
183  return SourceProviderError;
184  }
185 
186  iter->setMaximumTileWidth( mMaxTileWidth );
187  iter->setMaximumTileHeight( mMaxTileHeight );
188 
189  int nBands = iface->bandCount();
190  if ( nBands < 1 )
191  {
192  return SourceProviderError;
193  }
194 
195 
196  //check if all the bands have the same data type size, otherwise we cannot write it to the provider
197  //(at least not with the current interface)
198  int dataTypeSize = QgsRasterBlock::typeSize( srcProvider->sourceDataType( 1 ) );
199  for ( int i = 2; i <= nBands; ++i )
200  {
201  if ( QgsRasterBlock::typeSize( srcProvider->sourceDataType( 1 ) ) != dataTypeSize )
202  {
203  return DestProviderError;
204  }
205  }
206 
207  // Output data type - source data type is preferred but it may happen that we need
208  // to set 'no data' value (which was not set on source data) if output extent
209  // is larger than source extent (with or without reprojection) and there is no 'free'
210  // (not used) value available
211  QList<bool> destHasNoDataValueList;
212  QList<double> destNoDataValueList;
213  QList<Qgis::DataType> destDataTypeList;
214  destDataTypeList.reserve( nBands );
215  destHasNoDataValueList.reserve( nBands );
216  destNoDataValueList.reserve( nBands );
217 
218  const bool isGpkgOutput = mOutputProviderKey == "gdal" &&
219  mOutputFormat.compare( QLatin1String( "gpkg" ), Qt::CaseInsensitive ) == 0;
220  double pixelSize;
221  double geoTransform[6];
222  globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
223  const auto srcProviderExtent( srcProvider->extent() );
224 
225  for ( int bandNo = 1; bandNo <= nBands; bandNo++ )
226  {
227  QgsRasterNuller *nuller = pipe->nuller();
228 
229  const bool srcHasNoDataValue = srcProvider->sourceHasNoDataValue( bandNo );
230  bool destHasNoDataValue = false;
231  double destNoDataValue = std::numeric_limits<double>::quiet_NaN();
232  const Qgis::DataType srcDataType = srcProvider->sourceDataType( bandNo );
233  Qgis::DataType destDataType = srcDataType;
234  // TODO: verify what happens/should happen if srcNoDataValue is disabled by setUseSrcNoDataValue
235  QgsDebugMsgLevel( QStringLiteral( "srcHasNoDataValue = %1 srcNoDataValue = %2" ).arg( srcHasNoDataValue ).arg( srcProvider->sourceNoDataValue( bandNo ) ), 4 );
236  if ( srcHasNoDataValue )
237  {
238 
239  // If source has no data value, it is used by provider
240  destNoDataValue = srcProvider->sourceNoDataValue( bandNo );
241  destHasNoDataValue = true;
242  }
243  else if ( nuller && !nuller->noData( bandNo ).isEmpty() )
244  {
245  // Use one user defined no data value
246  destNoDataValue = nuller->noData( bandNo ).value( 0 ).min();
247  destHasNoDataValue = true;
248  }
249  // GeoPackage does not support nodata for Byte output, and does not
250  // support non-Byte multiband output, so do not take the risk of an accidental
251  // data type promotion.
252  else if ( !( isGpkgOutput && destDataType == Qgis::DataType::Byte ) )
253  {
254  // Verify if we really need no data value, i.e.
255  QgsRectangle outputExtentInSrcCrs = outputExtent;
256  QgsRasterProjector *projector = pipe->projector();
257  if ( projector && projector->destinationCrs() != projector->sourceCrs() )
258  {
259  QgsCoordinateTransform ct( projector->destinationCrs(), projector->sourceCrs(), transformContext );
260  outputExtentInSrcCrs = ct.transformBoundingBox( outputExtent );
261  }
262  if ( !srcProviderExtent.contains( outputExtentInSrcCrs ) &&
263  ( std::fabs( srcProviderExtent.xMinimum() - outputExtentInSrcCrs.xMinimum() ) > geoTransform[1] / 2 ||
264  std::fabs( srcProviderExtent.xMaximum() - outputExtentInSrcCrs.xMaximum() ) > geoTransform[1] / 2 ||
265  std::fabs( srcProviderExtent.yMinimum() - outputExtentInSrcCrs.yMinimum() ) > std::fabs( geoTransform[5] ) / 2 ||
266  std::fabs( srcProviderExtent.yMaximum() - outputExtentInSrcCrs.yMaximum() ) > std::fabs( geoTransform[5] ) / 2 ) )
267  {
268  // Destination extent is (at least partially) outside of source extent, we need destination no data values
269  // Get src sample statistics (estimation from sample)
270  QgsRasterBandStats stats = srcProvider->bandStatistics( bandNo, QgsRasterBandStats::Min | QgsRasterBandStats::Max, outputExtentInSrcCrs, 250000 );
271 
272  // Test if we have free (not used) values
273  const double typeMinValue = QgsContrastEnhancement::minimumValuePossible( srcDataType );
274  const double typeMaxValue = QgsContrastEnhancement::maximumValuePossible( srcDataType );
275  if ( stats.minimumValue > typeMinValue )
276  {
277  destNoDataValue = typeMinValue;
278  }
279  else if ( stats.maximumValue < typeMaxValue )
280  {
281  destNoDataValue = typeMaxValue;
282  }
283  else
284  {
285  // We have to use wider type
286  destDataType = QgsRasterBlock::typeWithNoDataValue( destDataType, &destNoDataValue );
287  }
288  destHasNoDataValue = true;
289  }
290  }
291 
292  if ( nuller && destHasNoDataValue )
293  {
294  nuller->setOutputNoDataValue( bandNo, destNoDataValue );
295  }
296 
297  QgsDebugMsgLevel( QStringLiteral( "bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( qgsEnumValueToKey( destDataType ) ).arg( destHasNoDataValue ).arg( destNoDataValue ), 4 );
298  destDataTypeList.append( destDataType );
299  destHasNoDataValueList.append( destHasNoDataValue );
300  destNoDataValueList.append( destNoDataValue );
301  }
302 
303  Qgis::DataType destDataType = destDataTypeList.value( 0 );
304  // Currently write API supports one output type for dataset only -> find the widest
305  for ( int i = 1; i < nBands; i++ )
306  {
307  if ( destDataTypeList.value( i ) > destDataType )
308  {
309  destDataType = destDataTypeList.value( i );
310  // no data value may be left per band (for future)
311  }
312  }
313 
314  WriterError error;
315  for ( int attempt = 0; attempt < 2; attempt ++ )
316  {
317  //create destProvider for whole dataset here
318  // initOutput() returns 0 in tile mode!
319  std::unique_ptr<QgsRasterDataProvider> destProvider(
320  initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList ) );
321  if ( !mTiledMode )
322  {
323  if ( !destProvider )
324  {
325  return CreateDatasourceError;
326  }
327  if ( !destProvider->isValid() )
328  {
329  if ( feedback && !destProvider->error().isEmpty() )
330  {
331  feedback->appendError( destProvider->error().summary() );
332  }
333  return CreateDatasourceError;
334  }
335  if ( nCols != destProvider->xSize() || nRows != destProvider->ySize() )
336  {
337  QgsDebugMsg( QStringLiteral( "Created raster does not have requested dimensions" ) );
338  if ( feedback )
339  {
340  feedback->appendError( QObject::tr( "Created raster does not have requested dimensions" ) );
341  }
342  return CreateDatasourceError;
343  }
344  if ( nBands != destProvider->bandCount() )
345  {
346  QgsDebugMsg( QStringLiteral( "Created raster does not have requested band count" ) );
347  if ( feedback )
348  {
349  feedback->appendError( QObject::tr( "Created raster does not have requested band count" ) );
350  }
351  return CreateDatasourceError;
352  }
353  if ( nBands )
354  {
355  // Some driver like GS7BG may accept Byte as requested data type,
356  // but actually return a driver with Float64...
357  destDataType = destProvider->dataType( 1 );
358  }
359  }
360 
361  error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider.get(), feedback );
362 
363  if ( attempt == 0 && error == NoDataConflict )
364  {
365  // The value used for no data was found in source data, we must use wider data type
366  if ( destProvider ) // no tiles
367  {
368  destProvider->remove();
369  destProvider.reset();
370  }
371  else // VRT
372  {
373  // TODO: remove created VRT
374  }
375 
376  // But we don't know which band -> wider all
377  for ( int i = 0; i < nBands; i++ )
378  {
379  double destNoDataValue;
380  Qgis::DataType destDataType = QgsRasterBlock::typeWithNoDataValue( destDataTypeList.value( i ), &destNoDataValue );
381  destDataTypeList.replace( i, destDataType );
382  destNoDataValueList.replace( i, destNoDataValue );
383  }
384  destDataType = destDataTypeList.value( 0 );
385 
386  // Try again
387  }
388  else
389  {
390  break;
391  }
392  }
393 
394  return error;
395 }
396 
397 static int qgsDivRoundUp( int a, int b )
398 {
399  return a / b + ( ( ( a % b ) != 0 ) ? 1 : 0 );
400 }
401 
402 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe *pipe,
403  QgsRasterIterator *iter,
404  int nCols, int nRows,
405  const QgsRectangle &outputExtent,
407  Qgis::DataType destDataType,
408  const QList<bool> &destHasNoDataValueList,
409  const QList<double> &destNoDataValueList,
410  QgsRasterDataProvider *destProvider,
411  QgsRasterBlockFeedback *feedback )
412 {
413  Q_UNUSED( pipe )
414  Q_UNUSED( destHasNoDataValueList )
415  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
416 
417  const QgsRasterInterface *iface = iter->input();
418  const QgsRasterDataProvider *srcProvider = dynamic_cast<const QgsRasterDataProvider *>( iface->sourceInput() );
419  int nBands = iface->bandCount();
420  QgsDebugMsgLevel( QStringLiteral( "nBands = %1" ).arg( nBands ), 4 );
421 
422  //Get output map units per pixel
423  int iterLeft = 0;
424  int iterTop = 0;
425  int iterCols = 0;
426  int iterRows = 0;
427 
428  std::vector< std::unique_ptr<QgsRasterBlock> > blockList;
429  std::vector< std::unique_ptr<QgsRasterBlock> > destBlockList;
430 
431  blockList.resize( nBands );
432  destBlockList.resize( nBands );
433 
434  for ( int i = 1; i <= nBands; ++i )
435  {
436  iter->startRasterRead( i, nCols, nRows, outputExtent );
437  if ( destProvider && destHasNoDataValueList.value( i - 1 ) ) // no tiles
438  {
439  destProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
440  }
441  }
442 
443  int nParts = 0;
444  int fileIndex = 0;
445  if ( feedback )
446  {
447  int nPartsX = qgsDivRoundUp( nCols, iter->maximumTileWidth() );
448  int nPartsY = qgsDivRoundUp( nRows, iter->maximumTileHeight() );
449  nParts = nPartsX * nPartsY;
450  }
451 
452  // hmm why is there a for(;;) here ..
453  // not good coding practice IMHO, it might be better to use [ for() and break ] or [ while (test) ]
454  Q_FOREVER
455  {
456  for ( int i = 1; i <= nBands; ++i )
457  {
458  QgsRasterBlock *block = nullptr;
459  if ( !iter->readNextRasterPart( i, iterCols, iterRows, &block, iterLeft, iterTop ) )
460  {
461  // No more parts, create VRT and return
462  if ( mTiledMode )
463  {
464  QString vrtFilePath( mOutputUrl + '/' + vrtFileName() );
465  writeVRT( vrtFilePath );
466  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
467  {
468  buildPyramids( vrtFilePath );
469  }
470  }
471  else
472  {
473  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
474  {
475  buildPyramids( mOutputUrl, destProvider );
476  }
477  }
478 
479  QgsDebugMsgLevel( QStringLiteral( "Done" ), 4 );
480  return NoError; //reached last tile, bail out
481  }
482  blockList[i - 1].reset( block );
483  // TODO: verify if NoDataConflict happened, to do that we need the whole pipe or nuller interface
484  }
485 
486  if ( feedback && fileIndex < ( nParts - 1 ) )
487  {
488  feedback->setProgress( 100.0 * fileIndex / static_cast< double >( nParts ) );
489  if ( feedback->isCanceled() )
490  {
491  break;
492  }
493  }
494 
495  // It may happen that internal data type (dataType) is wider than destDataType
496  for ( int i = 1; i <= nBands; ++i )
497  {
498  if ( srcProvider && srcProvider->dataType( i ) == destDataType )
499  {
500  // nothing
501  }
502  else
503  {
504  // TODO: this conversion should go to QgsRasterDataProvider::write with additional input data type param
505  blockList[i - 1]->convert( destDataType );
506  }
507  destBlockList[i - 1] = std::move( blockList[i - 1] );
508  }
509 
510  if ( mTiledMode ) //write to file
511  {
512  std::unique_ptr< QgsRasterDataProvider > partDestProvider( createPartProvider( outputExtent,
513  nCols, iterCols, iterRows,
514  iterLeft, iterTop, mOutputUrl,
515  fileIndex, nBands, destDataType, crs ) );
516 
517  if ( !partDestProvider || !partDestProvider->isValid() )
518  {
519  return DestProviderError;
520  }
521 
522  //write data to output file. todo: loop over the data list
523  for ( int i = 1; i <= nBands; ++i )
524  {
525  if ( destHasNoDataValueList.value( i - 1 ) )
526  {
527  partDestProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
528  }
529  if ( destBlockList[ i - 1 ]->isEmpty() )
530  continue;
531 
532  if ( !partDestProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, 0, 0 ) )
533  {
534  return WriteError;
535  }
536  addToVRT( partFileName( fileIndex ), i, iterCols, iterRows, iterLeft, iterTop );
537  }
538 
539  }
540  else if ( destProvider )
541  {
542  //loop over data
543  for ( int i = 1; i <= nBands; ++i )
544  {
545  if ( destBlockList[ i - 1 ]->isEmpty() )
546  continue;
547 
548  if ( !destProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, iterLeft, iterTop ) )
549  {
550  return WriteError;
551  }
552  }
553  }
554  ++fileIndex;
555  }
556 
557  QgsDebugMsgLevel( QStringLiteral( "Done" ), 4 );
558  return ( feedback && feedback->isCanceled() ) ? WriteCanceled : NoError;
559 }
560 
561 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,
563 {
564  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
565  if ( !iter )
566  {
567  return SourceProviderError;
568  }
569 
570  const QgsRasterInterface *iface = iter->input();
571  if ( !iface )
572  return SourceProviderError;
573 
574  Qgis::DataType inputDataType = iface->dataType( 1 );
575  if ( inputDataType != Qgis::DataType::ARGB32 && inputDataType != Qgis::DataType::ARGB32_Premultiplied )
576  {
577  return SourceProviderError;
578  }
579  const bool isPremultiplied = ( inputDataType == Qgis::DataType::ARGB32_Premultiplied );
580 
581  iter->setMaximumTileWidth( mMaxTileWidth );
582  iter->setMaximumTileHeight( mMaxTileHeight );
583 
584  const size_t nMaxPixels = static_cast<size_t>( mMaxTileWidth ) * mMaxTileHeight;
585  std::vector<unsigned char> redData( nMaxPixels );
586  std::vector<unsigned char> greenData( nMaxPixels );
587  std::vector<unsigned char> blueData( nMaxPixels );
588  std::vector<unsigned char> alphaData( nMaxPixels );
589  int iterLeft = 0, iterTop = 0, iterCols = 0, iterRows = 0;
590  int fileIndex = 0;
591 
592  //create destProvider for whole dataset here
593  double pixelSize;
594  double geoTransform[6];
595  globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
596 
597  const int nOutputBands = 4;
598  std::unique_ptr< QgsRasterDataProvider > destProvider( initOutput( nCols, nRows, crs, geoTransform, nOutputBands, Qgis::DataType::Byte ) );
599  if ( !mTiledMode )
600  {
601  if ( !destProvider )
602  {
603  return CreateDatasourceError;
604  }
605  if ( !destProvider->isValid() )
606  {
607  if ( feedback && !destProvider->error().isEmpty() )
608  {
609  feedback->appendError( destProvider->error().summary() );
610  }
611  return CreateDatasourceError;
612  }
613  if ( nCols != destProvider->xSize() || nRows != destProvider->ySize() )
614  {
615  QgsDebugMsg( QStringLiteral( "Created raster does not have requested dimensions" ) );
616  if ( feedback )
617  {
618  feedback->appendError( QObject::tr( "Created raster does not have requested dimensions" ) );
619  }
620  return CreateDatasourceError;
621  }
622  if ( nOutputBands != destProvider->bandCount() )
623  {
624  QgsDebugMsg( QStringLiteral( "Created raster does not have requested band count" ) );
625  if ( feedback )
626  {
627  feedback->appendError( QObject::tr( "Created raster does not have requested band count" ) );
628  }
629  return CreateDatasourceError;
630  }
631  if ( Qgis::DataType::Byte != destProvider->dataType( 1 ) )
632  {
633  QgsDebugMsg( QStringLiteral( "Created raster does not have requested data type" ) );
634  if ( feedback )
635  {
636  feedback->appendError( QObject::tr( "Created raster does not have requested data type" ) );
637  }
638  return CreateDatasourceError;
639  }
640  }
641 
642  iter->startRasterRead( 1, nCols, nRows, outputExtent, feedback );
643 
644  int nParts = 0;
645  if ( feedback )
646  {
647  int nPartsX = qgsDivRoundUp( nCols, iter->maximumTileWidth() );
648  int nPartsY = qgsDivRoundUp( nRows, iter->maximumTileHeight() );
649  nParts = nPartsX * nPartsY;
650  }
651 
652  std::unique_ptr< QgsRasterBlock > inputBlock;
653  while ( iter->readNextRasterPart( 1, iterCols, iterRows, inputBlock, iterLeft, iterTop ) )
654  {
655  if ( !inputBlock || inputBlock->isEmpty() )
656  {
657  continue;
658  }
659 
660  if ( feedback && fileIndex < ( nParts - 1 ) )
661  {
662  feedback->setProgress( 100.0 * fileIndex / static_cast< double >( nParts ) );
663  if ( feedback->isCanceled() )
664  {
665  break;
666  }
667  }
668 
669  //fill into red/green/blue/alpha channels
670  qgssize nPixels = static_cast< qgssize >( iterCols ) * iterRows;
671  for ( qgssize i = 0; i < nPixels; ++i )
672  {
673  QRgb c = inputBlock->color( i );
674  if ( isPremultiplied )
675  {
676  c = qUnpremultiply( c );
677  }
678  redData[i] = static_cast<unsigned char>( qRed( c ) );
679  greenData[i] = static_cast<unsigned char>( qGreen( c ) );
680  blueData[i] = static_cast<unsigned char>( qBlue( c ) );
681  alphaData[i] = static_cast<unsigned char>( qAlpha( c ) );
682  }
683 
684  //create output file
685  if ( mTiledMode )
686  {
687  std::unique_ptr< QgsRasterDataProvider > partDestProvider( createPartProvider( outputExtent,
688  nCols, iterCols, iterRows,
689  iterLeft, iterTop, mOutputUrl, fileIndex,
690  4, Qgis::DataType::Byte, crs ) );
691 
692  if ( !partDestProvider || partDestProvider->isValid() )
693  {
694  return DestProviderError;
695  }
696 
697  //write data to output file
698  if ( !partDestProvider->write( &redData[0], 1, iterCols, iterRows, 0, 0 ) ||
699  !partDestProvider->write( &greenData[0], 2, iterCols, iterRows, 0, 0 ) ||
700  !partDestProvider->write( &blueData[0], 3, iterCols, iterRows, 0, 0 ) ||
701  !partDestProvider->write( &alphaData[0], 4, iterCols, iterRows, 0, 0 ) )
702  {
703  return WriteError;
704  }
705 
706  addToVRT( partFileName( fileIndex ), 1, iterCols, iterRows, iterLeft, iterTop );
707  addToVRT( partFileName( fileIndex ), 2, iterCols, iterRows, iterLeft, iterTop );
708  addToVRT( partFileName( fileIndex ), 3, iterCols, iterRows, iterLeft, iterTop );
709  addToVRT( partFileName( fileIndex ), 4, iterCols, iterRows, iterLeft, iterTop );
710  }
711  else if ( destProvider )
712  {
713  if ( !destProvider->write( &redData[0], 1, iterCols, iterRows, iterLeft, iterTop ) ||
714  !destProvider->write( &greenData[0], 2, iterCols, iterRows, iterLeft, iterTop ) ||
715  !destProvider->write( &blueData[0], 3, iterCols, iterRows, iterLeft, iterTop ) ||
716  !destProvider->write( &alphaData[0], 4, iterCols, iterRows, iterLeft, iterTop ) )
717  {
718  return WriteError;
719  }
720  }
721 
722  ++fileIndex;
723  }
724  destProvider.reset();
725 
726  if ( feedback )
727  {
728  feedback->setProgress( 100.0 );
729  }
730 
731  if ( mTiledMode )
732  {
733  QString vrtFilePath( mOutputUrl + '/' + vrtFileName() );
734  writeVRT( vrtFilePath );
735  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
736  {
737  buildPyramids( vrtFilePath );
738  }
739  }
740  else
741  {
742  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
743  {
744  buildPyramids( mOutputUrl );
745  }
746  }
747  return ( feedback && feedback->isCanceled() ) ? WriteCanceled : NoError;
748 }
749 
750 void QgsRasterFileWriter::addToVRT( const QString &filename, int band, int xSize, int ySize, int xOffset, int yOffset )
751 {
752  QDomElement bandElem = mVRTBands.value( band - 1 );
753 
754  QDomElement simpleSourceElem = mVRTDocument.createElement( QStringLiteral( "SimpleSource" ) );
755 
756  //SourceFilename
757  QDomElement sourceFilenameElem = mVRTDocument.createElement( QStringLiteral( "SourceFilename" ) );
758  sourceFilenameElem.setAttribute( QStringLiteral( "relativeToVRT" ), QStringLiteral( "1" ) );
759  QDomText sourceFilenameText = mVRTDocument.createTextNode( filename );
760  sourceFilenameElem.appendChild( sourceFilenameText );
761  simpleSourceElem.appendChild( sourceFilenameElem );
762 
763  //SourceBand
764  QDomElement sourceBandElem = mVRTDocument.createElement( QStringLiteral( "SourceBand" ) );
765  QDomText sourceBandText = mVRTDocument.createTextNode( QString::number( band ) );
766  sourceBandElem.appendChild( sourceBandText );
767  simpleSourceElem.appendChild( sourceBandElem );
768 
769  //SourceProperties
770  QDomElement sourcePropertiesElem = mVRTDocument.createElement( QStringLiteral( "SourceProperties" ) );
771  sourcePropertiesElem.setAttribute( QStringLiteral( "RasterXSize" ), xSize );
772  sourcePropertiesElem.setAttribute( QStringLiteral( "RasterYSize" ), ySize );
773  sourcePropertiesElem.setAttribute( QStringLiteral( "BlockXSize" ), xSize );
774  sourcePropertiesElem.setAttribute( QStringLiteral( "BlockYSize" ), ySize );
775  sourcePropertiesElem.setAttribute( QStringLiteral( "DataType" ), QStringLiteral( "Byte" ) );
776  simpleSourceElem.appendChild( sourcePropertiesElem );
777 
778  //SrcRect
779  QDomElement srcRectElem = mVRTDocument.createElement( QStringLiteral( "SrcRect" ) );
780  srcRectElem.setAttribute( QStringLiteral( "xOff" ), QStringLiteral( "0" ) );
781  srcRectElem.setAttribute( QStringLiteral( "yOff" ), QStringLiteral( "0" ) );
782  srcRectElem.setAttribute( QStringLiteral( "xSize" ), xSize );
783  srcRectElem.setAttribute( QStringLiteral( "ySize" ), ySize );
784  simpleSourceElem.appendChild( srcRectElem );
785 
786  //DstRect
787  QDomElement dstRectElem = mVRTDocument.createElement( QStringLiteral( "DstRect" ) );
788  dstRectElem.setAttribute( QStringLiteral( "xOff" ), xOffset );
789  dstRectElem.setAttribute( QStringLiteral( "yOff" ), yOffset );
790  dstRectElem.setAttribute( QStringLiteral( "xSize" ), xSize );
791  dstRectElem.setAttribute( QStringLiteral( "ySize" ), ySize );
792  simpleSourceElem.appendChild( dstRectElem );
793 
794  bandElem.appendChild( simpleSourceElem );
795 }
796 
797 void QgsRasterFileWriter::buildPyramids( const QString &filename, QgsRasterDataProvider *destProviderIn )
798 {
799  QgsDebugMsgLevel( "filename = " + filename, 4 );
800  // open new dataProvider so we can build pyramids with it
801  QgsDataProvider::ProviderOptions providerOptions;
802  QgsRasterDataProvider *destProvider = destProviderIn;
803  if ( !destProvider )
804  {
805  destProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mOutputProviderKey, filename, providerOptions ) );
806  if ( !destProvider || !destProvider->isValid() )
807  {
808  delete destProvider;
809  return;
810  }
811  }
812 
813  // TODO progress report
814  // TODO test mTiledMode - not tested b/c segfault at line # 289
815  // connect( provider, SIGNAL( progressUpdate( int ) ), mPyramidProgress, SLOT( setValue( int ) ) );
816  QList< QgsRasterPyramid> myPyramidList;
817  if ( ! mPyramidsList.isEmpty() )
818  myPyramidList = destProvider->buildPyramidList( mPyramidsList );
819  for ( int myCounterInt = 0; myCounterInt < myPyramidList.count(); myCounterInt++ )
820  {
821  myPyramidList[myCounterInt].setBuild( true );
822  }
823 
824  QgsDebugMsgLevel( QStringLiteral( "building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ), 4 );
825  // QApplication::setOverrideCursor( Qt::WaitCursor );
826  QString res = destProvider->buildPyramids( myPyramidList, mPyramidsResampling,
827  mPyramidsFormat, mPyramidsConfigOptions );
828  // QApplication::restoreOverrideCursor();
829 
830  // TODO put this in provider or elsewhere
831  if ( !res.isNull() )
832  {
833  QString title, message;
834  if ( res == QLatin1String( "ERROR_WRITE_ACCESS" ) )
835  {
836  title = QObject::tr( "Building Pyramids" );
837  message = QObject::tr( "Write access denied. Adjust the file permissions and try again." );
838  }
839  else if ( res == QLatin1String( "ERROR_WRITE_FORMAT" ) )
840  {
841  title = QObject::tr( "Building Pyramids" );
842  message = QObject::tr( "The file was not writable. Some formats do not "
843  "support pyramid overviews. Consult the GDAL documentation if in doubt." );
844  }
845  else if ( res == QLatin1String( "FAILED_NOT_SUPPORTED" ) )
846  {
847  title = QObject::tr( "Building Pyramids" );
848  message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
849  }
850  else if ( res == QLatin1String( "ERROR_VIRTUAL" ) )
851  {
852  title = QObject::tr( "Building Pyramids" );
853  message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
854  }
855  QMessageBox::warning( nullptr, title, message );
856  QgsDebugMsgLevel( res + " - " + message, 4 );
857  }
858  if ( !destProviderIn )
859  delete destProvider;
860 }
861 
862 #if 0
863 int QgsRasterFileWriter::pyramidsProgress( double dfComplete, const char *pszMessage, void *pData )
864 {
865  Q_UNUSED( pszMessage )
866  GDALTermProgress( dfComplete, 0, 0 );
867  QProgressDialog *progressDialog = static_cast<QProgressDialog *>( pData );
868  if ( pData && progressDialog->wasCanceled() )
869  {
870  return 0;
871  }
872 
873  if ( pData )
874  {
875  progressDialog->setRange( 0, 100 );
876  progressDialog->setValue( dfComplete * 100 );
877  }
878  return 1;
879 }
880 #endif
881 
882 void QgsRasterFileWriter::createVRT( int xSize, int ySize, const QgsCoordinateReferenceSystem &crs, double *geoTransform, Qgis::DataType type, const QList<bool> &destHasNoDataValueList, const QList<double> &destNoDataValueList )
883 {
884  mVRTDocument.clear();
885  QDomElement VRTDatasetElem = mVRTDocument.createElement( QStringLiteral( "VRTDataset" ) );
886 
887  //xsize / ysize
888  VRTDatasetElem.setAttribute( QStringLiteral( "rasterXSize" ), xSize );
889  VRTDatasetElem.setAttribute( QStringLiteral( "rasterYSize" ), ySize );
890  mVRTDocument.appendChild( VRTDatasetElem );
891 
892  //CRS
893  QDomElement SRSElem = mVRTDocument.createElement( QStringLiteral( "SRS" ) );
894  QDomText crsText = mVRTDocument.createTextNode( crs.toWkt() );
895  SRSElem.appendChild( crsText );
896  VRTDatasetElem.appendChild( SRSElem );
897 
898  //geotransform
899  if ( geoTransform )
900  {
901  QDomElement geoTransformElem = mVRTDocument.createElement( QStringLiteral( "GeoTransform" ) );
902  QString geoTransformString = QString::number( geoTransform[0], 'f', 6 ) + ", " + QString::number( geoTransform[1] ) + ", " + QString::number( geoTransform[2] ) +
903  ", " + QString::number( geoTransform[3], 'f', 6 ) + ", " + QString::number( geoTransform[4] ) + ", " + QString::number( geoTransform[5] );
904  QDomText geoTransformText = mVRTDocument.createTextNode( geoTransformString );
905  geoTransformElem.appendChild( geoTransformText );
906  VRTDatasetElem.appendChild( geoTransformElem );
907  }
908 
909  int nBands;
910  if ( mMode == Raw )
911  {
912  nBands = mInput->bandCount();
913  }
914  else
915  {
916  nBands = 4;
917  }
918 
919  QStringList colorInterp;
920  colorInterp << QStringLiteral( "Red" ) << QStringLiteral( "Green" ) << QStringLiteral( "Blue" ) << QStringLiteral( "Alpha" );
921 
922  QMap<Qgis::DataType, QString> dataTypes;
923  dataTypes.insert( Qgis::DataType::Byte, QStringLiteral( "Byte" ) );
924  dataTypes.insert( Qgis::DataType::UInt16, QStringLiteral( "UInt16" ) );
925  dataTypes.insert( Qgis::DataType::Int16, QStringLiteral( "Int16" ) );
926  dataTypes.insert( Qgis::DataType::UInt32, QStringLiteral( "Int32" ) );
927  dataTypes.insert( Qgis::DataType::Float32, QStringLiteral( "Float32" ) );
928  dataTypes.insert( Qgis::DataType::Float64, QStringLiteral( "Float64" ) );
929  dataTypes.insert( Qgis::DataType::CInt16, QStringLiteral( "CInt16" ) );
930  dataTypes.insert( Qgis::DataType::CInt32, QStringLiteral( "CInt32" ) );
931  dataTypes.insert( Qgis::DataType::CFloat32, QStringLiteral( "CFloat32" ) );
932  dataTypes.insert( Qgis::DataType::CFloat64, QStringLiteral( "CFloat64" ) );
933 
934  for ( int i = 1; i <= nBands; i++ )
935  {
936  QDomElement VRTBand = mVRTDocument.createElement( QStringLiteral( "VRTRasterBand" ) );
937 
938  VRTBand.setAttribute( QStringLiteral( "band" ), QString::number( i ) );
939  QString dataType = dataTypes.value( type );
940  VRTBand.setAttribute( QStringLiteral( "dataType" ), dataType );
941 
942  if ( mMode == Image )
943  {
944  VRTBand.setAttribute( QStringLiteral( "dataType" ), QStringLiteral( "Byte" ) );
945  QDomElement colorInterpElement = mVRTDocument.createElement( QStringLiteral( "ColorInterp" ) );
946  QDomText interpText = mVRTDocument.createTextNode( colorInterp.value( i - 1 ) );
947  colorInterpElement.appendChild( interpText );
948  VRTBand.appendChild( colorInterpElement );
949  }
950 
951  if ( !destHasNoDataValueList.isEmpty() && destHasNoDataValueList.value( i - 1 ) )
952  {
953  VRTBand.setAttribute( QStringLiteral( "NoDataValue" ), QString::number( destNoDataValueList.value( i - 1 ) ) );
954  }
955 
956  mVRTBands.append( VRTBand );
957  VRTDatasetElem.appendChild( VRTBand );
958  }
959 }
960 
961 bool QgsRasterFileWriter::writeVRT( const QString &file )
962 {
963  QFile outputFile( file );
964  if ( ! outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
965  {
966  return false;
967  }
968 
969  QTextStream outStream( &outputFile );
970  mVRTDocument.save( outStream, 2 );
971  return true;
972 }
973 
974 QgsRasterDataProvider *QgsRasterFileWriter::createPartProvider( const QgsRectangle &extent, int nCols, int iterCols,
975  int iterRows, int iterLeft, int iterTop, const QString &outputUrl, int fileIndex, int nBands, Qgis::DataType type,
977 {
978  double mup = extent.width() / nCols;
979  double mapLeft = extent.xMinimum() + iterLeft * mup;
980  double mapRight = mapLeft + mup * iterCols;
981  double mapTop = extent.yMaximum() - iterTop * mup;
982  double mapBottom = mapTop - iterRows * mup;
983  QgsRectangle mapRect( mapLeft, mapBottom, mapRight, mapTop );
984 
985  QString outputFile = outputUrl + '/' + partFileName( fileIndex );
986 
987  //geotransform
988  double geoTransform[6];
989  geoTransform[0] = mapRect.xMinimum();
990  geoTransform[1] = mup;
991  geoTransform[2] = 0.0;
992  geoTransform[3] = mapRect.yMaximum();
993  geoTransform[4] = 0.0;
994  geoTransform[5] = -mup;
995 
996  // perhaps we need a separate createOptions for tiles ?
997 
998  QgsRasterDataProvider *destProvider = QgsRasterDataProvider::create( mOutputProviderKey, outputFile, mOutputFormat, nBands, type, iterCols, iterRows, geoTransform, crs, mCreateOptions );
999 
1000  // TODO: return provider and report error
1001  return destProvider;
1002 }
1003 
1004 QgsRasterDataProvider *QgsRasterFileWriter::initOutput( int nCols, int nRows, const QgsCoordinateReferenceSystem &crs,
1005  double *geoTransform, int nBands, Qgis::DataType type,
1006  const QList<bool> &destHasNoDataValueList, const QList<double> &destNoDataValueList )
1007 {
1008  if ( mTiledMode )
1009  {
1010  createVRT( nCols, nRows, crs, geoTransform, type, destHasNoDataValueList, destNoDataValueList );
1011  return nullptr;
1012  }
1013  else
1014  {
1015 #if 0
1016  // TODO enable "use existing", has no effect for now, because using Create() in gdal provider
1017  // should this belong in provider? should also test that source provider is gdal
1018  if ( mBuildPyramidsFlag == -4 && mOutputProviderKey == "gdal" && mOutputFormat.compare( QLatin1String( "gtiff" ), Qt::CaseInsensitive ) == 0 )
1019  mCreateOptions << "COPY_SRC_OVERVIEWS=YES";
1020 #endif
1021 
1022  QgsRasterDataProvider *destProvider = QgsRasterDataProvider::create( mOutputProviderKey, mOutputUrl, mOutputFormat, nBands, type, nCols, nRows, geoTransform, crs, mCreateOptions );
1023 
1024  if ( !destProvider )
1025  {
1026  QgsDebugMsg( QStringLiteral( "No provider created" ) );
1027  }
1028 
1029  return destProvider;
1030  }
1031 }
1032 
1033 void QgsRasterFileWriter::globalOutputParameters( const QgsRectangle &extent, int nCols, int &nRows,
1034  double *geoTransform, double &pixelSize )
1035 {
1036  pixelSize = extent.width() / nCols;
1037 
1038  //calculate nRows automatically for providers without exact resolution
1039  if ( nRows < 0 )
1040  {
1041  nRows = static_cast< double >( nCols ) / extent.width() * extent.height() + 0.5; //NOLINT
1042  }
1043  geoTransform[0] = extent.xMinimum();
1044  geoTransform[1] = pixelSize;
1045  geoTransform[2] = 0.0;
1046  geoTransform[3] = extent.yMaximum();
1047  geoTransform[4] = 0.0;
1048  geoTransform[5] = -( extent.height() / nRows );
1049 }
1050 
1051 QString QgsRasterFileWriter::partFileName( int fileIndex )
1052 {
1053  // .tif for now
1054  QFileInfo outputInfo( mOutputUrl );
1055  return QStringLiteral( "%1.%2.tif" ).arg( outputInfo.fileName() ).arg( fileIndex );
1056 }
1057 
1058 QString QgsRasterFileWriter::vrtFileName()
1059 {
1060  QFileInfo outputInfo( mOutputUrl );
1061  return QStringLiteral( "%1.vrt" ).arg( outputInfo.fileName() );
1062 }
1063 
1064 QString QgsRasterFileWriter::driverForExtension( const QString &extension )
1065 {
1066  QString ext = extension.trimmed();
1067  if ( ext.isEmpty() )
1068  return QString();
1069 
1070  if ( ext.startsWith( '.' ) )
1071  ext.remove( 0, 1 );
1072 
1073  GDALAllRegister();
1074  int const drvCount = GDALGetDriverCount();
1075 
1076  for ( int i = 0; i < drvCount; ++i )
1077  {
1078  GDALDriverH drv = GDALGetDriver( i );
1079  if ( drv )
1080  {
1081  char **driverMetadata = GDALGetMetadata( drv, nullptr );
1082  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
1083  {
1084  QString drvName = GDALGetDriverShortName( drv );
1085  QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
1086 
1087  const auto constDriverExtensions = driverExtensions;
1088  for ( const QString &driver : constDriverExtensions )
1089  {
1090  if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
1091  return drvName;
1092  }
1093  }
1094  }
1095  }
1096  return QString();
1097 }
1098 
1099 QStringList QgsRasterFileWriter::extensionsForFormat( const QString &format )
1100 {
1101  GDALDriverH drv = GDALGetDriverByName( format.toLocal8Bit().data() );
1102  if ( drv )
1103  {
1104  char **driverMetadata = GDALGetMetadata( drv, nullptr );
1105  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
1106  {
1107  return QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
1108  }
1109  }
1110  return QStringList();
1111 }
1112 
1113 QString QgsRasterFileWriter::filterForDriver( const QString &driverName )
1114 {
1115  GDALDriverH drv = GDALGetDriverByName( driverName.toLocal8Bit().data() );
1116  if ( drv )
1117  {
1118  QString drvName = GDALGetDriverLongName( drv );
1119  QString extensionsString = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) );
1120  if ( extensionsString.isEmpty() )
1121  {
1122  return QString();
1123  }
1124  QStringList extensions = extensionsString.split( ' ' );
1125  QString filter = drvName + " (";
1126  for ( const QString &ext : extensions )
1127  {
1128  filter.append( QStringLiteral( "*.%1 *.%2 " ).arg( ext.toLower(), ext.toUpper() ) );
1129  }
1130  filter = filter.trimmed().append( QStringLiteral( ")" ) );
1131  return filter;
1132  }
1133 
1134  return QString();
1135 }
1136 
1137 QList< QgsRasterFileWriter::FilterFormatDetails > QgsRasterFileWriter::supportedFiltersAndFormats( RasterFormatOptions options )
1138 {
1139  static QReadWriteLock sFilterLock;
1140  static QMap< RasterFormatOptions, QList< QgsRasterFileWriter::FilterFormatDetails > > sFilters;
1141 
1142  QgsReadWriteLocker locker( sFilterLock, QgsReadWriteLocker::Read );
1143 
1144  const auto it = sFilters.constFind( options );
1145  if ( it != sFilters.constEnd() )
1146  return it.value();
1147 
1148  GDALAllRegister();
1149  int const drvCount = GDALGetDriverCount();
1150 
1152  QList< QgsRasterFileWriter::FilterFormatDetails > results;
1153 
1154  FilterFormatDetails tifFormat;
1155 
1156  for ( int i = 0; i < drvCount; ++i )
1157  {
1158  GDALDriverH drv = GDALGetDriver( i );
1159  if ( drv )
1160  {
1162  {
1163  QString drvName = GDALGetDriverShortName( drv );
1164  QString filterString = filterForDriver( drvName );
1165  if ( filterString.isEmpty() )
1166  continue;
1167 
1168  FilterFormatDetails details;
1169  details.driverName = drvName;
1170  details.filterString = filterString;
1171 
1172  if ( options & SortRecommended )
1173  {
1174  if ( drvName == QLatin1String( "GTiff" ) )
1175  {
1176  tifFormat = details;
1177  continue;
1178  }
1179  }
1180 
1181  results << details;
1182  }
1183  }
1184  }
1185 
1186  std::sort( results.begin(), results.end(), []( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
1187  {
1188  return a.driverName < b.driverName;
1189  } );
1190 
1191  if ( options & SortRecommended )
1192  {
1193  if ( !tifFormat.filterString.isEmpty() )
1194  {
1195  results.insert( 0, tifFormat );
1196  }
1197  }
1198 
1199  sFilters.insert( options, results );
1200 
1201  return results;
1202 }
1203 
1204 QStringList QgsRasterFileWriter::supportedFormatExtensions( const RasterFormatOptions options )
1205 {
1206  const auto formats = supportedFiltersAndFormats( options );
1207  QStringList extensions;
1208 
1209  QRegularExpression rx( QStringLiteral( "\\*\\.([a-zA-Z0-9]*)" ) );
1210 
1211  for ( const FilterFormatDetails &format : formats )
1212  {
1213  QString ext = format.filterString;
1214  QRegularExpressionMatch match = rx.match( ext );
1215  if ( !match.hasMatch() )
1216  continue;
1217 
1218  QString matched = match.captured( 1 );
1219  extensions << matched;
1220  }
1221  return extensions;
1222 }
DataType
Raster data types.
Definition: qgis.h:119
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
static double maximumValuePossible(Qgis::DataType dataType)
Helper function that returns the maximum possible value for a data type.
static double minimumValuePossible(Qgis::DataType dataType)
Helper function that returns the minimum possible value for a data type.
This class represents a coordinate reference system (CRS).
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
virtual bool isValid() const =0
Returns true if this is a valid layer.
virtual QgsError error() const
Gets current status error.
bool isEmpty() const
Test if any error is set.
Definition: qgserror.h:111
QString summary() const
Short error description, usually the first error in chain, the real error.
Definition: qgserror.cpp:129
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
static bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
The RasterBandStats struct is a container for statistics about a single raster band.
double minimumValue
The minimum cell value in the raster band.
double maximumValue
The maximum cell value in the raster band.
Feedback object tailored for raster block reading.
void appendError(const QString &error)
Appends an error message to the stored list of errors.
Raster data container.
static int typeSize(Qgis::DataType dataType) SIP_HOLDGIL
Returns the size in bytes for the specified dataType.
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.
Base class for raster data providers.
virtual QString buildPyramids(const QList< QgsRasterPyramid > &pyramidList, const QString &resamplingMethod="NEAREST", QgsRaster::RasterPyramidsFormat format=QgsRaster::PyramidsGTiff, const QStringList &configOptions=QStringList(), QgsRasterBlockFeedback *feedback=nullptr)
Creates pyramid overviews.
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
virtual bool setNoDataValue(int bandNo, double noDataValue)
Set no data value on created dataset.
virtual QList< QgsRasterPyramid > buildPyramidList(const QList< int > &overviewList=QList< int >())
Returns the raster layers pyramid list.
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...
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
QgsRectangle extent() const override=0
Returns the extent of the layer.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
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.
virtual bool write(void *data, int band, int width, int height, int xOffset, int yOffset)
Writes into the provider datasource.
Q_DECL_DEPRECATED WriterError writeRaster(const QgsRasterPipe *pipe, int nCols, int nRows, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &crs, QgsRasterBlockFeedback *feedback=nullptr)
Write raster file.
static QStringList extensionsForFormat(const QString &format)
Returns a list of known file extensions for the given GDAL driver format.
static QString filterForDriver(const QString &driverName)
Creates a filter for an GDAL driver key.
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
QgsRasterFileWriter(const QString &outputUrl)
static QStringList supportedFormatExtensions(RasterFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats.
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.
QString outputUrl() const
Returns the output URL for the raster.
@ WriteCanceled
Writing was manually canceled.
@ NoDataConflict
Internal error if a value used for 'no data' was found in input.
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.
@ Image
Rendered image.
@ SortRecommended
Use recommended sort order, with extremely commonly used formats listed first.
static QList< QgsRasterFileWriter::FilterFormatDetails > supportedFiltersAndFormats(RasterFormatOptions options=SortRecommended)
Returns a list or pairs, with format filter string as first element and GDAL format key as second ele...
Base class for processing filters like renderers, reprojector, resampler etc.
virtual Qgis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
virtual int xSize() const
Gets raster size.
virtual const QgsRasterInterface * sourceInput() const
Gets source / raw input, the first in pipe, usually provider.
virtual int bandCount() const =0
Gets number of bands.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
virtual int ySize() const
Iterator for sequentially processing raster cells.
const QgsRasterInterface * input() const
Returns the input raster interface which is being iterated over.
int maximumTileWidth() const
Returns the maximum tile width returned during iteration.
void setMaximumTileWidth(int w)
Sets the maximum tile width returned during iteration.
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...
int maximumTileHeight() const
Returns the minimum tile width returned during iteration.
void startRasterRead(int bandNumber, qgssize nCols, qgssize nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback=nullptr)
Start reading of raster band.
void setMaximumTileHeight(int h)
Sets the minimum tile height returned during iteration.
Raster pipe that deals with null values.
void setOutputNoDataValue(int bandNo, double noData)
Sets the output no data value.
QgsRasterRangeList noData(int bandNo) const
Base class for processing modules.
Definition: qgsrasterpipe.h:47
QgsRasterDataProvider * provider() const
QgsRasterProjector * projector() const
QgsRasterInterface * last() const
QgsRasterNuller * nuller() const
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination CRS.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source CRS.
@ PyramidsFlagYes
Definition: qgsraster.h:77
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
@ Write
Lock for write.
@ Read
Lock for read.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsEnumValueToKey(const T &value)
Returns the value for the given key of an enum.
Definition: qgis.h:743
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:1051
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs
Setting options for creating vector data providers.
Details of available filters and formats.
QString filterString
Filter string for file picker dialogs.