Skip to content

Commit b842043

Browse files
authoredJul 24, 2017
Merge pull request #4901 from alexbruy/processing-random
[processing] restore "random" algorithms
2 parents 2723f4f + c440ade commit b842043

File tree

7 files changed

+358
-420
lines changed

7 files changed

+358
-420
lines changed
 

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@
8484
from .PostGISExecuteSQL import PostGISExecuteSQL
8585
from .RandomExtract import RandomExtract
8686
from .RandomExtractWithinSubsets import RandomExtractWithinSubsets
87+
from .RandomPointsAlongLines import RandomPointsAlongLines
88+
from .RandomPointsExtent import RandomPointsExtent
89+
from .RandomPointsLayer import RandomPointsLayer
90+
from .RandomPointsPolygons import RandomPointsPolygons
8791
from .RegularPoints import RegularPoints
8892
from .ReverseLineDirection import ReverseLineDirection
8993
from .Ruggedness import Ruggedness
@@ -141,11 +145,6 @@
141145
# from .PointsDisplacement import PointsDisplacement
142146
# from .PointsFromPolygons import PointsFromPolygons
143147
# from .PointsFromLines import PointsFromLines
144-
# from .RandomPointsExtent import RandomPointsExtent
145-
# from .RandomPointsLayer import RandomPointsLayer
146-
# from .RandomPointsPolygonsFixed import RandomPointsPolygonsFixed
147-
# from .RandomPointsPolygonsVariable import RandomPointsPolygonsVariable
148-
# from .RandomPointsAlongLines import RandomPointsAlongLines
149148
# from .PointsToPaths import PointsToPaths
150149
# from .SetVectorStyle import SetVectorStyle
151150
# from .SetRasterStyle import SetRasterStyle
@@ -272,6 +271,10 @@ def getAlgs(self):
272271
PostGISExecuteSQL(),
273272
RandomExtract(),
274273
RandomExtractWithinSubsets(),
274+
RandomPointsAlongLines(),
275+
RandomPointsExtent(),
276+
RandomPointsLayer(),
277+
RandomPointsPolygons(),
275278
RegularPoints(),
276279
ReverseLineDirection(),
277280
Ruggedness(),

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

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,32 @@
2929
import random
3030

3131
from qgis.PyQt.QtCore import QVariant
32-
from qgis.core import (QgsApplication,
32+
from qgis.core import (QgsField,
3333
QgsFeatureSink,
34+
QgsFeature,
3435
QgsFields,
35-
QgsField,
3636
QgsGeometry,
37-
QgsSpatialIndex,
37+
QgsPointXY,
3838
QgsWkbTypes,
39-
QgsDistanceArea,
39+
QgsSpatialIndex,
4040
QgsFeatureRequest,
41-
QgsFeature,
42-
QgsPointXY,
43-
QgsMessageLog,
41+
QgsDistanceArea,
4442
QgsProject,
45-
QgsProcessingUtils)
43+
QgsProcessing,
44+
QgsProcessingException,
45+
QgsProcessingParameterNumber,
46+
QgsProcessingParameterFeatureSource,
47+
QgsProcessingParameterFeatureSink,
48+
QgsProcessingParameterDefinition)
4649

4750
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
48-
from processing.core.parameters import ParameterVector
49-
from processing.core.parameters import ParameterNumber
50-
from processing.core.outputs import OutputVector
51-
from processing.tools import dataobjects, vector
51+
from processing.tools import vector
5252

5353

5454
class RandomPointsAlongLines(QgisAlgorithm):
5555

56-
VECTOR = 'VECTOR'
57-
POINT_NUMBER = 'POINT_NUMBER'
56+
INPUT = 'INPUT'
57+
POINTS_NUMBER = 'POINTS_NUMBER'
5858
MIN_DISTANCE = 'MIN_DISTANCE'
5959
OUTPUT = 'OUTPUT'
6060

@@ -65,13 +65,20 @@ def __init__(self):
6565
super().__init__()
6666

