Skip to content

Commit

Permalink
Merge pull request #7983 from rouault/fix_18804
Browse files Browse the repository at this point in the history
QgsRasterFilWriter: fix crash when building overviews (fixes #19679)
  • Loading branch information
rouault authored Sep 23, 2018
2 parents bdc1119 + 0c0e30b commit ac65ee0
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 66 deletions.
58 changes: 11 additions & 47 deletions src/core/raster/qgsrasterfilewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,11 @@ QgsRasterDataProvider *QgsRasterFileWriter::createMultiBandRaster( Qgis::DataTyp

QgsRasterFileWriter::QgsRasterFileWriter( const QString &outputUrl )
: mOutputUrl( outputUrl )
, mOutputProviderKey( QStringLiteral( "gdal" ) )
, mOutputFormat( QStringLiteral( "GTiff" ) )
{

}

QgsRasterFileWriter::QgsRasterFileWriter()
: mOutputProviderKey( QStringLiteral( "gdal" ) )
, mOutputFormat( QStringLiteral( "GTiff" ) )
{

}
Expand Down Expand Up @@ -400,7 +396,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const Qgs
{
if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
{
buildPyramids( mOutputUrl );
buildPyramids( mOutputUrl, destProvider );
}
}

Expand Down Expand Up @@ -682,48 +678,20 @@ void QgsRasterFileWriter::addToVRT( const QString &filename, int band, int xSize
bandElem.appendChild( simpleSourceElem );
}

#if 0
void QgsRasterFileWriter::buildPyramids( const QString &filename )
{
GDALDatasetH dataSet;
GDALAllRegister();
dataSet = GDALOpen( filename.toLocal8Bit().data(), GA_Update );
if ( !dataSet )
{
return;
}

//2,4,8,16,32,64
int overviewList[6];
overviewList[0] = 2;
overviewList[1] = 4;
overviewList[2] = 8;
overviewList[3] = 16;
overviewList[4] = 32;
overviewList[5] = 64;

#if 0
if ( mProgressDialog )
{
mProgressDialog->setLabelText( QObject::tr( "Building Pyramids..." ) );
mProgressDialog->setValue( 0 );
mProgressDialog->setWindowModality( Qt::WindowModal );
mProgressDialog->show();
}
#endif
GDALBuildOverviews( dataSet, "AVERAGE", 6, overviewList, 0, 0, /*pyramidsProgress*/ 0, /*mProgressDialog*/ 0 );
}
#endif

void QgsRasterFileWriter::buildPyramids( const QString &filename )
void QgsRasterFileWriter::buildPyramids( const QString &filename, QgsRasterDataProvider *destProviderIn )
{
QgsDebugMsgLevel( "filename = " + filename, 4 );
// open new dataProvider so we can build pyramids with it
QgsDataProvider::ProviderOptions providerOptions;
QgsRasterDataProvider *destProvider = dynamic_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mOutputProviderKey, filename, providerOptions ) );
QgsRasterDataProvider *destProvider = destProviderIn;
if ( !destProvider )
{
return;
destProvider = dynamic_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mOutputProviderKey, filename, providerOptions ) );
if ( !destProvider || !destProvider->isValid() )
{
delete destProvider;
return;
}
}

// TODO progress report
Expand Down Expand Up @@ -763,11 +731,6 @@ void QgsRasterFileWriter::buildPyramids( const QString &filename )
title = QObject::tr( "Building Pyramids" );
message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
}
else if ( res == QLatin1String( "ERROR_JPEG_COMPRESSION" ) )
{
title = QObject::tr( "Building Pyramids" );
message = QObject::tr( "Building internal pyramid overviews is not supported on raster layers with JPEG compression and your current libtiff library." );
}
else if ( res == QLatin1String( "ERROR_VIRTUAL" ) )
{
title = QObject::tr( "Building Pyramids" );
Expand All @@ -776,7 +739,8 @@ void QgsRasterFileWriter::buildPyramids( const QString &filename )
QMessageBox::warning( nullptr, title, message );
QgsDebugMsgLevel( res + " - " + message, 4 );
}
delete destProvider;
if ( !destProviderIn )
delete destProvider;
}

#if 0
Expand Down
8 changes: 4 additions & 4 deletions src/core/raster/qgsrasterfilewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class CORE_EXPORT QgsRasterFileWriter
bool writeVRT( const QString &file );
//add file entry to vrt
void addToVRT( const QString &filename, int band, int xSize, int ySize, int xOffset, int yOffset );
void buildPyramids( const QString &filename );
void buildPyramids( const QString &filename, QgsRasterDataProvider *destProviderIn = nullptr );

