Skip to content

Commit

Permalink
Improve documentation for raster pyramid creation methods/classes
Browse files Browse the repository at this point in the history
The PyQGIS documentation for this workflow was very poorly documented
  • Loading branch information
nyalldawson committed Mar 31, 2021
1 parent 6bc879f commit c4c8aab
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 103 deletions.
46 changes: 39 additions & 7 deletions python/core/auto_generated/raster/qgsrasterdataprovider.sip.in
Expand Up @@ -236,23 +236,55 @@ Returns a new image downloader for the raster legend.
const QStringList &configOptions = QStringList(),
QgsRasterBlockFeedback *feedback = 0 );
%Docstring
Create pyramid overviews
Creates pyramid overviews.

:param pyramidList: a list of :py:class:`QgsRasterPryamids` to create overviews for. The :py:func:`QgsRasterPryamid.setBuild()` flag
should be set to ``True`` for every layer where pyramids are desired.
:param resamplingMethod: resampling method to use when creating the pyramids. The :py:func:`~QgsRasterDataProvider.pyramidResamplingMethods` method
can be used to retrieve a list of valid resampling methods available for specific raster data providers.
:param format: raster pyramid format.
:param configOptions: optional configuration options which are passed to the specific data provider
for use during pyramid creation.
:param feedback: optional feedback argument for progress reports and cancelation support.

.. seealso:: :py:func:`buildPyramidList`

.. seealso:: :py:func:`hasPyramids`

.. seealso:: :py:func:`pyramidResamplingMethods`
%End

virtual QList<QgsRasterPyramid> buildPyramidList( QList<int> overviewList = QList<int>() );
virtual QList<QgsRasterPyramid> buildPyramidList( const QList<int> &overviewList = QList<int>() );
%Docstring
Returns the raster layers pyramid list.

This method returns a list of pyramid layers which are valid for the data provider. The returned list
is a complete list of all possible layers, and includes both pyramids layers which currently exist and
layers which have not yet been constructed. To know which of the pyramid layers
ACTUALLY exists you need to look at the :py:func:`QgsRasterPyramid.getExists()` member for each value in the
list.

The returned list is suitable for passing to the :py:func:`~QgsRasterDataProvider.buildPyramids` method. First, modify the returned list
by calling `QgsRasterPryamid.setBuild( ``True`` )` for every layer you want to create pyramids for, and then
pass the modified list to :py:func:`~QgsRasterDataProvider.buildPryamids`.

:param overviewList: used to construct the pyramid list (optional), when empty the list is defined by the provider.
A pyramid list defines the
POTENTIAL pyramids that can be in a raster. To know which of the pyramid layers
ACTUALLY exists you need to look at the existsFlag member in each struct stored in the
list.

.. seealso:: :py:func:`buildPyramids`

.. seealso:: :py:func:`hasPyramids`
%End

bool hasPyramids();
%Docstring
Returns ``True`` if raster has at least one populated histogram.
Returns ``True`` if raster has at least one existing pyramid.

The :py:func:`~QgsRasterDataProvider.buildPyramidList` method can be used to retrieve additional details about potential and existing
pryamid layers.

.. seealso:: :py:func:`buildPyramidList`

.. seealso:: :py:func:`buildPyramids`
%End

virtual QString htmlMetadata() = 0;
Expand Down
99 changes: 93 additions & 6 deletions python/core/auto_generated/raster/qgsrasterpyramid.sip.in
Expand Up @@ -18,13 +18,100 @@ This struct is used to store pyramid info for the raster layer.
#include "qgsrasterpyramid.h"
%End
public:
int level;
int xDim;
int yDim;
bool exists;
bool build;

QgsRasterPyramid();

int getLevel() const;
%Docstring
Returns the pyramid level.

Pyramid levels are as defined by GDAL, eg
level 2 is half the original raster size.

.. versionadded:: 3.20
%End

void setLevel( int level );
%Docstring
Sets the pyramid ``level``.

Pyramid levels are as defined by GDAL, eg
level 2 is half the original raster size.

.. versionadded:: 3.20
%End

%Property( name = level, get = getLevel, set = setLevel )

void setXDim( int dimension );
%Docstring
Sets the x ``dimension`` for this pyramid layer.

.. versionadded:: 3.20
%End

int getXDim() const;
%Docstring
Returns the x dimension for this pyramid layer.

.. versionadded:: 3.20
%End

