QGIS API Documentation 3.99.0-Master (357b655ed83)
Loading...
Searching...
No Matches
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
22#include <random>
23
24#include "qgsspatialindex.h"
25
26#include <QString>
27
28using namespace Qt::StringLiterals;
29
31
32QString QgsRandomPointsExtentAlgorithm::name() const
33{
34 return u"randompointsinextent"_s;
35}
36
37QString QgsRandomPointsExtentAlgorithm::displayName() const
38{
39 return QObject::tr( "Random points in extent" );
40}
41
42QStringList QgsRandomPointsExtentAlgorithm::tags() const
43{
44 return QObject::tr( "random,points,extent,create" ).split( ',' );
45}
46
47QString QgsRandomPointsExtentAlgorithm::group() const
48{
49 return QObject::tr( "Vector creation" );
50}
51
52QString QgsRandomPointsExtentAlgorithm::groupId() const
53{
54 return u"vectorcreation"_s;
55}
56
57void QgsRandomPointsExtentAlgorithm::initAlgorithm( const QVariantMap & )
58{
59 addParameter( new QgsProcessingParameterExtent( u"EXTENT"_s, QObject::tr( "Input extent" ) ) );
60 addParameter( new QgsProcessingParameterNumber( u"POINTS_NUMBER"_s, QObject::tr( "Number of points" ), Qgis::ProcessingNumberParameterType::Integer, 1, false, 1 ) );
61 addParameter( new QgsProcessingParameterDistance( u"MIN_DISTANCE"_s, QObject::tr( "Minimum distance between points" ), 0, u"TARGET_CRS"_s, true, 0 ) );
62 addParameter( new QgsProcessingParameterCrs( u"TARGET_CRS"_s, QObject::tr( "Target CRS" ), u"ProjectCrs"_s, false ) );
63
64 auto maxAttempts_param = std::make_unique<QgsProcessingParameterNumber>( u"MAX_ATTEMPTS"_s, QObject::tr( "Maximum number of search attempts given the minimum distance" ), Qgis::ProcessingNumberParameterType::Integer, 200, true, 1 );
65 maxAttempts_param->setFlags( maxAttempts_param->flags() | Qgis::ProcessingParameterFlag::Advanced );
66 addParameter( maxAttempts_param.release() );
67
68 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Random points" ), Qgis::ProcessingSourceType::VectorPoint ) );
69}
70
71QString QgsRandomPointsExtentAlgorithm::shortHelpString() const
72{
73 return QObject::tr( "This algorithm creates a new point layer with a given "
74 "number of random points, all of them within a given extent. "
75 "A distance factor can be specified, to avoid points being "
76 "too close to each other. If the minimum distance between points "
77 "makes it impossible to create new points, either "
78 "distance can be decreased or the maximum number of attempts may be "
79 "increased."
80 );
81}
82
83QString QgsRandomPointsExtentAlgorithm::shortDescription() const
84{
85 return QObject::tr( "Creates a point layer with a given number of random points, all of them within a given extent." );
86}
87
88QgsRandomPointsExtentAlgorithm *QgsRandomPointsExtentAlgorithm::createInstance() const
89{
90 return new QgsRandomPointsExtentAlgorithm();
91}
92
93bool QgsRandomPointsExtentAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
94{
95 mCrs = parameterAsCrs( parameters, u"TARGET_CRS"_s, context );
96 mExtent = parameterAsExtent( parameters, u"EXTENT"_s, context, mCrs );
97 mNumPoints = parameterAsInt( parameters, u"POINTS_NUMBER"_s, context );
98 mDistance = parameterAsDouble( parameters, u"MIN_DISTANCE"_s, context );
99 mMaxAttempts = parameterAsInt( parameters, u"MAX_ATTEMPTS"_s, context );
100
101 return true;
102}
103
104QVariantMap QgsRandomPointsExtentAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
105{
106 QgsFields fields = QgsFields();
107 fields.append( QgsField( u"id"_s, QMetaType::Type::LongLong ) );
108
109 QString dest;
110 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, dest, fields, Qgis::WkbType::Point, mCrs ) );
111 if ( !sink )
112 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
113
114 //initialize random engine
115 std::random_device random_device;
116 const std::mt19937 mersenne_twister( random_device() );
117
118 std::uniform_real_distribution<double> x_distribution( mExtent.xMinimum(), mExtent.xMaximum() );
119 std::uniform_real_distribution<double> y_distribution( mExtent.yMinimum(), mExtent.yMaximum() );
120
121 if ( mDistance == 0 )
122 {
123 int i = 0;
124 while ( i < mNumPoints )
125 {
126 if ( feedback->isCanceled() )
127 break;
128
129 const double rx = x_distribution( random_device );
130 const double ry = y_distribution( random_device );
131
132 QgsFeature f = QgsFeature( i );
133
134 f.setGeometry( QgsGeometry( new QgsPoint( rx, ry ) ) );
135 f.setAttributes( QgsAttributes() << i );
136 if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
137 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
138 i++;
139 feedback->setProgress( static_cast<int>( static_cast<double>( i ) / static_cast<double>( mNumPoints ) * 100 ) );
140 }
141 }
142 else
143 {
145 int distCheckIterations = 0;
146
147 int i = 0;
148 while ( i < mNumPoints )
149 {
150 if ( feedback->isCanceled() )
151 break;
152
153 const double rx = x_distribution( random_device );
154 const double ry = y_distribution( random_device );
155
156 //check if new random point is inside searching distance to existing points
157 const QList<QgsFeatureId> neighbors = index.nearestNeighbor( QgsPointXY( rx, ry ), 1, mDistance );
158 if ( neighbors.empty() )
159 {
160 QgsFeature f = QgsFeature( i );
161 f.setAttributes( QgsAttributes() << i );
162 const QgsGeometry randomPointGeom = QgsGeometry( new QgsPoint( rx, ry ) );
163 f.setGeometry( randomPointGeom );
164 if ( !index.addFeature( f ) || !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
165 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
166 i++;
167 distCheckIterations = 0; //reset distCheckIterations if a point is added
168 feedback->setProgress( static_cast<int>( static_cast<double>( i ) / static_cast<double>( mNumPoints ) * 100 ) );
169 }
170 else
171 {
172 if ( distCheckIterations == mMaxAttempts )
173 {
174 throw QgsProcessingException( QObject::tr( "%1 of %2 points have been successfully created, but no more random points could be found "
175 "due to the given minimum distance between points. Either choose a larger extent, "
176 "lower the minimum distance between points or try increasing the number "
177 "of attempts for searching new points." )
178 .arg( i )
179 .arg( mNumPoints ) );
180 }
181 else
182 {
183 distCheckIterations++;
184 continue; //retry with new point
185 }
186 }
187 }
188 }
189
190 sink->finalize();
191
192 QVariantMap outputs;
193 outputs.insert( u"OUTPUT"_s, dest );
194
195 return outputs;
196}
197
@ VectorPoint
Vector point layers.
Definition qgis.h:3605
@ Point
Point.
Definition qgis.h:282
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3834
A vector of attributes.
@ 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:60
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:63
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:76
A geometry is the spatial representation of a feature.
Represents a 2D point.
Definition qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
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.