QGIS API Documentation  3.12.1-București (121cc00ff0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
63  : mOutputUrl( outputUrl )
64 {
65 
66 }
67 
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,
403  const QgsCoordinateReferenceSystem &crs,
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,
973  const QgsCoordinateReferenceSystem &crs )
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 }
virtual int bandCount() const =0
Gets number of bands.
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.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsRasterFileWriter(const QString &outputUrl)
Base class for processing modules.
Definition: qgsrasterpipe.h:46
Iterator for sequentially processing raster cells.
Writing was manually canceled.
static double minimumValuePossible(Qgis::DataType dataType)
Helper function that returns the minimum possible value for a GDAL data type.
static Qgis::DataType typeWithNoDataValue(Qgis::DataType dataType, double *noDataValue)
For given data type returns wider type and sets no data value.
static bool typeIsColor(Qgis::DataType type)
Returns true if data type is color.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static QString filterForDriver(const QString &driverName)
Creates a filter for an GDAL driver key.
static double maximumValuePossible(Qgis::DataType dataType)
Helper function that returns the maximum possible value for a GDAL data type.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:64
QgsRasterProjector * projector() const
Raster pipe that deals with null values.
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:107
DataType
Raster data types.
Definition: qgis.h:101
QgsRasterInterface * last() const
double maximumValue
The maximum cell value in the raster band.
static QStringList extensionsForFormat(const QString &format)
Returns a list of known file extensions for the given GDAL driver format.
Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual bool setNoDataValue(int bandNo, double noDataValue)
Set no data value on created dataset.
virtual Qgis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
Thirty two bit floating point (float)
Definition: qgis.h:109
const QgsCoordinateReferenceSystem & crs
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.
static bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes.
Sixteen bit signed integer (qint16)
Definition: qgis.h:106
Complex Int16.
Definition: qgis.h:111
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
Sixty four bit floating point (double)
Definition: qgis.h:110
QgsDataProvider * createProvider(const QString &providerKey, const QString &dataSource, const QgsDataProvider::ProviderOptions &options=QgsDataProvider::ProviderOptions())
Creates a new instance of a provider.
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
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:116
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.
The RasterBandStats struct is a container for statistics about a single raster band.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
static QStringList supportedFormatExtensions(RasterFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats.
Raster data container.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source CRS.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
virtual QList< QgsRasterPyramid > buildPyramidList(QList< int > overviewList=QList< int >())
Returns the raster layers pyramid list.
QgsRectangle extent() const override=0
Returns the extent of the layer.
virtual bool isValid() const =0
Returns true if this is a valid layer.
void startRasterRead(int bandNumber, qgssize nCols, qgssize nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback=nullptr)
Start reading of raster band.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
Complex Float32.
Definition: qgis.h:113
void appendError(const QString &error)
Appends an error message to the stored list of errors.
Complex Int32.
Definition: qgis.h:112
static int typeSize(int dataType)
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:105
Q_DECL_DEPRECATED WriterError writeRaster(const QgsRasterPipe *pipe, int nCols, int nRows, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &crs, QgsRasterBlockFeedback *feedback=nullptr)
Write raster file.
QgsRasterRangeList noData(int bandNo) const
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...
Contains information about the context in which a coordinate transform is executed.
Base class for processing filters like renderers, reprojector, resampler etc.
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...
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
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:703
void setOutputNoDataValue(int bandNo, double noData)
Sets the output no data value.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
void setMaximumTileWidth(int w)
Sets the maximum tile width returned during iteration.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination CRS.
Setting options for creating vector data providers.
void setMaximumTileHeight(int h)
Sets the minimum tile height returned during iteration.
Internal error if a value used for &#39;no data&#39; was found in input.
const QgsRasterInterface * input() const
Returns the input raster interface which is being iterated over.
int maximumTileHeight() const
Returns the minimum tile width returned during iteration.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:55
This class represents a coordinate reference system (CRS).
Use recommended sort order, with extremely commonly used formats listed first.
Class for doing transforms between two map coordinate systems.
QgsRasterDataProvider * provider() const
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double minimumValue
The minimum cell value in the raster band.
QgsRasterNuller * nuller() const
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
virtual bool write(void *data, int band, int width, int height, int xOffset, int yOffset)
Writes into the provider datasource.
Details of available filters and formats.
Complex Float64.
Definition: qgis.h:114
QString filterString
Filter string for file picker dialogs.
Feedback object tailored for raster block reading.
QString outputUrl() const
Returns the output URL for the raster.
Eight bit unsigned integer (quint8)
Definition: qgis.h:104
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:115
virtual const QgsRasterInterface * sourceInput() const
Gets source / raw input, the first in pipe, usually provider.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
int maximumTileWidth() const
Returns the maximum tile width returned during iteration.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
Base class for raster data providers.
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.