Skip to content

Commit

Permalink
Revert usage of QGIS native API in KDE as it causes issues.
Browse files Browse the repository at this point in the history
This reverts commits 3e63d65
and 8255932.
  • Loading branch information
alexbruy committed Apr 13, 2020
1 parent a84ec12 commit de22b6a
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 37 deletions.
105 changes: 71 additions & 34 deletions src/analysis/raster/qgskde.cpp
Expand Up @@ -13,13 +13,10 @@
* *
***************************************************************************/

#include <QByteArray>

#include "qgskde.h"
#include "qgsfeaturesource.h"
#include "qgsfeatureiterator.h"
#include "qgsgeometry.h"
#include "qgsrasterfilewriter.h"

#define NO_DATA -9999

Expand All @@ -35,6 +32,8 @@ QgsKernelDensityEstimation::QgsKernelDensityEstimation( const QgsKernelDensityEs
, mDecay( parameters.decayRatio )
, mOutputValues( parameters.outputValues )
, mBufferSize( -1 )
, mDatasetH( nullptr )
, mRasterBandH( nullptr )
{
if ( !parameters.radiusField.isEmpty() )
mRadiusField = mSource->fields().lookupField( parameters.radiusField );
Expand Down Expand Up @@ -69,6 +68,14 @@ QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::run()

QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::prepare()
{
GDALAllRegister();

GDALDriverH driver = GDALGetDriverByName( mOutputFormat.toUtf8() );
if ( !driver )
{
return DriverError;
}

if ( !mSource )
return InvalidParameters;

Expand All @@ -79,28 +86,16 @@ QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::prepare()
int rows = std::max( std::ceil( mBounds.height() / mPixelSize ) + 1, 1.0 );
int cols = std::max( std::ceil( mBounds.width() / mPixelSize ) + 1, 1.0 );

// create empty raster and fill it with nodata values
QgsRasterFileWriter writer( mOutputFile );
writer.setOutputProviderKey( QStringLiteral( "gdal" ) );
writer.setOutputFormat( mOutputFormat );
mProvider = writer.createOneBandRaster( Qgis::Float32, cols, rows, mBounds, mSource->sourceCrs() );
if ( !mProvider )
{
if ( !createEmptyLayer( driver, mBounds, rows, cols ) )
return FileCreationError;
}

if ( !mProvider->setNoDataValue( 1, NO_DATA ) )
{
// open the raster in GA_Update mode
mDatasetH.reset( GDALOpen( mOutputFile.toUtf8().constData(), GA_Update ) );
if ( !mDatasetH )
return FileCreationError;
mRasterBandH = GDALGetRasterBand( mDatasetH.get(), 1 );
if ( !mRasterBandH )
return FileCreationError;
}

QgsRasterBlock block( Qgis::Float32, cols, 1 );
block.setNoDataValue( NO_DATA );
block.setIsNoData();
for ( int i = 0; i < rows ; i++ )
{
mProvider->writeBlock( &block, 1, 0, i );
}

mBufferSize = -1;
if ( mRadiusField < 0 )
Expand Down Expand Up @@ -167,13 +162,13 @@ QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::addFeature( const
unsigned int yPositionIO = ( ( mBounds.yMaximum() - ( *pointIt ).y() ) / mPixelSize ) - buffer;


// calculate buffer around pixel
QgsRectangle extent( ( *pointIt ).x() - radius, ( *pointIt ).y() - radius, ( *pointIt ).x() + radius, ( *pointIt ).y() + radius );

// get the data
std::unique_ptr< QgsRasterBlock > block( mProvider->block( 1, extent, blockSize, blockSize ) );
QByteArray blockData = block->data();
float *dataBuffer = ( float * ) blockData.data();
float *dataBuffer = ( float * ) CPLMalloc( sizeof( float ) * blockSize * blockSize );
if ( GDALRasterIO( mRasterBandH, GF_Read, xPosition, yPositionIO, blockSize, blockSize,
dataBuffer, blockSize, blockSize, GDT_Float32, 0, 0 ) != CE_None )
{
result = RasterIoError;
}

for ( int xp = 0; xp < blockSize; xp++ )
{
Expand All @@ -199,22 +194,21 @@ QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::addFeature( const
dataBuffer[ pos ] += pixelValue;
}
}

block->setData( blockData );
if ( !mProvider->writeBlock( block.get(), 1, xPosition, yPositionIO ) )
if ( GDALRasterIO( mRasterBandH, GF_Write, xPosition, yPositionIO, blockSize, blockSize,
dataBuffer, blockSize, blockSize, GDT_Float32, 0, 0 ) != CE_None )
{
result = RasterIoError;
}
CPLFree( dataBuffer );
}

return result;
}

QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::finalise()
{
mProvider->setEditable( false );
mProvider = nullptr;

mDatasetH.reset();
mRasterBandH = nullptr;
return Success;
}

Expand All @@ -228,6 +222,45 @@ int QgsKernelDensityEstimation::radiusSizeInPixels( double radius ) const
return buffer;
}

bool QgsKernelDensityEstimation::createEmptyLayer( GDALDriverH driver, const QgsRectangle &bounds, int rows, int columns ) const
{
double geoTransform[6] = { bounds.xMinimum(), mPixelSize, 0, bounds.yMaximum(), 0, -mPixelSize };
gdal::dataset_unique_ptr emptyDataset( GDALCreate( driver, mOutputFile.toUtf8(), columns, rows, 1, GDT_Float32, nullptr ) );
if ( !emptyDataset )
return false;

if ( GDALSetGeoTransform( emptyDataset.get(), geoTransform ) != CE_None )
return false;

// Set the projection on the raster destination to match the input layer
if ( GDALSetProjection( emptyDataset.get(), mSource->sourceCrs().toWkt().toLocal8Bit().data() ) != CE_None )
return false;

GDALRasterBandH poBand = GDALGetRasterBand( emptyDataset.get(), 1 );
if ( !poBand )
return false;

if ( GDALSetRasterNoDataValue( poBand, NO_DATA ) != CE_None )
return false;

float *line = static_cast< float * >( CPLMalloc( sizeof( float ) * columns ) );
for ( int i = 0; i < columns; i++ )
{
line[i] = NO_DATA;
}
// Write the empty raster
for ( int i = 0; i < rows ; i++ )
{
if ( GDALRasterIO( poBand, GF_Write, 0, i, columns, 1, line, columns, 1, GDT_Float32, 0, 0 ) != CE_None )
{
return false;
}
}

CPLFree( line );
return true;
}

double QgsKernelDensityEstimation::calculateKernelValue( const double distance, const double bandwidth, const QgsKernelDensityEstimation::KernelShape shape, const QgsKernelDensityEstimation::OutputValues outputType ) const
{
switch ( shape )
Expand Down Expand Up @@ -384,3 +417,7 @@ QgsRectangle QgsKernelDensityEstimation::calculateBounds() const
bbox.setYMaximum( bbox.yMaximum() + radius );
return bbox;
}




12 changes: 9 additions & 3 deletions src/analysis/raster/qgskde.h
Expand Up @@ -16,12 +16,15 @@
#ifndef QGSKDE_H
#define QGSKDE_H

#include "qgsrectangle.h"
#include "qgsogrutils.h"
#include <QString>

// GDAL includes
#include <gdal.h>
#include <cpl_string.h>
#include <cpl_conv.h>
#include "qgis_analysis.h"
#include "qgsrectangle.h"
#include "qgsrasterdataprovider.h"

class QgsFeatureSource;
class QgsFeature;
Expand Down Expand Up @@ -165,8 +168,11 @@ class ANALYSIS_EXPORT QgsKernelDensityEstimation

int mBufferSize;

QgsRasterDataProvider *mProvider = nullptr;
gdal::dataset_unique_ptr mDatasetH;
GDALRasterBandH mRasterBandH;

//! Creates a new raster layer and initializes it to the no data value
bool createEmptyLayer( GDALDriverH driver, const QgsRectangle &bounds, int rows, int columns ) const;
int radiusSizeInPixels( double radius ) const;

#ifdef SIP_RUN
Expand Down

0 comments on commit de22b6a

Please sign in to comment.