Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsalgorithmmeancoordinates.cpp
3 : : ---------------------
4 : : begin : April 2017
5 : : copyright : (C) 2017 by Nyall Dawson
6 : : email : nyall dot dawson 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 : : #include "qgsalgorithmmeancoordinates.h"
19 : :
20 : : ///@cond PRIVATE
21 : :
22 : 0 : QString QgsMeanCoordinatesAlgorithm::name() const
23 : : {
24 : 0 : return QStringLiteral( "meancoordinates" );
25 : : }
26 : :
27 : 0 : QString QgsMeanCoordinatesAlgorithm::displayName() const
28 : : {
29 : 0 : return QObject::tr( "Mean coordinate(s)" );
30 : : }
31 : :
32 : 0 : QStringList QgsMeanCoordinatesAlgorithm::tags() const
33 : : {
34 : 0 : return QObject::tr( "mean,average,coordinate" ).split( ',' );
35 : 0 : }
36 : :
37 : 0 : QString QgsMeanCoordinatesAlgorithm::group() const
38 : : {
39 : 0 : return QObject::tr( "Vector analysis" );
40 : : }
41 : :
42 : 0 : QString QgsMeanCoordinatesAlgorithm::groupId() const
43 : : {
44 : 0 : return QStringLiteral( "vectoranalysis" );
45 : : }
46 : :
47 : 0 : void QgsMeanCoordinatesAlgorithm::initAlgorithm( const QVariantMap & )
48 : : {
49 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
50 : 0 : QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
51 : 0 : addParameter( new QgsProcessingParameterField( QStringLiteral( "WEIGHT" ), QObject::tr( "Weight field" ),
52 : 0 : QVariant(), QStringLiteral( "INPUT" ),
53 : : QgsProcessingParameterField::Numeric, false, true ) );
54 : 0 : addParameter( new QgsProcessingParameterField( QStringLiteral( "UID" ),
55 : 0 : QObject::tr( "Unique ID field" ), QVariant(),
56 : 0 : QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, false, true ) );
57 : 0 : addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Mean coordinates" ), QgsProcessing::TypeVectorPoint ) );
58 : 0 : }
59 : :
60 : 0 : QString QgsMeanCoordinatesAlgorithm::shortHelpString() const
61 : : {
62 : 0 : return QObject::tr( "This algorithm computes a point layer with the center of mass of geometries in an input layer.\n\n"
63 : : "An attribute can be specified as containing weights to be applied to each feature when computing the center of mass.\n\n"
64 : : "If an attribute is selected in the <Unique ID field> parameter, features will be grouped according "
65 : : "to values in this field. Instead of a single point with the center of mass of the whole layer, "
66 : : "the output layer will contain a center of mass for the features in each category." );
67 : : }
68 : :
69 : 0 : QgsMeanCoordinatesAlgorithm *QgsMeanCoordinatesAlgorithm::createInstance() const
70 : : {
71 : 0 : return new QgsMeanCoordinatesAlgorithm();
72 : : }
73 : :
74 : 0 : QVariantMap QgsMeanCoordinatesAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
75 : : {
76 : 0 : std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
77 : 0 : if ( !source )
78 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
79 : :
80 : 0 : QString weightFieldName = parameterAsString( parameters, QStringLiteral( "WEIGHT" ), context );
81 : 0 : QString uniqueFieldName = parameterAsString( parameters, QStringLiteral( "UID" ), context );
82 : :
83 : 0 : QgsAttributeList attributes;
84 : 0 : int weightIndex = -1;
85 : 0 : if ( !weightFieldName.isEmpty() )
86 : : {
87 : 0 : weightIndex = source->fields().lookupField( weightFieldName );
88 : 0 : if ( weightIndex >= 0 )
89 : 0 : attributes.append( weightIndex );
90 : 0 : }
91 : :
92 : 0 : int uniqueFieldIndex = -1;
93 : 0 : if ( !uniqueFieldName.isEmpty() )
94 : : {
95 : 0 : uniqueFieldIndex = source->fields().lookupField( uniqueFieldName );
96 : 0 : if ( uniqueFieldIndex >= 0 )
97 : 0 : attributes.append( uniqueFieldIndex );
98 : 0 : }
99 : :
100 : 0 : QgsFields fields;
101 : 0 : fields.append( QgsField( QStringLiteral( "MEAN_X" ), QVariant::Double, QString(), 24, 15 ) );
102 : 0 : fields.append( QgsField( QStringLiteral( "MEAN_Y" ), QVariant::Double, QString(), 24, 15 ) );
103 : 0 : if ( uniqueFieldIndex >= 0 )
104 : : {
105 : 0 : QgsField uniqueField = source->fields().at( uniqueFieldIndex );
106 : 0 : fields.append( uniqueField );
107 : 0 : }
108 : :
109 : 0 : QString dest;
110 : 0 : std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields,
111 : 0 : QgsWkbTypes::Point, source->sourceCrs() ) );
112 : 0 : if ( !sink )
113 : 0 : throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
114 : :
115 : 0 : QgsFeatureIterator features = source->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( attributes ), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
116 : :
117 : 0 : double step = source->featureCount() > 0 ? 50.0 / source->featureCount() : 1;
118 : 0 : int i = 0;
119 : 0 : QgsFeature feat;
120 : :
121 : 0 : QHash< QVariant, QList< double > > means;
122 : 0 : while ( features.nextFeature( feat ) )
123 : : {
124 : 0 : i++;
125 : 0 : if ( feedback->isCanceled() )
126 : : {
127 : 0 : break;
128 : : }
129 : :
130 : 0 : feedback->setProgress( i * step );
131 : 0 : if ( !feat.hasGeometry() )
132 : 0 : continue;
133 : :
134 : :
135 : 0 : QVariant featureClass;
136 : 0 : if ( uniqueFieldIndex >= 0 )
137 : : {
138 : 0 : featureClass = feat.attribute( uniqueFieldIndex );
139 : 0 : }
140 : : else
141 : : {
142 : 0 : featureClass = QStringLiteral( "#####singleclass#####" );
143 : : }
144 : :
145 : 0 : double weight = 1;
146 : 0 : if ( weightIndex >= 0 )
147 : : {
148 : 0 : bool ok = false;
149 : 0 : weight = feat.attribute( weightIndex ).toDouble( &ok );
150 : 0 : if ( !ok )
151 : 0 : weight = 1.0;
152 : 0 : }
153 : :
154 : 0 : if ( weight < 0 )
155 : : {
156 : 0 : throw QgsProcessingException( QObject::tr( "Negative weight value found. Please fix your data and try again." ) );
157 : : }
158 : :
159 : 0 : QList< double > values = means.value( featureClass );
160 : 0 : double cx = 0;
161 : 0 : double cy = 0;
162 : 0 : double totalWeight = 0;
163 : 0 : if ( !values.empty() )
164 : : {
165 : 0 : cx = values.at( 0 );
166 : 0 : cy = values.at( 1 );
167 : 0 : totalWeight = values.at( 2 );
168 : 0 : }
169 : :
170 : 0 : QgsVertexId vid;
171 : 0 : QgsPoint pt;
172 : 0 : const QgsAbstractGeometry *g = feat.geometry().constGet();
173 : : // NOTE - should this be including the duplicate nodes for closed rings? currently it is,
174 : : // but I suspect that the expected behavior would be to NOT include these
175 : 0 : while ( g->nextVertex( vid, pt ) )
176 : : {
177 : 0 : cx += pt.x() * weight;
178 : 0 : cy += pt.y() * weight;
179 : 0 : totalWeight += weight;
180 : : }
181 : :
182 : 0 : means[featureClass] = QList< double >() << cx << cy << totalWeight;
183 : 0 : }
184 : :
185 : 0 : i = 0;
186 : 0 : step = !means.empty() ? 50.0 / means.count() : 1;
187 : 0 : for ( auto it = means.constBegin(); it != means.constEnd(); ++it )
188 : : {
189 : 0 : i++;
190 : 0 : if ( feedback->isCanceled() )
191 : : {
192 : 0 : break;
193 : : }
194 : :
195 : 0 : feedback->setProgress( 50 + i * step );
196 : 0 : if ( qgsDoubleNear( it.value().at( 2 ), 0 ) )
197 : 0 : continue;
198 : :
199 : 0 : QgsFeature outFeat;
200 : 0 : double cx = it.value().at( 0 ) / it.value().at( 2 );
201 : 0 : double cy = it.value().at( 1 ) / it.value().at( 2 );
202 : :
203 : 0 : QgsPointXY meanPoint( cx, cy );
204 : 0 : outFeat.setGeometry( QgsGeometry::fromPointXY( meanPoint ) );
205 : :
206 : 0 : QgsAttributes attributes;
207 : 0 : attributes << cx << cy;
208 : 0 : if ( uniqueFieldIndex >= 0 )
209 : 0 : attributes.append( it.key() );
210 : :
211 : 0 : outFeat.setAttributes( attributes );
212 : 0 : sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
213 : 0 : }
214 : :
215 : 0 : QVariantMap outputs;
216 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), dest );
217 : 0 : return outputs;
218 : 0 : }
219 : :
220 : :
221 : : ///@endcond
222 : :
223 : :
|