QGIS API Documentation  3.17.0-Master (df2c9ff931)
qgsalgorithmsnapgeometries.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmsnapgeometries.cpp
3  ---------------------
4  begin : May 2020
5  copyright : (C) 2020 by Alexander Bruy
6  email : alexander dot bruy 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 
20 #include "qgsvectorlayer.h"
23 
25 
26 QString QgsSnapGeometriesAlgorithm::name() const
27 {
28  return QStringLiteral( "snapgeometries" );
29 }
30 
31 QString QgsSnapGeometriesAlgorithm::displayName() const
32 {
33  return QObject::tr( "Snap geometries to layer" );
34 }
35 
36 QString QgsSnapGeometriesAlgorithm::shortHelpString() const
37 {
38  return QObject::tr( "Snaps the geometries in a layer. Snapping can be done either to the geometries "
39  "from another layer, or to geometries within the same layer." )
40  + QStringLiteral( "\n\n" )
41  + QObject::tr( "A tolerance is specified in layer units to control how close vertices need "
42  "to be to the reference layer geometries before they are snapped." )
43  + QStringLiteral( "\n\n" )
44  + QObject::tr( "Snapping occurs to both nodes and edges. Depending on the snapping behavior, "
45  "either nodes or edges will be preferred." )
46  + QStringLiteral( "\n\n" )
47  + QObject::tr( "Vertices will be inserted or removed as required to make the geometries match "
48  "the reference geometries." );
49 }
50 
51 QStringList QgsSnapGeometriesAlgorithm::tags() const
52 {
53  return QObject::tr( "geometry,snap,tolerance" ).split( ',' );
54 }
55 
56 QString QgsSnapGeometriesAlgorithm::group() const
57 {
58  return QObject::tr( "Vector geometry" );
59 }
60 
61 QString QgsSnapGeometriesAlgorithm::groupId() const
62 {
63  return QStringLiteral( "vectorgeometry" );
64 }
65 
66 QgsProcessingAlgorithm::Flags QgsSnapGeometriesAlgorithm::flags() const
67 {
70  return f;
71 }
72 
73 bool QgsSnapGeometriesAlgorithm::supportInPlaceEdit( const QgsMapLayer *l ) const
74 {
75  const QgsVectorLayer *layer = qobject_cast< const QgsVectorLayer * >( l );
76  if ( !layer )
77  return false;
78 
79  return layer->isSpatial();
80 }
81 
82 void QgsSnapGeometriesAlgorithm::initAlgorithm( const QVariantMap & )
83 {
84  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
86  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "REFERENCE_LAYER" ), QObject::tr( "Reference layer" ),
88  addParameter( new QgsProcessingParameterDistance( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ),
89  10.0, QStringLiteral( "INPUT" ), false, 0.00000001 ) );
90 
91  QStringList options = QStringList()
92  << QObject::tr( "Prefer aligning nodes, insert extra vertices where required" )
93  << QObject::tr( "Prefer closest point, insert extra vertices where required" )
94  << QObject::tr( "Prefer aligning nodes, don't insert new vertices" )
95  << QObject::tr( "Prefer closest point, don't insert new vertices" )
96  << QObject::tr( "Move end points only, prefer aligning nodes" )
97  << QObject::tr( "Move end points only, prefer closest point" )
98  << QObject::tr( "Snap end points to end points only" )
99  << QObject::tr( "Snap to anchor nodes (single layer only)" );
100  addParameter( new QgsProcessingParameterEnum( QStringLiteral( "BEHAVIOR" ), QObject::tr( "Behavior" ), options, false, QVariantList() << 0 ) );
101  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ),
102  QObject::tr( "Snapped geometry" ), QgsProcessing::TypeVectorPolygon ) );
103 }
104 
105 QgsSnapGeometriesAlgorithm *QgsSnapGeometriesAlgorithm::createInstance() const
106 {
107  return new QgsSnapGeometriesAlgorithm();
108 }
109 
110 QVariantMap QgsSnapGeometriesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
111 {
112  std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
113  if ( !source )
114  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
115 
116  std::unique_ptr< QgsProcessingFeatureSource > referenceSource( parameterAsSource( parameters, QStringLiteral( "REFERENCE_LAYER" ), context ) );
117  if ( !referenceSource )
118  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "REFERENCE_LAYER" ) ) );
119 
120  double tolerance = parameterAsDouble( parameters, QStringLiteral( "TOLERANCE" ), context );
121  QgsGeometrySnapper::SnapMode mode = static_cast<QgsGeometrySnapper::SnapMode>( parameterAsEnum( parameters, QStringLiteral( "BEHAVIOR" ), context ) );
122 
123  QString dest;
124  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), source->wkbType(), source->sourceCrs() ) );
125  if ( !sink )
126  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
127 
128  double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
129  QgsFeatureIterator features = source->getFeatures();
130 
131  if ( parameters.value( QStringLiteral( "INPUT" ) ) != parameters.value( QStringLiteral( "REFERENCE_LAYER" ) ) )
132  {
133  if ( mode == 7 )
134  throw QgsProcessingException( QObject::tr( "This mode applies when the input and reference layer are the same." ) );
135 
136  QgsGeometrySnapper snapper( referenceSource.get() );
137  long long processed = 0;
138  QgsFeature f;
139  while ( features.nextFeature( f ) )
140  {
141  if ( feedback->isCanceled() )
142  break;
143 
144  if ( f.hasGeometry() )
145  {
146  QgsFeature outFeature( f );
147  outFeature.setGeometry( snapper.snapGeometry( f.geometry(), tolerance, mode ) );
148  sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
149  }
150  else
151  {
152  sink->addFeature( f );
153  }
154  processed += 1;
155  feedback->setProgress( processed * step );
156  }
157  }
158  else if ( mode == 7 )
159  {
160  // input layer == reference layer
161  int modified = QgsGeometrySnapperSingleSource::run( *source, *sink, tolerance, feedback );
162  feedback->pushInfo( QObject::tr( "Snapped %1 geometries." ).arg( modified ) );
163  }
164  else
165  {
166  // snapping internally
167  QgsInternalGeometrySnapper snapper( tolerance, mode );
168  long long processed = 0;
169  QgsFeature f;
170  while ( features.nextFeature( f ) )
171  {
172  if ( feedback->isCanceled() )
173  break;
174 
175  if ( f.hasGeometry() )
176  {
177  QgsFeature outFeature( f );
178  outFeature.setGeometry( snapper.snapFeature( f ) );
179  sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
180  }
181  else
182  {
183  sink->addFeature( f );
184  }
185  processed += 1;
186  feedback->setProgress( processed * step );
187  }
188  }
189 
190  QVariantMap outputs;
191  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
192  return outputs;
193 }
194 
Wrapper for iterator of features from vector data provider or vector layer.
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
Base class for all map layer types.
Definition: qgsmaplayer.h:84
Base class for providing feedback from a processing algorithm.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:62
virtual Flags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:204
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
A feature sink output for processing algorithms.
An enum based parameter for processing algorithms, allowing for selection from predefined values...
QgsGeometrySnapper allows a geometry to be snapped to the geometries within a different reference lay...
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
A double numeric parameter for distance values.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
Vector polygon layers.
Definition: qgsprocessing.h:50
static int run(const QgsFeatureSource &source, QgsFeatureSink &sink, double thresh, QgsFeedback *feedback)
Run the algorithm on given source and output results to the sink, using threshold value in the source...
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
Vector point layers.
Definition: qgsprocessing.h:48
SnapMode
Snapping modes.
An input feature source (such as vector layers) parameter for processing algorithms.
Vector line layers.
Definition: qgsprocessing.h:49
QgsGeometry geometry
Definition: qgsfeature.h:67
bool nextFeature(QgsFeature &f)
Represents a vector layer which manages a vector based data sets.
Contains information about the context in which a processing algorithm is executed.
QgsInternalGeometrySnapper allows a set of geometries to be snapped to each other.