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

View File

@ -27,25 +27,29 @@ __revision__ = '$Format:%H$'
import os 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.QtCore import QVariant
from qgis.PyQt.QtGui import QIcon from qgis.PyQt.QtGui import QIcon
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm 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] pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class PoleOfInaccessibility(QgisAlgorithm): class PoleOfInaccessibility(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER' INPUT = 'INPUT'
TOLERANCE = 'TOLERANCE' TOLERANCE = 'TOLERANCE'
OUTPUT_LAYER = 'OUTPUT_LAYER' OUTPUT = 'OUTPUT'
def icon(self): def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'centroids.png')) return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'centroids.png'))
@ -60,12 +64,15 @@ class PoleOfInaccessibility(QgisAlgorithm):
super().__init__() super().__init__()
def initAlgorithm(self, config=None): def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT_LAYER, self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'),
self.tr('Input layer'), [QgsProcessing.TypeVectorPolygon]))
[dataobjects.TYPE_VECTOR_POLYGON])) self.addParameter(QgsProcessingParameterNumber(self.TOLERANCE,
self.addParameter(ParameterNumber(self.TOLERANCE, self.tr('Tolerance (layer units)'),
self.tr('Tolerance (layer units)'), default=1.0, minValue=0.0)) type=QgsProcessingParameterNumber.Double,
self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Point'), datatype=[dataobjects.TYPE_VECTOR_POINT])) defaultValue=1.0, minValue=0.0))
self.addParameter(
QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Point'), QgsProcessing.TypeVectorPoint))
def name(self): def name(self):
return 'poleofinaccessibility' return 'poleofinaccessibility'
@ -74,25 +81,27 @@ class PoleOfInaccessibility(QgisAlgorithm):
return self.tr('Pole of Inaccessibility') return self.tr('Pole of Inaccessibility')
def processAlgorithm(self, parameters, context, feedback): def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) source = self.parameterAsSource(parameters, self.INPUT, context)
tolerance = self.getParameterValue(self.TOLERANCE) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context)
fields = layer.fields() fields = source.fields()
fields.append(QgsField('dist_pole', QVariant.Double)) fields.append(QgsField('dist_pole', QVariant.Double))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Point, source.sourceCrs())
writer = self.getOutputFromName( features = source.getFeatures()
self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context) total = 100.0 / source.featureCount() if source.featureCount() else 0
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
for current, input_feature in enumerate(features): for current, input_feature in enumerate(features):
if feedback.isCanceled():
break
output_feature = input_feature output_feature = input_feature
input_geometry = input_feature.geometry() input_geometry = input_feature.geometry()
if input_geometry: if input_geometry:
output_geometry, distance = input_geometry.poleOfInaccessibility(tolerance) output_geometry, distance = input_geometry.poleOfInaccessibility(tolerance)
if not output_geometry: if not output_geometry:
raise GeoAlgorithmExecutionException( raise QgsProcessingException(
self.tr('Error calculating pole of inaccessibility')) self.tr('Error calculating pole of inaccessibility'))
attrs = input_feature.attributes() attrs = input_feature.attributes()
attrs.append(distance) attrs.append(distance)
@ -104,7 +113,7 @@ class PoleOfInaccessibility(QgisAlgorithm):
attrs.append(NULL) attrs.append(NULL)
output_feature.setAttributes(attrs) output_feature.setAttributes(attrs)
writer.addFeature(output_feature, QgsFeatureSink.FastInsert) sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) 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 .MeanCoords import MeanCoords
from .Merge import Merge from .Merge import Merge
from .NearestNeighbourAnalysis import NearestNeighbourAnalysis from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
from .OffsetLine import OffsetLine
from .Orthogonalize import Orthogonalize
from .PointDistance import PointDistance from .PointDistance import PointDistance
from .PointOnSurface import PointOnSurface from .PointOnSurface import PointOnSurface
from .PointsInPolygon import PointsInPolygon from .PointsInPolygon import PointsInPolygon
from .PointsLayerFromTable import PointsLayerFromTable from .PointsLayerFromTable import PointsLayerFromTable
from .PoleOfInaccessibility import PoleOfInaccessibility
from .PolygonsToLines import PolygonsToLines from .PolygonsToLines import PolygonsToLines
from .PostGISExecuteSQL import PostGISExecuteSQL from .PostGISExecuteSQL import PostGISExecuteSQL
from .RandomExtract import RandomExtract from .RandomExtract import RandomExtract
from .RandomExtractWithinSubsets import RandomExtractWithinSubsets from .RandomExtractWithinSubsets import RandomExtractWithinSubsets
from .RegularPoints import RegularPoints from .RegularPoints import RegularPoints
from .ReverseLineDirection import ReverseLineDirection
from .Ruggedness import Ruggedness from .Ruggedness import Ruggedness
from .SaveSelectedFeatures import SaveSelectedFeatures from .SaveSelectedFeatures import SaveSelectedFeatures
from .SelectByAttribute import SelectByAttribute from .SelectByAttribute import SelectByAttribute
@ -139,13 +143,11 @@ from .ZonalStatistics import ZonalStatistics
# from .FieldsMapper import FieldsMapper # from .FieldsMapper import FieldsMapper
# from .Datasources2Vrt import Datasources2Vrt # from .Datasources2Vrt import Datasources2Vrt
# from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox # from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
# from .ReverseLineDirection import ReverseLineDirection
# from .SpatialIndex import SpatialIndex # from .SpatialIndex import SpatialIndex
# from .DefineProjection import DefineProjection # from .DefineProjection import DefineProjection
# from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable # from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable
# from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed # from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed
# from .MergeLines import MergeLines # from .MergeLines import MergeLines
# from .OffsetLine import OffsetLine
# from .Translate import Translate # from .Translate import Translate
# from .SingleSidedBuffer import SingleSidedBuffer # from .SingleSidedBuffer import SingleSidedBuffer
# from .PointsAlongGeometry import PointsAlongGeometry # from .PointsAlongGeometry import PointsAlongGeometry
@ -155,9 +157,7 @@ from .ZonalStatistics import ZonalStatistics
# from .ExtendLines import ExtendLines # from .ExtendLines import ExtendLines
# from .ExtractSpecificNodes import ExtractSpecificNodes # from .ExtractSpecificNodes import ExtractSpecificNodes
# from .GeometryByExpression import GeometryByExpression # from .GeometryByExpression import GeometryByExpression
# from .PoleOfInaccessibility import PoleOfInaccessibility
# from .RasterCalculator import RasterCalculator # from .RasterCalculator import RasterCalculator
# from .Orthogonalize import Orthogonalize
# from .ShortestPathPointToPoint import ShortestPathPointToPoint # from .ShortestPathPointToPoint import ShortestPathPointToPoint
# from .ShortestPathPointToLayer import ShortestPathPointToLayer # from .ShortestPathPointToLayer import ShortestPathPointToLayer
# from .ShortestPathLayerToPoint import ShortestPathLayerToPoint # from .ShortestPathLayerToPoint import ShortestPathLayerToPoint
@ -211,19 +211,16 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
# SplitWithLines(), CreateConstantRaster(), # SplitWithLines(), CreateConstantRaster(),
# FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(), # FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(),
# OrientedMinimumBoundingBox(), # OrientedMinimumBoundingBox(),
# ReverseLineDirection(), SpatialIndex(), DefineProjection(), # SpatialIndex(), DefineProjection(),
# RectanglesOvalsDiamondsVariable(), # RectanglesOvalsDiamondsVariable(),
# RectanglesOvalsDiamondsFixed(), MergeLines(), # RectanglesOvalsDiamondsFixed(), MergeLines(),
# OffsetLine(), Translate(), # Translate(),
# SingleSidedBuffer(), PointsAlongGeometry(), # SingleSidedBuffer(), PointsAlongGeometry(),
# Slope(), Ruggedness(), Hillshade(),
# Relief(), # Relief(),
# IdwInterpolation(), TinInterpolation(), # IdwInterpolation(), TinInterpolation(),
# ExtendLines(), ExtractSpecificNodes(), # ExtendLines(), ExtractSpecificNodes(),
# GeometryByExpression(), # GeometryByExpression(),
# PoleOfInaccessibility(), # RasterCalculator(),
#
# RasterCalculator(), Heatmap(), Orthogonalize(),
# ShortestPathPointToPoint(), ShortestPathPointToLayer(), # ShortestPathPointToPoint(), ShortestPathPointToLayer(),
# ShortestPathLayerToPoint(), ServiceAreaFromPoint(), # ShortestPathLayerToPoint(), ServiceAreaFromPoint(),
# ServiceAreaFromLayer(), TruncateTable(), Polygonize(), # ServiceAreaFromLayer(), TruncateTable(), Polygonize(),
@ -260,15 +257,19 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
MeanCoords(), MeanCoords(),
Merge(), Merge(),
NearestNeighbourAnalysis(), NearestNeighbourAnalysis(),
OffsetLine(),
Orthogonalize(),
PointDistance(), PointDistance(),
PointOnSurface(), PointOnSurface(),
PointsInPolygon(), PointsInPolygon(),
PointsLayerFromTable(), PointsLayerFromTable(),
PoleOfInaccessibility(),
PolygonsToLines(), PolygonsToLines(),
PostGISExecuteSQL(), PostGISExecuteSQL(),
RandomExtract(), RandomExtract(),
RandomExtractWithinSubsets(), RandomExtractWithinSubsets(),
RegularPoints(), RegularPoints(),
ReverseLineDirection(),
Ruggedness(), Ruggedness(),
SaveSelectedFeatures(), SaveSelectedFeatures(),
SelectByAttribute(), SelectByAttribute(),

