diff --git a/src/plugins/heatmap/heatmap.cpp b/src/plugins/heatmap/heatmap.cpp index 0b453a7cab4..075c85a0197 100644 --- a/src/plugins/heatmap/heatmap.cpp +++ b/src/plugins/heatmap/heatmap.cpp @@ -132,7 +132,8 @@ void Heatmap::run() int rows = d.rows(); double cellsize = d.cellSizeX(); // or d.cellSizeY(); both have the same value mDecay = d.decayRatio(); - int kernelShape = d.kernelShape(); + KernelShape kernelShape = d.kernelShape(); + OutputValues valueType = d.outputValues(); //is input layer multipoint? bool isMultiPoint = inputLayer->wkbType() == QGis::WKBMultiPoint || inputLayer->wkbType() == QGis::WKBMultiPoint25D; @@ -311,7 +312,7 @@ void Heatmap::run() continue; } - double pixelValue = weight * calculateKernelValue( distance, myBuffer, kernelShape ); + double pixelValue = weight * calculateKernelValue( distance, myBuffer, kernelShape, valueType ); // clearing anamolies along the axes if ( xp == 0 && yp == 0 ) @@ -386,24 +387,24 @@ int Heatmap::bufferSize( double radius, double cellsize ) return buffer; } -double Heatmap::calculateKernelValue( double distance, int bandwidth, int kernelShape ) +double Heatmap::calculateKernelValue( const double distance, const int bandwidth, const KernelShape shape, const OutputValues outputType ) { - switch ( kernelShape ) + switch ( shape ) { case Heatmap::Triangular: - return triangularKernel( distance , bandwidth ); + return triangularKernel( distance , bandwidth, outputType ); case Heatmap::Uniform: - return uniformKernel( distance, bandwidth ); + return uniformKernel( distance, bandwidth, outputType ); case Heatmap::Quartic: - return quarticKernel( distance, bandwidth ); + return quarticKernel( distance, bandwidth, outputType ); case Heatmap::Triweight: - return triweightKernel( distance, bandwidth ); + return triweightKernel( distance, bandwidth, outputType ); case Heatmap::Epanechnikov: - return epanechnikovKernel( distance, bandwidth ); + return epanechnikovKernel( distance, bandwidth, outputType ); } return 0; @@ -417,59 +418,99 @@ double Heatmap::calculateKernelValue( double distance, int bandwidth, int kernel * 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 ) +double Heatmap::uniformKernel( const double distance, const int bandwidth, const OutputValues outputType ) const { Q_UNUSED( distance ); - // 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 - - if ( mDecay >= 0 ) + switch ( outputType ) { - double k = 3. / (( 1. + 2. * mDecay ) * M_PI * pow(( double )bandwidth, 2 ) ); + case Heatmap::Scaled: + { + // Normalizing constant + double k = 2. / ( M_PI * ( double )bandwidth ); - // Derived from Wand and Jones (1995), p. 175 (with addition of decay parameter) - return k * ( 1. - ( 1. - mDecay ) * ( distance / ( double )bandwidth ) ); + // Derived from Wand and Jones (1995), p. 175 + return k * ( 0.5 / ( double )bandwidth ); + } + default: + return 1.0; } - else +} + +double Heatmap::quarticKernel( const double distance, const int bandwidth, const OutputValues outputType ) const +{ + switch ( outputType ) { - // Non-standard or mathematically valid negative decay ("coolmap") - return ( 1. - ( 1. - mDecay ) * ( distance / ( double )bandwidth ) ); + case Heatmap::Scaled: + { + // Normalizing constant + double k = outputType == Heatmap::Scaled ? 116. / ( 5. * M_PI * pow(( double )bandwidth, 2 ) ) : 1.0; + + // Derived from Wand and Jones (1995), p. 175 + return k * ( 15. / 16. ) * pow( 1. - pow( distance / ( double )bandwidth, 2 ), 2 ); + } + default: + return pow( 1. - pow( distance / ( double )bandwidth, 2 ), 2 ); + } +} + +double Heatmap::triweightKernel( const double distance, const int bandwidth, const OutputValues outputType ) const +{ + switch ( outputType ) + { + case Heatmap::Scaled: + { + // Normalizing constant + double k = outputType == Heatmap::Scaled ? 128. / ( 35. * M_PI * pow(( double )bandwidth, 2 ) ) : 1.0; + + // Derived from Wand and Jones (1995), p. 175 + return k * ( 35. / 32. ) * pow( 1. - pow( distance / ( double )bandwidth, 2 ), 3 ); + } + default: + return pow( 1. - pow( distance / ( double )bandwidth, 2 ), 3 ); + } +} + +double Heatmap::epanechnikovKernel( const double distance, const int bandwidth, const OutputValues outputType ) const +{ + switch ( outputType ) + { + case Heatmap::Scaled: + { + // Normalizing constant + double k = outputType == Heatmap::Scaled ? 8. / ( 3. * M_PI * pow(( double )bandwidth, 2 ) ) : 1.0; + + // Derived from Wand and Jones (1995), p. 175 + return k * ( 3. / 4. ) * ( 1. - pow( distance / ( double )bandwidth, 2 ) ); + } + default: + return ( 1. - pow( distance / ( double )bandwidth, 2 ) ); + } +} + +double Heatmap::triangularKernel( const double distance, const int bandwidth, const OutputValues outputType ) const +{ + switch ( outputType ) + { + case Heatmap::Scaled: + { + // Normalizing constant. In this case it's calculated a little different + // due to the inclusion of the non-standard "decay" parameter + + if ( mDecay >= 0 ) + { + 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 ) ); + } + else + { + // Non-standard or mathematically valid negative decay ("coolmap") + return ( 1. - ( 1. - mDecay ) * ( distance / ( double )bandwidth ) ); + } + } + default: + return ( 1. - ( 1. - mDecay ) * ( distance / ( double )bandwidth ) ); } } diff --git a/src/plugins/heatmap/heatmap.h b/src/plugins/heatmap/heatmap.h index 9331993b2a6..690c478dafa 100644 --- a/src/plugins/heatmap/heatmap.h +++ b/src/plugins/heatmap/heatmap.h @@ -71,7 +71,7 @@ class Heatmap: public QObject, public QgisPlugin virtual ~Heatmap(); // Kernel shape type - enum kernelShape + enum KernelShape { Quartic, Triangular, @@ -80,6 +80,13 @@ class Heatmap: public QObject, public QgisPlugin Epanechnikov }; + // Output values type + enum OutputValues + { + Raw, + Scaled + }; + QMap mSessionSettings; public slots: @@ -100,17 +107,17 @@ 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 - double calculateKernelValue( double distance, int bandwidth, int kernelShape ); + double calculateKernelValue( const double distance, const int bandwidth, const KernelShape shape, const OutputValues outputType ); //! Uniform kernel function - double uniformKernel( double distance, int bandwidth ); + double uniformKernel( const double distance, const int bandwidth , const OutputValues outputType ) const; //! Quartic kernel function - double quarticKernel( double distance, int bandwidth ); + double quarticKernel( const double distance, const int bandwidth , const OutputValues outputType ) const; //! Triweight kernel function - double triweightKernel( double distance, int bandwidth ); + double triweightKernel( const double distance, const int bandwidth , const OutputValues outputType ) const; //! Epanechnikov kernel function - double epanechnikovKernel( double distance, int bandwidth ); + double epanechnikovKernel( const double distance, const int bandwidth, const OutputValues outputType ) const; //! Triangular kernel function - double triangularKernel( double distance, int bandwidth ); + double triangularKernel( const double distance, const int bandwidth , const OutputValues outputType ) const; // MANDATORY PLUGIN PROPERTY DECLARATIONS ..... diff --git a/src/plugins/heatmap/heatmapgui.cpp b/src/plugins/heatmap/heatmapgui.cpp index 6444225cc56..eaf94ca9dfe 100644 --- a/src/plugins/heatmap/heatmapgui.cpp +++ b/src/plugins/heatmap/heatmapgui.cpp @@ -46,6 +46,15 @@ HeatmapGui::HeatmapGui( QWidget* parent, Qt::WindowFlags fl, QMapaddItem( tr( "Quartic (biweight)" ), Heatmap::Quartic ); + mKernelShapeCombo->addItem( tr( "Triangular" ), Heatmap::Triangular ); + mKernelShapeCombo->addItem( tr( "Uniform" ), Heatmap::Uniform ); + mKernelShapeCombo->addItem( tr( "Triweight" ), Heatmap::Triweight ); + mKernelShapeCombo->addItem( tr( "Epanechnikov" ), Heatmap::Epanechnikov ); + + mOutputValuesComboBox->addItem( tr( "Raw values" ), Heatmap::Raw ); + mOutputValuesComboBox->addItem( tr( "Scaled by kernel size" ), Heatmap::Scaled ); + mHeatmapSessionSettings = temporarySettings; // Adding point layers to the inputLayerCombo @@ -124,6 +133,7 @@ void HeatmapGui::blockAllSignals( bool b ) mColumnsSpinBox->blockSignals( b ); mCellXLineEdit->blockSignals( b ); mCellYLineEdit->blockSignals( b ); + mOutputValuesComboBox->blockSignals( b ); } /* @@ -184,10 +194,15 @@ void HeatmapGui::restoreSettings( bool usingLastInputLayer ) // Kernel setting - not layer specific if ( mHeatmapSessionSettings->value( QString( "lastKernel" ) ).toInt() ) { - mKernelShapeCombo->setCurrentIndex( mHeatmapSessionSettings->value( QString( "lastKernel" ) ).toInt() ); + mKernelShapeCombo->setCurrentIndex( mKernelShapeCombo->findData( + ( Heatmap::KernelShape )( mHeatmapSessionSettings->value( QString( "lastKernel" ) ).toInt() ) ) ); mDecayLineEdit->setText( mHeatmapSessionSettings->value( QString( "decayRatio" ) ).toString() ); - mDecayLineEdit->setEnabled( mAdvancedGroupBox->isChecked() && mKernelShapeCombo->currentIndex() == Heatmap::Triangular ); + mDecayLineEdit->setEnabled( mAdvancedGroupBox->isChecked() && + ( Heatmap::KernelShape )( mKernelShapeCombo->itemData( mKernelShapeCombo->currentIndex() ).toInt() ) == Heatmap::Triangular ); } + mOutputValuesComboBox->setCurrentIndex( mOutputValuesComboBox->findData( + ( Heatmap::OutputValues )( mHeatmapSessionSettings->value( QString( "lastOutputValues" ), "0" ).toInt() ) ) ); + } void HeatmapGui::saveSettings() @@ -203,13 +218,14 @@ void HeatmapGui::saveSettings() mHeatmapSessionSettings->insert( QString( "lastRadiusUnit" ), QVariant( mBufferUnitCombo->currentIndex() ) ); mHeatmapSessionSettings->insert( QString( "advancedEnabled" ), QVariant( mAdvancedGroupBox->isChecked() ) ); mHeatmapSessionSettings->insert( QString( "lastRows" ), QVariant( mRowsSpinBox->value() ) ); - mHeatmapSessionSettings->insert( QString( "lastKernel" ), QVariant( mKernelShapeCombo->currentIndex() ) ); + mHeatmapSessionSettings->insert( QString( "lastKernel" ), QVariant( mKernelShapeCombo->itemData( mKernelShapeCombo->currentIndex() ).toInt() ) ); mHeatmapSessionSettings->insert( QString( "useRadius" ), QVariant( mRadiusFieldCheckBox->isChecked() ) ); mHeatmapSessionSettings->insert( QString( "radiusField" ), QVariant( mRadiusFieldCombo->currentField() ) ); mHeatmapSessionSettings->insert( QString( "radiusFieldUnit" ), QVariant( mRadiusFieldUnitCombo->currentIndex() ) ); mHeatmapSessionSettings->insert( QString( "useWeight" ), QVariant( mWeightFieldCheckBox->isChecked() ) ); mHeatmapSessionSettings->insert( QString( "weightField" ), QVariant( mWeightFieldCombo->currentField() ) ); mHeatmapSessionSettings->insert( QString( "decayRatio" ), QVariant( mDecayLineEdit->text() ) ); + mHeatmapSessionSettings->insert( QString( "lastOutputValues" ), QVariant( mOutputValuesComboBox->itemData( mOutputValuesComboBox->currentIndex() ).toInt() ) ); } void HeatmapGui::on_mButtonBox_rejected() @@ -260,7 +276,8 @@ void HeatmapGui::on_mAdvancedGroupBox_toggled( bool enabled ) } updateBBox(); - mDecayLineEdit->setEnabled( mKernelShapeCombo->currentIndex() == Heatmap::Triangular ); + mDecayLineEdit->setEnabled(( Heatmap::KernelShape )( mKernelShapeCombo->itemData( mKernelShapeCombo->currentIndex() ).toInt() ) == Heatmap::Triangular ); + } } @@ -464,7 +481,7 @@ void HeatmapGui::updateBBox() updateSize(); } -double HeatmapGui::mapUnitsOf( double meters, QgsCoordinateReferenceSystem layerCrs ) +double HeatmapGui::mapUnitsOf( double meters, QgsCoordinateReferenceSystem layerCrs ) const { // converter function to transform metres input to mapunits // so that bounding box can be updated @@ -485,17 +502,17 @@ double HeatmapGui::mapUnitsOf( double meters, QgsCoordinateReferenceSystem layer * */ -bool HeatmapGui::weighted() +bool HeatmapGui::weighted() const { return mWeightFieldCheckBox->isChecked(); } -bool HeatmapGui::variableRadius() +bool HeatmapGui::variableRadius() const { return mRadiusFieldCheckBox->isChecked(); } -double HeatmapGui::radius() +double HeatmapGui::radius() const { double radius = mBufferSizeLineEdit->text().toDouble(); if ( mBufferUnitCombo->currentIndex() == HeatmapGui::Meters ) @@ -505,7 +522,7 @@ double HeatmapGui::radius() return radius; } -int HeatmapGui::radiusUnit() +int HeatmapGui::radiusUnit() const { if ( mRadiusFieldCheckBox->isChecked() ) { @@ -514,17 +531,22 @@ int HeatmapGui::radiusUnit() return mBufferUnitCombo->currentIndex(); } -int HeatmapGui::kernelShape() +Heatmap::KernelShape HeatmapGui::kernelShape() const { - return mKernelShapeCombo->currentIndex(); + return ( Heatmap::KernelShape ) mKernelShapeCombo->itemData( mKernelShapeCombo->currentIndex() ).toInt(); } -double HeatmapGui::decayRatio() +Heatmap::OutputValues HeatmapGui::outputValues() const +{ + return ( Heatmap::OutputValues ) mOutputValuesComboBox->itemData( mOutputValuesComboBox->currentIndex() ).toInt(); +} + +double HeatmapGui::decayRatio() const { return mDecayLineEdit->text().toDouble(); } -int HeatmapGui::radiusField() +int HeatmapGui::radiusField() const { QgsVectorLayer *inputLayer = inputVectorLayer(); if ( !inputLayer ) @@ -533,7 +555,7 @@ int HeatmapGui::radiusField() return inputLayer->pendingFields().indexFromName( mRadiusFieldCombo->currentField() ); } -int HeatmapGui::weightField() +int HeatmapGui::weightField() const { QgsVectorLayer *inputLayer = inputVectorLayer(); if ( !inputLayer ) @@ -542,12 +564,12 @@ int HeatmapGui::weightField() return inputLayer->pendingFields().indexFromName( mWeightFieldCombo->currentField() ); } -bool HeatmapGui::addToCanvas() +bool HeatmapGui::addToCanvas() const { return mAddToCanvas->isChecked(); } -QString HeatmapGui::outputFilename() +QString HeatmapGui::outputFilename() const { QString outputFileName; QString outputFormat; @@ -582,7 +604,7 @@ QString HeatmapGui::outputFilename() return outputFileName; } -QString HeatmapGui::outputFormat() +QString HeatmapGui::outputFormat() const { return mFormatCombo->itemData( mFormatCombo->currentIndex() ).toString(); } diff --git a/src/plugins/heatmap/heatmapgui.h b/src/plugins/heatmap/heatmapgui.h index bf757e1963d..18828272f44 100644 --- a/src/plugins/heatmap/heatmapgui.h +++ b/src/plugins/heatmap/heatmapgui.h @@ -15,6 +15,7 @@ #include #include +#include "heatmap.h" #include "qgsvectorlayer.h" #include "qgscoordinatereferencesystem.h" #include "qgsgeometry.h" @@ -37,55 +38,58 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase }; /** Returns whether to apply weighted heat */ - bool weighted(); + bool weighted() const; /** Returns whether the radius is static or based on a field */ - bool variableRadius(); + bool variableRadius() const; /** Returns the fixed radius value */ - double radius(); + double radius() const; /** Return the radius Unit (meters/map units) */ - int radiusUnit(); + int radiusUnit() const; /** Return the selected kernel shape */ - int kernelShape(); + Heatmap::KernelShape kernelShape() const; + + /** Return the selected output values setting */ + Heatmap::OutputValues outputValues() const; /** Return the decay ratio */ - double decayRatio(); + double decayRatio() const; /** Return the attribute field for variable radius */ - int radiusField(); + int radiusField() const; /** Returns the attrinute field for weighted heat */ - int weightField(); + int weightField() const; /** Returns state of the add to canvas checkbox*/ - bool addToCanvas(); + bool addToCanvas() const; /** Returns the output filename/path */ - QString outputFilename(); + QString outputFilename() const; /** Returns the GDAL Format for output raster */ - QString outputFormat(); + QString outputFormat() const; /** Returns the input Vector layer */ QgsVectorLayer* inputVectorLayer() const; /** Returns the no of rows for the raster */ - int rows() { return mRows; } + int rows() const { return mRows; } /** Returns the no of columns in the raster */ - int columns() { return mColumns; } + int columns() const { return mColumns; } /** Returns the cell size X value */ - double cellSizeX() { return mXcellsize; } + double cellSizeX() const { return mXcellsize; } /** Returns the cell size Y valuue */ - double cellSizeY() { return mYcellsize; } + double cellSizeY() const { return mYcellsize; } /** Return the BBox */ - QgsRectangle bbox() { return mBBox; } + QgsRectangle bbox() const { return mBBox; } private: QMap mExtensionMap; @@ -116,7 +120,7 @@ class HeatmapGui : public QDialog, private Ui::HeatmapGuiBase void updateSize(); /** Convert Maters value to the corresponding map units based on Layer projection */ - double mapUnitsOf( double meters, QgsCoordinateReferenceSystem layerCrs ); + double mapUnitsOf( double meters, QgsCoordinateReferenceSystem layerCrs ) const; /** Estimate a reasonable starting value for the radius field */ double estimateRadius(); diff --git a/src/plugins/heatmap/heatmapguibase.ui b/src/plugins/heatmap/heatmapguibase.ui index d6fc4c7917e..11b02cef844 100644 --- a/src/plugins/heatmap/heatmapguibase.ui +++ b/src/plugins/heatmap/heatmapguibase.ui @@ -7,7 +7,7 @@ 0 0 460 - 447 + 493 @@ -133,108 +133,6 @@ false - - - - - - Use radius from field - - - - - - - false - - - - - - - false - - - - meters - - - - - map units - - - - - - - - Use weight from field - - - - - - - false - - - - - - - false - - - 0.0 - - - - - - - Decay ratio - - - - - - - - Quartic (biweight) - - - - - Triangular - - - - - Uniform - - - - - Triweight - - - - - Epanechnikov - - - - - - - - Kernel shape - - - - - @@ -314,6 +212,92 @@ + + + + + + Use weight from field + + + + + + + false + + + + + + + Use radius from field + + + + + + + + + + false + + + 0.0 + + + + + + + Decay ratio + + + + + + + Kernel shape + + + + + + + false + + + + meters + + + + + map units + + + + + + + + + + + Output values + + + + + + + false + + + + + @@ -411,22 +395,6 @@ - - mWeightFieldCheckBox - toggled(bool) - mWeightFieldCombo - setEnabled(bool) - - - 109 - 315 - - - 408 - 318 - - - mRadiusFieldCheckBox toggled(bool) @@ -507,5 +475,21 @@ + + mWeightFieldCheckBox + toggled(bool) + mWeightFieldCombo + setEnabled(bool) + + + 109 + 315 + + + 408 + 318 + + +