Merge pull request #4870 from nyalldawson/algs3

Port 4 more algs to new API
This commit is contained in:
Nyall Dawson 2017-07-17 07:51:47 +10:00 committed by GitHub
commit 3a2710efe5
11 changed files with 430 additions and 286 deletions

View File

@ -27,24 +27,22 @@ __revision__ = '$Format:%H$'
import os
from qgis.core import (QgsApplication,
QgsWkbTypes,
QgsFeatureSink,
QgsProcessingUtils)
from qgis.core import (QgsFeatureSink,
QgsProcessing,
QgsProcessingException,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterNumber,
QgsProcessingParameterEnum,
QgsProcessingParameterFeatureSink)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
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
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class OffsetLine(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER'
OUTPUT_LAYER = 'OUTPUT_LAYER'
INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
DISTANCE = 'DISTANCE'
SEGMENTS = 'SEGMENTS'
JOIN_STYLE = 'JOIN_STYLE'
@ -57,23 +55,29 @@ class OffsetLine(QgisAlgorithm):
super().__init__()
def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(ParameterNumber(self.DISTANCE,
self.tr('Distance'), default=10.0))
self.addParameter(ParameterNumber(self.SEGMENTS,
self.tr('Segments'), 1, default=8))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'),
[QgsProcessing.TypeVectorLine]))
self.addParameter(QgsProcessingParameterNumber(self.DISTANCE,
self.tr('Distance'),
type=QgsProcessingParameterNumber.Double,
defaultValue=10.0))
self.addParameter(QgsProcessingParameterNumber(self.SEGMENTS,
self.tr('Segments'),
type=QgsProcessingParameterNumber.Integer,
minValue=1, defaultValue=8))
self.join_styles = [self.tr('Round'),
'Mitre',
'Bevel']
self.addParameter(ParameterSelection(
self.addParameter(QgsProcessingParameterEnum(
self.JOIN_STYLE,
self.tr('Join style'),
self.join_styles))
self.addParameter(ParameterNumber(self.MITRE_LIMIT,
self.tr('Mitre limit'), 1, default=2))
options=self.join_styles))
self.addParameter(QgsProcessingParameterNumber(self.MITRE_LIMIT,
self.tr('Mitre limit'), type=QgsProcessingParameterNumber.Double,
minValue=1, defaultValue=2))
self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Offset'), datatype=[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(
QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Offset'), QgsProcessing.TypeVectorLine))
def name(self):
return 'offsetline'
@ -82,31 +86,33 @@ class OffsetLine(QgisAlgorithm):
return self.tr('Offset line')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
source = self.parameterAsSource(parameters, self.INPUT, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs())
writer = self.getOutputFromName(
self.OUTPUT_LAYER).getVectorWriter(layer.fields(), QgsWkbTypes.LineString, layer.crs(), context)
distance = self.parameterAsDouble(parameters, self.DISTANCE, context)
segments = self.parameterAsInt(parameters, self.SEGMENTS, context)
join_style = self.parameterAsEnum(parameters, self.JOIN_STYLE, context) + 1
miter_limit = self.parameterAsDouble(parameters, self.MITRE_LIMIT, context)
distance = self.getParameterValue(self.DISTANCE)
segments = int(self.getParameterValue(self.SEGMENTS))
join_style = self.getParameterValue(self.JOIN_STYLE) + 1
miter_limit = self.getParameterValue(self.MITRE_LIMIT)
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
for current, input_feature in enumerate(features):
if feedback.isCanceled():
break
output_feature = input_feature
input_geometry = input_feature.geometry()
if input_geometry:
output_geometry = input_geometry.offsetCurve(distance, segments, join_style, miter_limit)
if not output_geometry:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr('Error calculating line offset'))
output_feature.setGeometry(output_geometry)
writer.addFeature(output_feature, QgsFeatureSink.FastInsert)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
del writer
return {self.OUTPUT: dest_id}

View File

