Skip to content

Commit

Permalink
Merge pull request #471 from nyalldawson/heatmap_kernels
Browse files Browse the repository at this point in the history
[FEATURE] Add additional kernels to the Heatmap plugin...
  • Loading branch information
NathanW2 committed Mar 23, 2013
2 parents a006d99 + 7423e9a commit 2a9d016
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 24 deletions.
85 changes: 83 additions & 2 deletions src/plugins/heatmap/heatmap.cpp
Expand Up @@ -43,6 +43,9 @@

#define NO_DATA -9999

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

static const QString sName = QObject::tr( "Heatmap" );
static const QString sDescription = QObject::tr( "Creates a Heatmap raster for the input point vector" );
Expand Down Expand Up @@ -108,7 +111,8 @@ void Heatmap::run()
int columns = d.columns();
int rows = d.rows();
double cellsize = d.cellSizeX(); // or d.cellSizeY(); both have the same value
double myDecay = d.decayRatio();
mDecay = d.decayRatio();
int kernelShape = d.kernelShape();

// Start working on the input vector
QgsVectorLayer* inputLayer = d.inputVectorLayer();
Expand Down Expand Up @@ -259,7 +263,7 @@ void Heatmap::run()
continue;
}

double pixelValue = weight * ( 1 - (( 1 - myDecay ) * distance / myBuffer ) );
double pixelValue = weight * calculateKernelValue( distance, myBuffer, kernelShape );

// clearing anamolies along the axes
if ( xp == 0 && yp == 0 )
Expand Down Expand Up @@ -330,6 +334,83 @@ int Heatmap::bufferSize( double radius, double cellsize )
return buffer;
}

double Heatmap::calculateKernelValue( double distance, int bandwidth, int kernelShape )
{
switch ( kernelShape )
{
case Heatmap::Triangular:
return triangularKernel( distance , bandwidth );

case Heatmap::Uniform:
return uniformKernel( distance, bandwidth );

case Heatmap::Quartic:
return quarticKernel( distance, bandwidth );

case Heatmap::Triweight:
return triweightKernel( distance, bandwidth );

case Heatmap::Epanechnikov:
return epanechnikovKernel( distance, bandwidth );
}
return 0;

}

/* The kernel functions below are taken from "Kernel Smoothing" by Wand and Jones (1995), p. 175
*
* Each kernel is multiplied by a normalizing constant "k", which normalizes the kernel area
* to 1 for a given bandwidth size.
*
* k is calculated by polar double integration of the kernel function
* between a radius of 0 to the specified bandwidth and equating the area to 1. */

double Heatmap::uniformKernel( double distance, int bandwidth )
{
// Normalizing constant
double k = 2. / ( M_PI * ( double )bandwidth );

// Derived from Wand and Jones (1995), p. 175
return k * ( 0.5 / ( double )bandwidth );
}

double Heatmap::quarticKernel( double distance, int bandwidth )
{
// Normalizing constant
double k = 16. / ( 5. * M_PI * pow(( double )bandwidth, 2 ) );

// Derived from Wand and Jones (1995), p. 175
return k * ( 15. / 16. ) * pow( 1. - pow( distance / ( double )bandwidth, 2 ), 2 );
}

double Heatmap::triweightKernel( double distance, int bandwidth )
{
// Normalizing constant
double k = 128. / ( 35. * M_PI * pow(( double )bandwidth, 2 ) );

// Derived from Wand and Jones (1995), p. 175
return k * ( 35. / 32. ) * pow( 1. - pow( distance / ( double )bandwidth, 2 ), 3 );
}

double Heatmap::epanechnikovKernel( double distance, int bandwidth )
{
// Normalizing constant
double k = 8. / ( 3. * M_PI * pow(( double )bandwidth, 2 ) );

// Derived from Wand and Jones (1995), p. 175
return k * ( 3. / 4. ) * ( 1. - pow( distance / ( double )bandwidth, 2 ) );
}

double Heatmap::triangularKernel( double distance, int bandwidth )
{
// Normalizing constant. In this case it's calculated a little different
// due to the inclusion of the non-standard "decay" parameter

double k = 3. / (( 1. + 2. * mDecay ) * M_PI * pow(( double )bandwidth, 2 ) );

// Derived from Wand and Jones (1995), p. 175 (with addition of decay parameter)
return k * ( 1. - ( 1. - mDecay ) * ( distance / ( double )bandwidth ) );
}

// Unload the plugin by cleaning up the GUI
void Heatmap::unload()
Expand Down
26 changes: 25 additions & 1 deletion src/plugins/heatmap/heatmap.h
Expand Up @@ -69,6 +69,16 @@ class Heatmap: public QObject, public QgisPlugin
Heatmap( QgisInterface * theInterface );
//! Destructor
virtual ~Heatmap();

