QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 ( !partDestProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, 0, 0 ) )
527  {
528  return WriteError;
529  }
530  addToVRT( partFileName( fileIndex ), i, iterCols, iterRows, iterLeft, iterTop );
531  }
532 
533  }
534  else if ( destProvider )
535  {
536  //loop over data
537  for ( int i = 1; i <= nBands; ++i )
538  {
539  if ( !destProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, iterLeft, iterTop ) )
540  {
541  return WriteError;
542  }
543  }
544  }
545  ++fileIndex;
546  }
547 
548  QgsDebugMsgLevel( QStringLiteral( "Done" ), 4 );
549  return ( feedback && feedback->isCanceled() ) ? WriteCanceled : NoError;
550 }
551 
552 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,
554 {
555  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
556  if ( !iter )
557  {
558  return SourceProviderError;
559  }
560 
561  const QgsRasterInterface *iface = iter->input();
562  if ( !iface )
563  return SourceProviderError;
564 
565  Qgis::DataType inputDataType = iface->dataType( 1 );
566  if ( inputDataType != Qgis::ARGB32 && inputDataType != Qgis::ARGB32_Premultiplied )
567  {
568  return SourceProviderError;
569  }
570  const bool isPremultiplied = ( inputDataType == Qgis::ARGB32_Premultiplied );
571 
572  iter->setMaximumTileWidth( mMaxTileWidth );
573  iter->setMaximumTileHeight( mMaxTileHeight );
574 
575  const size_t nMaxPixels = static_cast<size_t>( mMaxTileWidth ) * mMaxTileHeight;
576  std::vector<unsigned char> redData( nMaxPixels );
577  std::vector<unsigned char> greenData( nMaxPixels );
578  std::vector<unsigned char> blueData( nMaxPixels );
579  std::vector<unsigned char> alphaData( nMaxPixels );
580  int iterLeft = 0, iterTop = 0, iterCols = 0, iterRows = 0;
581  int fileIndex = 0;
582 
583  //create destProvider for whole dataset here
584  double pixelSize;
585  double geoTransform[6];
586  globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
587 
588  const int nOutputBands = 4;
589  std::unique_ptr< QgsRasterDataProvider > destProvider( initOutput( nCols, nRows, crs, geoTransform, nOutputBands, Qgis::Byte ) );
590  if ( !mTiledMode )
591  {
592  if ( !destProvider )
593  {
594  return CreateDatasourceError;
595  }
596  if ( !destProvider->isValid() )
597  {
598  if ( feedback && !destProvider->error().isEmpty() )
599  {
600  feedback->appendError( destProvider->error().summary() );
601  }
602  return CreateDatasourceError;
603  }
604  if ( nCols != destProvider->xSize() || nRows != destProvider->ySize() )
605  {
606  QgsDebugMsg( QStringLiteral( "Created raster does not have requested dimensions" ) );
607  if ( feedback )
608  {
609  feedback->appendError( QObject::tr( "Created raster does not have requested dimensions" ) );
610  }
611  return CreateDatasourceError;
612  }
613  if ( nOutputBands != destProvider->bandCount() )
614  {
615  QgsDebugMsg( QStringLiteral( "Created raster does not have requested band count" ) );
616  if ( feedback )
617  {
618  feedback->appendError( QObject::tr( "Created raster does not have requested band count" ) );
619  }
620  return CreateDatasourceError;
621  }
622  if ( Qgis::Byte != destProvider->dataType( 1 ) )
623  {
624  QgsDebugMsg( QStringLiteral( "Created raster does not have requested data type" ) );
625  if ( feedback )
626  {
627  feedback->appendError( QObject::tr( "Created raster does not have requested data type" ) );
628  }
629  return CreateDatasourceError;
630  }
631  }
632 
633  iter->startRasterRead( 1, nCols, nRows, outputExtent, feedback );
634 
635  int nParts = 0;
636  if ( feedback )
637  {
638  int nPartsX = qgsDivRoundUp( nCols, iter->maximumTileWidth() );
639  int nPartsY = qgsDivRoundUp( nRows, iter->maximumTileHeight() );
640  nParts = nPartsX * nPartsY;
641  }
642 
643  std::unique_ptr< QgsRasterBlock > inputBlock;
644  while ( iter->readNextRasterPart( 1, iterCols, iterRows, inputBlock, iterLeft, iterTop ) )
645  {
646  if ( !inputBlock )
647  {
648  continue;
649  }
650 
651  if ( feedback && fileIndex < ( nParts - 1 ) )
652  {
653  feedback->setProgress( 100.0 * fileIndex / static_cast< double >( nParts ) );
654  if ( feedback->isCanceled() )
655  {
656  break;
657  }
658  }
659 
660  //fill into red/green/blue/alpha channels
661  qgssize nPixels = static_cast< qgssize >( iterCols ) * iterRows;
662  for ( qgssize i = 0; i < nPixels; ++i )
663  {
664  QRgb c = inputBlock->color( i );
665  if ( isPremultiplied )
666  {
667  c = qUnpremultiply( c );
668  }
669  redData[i] = static_cast<unsigned char>( qRed( c ) );
670  greenData[i] = static_cast<unsigned char>( qGreen( c ) );
671  blueData[i] = static_cast<unsigned char>( qBlue( c ) );
672  alphaData[i] = static_cast<unsigned char>( qAlpha( c ) );
673  }
674 
675  //create output file
676  if ( mTiledMode )
677  {
678  std::unique_ptr< QgsRasterDataProvider > partDestProvider( createPartProvider( outputExtent,
679  nCols, iterCols, iterRows,
680  iterLeft, iterTop, mOutputUrl, fileIndex,
681  4, Qgis::Byte, crs ) );
682 
683  if ( !partDestProvider || partDestProvider->isValid() )
684  {
685  return DestProviderError;
686  }
687 
688  //write data to output file
689  if ( !partDestProvider->write( &redData[0], 1, iterCols, iterRows, 0, 0 ) ||
690  !partDestProvider->write( &greenData[0], 2, iterCols, iterRows, 0, 0 ) ||
691  !partDestProvider->write( &blueData[0], 3, iterCols, iterRows, 0, 0 ) ||
692  !partDestProvider->write( &alphaData[0], 4, iterCols, iterRows, 0, 0 ) )
693  {
694  return WriteError;
695  }
696 
697  addToVRT( partFileName( fileIndex ), 1, iterCols, iterRows, iterLeft, iterTop );
698  addToVRT( partFileName( fileIndex ), 2, iterCols, iterRows, iterLeft, iterTop );
699  addToVRT( partFileName( fileIndex ), 3, iterCols, iterRows, iterLeft, iterTop );
700  addToVRT( partFileName( fileIndex ), 4, iterCols, iterRows, iterLeft, iterTop );
701  }
702  else if ( destProvider )
703  {
704  if ( !destProvider->write( &redData[0], 1, iterCols, iterRows, iterLeft, iterTop ) ||
705  !destProvider->write( &greenData[0], 2, iterCols, iterRows, iterLeft, iterTop ) ||
706  !destProvider->write( &blueData[0], 3, iterCols, iterRows, iterLeft, iterTop ) ||
707  !destProvider->write( &alphaData[0], 4, iterCols, iterRows, iterLeft, iterTop ) )
708  {
709  return WriteError;
710  }
711  }
712 
713  ++fileIndex;
714  }
715  destProvider.reset();
716 
717  if ( feedback )
718  {
719  feedback->setProgress( 100.0 );
720  }
721 
722  if ( mTiledMode )
723  {
724  QString vrtFilePath( mOutputUrl + '/' + vrtFileName() );
725  writeVRT( vrtFilePath );
726  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
727  {
728  buildPyramids( vrtFilePath );
729  }
730  }
731  else
732  {
733  if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
734  {
735  buildPyramids( mOutputUrl );
736  }
737  }
738  return ( feedback && feedback->isCanceled() ) ? WriteCanceled : NoError;
739 }
740 
741 void QgsRasterFileWriter::addToVRT( const QString &filename, int band, int xSize, int ySize, int xOffset, int yOffset )
742 {
743  QDomElement bandElem = mVRTBands.value( band - 1 );
744 
745  QDomElement simpleSourceElem = mVRTDocument.createElement( QStringLiteral( "SimpleSource" ) );
746 
747  //SourceFilename
748  QDomElement sourceFilenameElem = mVRTDocument.createElement( QStringLiteral( "SourceFilename" ) );
749  sourceFilenameElem.setAttribute( QStringLiteral( "relativeToVRT" ), QStringLiteral( "1" ) );
750  QDomText sourceFilenameText = mVRTDocument.createTextNode( filename );
751  sourceFilenameElem.appendChild( sourceFilenameText );
752  simpleSourceElem.appendChild( sourceFilenameElem );
753 
754  //SourceBand
755  QDomElement sourceBandElem = mVRTDocument.createElement( QStringLiteral( "SourceBand" ) );
756  QDomText sourceBandText = mVRTDocument.createTextNode( QString::number( band ) );
757  sourceBandElem.appendChild( sourceBandText );
758  simpleSourceElem.appendChild( sourceBandElem );
759 
760  //SourceProperties
761  QDomElement sourcePropertiesElem = mVRTDocument.createElement( QStringLiteral( "SourceProperties" ) );
762  sourcePropertiesElem.setAttribute( QStringLiteral( "RasterXSize" ), xSize );
763  sourcePropertiesElem.setAttribute( QStringLiteral( "RasterYSize" ), ySize );
764  sourcePropertiesElem.setAttribute( QStringLiteral( "BlockXSize" ), xSize );
765  sourcePropertiesElem.setAttribute( QStringLiteral( "BlockYSize" ), ySize );
766  sourcePropertiesElem.setAttribute( QStringLiteral( "DataType" ), QStringLiteral( "Byte" ) );
767  simpleSourceElem.appendChild( sourcePropertiesElem );
768 
769  //SrcRect
770  QDomElement srcRectElem = mVRTDocument.createElement( QStringLiteral( "SrcRect" ) );
771  srcRectElem.setAttribute( QStringLiteral( "xOff" ), QStringLiteral( "0" ) );
772  srcRectElem.setAttribute( QStringLiteral( "yOff" ), QStringLiteral( "0" ) );
773  srcRectElem.setAttribute( QStringLiteral( "xSize" ), xSize );
774  srcRectElem.setAttribute( QStringLiteral( "ySize" ), ySize );
775  simpleSourceElem.appendChild( srcRectElem );
776 
777  //DstRect
778  QDomElement dstRectElem = mVRTDocument.createElement( QStringLiteral( "DstRect" ) );
779  dstRectElem.setAttribute( QStringLiteral( "xOff" ), xOffset );
780  dstRectElem.setAttribute( QStringLiteral( "yOff" ), yOffset );
781  dstRectElem.setAttribute( QStringLiteral( "xSize" ), xSize );
782  dstRectElem.setAttribute( QStringLiteral( "ySize" ), ySize );
783  simpleSourceElem.appendChild( dstRectElem );
784 
785  bandElem.appendChild( simpleSourceElem );
786 }
787 
788 void QgsRasterFileWriter::buildPyramids( const QString &filename, QgsRasterDataProvider *destProviderIn )
789 {
790  QgsDebugMsgLevel( "filename = " + filename, 4 );
791  // open new dataProvider so we can build pyramids with it
792  QgsDataProvider::ProviderOptions providerOptions;
793  QgsRasterDataProvider *destProvider = destProviderIn;
794  if ( !destProvider )
795  {
796  destProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mOutputProviderKey, filename, providerOptions ) );
797  if ( !destProvider || !destProvider->isValid() )
798  {
799  delete destProvider;
800  return;
801  }
802  }
803 
804  // TODO progress report
805  // TODO test mTiledMode - not tested b/c segfault at line # 289
806  // connect( provider, SIGNAL( progressUpdate( int ) ), mPyramidProgress, SLOT( setValue( int ) ) );
807  QList< QgsRasterPyramid> myPyramidList;
808  if ( ! mPyramidsList.isEmpty() )
809  myPyramidList = destProvider->buildPyramidList( mPyramidsList );
810  for ( int myCounterInt = 0; myCounterInt < myPyramidList.count(); myCounterInt++ )
811  {
812  myPyramidList[myCounterInt].build = true;
813  }
814 
815  QgsDebugMsgLevel( QStringLiteral( "building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ), 4 );
816  // QApplication::setOverrideCursor( Qt::WaitCursor );
817  QString res = destProvider->buildPyramids( myPyramidList, mPyramidsResampling,
818  mPyramidsFormat, mPyramidsConfigOptions );
819  // QApplication::restoreOverrideCursor();
820 
821  // TODO put this in provider or elsewhere
822  if ( !res.isNull() )
823  {
824  QString title, message;
825  if ( res == QLatin1String( "ERROR_WRITE_ACCESS" ) )
826  {
827  title = QObject::tr( "Building Pyramids" );
828  message = QObject::tr( "Write access denied. Adjust the file permissions and try again." );
829  }
830  else if ( res == QLatin1String( "ERROR_WRITE_FORMAT" ) )
831  {
832  title = QObject::tr( "Building Pyramids" );
833  message = QObject::tr( "The file was not writable. Some formats do not "
834  "support pyramid overviews. Consult the GDAL documentation if in doubt." );
835  }
836  else if ( res == QLatin1String( "FAILED_NOT_SUPPORTED" ) )
837  {
838  title = QObject::tr( "Building Pyramids" );
839  message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
840  }
841  else if ( res == QLatin1String( "ERROR_VIRTUAL" ) )
842  {
843  title = QObject::tr( "Building Pyramids" );
844  message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
845  }
846  QMessageBox::warning( nullptr, title, message );
847  QgsDebugMsgLevel( res + " - " + message, 4 );
848  }
849  if ( !destProviderIn )
850  delete destProvider;
851 }
852 
853 #if 0
854 int QgsRasterFileWriter::pyramidsProgress( double dfComplete, const char *pszMessage, void *pData )
855 {
856  Q_UNUSED( pszMessage )
857  GDALTermProgress( dfComplete, 0, 0 );
858  QProgressDialog *progressDialog = static_cast<QProgressDialog *>( pData );
859  if ( pData && progressDialog->wasCanceled() )
860  {
861  return 0;
862  }
863 
864  if ( pData )
865  {
866  progressDialog->setRange( 0, 100 );
867  progressDialog->setValue( dfComplete * 100 );
868  }
869  return 1;
870 }
871 #endif
872 
873 void QgsRasterFileWriter::createVRT( int xSize, int ySize, const QgsCoordinateReferenceSystem &crs, double *geoTransform, Qgis::DataType type, const QList<bool> &destHasNoDataValueList, const QList<double> &destNoDataValueList )
874 {
875  mVRTDocument.clear();
876  QDomElement VRTDatasetElem = mVRTDocument.createElement( QStringLiteral( "VRTDataset" ) );
877 
878  //xsize / ysize
879  VRTDatasetElem.setAttribute( QStringLiteral( "rasterXSize" ), xSize );
880  VRTDatasetElem.setAttribute( QStringLiteral( "rasterYSize" ), ySize );
881  mVRTDocument.appendChild( VRTDatasetElem );
882 
883  //CRS
884  QDomElement SRSElem = mVRTDocument.createElement( QStringLiteral( "SRS" ) );
885  QDomText crsText = mVRTDocument.createTextNode( crs.toWkt() );
886  SRSElem.appendChild( crsText );
887  VRTDatasetElem.appendChild( SRSElem );
888 
889  //geotransform
890  if ( geoTransform )
891  {
892  QDomElement geoTransformElem = mVRTDocument.createElement( QStringLiteral( "GeoTransform" ) );
893  QString geoTransformString = QString::number( geoTransform[0], 'f', 6 ) + ", " + QString::number( geoTransform[1] ) + ", " + QString::number( geoTransform[2] ) +
894  ", " + QString::number( geoTransform[3], 'f', 6 ) + ", " + QString::number( geoTransform[4] ) + ", " + QString::number( geoTransform[5] );
895  QDomText geoTransformText = mVRTDocument.createTextNode( geoTransformString );
896  geoTransformElem.appendChild( geoTransformText );
897  VRTDatasetElem.appendChild( geoTransformElem );
898  }
899 
900  int nBands;
901  if ( mMode == Raw )
902  {
903  nBands = mInput->bandCount();
904  }
905  else
906  {
907  nBands = 4;
908  }
909 
910  QStringList colorInterp;
911  colorInterp << QStringLiteral( "Red" ) << QStringLiteral( "Green" ) << QStringLiteral( "Blue" ) << QStringLiteral( "Alpha" );
912 
913  QMap<Qgis::DataType, QString> dataTypes;
914  dataTypes.insert( Qgis::Byte, QStringLiteral( "Byte" ) );
915  dataTypes.insert( Qgis::UInt16, QStringLiteral( "UInt16" ) );
916  dataTypes.insert( Qgis::Int16, QStringLiteral( "Int16" ) );
917  dataTypes.insert( Qgis::UInt32, QStringLiteral( "Int32" ) );
918  dataTypes.insert( Qgis::Float32, QStringLiteral( "Float32" ) );
919  dataTypes.insert( Qgis::Float64, QStringLiteral( "Float64" ) );
920  dataTypes.insert( Qgis::CInt16, QStringLiteral( "CInt16" ) );
921  dataTypes.insert( Qgis::CInt32, QStringLiteral( "CInt32" ) );
922  dataTypes.insert( Qgis::CFloat32, QStringLiteral( "CFloat32" ) );
923  dataTypes.insert( Qgis::CFloat64, QStringLiteral( "CFloat64" ) );
924 
925  for ( int i = 1; i <= nBands; i++ )
926  {
927  QDomElement VRTBand = mVRTDocument.createElement( QStringLiteral( "VRTRasterBand" ) );
928 
929  VRTBand.setAttribute( QStringLiteral( "band" ), QString::number( i ) );
930  QString dataType = dataTypes.value( type );
931  VRTBand.setAttribute( QStringLiteral( "dataType" ), dataType );
932 
933  if ( mMode == Image )
934  {
935  VRTBand.setAttribute( QStringLiteral( "dataType" ), QStringLiteral( "Byte" ) );
936  QDomElement colorInterpElement = mVRTDocument.createElement( QStringLiteral( "ColorInterp" ) );
937  QDomText interpText = mVRTDocument.createTextNode( colorInterp.value( i - 1 ) );
938  colorInterpElement.appendChild( interpText );
939  VRTBand.appendChild( colorInterpElement );
940  }
941 
942  if ( !destHasNoDataValueList.isEmpty() && destHasNoDataValueList.value( i - 1 ) )
943  {
944  VRTBand.setAttribute( QStringLiteral( "NoDataValue" ), QString::number( destNoDataValueList.value( i - 1 ) ) );
945  }
946 
947  mVRTBands.append( VRTBand );
948  VRTDatasetElem.appendChild( VRTBand );
949  }
950 }
951 
952 bool QgsRasterFileWriter::writeVRT( const QString &file )
953 {
954  QFile outputFile( file );
955  if ( ! outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
956  {
957  return false;
958  }
959 
960  QTextStream outStream( &outputFile );
961  mVRTDocument.save( outStream, 2 );
962  return true;
963 }
964 
965 QgsRasterDataProvider *QgsRasterFileWriter::createPartProvider( const QgsRectangle &extent, int nCols, int iterCols,
966  int iterRows, int iterLeft, int iterTop, const QString &outputUrl, int fileIndex, int nBands, Qgis::DataType type,
967  const QgsCoordinateReferenceSystem &crs )
968 {
969  double mup = extent.width() / nCols;
970  double mapLeft = extent.xMinimum() + iterLeft * mup;
971  double mapRight = mapLeft + mup * iterCols;
972  double mapTop = extent.yMaximum() - iterTop * mup;
973  double mapBottom = mapTop - iterRows * mup;
974  QgsRectangle mapRect( mapLeft, mapBottom, mapRight, mapTop );
975 
976  QString outputFile = outputUrl + '/' + partFileName( fileIndex );
977 
978  //geotransform
979  double geoTransform[6];
980  geoTransform[0] = mapRect.xMinimum();
981  geoTransform[1] = mup;
982  geoTransform[2] = 0.0;
983  geoTransform[3] = mapRect.yMaximum();
984  geoTransform[4] = 0.0;
985  geoTransform[5] = -mup;
986 
987  // perhaps we need a separate createOptions for tiles ?
988 
989  QgsRasterDataProvider *destProvider = QgsRasterDataProvider::create( mOutputProviderKey, outputFile, mOutputFormat, nBands, type, iterCols, iterRows, geoTransform, crs, mCreateOptions );
990 
991  // TODO: return provider and report error
992  return destProvider;
993 }
994 
995 QgsRasterDataProvider *QgsRasterFileWriter::initOutput( int nCols, int nRows, const QgsCoordinateReferenceSystem &crs,
996  double *geoTransform, int nBands, Qgis::DataType type,
997  const QList<bool> &destHasNoDataValueList, const QList<double> &destNoDataValueList )
998 {
999  if ( mTiledMode )
1000  {
1001  createVRT( nCols, nRows, crs, geoTransform, type, destHasNoDataValueList, destNoDataValueList );
1002  return nullptr;
1003  }
1004  else
1005  {
1006 #if 0
1007  // TODO enable "use existing", has no effect for now, because using Create() in gdal provider
1008  // should this belong in provider? should also test that source provider is gdal
1009  if ( mBuildPyramidsFlag == -4 && mOutputProviderKey == "gdal" && mOutputFormat.compare( QLatin1String( "gtiff" ), Qt::CaseInsensitive ) == 0 )
1010  mCreateOptions << "COPY_SRC_OVERVIEWS=YES";
1011 #endif
1012 
1013  QgsRasterDataProvider *destProvider = QgsRasterDataProvider::create( mOutputProviderKey, mOutputUrl, mOutputFormat, nBands, type, nCols, nRows, geoTransform, crs, mCreateOptions );
1014 
1015  if ( !destProvider )
1016  {
1017  QgsDebugMsg( QStringLiteral( "No provider created" ) );
1018  }
1019 
1020  return destProvider;
1021  }
1022 }
1023 
1024 void QgsRasterFileWriter::globalOutputParameters( const QgsRectangle &extent, int nCols, int &nRows,
1025  double *geoTransform, double &pixelSize )
1026 {
1027  pixelSize = extent.width() / nCols;
1028 
1029  //calculate nRows automatically for providers without exact resolution
1030  if ( nRows < 0 )
1031  {
1032  nRows = static_cast< double >( nCols ) / extent.width() * extent.height() + 0.5; //NOLINT
1033  }
1034  geoTransform[0] = extent.xMinimum();
1035  geoTransform[1] = pixelSize;
1036  geoTransform[2] = 0.0;
1037  geoTransform[3] = extent.yMaximum();
1038  geoTransform[4] = 0.0;
1039  geoTransform[5] = -( extent.height() / nRows );
1040 }
1041 
1042 QString QgsRasterFileWriter::partFileName( int fileIndex )
1043 {
1044  // .tif for now
1045  QFileInfo outputInfo( mOutputUrl );
1046  return QStringLiteral( "%1.%2.tif" ).arg( outputInfo.fileName() ).arg( fileIndex );
1047 }
1048 
1049 QString QgsRasterFileWriter::vrtFileName()
1050 {
1051  QFileInfo outputInfo( mOutputUrl );
1052  return QStringLiteral( "%1.vrt" ).arg( outputInfo.fileName() );
1053 }
1054 
1055 QString QgsRasterFileWriter::driverForExtension( const QString &extension )
1056 {
1057  QString ext = extension.trimmed();
1058  if ( ext.isEmpty() )
1059  return QString();
1060 
1061  if ( ext.startsWith( '.' ) )
1062  ext.remove( 0, 1 );
1063 
1064  GDALAllRegister();
1065  int const drvCount = GDALGetDriverCount();
1066 
1067  for ( int i = 0; i < drvCount; ++i )
1068  {
1069  GDALDriverH drv = GDALGetDriver( i );
1070  if ( drv )
1071  {
1072  char **driverMetadata = GDALGetMetadata( drv, nullptr );
1073  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
1074  {
1075  QString drvName = GDALGetDriverShortName( drv );
1076  QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
1077 
1078  const auto constDriverExtensions = driverExtensions;
1079  for ( const QString &driver : constDriverExtensions )
1080  {
1081  if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
1082  return drvName;
1083  }
1084  }
1085  }
1086  }
1087  return QString();
1088 }
1089 
1090 QStringList QgsRasterFileWriter::extensionsForFormat( const QString &format )
1091 {
1092  GDALDriverH drv = GDALGetDriverByName( format.toLocal8Bit().data() );
1093  if ( drv )
1094  {
1095  char **driverMetadata = GDALGetMetadata( drv, nullptr );
1096  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
1097  {
1098  return QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
1099  }
1100  }
1101  return QStringList();
1102 }
1103 
1104 QString QgsRasterFileWriter::filterForDriver( const QString &driverName )
1105 {
1106  GDALDriverH drv = GDALGetDriverByName( driverName.toLocal8Bit().data() );
1107  if ( drv )
1108  {
1109  QString drvName = GDALGetDriverLongName( drv );
1110  QString extensionsString = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) );
1111  if ( extensionsString.isEmpty() )
1112  {
1113  return QString();
1114  }
1115  QStringList extensions = extensionsString.split( ' ' );
1116  QString filter = drvName + " (";
1117  for ( const QString &ext : extensions )
1118  {
1119  filter.append( QStringLiteral( "*.%1 *.%2 " ).arg( ext.toLower(), ext.toUpper() ) );
1120  }
1121  filter = filter.trimmed().append( QStringLiteral( ")" ) );
1122  return filter;
1123  }
1124 
1125  return QString();
1126 }
1127 
1128 QList< QgsRasterFileWriter::FilterFormatDetails > QgsRasterFileWriter::supportedFiltersAndFormats( RasterFormatOptions options )
1129 {
1130  QList< FilterFormatDetails > results;
1131 
1132  GDALAllRegister();
1133  int const drvCount = GDALGetDriverCount();
1134 
1135  FilterFormatDetails tifFormat;
1136 
1137  for ( int i = 0; i < drvCount; ++i )
1138  {
1139  GDALDriverH drv = GDALGetDriver( i );
1140  if ( drv )
1141  {
1143  {
1144  QString drvName = GDALGetDriverShortName( drv );
1145  QString filterString = filterForDriver( drvName );
1146  if ( filterString.isEmpty() )
1147  continue;
1148 
1149  FilterFormatDetails details;
1150  details.driverName = drvName;
1151  details.filterString = filterString;
1152 
1153  if ( options & SortRecommended )
1154  {
1155  if ( drvName == QLatin1String( "GTiff" ) )
1156  {
1157  tifFormat = details;
1158  continue;
1159  }
1160  }
1161 
1162  results << details;
1163  }
1164  }
1165  }
1166 
1167  std::sort( results.begin(), results.end(), []( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
1168  {
1169  return a.driverName < b.driverName;
1170  } );
1171 
1172  if ( options & SortRecommended )
1173  {
1174  if ( !tifFormat.filterString.isEmpty() )
1175  {
1176  results.insert( 0, tifFormat );
1177  }
1178  }
1179 
1180  return results;
1181 }
1182 
1183 QStringList QgsRasterFileWriter::supportedFormatExtensions( const RasterFormatOptions options )
1184 {
1185  const auto formats = supportedFiltersAndFormats( options );
1186  QStringList extensions;
1187 
1188  QRegularExpression rx( QStringLiteral( "\\*\\.([a-zA-Z0-9]*)" ) );
1189 
1190  for ( const FilterFormatDetails &format : formats )
1191  {
1192  QString ext = format.filterString;
1193  QRegularExpressionMatch match = rx.match( ext );
1194  if ( !match.hasMatch() )
1195  continue;
1196 
1197  QString matched = match.captured( 1 );
1198  extensions << matched;
1199  }
1200  return extensions;
1201 }
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:63
QgsRasterProjector * projector() const
Raster pipe that deals with null values.
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:86
DataType
Raster data types.
Definition: qgis.h:80
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:88
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:85
Complex Int16.
Definition: qgis.h:90
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
Sixty four bit floating point (double)
Definition: qgis.h:89
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:95
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.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
Complex Float32.
Definition: qgis.h:92
void appendError(const QString &error)
Appends an error message to the stored list of errors.
Complex Int32.
Definition: qgis.h:91
static int typeSize(int dataType)
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:84
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:621
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:54
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:93
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.
void startRasterRead(int bandNumber, int nCols, int nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback=nullptr)
Start reading of raster band.
Eight bit unsigned integer (quint8)
Definition: qgis.h:83
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:94
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.