QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsalgorithmangletonearest.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmangletonearest.cpp
3  ---------------------
4  begin : July 2020
5  copyright : (C) 2020 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 
19 #include "qgsprocessingoutputs.h"
20 #include "qgslinestring.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsrenderer.h"
23 #include "qgsstyleentityvisitor.h"
24 
26 
27 class SetMarkerRotationVisitor : public QgsStyleEntityVisitorInterface
28 {
29  public:
30 
31  SetMarkerRotationVisitor( const QString &rotationField )
32  : mRotationField( rotationField )
33  {}
34 
36  {
37  if ( const QgsStyleSymbolEntity *symbolEntity = dynamic_cast< const QgsStyleSymbolEntity * >( entity.entity ) )
38  {
39  if ( QgsMarkerSymbol *marker = dynamic_cast< QgsMarkerSymbol * >( symbolEntity->symbol() ) )
40  {
41  marker->setDataDefinedAngle( QgsProperty::fromField( mRotationField ) );
42  }
43  }
44  return true;
45  }
46 
47  private:
48  QString mRotationField;
49 
50 };
51 
52 class SetMarkerRotationPostProcessor : public QgsProcessingLayerPostProcessorInterface
53 {
54  public:
55 
56  SetMarkerRotationPostProcessor( std::unique_ptr< QgsFeatureRenderer > renderer, const QString &rotationField )
57  : mRenderer( std::move( renderer ) )
58  , mRotationField( rotationField )
59  {}
60 
62  {
63  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
64  {
65  SetMarkerRotationVisitor visitor( mRotationField );
66  mRenderer->accept( &visitor );
67  vl->setRenderer( mRenderer.release() );
68  vl->triggerRepaint();
69  }
70  }
71 
72  private:
73 
74  std::unique_ptr<QgsFeatureRenderer> mRenderer;
75  QString mRotationField;
76 };
77 
78 QString QgsAngleToNearestAlgorithm::name() const
79 {
80  return QStringLiteral( "angletonearest" );
81 }
82 
83 QString QgsAngleToNearestAlgorithm::displayName() const
84 {
85  return QObject::tr( "Align points to features" );
86 }
87 
88 QStringList QgsAngleToNearestAlgorithm::tags() const
89 {
90  return QObject::tr( "align,marker,stroke,fill,orient,points,lines,angles,rotation,rotate" ).split( ',' );
91 }
92 
93 QString QgsAngleToNearestAlgorithm::group() const
94 {
95  return QObject::tr( "Cartography" );
96 }
97 
98 QString QgsAngleToNearestAlgorithm::groupId() const
99 {
100  return QStringLiteral( "cartography" );
101 }
102 
103 QgsAngleToNearestAlgorithm::~QgsAngleToNearestAlgorithm() = default;
104 
105 void QgsAngleToNearestAlgorithm::initAlgorithm( const QVariantMap &configuration )
106 {
107  mIsInPlace = configuration.value( QStringLiteral( "IN_PLACE" ) ).toBool();
108 
109  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
110  QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorPoint ) );
111  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "REFERENCE_LAYER" ),
112  QObject::tr( "Reference layer" ) ) );
113 
114  addParameter( new QgsProcessingParameterDistance( QStringLiteral( "MAX_DISTANCE" ),
115  QObject::tr( "Maximum distance to consider" ), QVariant(), QStringLiteral( "INPUT" ), true, 0 ) );
116 
117  if ( !mIsInPlace )
118  addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD_NAME" ), QObject::tr( "Angle field name" ), QStringLiteral( "rotation" ) ) );
119  else
120  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD_NAME" ), QObject::tr( "Angle field name" ), QStringLiteral( "rotation" ), QStringLiteral( "INPUT" ) ) );
121 
122  addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "APPLY_SYMBOLOGY" ), QObject::tr( "Automatically apply symbology" ), true ) );
123 
124  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Aligned layer" ), QgsProcessing::TypeVectorPoint ) );
125 }
126 
127 QgsProcessingAlgorithm::Flags QgsAngleToNearestAlgorithm::flags() const
128 {
129  Flags f = QgsProcessingAlgorithm::flags();
131  return f;
132 }
133 
134 QString QgsAngleToNearestAlgorithm::shortHelpString() const
135 {
136  return QObject::tr( "This algorithm calculates the rotation required to align point features with their nearest "
137  "feature from another reference layer. A new field is added to the output layer which is filled with the angle "
138  "(in degrees, clockwise) to the nearest reference feature.\n\n"
139  "Optionally, the output layer's symbology can be set to automatically use the calculated rotation "
140  "field to rotate marker symbols.\n\n"
141  "If desired, a maximum distance to use when aligning points can be set, to avoid aligning isolated points "
142  "to distant features." );
143 }
144 
145 QString QgsAngleToNearestAlgorithm::shortDescription() const
146 {
147  return QObject::tr( "Rotates point features to align them to nearby features." );
148 }
149 
150 QgsAngleToNearestAlgorithm *QgsAngleToNearestAlgorithm::createInstance() const
151 {
152  return new QgsAngleToNearestAlgorithm();
153 }
154 
155 bool QgsAngleToNearestAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
156 {
157  if ( const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer ) )
158  {
159  return vl->geometryType() == QgsWkbTypes::PointGeometry;
160  }
161  return false;
162 }
163 
164 bool QgsAngleToNearestAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
165 {
166  if ( !mIsInPlace )
167  {
168  if ( QgsVectorLayer *sourceLayer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context ) )
169  {
170  mSourceRenderer.reset( sourceLayer->renderer()->clone() );
171  }
172  }
173 
174  return true;
175 }
176 
177 QVariantMap QgsAngleToNearestAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
178 {
179  const double maxDistance = parameters.value( QStringLiteral( "MAX_DISTANCE" ) ).isValid() ? parameterAsDouble( parameters, QStringLiteral( "MAX_DISTANCE" ), context ) : std::numeric_limits< double >::quiet_NaN();
180  std::unique_ptr< QgsProcessingFeatureSource > input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
181  if ( !input )
182  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
183 
184  std::unique_ptr< QgsProcessingFeatureSource > referenceSource( parameterAsSource( parameters, QStringLiteral( "REFERENCE_LAYER" ), context ) );
185  if ( !referenceSource )
186  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "REFERENCE_LAYER" ) ) );
187 
188  const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
189 
190  QgsFields outFields = input->fields();
191  int fieldIndex = -1;
192  if ( mIsInPlace )
193  {
194  fieldIndex = outFields.lookupField( fieldName );
195  }
196  else
197  {
198  outFields.append( QgsField( fieldName, QVariant::Double ) );
199  }
200 
201  QString dest;
202  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outFields,
203  input->wkbType(), input->sourceCrs() ) );
204  if ( parameters.value( QStringLiteral( "OUTPUT" ) ).isValid() && !sink )
205  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
206 
207  // make spatial index
208  QgsFeatureIterator f2 = referenceSource->getFeatures( QgsFeatureRequest().setDestinationCrs( input->sourceCrs(), context.transformContext() ).setNoAttributes() );
209  double step = referenceSource->featureCount() > 0 ? 50.0 / referenceSource->featureCount() : 1;
210  int i = 0;
211  QgsSpatialIndex index( f2, [&]( const QgsFeature & )->bool
212  {
213  i++;
214  if ( feedback->isCanceled() )
215  return false;
216 
217  feedback->setProgress( i * step );
218 
219  return true;
221 
222  QgsFeature f;
223 
224  // Create output vector layer with additional attributes
225  step = input->featureCount() > 0 ? 50.0 / input->featureCount() : 1;
226  QgsFeatureIterator features = input->getFeatures();
227  i = 0;
228  while ( features.nextFeature( f ) )
229  {
230  i++;
231  if ( feedback->isCanceled() )
232  {
233  break;
234  }
235 
236  feedback->setProgress( 50 + i * step );
237 
238  QgsAttributes attributes = f.attributes();
239 
240  if ( !f.hasGeometry() )
241  {
242  if ( !mIsInPlace )
243  attributes.append( QVariant() );
244  else
245  attributes[ fieldIndex ] = QVariant();
246  f.setAttributes( attributes );
247  sink->addFeature( f, QgsFeatureSink::FastInsert );
248  }
249  else
250  {
251  const QList< QgsFeatureId > nearest = index.nearestNeighbor( f.geometry(), 1, std::isnan( maxDistance ) ? 0 : maxDistance );
252  if ( nearest.empty() )
253  {
254  feedback->pushInfo( QObject::tr( "No matching features found within search distance" ) );
255  if ( !mIsInPlace )
256  attributes.append( QVariant() );
257  else
258  attributes[ fieldIndex ] = QVariant();
259  f.setAttributes( attributes );
260  sink->addFeature( f, QgsFeatureSink::FastInsert );
261  }
262  else
263  {
264  if ( nearest.count() > 1 )
265  {
266  feedback->pushInfo( QObject::tr( "Multiple matching features found at same distance from search feature, found %1 features" ).arg( nearest.count() ) );
267  }
268 
269  const QgsGeometry joinLine = f.geometry().shortestLine( index.geometry( nearest.at( 0 ) ) );
270  if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( joinLine.constGet() ) )
271  {
272  if ( !mIsInPlace )
273  attributes.append( line->startPoint().azimuth( line->endPoint() ) );
274  else
275  attributes[ fieldIndex ] = line->startPoint().azimuth( line->endPoint() );
276  }
277  else
278  {
279  if ( !mIsInPlace )
280  attributes.append( QVariant() );
281  else
282  attributes[ fieldIndex ] = QVariant();
283  }
284  f.setAttributes( attributes );
285  sink->addFeature( f, QgsFeatureSink::FastInsert );
286  }
287  }
288  }
289 
290  const bool applySymbology = parameterAsBool( parameters, QStringLiteral( "APPLY_SYMBOLOGY" ), context );
291  if ( applySymbology )
292  {
293  if ( mIsInPlace )
294  {
295  // get in place vector layer
296  // (possibly TODO - make this a reusable method!)
297  QVariantMap inPlaceParams = parameters;
298  inPlaceParams.insert( QStringLiteral( "INPUT" ), parameters.value( QStringLiteral( "INPUT" ) ).value< QgsProcessingFeatureSourceDefinition >().source );
299  if ( QgsVectorLayer *sourceLayer = parameterAsVectorLayer( inPlaceParams, QStringLiteral( "INPUT" ), context ) )
300  {
301  std::unique_ptr< QgsFeatureRenderer > sourceRenderer( sourceLayer->renderer()->clone() );
302  SetMarkerRotationPostProcessor processor( std::move( sourceRenderer ), fieldName );
303  processor.postProcessLayer( sourceLayer, context, feedback );
304  }
305  }
306  else if ( mSourceRenderer && context.willLoadLayerOnCompletion( dest ) )
307  {
308  context.layerToLoadOnCompletionDetails( dest ).setPostProcessor( new SetMarkerRotationPostProcessor( std::move( mSourceRenderer ), fieldName ) );
309  }
310  }
311 
312  QVariantMap outputs;
313  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
314  return outputs;
315 }
316 
317 
QgsFeedback::setProgress
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:62
QgsProperty::fromField
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
Definition: qgsproperty.cpp:220
QgsProcessingContext::LayerDetails::setPostProcessor
void setPostProcessor(QgsProcessingLayerPostProcessorInterface *processor)
Sets the layer post-processor.
Definition: qgsprocessingcontext.cpp:166
QgsProcessingContext::willLoadLayerOnCompletion
bool willLoadLayerOnCompletion(const QString &layer) const
Returns true if the given layer (by ID or datasource) will be loaded into the current project upon co...
Definition: qgsprocessingcontext.h:329
qgslinestring.h
QgsProcessingFeedback
Base class for providing feedback from a processing algorithm.
Definition: qgsprocessingfeedback.h:38
QgsStyleSymbolEntity
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1201
QgsProcessingFeedback::pushInfo
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
Definition: qgsprocessingfeedback.cpp:48
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsStyleEntityVisitorInterface
An interface for classes which can visit style entity (e.g.
Definition: qgsstyleentityvisitor.h:34
QgsProcessingParameterFeatureSource
An input feature source (such as vector layers) parameter for processing algorithms.
Definition: qgsprocessingparameters.h:2734
QgsFields::append
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
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
QgsProcessing::TypeVectorPoint
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:48
QgsStyleEntityVisitorInterface::StyleLeaf
Contains information relating to the style entity currently being visited.
Definition: qgsstyleentityvisitor.h:61
QgsProcessingParameterFeatureSink
A feature sink output for processing algorithms.
Definition: qgsprocessingparameters.h:2895
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:76
QgsProcessingContext
Contains information about the context in which a processing algorithm is executed.
Definition: qgsprocessingcontext.h:44
QgsMarkerSymbol
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:931
QgsProcessingParameterString
A string parameter for processing algorithms.
Definition: qgsprocessingparameters.h:2324
QgsSpatialIndex
A spatial index for QgsFeature objects.
Definition: qgsspatialindex.h:68
QgsProcessingContext::transformContext
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Definition: qgsprocessingcontext.h:149
QgsProcessingLayerPostProcessorInterface
An interface for layer post-processing handlers for execution following a processing algorithm operat...
Definition: qgsprocessingcontext.h:660
qgsrenderer.h
QgsFeatureRequest::setNoAttributes
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
Definition: qgsfeaturerequest.cpp:192
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsProcessingParameterDistance
A double numeric parameter for distance values.
Definition: qgsprocessingparameters.h:2063
QgsStyleEntityVisitorInterface::StyleLeaf::entity
const QgsStyleEntityInterface * entity
Reference to style entity being visited.
Definition: qgsstyleentityvisitor.h:103
qgsvectorlayer.h
QgsSpatialIndex::FlagStoreFeatureGeometries
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
Definition: qgsspatialindex.h:77
QgsFeedback::isCanceled
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
qgsprocessingoutputs.h
QgsProcessingParameterBoolean
A boolean parameter for processing algorithms.
Definition: qgsprocessingparameters.h:1507
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:142
QgsGeometry::shortestLine
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Definition: qgsgeometry.cpp:625
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:374
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsProcessingContext::layerToLoadOnCompletionDetails
QgsProcessingContext::LayerDetails & layerToLoadOnCompletionDetails(const QString &layer)
Returns a reference to the details for a given layer which is loaded on completion of the algorithm o...
Definition: qgsprocessingcontext.h:366
QgsProcessingLayerPostProcessorInterface::postProcessLayer
virtual void postProcessLayer(QgsMapLayer *layer, QgsProcessingContext &context, QgsProcessingFeedback *feedback)=0
Post-processes the specified layer, following successful execution of a processing algorithm.
QgsStyleEntityVisitorInterface::visit
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
Definition: qgsstyleentityvisitor.h:153
QgsAttributes
A vector of attributes.
Definition: qgsattributes.h:58
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
qgsalgorithmangletonearest.h
QgsProcessingAlgorithm::flags
virtual Flags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Definition: qgsprocessingalgorithm.cpp:88
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:265
QgsProcessingException
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
QgsProcessingParameterField
A vector layer or feature source field parameter for processing algorithms.
Definition: qgsprocessingparameters.h:2617
QgsProcessingAlgorithm::FlagSupportsInPlaceEdits
@ FlagSupportsInPlaceEdits
Algorithm supports in-place editing.
Definition: qgsprocessingalgorithm.h:77
qgsstyleentityvisitor.h
QgsFeatureSink::FastInsert
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
Definition: qgsfeaturesink.h:70
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50