QGIS API Documentation  3.21.0-Master (5b68dc587e)
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  const QgsCoordinateTransform ct( projector->destinationCrs(), projector->sourceCrs(), transformContext );
261  outputExtentInSrcCrs = ct.transformBoundingBox( outputExtent );
262  }
263  if ( !srcProviderExtent.contains( outputExtentInSrcCrs ) &&
264  ( std::fabs( srcProviderExtent.xMinimum() - outputExtentInSrcCrs.xMinimum() ) > geoTransform[1] / 2 ||
265  std::fabs( srcProviderExtent.xMaximum() - outputExtentInSrcCrs.xMaximum() ) > geoTransform[1] / 2 ||
266  std::fabs( srcProviderExtent.yMinimum() - outputExtentInSrcCrs.yMinimum() ) > std::fabs( geoTransform[5] ) / 2 ||
267  std::fabs( srcProviderExtent.yMaximum() - outputExtentInSrcCrs.yMaximum() ) > std::fabs( geoTransform[5] ) / 2 ) )
268  {
269  // Destination extent is (at least partially) outside of source extent, we need destination no data values
270  // Get src sample statistics (estimation from sample)
271  const QgsRasterBandStats stats = srcProvider->bandStatistics( bandNo, QgsRasterBandStats::Min | QgsRasterBandStats::Max, outputExtentInSrcCrs, 250000 );
272 
273  // Test if we have free (not used) values
274  const double typeMinValue = QgsContrastEnhancement::minimumValuePossible( srcDataType );
275  const double typeMaxValue = QgsContrastEnhancement::maximumValuePossible( srcDataType );
276  if ( stats.minimumValue > typeMinValue )
277  {
278  destNoDataValue = typeMinValue;
279  }
280  else if ( stats.maximumValue < typeMaxValue )
281  {
282  destNoDataValue = typeMaxValue;
283  }
284  else
285  {
286  // We have to use wider type
287  destDataType = QgsRasterBlock::typeWithNoDataValue( destDataType, &destNoDataValue );
288  }
289  destHasNoDataValue = true;
290  }
291  }
292 
293  if ( nuller && destHasNoDataValue )
294  {
295  nuller->setOutputNoDataValue( bandNo, destNoDataValue );
296  }
297 
298  QgsDebugMsgLevel( QStringLiteral( "bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( qgsEnumValueToKey( destDataType ) ).arg( destHasNoDataValue ).arg( destNoDataValue ), 4 );
299  destDataTypeList.append( destDataType );
300  destHasNoDataValueList.append( destHasNoDataValue );
301  destNoDataValueList.append( destNoDataValue );
302  }
303 
304  Qgis::DataType destDataType = destDataTypeList.value( 0 );
305  // Currently write API supports one output type for dataset only -> find the widest
306  for ( int i = 1; i < nBands; i++ )
307  {
308  if ( destDataTypeList.value( i ) > destDataType )
309  {
310  destDataType = destDataTypeList.value( i );
311  // no data value may be left per band (for future)
312  }
313  }
314 
315  WriterError error;
316  for ( int attempt = 0; attempt < 2; attempt ++ )
317  {
318  //create destProvider for whole dataset here
319  // initOutput() returns 0 in tile mode!
320  std::unique_ptr<QgsRasterDataProvider> destProvider(
321  initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList ) );
322  if ( !mTiledMode )
323  {
324  if ( !destProvider )
325  {
326  return CreateDatasourceError;
327  }
328  if ( !destProvider->isValid() )
329  {
330  if ( feedback && !destProvider->error().isEmpty() )
331  {
332  feedback->appendError( destProvider->error().summary() );
333  }
334  return CreateDatasourceError;
335  }
336  if ( nCols != destProvider->xSize() || nRows != destProvider->ySize() )
337  {
338  QgsDebugMsg( QStringLiteral( "Created raster does not have requested dimensions" ) );
339  if ( feedback )
340  {
341  feedback->appendError( QObject::tr( "Created raster does not have requested dimensions" ) );
342  }
343  return CreateDatasourceError;
344  }
345  if ( nBands != destProvider->bandCount() )
346  {
347  QgsDebugMsg( QStringLiteral( "Created raster does not have requested band count" ) );
348  if ( feedback )
349  {
350  feedback->appendError( QObject::tr( "Created raster does not have requested band count" ) );
351  }
352  return CreateDatasourceError;
353  }
354  if ( nBands )
355  {
356  // Some driver like GS7BG may accept Byte as requested data type,
357  // but actually return a driver with Float64...
358  destDataType = destProvider->dataType( 1 );
359  }
360  }
361 
362  error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider.get(), feedback );
363 
364  if ( attempt == 0 && error == NoDataConflict )
365  {
366  // The value used for no data was found in source data, we must use wider data type
367  if ( destProvider ) // no tiles
368  {
369  destProvider->remove();
370  destProvider.reset();
371  }
372  else // VRT
373  {
374  // TODO: remove created VRT
375  }
376 
377  // But we don't know which band -> wider all
378  for ( int i = 0; i < nBands; i++ )
379  {
380  double destNoDataValue;
381  const Qgis::DataType destDataType = QgsRasterBlock::typeWithNoDataValue( destDataTypeList.value( i ), &destNoDataValue );
382  destDataTypeList.replace( i, destDataType );
383  destNoDataValueList.replace( i, destNoDataValue );
384  }
385  destDataType = destDataTypeList.value( 0 );
386 
387  // Try again
388  }
389  else
390  {
391  break;
392  }
393  }
394 
395  return error;
396 }
397 
398 static int qgsDivRoundUp( int a, int b )
399 {
400  return a / b + ( ( ( a % b ) != 0 ) ? 1 : 0 );
401 }
402 
403 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe *pipe,
404  QgsRasterIterator *iter,
405  int nCols, int nRows,
406  const QgsRectangle &outputExtent,
408  Qgis::DataType destDataType,
409  const QList<bool> &destHasNoDataValueList,
410  const QList<double> &destNoDataValueList,
411  QgsRasterDataProvider *destProvider,
412  QgsRasterBlockFeedback *feedback )
413 {
414  Q_UNUSED( pipe )
415  Q_UNUSED( destHasNoDataValueList )
416  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
417 
418  const QgsRasterInterface *iface = iter->input();
419  const QgsRasterDataProvider *srcProvider = dynamic_cast<const QgsRasterDataProvider *>( iface->sourceInput() );
420  const int nBands = iface->bandCount();
421  QgsDebugMsgLevel( QStringLiteral( "nBands = %1" ).arg( nBands ), 4 );
422 
423  //Get output map units per pixel
424  int iterLeft = 0;
425  int iterTop = 0;
426  int iterCols = 0;
427  int iterRows = 0;
428 
429  std::vector< std::unique_ptr<QgsRasterBlock> > blockList;
430  std::vector< std::unique_ptr<QgsRasterBlock> > destBlockList;
431 
432  blockList.resize( nBands );
433  destBlockList.resize( nBands );
434 
435  for ( int i = 1; i <= nBands; ++i )
436  {
437  iter->startRasterRead( i, nCols, nRows, outputExtent );
438  if ( destProvider && destHasNoDataValueList.value( i - 1 ) ) // no tiles
439  {
440  destProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
441  }
442  }
443 
444  int nParts = 0;
445  int fileIndex = 0;
446  if ( feedback )
447  {
448  const int nPartsX = qgsDivRoundUp( nCols, iter->maximumTileWidth() );
449  const int nPartsY = qgsDivRoundUp( nRows, iter->maximumTileHeight() );
450  nParts = nPartsX * nPartsY;
451  }
452 
453  // hmm why is there a for(;;) here ..
454  // not good coding practice IMHO, it might be better to use [ for() and break ] or [ while (test) ]
455  Q_FOREVER
456  {
457  for ( int i = 1; i <= nBands; ++i )
458  {
459  QgsRasterBlock *block = nullptr;
460  if ( !iter->readNextRasterPart( i, iterCols, iterRows, &block, iterLeft, iterTop ) )
461  {
462  // No more parts, create VRT and return
463  if ( mTiledMode )
464  {
465  const QString vrtFilePath( mOutputUrl + '/' + vrtFileName() );
466  writeVRT( vrtFilePath );
467  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
468  {
469  buildPyramids( vrtFilePath );
470  }
471  }
472  else
473  {
474  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
475  {
476  buildPyramids( mOutputUrl, destProvider );
477  }
478  }
479 
480  QgsDebugMsgLevel( QStringLiteral( "Done" ), 4 );
481  return NoError; //reached last tile, bail out
482  }
483  blockList[i - 1].reset( block );
484  // TODO: verify if NoDataConflict happened, to do that we need the whole pipe or nuller interface
485  }
486 
487  if ( feedback && fileIndex < ( nParts - 1 ) )
488  {
489  feedback->setProgress( 100.0 * fileIndex / static_cast< double >( nParts ) );
490  if ( feedback->isCanceled() )
491  {
492  break;
493  }
494  }
495 
496  // It may happen that internal data type (dataType) is wider than destDataType
497  for ( int i = 1; i <= nBands; ++i )
498  {
499  if ( srcProvider && srcProvider->dataType( i ) == destDataType )
500  {
501  // nothing
502  }
503  else
504  {
505  // TODO: this conversion should go to QgsRasterDataProvider::write with additional input data type param
506  blockList[i - 1]->convert( destDataType );
507  }
508  destBlockList[i - 1] = std::move( blockList[i - 1] );
509  }
510 
511  if ( mTiledMode ) //write to file
512  {
513  std::unique_ptr< QgsRasterDataProvider > partDestProvider( createPartProvider( outputExtent,
514  nCols, iterCols, iterRows,
515  iterLeft, iterTop, mOutputUrl,
516  fileIndex, nBands, destDataType, crs ) );
517 
518  if ( !partDestProvider || !partDestProvider->isValid() )
519  {
520  return DestProviderError;
521  }
522 
523  //write data to output file. todo: loop over the data list
524  for ( int i = 1; i <= nBands; ++i )
525  {
526  if ( destHasNoDataValueList.value( i - 1 ) )
527  {
528  partDestProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
529  }
530  if ( destBlockList[ i - 1 ]->isEmpty() )
531  continue;
532 
533  if ( !partDestProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, 0, 0 ) )
534  {
535  return WriteError;
536  }
537  addToVRT( partFileName( fileIndex ), i, iterCols, iterRows, iterLeft, iterTop );
538  }
539 
540  }
541  else if ( destProvider )
542  {
543  //loop over data
544  for ( int i = 1; i <= nBands; ++i )
545  {
546  if ( destBlockList[ i - 1 ]->isEmpty() )
547  continue;
548 
549  if ( !destProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, iterLeft, iterTop ) )
550  {
551  return WriteError;
552  }
553  }
554  }
555  ++fileIndex;
556  }
557 
558  QgsDebugMsgLevel( QStringLiteral( "Done" ), 4 );
559  return ( feedback && feedback->isCanceled() ) ? WriteCanceled : NoError;
560 }
561 
562 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,
564 {
565  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
566  if ( !iter )
567  {
568  return SourceProviderError;
569  }
570 
571  const QgsRasterInterface *iface = iter->input();
572  if ( !iface )
573  return SourceProviderError;
574 
575  const Qgis::DataType inputDataType = iface->dataType( 1 );
576  if ( inputDataType != Qgis::DataType::ARGB32 && inputDataType != Qgis::DataType::ARGB32_Premultiplied )
577  {
578  return SourceProviderError;
579  }
580  const bool isPremultiplied = ( inputDataType == Qgis::DataType::ARGB32_Premultiplied );
581 
582  iter->setMaximumTileWidth( mMaxTileWidth );
583  iter->setMaximumTileHeight( mMaxTileHeight );
584 
585  const size_t nMaxPixels = static_cast<size_t>( mMaxTileWidth ) * mMaxTileHeight;
586  std::vector<unsigned char> redData( nMaxPixels );
587  std::vector<unsigned char> greenData( nMaxPixels );
588  std::vector<unsigned char> blueData( nMaxPixels );
589  std::vector<unsigned char> alphaData( nMaxPixels );
590  int iterLeft = 0, iterTop = 0, iterCols = 0, iterRows = 0;
591  int fileIndex = 0;
592 
593  //create destProvider for whole dataset here
594  double pixelSize;
595  double geoTransform[6];
596  globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
597 
598  const int nOutputBands = 4;
599  std::unique_ptr< QgsRasterDataProvider > destProvider( initOutput( nCols, nRows, crs, geoTransform, nOutputBands, Qgis::DataType::Byte ) );
600  if ( !mTiledMode )
601  {
602  if ( !destProvider )
603  {
604  return CreateDatasourceError;
605  }
606  if ( !destProvider->isValid() )
607  {
608  if ( feedback && !destProvider->error().isEmpty() )
609  {
610  feedback->appendError( destProvider->error().summary() );
611  }
612  return CreateDatasourceError;
613  }
614  if ( nCols != destProvider->xSize() || nRows != destProvider->ySize() )
615  {
616  QgsDebugMsg( QStringLiteral( "Created raster does not have requested dimensions" ) );
617  if ( feedback )
618  {
619  feedback->appendError( QObject::tr( "Created raster does not have requested dimensions" ) );
620  }
621  return CreateDatasourceError;
622  }
623  if ( nOutputBands != destProvider->bandCount() )
624  {
625  QgsDebugMsg( QStringLiteral( "Created raster does not have requested band count" ) );
626  if ( feedback )
627  {
628  feedback->appendError( QObject::tr( "Created raster does not have requested band count" ) );
629  }
630  return CreateDatasourceError;
631  }
632  if ( Qgis::DataType::Byte != destProvider->dataType( 1 ) )
633  {
634  QgsDebugMsg( QStringLiteral( "Created raster does not have requested data type" ) );
635  if ( feedback )
636  {
637  feedback->appendError( QObject::tr( "Created raster does not have requested data type" ) );
638  }
639  return CreateDatasourceError;
640  }
641  }
642 
643  iter->startRasterRead( 1, nCols, nRows, outputExtent, feedback );
644 
645  int nParts = 0;
646  if ( feedback )
647  {
648  const int nPartsX = qgsDivRoundUp( nCols, iter->maximumTileWidth() );
649  const int nPartsY = qgsDivRoundUp( nRows, iter->maximumTileHeight() );
650  nParts = nPartsX * nPartsY;
651  }
652 
653  std::unique_ptr< QgsRasterBlock > inputBlock;
654  while ( iter->readNextRasterPart( 1, iterCols, iterRows, inputBlock, iterLeft, iterTop ) )
655  {
656  if ( !inputBlock || inputBlock->isEmpty() )
657  {
658  continue;
659  }
660 
661  if ( feedback && fileIndex < ( nParts - 1 ) )
662  {
663  feedback->setProgress( 100.0 * fileIndex / static_cast< double >( nParts ) );
664  if ( feedback->isCanceled() )
665  {
666  break;
667  }
668  }
669 
670  //fill into red/green/blue/alpha channels
671  const qgssize nPixels = static_cast< qgssize >( iterCols ) * iterRows;
672  for ( qgssize i = 0; i < nPixels; ++i )
673  {
674  QRgb c = inputBlock->color( i );
675  if ( isPremultiplied )
676  {
677  c = qUnpremultiply( c );
678  }
679  redData[i] = static_cast<unsigned char>( qRed( c ) );
680  greenData[i] = static_cast<unsigned char>( qGreen( c ) );
681  blueData[i] = static_cast<unsigned char>( qBlue( c ) );
682  alphaData[i] = static_cast<unsigned char>( qAlpha( c ) );
683  }
684 
685  //create output file
686  if ( mTiledMode )
687  {
688  std::unique_ptr< QgsRasterDataProvider > partDestProvider( createPartProvider( outputExtent,
689  nCols, iterCols, iterRows,
690  iterLeft, iterTop, mOutputUrl, fileIndex,
691  4, Qgis::DataType::Byte, crs ) );
692 
693  if ( !partDestProvider || partDestProvider->isValid() )
694  {
695  return DestProviderError;
696  }
697 
698  //write data to output file
699  if ( !partDestProvider->write( &redData[0], 1, iterCols, iterRows, 0, 0 ) ||
700  !partDestProvider->write( &greenData[0], 2, iterCols, iterRows, 0, 0 ) ||
701  !partDestProvider->write( &blueData[0], 3, iterCols, iterRows, 0, 0 ) ||
702  !partDestProvider->write( &alphaData[0], 4, iterCols, iterRows, 0, 0 ) )
703  {
704  return WriteError;
705  }
706 
707  addToVRT( partFileName( fileIndex ), 1, iterCols, iterRows, iterLeft, iterTop );
708  addToVRT( partFileName( fileIndex ), 2, iterCols, iterRows, iterLeft, iterTop );
709  addToVRT( partFileName( fileIndex ), 3, iterCols, iterRows, iterLeft, iterTop );
710  addToVRT( partFileName( fileIndex ), 4, iterCols, iterRows, iterLeft, iterTop );
711  }
712  else if ( destProvider )
713  {
714  if ( !destProvider->write( &redData[0], 1, iterCols, iterRows, iterLeft, iterTop ) ||
715  !destProvider->write( &greenData[0], 2, iterCols, iterRows, iterLeft, iterTop ) ||
716  !destProvider->write( &blueData[0], 3, iterCols, iterRows, iterLeft, iterTop ) ||
717  !destProvider->write( &alphaData[0], 4, iterCols, iterRows, iterLeft, iterTop ) )
718  {
719  return WriteError;
720  }
721  }
722 
723  ++fileIndex;
724  }
725  destProvider.reset();
726 
727  if ( feedback )
728  {
729  feedback->setProgress( 100.0 );
730  }
731 
732  if ( mTiledMode )
733  {
734  const QString vrtFilePath( mOutputUrl + '/' + vrtFileName() );
735  writeVRT( vrtFilePath );
736  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
737  {
738  buildPyramids( vrtFilePath );
739  }
740  }
741  else
742  {
743  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
744  {
745  buildPyramids( mOutputUrl );
746  }
747  }
748  return ( feedback && feedback->isCanceled() ) ? WriteCanceled : NoError;
749 }
750 
751 void QgsRasterFileWriter::addToVRT( const QString &filename, int band, int xSize, int ySize, int xOffset, int yOffset )
752 {
753  QDomElement bandElem = mVRTBands.value( band - 1 );
754 
755  QDomElement simpleSourceElem = mVRTDocument.createElement( QStringLiteral( "SimpleSource" ) );
756 
757  //SourceFilename
758  QDomElement sourceFilenameElem = mVRTDocument.createElement( QStringLiteral( "SourceFilename" ) );
759  sourceFilenameElem.setAttribute( QStringLiteral( "relativeToVRT" ), QStringLiteral( "1" ) );
760  const QDomText sourceFilenameText = mVRTDocument.createTextNode( filename );
761  sourceFilenameElem.appendChild( sourceFilenameText );
762  simpleSourceElem.appendChild( sourceFilenameElem );
763 
764  //SourceBand
765  QDomElement sourceBandElem = mVRTDocument.createElement( QStringLiteral( "SourceBand" ) );
766  const QDomText sourceBandText = mVRTDocument.createTextNode( QString::number( band ) );
767  sourceBandElem.appendChild( sourceBandText );
768  simpleSourceElem.appendChild( sourceBandElem );
769 
770  //SourceProperties
771  QDomElement sourcePropertiesElem = mVRTDocument.createElement( QStringLiteral( "SourceProperties" ) );
772  sourcePropertiesElem.setAttribute( QStringLiteral( "RasterXSize" ), xSize );
773  sourcePropertiesElem.setAttribute( QStringLiteral( "RasterYSize" ), ySize );
774  sourcePropertiesElem.setAttribute( QStringLiteral( "BlockXSize" ), xSize );
775  sourcePropertiesElem.setAttribute( QStringLiteral( "BlockYSize" ), ySize );
776  sourcePropertiesElem.setAttribute( QStringLiteral( "DataType" ), QStringLiteral( "Byte" ) );
777  simpleSourceElem.appendChild( sourcePropertiesElem );
778 
779  //SrcRect
780  QDomElement srcRectElem = mVRTDocument.createElement( QStringLiteral( "SrcRect" ) );
781  srcRectElem.setAttribute( QStringLiteral( "xOff" ), QStringLiteral( "0" ) );
782  srcRectElem.setAttribute( QStringLiteral( "yOff" ), QStringLiteral( "0" ) );
783  srcRectElem.setAttribute( QStringLiteral( "xSize" ), xSize );
784  srcRectElem.setAttribute( QStringLiteral( "ySize" ), ySize );
785  simpleSourceElem.appendChild( srcRectElem );
786 
787  //DstRect
788  QDomElement dstRectElem = mVRTDocument.createElement( QStringLiteral( "DstRect" ) );
789  dstRectElem.setAttribute( QStringLiteral( "xOff" ), xOffset );
790  dstRectElem.setAttribute( QStringLiteral( "yOff" ), yOffset );
791  dstRectElem.setAttribute( QStringLiteral( "xSize" ), xSize );
792  dstRectElem.setAttribute( QStringLiteral( "ySize" ), ySize );
793  simpleSourceElem.appendChild( dstRectElem );
794 
795  bandElem.appendChild( simpleSourceElem );
796 }
797 
798 void QgsRasterFileWriter::buildPyramids( const QString &filename, QgsRasterDataProvider *destProviderIn )
799 {
800  QgsDebugMsgLevel( "filename = " + filename, 4 );
801  // open new dataProvider so we can build pyramids with it
802  const QgsDataProvider::ProviderOptions providerOptions;
803  QgsRasterDataProvider *destProvider = destProviderIn;
804  if ( !destProvider )
805  {
806  destProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mOutputProviderKey, filename, providerOptions ) );
807  if ( !destProvider || !destProvider->isValid() )
808  {
809  delete destProvider;
810  return;
811  }
812  }
813 
814  // TODO progress report
815  // TODO test mTiledMode - not tested b/c segfault at line # 289
816  // connect( provider, SIGNAL( progressUpdate( int ) ), mPyramidProgress, SLOT( setValue( int ) ) );
817  QList< QgsRasterPyramid> myPyramidList;
818  if ( ! mPyramidsList.isEmpty() )
819  myPyramidList = destProvider->buildPyramidList( mPyramidsList );
820  for ( int myCounterInt = 0; myCounterInt < myPyramidList.count(); myCounterInt++ )
821  {
822  myPyramidList[myCounterInt].setBuild( true );
823  }
824 
825  QgsDebugMsgLevel( QStringLiteral( "building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ), 4 );
826  // QApplication::setOverrideCursor( Qt::WaitCursor );
827  const QString res = destProvider->buildPyramids( myPyramidList, mPyramidsResampling,
828  mPyramidsFormat, mPyramidsConfigOptions );
829  // QApplication::restoreOverrideCursor();
830 
831  // TODO put this in provider or elsewhere
832  if ( !res.isNull() )
833  {
834  QString title, message;
835  if ( res == QLatin1String( "ERROR_WRITE_ACCESS" ) )
836  {
837  title = QObject::tr( "Building Pyramids" );
838  message = QObject::tr( "Write access denied. Adjust the file permissions and try again." );
839  }
840  else if ( res == QLatin1String( "ERROR_WRITE_FORMAT" ) )
841  {
842  title = QObject::tr( "Building Pyramids" );
843  message = QObject::tr( "The file was not writable. Some formats do not "
844  "support pyramid overviews. Consult the GDAL documentation if in doubt." );
845  }
846  else if ( res == QLatin1String( "FAILED_NOT_SUPPORTED" ) )
847  {
848  title = QObject::tr( "Building Pyramids" );
849  message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
850  }
851  else if ( res == QLatin1String( "ERROR_VIRTUAL" ) )
852  {
853  title = QObject::tr( "Building Pyramids" );
854  message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
855  }
856  QMessageBox::warning( nullptr, title, message );
857  QgsDebugMsgLevel( res + " - " + message, 4 );
858  }
859  if ( !destProviderIn )
860  delete destProvider;
861 }
862 
863 #if 0
864 int QgsRasterFileWriter::pyramidsProgress( double dfComplete, const char *pszMessage, void *pData )
865 {
866  Q_UNUSED( pszMessage )
867  GDALTermProgress( dfComplete, 0, 0 );
868  QProgressDialog *progressDialog = static_cast<QProgressDialog *>( pData );
869  if ( pData && progressDialog->wasCanceled() )
870  {
871  return 0;
872  }
873 
874  if ( pData )
875  {
876  progressDialog->setRange( 0, 100 );
877  progressDialog->setValue( dfComplete * 100 );
878  }
879  return 1;
880 }
881 #endif
882 
883 void QgsRasterFileWriter::createVRT( int xSize, int ySize, const QgsCoordinateReferenceSystem &crs, double *geoTransform, Qgis::DataType type, const QList<bool> &destHasNoDataValueList, const QList<double> &destNoDataValueList )
884 {
885  mVRTDocument.clear();
886  QDomElement VRTDatasetElem = mVRTDocument.createElement( QStringLiteral( "VRTDataset" ) );
887 
888  //xsize / ysize
889  VRTDatasetElem.setAttribute( QStringLiteral( "rasterXSize" ), xSize );
890  VRTDatasetElem.setAttribute( QStringLiteral( "rasterYSize" ), ySize );
891  mVRTDocument.appendChild( VRTDatasetElem );
892 
893  //CRS
894  QDomElement SRSElem = mVRTDocument.createElement( QStringLiteral( "SRS" ) );
895  const QDomText crsText = mVRTDocument.createTextNode( crs.toWkt() );
896  SRSElem.appendChild( crsText );
897  VRTDatasetElem.appendChild( SRSElem );
898 
899  //geotransform
900  if ( geoTransform )
901  {
902  QDomElement geoTransformElem = mVRTDocument.createElement( QStringLiteral( "GeoTransform" ) );
903  const QString geoTransformString = QString::number( geoTransform[0], 'f', 6 ) + ", " + QString::number( geoTransform[1] ) + ", " + QString::number( geoTransform[2] ) +
904  ", " + QString::number( geoTransform[3], 'f', 6 ) + ", " + QString::number( geoTransform[4] ) + ", " + QString::number( geoTransform[5] );
905  const QDomText geoTransformText = mVRTDocument.createTextNode( geoTransformString );
906  geoTransformElem.appendChild( geoTransformText );
907  VRTDatasetElem.appendChild( geoTransformElem );
908  }
909 
910  int nBands;
911  if ( mMode == Raw )
912  {
913  nBands = mInput->bandCount();
914  }
915  else
916  {
917  nBands = 4;
918  }
919 
920  QStringList colorInterp;
921  colorInterp << QStringLiteral( "Red" ) << QStringLiteral( "Green" ) << QStringLiteral( "Blue" ) << QStringLiteral( "Alpha" );
922 
923  QMap<Qgis::DataType, QString> dataTypes;
924  dataTypes.insert( Qgis::DataType::Byte, QStringLiteral( "Byte" ) );
925  dataTypes.insert( Qgis::DataType::UInt16, QStringLiteral( "UInt16" ) );
926  dataTypes.insert( Qgis::DataType::Int16, QStringLiteral( "Int16" ) );
927  dataTypes.insert( Qgis::DataType::UInt32, QStringLiteral( "Int32" ) );
928  dataTypes.insert( Qgis::DataType::Float32, QStringLiteral( "Float32" ) );
929  dataTypes.insert( Qgis::DataType::Float64, QStringLiteral( "Float64" ) );
930  dataTypes.insert( Qgis::DataType::CInt16, QStringLiteral( "CInt16" ) );
931  dataTypes.insert( Qgis::DataType::CInt32, QStringLiteral( "CInt32" ) );
932  dataTypes.insert( Qgis::DataType::CFloat32, QStringLiteral( "CFloat32" ) );
933  dataTypes.insert( Qgis::DataType::CFloat64, QStringLiteral( "CFloat64" ) );
934 
935  for ( int i = 1; i <= nBands; i++ )
936  {
937  QDomElement VRTBand = mVRTDocument.createElement( QStringLiteral( "VRTRasterBand" ) );
938 
939  VRTBand.setAttribute( QStringLiteral( "band" ), QString::number( i ) );
940  const QString dataType = dataTypes.value( type );
941  VRTBand.setAttribute( QStringLiteral( "dataType" ), dataType );
942 
943  if ( mMode == Image )
944  {
945  VRTBand.setAttribute( QStringLiteral( "dataType" ), QStringLiteral( "Byte" ) );
946  QDomElement colorInterpElement = mVRTDocument.createElement( QStringLiteral( "ColorInterp" ) );
947  const QDomText interpText = mVRTDocument.createTextNode( colorInterp.value( i - 1 ) );
948  colorInterpElement.appendChild( interpText );
949  VRTBand.appendChild( colorInterpElement );
950  }
951 
952  if ( !destHasNoDataValueList.isEmpty() && destHasNoDataValueList.value( i - 1 ) )
953  {
954  VRTBand.setAttribute( QStringLiteral( "NoDataValue" ), QString::number( destNoDataValueList.value( i - 1 ) ) );
955  }
956 
957  mVRTBands.append( VRTBand );
958  VRTDatasetElem.appendChild( VRTBand );
959  }
960 }
961 
962 bool QgsRasterFileWriter::writeVRT( const QString &file )
963 {
964  QFile outputFile( file );
965  if ( ! outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
966  {
967  return false;
968  }
969 
970  QTextStream outStream( &outputFile );
971  mVRTDocument.save( outStream, 2 );
972  return true;
973 }
974 
975 QgsRasterDataProvider *QgsRasterFileWriter::createPartProvider( const QgsRectangle &extent, int nCols, int iterCols,
976  int iterRows, int iterLeft, int iterTop, const QString &outputUrl, int fileIndex, int nBands, Qgis::DataType type,
978 {
979  const double mup = extent.width() / nCols;
980  const double mapLeft = extent.xMinimum() + iterLeft * mup;
981  const double mapRight = mapLeft + mup * iterCols;
982  const double mapTop = extent.yMaximum() - iterTop * mup;
983  const double mapBottom = mapTop - iterRows * mup;
984  const QgsRectangle mapRect( mapLeft, mapBottom, mapRight, mapTop );
985 
986  const QString outputFile = outputUrl + '/' + partFileName( fileIndex );
987 
988  //geotransform
989  double geoTransform[6];
990  geoTransform[0] = mapRect.xMinimum();
991  geoTransform[1] = mup;
992  geoTransform[2] = 0.0;
993  geoTransform[3] = mapRect.yMaximum();
994  geoTransform[4] = 0.0;
995  geoTransform[5] = -mup;
996 
997  // perhaps we need a separate createOptions for tiles ?
998 
999  QgsRasterDataProvider *destProvider = QgsRasterDataProvider::create( mOutputProviderKey, outputFile, mOutputFormat, nBands, type, iterCols, iterRows, geoTransform, crs, mCreateOptions );
1000 
1001  // TODO: return provider and report error
1002  return destProvider;
1003 }
1004 
1005 QgsRasterDataProvider *QgsRasterFileWriter::initOutput( int nCols, int nRows, const QgsCoordinateReferenceSystem &crs,
1006  double *geoTransform, int nBands, Qgis::DataType type,
1007  const QList<bool> &destHasNoDataValueList, const QList<double> &destNoDataValueList )
1008 {
1009  if ( mTiledMode )
1010  {
1011  createVRT( nCols, nRows, crs, geoTransform, type, destHasNoDataValueList, destNoDataValueList );
1012  return nullptr;
1013  }
1014  else
1015  {
1016 #if 0
1017  // TODO enable "use existing", has no effect for now, because using Create() in gdal provider
1018  // should this belong in provider? should also test that source provider is gdal
1019  if ( mBuildPyramidsFlag == -4 && mOutputProviderKey == "gdal" && mOutputFormat.compare( QLatin1String( "gtiff" ), Qt::CaseInsensitive ) == 0 )
1020  mCreateOptions << "COPY_SRC_OVERVIEWS=YES";
1021 #endif
1022 
1023  QgsRasterDataProvider *destProvider = QgsRasterDataProvider::create( mOutputProviderKey, mOutputUrl, mOutputFormat, nBands, type, nCols, nRows, geoTransform, crs, mCreateOptions );
1024 
1025  if ( !destProvider )
1026  {
1027  QgsDebugMsg( QStringLiteral( "No provider created" ) );
1028  }
1029 
1030  return destProvider;
1031  }
1032 }
1033 
1034 void QgsRasterFileWriter::globalOutputParameters( const QgsRectangle &extent, int nCols, int &nRows,
1035  double *geoTransform, double &pixelSize )
1036 {
1037  pixelSize = extent.width() / nCols;
1038 
1039  //calculate nRows automatically for providers without exact resolution
1040  if ( nRows < 0 )
1041  {
1042  nRows = static_cast< double >( nCols ) / extent.width() * extent.height() + 0.5; //NOLINT
1043  }
1044  geoTransform[0] = extent.xMinimum();
1045  geoTransform[1] = pixelSize;
1046  geoTransform[2] = 0.0;
1047  geoTransform[3] = extent.yMaximum();
1048  geoTransform[4] = 0.0;
1049  geoTransform[5] = -( extent.height() / nRows );
1050 }
1051 
1052 QString QgsRasterFileWriter::partFileName( int fileIndex )
1053 {
1054  // .tif for now
1055  const QFileInfo outputInfo( mOutputUrl );
1056  return QStringLiteral( "%1.%2.tif" ).arg( outputInfo.fileName() ).arg( fileIndex );
1057 }
1058 
1059 QString QgsRasterFileWriter::vrtFileName()
1060 {
1061  const QFileInfo outputInfo( mOutputUrl );
1062  return QStringLiteral( "%1.vrt" ).arg( outputInfo.fileName() );
1063 }
1064 
1065 QString QgsRasterFileWriter::driverForExtension( const QString &extension )
1066 {
1067  QString ext = extension.trimmed();
1068  if ( ext.isEmpty() )
1069  return QString();
1070 
1071  if ( ext.startsWith( '.' ) )
1072  ext.remove( 0, 1 );
1073 
1074  GDALAllRegister();
1075  int const drvCount = GDALGetDriverCount();
1076 
1077  for ( int i = 0; i < drvCount; ++i )
1078  {
1079  GDALDriverH drv = GDALGetDriver( i );
1080  if ( drv )
1081  {
1082  char **driverMetadata = GDALGetMetadata( drv, nullptr );
1083  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
1084  {
1085  QString drvName = GDALGetDriverShortName( drv );
1086  const QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
1087 
1088  const auto constDriverExtensions = driverExtensions;
1089  for ( const QString &driver : constDriverExtensions )
1090  {
1091  if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
1092  return drvName;
1093  }
1094  }
1095  }
1096  }
1097  return QString();
1098 }
1099 
1100 QStringList QgsRasterFileWriter::extensionsForFormat( const QString &format )
1101 {
1102  GDALDriverH drv = GDALGetDriverByName( format.toLocal8Bit().data() );
1103  if ( drv )
1104  {
1105  char **driverMetadata = GDALGetMetadata( drv, nullptr );
1106  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
1107  {
1108  return QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
1109  }
1110  }
1111  return QStringList();
1112 }
1113 
1114 QString QgsRasterFileWriter::filterForDriver( const QString &driverName )
1115 {
1116  GDALDriverH drv = GDALGetDriverByName( driverName.toLocal8Bit().data() );
1117  if ( drv )
1118  {
1119  const QString drvName = GDALGetDriverLongName( drv );
1120  const QString extensionsString = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) );
1121  if ( extensionsString.isEmpty() )
1122  {
1123  return QString();
1124  }
1125  const QStringList extensions = extensionsString.split( ' ' );
1126  QString filter = drvName + " (";
1127  for ( const QString &ext : extensions )
1128  {
1129  filter.append( QStringLiteral( "*.%1 *.%2 " ).arg( ext.toLower(), ext.toUpper() ) );
1130  }
1131  filter = filter.trimmed().append( QStringLiteral( ")" ) );
1132  return filter;
1133  }
1134 
1135  return QString();
1136 }
1137 
1138 QList< QgsRasterFileWriter::FilterFormatDetails > QgsRasterFileWriter::supportedFiltersAndFormats( RasterFormatOptions options )
1139 {
1140  static QReadWriteLock sFilterLock;
1141  static QMap< RasterFormatOptions, QList< QgsRasterFileWriter::FilterFormatDetails > > sFilters;
1142 
1143  QgsReadWriteLocker locker( sFilterLock, QgsReadWriteLocker::Read );
1144 
1145  const auto it = sFilters.constFind( options );
1146  if ( it != sFilters.constEnd() )
1147  return it.value();
1148 
1149  GDALAllRegister();
1150  int const drvCount = GDALGetDriverCount();
1151 
1153  QList< QgsRasterFileWriter::FilterFormatDetails > results;
1154 
1155  FilterFormatDetails tifFormat;
1156 
1157  for ( int i = 0; i < drvCount; ++i )
1158  {
1159  GDALDriverH drv = GDALGetDriver( i );
1160  if ( drv )
1161  {
1163  {
1164  const QString drvName = GDALGetDriverShortName( drv );
1165  const QString filterString = filterForDriver( drvName );
1166  if ( filterString.isEmpty() )
1167  continue;
1168 
1169  FilterFormatDetails details;
1170  details.driverName = drvName;
1171  details.filterString = filterString;
1172 
1173  if ( options & SortRecommended )
1174  {
1175  if ( drvName == QLatin1String( "GTiff" ) )
1176  {
1177  tifFormat = details;
1178  continue;
1179  }
1180  }
1181 
1182  results << details;
1183  }
1184  }
1185  }
1186 
1187  std::sort( results.begin(), results.end(), []( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
1188  {
1189  return a.driverName < b.driverName;
1190  } );
1191 
1192  if ( options & SortRecommended )
1193  {
1194  if ( !tifFormat.filterString.isEmpty() )
1195  {
1196  results.insert( 0, tifFormat );
1197  }
1198  }
1199 
1200  sFilters.insert( options, results );
1201 
1202  return results;
1203 }
1204 
1205 QStringList QgsRasterFileWriter::supportedFormatExtensions( const RasterFormatOptions options )
1206 {
1207  const auto formats = supportedFiltersAndFormats( options );
1208  QStringList extensions;
1209 
1210  const QRegularExpression rx( QStringLiteral( "\\*\\.([a-zA-Z0-9]*)" ) );
1211 
1212  for ( const FilterFormatDetails &format : formats )
1213  {
1214  const QString ext = format.filterString;
1215  const QRegularExpressionMatch match = rx.match( ext );
1216  if ( !match.hasMatch() )
1217  continue;
1218 
1219  const QString matched = match.captured( 1 );
1220  extensions << matched;
1221  }
1222  return extensions;
1223 }
DataType
Raster data types.
Definition: qgis.h:120
@ 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
Contains a pipeline of raster interfaces for sequential raster processing.
Definition: qgsrasterpipe.h:50
QgsRasterDataProvider * provider() const
Returns the data provider interface, or nullptr if no data provider is present in the pipe.
QgsRasterProjector * projector() const
Returns the projector interface, or nullptr if no projector is present in the pipe.
QgsRasterInterface * last() const
Returns last interface in the pipe.
QgsRasterNuller * nuller() const
Returns the raster nuller interface, or nullptr if no raster nuller is present in the pipe.
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:1393
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:1701
#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.