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