Skip to content

Commit f8b0c06

Browse files
committedJul 24, 2017
[processing] port Random points in polygons
1 parent ae2e32b commit f8b0c06

File tree

4 files changed

+222
-303
lines changed

4 files changed

+222
-303
lines changed
 

‎python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
from .RandomExtractWithinSubsets import RandomExtractWithinSubsets
8787
from .RandomPointsExtent import RandomPointsExtent
8888
from .RandomPointsLayer import RandomPointsLayer
89+
from .RandomPointsPolygons import RandomPointsPolygons
8990
from .RegularPoints import RegularPoints
9091
from .ReverseLineDirection import ReverseLineDirection
9192
from .Ruggedness import Ruggedness
@@ -143,8 +144,6 @@
143144
# from .PointsDisplacement import PointsDisplacement
144145
# from .PointsFromPolygons import PointsFromPolygons
145146
# from .PointsFromLines import PointsFromLines
146-
# from .RandomPointsPolygonsFixed import RandomPointsPolygonsFixed
147-
# from .RandomPointsPolygonsVariable import RandomPointsPolygonsVariable
148147
# from .RandomPointsAlongLines import RandomPointsAlongLines
149148
# from .PointsToPaths import PointsToPaths
150149
# from .SetVectorStyle import SetVectorStyle
@@ -274,6 +273,7 @@ def getAlgs(self):
274273
RandomExtractWithinSubsets(),
275274
RandomPointsExtent(),
276275
RandomPointsLayer(),
276+
RandomPointsPolygons(),
277277
RegularPoints(),
278278
ReverseLineDirection(),
279279
Ruggedness(),
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
RandomPointsPolygons.py
6+
---------------------
7+
Date : April 2014
8+
Copyright : (C) 2014 by Alexander Bruy
9+
Email : alexander dot bruy at gmail dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Alexander Bruy'
21+
__date__ = 'April 2014'
22+
__copyright__ = '(C) 2014, Alexander Bruy'
23+
24+
# This will get replaced with a git SHA1 when you do a git archive
25+
26+
__revision__ = '$Format:%H$'
27+
28+
import os
29+
import random
30+
31+
from qgis.PyQt.QtGui import QIcon
32+
from qgis.PyQt.QtCore import QVariant
33+
from qgis.core import (QgsField,
34+
QgsFeatureSink,
35+
QgsFeature,
36+
QgsFields,
37+
QgsGeometry,
38+
QgsPoint,
39+
QgsPointXY,
40+
QgsWkbTypes,
41+
QgsSpatialIndex,
42+
QgsFeatureRequest,
43+
QgsExpression,
44+
QgsDistanceArea,
45+
QgsProject,
46+
QgsProcessing,
47+
QgsProcessingException,
48+
QgsProcessingParameterNumber,
49+
QgsProcessingParameterBoolean,
50+
QgsProcessingParameterFeatureSource,
51+
QgsProcessingParameterFeatureSink,
52+
QgsProcessingParameterExpression,
53+
QgsProcessingParameterEnum,
54+
QgsProcessingParameterDefinition)
55+
56+
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
57+
from processing.tools import vector
58+
59+
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
60+
61+
62+
class RandomPointsPolygons(QgisAlgorithm):
63+
64+
INPUT = 'INPUT'
65+
EXPRESSION = 'EXPRESSION'
66+
MIN_DISTANCE = 'MIN_DISTANCE'
67+
STRATEGY = 'STRATEGY'
68+
ADD_Z = 'ADD_Z'
69+
ADD_M = 'ADD_M'
70+
OUTPUT = 'OUTPUT'
71+
72+
def icon(self):
73+
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'random_points.png'))
74+
75+
def group(self):
76+
return self.tr('Vector creation tools')
77+
78+
def __init__(self):
79+
super().__init__()
80+
81+
def initAlgorithm(self, config=None):
82+
self.strategies = [self.tr('Points count'),
83+
self.tr('Points density')]
84+
85+
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
86+
self.tr('Input layer'),
87+
[QgsProcessing.TypeVectorPolygon]))
88+
self.addParameter(QgsProcessingParameterEnum(self.STRATEGY,
89+
self.tr('Sampling strategy'),
90+
self.strategies,
91+
False,
92+
0))
93+
self.addParameter(QgsProcessingParameterExpression(self.EXPRESSION,
94+
self.tr('Expression'),
95+
parentLayerParameterName=self.INPUT))
96+
self.addParameter(QgsProcessingParameterNumber(self.MIN_DISTANCE,
97+
self.tr('Minimum distance between points'),
98+
QgsProcessingParameterNumber.Double,
99+
0, False, 0, 1000000000))
100+
params = []
101+
params.append(QgsProcessingParameterBoolean(self.ADD_Z,
102+
self.tr('Add Z coordinate'),
103+
False))
104+
params.append(QgsProcessingParameterBoolean(self.ADD_M,
105+
self.tr('Add M coordinate'),
106+
False))
107+
for p in params:
108+
p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
109+
self.addParameter(p)
110+
111+
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
112+
self.tr('Random points'),
113+
type=QgsProcessing.TypeVectorPoint))
114+
115+
def name(self):
116+
return 'randompointsinsidepolygons'
117+
118+
def displayName(self):
119+
return self.tr('Random points inside polygons')
120+
121+
def processAlgorithm(self, parameters, context, feedback):
122+
source = self.parameterAsSource(parameters, self.INPUT, context)
123+
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
124+
minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)
125+
addZ = self.parameterAsBool(parameters, self.ADD_Z, context)
126+
addM = self.parameterAsBool(parameters, self.ADD_M, context)
127+
128+
expression = QgsExpression(self.parameterAsString(parameters, self.EXPRESSION, context))
129+
if expression.hasParserError():
130+
raise ProcessingException(expression.parserErrorString())
131+
132+
expressionContext = self.createExpressionContext(parameters, context)
133+
if not expression.prepare(expressionContext):
134+
raise ProcessingException(
135+
self.tr('Evaluation error: {0}').format(expression.evalErrorString()))
136+
137+
fields = QgsFields()
138+
fields.append(QgsField('id', QVariant.Int, '', 10, 0))
139+
140+
wkbType = QgsWkbTypes.Point
141+
if addZ:
142+
wkbType = QgsWkbTypes.addZ(wkbType)
143+
if addM:
144+
wkbType = QgsWkbTypes.addM(wkbType)
145+
146+
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
147+
fields, wkbType, source.sourceCrs())
148+
149+
da = QgsDistanceArea()
150+
da.setSourceCrs(source.sourceCrs())
151+
da.setEllipsoid(QgsProject.instance().ellipsoid())
152+
153+
total = 100.0 / source.featureCount() if source.featureCount() else 0
154+
for current, f in enumerate(source.getFeatures()):
155+
if feedback.isCanceled():
156+
break
157+
158+
expressionContext.setFeature(f)
159+
value = expression.evaluate(expressionContext)
160+
if expression.hasEvalError():
161+
feedback.pushInfo(
162+
self.tr('Evaluation error for feature ID {}: {}').format(f.id(), expression.evalErrorString()))
163+
continue
164+
165+
fGeom = f.geometry()
166+
bbox = fGeom.boundingBox()
167+
if strategy == 0:
168+
pointCount = int(value)
169+
else:
170+
pointCount = int(round(value * da.measureArea(fGeom)))
171+
172+
if pointCount == 0:
173+
feedback.pushInfo("Skip feature {} as number of points for it is 0.")
174+
continue
175+
176+
index = QgsSpatialIndex()
177+
points = dict()
178+
179+
nPoints = 0
180+
nIterations = 0
181+
maxIterations = pointCount * 200
182+
total = 100.0 / pointCount if pointCount else 1
183+
184+
random.seed()
185+
186+
while nIterations < maxIterations and nPoints < pointCount:
187+
if feedback.isCanceled():
188+
break
189+
190+
rx = bbox.xMinimum() + bbox.width() * random.random()
191+
ry = bbox.yMinimum() + bbox.height() * random.random()
192+
193+
pnt = QgsPoint(rx, ry)
194+
p = QgsPointXY(rx, ry)
195+
if addZ:
196+
pnt.addZValue(0.0)
197+
if addM:
198+
pnt.addMValue(0.0)
199+
geom = QgsGeometry(pnt)
200+
if geom.within(fGeom) and \
201+
vector.checkMinDistance(p, index, minDistance, points):
202+
f = QgsFeature(nPoints)
203+
f.initAttributes(1)
204+
f.setFields(fields)
205+
f.setAttribute('id', nPoints)
206+
f.setGeometry(geom)
207+
sink.addFeature(f, QgsFeatureSink.FastInsert)
208+
index.insertFeature(f)
209+
points[nPoints] = p
210+
nPoints += 1
211+
feedback.setProgress(int(nPoints * total))
212+
nIterations += 1
213+
214+
if nPoints < pointCount:
215+
feedback.pushInfo(self.tr('Could not generate requested number of random '
216+
'points. Maximum number of attempts exceeded.'))
217+
218+
feedback.setProgress(0)
219+
220+
return {self.OUTPUT: dest_id}

‎python/plugins/processing/algs/qgis/RandomPointsPolygonsFixed.py

Lines changed: 0 additions & 150 deletions
This file was deleted.

‎python/plugins/processing/algs/qgis/RandomPointsPolygonsVariable.py

Lines changed: 0 additions & 151 deletions
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.