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