@ -25,21 +25,19 @@ __copyright__ = '(C) 2016, Nyall Dawson'
__revision__ = '$Format:%H$'
from qgis.core import (QgsApplication,
QgsFeatureSink,
QgsProcessingUtils,
QgsProcessingParameterDefinition)
from qgis.core import (QgsFeatureSink,
QgsProcessingException,
QgsProcessing,
QgsProcessingParameterDefinition,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterNumber,
QgsProcessingParameterFeatureSink)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector, ParameterNumber
from processing.core.outputs import OutputVector
from processing.tools import dataobjects
class Orthogonalize(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER'
OUTPUT_LAYER = 'OUTPUT_LAYER'
INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
MAX_ITERATIONS = 'MAX_ITERATIONS'
DISTANCE_THRESHOLD = 'DISTANCE_THRESHOLD'
ANGLE_TOLERANCE = 'ANGLE_TOLERANCE'
@ -54,20 +52,24 @@ class Orthogonalize(QgisAlgorithm):
super().__init__()
def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_LINE,
dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(ParameterNumber(self.ANGLE_TOLERANCE,
self.tr('Maximum angle tolerance (degrees)'),
0.0, 45.0, 15.0))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'),
[QgsProcessing.TypeVectorLine,
QgsProcessing.TypeVectorPolygon]))
max_iterations = ParameterNumber(self.MAX_ITERATIONS,
self.tr('Maximum algorithm iterations'),
1, 10000, 1000)
self.addParameter(QgsProcessingParameterNumber(self.ANGLE_TOLERANCE,
self.tr('Maximum angle tolerance (degrees)'),
type=QgsProcessingParameterNumber.Double,
minValue=0.0, maxValue=45.0, defaultValue=15.0))
max_iterations = QgsProcessingParameterNumber(self.MAX_ITERATIONS,
self.tr('Maximum algorithm iterations'),
type=QgsProcessingParameterNumber.Integer,
minValue=1, maxValue=10000, defaultValue=1000)
max_iterations.setFlags(max_iterations.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
self.addParameter(max_iterations)
self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Orthogonalized')))
self.addParameter(
QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Orthogonalized')))
def name(self):
return 'orthogonalize'
@ -76,27 +78,31 @@ class Orthogonalize(QgisAlgorithm):
return self.tr('Orthogonalize')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
max_iterations = self.getParameterValue(self.MAX_ITERATIONS)
angle_tolerance = self.getParameterValue(self.ANGLE_TOLERANCE)
writer = self.getOutputFromName(
self.OUTPUT_LAYER).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), context)
source = self.parameterAsSource(parameters, self.INPUT, context)
max_iterations = self.parameterAsInt(parameters, self.MAX_ITERATIONS, context)
angle_tolerance = self.parameterAsDouble(parameters, self.ANGLE_TOLERANCE, context)
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs())
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
for current, input_feature in enumerate(features):
if feedback.isCanceled():
break
output_feature = input_feature
input_geometry = input_feature.geometry()
if input_geometry:
output_geometry = input_geometry.orthogonalize(1.0e-8, max_iterations, angle_tolerance)
if not output_geometry:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr('Error orthogonalizing geometry'))
output_feature.setGeometry(output_geometry)
writer.addFeature(output_feature, QgsFeatureSink.FastInsert)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
del writer
return {self.OUTPUT: dest_id}

View File