View File

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

View File

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

View File

@ -155,25 +155,10 @@ Cell *getCentroidCell( const QgsPolygonV2 *polygon )
return new Cell( x / area, y / area, 0.0, polygon ); return new Cell( x / area, y / area, 0.0, polygon );
} }
///@endcond QgsPoint surfacePoleOfInaccessibility( const QgsSurface *surface, double precision, double &distanceFromBoundary )
QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision, double *distanceFromBoundary ) const
{ {
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; std::unique_ptr< QgsPolygonV2 > segmentizedPoly;
const QgsPolygonV2 *polygon = dynamic_cast< const QgsPolygonV2 * >( mGeometry ); const QgsPolygonV2 *polygon = dynamic_cast< const QgsPolygonV2 * >( surface );
if ( !polygon ) if ( !polygon )
{ {
segmentizedPoly.reset( static_cast< QgsPolygonV2 *>( surface->segmentize() ) ); segmentizedPoly.reset( static_cast< QgsPolygonV2 *>( surface->segmentize() ) );
@ -187,7 +172,7 @@ QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision,
double cellSize = qMin( bounds.width(), bounds.height() ); double cellSize = qMin( bounds.width(), bounds.height() );
if ( qgsDoubleNear( cellSize, 0.0 ) ) if ( qgsDoubleNear( cellSize, 0.0 ) )
return QgsGeometry( new QgsPoint( bounds.xMinimum(), bounds.yMinimum() ) ); return QgsPoint( bounds.xMinimum(), bounds.yMinimum() );
double h = cellSize / 2.0; double h = cellSize / 2.0;
std::priority_queue< Cell *, std::vector<Cell *>, GreaterThanByMax > cellQueue; 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 ) ); 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 // 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 ) bool dotProductWithinAngleTolerance( double dotProduct, double lowerThreshold, double upperThreshold )
{ {

View File

@ -5451,6 +5451,13 @@ void TestQgsGeometry::poleOfInaccessibility()
point = curved.poleOfInaccessibility( 0.01 ).asPoint(); point = curved.poleOfInaccessibility( 0.01 ).asPoint();
QGSCOMPARENEAR( point.x(), -0.4324, 0.0001 ); QGSCOMPARENEAR( point.x(), -0.4324, 0.0001 );
QGSCOMPARENEAR( point.y(), -0.2434, 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() void TestQgsGeometry::makeValid()