QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 "random"
22 
24 
25 QString QgsRandomPointsExtentAlgorithm::name() const
26 {
27  return QStringLiteral( "randompointsinextent" );
28 }
29 
30 QString QgsRandomPointsExtentAlgorithm::displayName() const
31 {
32  return QObject::tr( "Random points in extent" );
33 }
34 
35 QStringList QgsRandomPointsExtentAlgorithm::tags() const
36 {
37  return QObject::tr( "random,points,extent,create" ).split( ',' );
38 }
39 
40 QString QgsRandomPointsExtentAlgorithm::group() const
41 {
42  return QObject::tr( "Vector creation" );
43 }
44 
45 QString QgsRandomPointsExtentAlgorithm::groupId() const
46 {
47  return QStringLiteral( "vectorcreation" );
48 }
49 
50 void QgsRandomPointsExtentAlgorithm::initAlgorithm( const QVariantMap & )
51 {
52 
53  addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Input extent" ) ) );
54  addParameter( new QgsProcessingParameterNumber( QStringLiteral( "POINTS_NUMBER" ), QObject::tr( "Number of points" ), QgsProcessingParameterNumber::Integer, 1, false, 1 ) );
55  addParameter( new QgsProcessingParameterDistance( QStringLiteral( "MIN_DISTANCE" ), QObject::tr( "Minimum distance between points" ), 0, QStringLiteral( "TARGET_CRS" ), true, 0 ) );
56  addParameter( new QgsProcessingParameterCrs( QStringLiteral( "TARGET_CRS" ), QObject::tr( "Target CRS" ), QStringLiteral( "ProjectCrs" ), false ) );
57 
58  std::unique_ptr< QgsProcessingParameterNumber > maxAttempts_param = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "MAX_ATTEMPTS" ), QObject::tr( "Maximum number of search attempts given the minimum distance" ), QgsProcessingParameterNumber::Integer, 200, true, 1 );
59  maxAttempts_param->setFlags( maxAttempts_param->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
60  addParameter( maxAttempts_param.release() );
61 
62  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Random points" ), QgsProcessing::TypeVectorPoint ) );
63 }
64 
65 QString QgsRandomPointsExtentAlgorithm::shortHelpString() const
66 {
67  return QObject::tr( "This algorithm creates a new point layer with a given "
68  "number of random points, all of them within a given extent. "
69  "A distance factor can be specified, to avoid points being "
70  "too close to each other. If the minimum distance between points "
71  "makes it impossible to create new points, either "
72  "distance can be decreased or the maximum number of attempts may be "
73  "increased."
74  );
75 }
76 
77 QgsRandomPointsExtentAlgorithm *QgsRandomPointsExtentAlgorithm::createInstance() const
78 {
79  return new QgsRandomPointsExtentAlgorithm();
80 }
81 
82 bool QgsRandomPointsExtentAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
83 {
84  mCrs = parameterAsCrs( parameters, QStringLiteral( "TARGET_CRS" ), context );
85  mExtent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, mCrs );
86  mNumPoints = parameterAsInt( parameters, QStringLiteral( "POINTS_NUMBER" ), context );
87  mDistance = parameterAsDouble( parameters, QStringLiteral( "MIN_DISTANCE" ), context );
88  mMaxAttempts = parameterAsInt( parameters, QStringLiteral( "MAX_ATTEMPTS" ), context );
89 
90  return true;
91 }
92 
93 QVariantMap QgsRandomPointsExtentAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
94 {
95 
96  QgsFields fields = QgsFields();
97  fields.append( QgsField( QStringLiteral( "id" ), QVariant::LongLong ) );
98 
99  QString dest;
100  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::Point, mCrs ) );
101  if ( !sink )
102  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
103 
104  //initialize random engine
105  std::random_device random_device;
106  const std::mt19937 mersenne_twister( random_device() );
107 
108  std::uniform_real_distribution<double> x_distribution( mExtent.xMinimum(), mExtent.xMaximum() );
109  std::uniform_real_distribution<double> y_distribution( mExtent.yMinimum(), mExtent.yMaximum() );
110 
111  if ( mDistance == 0 )
112  {
113  int i = 0;
114  while ( i < mNumPoints )
115  {
116  if ( feedback->isCanceled() )
117  break;
118 
119  const double rx = x_distribution( random_device );
120  const double ry = y_distribution( random_device );
121 
122  QgsFeature f = QgsFeature( i );
123 
124  f.setGeometry( QgsGeometry( new QgsPoint( rx, ry ) ) );
125  f.setAttributes( QgsAttributes() << i );
126  if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
127  throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
128  i++;
129  feedback->setProgress( static_cast<int>( static_cast<double>( i ) / static_cast<double>( mNumPoints ) * 100 ) );
130  }
131  }
132  else
133  {
135  int distCheckIterations = 0;
136 
137  int i = 0;
138  while ( i < mNumPoints )
139  {
140  if ( feedback->isCanceled() )
141  break;
142 
143  const double rx = x_distribution( random_device );
144  const double ry = y_distribution( random_device );
145 
146  //check if new random point is inside searching distance to existing points
147  const QList<QgsFeatureId> neighbors = index.nearestNeighbor( QgsPointXY( rx, ry ), 1, mDistance );
148  if ( neighbors.empty() )
149  {
150  QgsFeature f = QgsFeature( i );
151  f.setAttributes( QgsAttributes() << i );
152  const QgsGeometry randomPointGeom = QgsGeometry( new QgsPoint( rx, ry ) );
153  f.setGeometry( randomPointGeom );
154  if ( !index.addFeature( f ) ||
155  !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
156  throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
157  i++;
158  distCheckIterations = 0; //reset distCheckIterations if a point is added
159  feedback->setProgress( static_cast<int>( static_cast<double>( i ) / static_cast<double>( mNumPoints ) * 100 ) );
160  }
161  else
162  {
163  if ( distCheckIterations == mMaxAttempts )
164  {
165  throw QgsProcessingException( QObject::tr( "%1 of %2 points have been successfully created, but no more random points could be found "
166  "due to the given minimum distance between points. Either choose a larger extent, "
167  "lower the minimum distance between points or try increasing the number "
168  "of attempts for searching new points." ).arg( i ).arg( mNumPoints ) );
169  }
170  else
171  {
172  distCheckIterations++;
173  continue; //retry with new point
174  }
175 
176  }
177  }
178  }
179 
180  QVariantMap outputs;
181  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
182 
183  return outputs;
184 }
185 
A vector of attributes.
Definition: qgsattributes.h:58
@ 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:153
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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:51
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:125
A class to represent a 2D point.
Definition: qgspointxy.h:59
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.
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
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.
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:49
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.