Skip to content

Commit d1c89fe

Browse files
committedJan 1, 2019
add cell size option to IDW and TIN interpolation algorithms
(cherry picked from commit 5813b96)
1 parent 816bb2d commit d1c89fe

File tree

3 files changed

+214
-26
lines changed

3 files changed

+214
-26
lines changed
 

‎python/plugins/processing/algs/qgis/IdwInterpolation.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
QgsGridFileWriter)
4141

4242
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
43-
from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData
43+
from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData, ParameterPixelSize
4444

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

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

5050
INTERPOLATION_DATA = 'INTERPOLATION_DATA'
5151
DISTANCE_COEFFICIENT = 'DISTANCE_COEFFICIENT'
52-
COLUMNS = 'COLUMNS'
53-
ROWS = 'ROWS'
52+
PIXEL_SIZE = 'PIXEL_SIZE'
5453
EXTENT = 'EXTENT'
5554
OUTPUT = 'OUTPUT'
5655

@@ -73,15 +72,17 @@ def initAlgorithm(self, config=None):
7372
self.addParameter(QgsProcessingParameterNumber(self.DISTANCE_COEFFICIENT,
7473
self.tr('Distance coefficient P'), type=QgsProcessingParameterNumber.Double,
7574
minValue=0.0, maxValue=99.99, defaultValue=2.0))
76-
self.addParameter(QgsProcessingParameterNumber(self.COLUMNS,
77-
self.tr('Number of columns'),
78-
minValue=0, maxValue=10000000, defaultValue=300))
79-
self.addParameter(QgsProcessingParameterNumber(self.ROWS,
80-
self.tr('Number of rows'),
81-
minValue=0, maxValue=10000000, defaultValue=300))
8275
self.addParameter(QgsProcessingParameterExtent(self.EXTENT,
8376
self.tr('Extent'),
8477
optional=False))
78+
pixel_size_param = ParameterPixelSize(self.PIXEL_SIZE,
79+
self.tr('Output raster size'),
80+
layersData=self.INTERPOLATION_DATA,
81+
extent=self.EXTENT,
82+
minValue=0.0,
83+
default=0.1)
84+
self.addParameter(pixel_size_param)
85+
8586
self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT,
8687
self.tr('Interpolated')))
8788

@@ -94,9 +95,8 @@ def displayName(self):
9495
def processAlgorithm(self, parameters, context, feedback):
9596
interpolationData = ParameterInterpolationData.parseValue(parameters[self.INTERPOLATION_DATA])
9697
coefficient = self.parameterAsDouble(parameters, self.DISTANCE_COEFFICIENT, context)
97-
columns = self.parameterAsInt(parameters, self.COLUMNS, context)
98-
rows = self.parameterAsInt(parameters, self.ROWS, context)
9998
bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
99+
pixel_size = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context)
100100
output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
101101

102102
if interpolationData is None:
@@ -127,6 +127,9 @@ def processAlgorithm(self, parameters, context, feedback):
127127
interpolator = QgsIDWInterpolator(layerData)
128128
interpolator.setDistanceCoefficient(coefficient)
129129

130+
rows = max(round(bbox.height() / pixel_size) + 1, 1)
131+
columns = max(round(bbox.width() / pixel_size) + 1, 1)
132+
130133
writer = QgsGridFileWriter(interpolator,
131134
output,
132135
bbox,

‎python/plugins/processing/algs/qgis/TinInterpolation.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,15 @@
4444
QgsGridFileWriter)
4545

4646
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
47-
from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData
47+
from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData, ParameterPixelSize
4848

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

5151

