Skip to content

Commit 8db9284

Browse files
committed
[FEATURE][processing] Optimised points along geometry algorithm
Supports also polygon geometries, handles null geometries, and records the original line angle along with the distance for each point.
1 parent 9a9a49c commit 8db9284

File tree

4 files changed

+134
-14
lines changed

4 files changed

+134
-14
lines changed

python/plugins/processing/algs/help/qgis.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,11 @@ qgis:orientedminimumboundingbox: >
284284

285285
As an alternative, the output layer can contain not just a single rectangle, but one for each input feature, representing the minimum rectangle that covers each of them.
286286

287+
qgis:pointsalonglines: >
288+
Creates points at regular intervals along line or polygon geometries. Created points will have new attributes added for the distance along the geometry and the angle of the line at the point.
289+
290+
An optional start and end offset can be specified, which controls how far from the start and end of the geometry the points should be created.
291+
287292
qgis:pointsdisplacement:
288293

289294

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
PointsAlongGeometry.py
6+
---------------------
7+
Date : August 2016
8+
Copyright : (C) 2016 by Nyall Dawson
9+
Email : nyall dot dawson at gmail dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Nyall Dawson'
21+
__date__ = 'August 2016'
22+
__copyright__ = '(C) 2016, Nyall Dawson'
23+
24+
# This will get replaced with a git SHA1 when you do a git archive
25+
26+
__revision__ = '$Format:%H$'
27+
28+
import os
29+
import math
30+
31+
from qgis.PyQt.QtCore import QVariant
32+
from qgis.PyQt.QtGui import QIcon
33+
34+
from qgis.core import QgsFeature, QgsGeometry, QgsWkbTypes, QgsField
35+
36+
from processing.core.GeoAlgorithm import GeoAlgorithm
37+
from processing.core.parameters import ParameterVector, ParameterNumber
38+
from processing.core.outputs import OutputVector
39+
from processing.tools import dataobjects, vector
40+
41+
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
42+
43+
44+
class PointsAlongGeometry(GeoAlgorithm):
45+
46+
INPUT = 'INPUT'
47+
OUTPUT = 'OUTPUT'
48+
DISTANCE = 'DISTANCE'
49+
START_OFFSET = 'START_OFFSET'
50+
END_OFFSET = 'END_OFFSET'
51+
52+
def getIcon(self):
53+
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'extract_nodes.png'))
54+
55+
def defineCharacteristics(self):
56+
self.name, self.i18n_name = self.trAlgorithm('Points along lines')
57+
self.group, self.i18n_group = self.trAlgorithm('Vector geometry tools')
58+
59+
self.addParameter(ParameterVector(self.INPUT,
60+
self.tr('Input layer'),
61+
[ParameterVector.VECTOR_TYPE_POLYGON, ParameterVector.VECTOR_TYPE_LINE]))
62+
self.addParameter(ParameterNumber(self.DISTANCE,
63+
self.tr('Distance'), default=1.0))
64+
self.addParameter(ParameterNumber(self.START_OFFSET,
65+
self.tr('Start offset'), default=0.0))
66+
self.addParameter(ParameterNumber(self.END_OFFSET,
67+
self.tr('End offset'), default=0.0))
68+
self.addOutput(OutputVector(self.OUTPUT, self.tr('Points')))
69+
70+
def processAlgorithm(self, progress):
71+
layer = dataobjects.getObjectFromUri(
72+
self.getParameterValue(self.INPUT))
73+
distance = self.getParameterValue(self.DISTANCE)
74+
start_offset = self.getParameterValue(self.START_OFFSET)
75+
end_offset = self.getParameterValue(self.END_OFFSET)
76+
77+
fields = layer.fields()
78+
fields.append(QgsField('distance', QVariant.Double))
79+
fields.append(QgsField('angle', QVariant.Double))
80+
81+
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
82+
fields, QgsWkbTypes.Point, layer.crs())
83+
84+
features = vector.features(layer)
85+
total = 100.0 / len(features)
86+
for current, input_feature in enumerate(features):
87+
input_geometry = input_feature.geometry()
88+
if not input_geometry:
89+
writer.addFeature(input_feature)
90+
else:
91+
if input_geometry.type == QgsWkbTypes.PolygonGeometry:
92+
length = input_geometry.geometry().perimeter()
93+
else:
94+
length = input_geometry.length() - end_offset
95+
current_distance = start_offset
96+
97+
while current_distance <= length:
98+
point = input_geometry.interpolate(current_distance)
99+
angle = math.degrees(input_geometry.interpolateAngle(current_distance))
100+
101+
output_feature = QgsFeature()
102+
output_feature.setGeometry(point)
103+
attrs = input_feature.attributes()
104+
attrs.append(current_distance)
105+
attrs.append(angle)
106+
output_feature.setAttributes(attrs)
107+
writer.addFeature(output_feature)
108+
109+
current_distance += distance
110+
111+
progress.setPercentage(int(current * total))
112+
113+
del writer