//! Create provider and datasource for a part image (vrt mode)
QgsRasterDataProvider *createPartProvider( const QgsRectangle &extent, int nCols, int iterCols, int iterRows,
Expand Down Expand Up @@ -275,8 +275,8 @@ class CORE_EXPORT QgsRasterFileWriter

Mode mMode = Raw;
QString mOutputUrl;
QString mOutputProviderKey;
QString mOutputFormat;
QString mOutputProviderKey = QStringLiteral( "gdal" );
QString mOutputFormat = QStringLiteral( "GTiff" );
QStringList mCreateOptions;
QgsCoordinateReferenceSystem mOutputCRS;

Expand All @@ -286,7 +286,7 @@ class CORE_EXPORT QgsRasterFileWriter
double mMaxTileHeight = 500;

QList< int > mPyramidsList;
QString mPyramidsResampling;
QString mPyramidsResampling = QStringLiteral( "AVERAGE" );
QgsRaster::RasterBuildPyramids mBuildPyramidsFlag = QgsRaster::PyramidsFlagNo;
QgsRaster::RasterPyramidsFormat mPyramidsFormat = QgsRaster::PyramidsGTiff;
QStringList mPyramidsConfigOptions;
Expand Down
21 changes: 7 additions & 14 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1618,20 +1618,6 @@ QString QgsGdalProvider::buildPyramids( const QList<QgsRasterPyramid> &rasterPyr
return QStringLiteral( "ERROR_WRITE_ACCESS" );
}

// libtiff < 4.0 has a bug that prevents safe building of overviews on JPEG compressed files
// we detect libtiff < 4.0 by checking that BIGTIFF is not in the creation options of the GTiff driver
// see https://trac.osgeo.org/qgis/ticket/1357
const char *pszGTiffCreationOptions =
GDALGetMetadataItem( GDALGetDriverByName( "GTiff" ), GDAL_DMD_CREATIONOPTIONLIST, "" );
if ( !strstr( pszGTiffCreationOptions, "BIGTIFF" ) )
{
QString myCompressionType = QString( GDALGetMetadataItem( mGdalDataset, "COMPRESSION", "IMAGE_STRUCTURE" ) );
if ( "JPEG" == myCompressionType )
{
return QStringLiteral( "ERROR_JPEG_COMPRESSION" );
}
}

// if needed close the gdal dataset and reopen it in read / write mode
// TODO this doesn't seem to work anymore... must fix it before 2.0!!!
// no errors are reported, but pyramids are not present in file.
Expand All @@ -1657,10 +1643,17 @@ QString QgsGdalProvider::buildPyramids( const QList<QgsRasterPyramid> &rasterPyr
// are we using Erdas Imagine external overviews?
QgsStringMap myConfigOptionsOld;
myConfigOptionsOld[ QStringLiteral( "USE_RRD" )] = CPLGetConfigOption( "USE_RRD", "NO" );
myConfigOptionsOld[ QStringLiteral( "TIFF_USE_OVR" )] = CPLGetConfigOption( "TIFF_USE_OVR", "NO" );
if ( format == QgsRaster::PyramidsErdas )
CPLSetConfigOption( "USE_RRD", "YES" );
else
{
CPLSetConfigOption( "USE_RRD", "NO" );
if ( format == QgsRaster::PyramidsGTiff )
{
CPLSetConfigOption( "TIFF_USE_OVR", "YES" );
}
}

// add any driver-specific configuration options, save values to be restored later
if ( format != QgsRaster::PyramidsErdas && ! configOptions.isEmpty() )
Expand Down
49 changes: 48 additions & 1 deletion tests/src/python/test_qgsrasterfilewriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

from osgeo import gdal
from qgis.PyQt.QtCore import QTemporaryFile, QDir
from qgis.core import (QgsRasterLayer,
from qgis.core import (QgsRaster,
QgsRasterLayer,
QgsRasterChecker,
QgsRasterPipe,
QgsRasterFileWriter,
Expand Down Expand Up @@ -178,6 +179,52 @@ def testImportIntoGpkg(self):
# remove result file
os.unlink(test_gpkg)

def _testGeneratePyramids(self, pyramidFormat):
tmpName = tempfile.mktemp(suffix='.tif')
source = QgsRasterLayer(os.path.join(self.testDataDir, 'raster', 'byte.tif'), 'my', 'gdal')
self.assertTrue(source.isValid())
provider = source.dataProvider()
fw = QgsRasterFileWriter(tmpName)

fw.setBuildPyramidsFlag(QgsRaster.PyramidsFlagYes)
fw.setPyramidsFormat(pyramidFormat)
fw.setPyramidsList([2])

pipe = QgsRasterPipe()
self.assertTrue(pipe.set(provider.clone()))

projector = QgsRasterProjector()
projector.setCrs(provider.crs(), provider.crs())
self.assertTrue(pipe.insert(2, projector))

self.assertEqual(fw.writeRaster(pipe,
provider.xSize(),
provider.ySize(),
provider.extent(),
provider.crs()), 0)
del fw
ds = gdal.Open(tmpName)
self.assertEqual(ds.GetRasterBand(1).GetOverviewCount(), 1)
fl = ds.GetFileList()
if pyramidFormat == QgsRaster.PyramidsGTiff:
self.assertEqual(len(fl), 2, fl)
self.assertIn('.ovr', fl[1])
elif pyramidFormat == QgsRaster.PyramidsInternal:
self.assertEqual(len(fl), 1, fl)
elif pyramidFormat == QgsRaster.PyramidsErdas:
self.assertEqual(len(fl), 2, fl)
self.assertIn('.aux', fl[1])
os.unlink(tmpName)

def testGeneratePyramidsExternal(self):
return self._testGeneratePyramids(QgsRaster.PyramidsGTiff)

def testGeneratePyramidsInternal(self):
return self._testGeneratePyramids(QgsRaster.PyramidsInternal)

def testGeneratePyramidsErdas(self):
return self._testGeneratePyramids(QgsRaster.PyramidsErdas)


if __name__ == '__main__':
unittest.main()

0 comments on commit ac65ee0

Please sign in to comment.