5252
class TinInterpolation(QgisAlgorithm):
5353
INTERPOLATION_DATA = 'INTERPOLATION_DATA'
5454
METHOD = 'METHOD'
55-
COLUMNS = 'COLUMNS'
56-
ROWS = 'ROWS'
55+
PIXEL_SIZE = 'PIXEL_SIZE'
5756
EXTENT = 'EXTENT'
5857
OUTPUT = 'OUTPUT'
5958
TRIANGULATION = 'TRIANGULATION'
@@ -81,15 +80,17 @@ def initAlgorithm(self, config=None):
8180
self.tr('Interpolation method'),
8281
options=self.METHODS,
8382
defaultValue=0))
84-
self.addParameter(QgsProcessingParameterNumber(self.COLUMNS,
85-
self.tr('Number of columns'),
86-
minValue=0, maxValue=10000000, defaultValue=300))
87-
self.addParameter(QgsProcessingParameterNumber(self.ROWS,
88-
self.tr('Number of rows'),
89-
minValue=0, maxValue=10000000, defaultValue=300))
9083
self.addParameter(QgsProcessingParameterExtent(self.EXTENT,
9184
self.tr('Extent'),
9285
optional=False))
86+
pixel_size_param = ParameterPixelSize(self.PIXEL_SIZE,
87+
self.tr('Output raster size'),
88+
layersData=self.INTERPOLATION_DATA,
89+
extent=self.EXTENT,
90+
minValue=0.0,
91+
default=0.1)
92+
self.addParameter(pixel_size_param)
93+
9394
self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT,
9495
self.tr('Interpolated')))
9596

@@ -109,9 +110,8 @@ def displayName(self):
109110
def processAlgorithm(self, parameters, context, feedback):
110111
interpolationData = ParameterInterpolationData.parseValue(parameters[self.INTERPOLATION_DATA])
111112
method = self.parameterAsEnum(parameters, self.METHOD, context)
112-
columns = self.parameterAsInt(parameters, self.COLUMNS, context)
113-
rows = self.parameterAsInt(parameters, self.ROWS, context)
114113
bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
114+
pixel_size = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context)
115115
output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
116116

117117
if interpolationData is None:
@@ -154,6 +154,9 @@ def processAlgorithm(self, parameters, context, feedback):
154154
if triangulation_sink is not None:
155155
interpolator.setTriangulationSink(triangulation_sink)
156156

