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 +