Skip to content

Commit a05747c

Browse files
committed
[FEATURE] Add option to select kernel shape (quartic, triangular or uniform) in heatmap plugin
1 parent 7bd89ce commit a05747c

File tree

5 files changed

+96
-10
lines changed

5 files changed

+96
-10
lines changed

src/plugins/heatmap/heatmap.cpp

+40-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343

4444
#define NO_DATA -9999
4545

46+
#ifndef M_PI
47+
#define M_PI 3.14159265358979323846
48+
#endif
4649

4750
static const QString sName = QObject::tr( "Heatmap" );
4851
static const QString sDescription = QObject::tr( "Creates a Heatmap raster for the input point vector" );
@@ -109,7 +112,8 @@ void Heatmap::run()
109112
int rows = d.rows();
110113
float cellsize = d.cellSizeX(); // or d.cellSizeY(); both have the same value
111114
float myDecay = d.decayRatio();
112-
115+
int kernelShape = d.kernelShape();
116+
113117
// Start working on the input vector
114118
QgsVectorLayer* inputLayer = d.inputVectorLayer();
115119

@@ -259,7 +263,7 @@ void Heatmap::run()
259263
continue;
260264
}
261265

262-
float pixelValue = weight * ( 1 - (( 1 - myDecay ) * distance / myBuffer ) );
266+
float pixelValue = weight * calculateKernelValue( distance, myBuffer, kernelShape );
263267

264268
// clearing anamolies along the axes
265269
if ( xp == 0 && yp == 0 )
@@ -330,6 +334,40 @@ int Heatmap::bufferSize( float radius, float cellsize )
330334
return buffer;
331335
}
332336

337+
float Heatmap::calculateKernelValue( float distance, int bandwidth, int kernelShape )
338+
{
339+
switch (kernelShape) {
340+
case HeatmapGui::Triangular:
341+
return ( 1 - ( distance / bandwidth ) );
342+
343+
case HeatmapGui::Uniform:
344+
return uniformKernel( distance, bandwidth );
345+
346+
case HeatmapGui::Quartic:
347+
return quarticKernel( distance, bandwidth );
348+
}
349+
return 0;
350+
351+
}
352+
353+
float Heatmap::uniformKernel( float distance, int bandwidth )
354+
{
355+
// Normalizing constant. Calculated by polar double integrating the kernel function
356+
// with radius of 0 to bandwidth and equating area to 1.
357+
float k = 2. / (M_PI * (float)bandwidth);
358+
359+
// Derived from Wand and Jones (1995), p. 175
360+
return k * ( 0.5 / (float)bandwidth);
361+
}
362+
363+
float Heatmap::quarticKernel( float distance, int bandwidth )
364+
{
365+
// Normalizing constant
366+
float k = 16. / (5. * M_PI * pow((float)bandwidth, 2));
367+
368+
// Derived from Wand and Jones (1995), p. 175
369+
return k * (15. / 16. ) * pow( 1. - pow( distance / (float)bandwidth, 2), 2);
370+
}
333371

334372
// Unload the plugin by cleaning up the GUI
335373
void Heatmap::unload()

src/plugins/heatmap/heatmap.h

+7
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ class Heatmap: public QObject, public QgisPlugin
8585
float mapUnitsOf( float meters, QgsCoordinateReferenceSystem crs );
8686
//! Worker to calculate buffer size in pixels
8787
int bufferSize( float radius, float cellsize );
88+
//! Calculate the value given to a point width a given distance for a specified kernel shape
89+
float calculateKernelValue( float distance, int bandwidth, int kernelShape );
90+
//! Uniform kernel function
91+
float uniformKernel( float distance, int bandwidth );
92+
//! Quartic kernel function
93+
float quarticKernel( float distance, int bandwidth );
94+
8895

8996
// MANDATORY PLUGIN PROPERTY DECLARATIONS .....
9097

src/plugins/heatmap/heatmapgui.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,11 @@ int HeatmapGui::radiusUnit()
375375
return mRadiusUnitCombo->currentIndex();
376376
}
377377

