From 60d9a60089471629368b5e356830c5b87d232a6e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 9 Aug 2016 19:39:02 +1000 Subject: [PATCH] [processing] Allow modification of feature request when using vector.features Allows for optimising the request through subsets of attributes or no geometry fetching (cherry-picked from 9e1ddcb54d4399b2ca90b8a854a592f4fd1a7653) --- .../plugins/processing/tests/CMakeLists.txt | 1 + python/plugins/processing/tests/ToolsTest.py | 89 +++++++++++++++++++ python/plugins/processing/tools/vector.py | 21 ++--- 3 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 python/plugins/processing/tests/ToolsTest.py diff --git a/python/plugins/processing/tests/CMakeLists.txt b/python/plugins/processing/tests/CMakeLists.txt index ac9cb33c6b43..3cf8fa4c2316 100644 --- a/python/plugins/processing/tests/CMakeLists.txt +++ b/python/plugins/processing/tests/CMakeLists.txt @@ -7,6 +7,7 @@ PLUGIN_INSTALL(processing tests/data ${TEST_DATA_FILES}) IF(ENABLE_TESTS) INCLUDE(UsePythonTest) ADD_PYTHON_TEST(ProcessingParametersTest ParametersTest.py) + ADD_PYTHON_TEST(ProcessingToolsTest ToolsTest.py) ADD_PYTHON_TEST(ProcessingQgisAlgorithmsTest QgisAlgorithmsTest.py) ADD_PYTHON_TEST(ProcessingGdalAlgorithmsTest GdalAlgorithmsTest.py) ADD_PYTHON_TEST(ProcessingGrass7AlgorithmsImageryTest Grass7AlgorithmsImageryTest.py) diff --git a/python/plugins/processing/tests/ToolsTest.py b/python/plugins/processing/tests/ToolsTest.py new file mode 100644 index 000000000000..ca0a3e990dec --- /dev/null +++ b/python/plugins/processing/tests/ToolsTest.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + ToolsTest + --------------------- + Date : July 2017 + Copyright : (C) 2017 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__ = 'July 2016' +__copyright__ = '(C) 2016, Nyall Dawson' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from qgis.testing import start_app, unittest +from processing.tests.TestData import points2 +from processing.tools import vector +from qgis.core import (QgsVectorLayer, QgsFeatureRequest) +from processing.core.ProcessingConfig import ProcessingConfig + +start_app() + + +class VectorTest(unittest.TestCase): + + def testFeatures(self): + ProcessingConfig.initialize() + + test_data = points2() + test_layer = QgsVectorLayer(test_data, 'test', 'ogr') + + # test with all features + features = vector.features(test_layer) + self.assertEqual(len(features), 8) + self.assertEqual(set([f.id() for f in features]), set([0, 1, 2, 3, 4, 5, 6, 7])) + + # test with selected features + previous_value = ProcessingConfig.getSetting(ProcessingConfig.USE_SELECTED) + ProcessingConfig.setSettingValue(ProcessingConfig.USE_SELECTED, True) + test_layer.selectByIds([2, 4, 6]) + features = vector.features(test_layer) + self.assertEqual(len(features), 3) + self.assertEqual(set([f.id() for f in features]), set([2, 4, 6])) + + # selection, but not using selected features + ProcessingConfig.setSettingValue(ProcessingConfig.USE_SELECTED, False) + test_layer.selectByIds([2, 4, 6]) + features = vector.features(test_layer) + self.assertEqual(len(features), 8) + self.assertEqual(set([f.id() for f in features]), set([0, 1, 2, 3, 4, 5, 6, 7])) + + # using selected features, but no selection + ProcessingConfig.setSettingValue(ProcessingConfig.USE_SELECTED, True) + test_layer.removeSelection() + features = vector.features(test_layer) + self.assertEqual(len(features), 8) + self.assertEqual(set([f.id() for f in features]), set([0, 1, 2, 3, 4, 5, 6, 7])) + + # test that feature request is honored + ProcessingConfig.setSettingValue(ProcessingConfig.USE_SELECTED, False) + features = vector.features(test_layer, QgsFeatureRequest().setFilterFids([1, 3, 5])) + self.assertEqual(set([f.id() for f in features]), set([1, 3, 5])) + + # test that feature request is honored when using selections + ProcessingConfig.setSettingValue(ProcessingConfig.USE_SELECTED, True) + test_layer.selectByIds([2, 4, 6]) + features = vector.features(test_layer, QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)) + self.assertTrue(all([f.geometry() == None for f in features])) + features = vector.features(test_layer, QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)) + self.assertEqual(set([f.id() for f in features]), set([2, 4, 6])) + + ProcessingConfig.setSettingValue(ProcessingConfig.USE_SELECTED, previous_value) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/plugins/processing/tools/vector.py b/python/plugins/processing/tools/vector.py index babac4066336..c1d339439bab 100644 --- a/python/plugins/processing/tools/vector.py +++ b/python/plugins/processing/tools/vector.py @@ -37,7 +37,8 @@ from qgis.PyQt.QtCore import QVariant, QSettings from qgis.core import (QGis, QgsFields, QgsField, QgsGeometry, QgsRectangle, QgsSpatialIndex, QgsMapLayerRegistry, QgsMapLayer, QgsVectorLayer, - QgsVectorFileWriter, QgsDistanceArea, QgsDataSourceURI, QgsCredentials) + QgsVectorFileWriter, QgsDistanceArea, QgsDataSourceURI, QgsCredentials, + QgsFeatureRequest) from processing.core.ProcessingConfig import ProcessingConfig from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException @@ -86,7 +87,7 @@ } -def features(layer): +def features(layer, request=QgsFeatureRequest()): """This returns an iterator over features in a vector layer, considering the selection that might exist in the layer, and the configuration that indicates whether to use only selected feature @@ -97,15 +98,15 @@ def features(layer): """ class Features: - def __init__(self, layer): + def __init__(self, layer, request): self.layer = layer self.selection = False - self.iter = layer.getFeatures() - if ProcessingConfig.getSetting(ProcessingConfig.USE_SELECTED): - selected = layer.selectedFeatures() - if len(selected) > 0: - self.selection = True - self.iter = iter(selected) + if ProcessingConfig.getSetting(ProcessingConfig.USE_SELECTED)\ + and layer.selectedFeatureCount() > 0: + self.iter = layer.selectedFeaturesIterator(request) + self.selection = True + else: + self.iter = layer.getFeatures(request) def __iter__(self): return self.iter @@ -116,7 +117,7 @@ def __len__(self): else: return int(self.layer.featureCount()) - return Features(layer) + return Features(layer, request) def uniqueValues(layer, attribute):