Skip to content

Commit

Permalink
[FEATURE][processing] New algorithm for single sided buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Aug 16, 2016
1 parent 298d047 commit 989d986
Show file tree
Hide file tree
Showing 10 changed files with 351 additions and 1 deletion.
9 changes: 9 additions & 0 deletions python/plugins/processing/algs/help/qgis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,15 @@ qgis:simplifygeometries: >
qgis:singlepartstomultipart:


qgis:singlesidedbuffer: >
This algorithm buffers lines by a specified distance on one side of the line only.

The segments parameter controls the number of line segments to use to approximate a quarter circle when creating rounded buffers.

The join style parameter specifies whether round, mitre or beveled joins should be used when buffering corners in a line.

The mitre limit parameter is only applicable for mitre join styles, and controls the maximum distance from the buffer to use when creating a mitred join.

qgis:snappointstogrid: >
This algorithm modifies the position of points in a vector layer, so they fall in the coordinates of a grid.

Expand Down
3 changes: 2 additions & 1 deletion python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
from .OffsetLine import OffsetLine
from .PolygonCentroids import PolygonCentroids
from .Translate import Translate
from .SingleSidedBuffer import SingleSidedBuffer

pluginPath = os.path.normpath(os.path.join(
os.path.split(os.path.dirname(__file__))[0], os.pardir))
Expand Down Expand Up @@ -207,7 +208,7 @@ def __init__(self):
RectanglesOvalsDiamondsFixed(), MergeLines(),
BoundingBox(), Boundary(), PointOnSurface(),
OffsetLine(), PolygonCentroids(),
Translate()
Translate(), SingleSidedBuffer()
]

if hasMatplotlib:
Expand Down
118 changes: 118 additions & 0 deletions python/plugins/processing/algs/qgis/SingleSidedBuffer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-