@ -27,25 +27,29 @@ __revision__ = '$Format:%H$'
import os
from qgis.core import QgsWkbTypes, QgsField, NULL, QgsFeatureSink, QgsProcessingUtils
from qgis.core import (QgsWkbTypes,
QgsField,
NULL,
QgsFeatureSink,
QgsProcessing,
QgsProcessingException,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterNumber,
QgsProcessingParameterFeatureSink)
from qgis.PyQt.QtCore import QVariant
from qgis.PyQt.QtGui import QIcon
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector, ParameterNumber
from processing.core.outputs import OutputVector
from processing.tools import dataobjects
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class PoleOfInaccessibility(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER'
INPUT = 'INPUT'
TOLERANCE = 'TOLERANCE'
OUTPUT_LAYER = 'OUTPUT_LAYER'
OUTPUT = 'OUTPUT'
def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'centroids.png'))
@ -60,12 +64,15 @@ class PoleOfInaccessibility(QgisAlgorithm):
super().__init__()
def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer'),
[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(ParameterNumber(self.TOLERANCE,
self.tr('Tolerance (layer units)'), default=1.0, minValue=0.0))
self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Point'), datatype=[dataobjects.TYPE_VECTOR_POINT]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'),
[QgsProcessing.TypeVectorPolygon]))
self.addParameter(QgsProcessingParameterNumber(self.TOLERANCE,
self.tr('Tolerance (layer units)'),
type=QgsProcessingParameterNumber.Double,
defaultValue=1.0, minValue=0.0))
self.addParameter(
QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Point'), QgsProcessing.TypeVectorPoint))
def name(self):
return 'poleofinaccessibility'
@ -74,25 +81,27 @@ class PoleOfInaccessibility(QgisAlgorithm):
return self.tr('Pole of Inaccessibility')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
tolerance = self.getParameterValue(self.TOLERANCE)
source = self.parameterAsSource(parameters, self.INPUT, context)
tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context)
fields = layer.fields()
fields = source.fields()
fields.append(QgsField('dist_pole', QVariant.Double))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Point, source.sourceCrs())
writer = self.getOutputFromName(
self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context)
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
for current, input_feature in enumerate(features):
if feedback.isCanceled():
break
output_feature = input_feature
input_geometry = input_feature.geometry()
if input_geometry:
output_geometry, distance = input_geometry.poleOfInaccessibility(tolerance)
if not output_geometry:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr('Error calculating pole of inaccessibility'))
attrs = input_feature.attributes()
attrs.append(distance)
@ -104,7 +113,7 @@ class PoleOfInaccessibility(QgisAlgorithm):
attrs.append(NULL)
output_feature.setAttributes(attrs)
writer.addFeature(output_feature, QgsFeatureSink.FastInsert)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
del writer
return {self.OUTPUT: dest_id}

View File

@ -70,15 +70,19 @@ from .LinesToPolygons import LinesToPolygons
from .MeanCoords import MeanCoords
from .Merge import Merge
from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
from .OffsetLine import OffsetLine
from .Orthogonalize import Orthogonalize
from .PointDistance import PointDistance
from .PointOnSurface import PointOnSurface
from .PointsInPolygon import PointsInPolygon
from .PointsLayerFromTable import PointsLayerFromTable
from .PoleOfInaccessibility import PoleOfInaccessibility
from .PolygonsToLines import PolygonsToLines
from .PostGISExecuteSQL import PostGISExecuteSQL
from .RandomExtract import RandomExtract
from .RandomExtractWithinSubsets import RandomExtractWithinSubsets
from .RegularPoints import RegularPoints
from .ReverseLineDirection import ReverseLineDirection
from .Ruggedness import Ruggedness
from .SaveSelectedFeatures import SaveSelectedFeatures
from .SelectByAttribute import SelectByAttribute
@ -139,13 +143,11 @@ from .ZonalStatistics import ZonalStatistics
# from .FieldsMapper import FieldsMapper
# from .Datasources2Vrt import Datasources2Vrt
# from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
# from .ReverseLineDirection import ReverseLineDirection
# from .SpatialIndex import SpatialIndex
# from .DefineProjection import DefineProjection
# from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable
# from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed
# from .MergeLines import MergeLines
# from .OffsetLine import OffsetLine
# from .Translate import Translate
# from .SingleSidedBuffer import SingleSidedBuffer
# from .PointsAlongGeometry import PointsAlongGeometry
@ -155,9 +157,7 @@ from .ZonalStatistics import ZonalStatistics
# from .ExtendLines import ExtendLines
# from .ExtractSpecificNodes import ExtractSpecificNodes
# from .GeometryByExpression import GeometryByExpression
# from .PoleOfInaccessibility import PoleOfInaccessibility
# from .RasterCalculator import RasterCalculator
# from .Orthogonalize import Orthogonalize
# from .ShortestPathPointToPoint import ShortestPathPointToPoint
# from .ShortestPathPointToLayer import ShortestPathPointToLayer
# from .ShortestPathLayerToPoint import ShortestPathLayerToPoint
@ -211,19 +211,16 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
# SplitWithLines(), CreateConstantRaster(),
# FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(),
# OrientedMinimumBoundingBox(),
# ReverseLineDirection(), SpatialIndex(), DefineProjection(),
# SpatialIndex(), DefineProjection(),
# RectanglesOvalsDiamondsVariable(),
# RectanglesOvalsDiamondsFixed(), MergeLines(),
# OffsetLine(), Translate(),
# Translate(),
# SingleSidedBuffer(), PointsAlongGeometry(),
# Slope(), Ruggedness(), Hillshade(),
# Relief(),
# IdwInterpolation(), TinInterpolation(),
# ExtendLines(), ExtractSpecificNodes(),
# GeometryByExpression(),
# PoleOfInaccessibility(),
#
# RasterCalculator(), Heatmap(), Orthogonalize(),
# RasterCalculator(),
# ShortestPathPointToPoint(), ShortestPathPointToLayer(),
# ShortestPathLayerToPoint(), ServiceAreaFromPoint(),
# ServiceAreaFromLayer(), TruncateTable(), Polygonize(),
@ -260,15 +257,19 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
MeanCoords(),
Merge(),
NearestNeighbourAnalysis(),
OffsetLine(),
Orthogonalize(),
PointDistance(),
PointOnSurface(),
PointsInPolygon(),
PointsLayerFromTable(),
PoleOfInaccessibility(),
PolygonsToLines(),
PostGISExecuteSQL(),
RandomExtract(),
RandomExtractWithinSubsets(),
RegularPoints(),
ReverseLineDirection(),
Ruggedness(),
SaveSelectedFeatures(),
SelectByAttribute(),

