Skip to content

Commit 72997dc

Browse files
committed
[processing] new tool: random points within polygons
Work done for Faunalia funded by Prof. António Mira (University of Évora, Portugal, Unidade de Biologia da Conservação) and Dr. Rosana Peixoto
1 parent 1b423ca commit 72997dc

2 files changed

Lines changed: 148 additions & 1 deletion

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
from PointsFromLines import PointsFromLines
9393
from RandomPointsExtent import RandomPointsExtent
9494
from RandomPointsLayer import RandomPointsLayer
95+
from RandomPointsPolygonsFixed import RandomPointsPolygonsFixed
9596

9697
# from VectorLayerHistogram import VectorLayerHistogram
9798
# from VectorLayerScatterplot import VectorLayerScatterplot
@@ -145,7 +146,7 @@ def __init__(self):
145146
RasterLayerStatistics(), PointsDisplacement(),
146147
ZonalStatistics(), PointsFromPolygons(),
147148
PointsFromLines(), RandomPointsExtent(),
148-
RandomPointsLayer(),
149+
RandomPointsLayer(), RandomPointsPolygonsFixed(),
149150
# ------ raster ------
150151
# CreateConstantRaster(),
151152
# ------ graphics ------
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
RandomPointsPolygonsFixed.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 math
29+
import random
30+
31+
from PyQt4.QtCore import *
32+
33+
from qgis.core import *
34+
35+
from processing import interface
36+
from processing.core.GeoAlgorithm import GeoAlgorithm
37+
from processing.core.ProcessingLog import ProcessingLog
38+
from processing.parameters.ParameterVector import ParameterVector
39+
from processing.parameters.ParameterNumber import ParameterNumber
40+
from processing.parameters.ParameterSelection import ParameterSelection
41+
from processing.outputs.OutputVector import OutputVector
42+
from processing.tools import dataobjects, vector
43+
44+
45+
class RandomPointsPolygonsFixed(GeoAlgorithm):
46+
47+
VECTOR = 'VECTOR'
48+
VALUE = 'VALUE'
49+
MIN_DISTANCE = 'MIN_DISTANCE'
50+
STRATEGY = 'STRATEGY'
51+
OUTPUT = 'OUTPUT'
52+
53+
STRATEGIES = ['Points count',
54+
'Points density'
55+
]
56+
57+
def defineCharacteristics(self):
58+
self.name = 'Random points inside polygons'
59+
self.group = 'Vector creation tools'
60+
self.addParameter(ParameterVector(self.VECTOR,
61+
'Input layer',[ParameterVector.VECTOR_TYPE_POLYGON]))
62+
self.addParameter(ParameterSelection(
63+
self.STRATEGY, 'Sampling strategy', self.STRATEGIES, 0))
64+
self.addParameter(
65+
ParameterNumber(self.VALUE, 'Number or density of points', 0.0001, 9999999.0, 1.0))
66+
self.addParameter(ParameterNumber(
67+
self.MIN_DISTANCE, 'Minimum distance', 0.0, 9999999, 0.0))
68+
self.addOutput(OutputVector(self.OUTPUT, 'Random points'))
69+
70+
def processAlgorithm(self, progress):
71+
layer = dataobjects.getObjectFromUri(
72+
self.getParameterValue(self.VECTOR))
73+
value = float(self.getParameterValue(self.VALUE))
74+
minDistance = float(self.getParameterValue(self.MIN_DISTANCE))
75+
strategy = self.getParameterValue(self.STRATEGY)
76+
77+
fields = QgsFields()
78+
fields.append(QgsField('id', QVariant.Int, '', 10, 0))
79+
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
80+
fields, QGis.WKBPoint, layer.dataProvider().crs())
81+
82+
83+
request = QgsFeatureRequest()
84+
85+
da = QgsDistanceArea()
86+
features = vector.features(layer)
87+
for current, f in enumerate(features):
88+
fGeom = QgsGeometry(f.geometry())
89+
bbox = fGeom.boundingBox()
90+
if strategy == 0:
91+
pointCount = int(value)
92+
else:
93+
pointCount = int(round(value * da.measure(fGeom)))
94+
95+
index = QgsSpatialIndex()
96+
points = dict()
97+
98+
nPoints = 0
99+
nIterations = 0
100+
maxIterations = pointCount * 200
101+
total = 100.0 / pointCount
102+
103+
while nIterations < maxIterations and nPoints < pointCount:
104+
rx = bbox.xMinimum() + bbox.width() * random.random()
105+
ry = bbox.yMinimum() + bbox.height() * random.random()
106+
107+
pnt = QgsPoint(rx, ry)
108+
geom = QgsGeometry.fromPoint(pnt)
109+
if geom.within(fGeom) and \
110+
self.checkMinDistance(pnt, index, minDistance, points):
111+
f = QgsFeature(nPoints)
112+
f.initAttributes(1)
113+
f.setFields(fields)
114+
f.setAttribute('id', nPoints)
115+
f.setGeometry(geom)
116+
writer.addFeature(f)
117+
index.insertFeature(f)
118+
points[nPoints] = pnt
119+
nPoints += 1
120+
progress.setPercentage(int(nPoints * total))
121+
nIterations += 1
122+
123+
if nPoints < pointCount:
124+
ProcessingLog.addToLog(
125+
ProcessingLog.LOG_INFO,
126+
'Can not generate requested number of random points. Maximum '
127+
'number of attempts exceeded.')
128+
129+
progress.setPercentage(0)
130+
131+
del writer
132+
133+
def checkMinDistance(self, point, index, distance, points):
134+
if distance == 0:
135+
return True
136+
137+
neighbors = index.nearestNeighbor(point, 1)
138+
if len(neighbors) == 0:
139+
return True
140+
141+
if neighbors[0] in points:
142+
np = points[neighbors[0]]
143+
if np.sqrDist(point) < (distance * distance):
144+
return False
145+
146+
return True

0 commit comments

Comments
 (0)