157+
rows = max(round(bbox.height() / pixel_size) + 1, 1)
158+
columns = max(round(bbox.width() / pixel_size) + 1, 1)
159+
157160
writer = QgsGridFileWriter(interpolator,
158161
output,
159162
bbox,

‎python/plugins/processing/algs/qgis/ui/InterpolationWidgets.py

Lines changed: 186 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,25 @@
2828
import os
2929

3030
from qgis.PyQt import uic
31-
from qgis.PyQt.QtCore import pyqtSlot
31+
from qgis.PyQt.QtCore import pyqtSignal
3232
from qgis.PyQt.QtWidgets import (QTreeWidgetItem,
3333
QComboBox
3434
)
3535
from qgis.core import (QgsApplication,
3636
QgsMapLayer,
3737
QgsMapLayerProxyModel,
3838
QgsWkbTypes,
39+
QgsRectangle,
40+
QgsReferencedRectangle,
41+
QgsCoordinateReferenceSystem,
3942
QgsProcessingUtils,
43+
QgsProcessingParameterNumber,
4044
QgsProcessingParameterDefinition
4145
)
4246
from qgis.core import QgsFieldProxyModel
4347
from qgis.analysis import QgsInterpolator
4448

45-
from processing.gui.wrappers import WidgetWrapper
49+
from processing.gui.wrappers import WidgetWrapper, DIALOG_STANDARD
4650
from processing.tools import dataobjects
4751

4852
pluginPath = os.path.dirname(__file__)
@@ -53,7 +57,7 @@ class ParameterInterpolationData(QgsProcessingParameterDefinition):
5357
def __init__(self, name='', description=''):
5458
super().__init__(name, description)
5559
self.setMetadata({
56-
'widget_wrapper': 'processing.algs.qgis.ui.InterpolationDataWidget.InterpolationDataWidgetWrapper'
60+
'widget_wrapper': 'processing.algs.qgis.ui.InterpolationWidgets.InterpolationDataWidgetWrapper'
5761
})
5862

5963
def type(self):
@@ -91,7 +95,7 @@ def dataToString(data):
9195

9296
class InterpolationDataWidget(BASE, WIDGET):
9397

94-
hasChanged = pyqtSlot()
98+
hasChanged = pyqtSignal()
9599

96100
def __init__(self):
97101
super(InterpolationDataWidget, self).__init__(None)
@@ -218,3 +222,181 @@ def setValue(self, value):
218222

219223
def value(self):
220224
return self.widget.value()
225+
226+
227+
class ParameterPixelSize(QgsProcessingParameterNumber):
228+
229+
def __init__(self, name='', description='', layersData=None, extent=None, minValue=None, default=None, optional=False):
230+
QgsProcessingParameterNumber.__init__(self, name, description, QgsProcessingParameterNumber.Double, default, optional, minValue)
231+
self.setMetadata({
232+
'widget_wrapper': 'processing.algs.qgis.ui.InterpolationWidgets.PixelSizeWidgetWrapper'
233+
})
234+
235+
self.layersData = layersData
236+
self.extent = extent
237+
self.layers = []
238+
239+
def clone(self):
240+
copy = ParameterPixelSize(self.name(), self.description(), self.layersData, self.extent, self.minimum(), self.defaultValue(), self.flags() & QgsProcessingParameterDefinition.FlagOptional)
241+
return copy
242+
243+
244+
WIDGET, BASE = uic.loadUiType(os.path.join(pluginPath, 'RasterResolutionWidget.ui'))
245+
246+
247+
class PixelSizeWidget(BASE, WIDGET):
248+
249+
def __init__(self):
250+
super(PixelSizeWidget, self).__init__(None)
251+
self.setupUi(self)
252+
self.context = dataobjects.createContext()
253+
254+
self.extent = QgsRectangle()
255+
self.layers = []
256+
257+
self.mCellXSpinBox.setShowClearButton(False)
258+
self.mCellYSpinBox.setShowClearButton(False)
259+
self.mRowsSpinBox.setShowClearButton(False)
260+
self.mColumnsSpinBox.setShowClearButton(False)
261+
262+
self.mCellYSpinBox.valueChanged.connect(self.mCellXSpinBox.setValue)
263+
self.mCellXSpinBox.valueChanged.connect(self.pixelSizeChanged)
264+
self.mRowsSpinBox.valueChanged.connect(self.rowsChanged)
265+
self.mColumnsSpinBox.valueChanged.connect(self.columnsChanged)
266+
267+
def setLayers(self, layersData):
268+
self.extent = QgsRectangle()
269+
self.layers = []
270+
for row in layersData.split(';'):
271+
v = row.split('::~::')
272+
# need to keep a reference until interpolation is complete
273+
layer = QgsProcessingUtils.variantToSource(v[0], self.context)
274+
if layer:
275+
self.layers.append(layer)
276+
bbox = layer.sourceExtent()
277+
if self.extent.isEmpty():
278+
self.extent = bbox
279+
else:
280+
self.extent.combineExtentWith(bbox)
281+
282+
self.pixelSizeChanged()
283+
284+
def setExtent(self, extent):
285+
if extent is not None:
286+
tokens = extent.split(' ')[0].split(',')
287+
ext = QgsRectangle(float(tokens[0]), float(tokens[2]), float(tokens[1]), float(tokens[3]))
288+
if len(tokens) > 1:
289+
self.extent = QgsReferencedRectangle(ext, QgsCoordinateReferenceSystem(tokens[1][1:-1]))
290+
else:
291+
self.extent = ext
292+
self.pixelSizeChanged()
293+
294+
def pixelSizeChanged(self):
295+
cell_size = self.mCellXSpinBox.value()
296+
if cell_size <= 0:
297+
return
298+
299+
self.mCellYSpinBox.blockSignals(True)
300+
self.mCellYSpinBox.setValue(cell_size)
301+
self.mCellYSpinBox.blockSignals(False)
302+
rows = max(round(self.extent.height() / cell_size) + 1, 1)
303+
cols = max(round(self.extent.width() / cell_size) + 1, 1)
304+
self.mRowsSpinBox.blockSignals(True)
305+
self.mRowsSpinBox.setValue(rows)
306+
self.mRowsSpinBox.blockSignals(False)
307+
self.mColumnsSpinBox.blockSignals(True)
308+
self.mColumnsSpinBox.setValue(cols)
309+
self.mColumnsSpinBox.blockSignals(False)
310+
311+
def rowsChanged(self):
312+
rows = self.mRowsSpinBox.value()
313+
if rows <= 0:
314+
return
315+
cell_size = self.extent.height() / rows
316+
cols = max(round(self.extent.width() / cell_size) + 1, 1)
317+
self.mColumnsSpinBox.blockSignals(True)
318+
self.mColumnsSpinBox.setValue(cols)
319+
self.mColumnsSpinBox.blockSignals(False)
320+
for w in [self.mCellXSpinBox, self.mCellYSpinBox]:
321+
w.blockSignals(True)
322+
w.setValue(cell_size)
323+
w.blockSignals(False)
324+
325+
def columnsChanged(self):
326+
cols = self.mColumnsSpinBox.value()
327+
if cols < 2:
328+
return
329+
cell_size = self.extent.width() / (cols - 1)
330+
rows = max(round(self.extent.height() / cell_size), 1)
331+
self.mRowsSpinBox.blockSignals(True)
332+
self.mRowsSpinBox.setValue(rows)
333+
self.mRowsSpinBox.blockSignals(False)
334+
for w in [self.mCellXSpinBox, self.mCellYSpinBox]:
335+
w.blockSignals(True)
336+
w.setValue(cell_size)
337+
w.blockSignals(False)
338+
339+
def setValue(self, value):
340+
try:
341+
numeric_value = float(value)
342+
except:
343+
return False
344+
345+
self.mCellXSpinBox.setValue(numeric_value)
346+
self.mCellYSpinBox.setValue(numeric_value)
347+
return True
348+
349+
def value(self):
350+
return self.mCellXSpinBox.value()
351+
352+
353+
class PixelSizeWidgetWrapper(WidgetWrapper):
354+
355+
def __init__(self, param, dialog, row=0, col=0, **kwargs):
356+
super().__init__(param, dialog, row, col, **kwargs)
357+
self.context = dataobjects.createContext()
358+
359+
def _panel(self):
360+
return PixelSizeWidget()
361+
362+
def createWidget(self):
363+
if self.dialogType == DIALOG_STANDARD:
364+
return self._panel()
365+
else:
366+
w = QgsDoubleSpinBox()
367+
w.setShowClearButton(False)
368+
w.setMinimum(0)
369+
w.setMaximum(99999999999)
370+
w.setDecimals(6)
371+
w.setToolTip(self.tr('Resolution of each pixel in output raster, in layer units'))
372+
return w
373+
374+
def postInitialize(self, wrappers):
375+
if self.dialogType != DIALOG_STANDARD:
376+
return
377+
378+
for wrapper in wrappers:
379+
if wrapper.parameterDefinition().name() == self.param.layersData:
380+
self.setLayers(wrapper.parameterValue())
381+
wrapper.widgetValueHasChanged.connect(self.layersChanged)
382+
elif wrapper.parameterDefinition().name() == self.param.extent:
383+
self.setExtent(wrapper.parameterValue())
384+
wrapper.widgetValueHasChanged.connect(self.extentChanged)
385+
386+
def layersChanged(self, wrapper):
387+
self.setLayers(wrapper.parameterValue())
388+
389+
def setLayers(self, layersData):
390+
self.widget.setLayers(layersData)
391+
392+
def extentChanged(self, wrapper):
393+
self.setExtent(wrapper.parameterValue())
394+
395+
def setExtent(self, extent):
396+
self.widget.setExtent(extent)
397+
398+
def setValue(self, value):
399+
return self.widget.setValue(value)
400+
401+
def value(self):
402+
return self.widget.value()

0 commit comments

Comments
 (0)
Please sign in to comment.