Skip to content

Commit

Permalink
add cell size option to IDW and TIN interpolation algorithms
Browse files Browse the repository at this point in the history
(cherry picked from commit 5813b96)
  • Loading branch information
alexbruy committed Jan 1, 2019
1 parent 816bb2d commit d1c89fe
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 26 deletions.
25 changes: 14 additions & 11 deletions python/plugins/processing/algs/qgis/IdwInterpolation.py
Expand Up @@ -40,7 +40,7 @@
QgsGridFileWriter)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData
from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData, ParameterPixelSize

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]

Expand All @@ -49,8 +49,7 @@ class IdwInterpolation(QgisAlgorithm):

INTERPOLATION_DATA = 'INTERPOLATION_DATA'
DISTANCE_COEFFICIENT = 'DISTANCE_COEFFICIENT'
COLUMNS = 'COLUMNS'
ROWS = 'ROWS'
PIXEL_SIZE = 'PIXEL_SIZE'
EXTENT = 'EXTENT'
OUTPUT = 'OUTPUT'

Expand All @@ -73,15 +72,17 @@ def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterNumber(self.DISTANCE_COEFFICIENT,
self.tr('Distance coefficient P'), type=QgsProcessingParameterNumber.Double,
minValue=0.0, maxValue=99.99, defaultValue=2.0))
self.addParameter(QgsProcessingParameterNumber(self.COLUMNS,
self.tr('Number of columns'),
minValue=0, maxValue=10000000, defaultValue=300))
self.addParameter(QgsProcessingParameterNumber(self.ROWS,
self.tr('Number of rows'),
minValue=0, maxValue=10000000, defaultValue=300))
self.addParameter(QgsProcessingParameterExtent(self.EXTENT,
self.tr('Extent'),
optional=False))
pixel_size_param = ParameterPixelSize(self.PIXEL_SIZE,
self.tr('Output raster size'),
layersData=self.INTERPOLATION_DATA,
extent=self.EXTENT,
minValue=0.0,
default=0.1)
self.addParameter(pixel_size_param)

self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT,
self.tr('Interpolated')))

Expand All @@ -94,9 +95,8 @@ def displayName(self):
def processAlgorithm(self, parameters, context, feedback):
interpolationData = ParameterInterpolationData.parseValue(parameters[self.INTERPOLATION_DATA])
coefficient = self.parameterAsDouble(parameters, self.DISTANCE_COEFFICIENT, context)
columns = self.parameterAsInt(parameters, self.COLUMNS, context)
rows = self.parameterAsInt(parameters, self.ROWS, context)
bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
pixel_size = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context)
output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)

if interpolationData is None:
Expand Down Expand Up @@ -127,6 +127,9 @@ def processAlgorithm(self, parameters, context, feedback):
interpolator = QgsIDWInterpolator(layerData)
interpolator.setDistanceCoefficient(coefficient)

rows = max(round(bbox.height() / pixel_size) + 1, 1)
columns = max(round(bbox.width() / pixel_size) + 1, 1)

writer = QgsGridFileWriter(interpolator,
output,
bbox,
Expand Down
25 changes: 14 additions & 11 deletions python/plugins/processing/algs/qgis/TinInterpolation.py
Expand Up @@ -44,16 +44,15 @@
QgsGridFileWriter)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData
from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData, ParameterPixelSize

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]


class TinInterpolation(QgisAlgorithm):
INTERPOLATION_DATA = 'INTERPOLATION_DATA'
METHOD = 'METHOD'
COLUMNS = 'COLUMNS'
ROWS = 'ROWS'
PIXEL_SIZE = 'PIXEL_SIZE'
EXTENT = 'EXTENT'
OUTPUT = 'OUTPUT'
TRIANGULATION = 'TRIANGULATION'
Expand Down Expand Up @@ -81,15 +80,17 @@ def initAlgorithm(self, config=None):
self.tr('Interpolation method'),
options=self.METHODS,
defaultValue=0))
self.addParameter(QgsProcessingParameterNumber(self.COLUMNS,
self.tr('Number of columns'),
minValue=0, maxValue=10000000, defaultValue=300))
self.addParameter(QgsProcessingParameterNumber(self.ROWS,
self.tr('Number of rows'),
minValue=0, maxValue=10000000, defaultValue=300))
self.addParameter(QgsProcessingParameterExtent(self.EXTENT,
self.tr('Extent'),
optional=False))
pixel_size_param = ParameterPixelSize(self.PIXEL_SIZE,
self.tr('Output raster size'),
layersData=self.INTERPOLATION_DATA,
extent=self.EXTENT,
minValue=0.0,
default=0.1)
self.addParameter(pixel_size_param)