6767
def initAlgorithm(self, config=None):
68-
self.addParameter(ParameterVector(self.VECTOR,
69-
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_LINE]))
70-
self.addParameter(ParameterNumber(self.POINT_NUMBER,
71-
self.tr('Number of points'), 1, None, 1))
72-
self.addParameter(ParameterNumber(self.MIN_DISTANCE,
73-
self.tr('Minimum distance'), 0.0, None, 0.0))
74-
self.addOutput(OutputVector(self.OUTPUT, self.tr('Random points'), datatype=[dataobjects.TYPE_VECTOR_POINT]))
68+
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
69+
self.tr('Input layer'),
70+
[QgsProcessing.TypeVectorLine]))
71+
self.addParameter(QgsProcessingParameterNumber(self.POINTS_NUMBER,
72+
self.tr('Number of points'),
73+
QgsProcessingParameterNumber.Integer,
74+
1, False, 1, 1000000000))
75+
self.addParameter(QgsProcessingParameterNumber(self.MIN_DISTANCE,
76+
self.tr('Minimum distance between points'),
77+
QgsProcessingParameterNumber.Double,
78+
0, False, 0, 1000000000))
79+
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
80+
self.tr('Random points'),
81+
type=QgsProcessing.TypeVectorPoint))
7582

7683
def name(self):
7784
return 'randompointsalongline'
@@ -80,35 +87,40 @@ def displayName(self):
8087
return self.tr('Random points along line')
8188

8289
def processAlgorithm(self, parameters, context, feedback):
83-
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.VECTOR), context)
84-
pointCount = float(self.getParameterValue(self.POINT_NUMBER))
85-
minDistance = float(self.getParameterValue(self.MIN_DISTANCE))
90+
source = self.parameterAsSource(parameters, self.INPUT, context)
91+
pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context)
92+
minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)
8693

8794
fields = QgsFields()
8895
fields.append(QgsField('id', QVariant.Int, '', 10, 0))
89-
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context)
96+
97+
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
98+
fields, QgsWkbTypes.Point, source.sourceCrs())
9099

91100
nPoints = 0
92101
nIterations = 0
93102
maxIterations = pointCount * 200
94-
featureCount = layer.featureCount()
103+
featureCount = source.featureCount()
95104
total = 100.0 / pointCount if pointCount else 1
96105

97106
index = QgsSpatialIndex()
98107
points = dict()
99108

100109
da = QgsDistanceArea()
101-
da.setSourceCrs(layer.sourceCrs())
110+
da.setSourceCrs(source.sourceCrs())
102111
da.setEllipsoid(QgsProject.instance().ellipsoid())
103112

104113
request = QgsFeatureRequest()
105114

106115
random.seed()
107116

108117
while nIterations < maxIterations and nPoints < pointCount:
118+
if feedback.isCanceled():
119+
break
120+
109121
# pick random feature
110122
fid = random.randint(0, featureCount - 1)
111-
f = next(layer.getFeatures(request.setFilterFid(fid).setSubsetOfAttributes([])))
123+
f = next(source.getFeatures(request.setFilterFid(fid).setSubsetOfAttributes([])))
112124
fGeom = f.geometry()
113125

114126
if fGeom.isMultipart():
@@ -135,23 +147,23 @@ def processAlgorithm(self, parameters, context, feedback):
135147
ry = (startPoint.y() + d * endPoint.y()) / (1 + d)
136148

137149
# generate random point
138-
pnt = QgsPointXY(rx, ry)
139-
geom = QgsGeometry.fromPoint(pnt)
140-
if vector.checkMinDistance(pnt, index, minDistance, points):
150+
p = QgsPointXY(rx, ry)
151+
geom = QgsGeometry.fromPoint(p)
152+
if vector.checkMinDistance(p, index, minDistance, points):
141153
f = QgsFeature(nPoints)
142154
f.initAttributes(1)
143155
f.setFields(fields)
144156
f.setAttribute('id', nPoints)
145157
f.setGeometry(geom)
146-
writer.addFeature(f, QgsFeatureSink.FastInsert)
158+
sink.addFeature(f, QgsFeatureSink.FastInsert)
147159
index.insertFeature(f)
148-
points[nPoints] = pnt
160+
points[nPoints] = p
149161
nPoints += 1
150162
feedback.setProgress(int(nPoints * total))
151163
nIterations += 1
152164