378+
int HeatmapGui::kernelShape()
379+
{
380+
return kernelShapeCombo->currentIndex();
381+
}
382+
378383
float HeatmapGui::decayRatio()
379384
{
380385
return mDecayLineEdit->text().toFloat();

src/plugins/heatmap/heatmapgui.h

+10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase
3535
Meters,
3636
MapUnits
3737
};
38+
39+
enum kernelShape
40+
{
41+
Triangular,
42+
Uniform,
43+
Quartic
44+
};
3845

3946
/** Returns whether to apply weighted heat */
4047
bool weighted();
@@ -47,6 +54,9 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase
4754

4855
/** Return the radius Unit (meters/map units) */
4956
int radiusUnit();
57+
58+
/** Return the selected kernel shape */
59+
int kernelShape();
5060

5161
/** Return the decay ratio */
5262
float decayRatio();

src/plugins/heatmap/heatmapguibase.ui

+34-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<x>0</x>
88
<y>0</y>
99
<width>460</width>
10-
<height>382</height>
10+
<height>415</height>
1111
</rect>
1212
</property>
1313
<property name="sizePolicy">
@@ -180,21 +180,21 @@
180180
</item>
181181
<item row="1" column="0">
182182
<layout class="QGridLayout" name="gridLayout_2">
183-
<item row="0" column="0">
183+
<item row="1" column="0">
184184
<widget class="QCheckBox" name="useRadius">
185185
<property name="text">
186186
<string>Use Radius from field</string>
187187
</property>
188188
</widget>
189189
</item>
190-
<item row="0" column="1">
190+
<item row="1" column="1">
191191
<widget class="QComboBox" name="radiusFieldCombo">
192192
<property name="enabled">
193193
<bool>false</bool>
194194
</property>
195195
</widget>
196196
</item>
197-
<item row="0" column="2">
197+
<item row="1" column="2">
198198
<widget class="QComboBox" name="radiusFieldUnitCombo">
199199
<property name="enabled">
200200
<bool>false</bool>
@@ -211,34 +211,60 @@
211211
</item>
212212
</widget>
213213
</item>
214-
<item row="1" column="0">
214+
<item row="2" column="0">
215215
<widget class="QCheckBox" name="useWeight">
216216
<property name="text">
217217
<string>Use Weight from field</string>
218218
</property>
219219
</widget>
220220
</item>
221-
<item row="1" column="1" colspan="2">
221+
<item row="2" column="1" colspan="2">
222222
<widget class="QComboBox" name="weightFieldCombo">
223223
<property name="enabled">
224224
<bool>false</bool>
225225
</property>
226226
</widget>
227227
</item>
228-
<item row="2" column="1" colspan="2">
228+
<item row="3" column="1" colspan="2">
229229
<widget class="QLineEdit" name="mDecayLineEdit">
230230
<property name="text">
231231
<string>0.0</string>
232232
</property>
233233
</widget>
234234
</item>
235-
<item row="2" column="0">
235+
<item row="3" column="0">
236236
<widget class="QLabel" name="mDecayLabel">
237237
<property name="text">
238238
<string>Decay Ratio</string>
239239
</property>
240240
</widget>
241241
</item>
242+
<item row="0" column="1" colspan="2">
243+
<widget class="QComboBox" name="kernelShapeCombo">
244+
<item>
245+
<property name="text">
246+
<string>Triangular</string>
247+
</property>
248+
</item>
249+
<item>
250+
<property name="text">
251+
<string>Uniform</string>
252+
</property>
253+
</item>
254+
<item>
255+
<property name="text">
256+
<string>Quartic</string>
257+
</property>
258+
</item>
259+
</widget>
260+
</item>
261+
<item row="0" column="0">
262+
<widget class="QLabel" name="kernelShapeLable">
263+
<property name="text">
264+
<string>Kernel Shape</string>
265+
</property>
266+
</widget>
267+
</item>
242268
</layout>
243269
</item>
244270
</layout>

0 commit comments

Comments
 (0)