From 7bd89ce7ada9bf4c38ea4dacdc634b62f983d134 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 12 Mar 2013 13:14:09 +1100 Subject: [PATCH 1/5] Move decay radio widget into advanced group box, and set the default value to 0. This method of controlling the kernel shape is non-standard, and should be achieved instead by giving users a choice of standard kernel functions. --- src/plugins/heatmap/heatmapguibase.ui | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/plugins/heatmap/heatmapguibase.ui b/src/plugins/heatmap/heatmapguibase.ui index 216323c05a8c..62f1e98ba74d 100644 --- a/src/plugins/heatmap/heatmapguibase.ui +++ b/src/plugins/heatmap/heatmapguibase.ui @@ -6,8 +6,8 @@ 0 0 - 430 - 380 + 460 + 382 @@ -101,20 +101,6 @@ - - - - Decay Ratio - - - - - - - 0.1 - - - @@ -239,6 +225,20 @@ + + + + 0.0 + + + + + + + Decay Ratio + + + From a05747c40d0f992b7f6c7e0f3f7fd9546f812843 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 13 Mar 2013 07:18:24 +1100 Subject: [PATCH 2/5] [FEATURE] Add option to select kernel shape (quartic, triangular or uniform) in heatmap plugin --- src/plugins/heatmap/heatmap.cpp | 42 +++++++++++++++++++++++++-- src/plugins/heatmap/heatmap.h | 7 +++++ src/plugins/heatmap/heatmapgui.cpp | 5 ++++ src/plugins/heatmap/heatmapgui.h | 10 +++++++ src/plugins/heatmap/heatmapguibase.ui | 42 ++++++++++++++++++++++----- 5 files changed, 96 insertions(+), 10 deletions(-) diff --git a/src/plugins/heatmap/heatmap.cpp b/src/plugins/heatmap/heatmap.cpp index 247495f5c9af..d2f26173fa6e 100644 --- a/src/plugins/heatmap/heatmap.cpp +++ b/src/plugins/heatmap/heatmap.cpp @@ -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" ); @@ -109,7 +112,8 @@ void Heatmap::run() int rows = d.rows(); float cellsize = d.cellSizeX(); // or d.cellSizeY(); both have the same value float myDecay = d.decayRatio(); - + int kernelShape = d.kernelShape(); + // Start working on the input vector QgsVectorLayer* inputLayer = d.inputVectorLayer(); @@ -259,7 +263,7 @@ void Heatmap::run() continue; } - float pixelValue = weight * ( 1 - (( 1 - myDecay ) * distance / myBuffer ) ); + float pixelValue = weight * calculateKernelValue( distance, myBuffer, kernelShape ); // clearing anamolies along the axes if ( xp == 0 && yp == 0 ) @@ -330,6 +334,40 @@ int Heatmap::bufferSize( float radius, float cellsize ) return buffer; } +float Heatmap::calculateKernelValue( float distance, int bandwidth, int kernelShape ) +{ + switch (kernelShape) { + case HeatmapGui::Triangular: + return ( 1 - ( distance / bandwidth ) ); + + case HeatmapGui::Uniform: + return uniformKernel( distance, bandwidth ); + + case HeatmapGui::Quartic: + return quarticKernel( distance, bandwidth ); + } + return 0; + +} + +float Heatmap::uniformKernel( float distance, int bandwidth ) +{ + // Normalizing constant. Calculated by polar double integrating the kernel function + // with radius of 0 to bandwidth and equating area to 1. + float k = 2. / (M_PI * (float)bandwidth); + + // Derived from Wand and Jones (1995), p. 175 + return k * ( 0.5 / (float)bandwidth); +} + +float Heatmap::quarticKernel( float distance, int bandwidth ) +{ + // Normalizing constant + float k = 16. / (5. * M_PI * pow((float)bandwidth, 2)); + + // Derived from Wand and Jones (1995), p. 175 + return k * (15. / 16. ) * pow( 1. - pow( distance / (float)bandwidth, 2), 2); +} // Unload the plugin by cleaning up the GUI void Heatmap::unload() diff --git a/src/plugins/heatmap/heatmap.h b/src/plugins/heatmap/heatmap.h index 9b7128076a79..cc0e1680ddde 100644 --- a/src/plugins/heatmap/heatmap.h +++ b/src/plugins/heatmap/heatmap.h @@ -85,6 +85,13 @@ class Heatmap: public QObject, public QgisPlugin float mapUnitsOf( float meters, QgsCoordinateReferenceSystem crs ); //! Worker to calculate buffer size in pixels int bufferSize( float radius, float cellsize ); + //! Calculate the value given to a point width a given distance for a specified kernel shape + float calculateKernelValue( float distance, int bandwidth, int kernelShape ); + //! Uniform kernel function + float uniformKernel( float distance, int bandwidth ); + //! Quartic kernel function + float quarticKernel( float distance, int bandwidth ); + // MANDATORY PLUGIN PROPERTY DECLARATIONS ..... diff --git a/src/plugins/heatmap/heatmapgui.cpp b/src/plugins/heatmap/heatmapgui.cpp index 8d655f780ce3..ce6780ac1e4b 100644 --- a/src/plugins/heatmap/heatmapgui.cpp +++ b/src/plugins/heatmap/heatmapgui.cpp @@ -375,6 +375,11 @@ int HeatmapGui::radiusUnit() return mRadiusUnitCombo->currentIndex(); } +int HeatmapGui::kernelShape() +{ + return kernelShapeCombo->currentIndex(); +} + float HeatmapGui::decayRatio() { return mDecayLineEdit->text().toFloat(); diff --git a/src/plugins/heatmap/heatmapgui.h b/src/plugins/heatmap/heatmapgui.h index 96370137228e..ab3665e98b61 100644 --- a/src/plugins/heatmap/heatmapgui.h +++ b/src/plugins/heatmap/heatmapgui.h @@ -35,6 +35,13 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase Meters, MapUnits }; + + enum kernelShape + { + Triangular, + Uniform, + Quartic + }; /** Returns whether to apply weighted heat */ bool weighted(); @@ -47,6 +54,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 */ float decayRatio(); diff --git a/src/plugins/heatmap/heatmapguibase.ui b/src/plugins/heatmap/heatmapguibase.ui index 62f1e98ba74d..1a690b08b5f6 100644 --- a/src/plugins/heatmap/heatmapguibase.ui +++ b/src/plugins/heatmap/heatmapguibase.ui @@ -7,7 +7,7 @@ 0 0 460 - 382 + 415 @@ -180,21 +180,21 @@ - + Use Radius from field - + false - + false @@ -211,34 +211,60 @@ - + Use Weight from field - + false - + 0.0 - + Decay Ratio + + + + + Triangular + + + + + Uniform + + + + + Quartic + + + + + + + + Kernel Shape + + + From ec01afb544797f86ddd31ec76a87a58ee7921fa8 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 14 Mar 2013 07:30:14 +1100 Subject: [PATCH 3/5] Change float to double for calculations --- src/plugins/heatmap/heatmap.cpp | 14 +++++++------- src/plugins/heatmap/heatmap.h | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/plugins/heatmap/heatmap.cpp b/src/plugins/heatmap/heatmap.cpp index 1ebfcade7884..eb5326bf5fb9 100644 --- a/src/plugins/heatmap/heatmap.cpp +++ b/src/plugins/heatmap/heatmap.cpp @@ -334,7 +334,7 @@ int Heatmap::bufferSize( double radius, double cellsize ) return buffer; } -float Heatmap::calculateKernelValue( float distance, int bandwidth, int kernelShape ) +double Heatmap::calculateKernelValue( double distance, int bandwidth, int kernelShape ) { switch (kernelShape) { case HeatmapGui::Triangular: @@ -350,23 +350,23 @@ float Heatmap::calculateKernelValue( float distance, int bandwidth, int kernelSh } -float Heatmap::uniformKernel( float distance, int bandwidth ) +double Heatmap::uniformKernel( double distance, int bandwidth ) { // Normalizing constant. Calculated by polar double integrating the kernel function // with radius of 0 to bandwidth and equating area to 1. - float k = 2. / (M_PI * (float)bandwidth); + double k = 2. / (M_PI * (double)bandwidth); // Derived from Wand and Jones (1995), p. 175 - return k * ( 0.5 / (float)bandwidth); + return k * ( 0.5 / (double)bandwidth); } -float Heatmap::quarticKernel( float distance, int bandwidth ) +double Heatmap::quarticKernel( double distance, int bandwidth ) { // Normalizing constant - float k = 16. / (5. * M_PI * pow((float)bandwidth, 2)); + 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 / (float)bandwidth, 2), 2); + return k * (15. / 16. ) * pow( 1. - pow( distance / (double)bandwidth, 2), 2); } // Unload the plugin by cleaning up the GUI diff --git a/src/plugins/heatmap/heatmap.h b/src/plugins/heatmap/heatmap.h index a067d9e06f7e..5dba7db97baa 100644 --- a/src/plugins/heatmap/heatmap.h +++ b/src/plugins/heatmap/heatmap.h @@ -86,11 +86,11 @@ class Heatmap: public QObject, public QgisPlugin //! 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 - float calculateKernelValue( float distance, int bandwidth, int kernelShape ); + double calculateKernelValue( double distance, int bandwidth, int kernelShape ); //! Uniform kernel function - float uniformKernel( float distance, int bandwidth ); + double uniformKernel( double distance, int bandwidth ); //! Quartic kernel function - float quarticKernel( float distance, int bandwidth ); + double quarticKernel( double distance, int bandwidth ); // MANDATORY PLUGIN PROPERTY DECLARATIONS ..... From 80c39816a65bf87a3569613f3faec7d28c8b8927 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 18 Mar 2013 12:50:49 +1100 Subject: [PATCH 4/5] [FEATURE] Add triweight and Epanechnikov kernels. Make decay disabled for all kernels expect Triangular kernel. Fix math for triangular kernels. --- src/plugins/heatmap/heatmap.cpp | 77 +++++++++++++++++++++------ src/plugins/heatmap/heatmap.h | 13 +++-- src/plugins/heatmap/heatmapgui.cpp | 8 +++ src/plugins/heatmap/heatmapgui.h | 9 ++-- src/plugins/heatmap/heatmapguibase.ui | 17 +++++- 5 files changed, 99 insertions(+), 25 deletions(-) diff --git a/src/plugins/heatmap/heatmap.cpp b/src/plugins/heatmap/heatmap.cpp index eb5326bf5fb9..19bfd92e2438 100644 --- a/src/plugins/heatmap/heatmap.cpp +++ b/src/plugins/heatmap/heatmap.cpp @@ -111,7 +111,7 @@ 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 @@ -262,7 +262,7 @@ void Heatmap::run() { continue; } - + double pixelValue = weight * calculateKernelValue( distance, myBuffer, kernelShape ); // clearing anamolies along the axes @@ -332,41 +332,84 @@ int Heatmap::bufferSize( double radius, double cellsize ) ++buffer; } return buffer; -} - +} + double Heatmap::calculateKernelValue( double distance, int bandwidth, int kernelShape ) { - switch (kernelShape) { + switch ( kernelShape ) + { case HeatmapGui::Triangular: - return ( 1 - ( distance / bandwidth ) ); - + return triangularKernel( distance , bandwidth ); + case HeatmapGui::Uniform: return uniformKernel( distance, bandwidth ); - + case HeatmapGui::Quartic: return quarticKernel( distance, bandwidth ); + + case HeatmapGui::Triweight: + return triweightKernel( distance, bandwidth ); + + case HeatmapGui::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. Calculated by polar double integrating the kernel function - // with radius of 0 to bandwidth and equating area to 1. - double k = 2. / (M_PI * (double)bandwidth); - + // Normalizing constant + double k = 2. / ( M_PI * ( double )bandwidth ); + // Derived from Wand and Jones (1995), p. 175 - return k * ( 0.5 / (double)bandwidth); + 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)); - + 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 * (15. / 16. ) * pow( 1. - pow( distance / (double)bandwidth, 2), 2); + 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 diff --git a/src/plugins/heatmap/heatmap.h b/src/plugins/heatmap/heatmap.h index 5dba7db97baa..9ae866aa8008 100644 --- a/src/plugins/heatmap/heatmap.h +++ b/src/plugins/heatmap/heatmap.h @@ -81,6 +81,8 @@ 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 @@ -89,10 +91,15 @@ class Heatmap: public QObject, public QgisPlugin double calculateKernelValue( double distance, int bandwidth, int kernelShape ); //! Uniform kernel function double uniformKernel( double distance, int bandwidth ); - //! Quartic kernel function + //! 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; diff --git a/src/plugins/heatmap/heatmapgui.cpp b/src/plugins/heatmap/heatmapgui.cpp index aaaba04aed5f..a50500e34e2a 100644 --- a/src/plugins/heatmap/heatmapgui.cpp +++ b/src/plugins/heatmap/heatmapgui.cpp @@ -225,6 +225,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 == HeatmapGui::Triangular ); +} + /* * * Private Functions diff --git a/src/plugins/heatmap/heatmapgui.h b/src/plugins/heatmap/heatmapgui.h index c759cc06c308..03a61a8aef5b 100644 --- a/src/plugins/heatmap/heatmapgui.h +++ b/src/plugins/heatmap/heatmapgui.h @@ -35,12 +35,14 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase Meters, MapUnits }; - + enum kernelShape { + Quartic, Triangular, Uniform, - Quartic + Triweight, + Epanechnikov }; /** Returns whether to apply weighted heat */ @@ -54,7 +56,7 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase /** Return the radius Unit (meters/map units) */ int radiusUnit(); - + /** Return the selected kernel shape */ int kernelShape(); @@ -130,6 +132,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 diff --git a/src/plugins/heatmap/heatmapguibase.ui b/src/plugins/heatmap/heatmapguibase.ui index 1a690b08b5f6..551d88ffd36e 100644 --- a/src/plugins/heatmap/heatmapguibase.ui +++ b/src/plugins/heatmap/heatmapguibase.ui @@ -227,6 +227,9 @@ + + false + 0.0 @@ -241,6 +244,11 @@ + + + Quartic (Biweight) + + Triangular @@ -253,9 +261,14 @@ - Quartic + Triweight + + + + + Epanechnikov - + From 7423e9aad75880475e4d21f1e06f4cabf0b485e9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 19 Mar 2013 07:21:23 +1100 Subject: [PATCH 5/5] Move kernelShape enum to heatmap.h --- src/plugins/heatmap/heatmap.cpp | 10 +++++----- src/plugins/heatmap/heatmap.h | 10 ++++++++++ src/plugins/heatmap/heatmapgui.cpp | 3 ++- src/plugins/heatmap/heatmapgui.h | 9 --------- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/plugins/heatmap/heatmap.cpp b/src/plugins/heatmap/heatmap.cpp index 19bfd92e2438..8f556287d1b1 100644 --- a/src/plugins/heatmap/heatmap.cpp +++ b/src/plugins/heatmap/heatmap.cpp @@ -338,19 +338,19 @@ double Heatmap::calculateKernelValue( double distance, int bandwidth, int kernel { switch ( kernelShape ) { - case HeatmapGui::Triangular: + case Heatmap::Triangular: return triangularKernel( distance , bandwidth ); - case HeatmapGui::Uniform: + case Heatmap::Uniform: return uniformKernel( distance, bandwidth ); - case HeatmapGui::Quartic: + case Heatmap::Quartic: return quarticKernel( distance, bandwidth ); - case HeatmapGui::Triweight: + case Heatmap::Triweight: return triweightKernel( distance, bandwidth ); - case HeatmapGui::Epanechnikov: + case Heatmap::Epanechnikov: return epanechnikovKernel( distance, bandwidth ); } return 0; diff --git a/src/plugins/heatmap/heatmap.h b/src/plugins/heatmap/heatmap.h index 9ae866aa8008..c163f5257380 100644 --- a/src/plugins/heatmap/heatmap.h +++ b/src/plugins/heatmap/heatmap.h @@ -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 diff --git a/src/plugins/heatmap/heatmapgui.cpp b/src/plugins/heatmap/heatmapgui.cpp index a50500e34e2a..2755ac9b804a 100644 --- a/src/plugins/heatmap/heatmapgui.cpp +++ b/src/plugins/heatmap/heatmapgui.cpp @@ -12,6 +12,7 @@ // qgis includes #include "qgis.h" #include "heatmapgui.h" +#include "heatmap.h" #include "qgscontexthelp.h" #include "qgsmaplayer.h" #include "qgsmaplayerregistry.h" @@ -230,7 +231,7 @@ 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 == HeatmapGui::Triangular ); + mDecayLineEdit->setEnabled( index == Heatmap::Triangular ); } /* diff --git a/src/plugins/heatmap/heatmapgui.h b/src/plugins/heatmap/heatmapgui.h index 03a61a8aef5b..8e6ce30efc80 100644 --- a/src/plugins/heatmap/heatmapgui.h +++ b/src/plugins/heatmap/heatmapgui.h @@ -36,15 +36,6 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase MapUnits }; - enum kernelShape - { - Quartic, - Triangular, - Uniform, - Triweight, - Epanechnikov - }; - /** Returns whether to apply weighted heat */ bool weighted();