153165
if nPoints < pointCount:
154-
QgsMessageLog.logMessage(self.tr('Can not generate requested number of random points. '
155-
'Maximum number of attempts exceeded.'), self.tr('Processing'), QgsMessageLog.INFO)
166+
feedback.pushInfo(self.tr('Could not generate requested number of random points. '
167+
'Maximum number of attempts exceeded.'))
156168

157-
del writer
169+
return {self.OUTPUT: dest_id}

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

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,35 @@
3131

3232
from qgis.PyQt.QtGui import QIcon
3333
from qgis.PyQt.QtCore import QVariant
34-
from qgis.core import (QgsGeometry, QgsFeatureSink, QgsRectangle, QgsFeature, QgsFields, QgsWkbTypes,
35-
QgsField, QgsSpatialIndex, QgsPointXY,
36-
QgsCoordinateReferenceSystem,
37-
QgsMessageLog,
38-
QgsProcessingUtils)
34+
from qgis.core import (QgsField,
35+
QgsFeatureSink,
36+
QgsFeature,
37+
QgsFields,
38+
QgsGeometry,
39+
QgsPointXY,
40+
QgsWkbTypes,
41+
QgsSpatialIndex,
42+
QgsProcessing,
43+
QgsProcessingException,
44+
QgsProcessingParameterExtent,
45+
QgsProcessingParameterNumber,
46+
QgsProcessingParameterCrs,
47+
QgsProcessingParameterFeatureSink,
48+
QgsProcessingParameterDefinition)
3949

4050
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
41-
from processing.core.parameters import ParameterExtent
42-
from processing.core.parameters import ParameterNumber
43-
from processing.core.parameters import ParameterCrs
44-
from processing.core.outputs import OutputVector
45-
from processing.tools import vector, dataobjects
51+
from processing.tools import vector
4652

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

4955

5056
class RandomPointsExtent(QgisAlgorithm):
5157

5258
EXTENT = 'EXTENT'
53-
POINT_NUMBER = 'POINT_NUMBER'
59+
POINTS_NUMBER = 'POINTS_NUMBER'
5460
MIN_DISTANCE = 'MIN_DISTANCE'
61+
TARGET_CRS = 'TARGET_CRS'
5562
OUTPUT = 'OUTPUT'
56-
CRS = 'CRS'
5763

5864
def icon(self):
5965
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'random_points.png'))
@@ -65,15 +71,21 @@ def __init__(self):
6571
super().__init__()
6672

6773
def initAlgorithm(self, config=None):
68-
self.addParameter(ParameterExtent(self.EXTENT,
69-
self.tr('Input extent'), optional=False))
70-
self.addParameter(ParameterNumber(self.POINT_NUMBER,
71-
self.tr('Points number'), 1, None, 1))
72-
self.addParameter(ParameterNumber(self.MIN_DISTANCE,
73-
self.tr('Minimum distance'), 0.0, None, 0.0))
74-
self.addParameter(ParameterCrs(self.CRS,
75-
self.tr('Output layer CRS'), 'ProjectCrs'))
76-
self.addOutput(OutputVector(self.OUTPUT, self.tr('Random points'), datatype=[dataobjects.TYPE_VECTOR_POINT]))
74+
self.addParameter(QgsProcessingParameterExtent(self.EXTENT, self.tr('Input extent')))
75+
self.addParameter(QgsProcessingParameterNumber(self.POINTS_NUMBER,
76+
self.tr('Number of points'),
77+
QgsProcessingParameterNumber.Integer,
78+
1, False, 1, 1000000000))
79+
self.addParameter(QgsProcessingParameterNumber(self.MIN_DISTANCE,
80+
self.tr('Minimum distance between points'),
81+
QgsProcessingParameterNumber.Double,
82+
0, False, 0, 1000000000))
83+
self.addParameter(QgsProcessingParameterCrs(self.TARGET_CRS,
84+
self.tr('Target CRS'),
85+
'ProjectCrs'))
86+
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
87+
self.tr('Random points'),
88+
type=QgsProcessing.TypeVectorPoint))
7789

