Skip to content

Commit

Permalink
[processing] share code between zonal histrogram and zonal stats algs
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Jun 8, 2018
1 parent 827eee9 commit 918ac69
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 316 deletions.
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ SET(QGIS_ANALYSIS_SRCS


processing/qgsnativealgorithms.cpp processing/qgsnativealgorithms.cpp
processing/qgsoverlayutils.cpp processing/qgsoverlayutils.cpp
processing/qgsrasteranalysisutils.cpp


raster/qgsalignraster.cpp raster/qgsalignraster.cpp
raster/qgsninecellfilter.cpp raster/qgsninecellfilter.cpp
Expand Down
146 changes: 20 additions & 126 deletions src/analysis/processing/qgsalgorithmzonalhistogram.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
***************************************************************************/ ***************************************************************************/


#include "qgsalgorithmzonalhistogram.h" #include "qgsalgorithmzonalhistogram.h"
#include "qgsgeos.h" #include "qgsrasteranalysisutils.h"
#include "qgslogger.h" #include "qgslogger.h"


///@cond PRIVATE ///@cond PRIVATE
Expand Down Expand Up @@ -74,20 +74,19 @@ QgsZonalHistogramAlgorithm *QgsZonalHistogramAlgorithm::createInstance() const
bool QgsZonalHistogramAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * ) bool QgsZonalHistogramAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{ {
QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_RASTER" ), context ); QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_RASTER" ), context );
mBand = parameterAsInt( parameters, QStringLiteral( "RASTER_BAND" ), context );

if ( !layer ) if ( !layer )
throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT_RASTER" ) ) ); throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT_RASTER" ) ) );


mInterface.reset( layer->dataProvider()->clone() ); mRasterBand = parameterAsInt( parameters, QStringLiteral( "RASTER_BAND" ), context );
mHasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( mBand ); mHasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( mRasterBand );
mNodataValue = layer->dataProvider()->sourceNoDataValue( mBand ); mNodataValue = layer->dataProvider()->sourceNoDataValue( mRasterBand );
mExtent = layer->extent(); mRasterInterface.reset( layer->dataProvider()->clone() );
mRasterExtent = layer->extent();
mCrs = layer->crs(); mCrs = layer->crs();
mRasterUnitsPerPixelX = std::abs( layer->rasterUnitsPerPixelX() ); mCellSizeX = std::abs( layer->rasterUnitsPerPixelX() );
mRasterUnitsPerPixelY = std::abs( layer->rasterUnitsPerPixelX() ); mCellSizeY = std::abs( layer->rasterUnitsPerPixelX() );
mNbCellsXProvider = mInterface->xSize(); mNbCellsXProvider = mRasterInterface->xSize();
mNbCellsYProvider = mInterface->ySize(); mNbCellsYProvider = mRasterInterface->ySize();


return true; return true;
} }
Expand Down Expand Up @@ -130,41 +129,27 @@ QVariantMap QgsZonalHistogramAlgorithm::processAlgorithm( const QVariantMap &par
} }


QgsGeometry featureGeometry = f.geometry(); QgsGeometry featureGeometry = f.geometry();
QgsRectangle intersectRect = featureGeometry.boundingBox().intersect( &mExtent ); QgsRectangle featureRect = featureGeometry.boundingBox().intersect( &mRasterExtent );
if ( intersectRect.isEmpty() ) if ( featureRect.isEmpty() )
{ {
current++; current++;
continue; continue;
} }


int offsetX, offsetY, nCellsX, nCellsY; int nCellsX, nCellsY;
// Get offset in pixels in x- and y- direction QgsRectangle rasterBlockExtent;
offsetX = ( int )( ( intersectRect.xMinimum() - mExtent.xMinimum() ) / mRasterUnitsPerPixelX ); QgsRasterAnalysisUtils::cellInfoForBBox( mRasterExtent, featureRect, mCellSizeX, mCellSizeY, nCellsX, nCellsY, mNbCellsXProvider, mNbCellsYProvider, rasterBlockExtent );
offsetY = ( int )( ( mExtent.yMaximum() - intersectRect.yMaximum() ) / mRasterUnitsPerPixelY );

