QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsalgorithmrandompointsextent.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmrandompointsextent.cpp
3 ---------------------
4 begin : November 2019
5 copyright : (C) 2019 by Clemens Raffler
6 email : clemens dot raffler at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18//Disclaimer: The algorithm optimizes the original Random points in extent algorithm, (C) Alexander Bruy, 2014
19
21#include "qgsspatialindex.h"
22
23#include <random>
24
26
27QString QgsRandomPointsExtentAlgorithm::name() const
28{
29 return QStringLiteral( "randompointsinextent" );
30}
31
32QString QgsRandomPointsExtentAlgorithm::displayName() const
33{
34 return QObject::tr( "Random points in extent" );
35}
36
37QStringList QgsRandomPointsExtentAlgorithm::tags() const
38{
39 return QObject::tr( "random,points,extent,create" ).split( ',' );
40}
41
42QString QgsRandomPointsExtentAlgorithm::group() const
43{
44 return QObject::tr( "Vector creation" );
45}
46
47QString QgsRandomPointsExtentAlgorithm::groupId() const
48{
49 return QStringLiteral( "vectorcreation" );
50}
51
52void QgsRandomPointsExtentAlgorithm::initAlgorithm( const QVariantMap & )
53{
54
55 addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Input extent" ) ) );
56 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "POINTS_NUMBER" ), QObject::tr( "Number of points" ), Qgis::ProcessingNumberParameterType::Integer, 1, false, 1 ) );
57 addParameter( new QgsProcessingParameterDistance( QStringLiteral( "MIN_DISTANCE" ), QObject::tr( "Minimum distance between points" ), 0, QStringLiteral( "TARGET_CRS" ), true, 0 ) );
58 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "TARGET_CRS" ), QObject::tr( "Target CRS" ), QStringLiteral( "ProjectCrs" ), false ) );
59
60 std::unique_ptr< QgsProcessingParameterNumber > maxAttempts_param = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "MAX_ATTEMPTS" ), QObject::tr( "Maximum number of search attempts given the minimum distance" ), Qgis::ProcessingNumberParameterType::Integer, 200, true, 1 );
61 maxAttempts_param->setFlags( maxAttempts_param->flags() | Qgis::ProcessingParameterFlag::Advanced );
62 addParameter( maxAttempts_param.release() );
63
64 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Random points" ), Qgis::ProcessingSourceType::VectorPoint ) );
65}
66
67QString QgsRandomPointsExtentAlgorithm::shortHelpString() const
68{
69 return QObject::tr( "This algorithm creates a new point layer with a given "
70 "number of random points, all of them within a given extent. "
71 "A distance factor can be specified, to avoid points being "
72 "too close to each other. If the minimum distance between points "
73 "makes it impossible to create new points, either "
74 "distance can be decreased or the maximum number of attempts may be "
75 "increased."
76 );
77}
78
79QgsRandomPointsExtentAlgorithm *QgsRandomPointsExtentAlgorithm::createInstance() const
80{
81 return new QgsRandomPointsExtentAlgorithm();
82}
83
84bool QgsRandomPointsExtentAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
85{
86 mCrs = parameterAsCrs( parameters, QStringLiteral( "TARGET_CRS" ), context );
87 mExtent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, mCrs );
88 mNumPoints = parameterAsInt( parameters, QStringLiteral( "POINTS_NUMBER" ), context );
89 mDistance = parameterAsDouble( parameters, QStringLiteral( "MIN_DISTANCE" ), context );
90 mMaxAttempts = parameterAsInt( parameters, QStringLiteral( "MAX_ATTEMPTS" ), context );
91
92 return true;
93}
94
95QVariantMap QgsRandomPointsExtentAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
96{
97
98 QgsFields fields = QgsFields();
99 fields.append( QgsField( QStringLiteral( "id" ), QVariant::LongLong ) );
100
101 QString dest;
102 std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, Qgis::WkbType::Point, mCrs ) );
103 if ( !sink )
104 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
105
106 //initialize random engine
107 std::random_device random_device;
108 const std::mt19937 mersenne_twister( random_device() );
109
110 std::uniform_real_distribution<double> x_distribution( mExtent.xMinimum(), mExtent.xMaximum() );
111 std::uniform_real_distribution<double> y_distribution( mExtent.yMinimum(), mExtent.yMaximum() );
112
113 if ( mDistance == 0 )
114 {
115 int i = 0;
116 while ( i < mNumPoints )
117 {
118 if ( feedback->isCanceled() )
119 break;
120
121 const double rx = x_distribution( random_device );
122 const double ry = y_distribution( random_device );
123
124 QgsFeature f = QgsFeature( i );
125
126 f.setGeometry( QgsGeometry( new QgsPoint( rx, ry ) ) );
127 f.setAttributes( QgsAttributes() << i );
128 if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
129 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
130 i++;
131 feedback->setProgress( static_cast<int>( static_cast<double>( i ) / static_cast<double>( mNumPoints ) * 100 ) );
132 }
133 }
134 else
135 {
137 int distCheckIterations = 0;
138
139 int i = 0;
140 while ( i < mNumPoints )
141 {
142 if ( feedback->isCanceled() )
143 break;
144
145 const double rx = x_distribution( random_device );
146 const double ry = y_distribution( random_device );
147
148 //check if new random point is inside searching distance to existing points
149 const QList<QgsFeatureId> neighbors = index.nearestNeighbor( QgsPointXY( rx, ry ), 1, mDistance );
150 if ( neighbors.empty() )
151 {
152 QgsFeature f = QgsFeature( i );
153 f.setAttributes( QgsAttributes() << i );
154 const QgsGeometry randomPointGeom = QgsGeometry( new QgsPoint( rx, ry ) );
155 f.setGeometry( randomPointGeom );
156 if ( !index.addFeature( f ) ||
157 !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
158 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
159 i++;
160 distCheckIterations = 0; //reset distCheckIterations if a point is added
161 feedback->setProgress( static_cast<int>( static_cast<double>( i ) / static_cast<double>( mNumPoints ) * 100 ) );
162 }
163 else
164 {
165 if ( distCheckIterations == mMaxAttempts )
166 {
167 throw QgsProcessingException( QObject::tr( "%1 of %2 points have been successfully created, but no more random points could be found "
168 "due to the given minimum distance between points. Either choose a larger extent, "
169 "lower the minimum distance between points or try increasing the number "
170 "of attempts for searching new points." ).arg( i ).arg( mNumPoints ) );
171 }
172 else
173 {
174 distCheckIterations++;
175 continue; //retry with new point
176 }
177
178 }
179 }
180 }
181
182 QVariantMap outputs;
183 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
184
185 return outputs;
186}
187
@ VectorPoint
Vector point layers.
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
A vector of attributes.
Definition: qgsattributes.h:59
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:160
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:167
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:162
A class to represent a 2D point.
Definition: qgspointxy.h:60
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
A coordinate reference system parameter for processing algorithms.
A double numeric parameter for distance values.
A rectangular map extent parameter for processing algorithms.
A feature sink output for processing algorithms.
A numeric parameter for processing algorithms.
A spatial index for QgsFeature objects.
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a feature to the index.