7890
def name(self):
7991
return 'randompointsinextent'
@@ -82,24 +94,18 @@ def displayName(self):
8294
return self.tr('Random points in extent')
8395

8496
def processAlgorithm(self, parameters, context, feedback):
85-
pointCount = int(self.getParameterValue(self.POINT_NUMBER))
86-
minDistance = float(self.getParameterValue(self.MIN_DISTANCE))
87-
extent = str(self.getParameterValue(self.EXTENT)).split(',')
97+
bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
98+
pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context)
99+
minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)
100+
crs = self.parameterAsCrs(parameters, self.TARGET_CRS, context)
88101

89-
crsId = self.getParameterValue(self.CRS)
90-
crs = QgsCoordinateReferenceSystem()
91-
crs.createFromUserInput(crsId)
92-
93-
xMin = float(extent[0])
94-
xMax = float(extent[1])
95-
yMin = float(extent[2])
96-
yMax = float(extent[3])
97-
extent = QgsGeometry().fromRect(
98-
QgsRectangle(xMin, yMin, xMax, yMax))
102+
extent = QgsGeometry().fromRect(bbox)
99103

100104
fields = QgsFields()
101105
fields.append(QgsField('id', QVariant.Int, '', 10, 0))
102-
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, crs, context)
106+
107+
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
108+
fields, QgsWkbTypes.Point, crs)
103109

104110
nPoints = 0
105111
nIterations = 0
@@ -112,27 +118,30 @@ def processAlgorithm(self, parameters, context, feedback):
112118
random.seed()
113119

114120
while nIterations < maxIterations and nPoints < pointCount:
115-
rx = xMin + (xMax - xMin) * random.random()
116-
ry = yMin + (yMax - yMin) * random.random()
121+
if feedback.isCanceled():
122+
break
123+
124+
rx = bbox.xMinimum() + bbox.width() * random.random()
125+
ry = bbox.yMinimum() + bbox.height() * random.random()
117126

118-
pnt = QgsPointXY(rx, ry)
119-
geom = QgsGeometry.fromPoint(pnt)
127+
p = QgsPointXY(rx, ry)
128+
geom = QgsGeometry.fromPoint(p)
120129
if geom.within(extent) and \
121-
vector.checkMinDistance(pnt, index, minDistance, points):
130+
vector.checkMinDistance(p, index, minDistance, points):
122131
f = QgsFeature(nPoints)
123132
f.initAttributes(1)
124133
f.setFields(fields)
125134
f.setAttribute('id', nPoints)
126135
f.setGeometry(geom)
127-
writer.addFeature(f, QgsFeatureSink.FastInsert)
136+
sink.addFeature(f, QgsFeatureSink.FastInsert)
128137
index.insertFeature(f)
129-
points[nPoints] = pnt
138+
points[nPoints] = p
130139
nPoints += 1
131140
feedback.setProgress(int(nPoints * total))
132141
nIterations += 1
133142

134143
if nPoints < pointCount:
135-
QgsMessageLog.logMessage(self.tr('Can not generate requested number of random points. '
136-
'Maximum number of attempts exceeded.'), self.tr('Processing'), QgsMessageLog.INFO)
144+
feedback.pushInfo(self.tr('Could not generate requested number of random points. '
145+
'Maximum number of attempts exceeded.'))
137146

138-
del writer
147+
return {self.OUTPUT: dest_id}

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

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,32 @@
3030