python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154
from .PolygonCentroids import PolygonCentroids
155155
from .Translate import Translate
156156
from .SingleSidedBuffer import SingleSidedBuffer
157+
from .PointsAlongGeometry import PointsAlongGeometry
157158

158159
pluginPath = os.path.normpath(os.path.join(
159160
os.path.split(os.path.dirname(__file__))[0], os.pardir))
@@ -208,7 +209,8 @@ def __init__(self):
208209
RectanglesOvalsDiamondsFixed(), MergeLines(),
209210
BoundingBox(), Boundary(), PointOnSurface(),
210211
OffsetLine(), PolygonCentroids(),
211-
Translate(), SingleSidedBuffer()
212+
Translate(), SingleSidedBuffer(),
213+
PointsAlongGeometry()
212214
]
213215

214216
if hasMatplotlib:

python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml

+13-13
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ tests:
2323
OUTPUT:
2424
name: expected/polys_deleteholes.gml
2525
type: vector
26-
26+
2727
- algorithm: qgis:clip
2828
name: Clip lines by polygons
2929
params:
@@ -37,8 +37,8 @@ tests:
3737
OUTPUT:
3838
name: expected/clip_lines_by_polygon.gml
3939
type: vector
40-
41-
40+
41+
4242
- algorithm: qgis:clip
4343
name: Clip lines by multipolygon
4444
params:
@@ -52,7 +52,7 @@ tests:
5252
OUTPUT:
5353
name: expected/clip_lines_by_multipolygon.gml
5454
type: vector
55-
55+
5656
- algorithm: qgis:clip
5757
name: Clip polygons by multipolygons
5858
params:
@@ -66,7 +66,7 @@ tests:
6666
OUTPUT:
6767
name: expected/clip_polys_by_multipolygon.gml
6868
type: vector
69-
69+
7070
- algorithm: qgis:clip
7171
name: Clip multipolygons by polygons
7272
params:
@@ -80,7 +80,7 @@ tests:
8080
OUTPUT:
8181
name: expected/clip_multipolygons_by_polygons.gml
8282
type: vector
83-
83+
8484
- algorithm: qgis:clip
8585
name: Clip points by polygons
8686
params:
@@ -94,7 +94,7 @@ tests:
9494
OUTPUT:
9595
name: expected/clip_points_by_polygons.gml
9696
type: vector
97-
97+
9898
- algorithm: qgis:clip
9999
name: Clip points by multipolygons
100100
params:
@@ -108,8 +108,8 @@ tests:
108108
OUTPUT:
109109
name: expected/clip_points_by_multipolygons.gml
110110
type: vector
111-
112-
111+
112+
113113
# These datasets should produce a geometry collection and not a polygon only
114114
# dataset. If the algorithm is fixed, a new test should be introduced to
115115
# check this behavior.
@@ -439,7 +439,7 @@ tests:
439439
OUTPUT:
440440
name: expected/multi_to_single.gml
441441
type: vector
442-
442+
443443
- algorithm: qgis:boundingboxes
444444
name: Bounding boxes for lines
445445
params:
@@ -808,7 +808,7 @@ tests:
808808
OUTPUT_LAYER:
809809
name: expected/centroid_polys.gml
810810
type: vector
811-
811+
812812
- algorithm: qgis:translategeometry
813813
name: Lines translated
814814
params:
@@ -822,7 +822,7 @@ tests:
822822
name: expected/lines_translated.gml
823823
type: vector
824824

825-
825+
826826
- algorithm: qgis:singlesidedbuffer
827827
name: Single sided buffer lines (left, round)
828828
params:
@@ -869,4 +869,4 @@ tests:
869869
results:
870870
OUTPUT_LAYER:
871871
name: expected/single_sided_buffer_multiline_bevel.gml
872-
type: vector
872+
type: vector

0 commit comments

Comments
 (0)