Skip to content

Commit

Permalink
Fix processing algorithm checkvalidity reason
Browse files Browse the repository at this point in the history
Fixex #19617 - Check validity algorithm returns empty reason in attribute table
  • Loading branch information
elpaso committed Sep 18, 2018
1 parent 5b7e60a commit 001a81b
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 3 deletions.
3 changes: 0 additions & 3 deletions python/plugins/processing/algs/qgis/CheckValidity.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,6 @@ def doCheck(self, method, parameters, context, feedback):
if not geom.isNull() and not geom.isEmpty():
errors = list(geom.validateGeometry(method))
if errors:
# QGIS method return a summary at the end
if method == 1:
errors.pop()
valid = False
reasons = []
for error in errors:
Expand Down
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ ADD_PYTHON_TEST(PyQgsPointDisplacementRenderer test_qgspointdisplacementrenderer
ADD_PYTHON_TEST(PyQgsPostgresDomain test_qgspostgresdomain.py)
ADD_PYTHON_TEST(PyQgsProcessingRecentAlgorithmLog test_qgsprocessingrecentalgorithmslog.py)
ADD_PYTHON_TEST(PyQgsProcessingInPlace test_qgsprocessinginplace.py)
ADD_PYTHON_TEST(PyQgsProcessingCheckValidity test_qgsprocessing_checkvalidity.py)
ADD_PYTHON_TEST(PyQgsProjectionSelectionWidgets test_qgsprojectionselectionwidgets.py)
ADD_PYTHON_TEST(PyQgsProjectMetadata test_qgsprojectmetadata.py)
ADD_PYTHON_TEST(PyQgsRange test_qgsrange.py)
Expand Down
135 changes: 135 additions & 0 deletions tests/src/python/test_qgsprocessing_checkvalidity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for Processing CheckValidity algorithm.
.. note:: 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__ = 'Alessandro Pasotti'
__date__ = '2018-09'
__copyright__ = 'Copyright 2018, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'

from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (
QgsFeature,
QgsGeometry,
QgsApplication,
QgsMemoryProviderUtils,
QgsWkbTypes,
QgsField,
QgsFields,
QgsProcessingContext,
QgsProcessingFeedback,
QgsCoordinateReferenceSystem,
QgsProject,
QgsProcessingException,
QgsProcessingUtils,
QgsSettings
)
from processing.core.Processing import Processing
from processing.gui.AlgorithmExecutor import execute
from qgis.testing import start_app, unittest
from qgis.PyQt.QtTest import QSignalSpy
from qgis.analysis import QgsNativeAlgorithms

start_app()


class ConsoleFeedBack(QgsProcessingFeedback):

def reportError(self, error, fatalError=False):
print(error)


class TestQgsProcessingCheckValidity(unittest.TestCase):

@classmethod
def setUpClass(cls):
"""Run before all tests"""
QCoreApplication.setOrganizationName("QGIS_Test")
QCoreApplication.setOrganizationDomain(
"QGIS_TestPyQgsProcessingCheckValidity.com")
QCoreApplication.setApplicationName(
"QGIS_TestPyQgsProcessingCheckValidity")
QgsSettings().clear()
Processing.initialize()
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
cls.registry = QgsApplication.instance().processingRegistry()

def _make_layer(self, layer_wkb_name):
fields = QgsFields()
wkb_type = getattr(QgsWkbTypes, layer_wkb_name)
fields.append(QgsField('int_f', QVariant.Int))
layer = QgsMemoryProviderUtils.createMemoryLayer(
'%s_layer' % layer_wkb_name, fields, wkb_type, QgsCoordinateReferenceSystem(4326))
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), wkb_type)
return layer

def test_check_validity(self):
"""Test that the output invalid contains the error reason"""

polygon_layer = self._make_layer('Polygon')
self.assertTrue(polygon_layer.startEditing())
f = QgsFeature(polygon_layer.fields())
f.setAttributes([1])
# Flake!
f.setGeometry(QgsGeometry.fromWkt(
'POLYGON ((0 0, 2 2, 0 2, 2 0, 0 0))'))
self.assertTrue(f.isValid())
f2 = QgsFeature(polygon_layer.fields())
f2.setAttributes([1])
f2.setGeometry(QgsGeometry.fromWkt(
'POLYGON((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))'))
self.assertTrue(f2.isValid())
self.assertTrue(polygon_layer.addFeatures([f, f2]))
polygon_layer.commitChanges()
polygon_layer.rollBack()
self.assertEqual(polygon_layer.featureCount(), 2)

QgsProject.instance().addMapLayers([polygon_layer])

alg = self.registry.createAlgorithmById('qgis:checkvalidity')

context = QgsProcessingContext()
context.setProject(QgsProject.instance())
feedback = ConsoleFeedBack()

self.assertIsNotNone(alg)
parameters = {}
parameters['INPUT_LAYER'] = polygon_layer.id()
parameters['VALID_OUTPUT'] = 'memory:'
parameters['INVALID_OUTPUT'] = 'memory:'
parameters['ERROR_OUTPUT'] = 'memory:'

# QGIS method
parameters['METHOD'] = 1
ok, results = execute(
alg, parameters, context=context, feedback=feedback)
self.assertTrue(ok)
invalid_layer = QgsProcessingUtils.mapLayerFromString(
results['INVALID_OUTPUT'], context)
self.assertEqual(invalid_layer.fields().names()[-1], '_errors')
self.assertEqual(invalid_layer.featureCount(), 1)
f = next(invalid_layer.getFeatures())
self.assertEqual(f.attributes(), [
1, 'segments 0 and 2 of line 0 intersect at 1, 1\nGeometry has 1 errors.'])

# GEOS method
parameters['METHOD'] = 2
ok, results = execute(
alg, parameters, context=context, feedback=feedback)
self.assertTrue(ok)
invalid_layer = QgsProcessingUtils.mapLayerFromString(
results['INVALID_OUTPUT'], context)
self.assertEqual(invalid_layer.fields().names()[-1], '_errors')
self.assertEqual(invalid_layer.featureCount(), 1)
f = next(invalid_layer.getFeatures())
self.assertEqual(f.attributes(), [1, 'Self-intersection'])


if __name__ == '__main__':
unittest.main()

0 comments on commit 001a81b

Please sign in to comment.