3131
from qgis.PyQt.QtGui import QIcon
3232
from qgis.PyQt.QtCore import QVariant
33-
from qgis.core import (QgsGeometry, QgsFeatureSink, QgsFields, QgsField, QgsSpatialIndex, QgsWkbTypes,
34-
QgsPointXY, QgsFeature, QgsFeatureRequest,
35-
QgsMessageLog,
36-
QgsProcessingUtils)
33+
from qgis.core import (QgsField,
34+
QgsFeatureSink,
35+
QgsFeature,
36+
QgsFields,
37+
QgsGeometry,
38+
QgsPointXY,
39+
QgsWkbTypes,
40+
QgsSpatialIndex,
41+
QgsFeatureRequest,
42+
QgsProcessing,
43+
QgsProcessingException,
44+
QgsProcessingParameterNumber,
45+
QgsProcessingParameterFeatureSource,
46+
QgsProcessingParameterFeatureSink,
47+
QgsProcessingParameterDefinition)
3748

3849
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
39-
from processing.core.parameters import ParameterVector
40-
from processing.core.parameters import ParameterNumber
41-
from processing.core.outputs import OutputVector
42-
from processing.tools import dataobjects, vector
50+
from processing.tools import vector
4351

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

4654

4755
class RandomPointsLayer(QgisAlgorithm):
4856

49-
VECTOR = 'VECTOR'
50-
POINT_NUMBER = 'POINT_NUMBER'
57+
INPUT = 'INPUT'
58+
POINTS_NUMBER = 'POINTS_NUMBER'
5159
MIN_DISTANCE = 'MIN_DISTANCE'
5260
OUTPUT = 'OUTPUT'
5361

@@ -61,13 +69,20 @@ def __init__(self):
6169
super().__init__()
6270

6371
def initAlgorithm(self, config=None):
64-
self.addParameter(ParameterVector(self.VECTOR,
65-
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POLYGON]))
66-
self.addParameter(ParameterNumber(self.POINT_NUMBER,
67-
self.tr('Points number'), 1, None, 1))
68-
self.addParameter(ParameterNumber(self.MIN_DISTANCE,
69-
self.tr('Minimum distance'), 0.0, None, 0.0))
70-
self.addOutput(OutputVector(self.OUTPUT, self.tr('Random points'), datatype=[dataobjects.TYPE_VECTOR_POINT]))
72+
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
73+
self.tr('Input layer'),
74+
[QgsProcessing.TypeVectorPolygon]))
75+
self.addParameter(QgsProcessingParameterNumber(self.POINTS_NUMBER,
76+
self.tr('Number of points'),
77+
QgsProcessingParameterNumber.Integer,
78+
1, False, 1, 1000000000))
79+
self.addParameter(QgsProcessingParameterNumber(self.MIN_DISTANCE,
80+
self.tr('Minimum distance between points'),
81+
QgsProcessingParameterNumber.Double,
82+
0, False, 0, 1000000000))
83+
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
84+
self.tr('Random points'),
85+
type=QgsProcessing.TypeVectorPoint))
7186

7287
def name(self):
7388
return 'randompointsinlayerbounds'
@@ -76,16 +91,18 @@ def displayName(self):
7691
return self.tr('Random points in layer bounds')
7792

7893
def processAlgorithm(self, parameters, context, feedback):
79-
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.VECTOR), context)
80-
pointCount = int(self.getParameterValue(self.POINT_NUMBER))
81-
minDistance = float(self.getParameterValue(self.MIN_DISTANCE))
94+
source = self.parameterAsSource(parameters, self.INPUT, context)
95+
pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context)
96+
minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)
8297

83-
bbox = layer.extent()
84-
idxLayer = QgsProcessingUtils.createSpatialIndex(layer, context)
98+
bbox = source.sourceExtent()
99+
sourceIndex = QgsSpatialIndex(source, feedback)
85100

86101
fields = QgsFields()
87102
fields.append(QgsField('id', QVariant.Int, '', 10, 0))
88-
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context)
103+
104+
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
105+
fields, QgsWkbTypes.Point, source.sourceCrs())
89106

