Branch data Line data Source code
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 : :
20 : : #include "qgsalgorithmrandompointsextent.h"
21 : : #include "random"
22 : :
23 : : ///@cond PRIVATE
24 : :
25 : 0 : QString QgsRandomPointsExtentAlgorithm::name() const
26 : : {
27 : 0 : return QStringLiteral( "randompointsinextent" );
28 : : }
29 : :
30 : 0 : QString QgsRandomPointsExtentAlgorithm::displayName() const
31 : : {
32 : 0 : return QObject::tr( "Random points in extent" );
33 : : }
34 : :
35 : 0 : QStringList QgsRandomPointsExtentAlgorithm::tags() const
36 : : {
37 : 0 : return QObject::tr( "random,points,extent,create" ).split( ',' );
38 : 0 : }
39 : :
40 : 0 : QString QgsRandomPointsExtentAlgorithm::group() const
41 : : {
42 : 0 : return QObject::tr( "Vector creation" );
43 : : }
44 : :
45 : 0 : QString QgsRandomPointsExtentAlgorithm::groupId() const
46 : : {
47 : 0 : return QStringLiteral( "vectorcreation" );
48 : : }
49 : :
50 : 0 : void QgsRandomPointsExtentAlgorithm::initAlgorithm( const QVariantMap & )
51 : : {
52 : :
53 : 0 : addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Input extent" ) ) );
54 : 0 : addParameter( new QgsProcessingParameterNumber( QStringLiteral( "POINTS_NUMBER" ), QObject::tr( "Number of points" ), QgsProcessingParameterNumber::Integer, 1, false, 1 ) );
55 : 0 : addParameter( new QgsProcessingParameterDistance( QStringLiteral( "MIN_DISTANCE" ), QObject::tr( "Minimum distance between points" ), 0, QStringLiteral( "TARGET_CRS" ), true, 0 ) );
56 : 0 : addParameter( new QgsProcessingParameterCrs( QStringLiteral( "TARGET_CRS" ), QObject::tr( "Target CRS" ), QStringLiteral( "ProjectCrs" ), false ) );
57 : :
58 : 0 : 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 : 0 : maxAttempts_param->setFlags( maxAttempts_param->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
60 : 0 : addParameter( maxAttempts_param.release() );
61 : :
62 : 0 : addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Random points" ), QgsProcessing::TypeVectorPoint ) );
63 : 0 : }
64 : :
65 : 0 : QString QgsRandomPointsExtentAlgorithm::shortHelpString() const
66 : : {
67 : 0 : 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 : 0 : QgsRandomPointsExtentAlgorithm *QgsRandomPointsExtentAlgorithm::createInstance() const
78 : : {
79 : 0 : return new QgsRandomPointsExtentAlgorithm();
80 : 0 : }
81 : :
82 : 0 : bool QgsRandomPointsExtentAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * )
83 : : {
84 : 0 : mCrs = parameterAsCrs( parameters, QStringLiteral( "TARGET_CRS" ), context );
85 : 0 : mExtent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, mCrs );
86 : 0 : mNumPoints = parameterAsInt( parameters, QStringLiteral( "POINTS_NUMBER" ), context );
87 : 0 : mDistance = parameterAsDouble( parameters, QStringLiteral( "MIN_DISTANCE" ), context );
88 : 0 : mMaxAttempts = parameterAsInt( parameters, QStringLiteral( "MAX_ATTEMPTS" ), context );
89 : :
90 : 0 : return true;
91 : 0 : }
92 : :
93 : 0 : QVariantMap QgsRandomPointsExtentAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
94 : : {
95 : :
96 : 0 : QgsFields fields = QgsFields();
97 : 0 : fields.append( QgsField( QStringLiteral( "id" ), QVariant::LongLong ) );
98 : :
99 : 0 : QString dest;
100 : 0 : std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::Point, mCrs ) );
101 : 0 : if ( !sink )
102 : 0 : throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
103 : :
104 : : //initialize random engine
105 : 0 : std::random_device random_device;
106 : 0 : std::mt19937 mersenne_twister( random_device() );
107 : :
108 : 0 : std::uniform_real_distribution<double> x_distribution( mExtent.xMinimum(), mExtent.xMaximum() );
109 : 0 : std::uniform_real_distribution<double> y_distribution( mExtent.yMinimum(), mExtent.yMaximum() );
110 : :
111 : 0 : if ( mDistance == 0 )
112 : : {
113 : 0 : int i = 0;
114 : 0 : while ( i < mNumPoints )
115 : : {
116 : 0 : if ( feedback->isCanceled() )
117 : 0 : break;
118 : :
119 : 0 : double rx = x_distribution( random_device );
120 : 0 : double ry = y_distribution( random_device );
121 : :
122 : 0 : QgsFeature f = QgsFeature( i );
123 : :
124 : 0 : f.setGeometry( QgsGeometry( new QgsPoint( rx, ry ) ) );
125 : 0 : f.setAttributes( QgsAttributes() << i );
126 : 0 : sink->addFeature( f, QgsFeatureSink::FastInsert );
127 : 0 : i++;
128 : 0 : feedback->setProgress( static_cast<int>( static_cast<double>( i ) / static_cast<double>( mNumPoints ) * 100 ) );
129 : 0 : }
130 : 0 : }
131 : : else
132 : : {
133 : 0 : QgsSpatialIndex index = QgsSpatialIndex();
134 : 0 : int distCheckIterations = 0;
135 : :
136 : 0 : int i = 0;
137 : 0 : while ( i < mNumPoints )
138 : : {
139 : 0 : if ( feedback->isCanceled() )
140 : 0 : break;
141 : :
142 : 0 : double rx = x_distribution( random_device );
143 : 0 : double ry = y_distribution( random_device );
144 : :
145 : : //check if new random point is inside searching distance to existing points
146 : 0 : QList<QgsFeatureId> neighbors = index.nearestNeighbor( QgsPointXY( rx, ry ), 1, mDistance );
147 : 0 : if ( neighbors.empty() )
148 : : {
149 : 0 : QgsFeature f = QgsFeature( i );
150 : 0 : f.setAttributes( QgsAttributes() << i );
151 : 0 : QgsGeometry randomPointGeom = QgsGeometry( new QgsPoint( rx, ry ) );
152 : 0 : f.setGeometry( randomPointGeom );
153 : 0 : index.addFeature( f );
154 : 0 : sink->addFeature( f, QgsFeatureSink::FastInsert );
155 : 0 : i++;
156 : 0 : distCheckIterations = 0; //reset distCheckIterations if a point is added
157 : 0 : feedback->setProgress( static_cast<int>( static_cast<double>( i ) / static_cast<double>( mNumPoints ) * 100 ) );
158 : 0 : }
159 : : else
160 : : {
161 : 0 : if ( distCheckIterations == mMaxAttempts )
162 : : {
163 : 0 : throw QgsProcessingException( QObject::tr( "%1 of %2 points have been successfully created, but no more random points could be found "
164 : : "due to the given minimum distance between points. Either choose a larger extent, "
165 : : "lower the minimum distance between points or try increasing the number "
166 : 0 : "of attempts for searching new points." ).arg( i ).arg( mNumPoints ) );
167 : : }
168 : : else
169 : : {
170 : 0 : distCheckIterations++;
171 : 0 : continue; //retry with new point
172 : : }
173 : :
174 : : }
175 : 0 : }
176 : 0 : }
177 : :
178 : 0 : QVariantMap outputs;
179 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), dest );
180 : :
181 : 0 : return outputs;
182 : 0 : }
183 : :
184 : : ///@endcond
|