diff --git a/python/plugins/processing/algs/grass7/Grass7Algorithm.py b/python/plugins/processing/algs/grass7/Grass7Algorithm.py index 6f9a29fa546..9def6636041 100644 --- a/python/plugins/processing/algs/grass7/Grass7Algorithm.py +++ b/python/plugins/processing/algs/grass7/Grass7Algorithm.py @@ -426,7 +426,7 @@ class Grass7Algorithm(GeoAlgorithm): command += ' ' + param.name elif isinstance(param, ParameterSelection): idx = int(param.value) - command += ' ' + param.name + '=' + str(param.options[idx]) + command += ' ' + param.name + '=' + str(param.options[idx][1]) elif isinstance(param, ParameterString): command += ' ' + param.name + '="' + str(param.value) + '"' elif isinstance(param, ParameterPoint): diff --git a/python/plugins/processing/algs/otb/OTBAlgorithm.py b/python/plugins/processing/algs/otb/OTBAlgorithm.py index 237c3cbd670..0d519d261bb 100644 --- a/python/plugins/processing/algs/otb/OTBAlgorithm.py +++ b/python/plugins/processing/algs/otb/OTBAlgorithm.py @@ -276,7 +276,7 @@ class OTBAlgorithm(GeoAlgorithm): elif isinstance(param, ParameterSelection): commands.append(param.name) idx = int(param.value) - commands.append(str(param.options[idx])) + commands.append(str(param.options[idx][1])) elif isinstance(param, ParameterBoolean): if param.value: commands.append(param.name) diff --git a/python/plugins/processing/algs/qgis/ExtractByLocation.py b/python/plugins/processing/algs/qgis/ExtractByLocation.py index 688e977d213..8dfd31119af 100644 --- a/python/plugins/processing/algs/qgis/ExtractByLocation.py +++ b/python/plugins/processing/algs/qgis/ExtractByLocation.py @@ -29,7 +29,7 @@ __revision__ = '$Format:%H$' from qgis.core import QgsFeatureRequest from processing.core.GeoAlgorithm import GeoAlgorithm from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterGeometryPredicate +from processing.core.parameters import ParameterSelection from processing.core.parameters import ParameterNumber from processing.core.outputs import OutputVector from processing.tools import dataobjects, vector @@ -48,13 +48,24 @@ class ExtractByLocation(GeoAlgorithm): self.group, self.i18n_group = self.trAlgorithm('Vector selection tools') self.tags = self.tr('extract,filter,location,intersects,contains,within') + self.predicates = ( + ('intersects', self.tr('intersects')), + ('contains', self.tr('contains')), + ('disjoint', self.tr('disjoint')), + ('equals', self.tr('equals')), + ('touches', self.tr('touches')), + ('overlaps', self.tr('overlaps')), + ('within', self.tr('within')), + ('crosses', self.tr('crosses'))) + self.addParameter(ParameterVector(self.INPUT, self.tr('Layer to select from'))) self.addParameter(ParameterVector(self.INTERSECT, self.tr('Additional layer (intersection layer)'))) - self.addParameter(ParameterGeometryPredicate(self.PREDICATE, - self.tr('Geometric predicate'), - left=self.INPUT, right=self.INTERSECT)) + self.addParameter(ParameterSelection(self.PREDICATE, + self.tr('Geometric predicate'), + self.predicates, + multiple=True)) self.addParameter(ParameterNumber(self.PRECISION, self.tr('Precision'), 0.0, None, 0.0)) @@ -98,20 +109,7 @@ class ExtractByLocation(GeoAlgorithm): except: pass # already removed else: - if predicate == 'intersects': - res = tmpGeom.intersects(geom) - elif predicate == 'contains': - res = tmpGeom.contains(geom) - elif predicate == 'equals': - res = tmpGeom.equals(geom) - elif predicate == 'touches': - res = tmpGeom.touches(geom) - elif predicate == 'overlaps': - res = tmpGeom.overlaps(geom) - elif predicate == 'within': - res = tmpGeom.within(geom) - elif predicate == 'crosses': - res = tmpGeom.crosses(geom) + res = getattr(tmpGeom, predicate)(geom) if res: selectedSet.append(feat.id()) break diff --git a/python/plugins/processing/algs/qgis/SelectByLocation.py b/python/plugins/processing/algs/qgis/SelectByLocation.py index 1597faedbb0..e5bf78591c3 100644 --- a/python/plugins/processing/algs/qgis/SelectByLocation.py +++ b/python/plugins/processing/algs/qgis/SelectByLocation.py @@ -35,7 +35,6 @@ from qgis.core import QgsGeometry, QgsFeatureRequest from processing.core.GeoAlgorithm import GeoAlgorithm from processing.core.parameters import ParameterSelection from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterGeometryPredicate from processing.core.parameters import ParameterNumber from processing.core.outputs import OutputVector from processing.tools import dataobjects, vector @@ -59,6 +58,16 @@ class SelectByLocation(GeoAlgorithm): self.name, self.i18n_name = self.trAlgorithm('Select by location') self.group, self.i18n_group = self.trAlgorithm('Vector selection tools') + self.predicates = ( + ('intersects', self.tr('intersects')), + ('contains', self.tr('contains')), + ('disjoint', self.tr('disjoint')), + ('equals', self.tr('equals')), + ('touches', self.tr('touches')), + ('overlaps', self.tr('overlaps')), + ('within', self.tr('within')), + ('crosses', self.tr('crosses'))) + self.methods = [self.tr('creating new selection'), self.tr('adding to current selection'), self.tr('removing from current selection')] @@ -67,9 +76,10 @@ class SelectByLocation(GeoAlgorithm): self.tr('Layer to select from'))) self.addParameter(ParameterVector(self.INTERSECT, self.tr('Additional layer (intersection layer)'))) - self.addParameter(ParameterGeometryPredicate(self.PREDICATE, - self.tr('Geometric predicate'), - left=self.INPUT, right=self.INTERSECT)) + self.addParameter(ParameterSelection(self.PREDICATE, + self.tr('Geometric predicate'), + self.predicates, + multiple=True)) self.addParameter(ParameterNumber(self.PRECISION, self.tr('Precision'), 0.0, None, 0.0)) @@ -118,20 +128,7 @@ class SelectByLocation(GeoAlgorithm): except: pass # already removed else: - if predicate == 'intersects': - res = tmpGeom.intersects(geom) - elif predicate == 'contains': - res = tmpGeom.contains(geom) - elif predicate == 'equals': - res = tmpGeom.equals(geom) - elif predicate == 'touches': - res = tmpGeom.touches(geom) - elif predicate == 'overlaps': - res = tmpGeom.overlaps(geom) - elif predicate == 'within': - res = tmpGeom.within(geom) - elif predicate == 'crosses': - res = tmpGeom.crosses(geom) + res = getattr(tmpGeom, predicate)(geom) if res: selectedSet.append(feat.id()) break diff --git a/python/plugins/processing/algs/qgis/SpatialJoin.py b/python/plugins/processing/algs/qgis/SpatialJoin.py index 15c151fc9d9..bb2a5eb3f07 100644 --- a/python/plugins/processing/algs/qgis/SpatialJoin.py +++ b/python/plugins/processing/algs/qgis/SpatialJoin.py @@ -37,7 +37,6 @@ from qgis.core import Qgis, QgsFields, QgsField, QgsFeature, QgsGeometry, NULL, from processing.core.GeoAlgorithm import GeoAlgorithm from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterGeometryPredicate from processing.core.parameters import ParameterNumber from processing.core.parameters import ParameterSelection from processing.core.parameters import ParameterString @@ -64,6 +63,15 @@ class SpatialJoin(GeoAlgorithm): self.name, self.i18n_name = self.trAlgorithm('Join attributes by location') self.group, self.i18n_group = self.trAlgorithm('Vector general tools') + self.predicates = ( + ('intersects', self.tr('intersects')), + ('contains', self.tr('contains')), + ('equals', self.tr('equals')), + ('touches', self.tr('touches')), + ('overlaps', self.tr('overlaps')), + ('within', self.tr('within')), + ('crosses', self.tr('crosses'))) + self.summarys = [ self.tr('Take attributes of the first located feature'), self.tr('Take summary of intersecting features') @@ -78,12 +86,10 @@ class SpatialJoin(GeoAlgorithm): self.tr('Target vector layer'))) self.addParameter(ParameterVector(self.JOIN, self.tr('Join vector layer'))) - predicates = list(ParameterGeometryPredicate.predicates) - predicates.remove('disjoint') - self.addParameter(ParameterGeometryPredicate(self.PREDICATE, - self.tr('Geometric predicate'), - left=self.TARGET, right=self.JOIN, - enabledPredicates=predicates)) + self.addParameter(ParameterSelection(self.PREDICATE, + self.tr('Geometric predicate'), + self.predicates, + multiple=True)) self.addParameter(ParameterNumber(self.PRECISION, self.tr('Precision'), 0.0, None, 0.0)) @@ -174,20 +180,7 @@ class SpatialJoin(GeoAlgorithm): res = False for predicate in predicates: - if predicate == 'intersects': - res = inGeom.intersects(inGeomB) - elif predicate == 'contains': - res = inGeom.contains(inGeomB) - elif predicate == 'equals': - res = inGeom.equals(inGeomB) - elif predicate == 'touches': - res = inGeom.touches(inGeomB) - elif predicate == 'overlaps': - res = inGeom.overlaps(inGeomB) - elif predicate == 'within': - res = inGeom.within(inGeomB) - elif predicate == 'crosses': - res = inGeom.crosses(inGeomB) + res= getattr(inGeom, predicate)(inGeomB) if res: break diff --git a/python/plugins/processing/core/parameters.py b/python/plugins/processing/core/parameters.py index db49b1d9630..6832fe6ffbf 100644 --- a/python/plugins/processing/core/parameters.py +++ b/python/plugins/processing/core/parameters.py @@ -1063,18 +1063,25 @@ class ParameterSelection(Parameter): elif isinstance(self.options, str): self.options = self.options.split(";") + # compute options as (value, text) + options = [] + for i, option in enumerate(self.options): + if option is None or isinstance(option, basestring): + options.append((i, option)) + else: + options.append((option[0], option[1])) + self.options = options + self.values = [option[0] for option in options] + + self.value = None if default is not None: - try: - self.default = int(default) - except: - self.default = 0 - self.value = self.default + self.setValue(self.default) def setValue(self, value): if value is None: if not self.optional: return False - self.value = 0 + self.value = None return True if isinstance(value, list): @@ -1082,22 +1089,32 @@ class ParameterSelection(Parameter): return False values = [] for v in value: + if v in self.values: + values.append(v) + continue try: - n = int(v) - values.append(n) + v = int(v) except: + pass + if not v in self.values: return False + values.append(v) if not self.optional and len(values) == 0: return False self.value = values return True else: - try: - n = int(value) - self.value = n + if value in self.values: + self.value = value return True + try: + value = int(value) except: + pass + if not value in self.values: return False + self.value = value + return True @classmethod def fromScriptCode(self, line): @@ -1510,46 +1527,6 @@ class ParameterVector(ParameterDataObject): [dataobjects.TYPE_VECTOR_POLYGON], isOptional) -class ParameterGeometryPredicate(Parameter): - - predicates = ('intersects', - 'contains', - 'disjoint', - 'equals', - 'touches', - 'overlaps', - 'within', - 'crosses') - - def __init__(self, name='', description='', left=None, right=None, - optional=False, enabledPredicates=None): - Parameter.__init__(self, name, description, None, optional) - self.left = left - self.right = right - self.value = None - self.enabledPredicates = enabledPredicates - if self.enabledPredicates is None: - self.enabledPredicates = self.predicates - - def getValueAsCommandLineParameter(self): - return str(self.value) - - def setValue(self, value): - if value is None: - if not self.optional: - return False - self.value = None - return True - elif len(value) == 0 and not self.optional: - return False - - if isinstance(value, str): - self.value = value.split(';') # relates to ModelerAlgorithm.resolveValue - else: - self.value = value - return True - - paramClasses = [c for c in list(sys.modules[__name__].__dict__.values()) if isclass(c) and issubclass(c, Parameter)] diff --git a/python/plugins/processing/gui/BatchPanel.py b/python/plugins/processing/gui/BatchPanel.py index 8db29a59cb9..1f0cf3410bd 100644 --- a/python/plugins/processing/gui/BatchPanel.py +++ b/python/plugins/processing/gui/BatchPanel.py @@ -38,7 +38,6 @@ from qgis.core import QgsApplication from qgis.gui import QgsMessageBar from processing.gui.BatchOutputSelectionPanel import BatchOutputSelectionPanel -from processing.gui.GeometryPredicateSelectionPanel import GeometryPredicateSelectionPanel from processing.core.parameters import ParameterFile from processing.core.parameters import ParameterRaster @@ -50,7 +49,6 @@ from processing.core.parameters import ParameterPoint from processing.core.parameters import ParameterSelection from processing.core.parameters import ParameterFixedTable from processing.core.parameters import ParameterMultipleInput -from processing.core.parameters import ParameterGeometryPredicate pluginPath = os.path.split(os.path.dirname(__file__))[0] WIDGET, BASE = uic.loadUiType( diff --git a/python/plugins/processing/gui/GeometryPredicateSelectionPanel.py b/python/plugins/processing/gui/GeometryPredicateSelectionPanel.py deleted file mode 100644 index 8aeaa640305..00000000000 --- a/python/plugins/processing/gui/GeometryPredicateSelectionPanel.py +++ /dev/null @@ -1,123 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - PredicatePanel.py - --------------------- - Date : January 2015 - Copyright : (C) 2015 by Arnaud Morvan - Email : arnaud dot morvan at camptocamp dot com - Contributors : Arnaud Morvan -*************************************************************************** -* * -* 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. * -* * -*************************************************************************** -""" -from builtins import range - -__author__ = 'Arnaud Morvan' -__date__ = 'January 2015' -__copyright__ = '(C) 2015, Arnaud Morvan' - -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import os - -from qgis.PyQt import uic -from qgis.PyQt.QtWidgets import QCheckBox -from qgis.core import Qgis, QgsVectorLayer, QgsWkbTypes, QgsWkbTypes - -from processing.core.parameters import ParameterGeometryPredicate - -pluginPath = os.path.split(os.path.dirname(__file__))[0] -WIDGET, BASE = uic.loadUiType( - os.path.join(pluginPath, 'ui', 'widgetGeometryPredicateSelector.ui')) - - -class GeometryPredicateSelectionPanel(BASE, WIDGET): - - unusablePredicates = { - QgsWkbTypes.PointGeometry: { - QgsWkbTypes.PointGeometry: ('touches', 'crosses'), - QgsWkbTypes.LineGeometry: ('equals', 'contains', 'overlaps'), - QgsWkbTypes.PolygonGeometry: ('equals', 'contains', 'overlaps') - }, - QgsWkbTypes.LineGeometry: { - QgsWkbTypes.PointGeometry: ('equals', 'within', 'overlaps'), - QgsWkbTypes.LineGeometry: [], - QgsWkbTypes.PolygonGeometry: ('equals', 'contains', 'overlaps') - }, - QgsWkbTypes.PolygonGeometry: { - QgsWkbTypes.PointGeometry: ('equals', 'within', 'overlaps'), - QgsWkbTypes.LineGeometry: ('equals', 'within', 'overlaps'), - QgsWkbTypes.PolygonGeometry: ('crosses') - } - } - - def __init__(self, - enabledPredicated=ParameterGeometryPredicate.predicates, - rows=4): - super(GeometryPredicateSelectionPanel, self).__init__(None) - self.setupUi(self) - - self.enabledPredicated = enabledPredicated - self.leftLayer = None - self.rightLayer = None - self.setRows(rows) - self.updatePredicates() - - def onLeftLayerChange(self): - sender = self.sender() - self.leftLayer = sender.itemData(sender.currentIndex()) - self.updatePredicates() - - def onRightLayerChange(self): - sender = self.sender() - self.rightLayer = sender.itemData(sender.currentIndex()) - self.updatePredicates() - - def updatePredicates(self): - if (isinstance(self.leftLayer, QgsVectorLayer) - and isinstance(self.rightLayer, QgsVectorLayer)): - leftType = self.leftLayer.geometryType() - rightType = self.rightLayer.geometryType() - unusablePredicates = self.unusablePredicates[leftType][rightType] - else: - unusablePredicates = [] - for predicate in ParameterGeometryPredicate.predicates: - widget = self.getWidget(predicate) - widget.setEnabled(predicate in self.enabledPredicated - and predicate not in unusablePredicates) - - def setRows(self, rows): - widgets = [] - for predicate in ParameterGeometryPredicate.predicates: - widget = self.getWidget(predicate) - self.gridLayout.removeWidget(widget) - widgets.append(widget) - for i in range(0, len(widgets)): - widget = widgets[i] - self.gridLayout.addWidget(widget, i % rows, i / rows) - - def getWidget(self, predicate): - return self.findChild(QCheckBox, predicate + 'Box') - - def value(self): - values = [] - for predicate in ParameterGeometryPredicate.predicates: - widget = self.getWidget(predicate) - if widget.isEnabled() and widget.isChecked(): - values.append(predicate) - return values - - def setValue(self, values): - if values: - for predicate in ParameterGeometryPredicate.predicates: - widget = self.getWidget(predicate) - widget.setChecked(predicate in values) - return True diff --git a/python/plugins/processing/gui/MultipleInputDialog.py b/python/plugins/processing/gui/MultipleInputDialog.py index 41fc035d85d..bd668586e04 100644 --- a/python/plugins/processing/gui/MultipleInputDialog.py +++ b/python/plugins/processing/gui/MultipleInputDialog.py @@ -68,9 +68,10 @@ class MultipleInputDialog(BASE, WIDGET): def populateList(self): model = QStandardItemModel() - for i, option in enumerate(self.options): - item = QStandardItem(option) - item.setCheckState(Qt.Checked if i in self.selectedoptions else Qt.Unchecked) + for value, text in self.options: + item = QStandardItem(text) + item.setData(value, Qt.UserRole) + item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked) item.setCheckable(True) model.appendRow(item) @@ -82,7 +83,7 @@ class MultipleInputDialog(BASE, WIDGET): for i in range(model.rowCount()): item = model.item(i) if item.checkState() == Qt.Checked: - self.selectedoptions.append(i) + self.selectedoptions.append(item.data(Qt.UserRole)) QDialog.accept(self) def reject(self): diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 18680bc2dd9..493d346f8a1 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -73,7 +73,6 @@ from processing.gui.BatchInputSelectionPanel import BatchInputSelectionPanel from processing.gui.FixedTablePanel import FixedTablePanel from processing.gui.ExtentSelectionPanel import ExtentSelectionPanel from processing.gui.StringInputPanel import StringInputPanel -from processing.gui.GeometryPredicateSelectionPanel import GeometryPredicateSelectionPanel DIALOG_STANDARD = 'standard' @@ -662,22 +661,23 @@ class SelectionWidgetWrapper(WidgetWrapper): return MultipleInputPanel(options=self.param.options) else: widget = QComboBox() - widget.addItems(self.param.options) + for option in self.param.options: + widget.addItem(option[1], option[0]) if self.param.default: - widget.setCurrentIndex(self.param.default) + widget.setCurrentIndex(widget.findData(self.param.default)) return widget def setValue(self, value): if self.param.multiple: self.widget.setSelectedItems(value) else: - self.widget.setCurrentIndex(int(value)) + self.widget.setCurrentIndex(self.widget.findData(value)) def value(self): if self.param.multiple: return self.widget.selectedoptions else: - return self.widget.currentIndex() + return self.widget.currentData() class VectorWidgetWrapper(WidgetWrapper): diff --git a/python/plugins/processing/tests/ParametersTest.py b/python/plugins/processing/tests/ParametersTest.py index 5da37bd965d..7643b3e6f3f 100644 --- a/python/plugins/processing/tests/ParametersTest.py +++ b/python/plugins/processing/tests/ParametersTest.py @@ -272,7 +272,7 @@ class ParameterSelectionTest(unittest.TestCase): parameter = ParameterSelection('myName', 'myDesc', ['option1', 'option2', 'option3'], default=0.0) self.assertEqual(parameter.value, 0) parameter = ParameterSelection('myName', 'myDesc', ['option1', 'option2', 'option3'], default='a') - self.assertEqual(parameter.value, 0) + self.assertEqual(parameter.value, None) def testOptional(self): optionalParameter = ParameterSelection('myName', 'myDesc', ['option1', 'option2', 'option3'], default=0, optional=True) @@ -280,7 +280,7 @@ class ParameterSelectionTest(unittest.TestCase): optionalParameter.setValue(1) self.assertEqual(optionalParameter.value, 1) self.assertTrue(optionalParameter.setValue(None)) - self.assertEqual(optionalParameter.value, 0) + self.assertEqual(optionalParameter.value, None) requiredParameter = ParameterSelection('myName', 'myDesc', ['option1', 'option2', 'option3'], default=0, optional=False) self.assertEqual(requiredParameter.value, 0) @@ -289,6 +289,22 @@ class ParameterSelectionTest(unittest.TestCase): self.assertFalse(requiredParameter.setValue(None)) self.assertEqual(requiredParameter.value, 1) + def testTupleOptions(self): + options = ( + ('o1', 'option1'), + ('o2', 'option2'), + ('o3', 'option3')) + + optionalParameter = ParameterSelection('myName', 'myDesc', options, default='o1') + self.assertEqual(optionalParameter.value, 'o1') + optionalParameter.setValue('o2') + self.assertEqual(optionalParameter.value, 'o2') + + optionalParameter = ParameterSelection('myName', 'myDesc', options, default=['o1', 'o2'], multiple=True) + self.assertEqual(optionalParameter.value, ['o1', 'o2']) + optionalParameter.setValue(['o2']) + self.assertEqual(optionalParameter.value, ['o2']) + class ParameterFileTest(unittest.TestCase):