int maxColumn = ( int )( ( intersectRect.xMaximum() - mExtent.xMinimum() ) / mRasterUnitsPerPixelX ) + 1;
int maxRow = ( int )( ( mExtent.yMaximum() - intersectRect.yMinimum() ) / mRasterUnitsPerPixelY ) + 1;

nCellsX = maxColumn - offsetX;
nCellsY = maxRow - offsetY;

// Avoid access to cells outside of the raster (may occur because of rounding)
if ( ( offsetX + nCellsX ) > mNbCellsXProvider )
{
nCellsX = mNbCellsXProvider - offsetX;
}
if ( ( offsetY + nCellsY ) > mNbCellsYProvider )
{
nCellsY = mNbCellsYProvider - offsetY;
}


QHash< double, qgssize > fUniqueValues; QHash< double, qgssize > fUniqueValues;
middlePoints( featureGeometry, offsetX, offsetY, nCellsX, nCellsY, fUniqueValues ); QgsRasterAnalysisUtils::statisticsFromMiddlePointTest( mRasterInterface.get(), mRasterBand, featureGeometry, nCellsX, nCellsY, mCellSizeX, mCellSizeY,
rasterBlockExtent, [ &fUniqueValues]( double value ) { fUniqueValues[value]++; }, false );


if ( fUniqueValues.count() < 1 ) if ( fUniqueValues.count() < 1 )
{ {
// The cell resolution is probably larger than the polygon area. We switch to slower precise pixel - polygon intersection in this case // The cell resolution is probably larger than the polygon area. We switch to slower precise pixel - polygon intersection in this case
preciseIntersection( featureGeometry, offsetX, offsetY, nCellsX, nCellsY, fUniqueValues ); // TODO: eventually deal with weight if needed
QgsRasterAnalysisUtils::statisticsFromPreciseIntersection( mRasterInterface.get(), mRasterBand, featureGeometry, nCellsX, nCellsY, mCellSizeX, mCellSizeY,
rasterBlockExtent, [ &fUniqueValues]( double value, double ) { fUniqueValues[value]++; }, false );
} }


for ( auto it = fUniqueValues.constBegin(); it != fUniqueValues.constEnd(); ++it ) for ( auto it = fUniqueValues.constBegin(); it != fUniqueValues.constEnd(); ++it )
Expand Down Expand Up @@ -217,97 +202,6 @@ QVariantMap QgsZonalHistogramAlgorithm::processAlgorithm( const QVariantMap &par
return outputs; return outputs;
} }


void QgsZonalHistogramAlgorithm::middlePoints( const QgsGeometry &poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, QHash< double, qgssize > &uniqueValues )
{
double cellCenterX, cellCenterY;

cellCenterY = mExtent.yMaximum() - pixelOffsetY * mRasterUnitsPerPixelY - mRasterUnitsPerPixelY / 2;

geos::unique_ptr polyGeos( QgsGeos::asGeos( poly ) );
if ( !polyGeos )
{
return;
}

GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
geos::prepared_unique_ptr polyGeosPrepared( GEOSPrepare_r( geosctxt, polyGeos.get() ) );
if ( !polyGeosPrepared )
{
return;
}

GEOSCoordSequence *cellCenterCoords = nullptr;
geos::unique_ptr currentCellCenter;

QgsRectangle blockRect( mExtent.xMinimum() + pixelOffsetX * mRasterUnitsPerPixelX,
mExtent.yMaximum() - pixelOffsetY * mRasterUnitsPerPixelY - nCellsY * mRasterUnitsPerPixelY,
mExtent.xMinimum() + pixelOffsetX * mRasterUnitsPerPixelX + nCellsX * mRasterUnitsPerPixelX,
mExtent.yMaximum() - pixelOffsetY * mRasterUnitsPerPixelY );
std::unique_ptr< QgsRasterBlock > block( mInterface->block( mBand, blockRect, nCellsX, nCellsY ) );
for ( int i = 0; i < nCellsY; ++i )
{
cellCenterX = mExtent.xMinimum() + pixelOffsetX * mRasterUnitsPerPixelX + mRasterUnitsPerPixelX / 2;
for ( int j = 0; j < nCellsX; ++j )
{
if ( !std::isnan( block->value( i, j ) ) )
{
cellCenterCoords = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
GEOSCoordSeq_setX_r( geosctxt, cellCenterCoords, 0, cellCenterX );
GEOSCoordSeq_setY_r( geosctxt, cellCenterCoords, 0, cellCenterY );
currentCellCenter.reset( GEOSGeom_createPoint_r( geosctxt, cellCenterCoords ) );
if ( GEOSPreparedContains_r( geosctxt, polyGeosPrepared.get(), currentCellCenter.get() ) )
{
uniqueValues[block->value( i, j )]++;
}
}
cellCenterX += mRasterUnitsPerPixelX;
}
cellCenterY -= mRasterUnitsPerPixelY;
}
}