// Kernel shape type
enum kernelShape
{
Quartic,
Triangular,
Uniform,
Triweight,
Epanechnikov
};

public slots:
//! init the gui
Expand All @@ -81,11 +91,25 @@ class Heatmap: public QObject, public QgisPlugin
void help();

private:
double mDecay;

//! Worker to convert meters to map units
double mapUnitsOf( double meters, QgsCoordinateReferenceSystem layerCrs );
//! Worker to calculate buffer size in pixels
int bufferSize( double radius, double cellsize );

//! Calculate the value given to a point width a given distance for a specified kernel shape
double calculateKernelValue( double distance, int bandwidth, int kernelShape );
//! Uniform kernel function
double uniformKernel( double distance, int bandwidth );
//! Quartic kernel function
double quarticKernel( double distance, int bandwidth );
//! Triweight kernel function
double triweightKernel( double distance, int bandwidth );
//! Epanechnikov kernel function
double epanechnikovKernel( double distance, int bandwidth );
//! Triangular kernel function
double triangularKernel( double distance, int bandwidth );

// MANDATORY PLUGIN PROPERTY DECLARATIONS .....

int mPluginType;
Expand Down
14 changes: 14 additions & 0 deletions src/plugins/heatmap/heatmapgui.cpp
Expand Up @@ -12,6 +12,7 @@
// qgis includes
#include "qgis.h"
#include "heatmapgui.h"
#include "heatmap.h"
#include "qgscontexthelp.h"
#include "qgsmaplayer.h"
#include "qgsmaplayerregistry.h"
Expand Down Expand Up @@ -225,6 +226,14 @@ void HeatmapGui::on_mBufferLineEdit_editingFinished()
{
updateBBox();
}

void HeatmapGui::on_kernelShapeCombo_currentIndexChanged( int index )
{
Q_UNUSED( index );
// Only enable the decay edit if the kernel shape is set to triangular
mDecayLineEdit->setEnabled( index == Heatmap::Triangular );
}

/*
*
* Private Functions
Expand Down Expand Up @@ -375,6 +384,11 @@ int HeatmapGui::radiusUnit()
return mRadiusUnitCombo->currentIndex();
}

int HeatmapGui::kernelShape()
{
return kernelShapeCombo->currentIndex();
}

double HeatmapGui::decayRatio()
{
return mDecayLineEdit->text().toDouble();
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/heatmap/heatmapgui.h
Expand Up @@ -48,6 +48,9 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase
/** Return the radius Unit (meters/map units) */
int radiusUnit();

/** Return the selected kernel shape */
int kernelShape();

/** Return the decay ratio */
double decayRatio();

Expand Down Expand Up @@ -120,6 +123,7 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase
void on_mRadiusUnitCombo_currentIndexChanged( int index );
void on_mInputVectorCombo_currentIndexChanged( int index );
void on_mBufferLineEdit_editingFinished();
void on_kernelShapeCombo_currentIndexChanged( int index );
};

#endif
81 changes: 60 additions & 21 deletions src/plugins/heatmap/heatmapguibase.ui
Expand Up @@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>430</width>
<height>380</height>
<width>460</width>
<height>415</height>
</rect>
</property>
<property name="sizePolicy">
Expand Down Expand Up @@ -101,20 +101,6 @@
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="mDecayLabel">
<property name="text">
<string>Decay Ratio</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2">
<widget class="QLineEdit" name="mDecayLineEdit">
<property name="text">
<string>0.1</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="3">
<widget class="QGroupBox" name="advancedGroupBox">
<property name="sizePolicy">
Expand Down Expand Up @@ -194,21 +180,21 @@
</item>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<item row="1" column="0">
<widget class="QCheckBox" name="useRadius">
<property name="text">
<string>Use Radius from field</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QComboBox" name="radiusFieldCombo">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<item row="1" column="2">
<widget class="QComboBox" name="radiusFieldUnitCombo">
<property name="enabled">
<bool>false</bool>
Expand All @@ -225,20 +211,73 @@
</item>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QCheckBox" name="useWeight">
<property name="text">
<string>Use Weight from field</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="weightFieldCombo">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLineEdit" name="mDecayLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>0.0</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mDecayLabel">
<property name="text">
<string>Decay Ratio</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="kernelShapeCombo">
<item>
<property name="text">
<string>Quartic (Biweight)</string>
</property>
</item>
<item>
<property name="text">
<string>Triangular</string>
</property>
</item>
<item>
<property name="text">
<string>Uniform</string>
</property>
</item>
<item>
<property name="text">
<string>Triweight</string>
</property>
</item>
<item>
<property name="text">
<string>Epanechnikov</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="kernelShapeLable">
<property name="text">
<string>Kernel Shape</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
Expand Down

0 comments on commit 2a9d016

Please sign in to comment.