diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 6349729559e..cf9f871a1bc 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -84,6 +84,7 @@ from .PolygonsToLines import PolygonsToLines from .PostGISExecuteSQL import PostGISExecuteSQL from .RandomExtract import RandomExtract from .RandomExtractWithinSubsets import RandomExtractWithinSubsets +from .RandomPointsAlongLines import RandomPointsAlongLines from .RandomPointsExtent import RandomPointsExtent from .RandomPointsLayer import RandomPointsLayer from .RandomPointsPolygons import RandomPointsPolygons @@ -144,7 +145,6 @@ from .ZonalStatistics import ZonalStatistics # from .PointsDisplacement import PointsDisplacement # from .PointsFromPolygons import PointsFromPolygons # from .PointsFromLines import PointsFromLines -# from .RandomPointsAlongLines import RandomPointsAlongLines # from .PointsToPaths import PointsToPaths # from .SetVectorStyle import SetVectorStyle # from .SetRasterStyle import SetRasterStyle @@ -271,6 +271,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): PostGISExecuteSQL(), RandomExtract(), RandomExtractWithinSubsets(), + RandomPointsAlongLines(), RandomPointsExtent(), RandomPointsLayer(), RandomPointsPolygons(), diff --git a/python/plugins/processing/algs/qgis/RandomPointsAlongLines.py b/python/plugins/processing/algs/qgis/RandomPointsAlongLines.py index 638b90ca461..fd56b8de702 100644 --- a/python/plugins/processing/algs/qgis/RandomPointsAlongLines.py +++ b/python/plugins/processing/algs/qgis/RandomPointsAlongLines.py @@ -29,33 +29,37 @@ __revision__ = '$Format:%H$' import random from qgis.PyQt.QtCore import QVariant -from qgis.core import (QgsApplication, +from qgis.core import (QgsField, QgsFeatureSink, - QgsFields, - QgsField, - QgsGeometry, - QgsSpatialIndex, - QgsWkbTypes, - QgsDistanceArea, - QgsFeatureRequest, QgsFeature, + QgsFields, + QgsGeometry, + QgsPoint, QgsPointXY, - QgsMessageLog, + QgsWkbTypes, + QgsSpatialIndex, + QgsFeatureRequest, + QgsDistanceArea, QgsProject, - QgsProcessingUtils) + QgsProcessing, + QgsProcessingException, + QgsProcessingParameterNumber, + QgsProcessingParameterBoolean, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterFeatureSink, + QgsProcessingParameterDefinition) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterNumber -from processing.core.outputs import OutputVector -from processing.tools import dataobjects, vector +from processing.tools import vector class RandomPointsAlongLines(QgisAlgorithm): - VECTOR = 'VECTOR' - POINT_NUMBER = 'POINT_NUMBER' + INPUT = 'INPUT' + POINTS_NUMBER = 'POINTS_NUMBER' MIN_DISTANCE = 'MIN_DISTANCE' + ADD_Z = 'ADD_Z' + ADD_M = 'ADD_M' OUTPUT = 'OUTPUT' def group(self): @@ -65,13 +69,31 @@ class RandomPointsAlongLines(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.VECTOR, - self.tr('Input layer'), [dataobjects.TYPE_VECTOR_LINE])) - self.addParameter(ParameterNumber(self.POINT_NUMBER, - self.tr('Number of points'), 1, None, 1)) - self.addParameter(ParameterNumber(self.MIN_DISTANCE, - self.tr('Minimum distance'), 0.0, None, 0.0)) - self.addOutput(OutputVector(self.OUTPUT, self.tr('Random points'), datatype=[dataobjects.TYPE_VECTOR_POINT])) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), + [QgsProcessing.TypeVectorLine])) + self.addParameter(QgsProcessingParameterNumber(self.POINTS_NUMBER, + self.tr('Number of points'), + QgsProcessingParameterNumber.Integer, + 1, False, 1, 1000000000)) + self.addParameter(QgsProcessingParameterNumber(self.MIN_DISTANCE, + self.tr('Minimum distance between points'), + QgsProcessingParameterNumber.Double, + 0, False, 0, 1000000000)) + params = [] + params.append(QgsProcessingParameterBoolean(self.ADD_Z, + self.tr('Add Z coordinate'), + False)) + params.append(QgsProcessingParameterBoolean(self.ADD_M, + self.tr('Add M coordinate'), + False)) + for p in params: + p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) + self.addParameter(p) + + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, + self.tr('Random points'), + type=QgsProcessing.TypeVectorPoint)) def name(self): return 'randompointsalongline' @@ -80,25 +102,35 @@ class RandomPointsAlongLines(QgisAlgorithm): return self.tr('Random points along line') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.VECTOR), context) - pointCount = float(self.getParameterValue(self.POINT_NUMBER)) - minDistance = float(self.getParameterValue(self.MIN_DISTANCE)) + source = self.parameterAsSource(parameters, self.INPUT, context) + pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context) + minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) + addZ = self.parameterAsBool(parameters, self.ADD_Z, context) + addM = self.parameterAsBool(parameters, self.ADD_M, context) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) - writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context) + + wkbType = QgsWkbTypes.Point + if addZ: + wkbType = QgsWkbTypes.addZ(wkbType) + if addM: + wkbType = QgsWkbTypes.addM(wkbType) + + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, wkbType, source.sourceCrs()) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 - featureCount = layer.featureCount() + featureCount = source.featureCount() total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() da = QgsDistanceArea() - da.setSourceCrs(layer.sourceCrs()) + da.setSourceCrs(source.sourceCrs()) da.setEllipsoid(QgsProject.instance().ellipsoid()) request = QgsFeatureRequest() @@ -106,9 +138,12 @@ class RandomPointsAlongLines(QgisAlgorithm): random.seed() while nIterations < maxIterations and nPoints < pointCount: + if feedback.isCanceled(): + break + # pick random feature fid = random.randint(0, featureCount - 1) - f = next(layer.getFeatures(request.setFilterFid(fid).setSubsetOfAttributes([]))) + f = next(source.getFeatures(request.setFilterFid(fid).setSubsetOfAttributes([]))) fGeom = f.geometry() if fGeom.isMultipart(): @@ -135,23 +170,28 @@ class RandomPointsAlongLines(QgisAlgorithm): ry = (startPoint.y() + d * endPoint.y()) / (1 + d) # generate random point - pnt = QgsPointXY(rx, ry) - geom = QgsGeometry.fromPoint(pnt) - if vector.checkMinDistance(pnt, index, minDistance, points): + pnt = QgsPoint(rx, ry) + p = QgsPointXY(rx, ry) + if addZ: + pnt.addZValue(0.0) + if addM: + pnt.addMValue(0.0) + geom = QgsGeometry(pnt) + if vector.checkMinDistance(p, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) - writer.addFeature(f, QgsFeatureSink.FastInsert) + sink.addFeature(f, QgsFeatureSink.FastInsert) index.insertFeature(f) - points[nPoints] = pnt + points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: - QgsMessageLog.logMessage(self.tr('Can not generate requested number of random points. ' - 'Maximum number of attempts exceeded.'), self.tr('Processing'), QgsMessageLog.INFO) + feedback.pushInfo(self.tr('Could not generate requested number of random points. ' + 'Maximum number of attempts exceeded.')) - del writer + return {self.OUTPUT: dest_id}