QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgskde.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgskde.cpp
3  ----------
4  Date : October 2016
5  Copyright : (C) 2016 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <QByteArray>
17 
18 #include "qgskde.h"
19 #include "qgsfeaturesource.h"
20 #include "qgsfeatureiterator.h"
21 #include "qgsgeometry.h"
22 #include "qgsrasterfilewriter.h"
23 
24 #define NO_DATA -9999
25 
26 QgsKernelDensityEstimation::QgsKernelDensityEstimation( const QgsKernelDensityEstimation::Parameters &parameters, const QString &outputFile, const QString &outputFormat )
27  : mSource( parameters.source )
28  , mOutputFile( outputFile )
29  , mOutputFormat( outputFormat )
30  , mRadiusField( -1 )
31  , mWeightField( -1 )
32  , mRadius( parameters.radius )
33  , mPixelSize( parameters.pixelSize )
34  , mShape( parameters.shape )
35  , mDecay( parameters.decayRatio )
36  , mOutputValues( parameters.outputValues )
37  , mBufferSize( -1 )
38 {
39  if ( !parameters.radiusField.isEmpty() )
40  mRadiusField = mSource->fields().lookupField( parameters.radiusField );
41  if ( !parameters.weightField.isEmpty() )
42  mWeightField = mSource->fields().lookupField( parameters.weightField );
43 }
44 
46 {
47  Result result = prepare();
48  if ( result != Success )
49  return result;
50 
51  QgsAttributeList requiredAttributes;
52 
53  if ( mRadiusField >= 0 )
54  requiredAttributes << mRadiusField;
55 
56  if ( mWeightField >= 0 )
57  requiredAttributes << mWeightField;
58 
59  QgsFeatureIterator fit = mSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( requiredAttributes ) );
60 
61  QgsFeature f;
62  while ( fit.nextFeature( f ) )
63  {
64  addFeature( f );
65  }
66 
67  return finalise();
68 }
69 
71 {
72  if ( !mSource )
73  return InvalidParameters;
74 
75  mBounds = calculateBounds();
76  if ( mBounds.isNull() )
77  return InvalidParameters;
78 
79  int rows = std::max( std::ceil( mBounds.height() / mPixelSize ) + 1, 1.0 );
80  int cols = std::max( std::ceil( mBounds.width() / mPixelSize ) + 1, 1.0 );
81 
82  // create empty raster and fill it with nodata values
83  QgsRasterFileWriter writer( mOutputFile );
84  writer.setOutputProviderKey( QStringLiteral( "gdal" ) );
85  writer.setOutputFormat( mOutputFormat );
86  mProvider = writer.createOneBandRaster( Qgis::Float32, cols, rows, mBounds, mSource->sourceCrs() );
87  if ( !mProvider )
88  {
89  return FileCreationError;
90  }
91 
92  if ( !mProvider->setNoDataValue( 1, NO_DATA ) )
93  {
94  return FileCreationError;
95  }
96 
97  QgsRasterBlock block( Qgis::Float32, cols, 1 );
98  block.setNoDataValue( NO_DATA );
99  block.setIsNoData();
100  for ( int i = 0; i < rows ; i++ )
101  {
102  mProvider->writeBlock( &block, 1, 0, i );
103  }
104 
105  mBufferSize = -1;
106  if ( mRadiusField < 0 )
107  mBufferSize = radiusSizeInPixels( mRadius );
108 
109  return Success;
110 }
111 
113 {
114  QgsGeometry featureGeometry = feature.geometry();
115  if ( featureGeometry.isNull() )
116  {
117  return Success;
118  }
119 
120  // convert the geometry to multipoint
121  QgsMultiPointXY multiPoints;
122  if ( !featureGeometry.isMultipart() )
123  {
124  QgsPointXY p = featureGeometry.asPoint();
125  // avoiding any empty points or out of extent points
126  if ( !mBounds.contains( p ) )
127  return Success;
128 
129  multiPoints << p;
130  }
131  else
132  {
133  multiPoints = featureGeometry.asMultiPoint();
134  }
135 
136  // if radius is variable then fetch it and calculate new pixel buffer size
137  double radius = mRadius;
138  int buffer = mBufferSize;
139  if ( mRadiusField >= 0 )
140  {
141  radius = feature.attribute( mRadiusField ).toDouble();
142  buffer = radiusSizeInPixels( radius );
143  }
144  int blockSize = 2 * buffer + 1; //Block SIDE would be more appropriate
145 
146  // calculate weight
147  double weight = 1.0;
148  if ( mWeightField >= 0 )
149  {
150  weight = feature.attribute( mWeightField ).toDouble();
151  }
152 
153  Result result = Success;
154 
155  //loop through all points in multipoint
156  for ( QgsMultiPointXY::const_iterator pointIt = multiPoints.constBegin(); pointIt != multiPoints.constEnd(); ++pointIt )
157  {
158  // avoiding any empty points or out of extent points
159  if ( !mBounds.contains( *pointIt ) )
160  {
161  continue;
162  }
163 
164  // calculate the pixel position
165  unsigned int xPosition = ( ( ( *pointIt ).x() - mBounds.xMinimum() ) / mPixelSize ) - buffer;
166  unsigned int yPosition = ( ( ( *pointIt ).y() - mBounds.yMinimum() ) / mPixelSize ) - buffer;
167  unsigned int yPositionIO = ( ( mBounds.yMaximum() - ( *pointIt ).y() ) / mPixelSize ) - buffer;
168 
169 
170  // calculate buffer around pixel
171  QgsRectangle extent( ( *pointIt ).x() - radius, ( *pointIt ).y() - radius, ( *pointIt ).x() + radius, ( *pointIt ).y() + radius );
172 
173  // get the data
174  std::unique_ptr< QgsRasterBlock > block( mProvider->block( 1, extent, blockSize, blockSize ) );
175  QByteArray blockData = block->data();
176  float *dataBuffer = ( float * ) blockData.data();
177 
178  for ( int xp = 0; xp < blockSize; xp++ )
179  {
180  for ( int yp = 0; yp < blockSize; yp++ )
181  {
182  double pixelCentroidX = ( xPosition + xp + 0.5 ) * mPixelSize + mBounds.xMinimum();
183  double pixelCentroidY = ( yPosition + yp + 0.5 ) * mPixelSize + mBounds.yMinimum();
184 
185  double distance = std::sqrt( std::pow( pixelCentroidX - ( *pointIt ).x(), 2.0 ) + std::pow( pixelCentroidY - ( *pointIt ).y(), 2.0 ) );
186 
187  // is pixel outside search bandwidth of feature?
188  if ( distance > radius )
189  {
190  continue;
191  }
192 
193  double pixelValue = weight * calculateKernelValue( distance, radius, mShape, mOutputValues );
194  int pos = xp + blockSize * yp;
195  if ( dataBuffer[ pos ] == NO_DATA )
196  {
197  dataBuffer[ pos ] = 0;
198  }
199  dataBuffer[ pos ] += pixelValue;
200  }
201  }
202 
203  block->setData( blockData );
204  if ( !mProvider->writeBlock( block.get(), 1, xPosition, yPositionIO ) )
205  {
206  result = RasterIoError;
207  }
208  }
209 
210  return result;
211 }
212 
214 {
215  mProvider->setEditable( false );
216  mProvider = nullptr;
217 
218  return Success;
219 }
220 
221 int QgsKernelDensityEstimation::radiusSizeInPixels( double radius ) const
222 {
223  int buffer = radius / mPixelSize;
224  if ( radius - ( mPixelSize * buffer ) > 0.5 )
225  {
226  ++buffer;
227  }
228  return buffer;
229 }
230 
231 double QgsKernelDensityEstimation::calculateKernelValue( const double distance, const double bandwidth, const QgsKernelDensityEstimation::KernelShape shape, const QgsKernelDensityEstimation::OutputValues outputType ) const
232 {
233  switch ( shape )
234  {
235  case KernelTriangular:
236  return triangularKernel( distance, bandwidth, outputType );
237 
238  case KernelUniform:
239  return uniformKernel( distance, bandwidth, outputType );
240 
241  case KernelQuartic:
242  return quarticKernel( distance, bandwidth, outputType );
243 
244  case KernelTriweight:
245  return triweightKernel( distance, bandwidth, outputType );
246 
247  case KernelEpanechnikov:
248  return epanechnikovKernel( distance, bandwidth, outputType );
249  }
250  return 0; //no warnings
251 }
252 
253 /* The kernel functions below are taken from "Kernel Smoothing" by Wand and Jones (1995), p. 175
254  *
255  * Each kernel is multiplied by a normalizing constant "k", which normalizes the kernel area
256  * to 1 for a given bandwidth size.
257  *
258  * k is calculated by polar double integration of the kernel function
259  * between a radius of 0 to the specified bandwidth and equating the area to 1. */
260 
261 double QgsKernelDensityEstimation::uniformKernel( const double distance, const double bandwidth, const QgsKernelDensityEstimation::OutputValues outputType ) const
262 {
263  Q_UNUSED( distance )
264  switch ( outputType )
265  {
266  case OutputScaled:
267  {
268  // Normalizing constant
269  double k = 2. / ( M_PI * bandwidth );
270 
271  // Derived from Wand and Jones (1995), p. 175
272  return k * ( 0.5 / bandwidth );
273  }
274  case OutputRaw:
275  return 1.0;
276  }
277  return 0.0; // NO warnings!!!!!
278 }
279 
280 double QgsKernelDensityEstimation::quarticKernel( const double distance, const double bandwidth, const QgsKernelDensityEstimation::OutputValues outputType ) const
281 {
282  switch ( outputType )
283  {
284  case OutputScaled:
285  {
286  // Normalizing constant
287  double k = 116. / ( 5. * M_PI * std::pow( bandwidth, 2 ) );
288 
289  // Derived from Wand and Jones (1995), p. 175
290  return k * ( 15. / 16. ) * std::pow( 1. - std::pow( distance / bandwidth, 2 ), 2 );
291  }
292  case OutputRaw:
293  return std::pow( 1. - std::pow( distance / bandwidth, 2 ), 2 );
294  }
295  return 0.0; //no, seriously, I told you NO WARNINGS!
296 }
297 
298 double QgsKernelDensityEstimation::triweightKernel( const double distance, const double bandwidth, const QgsKernelDensityEstimation::OutputValues outputType ) const
299 {
300  switch ( outputType )
301  {
302  case OutputScaled:
303  {
304  // Normalizing constant
305  double k = 128. / ( 35. * M_PI * std::pow( bandwidth, 2 ) );
306 
307  // Derived from Wand and Jones (1995), p. 175
308  return k * ( 35. / 32. ) * std::pow( 1. - std::pow( distance / bandwidth, 2 ), 3 );
309  }
310  case OutputRaw:
311  return std::pow( 1. - std::pow( distance / bandwidth, 2 ), 3 );
312  }
313  return 0.0; // this is getting ridiculous... don't you ever listen to a word I say?
314 }
315 
316 double QgsKernelDensityEstimation::epanechnikovKernel( const double distance, const double bandwidth, const QgsKernelDensityEstimation::OutputValues outputType ) const
317 {
318  switch ( outputType )
319  {
320  case OutputScaled:
321  {
322  // Normalizing constant
323  double k = 8. / ( 3. * M_PI * std::pow( bandwidth, 2 ) );
324 
325  // Derived from Wand and Jones (1995), p. 175
326  return k * ( 3. / 4. ) * ( 1. - std::pow( distance / bandwidth, 2 ) );
327  }
328  case OutputRaw:
329  return ( 1. - std::pow( distance / bandwidth, 2 ) );
330  }
331 
332  return 0.0; // la la la i'm not listening
333 }
334 
335 double QgsKernelDensityEstimation::triangularKernel( const double distance, const double bandwidth, const QgsKernelDensityEstimation::OutputValues outputType ) const
336 {
337  switch ( outputType )
338  {
339  case OutputScaled:
340  {
341  // Normalizing constant. In this case it's calculated a little different
342  // due to the inclusion of the non-standard "decay" parameter
343 
344  if ( mDecay >= 0 )
345  {
346  double k = 3. / ( ( 1. + 2. * mDecay ) * M_PI * std::pow( bandwidth, 2 ) );
347 
348  // Derived from Wand and Jones (1995), p. 175 (with addition of decay parameter)
349  return k * ( 1. - ( 1. - mDecay ) * ( distance / bandwidth ) );
350  }
351  else
352  {
353  // Non-standard or mathematically valid negative decay ("coolmap")
354  return ( 1. - ( 1. - mDecay ) * ( distance / bandwidth ) );
355  }
356  }
357  case OutputRaw:
358  return ( 1. - ( 1. - mDecay ) * ( distance / bandwidth ) );
359  }
360  return 0.0; // ....
361 }
362 
363 QgsRectangle QgsKernelDensityEstimation::calculateBounds() const
364 {
365  if ( !mSource )
366  return QgsRectangle();
367 
368  QgsRectangle bbox = mSource->sourceExtent();
369 
370  double radius = 0;
371  if ( mRadiusField >= 0 )
372  {
373  // if radius is using a field, find the max value
374  radius = mSource->maximumValue( mRadiusField ).toDouble();
375  }
376  else
377  {
378  radius = mRadius;
379  }
380  // expand the bounds by the maximum search radius
381  bbox.setXMinimum( bbox.xMinimum() - radius );
382  bbox.setYMinimum( bbox.yMinimum() - radius );
383  bbox.setXMaximum( bbox.xMaximum() + radius );
384  bbox.setYMaximum( bbox.yMaximum() + radius );
385  return bbox;
386 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
QgsRasterDataProvider * createOneBandRaster(Qgis::DataType dataType, int width, int height, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &crs)
Create a raster file with one band without initializing the pixel data.
QgsKernelDensityEstimation(const Parameters &parameters, const QString &outputFile, const QString &outputFormat)
Constructor for QgsKernelDensityEstimation.
Definition: qgskde.cpp:26
void setNoDataValue(double noDataValue)
Sets cell value that will be considered as "no data".
Wrapper for iterator of features from vector data provider or vector layer.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:342
virtual QgsRectangle sourceExtent() const
Returns the extent of all geometries from the source.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QString weightField
Field name for weighting field, or empty if not using weights.
Definition: qgskde.h:80
virtual QgsFields fields() const =0
Returns the fields associated with features in the source.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
Uniform (flat) kernel.
Definition: qgskde.h:45
#define NO_DATA
Definition: qgskde.cpp:24
A class to represent a 2D point.
Definition: qgspointxy.h:43
void setOutputFormat(const QString &format)
Error writing to raster.
Definition: qgskde.h:64
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
virtual bool setNoDataValue(int bandNo, double noDataValue)
Set no data value on created dataset.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:80
Thirty two bit floating point (float)
Definition: qgis.h:109
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
KernelShape
Kernel shape type.
Definition: qgskde.h:41
bool setIsNoData(int row, int column)
Set no data on pixel.
OutputValues
Output values type.
Definition: qgskde.h:51
virtual bool setEditable(bool enabled)
Turns on/off editing mode of the provider.
Raster data container.
Input parameters were not valid.
Definition: qgskde.h:62
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
Result finalise()
Finalises the output file.
Definition: qgskde.cpp:213
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Output the raw KDE values.
Definition: qgskde.h:53
void setOutputProviderKey(const QString &key)
Output mathematically correct scaled values.
Definition: qgskde.h:54
virtual QgsCoordinateReferenceSystem sourceCrs() const =0
Returns the coordinate reference system for features in the source.
Operation completed successfully.
Definition: qgskde.h:60
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsRasterBlock * block(int bandNo, const QgsRectangle &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QString radiusField
Field for radius, or empty if using a fixed radius.
Definition: qgskde.h:77
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
Result addFeature(const QgsFeature &feature)
Adds a single feature to the KDE surface.
Definition: qgskde.cpp:112
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:436
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
Result run()
Runs the KDE calculation across the whole layer at once.
Definition: qgskde.cpp:45
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsGeometry geometry
Definition: qgsfeature.h:67
virtual QVariant maximumValue(int fieldIndex) const
Returns the maximum value for an attribute column or an invalid variant in case of error...
QList< int > QgsAttributeList
Definition: qgsfield.h:26
bool nextFeature(QgsFeature &f)
Result
Result of operation.
Definition: qgskde.h:58
Result prepare()
Prepares the output file for writing and setups up the surface calculation.
Definition: qgskde.cpp:70
bool writeBlock(QgsRasterBlock *block, int band, int xOffset=0, int yOffset=0)
Writes pixel data from a raster block into the provider data source.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
Error creating output file.
Definition: qgskde.h:63
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const =0
Returns an iterator for the features in the source.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
The raster file writer which allows you to save a raster to a new file.