View File

@ -25,22 +25,20 @@ __copyright__ = '(C) 2015, Nyall Dawson'
__revision__ = '$Format:%H$'
from qgis.core import (QgsApplication,
QgsGeometry,
from qgis.core import (QgsGeometry,
QgsFeature,
QgsFeatureSink,
QgsProcessingUtils)
QgsProcessingException,
QgsProcessing,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
from processing.core.outputs import OutputVector
from processing.tools import dataobjects
class ReverseLineDirection(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER'
OUTPUT_LAYER = 'OUTPUT_LAYER'
INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
def group(self):
return self.tr('Vector geometry tools')
@ -49,9 +47,10 @@ class ReverseLineDirection(QgisAlgorithm):
super().__init__()
def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_LINE]))
self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Reversed'), datatype=[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'),
[QgsProcessing.TypeVectorLine]))
self.addParameter(
QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Reversed'), QgsProcessing.TypeVectorLine))
def name(self):
return 'reverselinedirection'
@ -60,30 +59,28 @@ class ReverseLineDirection(QgisAlgorithm):
return self.tr('Reverse line direction')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
source = self.parameterAsSource(parameters, self.INPUT, context)
writer = self.getOutputFromName(
self.OUTPUT_LAYER).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs())
outFeat = QgsFeature()
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
for current, inFeat in enumerate(features):
inGeom = inFeat.geometry()
attrs = inFeat.attributes()
if feedback.isCanceled():
break
outGeom = None
if not inGeom.isNull():
outFeat = inFeat
if inFeat.geometry():
inGeom = inFeat.geometry()
reversedLine = inGeom.geometry().reversed()
if not reversedLine:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr('Error reversing line'))
outGeom = QgsGeometry(reversedLine)
outFeat.setGeometry(outGeom)
outFeat.setAttributes(attrs)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
outFeat.setGeometry(outGeom)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
del writer
return {self.OUTPUT: dest_id}

View File

@ -0,0 +1,16 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>lines_reversed</Name>
<ElementPath>lines_reversed</ElementPath>
<!--LINESTRING-->
<GeometryType>2</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>7</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>11.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>5.00000</ExtentYMax>
</DatasetSpecificInfo>
</GMLFeatureClass>
</GMLFeatureClassList>

View File