%Property( name = xDim, get = getXDim, set = setXDim )

void setYDim( int dimension );
%Docstring
Sets the y ``dimension`` for this pyramid layer.

.. versionadded:: 3.20
%End

int getYDim() const;
%Docstring
Returns the y dimension for this pyramid layer.

.. versionadded:: 3.20
%End

%Property( name = yDim, get = getYDim, set = setYDim )

bool getExists() const;
%Docstring
Returns ``True`` if the pyramid layer currently exists.

.. versionadded:: 3.20
%End

void setExists( bool exists );
%Docstring
Sets whether the pyramid layer currently exists.

.. versionadded:: 3.20
%End

%Property( name = exists, get = getExists, set = setExists )

bool getBuild() const;
%Docstring
Returns ``True`` if the pyramid layer will be built.

When used with :py:func:`QgsRasterDataProvider.buildPyramids()` this flag controls
whether pyramids will be built for the layer.

.. versionadded:: 3.20
%End

void setBuild( bool build );
%Docstring
Sets whether the pyramid layer will be built.

When used with :py:func:`QgsRasterDataProvider.buildPyramids()` this flag controls
whether pyramids will be built for the layer. Set to ``True`` to build
the pyramids.

.. versionadded:: 3.20
%End

%Property( name = build, get = getBuild, set = setBuild )

};
/************************************************************************
Expand Down
45 changes: 23 additions & 22 deletions src/core/providers/gdal/qgsgdalprovider.cpp
Expand Up @@ -2038,16 +2038,16 @@ QString QgsGdalProvider::buildPyramids( const QList<QgsRasterPyramid> &rasterPyr
++myRasterPyramidIterator )
{
#ifdef QGISDEBUG
QgsDebugMsgLevel( QStringLiteral( "Build pyramids:: Level %1" ).arg( myRasterPyramidIterator->level ), 2 );
QgsDebugMsgLevel( QStringLiteral( "x:%1" ).arg( myRasterPyramidIterator->xDim ), 2 );
QgsDebugMsgLevel( QStringLiteral( "y:%1" ).arg( myRasterPyramidIterator->yDim ), 2 );
QgsDebugMsgLevel( QStringLiteral( "exists : %1" ).arg( myRasterPyramidIterator->exists ), 2 );
QgsDebugMsgLevel( QStringLiteral( "Build pyramids:: Level %1" ).arg( myRasterPyramidIterator->getLevel() ), 2 );
QgsDebugMsgLevel( QStringLiteral( "x:%1" ).arg( myRasterPyramidIterator->getXDim() ), 2 );
QgsDebugMsgLevel( QStringLiteral( "y:%1" ).arg( myRasterPyramidIterator->getYDim() ), 2 );
QgsDebugMsgLevel( QStringLiteral( "exists : %1" ).arg( myRasterPyramidIterator->getExists() ), 2 );
#endif
if ( myRasterPyramidIterator->build )
if ( myRasterPyramidIterator->getBuild() )
{
QgsDebugMsgLevel( QStringLiteral( "adding overview at level %1 to list"
).arg( myRasterPyramidIterator->level ), 2 );
myOverviewLevelsVector.append( myRasterPyramidIterator->level );
).arg( myRasterPyramidIterator->getLevel() ), 2 );
myOverviewLevelsVector.append( myRasterPyramidIterator->getLevel() );
}
}
/* From : http://www.gdal.org/classGDALDataset.html#a2aa6f88b3bbc840a5696236af11dde15
Expand Down Expand Up @@ -2226,8 +2226,9 @@ QList<QgsRasterPyramid> QgsGdalProvider::buildPyramidList()
}
#endif

QList<QgsRasterPyramid> QgsGdalProvider::buildPyramidList( QList<int> overviewList )
QList<QgsRasterPyramid> QgsGdalProvider::buildPyramidList( const QList<int> &list )
{
QList< int > overviewList = list;
QMutexLocker locker( mpMutex );

int myWidth = mWidth;
Expand Down Expand Up @@ -2260,12 +2261,12 @@ QList<QgsRasterPyramid> QgsGdalProvider::buildPyramidList( QList<int> overviewLi
//

QgsRasterPyramid myRasterPyramid;
myRasterPyramid.level = myDivisor;
myRasterPyramid.xDim = ( int )( 0.5 + ( myWidth / static_cast<double>( myDivisor ) ) ); // NOLINT
myRasterPyramid.yDim = ( int )( 0.5 + ( myHeight / static_cast<double>( myDivisor ) ) ); // NOLINT
myRasterPyramid.exists = false;
myRasterPyramid.setLevel( myDivisor );
myRasterPyramid.setXDim( ( int )( 0.5 + ( myWidth / static_cast<double>( myDivisor ) ) ) ); // NOLINT
myRasterPyramid.setYDim( ( int )( 0.5 + ( myHeight / static_cast<double>( myDivisor ) ) ) ); // NOLINT
myRasterPyramid.setExists( false );

QgsDebugMsgLevel( QStringLiteral( "Pyramid %1 xDim %2 yDim %3" ).arg( myRasterPyramid.level ).arg( myRasterPyramid.xDim ).arg( myRasterPyramid.yDim ), 2 );
QgsDebugMsgLevel( QStringLiteral( "Pyramid %1 xDim %2 yDim %3" ).arg( myRasterPyramid.getLevel() ).arg( myRasterPyramid.getXDim() ).arg( myRasterPyramid.getYDim() ), 2 );

//
// Now we check if it actually exists in the raster layer
Expand All @@ -2288,20 +2289,20 @@ QList<QgsRasterPyramid> QgsGdalProvider::buildPyramidList( QList<int> overviewLi
// here is where we check if its a near match:
// we will see if its within 5 cells either side of
//
QgsDebugMsgLevel( "Checking whether " + QString::number( myRasterPyramid.xDim ) + " x " +
QString::number( myRasterPyramid.yDim ) + " matches " +
QgsDebugMsgLevel( "Checking whether " + QString::number( myRasterPyramid.getXDim() ) + " x " +
QString::number( myRasterPyramid.getYDim() ) + " matches " +
QString::number( myOverviewXDim ) + " x " + QString::number( myOverviewYDim ), 2 );


if ( ( myOverviewXDim <= ( myRasterPyramid.xDim + myNearMatchLimit ) ) &&
( myOverviewXDim >= ( myRasterPyramid.xDim - myNearMatchLimit ) ) &&
( myOverviewYDim <= ( myRasterPyramid.yDim + myNearMatchLimit ) ) &&
( myOverviewYDim >= ( myRasterPyramid.yDim - myNearMatchLimit ) ) )
if ( ( myOverviewXDim <= ( myRasterPyramid.getXDim() + myNearMatchLimit ) ) &&
( myOverviewXDim >= ( myRasterPyramid.getXDim() - myNearMatchLimit ) ) &&
( myOverviewYDim <= ( myRasterPyramid.getYDim() + myNearMatchLimit ) ) &&
( myOverviewYDim >= ( myRasterPyramid.getYDim() - myNearMatchLimit ) ) )
{
//right we have a match so adjust the a / y before they get added to the list
myRasterPyramid.xDim = myOverviewXDim;
myRasterPyramid.yDim = myOverviewYDim;
myRasterPyramid.exists = true;
myRasterPyramid.setXDim( myOverviewXDim );
myRasterPyramid.setYDim( myOverviewYDim );
myRasterPyramid.setExists( true );
QgsDebugMsgLevel( QStringLiteral( ".....YES!" ), 2 );
}
else
Expand Down
2 changes: 1 addition & 1 deletion src/core/providers/gdal/qgsgdalprovider.h
Expand Up @@ -189,7 +189,7 @@ class QgsGdalProvider final: public QgsRasterDataProvider, QgsGdalProviderBase
QgsRaster::RasterPyramidsFormat format = QgsRaster::PyramidsGTiff,
const QStringList &createOptions = QStringList(),
QgsRasterBlockFeedback *feedback = nullptr ) override;
QList<QgsRasterPyramid> buildPyramidList( QList<int> overviewList = QList<int>() ) override;
QList<QgsRasterPyramid> buildPyramidList( const QList<int> &overviewList = QList<int>() ) override;

static QMap<QString, QString> supportedMimes();

Expand Down
18 changes: 2 additions & 16 deletions src/core/raster/qgsrasterdataprovider.cpp
Expand Up @@ -374,22 +374,8 @@ QList<QPair<QString, QString> > QgsRasterDataProvider::pyramidResamplingMethods(

bool QgsRasterDataProvider::hasPyramids()
{
QList<QgsRasterPyramid> myPyramidList = buildPyramidList();

if ( myPyramidList.isEmpty() )
return false;

QList<QgsRasterPyramid>::iterator myRasterPyramidIterator;
for ( myRasterPyramidIterator = myPyramidList.begin();
myRasterPyramidIterator != myPyramidList.end();
++myRasterPyramidIterator )
{
if ( myRasterPyramidIterator->exists )
{
return true;
}
}
return false;
const QList<QgsRasterPyramid> pyramidList = buildPyramidList();
return std::any_of( pyramidList.constBegin(), pyramidList.constEnd(), []( QgsRasterPyramid pyramid ) { return pyramid.getExists(); } );
}

void QgsRasterDataProvider::setUserNoDataValue( int bandNo, const QgsRasterRangeList &noData )
Expand Down
47 changes: 40 additions & 7 deletions src/core/raster/qgsrasterdataprovider.h
Expand Up @@ -309,7 +309,22 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
return nullptr;
}

//! \brief Create pyramid overviews
/**
* Creates pyramid overviews.
*
* \param pyramidList a list of QgsRasterPryamids to create overviews for. The QgsRasterPryamid::setBuild() flag
* should be set to TRUE for every layer where pyramids are desired.
* \param resamplingMethod resampling method to use when creating the pyramids. The pyramidResamplingMethods() method
* can be used to retrieve a list of valid resampling methods available for specific raster data providers.
* \param format raster pyramid format.
* \param configOptions optional configuration options which are passed to the specific data provider
* for use during pyramid creation.
* \param feedback optional feedback argument for progress reports and cancelation support.
*
* \see buildPyramidList()
* \see hasPyramids()
* \see pyramidResamplingMethods()
*/
virtual QString buildPyramids( const QList<QgsRasterPyramid> &pyramidList,
const QString &resamplingMethod = "NEAREST",
QgsRaster::RasterPyramidsFormat format = QgsRaster::PyramidsGTiff,
Expand All @@ -326,16 +341,34 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast

/**
* Returns the raster layers pyramid list.
* \param overviewList used to construct the pyramid list (optional), when empty the list is defined by the provider.
* A pyramid list defines the
* POTENTIAL pyramids that can be in a raster. To know which of the pyramid layers
* ACTUALLY exists you need to look at the existsFlag member in each struct stored in the
*
* This method returns a list of pyramid layers which are valid for the data provider. The returned list
* is a complete list of all possible layers, and includes both pyramids layers which currently exist and
* layers which have not yet been constructed. To know which of the pyramid layers
* ACTUALLY exists you need to look at the QgsRasterPyramid::getExists() member for each value in the
* list.
*
* The returned list is suitable for passing to the buildPyramids() method. First, modify the returned list
* by calling `QgsRasterPryamid::setBuild( TRUE )` for every layer you want to create pyramids for, and then
* pass the modified list to buildPryamids().
*
* \param overviewList used to construct the pyramid list (optional), when empty the list is defined by the provider.
*
* \see buildPyramids()
* \see hasPyramids()
*/
virtual QList<QgsRasterPyramid> buildPyramidList( QList<int> overviewList = QList<int>() ) // clazy:exclude=function-args-by-ref
virtual QList<QgsRasterPyramid> buildPyramidList( const QList<int> &overviewList = QList<int>() )
{ Q_UNUSED( overviewList ) return QList<QgsRasterPyramid>(); }

//! \brief Returns TRUE if raster has at least one populated histogram.
/**
* Returns TRUE if raster has at least one existing pyramid.
*
* The buildPyramidList() method can be used to retrieve additional details about potential and existing
* pryamid layers.
*
* \see buildPyramidList()
* \see buildPyramids()
*/
bool hasPyramids();

/**
Expand Down
2 changes: 1 addition & 1 deletion src/core/raster/qgsrasterfilewriter.cpp
Expand Up @@ -818,7 +818,7 @@ void QgsRasterFileWriter::buildPyramids( const QString &filename, QgsRasterDataP
myPyramidList = destProvider->buildPyramidList( mPyramidsList );
for ( int myCounterInt = 0; myCounterInt < myPyramidList.count(); myCounterInt++ )
{
myPyramidList[myCounterInt].build = true;
myPyramidList[myCounterInt].setBuild( true );
}

QgsDebugMsgLevel( QStringLiteral( "building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ), 4 );
Expand Down

0 comments on commit c4c8aab

Please sign in to comment.