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