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