90107
nPoints = 0
91108
nIterations = 0
@@ -98,32 +115,38 @@ def processAlgorithm(self, parameters, context, feedback):
98115
random.seed()
99116

100117
while nIterations < maxIterations and nPoints < pointCount:
118+
if feedback.isCanceled():
119+
break
120+
101121
rx = bbox.xMinimum() + bbox.width() * random.random()
102122
ry = bbox.yMinimum() + bbox.height() * random.random()
103123

104-
pnt = QgsPointXY(rx, ry)
105-
geom = QgsGeometry.fromPoint(pnt)
106-
ids = idxLayer.intersects(geom.buffer(5, 5).boundingBox())
124+
p = QgsPointXY(rx, ry)
125+
geom = QgsGeometry.fromPoint(p)
126+
ids = sourceIndex.intersects(geom.buffer(5, 5).boundingBox())
107127
if len(ids) > 0 and \
108-
vector.checkMinDistance(pnt, index, minDistance, points):
128+
vector.checkMinDistance(p, index, minDistance, points):
109129
request = QgsFeatureRequest().setFilterFids(ids).setSubsetOfAttributes([])
110-
for f in layer.getFeatures(request):
130+
for f in source.getFeatures(request):
131+
if feedback.isCanceled():
132+
break
133+
111134
tmpGeom = f.geometry()
112135
if geom.within(tmpGeom):
113136
f = QgsFeature(nPoints)
114137
f.initAttributes(1)
115138
f.setFields(fields)
116139
f.setAttribute('id', nPoints)
117140
f.setGeometry(geom)
118-
writer.addFeature(f, QgsFeatureSink.FastInsert)
141+
sink.addFeature(f, QgsFeatureSink.FastInsert)
119142
index.insertFeature(f)
120-
points[nPoints] = pnt
143+
points[nPoints] = p
121144
nPoints += 1
122145
feedback.setProgress(int(nPoints * total))
123146
nIterations += 1
124147

125148
if nPoints < pointCount:
126-
QgsMessageLog.logMessage(self.tr('Can not generate requested number of random points. '
127-
'Maximum number of attempts exceeded.'), self.tr('Processing'), QgsMessageLog.INFO)
149+
feedback.pushInfo(self.tr('Could not generate requested number of random points. '
150+
'Maximum number of attempts exceeded.'))
128151

