From 9fa4e776dbf2ba5004583be1bcb0e8eb8c343301 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 25 Aug 2016 18:16:40 +1000 Subject: [PATCH] [FEATURE][processing] Extract nodes algorithm now saves node index, distance along line and angle at node Also correctly handles null geometries --- python/plugins/processing/algs/help/qgis.yaml | 2 + .../processing/algs/qgis/ExtractNodes.py | 39 +- .../testdata/expected/extract_nodes_lines.gfs | 25 ++ .../testdata/expected/extract_nodes_lines.gml | 146 +++++++ .../expected/extract_nodes_multilines.gfs | 25 ++ .../expected/extract_nodes_multilines.gml | 130 +++++++ .../expected/extract_nodes_multipolys.gfs | 41 ++ .../expected/extract_nodes_multipolys.gml | 249 ++++++++++++ .../testdata/expected/extract_nodes_polys.gfs | 41 ++ .../testdata/expected/extract_nodes_polys.gml | 357 ++++++++++++++++++ .../tests/testdata/qgis_algorithm_tests.yaml | 46 +++ 11 files changed, 1087 insertions(+), 14 deletions(-) create mode 100644 python/plugins/processing/tests/testdata/expected/extract_nodes_lines.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/extract_nodes_lines.gml create mode 100644 python/plugins/processing/tests/testdata/expected/extract_nodes_multilines.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/extract_nodes_multilines.gml create mode 100644 python/plugins/processing/tests/testdata/expected/extract_nodes_multipolys.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/extract_nodes_multipolys.gml create mode 100644 python/plugins/processing/tests/testdata/expected/extract_nodes_polys.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/extract_nodes_polys.gml diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index d77ffcd7266..a1faf2ad293 100644 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -166,6 +166,8 @@ qgis:extractbylocation: > qgis:extractnodes: > This algorithm takes a line or polygon layer and generates a point layer with points representing the nodes in the input lines or polygons. The attributes associated to each point are the same ones associated to the line or polygon that the point belongs to. + Additional fields are added to the nodes indicating the node index (beginning at 0), distance along original geometry and bisector angle of node for original geometry. + qgis:fieldcalculator: > This algorithm computes a new vector layer with the same features of the input layer, but with an additional attribute. The values of this new attribute are computed from each feature using a mathematical formula, based on te properties and attributes of the feature. diff --git a/python/plugins/processing/algs/qgis/ExtractNodes.py b/python/plugins/processing/algs/qgis/ExtractNodes.py index f412c4ac978..aae79613ed2 100644 --- a/python/plugins/processing/algs/qgis/ExtractNodes.py +++ b/python/plugins/processing/algs/qgis/ExtractNodes.py @@ -26,10 +26,12 @@ __copyright__ = '(C) 2012, Victor Olaya' __revision__ = '$Format:%H$' import os +import math from qgis.PyQt.QtGui import QIcon +from qgis.PyQt.QtCore import QVariant -from qgis.core import Qgis, QgsFeature, QgsGeometry, QgsWkbTypes +from qgis.core import QgsFeature, QgsGeometry, QgsWkbTypes, QgsField from processing.core.GeoAlgorithm import GeoAlgorithm from processing.core.parameters import ParameterVector @@ -61,25 +63,34 @@ class ExtractNodes(GeoAlgorithm): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT)) - writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( - layer.fields().toList(), QgsWkbTypes.Point, layer.crs()) + fields = layer.fields() + fields.append(QgsField('node_index', QVariant.Int)) + fields.append(QgsField('distance', QVariant.Double)) + fields.append(QgsField('angle', QVariant.Double)) - outFeat = QgsFeature() - inGeom = QgsGeometry() - outGeom = QgsGeometry() + writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( + fields, QgsWkbTypes.Point, layer.crs()) features = vector.features(layer) total = 100.0 / len(features) for current, f in enumerate(features): - inGeom = f.geometry() - attrs = f.attributes() + input_geometry = f.geometry() + if not input_geometry: + writer.addFeature(f) + else: + points = vector.extractPoints(input_geometry) - points = vector.extractPoints(inGeom) - outFeat.setAttributes(attrs) - - for i in points: - outFeat.setGeometry(outGeom.fromPoint(i)) - writer.addFeature(outFeat) + for i, point in enumerate(points): + distance = input_geometry.distanceToVertex(i) + angle = math.degrees(input_geometry.angleAtVertex(i)) + attrs = f.attributes() + attrs.append(i) + attrs.append(distance) + attrs.append(angle) + output_feature = QgsFeature() + output_feature.setAttributes(attrs) + output_feature.setGeometry(QgsGeometry.fromPoint(point)) + writer.addFeature(output_feature) progress.setPercentage(int(current * total)) diff --git a/python/plugins/processing/tests/testdata/expected/extract_nodes_lines.gfs b/python/plugins/processing/tests/testdata/expected/extract_nodes_lines.gfs new file mode 100644 index 00000000000..d8c3db83c23 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_nodes_lines.gfs @@ -0,0 +1,25 @@ + + + extract_nodes_lines + extract_nodes_lines + 1 + EPSG:4326 + + 17 + -1.00000 + 11.00000 + -3.00000 + 5.00000 + + + distance + distance + Real + + + angle + angle + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/extract_nodes_lines.gml b/python/plugins/processing/tests/testdata/expected/extract_nodes_lines.gml new file mode 100644 index 00000000000..02909905bca --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_nodes_lines.gml @@ -0,0 +1,146 @@ + + + + + -1-3 + 115 + + + + + + 6,2 + 0 + 0 + 90 + + + + + 9,2 + 1 + 3 + 45 + + + + + 9,3 + 2 + 4 + 22.5 + + + + + 11,5 + 3 + 6.82842712474619 + 45 + + + + + -1,-1 + 0 + 0 + 90 + + + + + 1,-1 + 1 + 2 + 90 + + + + + 2,0 + 0 + 0 + 0 + + + + + 2,2 + 1 + 2 + 45 + + + + + 3,2 + 2 + 3 + 45 + + + + + 3,3 + 3 + 4 + 0 + + + + + 3,1 + 0 + 0 + 90 + + + + + 5,1 + 1 + 2 + 90 + + + + + 7,-3 + 0 + 0 + 90 + + + + + 10,-3 + 1 + 3 + 90 + + + + + 6,-3 + 0 + 0 + 45 + + + + + 10,1 + 1 + 5.65685424949238 + 45 + + + + + + + diff --git a/python/plugins/processing/tests/testdata/expected/extract_nodes_multilines.gfs b/python/plugins/processing/tests/testdata/expected/extract_nodes_multilines.gfs new file mode 100644 index 00000000000..ca0aa911fb6 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_nodes_multilines.gfs @@ -0,0 +1,25 @@ + + + extract_nodes_multilines + extract_nodes_multilines + 1 + EPSG:4326 + + 15 + -1.00000 + 5.58042 + -1.00000 + 4.11977 + + + distance + distance + Real + + + angle + angle + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/extract_nodes_multilines.gml b/python/plugins/processing/tests/testdata/expected/extract_nodes_multilines.gml new file mode 100644 index 00000000000..e2d8954e3fe --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_nodes_multilines.gml @@ -0,0 +1,130 @@ + + + + + -1-1 + 5.580422264875244.119769673704415 + + + + + + -1,-1 + 0 + 0 + 90 + + + + + 1,-1 + 1 + 2 + 90 + + + + + 3,1 + 0 + 0 + 90 + + + + + 5,1 + 1 + 2 + 90 + + + + + 5.024184261036468,2.414779270633399 + 2 + 3.41498595862145 + 180.979319654339 + + + + + 5,1 + 3 + 4.82997191724289 + 180.979319654339 + + + + + + + + + 2,0 + 0 + 0 + 0 + + + + + 2,2 + 1 + 2 + 45 + + + + + 3,2 + 2 + 3 + 45 + + + + + 3,3 + 3 + 4 + 0 + + + + + 2.944337811900192,4.04721689059501 + 4 + 5.04869513927144 + 88.3476953223487 + + + + + 5.459500959692898,4.119769673704415 + 5 + 7.56490450384177 + 88.3476953223487 + + + + + 3,3 + 6 + 10.2673162217113 + 91.1803544802029 + + + + + 5.58042226487524,2.946833013435702 + 7 + 12.8482861544157 + 91.1803544802029 + + + diff --git a/python/plugins/processing/tests/testdata/expected/extract_nodes_multipolys.gfs b/python/plugins/processing/tests/testdata/expected/extract_nodes_multipolys.gfs new file mode 100644 index 00000000000..d753b35dc27 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_nodes_multipolys.gfs @@ -0,0 +1,41 @@ + + + extract_nodes_multipolys + extract_nodes_multipolys + 1 + EPSG:4326 + + 25 + 0.00000 + 9.00000 + -1.00000 + 6.00000 + + + Bname + Bname + String + 4 + + + Bintval + Bintval + Integer + + + Bfloatval + Bfloatval + Real + + + distance + distance + Real + + + angle + angle + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/extract_nodes_multipolys.gml b/python/plugins/processing/tests/testdata/expected/extract_nodes_multipolys.gml new file mode 100644 index 00000000000..eee2774410b --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_nodes_multipolys.gml @@ -0,0 +1,249 @@ + + + + + 0-1 + 96 + + + + + + 2,1 + Test + 1 + 0.123 + 0 + 0 + 315 + + + + + 2,2 + Test + 1 + 0.123 + 1 + 1 + 45 + + + + + 3,2 + Test + 1 + 0.123 + 2 + 2 + 45 + + + + + 3,3 + Test + 1 + 0.123 + 3 + 3 + 45 + + + + + 4,3 + Test + 1 + 0.123 + 4 + 4 + 135 + + + + + 4,1 + Test + 1 + 0.123 + 5 + 6 + 225 + + + + + 2,1 + Test + 1 + 0.123 + 6 + 8 + 315 + + + + + 7,-1 + 0 + 0 + 135 + + + + + 8,-1 + 1 + 1 + 45 + + + + + 8,3 + 2 + 5 + 315 + + + + + 7,3 + 3 + 6 + 225 + + + + + 7,-1 + 4 + 10 + 135 + + + + + 7,6 + 5 + 17 + 225 + + + + + 7,5 + 6 + 18 + 180 + + + + + 7,4 + 7 + 19 + 135 + + + + + 8,4 + 8 + 20 + 67.5 + + + + + 9,5 + 9 + 21.4142135623731 + 22.5 + + + + + 9,6 + 10 + 22.4142135623731 + 315 + + + + + 7,6 + 11 + 24.4142135623731 + 225 + + + + + 0,0 + Test + 2 + -0.123 + 0 + 0 + 315 + + + + + 0,1 + Test + 2 + -0.123 + 1 + 1 + 45 + + + + + 1,1 + Test + 2 + -0.123 + 2 + 2 + 135 + + + + + 1,0 + Test + 2 + -0.123 + 3 + 3 + 225 + + + + + 0,0 + Test + 2 + -0.123 + 4 + 4 + 315 + + + + + Test + 3 + 0 + + + diff --git a/python/plugins/processing/tests/testdata/expected/extract_nodes_polys.gfs b/python/plugins/processing/tests/testdata/expected/extract_nodes_polys.gfs new file mode 100644 index 00000000000..ead098a9e55 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_nodes_polys.gfs @@ -0,0 +1,41 @@ + + + extract_nodes_polys + extract_nodes_polys + 1 + EPSG:4326 + + 33 + -1.00000 + 10.00000 + -3.00000 + 6.00000 + + + name + name + String + 5 + + + intval + intval + Integer + + + floatval + floatval + Real + + + distance + distance + Real + + + angle + angle + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/extract_nodes_polys.gml b/python/plugins/processing/tests/testdata/expected/extract_nodes_polys.gml new file mode 100644 index 00000000000..9576d042004 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_nodes_polys.gml @@ -0,0 +1,357 @@ + + + + + -1-3 + 106 + + + + + + -1,-1 + aaaaa + 33 + 44.123456 + 0 + 0 + 315 + + + + + -1,3 + aaaaa + 33 + 44.123456 + 1 + 4 + 45 + + + + + 3,3 + aaaaa + 33 + 44.123456 + 2 + 8 + 135 + + + + + 3,2 + aaaaa + 33 + 44.123456 + 3 + 9 + 225 + + + + + 2,2 + aaaaa + 33 + 44.123456 + 4 + 10 + 225 + + + + + 2,-1 + aaaaa + 33 + 44.123456 + 5 + 13 + 225 + + + + + -1,-1 + aaaaa + 33 + 44.123456 + 6 + 16 + 315 + + + + + 5,5 + Aaaaa + -33 + 0 + 0 + 0 + 90 + + + + + 6,4 + Aaaaa + -33 + 0 + 1 + 1.4142135623731 + 202.5 + + + + + 4,4 + Aaaaa + -33 + 0 + 2 + 3.41421356237309 + 337.5 + + + + + 5,5 + Aaaaa + -33 + 0 + 3 + 4.82842712474619 + 90 + + + + + 2,5 + bbaaa + 0.123 + 0 + 0 + 315 + + + + + 2,6 + bbaaa + 0.123 + 1 + 1 + 45 + + + + + 3,6 + bbaaa + 0.123 + 2 + 2 + 135 + + + + + 3,5 + bbaaa + 0.123 + 3 + 3 + 225 + + + + + 2,5 + bbaaa + 0.123 + 4 + 4 + 315 + + + + + 6,1 + ASDF + 0 + 0 + 0 + 45 + + + + + 10,1 + ASDF + 0 + 1 + 4 + 135 + + + + + 10,-3 + ASDF + 0 + 2 + 8 + 225 + + + + + 6,-3 + ASDF + 0 + 3 + 12 + 315 + + + + + 6,1 + ASDF + 0 + 4 + 16 + 45 + + + + + 7,0 + ASDF + 0 + 5 + 17.4142135623731 + 225 + + + + + 7,-2 + ASDF + 0 + 6 + 19.4142135623731 + 135 + + + + + 9,-2 + ASDF + 0 + 7 + 21.4142135623731 + 45 + + + + + 9,0 + ASDF + 0 + 8 + 23.4142135623731 + 315 + + + + + 7,0 + ASDF + 0 + 9 + 25.4142135623731 + 225 + + + + + 120 + -100291.43213 + + + + + 3,2 + elim + 2 + 3.33 + 0 + 0 + 99.217474411461 + + + + + 6,1 + elim + 2 + 3.33 + 1 + 3.16227766016838 + 144.217474411461 + + + + + 6,-3 + elim + 2 + 3.33 + 2 + 7.16227766016838 + 238.282525588539 + + + + + 2,-1 + elim + 2 + 3.33 + 3 + 11.634413615168 + 328.282525588539 + + + + + 2,2 + elim + 2 + 3.33 + 4 + 14.634413615168 + 45 + + + + + 3,2 + elim + 2 + 3.33 + 5 + 15.634413615168 + 99.217474411461 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 22e08c56ad5..df8a95d03cf 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -870,3 +870,49 @@ tests: OUTPUT_LAYER: name: expected/single_sided_buffer_multiline_bevel.gml type: vector + + + - algorithm: qgis:extractnodes + name: Test (qgis:extractnodes) + params: + INPUT: + name: multipolys.gml + type: vector + results: + OUTPUT: + name: expected/extract_nodes_multipolys.gml + type: vector + + - algorithm: qgis:extractnodes + name: Test (qgis:extractnodes) + params: + INPUT: + name: polys.gml + type: vector + results: + OUTPUT: + name: expected/extract_nodes_polys.gml + type: vector + + - algorithm: qgis:extractnodes + name: Test (qgis:extractnodes) + params: + INPUT: + name: multilines.gml + type: vector + results: + OUTPUT: + name: expected/extract_nodes_multilines.gml + type: vector + + - algorithm: qgis:extractnodes + name: Test (qgis:extractnodes) + params: + INPUT: + name: lines.gml + type: vector + results: + OUTPUT: + name: expected/extract_nodes_lines.gml + type: vector +