"""
***************************************************************************
SingleSidedBuffer.py
--------------------
Date : August 2016
Copyright : (C) 2016 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__ = 'August 2016'
__copyright__ = '(C) 2016, Nyall Dawson'

# This will get replaced with a git SHA1 when you do a git archive323

__revision__ = '$Format:%H$'

import os

from qgis.core import QgsGeometry, QgsWkbTypes

from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector, ParameterSelection, ParameterNumber
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]


class SingleSidedBuffer(GeoAlgorithm):

INPUT_LAYER = 'INPUT_LAYER'
OUTPUT_LAYER = 'OUTPUT_LAYER'
DISTANCE = 'DISTANCE'
SIDE = 'SIDE'
SEGMENTS = 'SEGMENTS'
JOIN_STYLE = 'JOIN_STYLE'
MITRE_LIMIT = 'MITRE_LIMIT'

def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Single sided buffer')
self.group, self.i18n_group = self.trAlgorithm('Vector geometry tools')

self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer'), [ParameterVector.VECTOR_TYPE_LINE]))
self.addParameter(ParameterNumber(self.DISTANCE,
self.tr('Distance'), default=10.0))
self.sides = [self.tr('Left'),
'Right']
self.addParameter(ParameterSelection(
self.SIDE,
self.tr('Side'),
self.sides))

self.addParameter(ParameterNumber(self.SEGMENTS,
self.tr('Segments'), 1, default=8))

self.join_styles = [self.tr('Round'),
'Mitre',
'Bevel']
self.addParameter(ParameterSelection(
self.JOIN_STYLE,
self.tr('Join style'),
self.join_styles))
self.addParameter(ParameterNumber(self.MITRE_LIMIT,
self.tr('Mitre limit'), 1, default=2))

self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Single sided buffers')))

def processAlgorithm(self, progress):
layer = dataobjects.getObjectFromUri(
self.getParameterValue(self.INPUT_LAYER))

writer = self.getOutputFromName(
self.OUTPUT_LAYER).getVectorWriter(
layer.fields().toList(),
QgsWkbTypes.Polygon,
layer.crs())

distance = self.getParameterValue(self.DISTANCE)
segments = int(self.getParameterValue(self.SEGMENTS))
join_style = self.getParameterValue(self.JOIN_STYLE) + 1
if self.getParameterValue(self.SIDE) == 0:
side = QgsGeometry.SideLeft
else:
side = QgsGeometry.SideRight
miter_limit = self.getParameterValue(self.MITRE_LIMIT)

features = vector.features(layer)
total = 100.0 / len(features)

for current, input_feature in enumerate(features):
output_feature = input_feature
input_geometry = input_feature.geometry()
if input_geometry:
output_geometry = input_geometry.singleSidedBuffer(distance, segments,
side, join_style, miter_limit)
if not output_geometry:
raise GeoAlgorithmExecutionException(
self.tr('Error calculating single sided buffer'))

output_feature.setGeometry(output_geometry)

writer.addFeature(output_feature)
progress.setPercentage(int(current * total))

del writer
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>single_sided_buffer_line</Name>
<ElementPath>single_sided_buffer_line</ElementPath>
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>7</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>11.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>5.70711</ExtentYMax>
</DatasetSpecificInfo>
</GMLFeatureClass>
</GMLFeatureClassList>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-1</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>11</gml:X><gml:Y>5.707106781186548</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:single_sided_buffer_line fid="lines.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>11,5 9,3 9,2 6,2 6,3 8,3 8.01921471959677,3.195090322016128 8.076120467488714,3.38268343236509 8.168530387697455,3.555570233019602 8.292893218813452,3.707106781186547 10.292893218813452,5.707106781186548 11,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line fid="lines.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,-1 -1,-1 -1,0 1,0 1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line fid="lines.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,3 3,2 2,2 2,0 1,0 1,2 1.01921471959677,2.195090322016128 1.076120467488713,2.38268343236509 1.168530387697455,2.555570233019602 1.292893218813453,2.707106781186547 1.444429766980398,2.831469612302545 1.61731656763491,2.923879532511287 1.804909677983872,2.98078528040323 2,3 3,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line fid="lines.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,1 3,1 3,2 5,2 5,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line fid="lines.4">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>10,-3 7,-3 7,-2 10,-2 10,-3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line fid="lines.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>10,1 6,-3 5.292893218813452,-2.292893218813453 9.292893218813452,1.707106781186547 10,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line fid="lines.6">
</ogr:single_sided_buffer_line>
</gml:featureMember>
</ogr:FeatureCollection>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>single_sided_buffer_line_mitre</Name>
<ElementPath>single_sided_buffer_line_mitre</ElementPath>
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>7</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>11.70711</ExtentXMax>
<ExtentYMin>-4.00000</ExtentYMin>
<ExtentYMax>5.00000</ExtentYMax>
</DatasetSpecificInfo>
</GMLFeatureClass>
</GMLFeatureClassList>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-1</gml:X><gml:Y>-4</gml:Y></gml:coord>
<gml:coord><gml:X>11.70710678118655</gml:X><gml:Y>5</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:single_sided_buffer_line_mitre fid="lines.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,2 9,2 9,3 11,5 11.707106781186548,4.292893218813452 10.0,2.585786437626905 10,1 6,1 6,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line_mitre>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line_mitre fid="lines.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-1 1,-1 1,-2 -1,-2 -1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line_mitre>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line_mitre fid="lines.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,0 2,2 3,2 3,3 4,3 4,1 3,1 3,0 2,0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line_mitre>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line_mitre fid="lines.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,1 5,1 5,0 3,0 3,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line_mitre>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line_mitre fid="lines.4">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>7,-3 10,-3 10,-4 7,-4 7,-3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line_mitre>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line_mitre fid="lines.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,-3 10,1 10.707106781186548,0.292893218813453 6.707106781186548,-3.707106781186547 6,-3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_line_mitre>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_line_mitre fid="lines.6">
</ogr:single_sided_buffer_line_mitre>
</gml:featureMember>
</ogr:FeatureCollection>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>single_sided_buffer_multiline_bevel</Name>
<ElementPath>single_sided_buffer_multiline_bevel</ElementPath>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>4</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>6.02404</ExtentXMax>
<ExtentYMin>-1.00000</ExtentYMin>
<ExtentYMax>5.11935</ExtentYMax>
</DatasetSpecificInfo>
</GMLFeatureClass>
</GMLFeatureClassList>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-1</gml:X><gml:Y>-1</gml:Y></gml:coord>
<gml:coord><gml:X>6.024038190337471</gml:X><gml:Y>5.119353882875417</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:single_sided_buffer_multiline_bevel fid="lines.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,-1 -1,-1 -1,0 1,0 1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:single_sided_buffer_multiline_bevel>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_multiline_bevel fid="lines.2">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,1 3,1 3,2 5,2 5,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,1 5.024184261036468,2.414779270633399 6.024038190337471,2.397687750474408 5.999853929301003,0.982908479841009 5,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
</ogr:single_sided_buffer_multiline_bevel>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_multiline_bevel fid="lines.3">
</ogr:single_sided_buffer_multiline_bevel>
</gml:featureMember>
<gml:featureMember>
<ogr:single_sided_buffer_multiline_bevel fid="lines.4">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,3 3,2 2,2 2,0 1,0 1,2 2,3 3,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5.459500959692898,4.119769673704415 2.944337811900192,4.04721689059501 2.915503652020259,5.046801099766013 5.430666799812965,5.119353882875417 5.459500959692898,4.119769673704415</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5.58042226487524,2.946833013435702 3,3 3.020599614854323,3.999787805420657 5.601021879729563,3.946620818856359 5.58042226487524,2.946833013435702</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
</ogr:single_sided_buffer_multiline_bevel>
</gml:featureMember>
</ogr:FeatureCollection>
49 changes: 49 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -821,3 +821,52 @@ tests:
OUTPUT_LAYER:
name: expected/lines_translated.gml
type: vector


- algorithm: qgis:singlesidedbuffer
name: Single sided buffer lines (left, round)
params:
DISTANCE: 1.0
INPUT_LAYER:
name: lines.gml
type: vector
JOIN_STYLE: '0'
MITRE_LIMIT: 2
SEGMENTS: 8
SIDE: '0'
results:
OUTPUT_LAYER:
name: expected/single_sided_buffer_line.gml
type: vector

- algorithm: qgis:singlesidedbuffer
name: Single sided buffer lines (Right, mitre)
params:
DISTANCE: 1.0
INPUT_LAYER:
name: lines.gml
type: vector
JOIN_STYLE: '1'
MITRE_LIMIT: 2
SEGMENTS: 8
SIDE: '1'
results:
OUTPUT_LAYER:
name: expected/single_sided_buffer_line_mitre.gml
type: vector

- algorithm: qgis:singlesidedbuffer
name: Single sided buffer multiline (bevel)
params:
DISTANCE: 1.0
INPUT_LAYER:
name: multilines.gml
type: vector
JOIN_STYLE: '2'
MITRE_LIMIT: 2
SEGMENTS: 8
SIDE: '0'
results:
OUTPUT_LAYER:
name: expected/single_sided_buffer_multiline_bevel.gml
type: vector

0 comments on commit 989d986

Please sign in to comment.