self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT,
self.tr('Interpolated')))

Expand All @@ -109,9 +110,8 @@ def displayName(self):
def processAlgorithm(self, parameters, context, feedback):
interpolationData = ParameterInterpolationData.parseValue(parameters[self.INTERPOLATION_DATA])
method = self.parameterAsEnum(parameters, self.METHOD, context)
columns = self.parameterAsInt(parameters, self.COLUMNS, context)
rows = self.parameterAsInt(parameters, self.ROWS, context)
bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
pixel_size = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context)
output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)

if interpolationData is None:
Expand Down Expand Up @@ -154,6 +154,9 @@ def processAlgorithm(self, parameters, context, feedback):
if triangulation_sink is not None:
interpolator.setTriangulationSink(triangulation_sink)

rows = max(round(bbox.height() / pixel_size) + 1, 1)
columns = max(round(bbox.width() / pixel_size) + 1, 1)

writer = QgsGridFileWriter(interpolator,
output,
bbox,
Expand Down
190 changes: 186 additions & 4 deletions python/plugins/processing/algs/qgis/ui/InterpolationWidgets.py
Expand Up @@ -28,21 +28,25 @@
import os

from qgis.PyQt import uic
from qgis.PyQt.QtCore import pyqtSlot
from qgis.PyQt.QtCore import pyqtSignal
from qgis.PyQt.QtWidgets import (QTreeWidgetItem,
QComboBox
)
from qgis.core import (QgsApplication,
QgsMapLayer,
QgsMapLayerProxyModel,
QgsWkbTypes,
QgsRectangle,
QgsReferencedRectangle,
QgsCoordinateReferenceSystem,
QgsProcessingUtils,
QgsProcessingParameterNumber,
QgsProcessingParameterDefinition
)
from qgis.core import QgsFieldProxyModel
from qgis.analysis import QgsInterpolator

from processing.gui.wrappers import WidgetWrapper
from processing.gui.wrappers import WidgetWrapper, DIALOG_STANDARD
from processing.tools import dataobjects

pluginPath = os.path.dirname(__file__)
Expand All @@ -53,7 +57,7 @@ class ParameterInterpolationData(QgsProcessingParameterDefinition):
def __init__(self, name='', description=''):
super().__init__(name, description)
self.setMetadata({
'widget_wrapper': 'processing.algs.qgis.ui.InterpolationDataWidget.InterpolationDataWidgetWrapper'
'widget_wrapper': 'processing.algs.qgis.ui.InterpolationWidgets.InterpolationDataWidgetWrapper'
})

def type(self):
Expand Down Expand Up @@ -91,7 +95,7 @@ def dataToString(data):

class InterpolationDataWidget(BASE, WIDGET):

hasChanged = pyqtSlot()
hasChanged = pyqtSignal()

def __init__(self):
super(InterpolationDataWidget, self).__init__(None)
Expand Down Expand Up @@ -218,3 +222,181 @@ def setValue(self, value):

def value(self):
return self.widget.value()


class ParameterPixelSize(QgsProcessingParameterNumber):

def __init__(self, name='', description='', layersData=None, extent=None, minValue=None, default=None, optional=False):
QgsProcessingParameterNumber.__init__(self, name, description, QgsProcessingParameterNumber.Double, default, optional, minValue)
self.setMetadata({
'widget_wrapper': 'processing.algs.qgis.ui.InterpolationWidgets.PixelSizeWidgetWrapper'
})

self.layersData = layersData
self.extent = extent
self.layers = []

def clone(self):
copy = ParameterPixelSize(self.name(), self.description(), self.layersData, self.extent, self.minimum(), self.defaultValue(), self.flags() & QgsProcessingParameterDefinition.FlagOptional)
return copy


WIDGET, BASE = uic.loadUiType(os.path.join(pluginPath, 'RasterResolutionWidget.ui'))


class PixelSizeWidget(BASE, WIDGET):

def __init__(self):
super(PixelSizeWidget, self).__init__(None)
self.setupUi(self)
self.context = dataobjects.createContext()

self.extent = QgsRectangle()
self.layers = []

self.mCellXSpinBox.setShowClearButton(False)
self.mCellYSpinBox.setShowClearButton(False)
self.mRowsSpinBox.setShowClearButton(False)
self.mColumnsSpinBox.setShowClearButton(False)

self.mCellYSpinBox.valueChanged.connect(self.mCellXSpinBox.setValue)
self.mCellXSpinBox.valueChanged.connect(self.pixelSizeChanged)
self.mRowsSpinBox.valueChanged.connect(self.rowsChanged)
self.mColumnsSpinBox.valueChanged.connect(self.columnsChanged)

def setLayers(self, layersData):
self.extent = QgsRectangle()
self.layers = []
for row in layersData.split(';'):
v = row.split('::~::')
# need to keep a reference until interpolation is complete
layer = QgsProcessingUtils.variantToSource(v[0], self.context)
if layer:
self.layers.append(layer)
bbox = layer.sourceExtent()
if self.extent.isEmpty():
self.extent = bbox
else:
self.extent.combineExtentWith(bbox)

self.pixelSizeChanged()

def setExtent(self, extent):
if extent is not None:
tokens = extent.split(' ')[0].split(',')
ext = QgsRectangle(float(tokens[0]), float(tokens[2]), float(tokens[1]), float(tokens[3]))
if len(tokens) > 1:
self.extent = QgsReferencedRectangle(ext, QgsCoordinateReferenceSystem(tokens[1][1:-1]))
else:
self.extent = ext
self.pixelSizeChanged()

def pixelSizeChanged(self):
cell_size = self.mCellXSpinBox.value()
if cell_size <= 0:
return

self.mCellYSpinBox.blockSignals(True)
self.mCellYSpinBox.setValue(cell_size)
self.mCellYSpinBox.blockSignals(False)
rows = max(round(self.extent.height() / cell_size) + 1, 1)
cols = max(round(self.extent.width() / cell_size) + 1, 1)
self.mRowsSpinBox.blockSignals(True)
self.mRowsSpinBox.setValue(rows)
self.mRowsSpinBox.blockSignals(False)
self.mColumnsSpinBox.blockSignals(True)
self.mColumnsSpinBox.setValue(cols)
self.mColumnsSpinBox.blockSignals(False)

def rowsChanged(self):
rows = self.mRowsSpinBox.value()
if rows <= 0:
return
cell_size = self.extent.height() / rows
cols = max(round(self.extent.width() / cell_size) + 1, 1)
self.mColumnsSpinBox.blockSignals(True)
self.mColumnsSpinBox.setValue(cols)
self.mColumnsSpinBox.blockSignals(False)
for w in [self.mCellXSpinBox, self.mCellYSpinBox]:
w.blockSignals(True)
w.setValue(cell_size)
w.blockSignals(False)

def columnsChanged(self):
cols = self.mColumnsSpinBox.value()
if cols < 2:
return
cell_size = self.extent.width() / (cols - 1)
rows = max(round(self.extent.height() / cell_size), 1)
self.mRowsSpinBox.blockSignals(True)
self.mRowsSpinBox.setValue(rows)
self.mRowsSpinBox.blockSignals(False)
for w in [self.mCellXSpinBox, self.mCellYSpinBox]:
w.blockSignals(True)
w.setValue(cell_size)
w.blockSignals(False)

def setValue(self, value):
try:
numeric_value = float(value)
except:
return False

self.mCellXSpinBox.setValue(numeric_value)
self.mCellYSpinBox.setValue(numeric_value)
return True

def value(self):
return self.mCellXSpinBox.value()


class PixelSizeWidgetWrapper(WidgetWrapper):

def __init__(self, param, dialog, row=0, col=0, **kwargs):
super().__init__(param, dialog, row, col, **kwargs)
self.context = dataobjects.createContext()

def _panel(self):
return PixelSizeWidget()

def createWidget(self):
if self.dialogType == DIALOG_STANDARD:
return self._panel()
else:
w = QgsDoubleSpinBox()
w.setShowClearButton(False)
w.setMinimum(0)
w.setMaximum(99999999999)
w.setDecimals(6)
w.setToolTip(self.tr('Resolution of each pixel in output raster, in layer units'))
return w

def postInitialize(self, wrappers):
if self.dialogType != DIALOG_STANDARD:
return

for wrapper in wrappers:
if wrapper.parameterDefinition().name() == self.param.layersData:
self.setLayers(wrapper.parameterValue())
wrapper.widgetValueHasChanged.connect(self.layersChanged)
elif wrapper.parameterDefinition().name() == self.param.extent:
self.setExtent(wrapper.parameterValue())
wrapper.widgetValueHasChanged.connect(self.extentChanged)

def layersChanged(self, wrapper):
self.setLayers(wrapper.parameterValue())

def setLayers(self, layersData):
self.widget.setLayers(layersData)

def extentChanged(self, wrapper):
self.setExtent(wrapper.parameterValue())

def setExtent(self, extent):
self.widget.setExtent(extent)

def setValue(self, value):
return self.widget.setValue(value)

def value(self):
return self.widget.value()

0 comments on commit d1c89fe

Please sign in to comment.