187 lines
8.4 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""
***************************************************************************
Heatmap.py
---------------------
Date : November 2016
Copyright : (C) 2016 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""
__author__ = 'Nyall Dawson'
__date__ = 'November 2016'
__copyright__ = '(C) 2016, Nyall Dawson'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import os
from qgis.PyQt.QtGui import QIcon
from qgis.core import (QgsFeatureRequest,
QgsMessageLog,
2017-05-16 16:36:00 +10:00
QgsProcessingUtils,
QgsProcessingParameterDefinition)
from qgis.analysis import QgsKernelDensityEstimation
2017-06-06 13:41:42 +10:00
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterNumber
from processing.core.parameters import ParameterSelection
from processing.core.parameters import ParameterTableField
from processing.core.outputs import OutputRaster
2017-05-02 14:47:58 +10:00
from processing.tools import dataobjects, raster
from processing.algs.qgis.ui.HeatmapWidgets import HeatmapPixelSizeWidgetWrapper
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class Heatmap(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER'
RADIUS = 'RADIUS'
RADIUS_FIELD = 'RADIUS_FIELD'
WEIGHT_FIELD = 'WEIGHT_FIELD'
PIXEL_SIZE = 'PIXEL_SIZE'
KERNELS = ['Quartic',
'Triangular',
'Uniform',
'Triweight',
'Epanechnikov'
]
KERNEL = 'KERNEL'
DECAY = 'DECAY'
OUTPUT_VALUES = ['Raw',
'Scaled'
]
OUTPUT_VALUE = 'OUTPUT_VALUE'
OUTPUT_LAYER = 'OUTPUT_LAYER'
def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'heatmap.png'))
def tags(self):
return self.tr('heatmap,kde,hotspot').split(',')
def group(self):
return self.tr('Interpolation')
def __init__(self):
super().__init__()
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Point layer'), [dataobjects.TYPE_VECTOR_POINT]))
self.addParameter(ParameterNumber(self.RADIUS,
self.tr('Radius (layer units)'),
0.0, 9999999999, 100.0))
radius_field_param = ParameterTableField(self.RADIUS_FIELD,
self.tr('Radius from field'), self.INPUT_LAYER, optional=True, datatype=ParameterTableField.DATA_TYPE_NUMBER)
2017-05-16 16:36:00 +10:00
radius_field_param.setFlags(radius_field_param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
self.addParameter(radius_field_param)
class ParameterHeatmapPixelSize(ParameterNumber):
def __init__(self, name='', description='', parent_layer=None, radius_param=None, radius_field_param=None, minValue=None, maxValue=None,
default=None, optional=False, metadata={}):
ParameterNumber.__init__(self, name, description, minValue, maxValue, default, optional, metadata)
self.parent_layer = parent_layer
self.radius_param = radius_param
self.radius_field_param = radius_field_param
self.addParameter(ParameterHeatmapPixelSize(self.PIXEL_SIZE,
self.tr('Output raster size'), parent_layer=self.INPUT_LAYER, radius_param=self.RADIUS,
radius_field_param=self.RADIUS_FIELD,
minValue=0.0, maxValue=9999999999, default=0.1,
metadata={'widget_wrapper': HeatmapPixelSizeWidgetWrapper}))
weight_field_param = ParameterTableField(self.WEIGHT_FIELD,
self.tr('Weight from field'), self.INPUT_LAYER, optional=True, datatype=ParameterTableField.DATA_TYPE_NUMBER)
2017-05-16 16:36:00 +10:00
weight_field_param.setFlags(weight_field_param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
self.addParameter(weight_field_param)
kernel_shape_param = ParameterSelection(self.KERNEL,
self.tr('Kernel shape'), self.KERNELS)
2017-05-16 16:36:00 +10:00
kernel_shape_param.setFlags(kernel_shape_param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
self.addParameter(kernel_shape_param)
decay_ratio = ParameterNumber(self.DECAY,
self.tr('Decay ratio (Triangular kernels only)'),
-100.0, 100.0, 0.0)
2017-05-16 16:36:00 +10:00
decay_ratio.setFlags(decay_ratio.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
self.addParameter(decay_ratio)
output_scaling = ParameterSelection(self.OUTPUT_VALUE,
self.tr('Output value scaling'), self.OUTPUT_VALUES)
2017-05-16 16:36:00 +10:00
output_scaling.setFlags(output_scaling.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
self.addParameter(output_scaling)
self.addOutput(OutputRaster(self.OUTPUT_LAYER,
self.tr('Heatmap')))
def name(self):
return 'heatmapkerneldensityestimation'
def displayName(self):
return self.tr('Heatmap (Kernel Density Estimation)')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
radius = self.getParameterValue(self.RADIUS)
kernel_shape = self.getParameterValue(self.KERNEL)
pixel_size = self.getParameterValue(self.PIXEL_SIZE)
decay = self.getParameterValue(self.DECAY)
output_values = self.getParameterValue(self.OUTPUT_VALUE)
output = self.getOutputValue(self.OUTPUT_LAYER)
output_format = raster.formatShortNameFromFileName(output)
weight_field = self.getParameterValue(self.WEIGHT_FIELD)
radius_field = self.getParameterValue(self.RADIUS_FIELD)
attrs = []
kde_params = QgsKernelDensityEstimation.Parameters()
kde_params.vectorLayer = layer
kde_params.radius = radius
kde_params.pixelSize = pixel_size
# radius field
if radius_field:
kde_params.radiusField = radius_field
attrs.append(layer.fields().lookupField(radius_field))
# weight field
if weight_field:
kde_params.weightField = weight_field
attrs.append(layer.fields().lookupField(weight_field))
kde_params.shape = kernel_shape
kde_params.decayRatio = decay
kde_params.outputValues = output_values
kde = QgsKernelDensityEstimation(kde_params, output, output_format)
if kde.prepare() != QgsKernelDensityEstimation.Success:
raise GeoAlgorithmExecutionException(
self.tr('Could not create destination layer'))
request = QgsFeatureRequest()
request.setSubsetOfAttributes(attrs)
features = QgsProcessingUtils.getFeatures(layer, context, request)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
for current, f in enumerate(features):
if kde.addFeature(f) != QgsKernelDensityEstimation.Success:
QgsMessageLog.logMessage(self.tr('Error adding feature with ID {} to heatmap').format(f.id()), self.tr('Processing'), QgsMessageLog.CRITICAL)
feedback.setProgress(int(current * total))
if kde.finalise() != QgsKernelDensityEstimation.Success:
raise GeoAlgorithmExecutionException(
self.tr('Could not save destination layer'))