@ -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</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>
<gml:featureMember>
<ogr:lines_reversed fid="lines.0">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>11,5 9,3 9,2 6,2</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:lines_reversed>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_reversed fid="lines.1">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>1,-1 -1,-1</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:lines_reversed>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_reversed fid="lines.2">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>3,3 3,2 2,2 2,0</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:lines_reversed>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_reversed fid="lines.3">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>5,1 3,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:lines_reversed>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_reversed fid="lines.4">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>10,-3 7,-3</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:lines_reversed>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_reversed fid="lines.5">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>10,1 6,-3</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:lines_reversed>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_reversed fid="lines.6">
</ogr:lines_reversed>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -13,12 +13,12 @@
<gml:featureMember>
<ogr:multiline_offset fid="lines.1">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1,0 1,0</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>-1,0 1,0</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:multiline_offset>
</gml:featureMember>
<gml:featureMember>
<ogr:multiline_offset fid="lines.2">
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>3,2 5,2</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>6.024038190337471,2.397687750474408 5.999853929301003,0.982908479841009</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>3,2 5,2</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>6.02403819033747,2.39768775047441 5.999853929301,0.982908479841009</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:multiline_offset>
</gml:featureMember>
<gml:featureMember>
@ -27,7 +27,7 @@
</gml:featureMember>
<gml:featureMember>
<ogr:multiline_offset fid="lines.4">
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>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</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>2.915503652020259,5.046801099766013 5.430666799812965,5.119353882875417</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>3.020599614854323,3.999787805420657 5.601021879729563,3.946620818856359</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>1,0 1,2 1.01921471959677,2.19509032201613 1.07612046748871,2.38268343236509 1.16853038769745,2.5555702330196 1.29289321881345,2.70710678118655 1.4444297669804,2.83146961230255 1.61731656763491,2.92387953251129 1.80490967798387,2.98078528040323 2,3</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>2.91550365202026,5.04680109976601 5.43066679981296,5.11935388287542</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>3.02059961485432,3.99978780542066 5.60102187972956,3.94662081885636</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:multiline_offset>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -555,96 +555,110 @@ tests:
name: expected/point_on_line.gml
type: vector
# - algorithm: qgis:offsetline
# name: Offset line positive
# params:
# DISTANCE: 1.0
# INPUT_LAYER:
# name: lines.gml
# type: vector
# JOIN_STYLE: '0'
# MITRE_LIMIT: 2
# SEGMENTS: 8
# results:
# OUTPUT_LAYER:
# name: expected/line_offset_round_positive.gml
# type: vector
# compare:
# geometry:
# precision: 7
#
# - algorithm: qgis:offsetline
# name: Offset line negative
# params:
# DISTANCE: -1.0
# INPUT_LAYER:
# name: lines.gml
# type: vector
# JOIN_STYLE: '0'
# MITRE_LIMIT: 2
# SEGMENTS: 8
# results:
# OUTPUT_LAYER:
# name: expected/line_offset_round_negative.gml
# type: vector
# compare:
# geometry:
# precision: 7
#
# - algorithm: qgis:offsetline
# name: Offset line mitre
# params:
# DISTANCE: 1.0
# INPUT_LAYER:
# name: lines.gml
# type: vector
# JOIN_STYLE: '1'
# MITRE_LIMIT: 2
# SEGMENTS: 4
# results:
# OUTPUT_LAYER:
# name: expected/line_offset_mitre.gml
# type: vector
# compare:
# geometry:
# precision: 7
#
# - algorithm: qgis:offsetline
# name: Offset line bevel
# params:
# DISTANCE: 1.0
# INPUT_LAYER:
# name: lines.gml
# type: vector
# JOIN_STYLE: '2'
# MITRE_LIMIT: 2
# SEGMENTS: 8
# results:
# OUTPUT_LAYER:
# name: expected/line_offset_bevel.gml
# type: vector
# compare:
# geometry:
# precision: 7
#
# - algorithm: qgis:offsetline
# name: Offset multilines
# params:
# DISTANCE: 1.0
# INPUT_LAYER:
# name: multilines.gml
# type: vector
# JOIN_STYLE: '0'
# MITRE_LIMIT: 2
# SEGMENTS: 8
# results:
# OUTPUT_LAYER:
# name: expected/multiline_offset.gml
# type: vector
# compare:
# geometry:
# precision: 7
#
- algorithm: qgis:reverselinedirection
name: Reverse line direction
params:
INPUT:
name: lines.gml
type: vector
results:
OUTPUT:
name: expected/lines_reversed.gml
type: vector
compare:
geometry:
precision: 7
- algorithm: qgis:offsetline
name: Offset line positive
params:
DISTANCE: 1.0
INPUT:
name: lines.gml
type: vector
JOIN_STYLE: '0'
MITRE_LIMIT: 2
SEGMENTS: 8
results:
OUTPUT:
name: expected/line_offset_round_positive.gml
type: vector
compare:
geometry:
precision: 7
- algorithm: qgis:offsetline
name: Offset line negative
params:
DISTANCE: -1.0
INPUT:
name: lines.gml
type: vector
JOIN_STYLE: '0'
MITRE_LIMIT: 2
SEGMENTS: 8
results:
OUTPUT:
name: expected/line_offset_round_negative.gml
type: vector
compare:
geometry:
precision: 7
- algorithm: qgis:offsetline
name: Offset line mitre
params:
DISTANCE: 1.0
INPUT:
name: lines.gml
type: vector
JOIN_STYLE: '1'
MITRE_LIMIT: 2
SEGMENTS: 4
results:
OUTPUT:
name: expected/line_offset_mitre.gml
type: vector
compare:
geometry:
precision: 7
- algorithm: qgis:offsetline
name: Offset line bevel
params:
DISTANCE: 1.0
INPUT:
name: lines.gml
type: vector
JOIN_STYLE: '2'
MITRE_LIMIT: 2
SEGMENTS: 8
results:
OUTPUT:
name: expected/line_offset_bevel.gml
type: vector
compare:
geometry:
precision: 7
- algorithm: qgis:offsetline
name: Offset multilines
params:
DISTANCE: 1.0
INPUT:
name: multilines.gml
type: vector
JOIN_STYLE: '0'
MITRE_LIMIT: 2
SEGMENTS: 8
results:
OUTPUT:
name: expected/multiline_offset.gml
type: vector
compare:
geometry:
precision: 7
# - algorithm: qgis:fixeddistancebuffer
# name: Buffer polygons using bevel
# params:
@ -1481,21 +1495,21 @@ tests:
name: expected/snap_internal.gml
type: vector
# - algorithm: qgis:poleofinaccessibility
# name: Pole of inaccessibility (polygons)
# params:
# INPUT_LAYER:
# name: polys.gml
# type: vector
# TOLERANCE: 1.0e-05
# results:
# OUTPUT_LAYER:
# name: expected/pole_inaccessibility_polys.gml
# type: vector
# compare:
# geometry:
# precision: 7
#
- algorithm: qgis:poleofinaccessibility
name: Pole of inaccessibility (polygons)
params:
INPUT:
name: polys.gml
type: vector
TOLERANCE: 1.0e-05
results:
OUTPUT:
name: expected/pole_inaccessibility_polys.gml
type: vector
compare:
geometry:
precision: 7
- algorithm: native:extractbyattribute
name: Extract by attribute (is null)
params:
@ -1945,34 +1959,34 @@ tests:
# compare:
# geometry:
# precision: 7
#
# - algorithm: qgis:orthogonalize
# name: Orthogonalize polys
# params:
# INPUT_LAYER:
# name: custom/polys_to_orth.gml
# type: vector
# results:
# OUTPUT_LAYER:
# name: expected/orthagonal_polys.gml
# type: vector
# compare:
# geometry:
# precision: 7
#
# - algorithm: qgis:orthogonalize
# name: Orthogonalize lines
# params:
# INPUT_LAYER:
# name: custom/lines_to_orth.gml
# type: vector
# results:
# OUTPUT_LAYER:
# name: expected/orthagonal_lines.gml
# type: vector
# compare:
# geometry:
# precision: 7
- algorithm: qgis:orthogonalize
name: Orthogonalize polys
params:
INPUT:
name: custom/polys_to_orth.gml
type: vector
results:
OUTPUT:
name: expected/orthagonal_polys.gml
type: vector
compare:
geometry:
precision: 7
- algorithm: qgis:orthogonalize
name: Orthogonalize lines
params:
INPUT:
name: custom/lines_to_orth.gml
type: vector
results:
OUTPUT:
name: expected/orthagonal_lines.gml
type: vector
compare:
geometry:
precision: 7
- algorithm: native:reprojectlayer