129-
del writer
152+
return {self.OUTPUT: dest_id}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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+
QgsPointXY,
39+
QgsWkbTypes,
40+
QgsSpatialIndex,
41+
QgsFeatureRequest,
42+
QgsExpression,
43+
QgsDistanceArea,
44+
QgsProject,
45+
QgsProcessing,
46+
QgsProcessingException,
47+
QgsProcessingParameterNumber,
48+
QgsProcessingParameterFeatureSource,
49+
QgsProcessingParameterFeatureSink,
50+
QgsProcessingParameterExpression,
51+
QgsProcessingParameterEnum,
52+
QgsProcessingParameterDefinition)
53+
54+
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
55+
from processing.tools import vector
56+
57+
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
58+
59+
60+
class RandomPointsPolygons(QgisAlgorithm):
61+
62+
INPUT = 'INPUT'
63+
EXPRESSION = 'EXPRESSION'
64+
MIN_DISTANCE = 'MIN_DISTANCE'
65+
STRATEGY = 'STRATEGY'
66+
OUTPUT = 'OUTPUT'
67+
68+
def icon(self):
69+
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'random_points.png'))
70+
71+
def group(self):
72+
return self.tr('Vector creation tools')
73+
74+
def __init__(self):
75+
super().__init__()
76+
77+
def initAlgorithm(self, config=None):
78+
self.strategies = [self.tr('Points count'),
79+
self.tr('Points density')]
80+
81+
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
82+
self.tr('Input layer'),
83+
[QgsProcessing.TypeVectorPolygon]))
84+
self.addParameter(QgsProcessingParameterEnum(self.STRATEGY,
85+
self.tr('Sampling strategy'),
86+
self.strategies,
87+
False,
88+
0))
89+
self.addParameter(QgsProcessingParameterExpression(self.EXPRESSION,
90+
self.tr('Expression'),
91+
parentLayerParameterName=self.INPUT))
92+
self.addParameter(QgsProcessingParameterNumber(self.MIN_DISTANCE,
93+
self.tr('Minimum distance between points'),
94+
QgsProcessingParameterNumber.Double,
95+
0, False, 0, 1000000000))
96+
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
97+
self.tr('Random points'),
98+
type=QgsProcessing.TypeVectorPoint))
99+
100+
def name(self):
101+
return 'randompointsinsidepolygons'
102+
103+
def displayName(self):
104+
return self.tr('Random points inside polygons')
105+
106+
def processAlgorithm(self, parameters, context, feedback):
107+
source = self.parameterAsSource(parameters, self.INPUT, context)
108+
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
109+
minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)
110+
111+
expression = QgsExpression(self.parameterAsString(parameters, self.EXPRESSION, context))
112+
if expression.hasParserError():
113+
raise ProcessingException(expression.parserErrorString())
114+
115+
expressionContext = self.createExpressionContext(parameters, context)
116+
if not expression.prepare(expressionContext):
117+
raise ProcessingException(
118+
self.tr('Evaluation error: {0}').format(expression.evalErrorString()))
119+
120+
fields = QgsFields()
121+
fields.append(QgsField('id', QVariant.Int, '', 10, 0))
122+
123+
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
124+
fields, QgsWkbTypes.Point, source.sourceCrs())
125+
126+
da = QgsDistanceArea()
127+
da.setSourceCrs(source.sourceCrs())
128+
da.setEllipsoid(QgsProject.instance().ellipsoid())
129+
130+
total = 100.0 / source.featureCount() if source.featureCount() else 0
131+
for current, f in enumerate(source.getFeatures()):
132+
if feedback.isCanceled():
133+
break
134+
135+
expressionContext.setFeature(f)
136+
value = expression.evaluate(expressionContext)
137+
if expression.hasEvalError():
138+
feedback.pushInfo(
139+
self.tr('Evaluation error for feature ID {}: {}').format(f.id(), expression.evalErrorString()))
140+
continue
141+
142+
fGeom = f.geometry()
143+
bbox = fGeom.boundingBox()
144+
if strategy == 0:
145+
pointCount = int(value)
146+
else:
147+
pointCount = int(round(value * da.measureArea(fGeom)))
148+
149+
if pointCount == 0:
150+
feedback.pushInfo("Skip feature {} as number of points for it is 0.")
151+
continue
152+
153+
index = QgsSpatialIndex()
154+
points = dict()
155+
156+
nPoints = 0
157+
nIterations = 0
158+
maxIterations = pointCount * 200
159+
total = 100.0 / pointCount if pointCount else 1
160+
161+
random.seed()
162+
163+
while nIterations < maxIterations and nPoints < pointCount:
164+
if feedback.isCanceled():
165+
break
166+
167+
rx = bbox.xMinimum() + bbox.width() * random.random()
168+
ry = bbox.yMinimum() + bbox.height() * random.random()
169+
170+
p = QgsPointXY(rx, ry)
171+
geom = QgsGeometry.fromPoint(p)
172+
if geom.within(fGeom) and \
173+
vector.checkMinDistance(p, index, minDistance, points):
174+
f = QgsFeature(nPoints)
175+
f.initAttributes(1)
176+
f.setFields(fields)
177+
f.setAttribute('id', nPoints)
178+
f.setGeometry(geom)
179+
sink.addFeature(f, QgsFeatureSink.FastInsert)
180+
index.insertFeature(f)
181+
points[nPoints] = p
182+
nPoints += 1
183+
feedback.setProgress(int(nPoints * total))
184+
nIterations += 1
185+
186+
if nPoints < pointCount:
187+
feedback.pushInfo(self.tr('Could not generate requested number of random '
188+
'points. Maximum number of attempts exceeded.'))
189+
190+
feedback.setProgress(0)
191+
192+
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.