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