void QgsZonalHistogramAlgorithm::preciseIntersection( const QgsGeometry &poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, QHash< double, qgssize > &uniqueValues )
{
double currentY = mExtent.yMaximum() - pixelOffsetY * mRasterUnitsPerPixelY - mRasterUnitsPerPixelY / 2;
QgsGeometry pixelRectGeometry;

double hCellSizeX = mRasterUnitsPerPixelX / 2.0;
double hCellSizeY = mRasterUnitsPerPixelY / 2.0;

QgsRectangle blockRect( mExtent.xMinimum() + pixelOffsetX * mRasterUnitsPerPixelX,
mExtent.yMaximum() - pixelOffsetY * mRasterUnitsPerPixelY - nCellsY * mRasterUnitsPerPixelY,
mExtent.xMinimum() + pixelOffsetX * mRasterUnitsPerPixelX + nCellsX * mRasterUnitsPerPixelX,
mExtent.yMaximum() - pixelOffsetY * mRasterUnitsPerPixelY );
std::unique_ptr< QgsRasterBlock > block( mInterface->block( mBand, blockRect, nCellsX, nCellsY ) );
for ( int i = 0; i < nCellsY; ++i )
{
double currentX = mExtent.xMinimum() + mRasterUnitsPerPixelX / 2.0 + pixelOffsetX * mRasterUnitsPerPixelX;
for ( int j = 0; j < nCellsX; ++j )
{
if ( !std::isnan( block->value( i, j ) ) )
{
pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );
if ( !pixelRectGeometry.isNull() )
{
//intersection
QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly );
if ( !intersectGeometry.isNull() )
{
double intersectionArea = intersectGeometry.area();
if ( intersectionArea >= 0.0 )
{
uniqueValues[block->value( i, j )]++;
}
}
pixelRectGeometry = QgsGeometry();
}
}
currentX += mRasterUnitsPerPixelX;
}
currentY -= mRasterUnitsPerPixelY;
}
}

///@endcond ///@endcond




Expand Down
16 changes: 5 additions & 11 deletions src/analysis/processing/qgsalgorithmzonalhistogram.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -49,22 +49,16 @@ class QgsZonalHistogramAlgorithm : public QgsProcessingAlgorithm
QVariantMap processAlgorithm( const QVariantMap &parameters, QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;


//! Fetch unique values by considering the pixels where the center point is within the polygon (fast)
void middlePoints( const QgsGeometry &poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, QHash< double, qgssize > &uniqueValues );

//! Fetch unique values with precise pixel - polygon intersection test (slow)
void preciseIntersection( const QgsGeometry &poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, QHash< double, qgssize > &uniqueValues );

private: private:


std::unique_ptr< QgsRasterInterface > mInterface; std::unique_ptr< QgsRasterInterface > mRasterInterface;
int mBand; int mRasterBand;
bool mHasNoDataValue = false; bool mHasNoDataValue = false;
float mNodataValue = -1; float mNodataValue = -1;
QgsRectangle mExtent; QgsRectangle mRasterExtent;
QgsCoordinateReferenceSystem mCrs; QgsCoordinateReferenceSystem mCrs;
double mRasterUnitsPerPixelX; double mCellSizeX;
double mRasterUnitsPerPixelY; double mCellSizeY;
double mNbCellsXProvider; double mNbCellsXProvider;
double mNbCellsYProvider; double mNbCellsYProvider;


Expand Down
Loading

0 comments on commit 918ac69

Please sign in to comment.