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