View File

@ -155,25 +155,10 @@ Cell *getCentroidCell( const QgsPolygonV2 *polygon )
return new Cell( x / area, y / area, 0.0, polygon );
}
///@endcond
QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision, double *distanceFromBoundary ) const
QgsPoint surfacePoleOfInaccessibility( const QgsSurface *surface, double precision, double &distanceFromBoundary )
{
if ( distanceFromBoundary )
*distanceFromBoundary = DBL_MAX;
if ( !mGeometry || mGeometry->isEmpty() )
return QgsGeometry();
if ( precision <= 0 )
return QgsGeometry();
const QgsSurface *surface = dynamic_cast< const QgsSurface * >( mGeometry );
if ( !surface )
return QgsGeometry();
std::unique_ptr< QgsPolygonV2 > segmentizedPoly;
const QgsPolygonV2 *polygon = dynamic_cast< const QgsPolygonV2 * >( mGeometry );
const QgsPolygonV2 *polygon = dynamic_cast< const QgsPolygonV2 * >( surface );
if ( !polygon )
{
segmentizedPoly.reset( static_cast< QgsPolygonV2 *>( surface->segmentize() ) );
@ -187,7 +172,7 @@ QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision,
double cellSize = qMin( bounds.width(), bounds.height() );
if ( qgsDoubleNear( cellSize, 0.0 ) )
return QgsGeometry( new QgsPoint( bounds.xMinimum(), bounds.yMinimum() ) );
return QgsPoint( bounds.xMinimum(), bounds.yMinimum() );
double h = cellSize / 2.0;
std::priority_queue< Cell *, std::vector<Cell *>, GreaterThanByMax > cellQueue;
@ -238,15 +223,70 @@ QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision,
cellQueue.push( new Cell( currentCell->x + h, currentCell->y + h, h, polygon ) );
}
if ( distanceFromBoundary )
*distanceFromBoundary = bestCell->d;
distanceFromBoundary = bestCell->d;
return QgsGeometry( new QgsPoint( bestCell->x, bestCell->y ) );
return QgsPoint( bestCell->x, bestCell->y );
}
///@endcond
QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision, double *distanceFromBoundary ) const
{
if ( distanceFromBoundary )
*distanceFromBoundary = DBL_MAX;
if ( !mGeometry || mGeometry->isEmpty() )
return QgsGeometry();
if ( precision <= 0 )
return QgsGeometry();
if ( const QgsGeometryCollection *gc = dynamic_cast< const QgsGeometryCollection *>( mGeometry ) )
{
int numGeom = gc->numGeometries();
double maxDist = 0;
QgsPoint bestPoint;
bool found = false;
for ( int i = 0; i < numGeom; ++i )
{
const QgsSurface *surface = dynamic_cast< const QgsSurface * >( gc->geometryN( i ) );
if ( !surface )
continue;
found = true;
double dist = DBL_MAX;
QgsPoint p = surfacePoleOfInaccessibility( surface, precision, dist );
if ( dist > maxDist )
{
maxDist = dist;
bestPoint = p;
}
}
if ( !found )
return QgsGeometry();
if ( distanceFromBoundary )
*distanceFromBoundary = maxDist;
return QgsGeometry( new QgsPoint( bestPoint ) );
}
else
{
const QgsSurface *surface = dynamic_cast< const QgsSurface * >( mGeometry );
if ( !surface )
return QgsGeometry();
double dist = DBL_MAX;
QgsPoint p = surfacePoleOfInaccessibility( surface, precision, dist );
if ( distanceFromBoundary )
*distanceFromBoundary = dist;
return QgsGeometry( new QgsPoint( p ) );
}
}
// helpers for orthogonalize
// adapted from original code in potlach/id osm editor
// adapted from original code in potlatch/id osm editor
bool dotProductWithinAngleTolerance( double dotProduct, double lowerThreshold, double upperThreshold )
{

View File

@ -5451,6 +5451,13 @@ void TestQgsGeometry::poleOfInaccessibility()
point = curved.poleOfInaccessibility( 0.01 ).asPoint();
QGSCOMPARENEAR( point.x(), -0.4324, 0.0001 );
QGSCOMPARENEAR( point.y(), -0.2434, 0.0001 );
// multipolygon
QgsGeometry multiPoly = QgsGeometry::fromWkt( QStringLiteral( "MultiPolygon (((0 0, 10 0, 10 10, 0 10, 0 0)),((30 30, 50 30, 50 60, 30 60, 30 30)))" ) );
point = multiPoly.poleOfInaccessibility( 0.01, &distance ).asPoint();
QGSCOMPARENEAR( point.x(), 40, 0.0001 );
QGSCOMPARENEAR( point.y(), 45, 0.0001 );
QGSCOMPARENEAR( distance, 10.0, 0.00001 );
}
void TestQgsGeometry::makeValid()