Skip to content

Commit

Permalink
[processing] port Random points in polygons
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbruy committed Jul 24, 2017
1 parent ae2e32b commit f8b0c06
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 303 deletions.
4 changes: 2 additions & 2 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -86,6 +86,7 @@
from .RandomExtractWithinSubsets import RandomExtractWithinSubsets
from .RandomPointsExtent import RandomPointsExtent
from .RandomPointsLayer import RandomPointsLayer
from .RandomPointsPolygons import RandomPointsPolygons
from .RegularPoints import RegularPoints
from .ReverseLineDirection import ReverseLineDirection
from .Ruggedness import Ruggedness
Expand Down Expand Up @@ -143,8 +144,6 @@
# from .PointsDisplacement import PointsDisplacement
# from .PointsFromPolygons import PointsFromPolygons
# from .PointsFromLines import PointsFromLines
# from .RandomPointsPolygonsFixed import RandomPointsPolygonsFixed
# from .RandomPointsPolygonsVariable import RandomPointsPolygonsVariable
# from .RandomPointsAlongLines import RandomPointsAlongLines
# from .PointsToPaths import PointsToPaths
# from .SetVectorStyle import SetVectorStyle
Expand Down Expand Up @@ -274,6 +273,7 @@ def getAlgs(self):
RandomExtractWithinSubsets(),
RandomPointsExtent(),
RandomPointsLayer(),
RandomPointsPolygons(),
RegularPoints(),
ReverseLineDirection(),
Ruggedness(),
Expand Down
220 changes: 220 additions & 0 deletions python/plugins/processing/algs/qgis/RandomPointsPolygons.py
@@ -0,0 +1,220 @@
# -*- coding: utf-8 -*-

"""
***************************************************************************
RandomPointsPolygons.py
---------------------
Date : April 2014
Copyright : (C) 2014 by Alexander Bruy
Email : alexander dot bruy 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__ = 'Alexander Bruy'
__date__ = 'April 2014'
__copyright__ = '(C) 2014, Alexander Bruy'

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

__revision__ = '$Format:%H$'

import os
import random

from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsField,
QgsFeatureSink,
QgsFeature,
QgsFields,
QgsGeometry,
QgsPoint,
QgsPointXY,
QgsWkbTypes,
QgsSpatialIndex,
QgsFeatureRequest,
QgsExpression,
QgsDistanceArea,
QgsProject,
QgsProcessing,
QgsProcessingException,
QgsProcessingParameterNumber,
QgsProcessingParameterBoolean,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterExpression,
QgsProcessingParameterEnum,
QgsProcessingParameterDefinition)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.tools import vector

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


class RandomPointsPolygons(QgisAlgorithm):

INPUT = 'INPUT'
EXPRESSION = 'EXPRESSION'
MIN_DISTANCE = 'MIN_DISTANCE'
STRATEGY = 'STRATEGY'
ADD_Z = 'ADD_Z'
ADD_M = 'ADD_M'
OUTPUT = 'OUTPUT'

def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'random_points.png'))

def group(self):
return self.tr('Vector creation tools')

def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.strategies = [self.tr('Points count'),
self.tr('Points density')]

self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer'),
[QgsProcessing.TypeVectorPolygon]))
self.addParameter(QgsProcessingParameterEnum(self.STRATEGY,
self.tr('Sampling strategy'),
self.strategies,
False,
0))
self.addParameter(QgsProcessingParameterExpression(self.EXPRESSION,
self.tr('Expression'),
parentLayerParameterName=self.INPUT))
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 'randompointsinsidepolygons'

def displayName(self):
return self.tr('Random points inside polygons')

def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context)
strategy = self.parameterAsEnum(parameters, self.STRATEGY, 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)

expression = QgsExpression(self.parameterAsString(parameters, self.EXPRESSION, context))
if expression.hasParserError():
raise ProcessingException(expression.parserErrorString())

expressionContext = self.createExpressionContext(parameters, context)
if not expression.prepare(expressionContext):
raise ProcessingException(
self.tr('Evaluation error: {0}').format(expression.evalErrorString()))

fields = QgsFields()
fields.append(QgsField('id', QVariant.Int, '', 10, 0))

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())

da = QgsDistanceArea()
da.setSourceCrs(source.sourceCrs())
da.setEllipsoid(QgsProject.instance().ellipsoid())

total = 100.0 / source.featureCount() if source.featureCount() else 0
for current, f in enumerate(source.getFeatures()):
if feedback.isCanceled():
break

expressionContext.setFeature(f)
value = expression.evaluate(expressionContext)
if expression.hasEvalError():
feedback.pushInfo(
self.tr('Evaluation error for feature ID {}: {}').format(f.id(), expression.evalErrorString()))
continue

fGeom = f.geometry()
bbox = fGeom.boundingBox()
if strategy == 0:
pointCount = int(value)
else:
pointCount = int(round(value * da.measureArea(fGeom)))

if pointCount == 0:
feedback.pushInfo("Skip feature {} as number of points for it is 0.")
continue

index = QgsSpatialIndex()
points = dict()

nPoints = 0
nIterations = 0
maxIterations = pointCount * 200
total = 100.0 / pointCount if pointCount else 1

random.seed()

while nIterations < maxIterations and nPoints < pointCount:
if feedback.isCanceled():
break

rx = bbox.xMinimum() + bbox.width() * random.random()
ry = bbox.yMinimum() + bbox.height() * random.random()

pnt = QgsPoint(rx, ry)
p = QgsPointXY(rx, ry)
if addZ:
pnt.addZValue(0.0)
if addM:
pnt.addMValue(0.0)
geom = QgsGeometry(pnt)
if geom.within(fGeom) and \
vector.checkMinDistance(p, index, minDistance, points):
f = QgsFeature(nPoints)
f.initAttributes(1)
f.setFields(fields)
f.setAttribute('id', nPoints)
f.setGeometry(geom)
sink.addFeature(f, QgsFeatureSink.FastInsert)
index.insertFeature(f)
points[nPoints] = p
nPoints += 1
feedback.setProgress(int(nPoints * total))
nIterations += 1

if nPoints < pointCount:
feedback.pushInfo(self.tr('Could not generate requested number of random '
'points. Maximum number of attempts exceeded.'))

feedback.setProgress(0)

return {self.OUTPUT: dest_id}
150 changes: 0 additions & 150 deletions python/plugins/processing/algs/qgis/RandomPointsPolygonsFixed.py

This file was deleted.

0 comments on commit f8b0c06

Please sign in to comment.