Branch data Line data Source code
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 : :
18 : : #include "qgsalgorithmsnapgeometries.h"
19 : :
20 : : #include "qgsvectorlayer.h"
21 : : #include "vector/qgsgeometrysnapper.h"
22 : : #include "vector/qgsgeometrysnappersinglesource.h"
23 : :
24 : : ///@cond PRIVATE
25 : :
26 : 0 : QString QgsSnapGeometriesAlgorithm::name() const
27 : : {
28 : 0 : return QStringLiteral( "snapgeometries" );
29 : : }
30 : :
31 : 0 : QString QgsSnapGeometriesAlgorithm::displayName() const
32 : : {
33 : 0 : return QObject::tr( "Snap geometries to layer" );
34 : : }
35 : :
36 : 0 : QString QgsSnapGeometriesAlgorithm::shortHelpString() const
37 : : {
38 : 0 : 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 : 0 : + QStringLiteral( "\n\n" )
41 : 0 : + 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 : 0 : + QStringLiteral( "\n\n" )
44 : 0 : + QObject::tr( "Snapping occurs to both nodes and edges. Depending on the snapping behavior, "
45 : : "either nodes or edges will be preferred." )
46 : 0 : + QStringLiteral( "\n\n" )
47 : 0 : + QObject::tr( "Vertices will be inserted or removed as required to make the geometries match "
48 : : "the reference geometries." );
49 : 0 : }
50 : :
51 : 0 : QStringList QgsSnapGeometriesAlgorithm::tags() const
52 : : {
53 : 0 : return QObject::tr( "geometry,snap,tolerance" ).split( ',' );
54 : 0 : }
55 : :
56 : 0 : QString QgsSnapGeometriesAlgorithm::group() const
57 : : {
58 : 0 : return QObject::tr( "Vector geometry" );
59 : : }
60 : :
61 : 0 : QString QgsSnapGeometriesAlgorithm::groupId() const
62 : : {
63 : 0 : return QStringLiteral( "vectorgeometry" );
64 : : }
65 : :
66 : 0 : QgsProcessingAlgorithm::Flags QgsSnapGeometriesAlgorithm::flags() const
67 : : {
68 : 0 : Flags f = QgsProcessingAlgorithm::flags();
69 : 0 : f |= QgsProcessingAlgorithm::FlagSupportsInPlaceEdits;
70 : 0 : return f;
71 : : }
72 : :
73 : 0 : bool QgsSnapGeometriesAlgorithm::supportInPlaceEdit( const QgsMapLayer *l ) const
74 : : {
75 : 0 : const QgsVectorLayer *layer = qobject_cast< const QgsVectorLayer * >( l );
76 : 0 : if ( !layer )
77 : 0 : return false;
78 : :
79 : 0 : return layer->isSpatial();
80 : 0 : }
81 : :
82 : 0 : void QgsSnapGeometriesAlgorithm::initAlgorithm( const QVariantMap & )
83 : : {
84 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
85 : 0 : QList< int >() << QgsProcessing::TypeVectorPoint << QgsProcessing::TypeVectorLine << QgsProcessing::TypeVectorPolygon ) );
86 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "REFERENCE_LAYER" ), QObject::tr( "Reference layer" ),
87 : 0 : QList< int >() << QgsProcessing::TypeVectorPoint << QgsProcessing::TypeVectorLine << QgsProcessing::TypeVectorPolygon ) );
88 : 0 : addParameter( new QgsProcessingParameterDistance( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ),
89 : 0 : 10.0, QStringLiteral( "INPUT" ), false, 0.00000001 ) );
90 : :
91 : 0 : QStringList options = QStringList()
92 : 0 : << QObject::tr( "Prefer aligning nodes, insert extra vertices where required" )
93 : 0 : << QObject::tr( "Prefer closest point, insert extra vertices where required" )
94 : 0 : << QObject::tr( "Prefer aligning nodes, don't insert new vertices" )
95 : 0 : << QObject::tr( "Prefer closest point, don't insert new vertices" )
96 : 0 : << QObject::tr( "Move end points only, prefer aligning nodes" )
97 : 0 : << QObject::tr( "Move end points only, prefer closest point" )
98 : 0 : << QObject::tr( "Snap end points to end points only" )
99 : 0 : << QObject::tr( "Snap to anchor nodes (single layer only)" );
100 : 0 : addParameter( new QgsProcessingParameterEnum( QStringLiteral( "BEHAVIOR" ), QObject::tr( "Behavior" ), options, false, QVariantList() << 0 ) );
101 : 0 : addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ),
102 : 0 : QObject::tr( "Snapped geometry" ), QgsProcessing::TypeVectorAnyGeometry ) );
103 : 0 : }
104 : :
105 : 0 : QgsSnapGeometriesAlgorithm *QgsSnapGeometriesAlgorithm::createInstance() const
106 : : {
107 : 0 : return new QgsSnapGeometriesAlgorithm();
108 : : }
109 : :
110 : 0 : QVariantMap QgsSnapGeometriesAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
111 : : {
112 : 0 : std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
113 : 0 : if ( !source )
114 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
115 : :
116 : 0 : std::unique_ptr< QgsProcessingFeatureSource > referenceSource( parameterAsSource( parameters, QStringLiteral( "REFERENCE_LAYER" ), context ) );
117 : 0 : if ( !referenceSource )
118 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "REFERENCE_LAYER" ) ) );
119 : :
120 : 0 : double tolerance = parameterAsDouble( parameters, QStringLiteral( "TOLERANCE" ), context );
121 : 0 : QgsGeometrySnapper::SnapMode mode = static_cast<QgsGeometrySnapper::SnapMode>( parameterAsEnum( parameters, QStringLiteral( "BEHAVIOR" ), context ) );
122 : :
123 : 0 : QString dest;
124 : 0 : std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), source->wkbType(), source->sourceCrs() ) );
125 : 0 : if ( !sink )
126 : 0 : throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
127 : :
128 : 0 : double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
129 : 0 : QgsFeatureIterator features = source->getFeatures();
130 : :
131 : 0 : if ( parameters.value( QStringLiteral( "INPUT" ) ) != parameters.value( QStringLiteral( "REFERENCE_LAYER" ) ) )
132 : : {
133 : 0 : if ( mode == 7 )
134 : 0 : throw QgsProcessingException( QObject::tr( "This mode applies when the input and reference layer are the same." ) );
135 : :
136 : 0 : QgsGeometrySnapper snapper( referenceSource.get() );
137 : 0 : long long processed = 0;
138 : 0 : QgsFeature f;
139 : 0 : while ( features.nextFeature( f ) )
140 : : {
141 : 0 : if ( feedback->isCanceled() )
142 : 0 : break;
143 : :
144 : 0 : if ( f.hasGeometry() )
145 : : {
146 : 0 : QgsFeature outFeature( f );
147 : 0 : outFeature.setGeometry( snapper.snapGeometry( f.geometry(), tolerance, mode ) );
148 : 0 : sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
149 : 0 : }
150 : : else
151 : : {
152 : 0 : sink->addFeature( f );
153 : : }
154 : 0 : processed += 1;
155 : 0 : feedback->setProgress( processed * step );
156 : : }
157 : 0 : }
158 : 0 : else if ( mode == 7 )
159 : : {
160 : : // input layer == reference layer
161 : 0 : int modified = QgsGeometrySnapperSingleSource::run( *source, *sink, tolerance, feedback );
162 : 0 : feedback->pushInfo( QObject::tr( "Snapped %1 geometries." ).arg( modified ) );
163 : 0 : }
164 : : else
165 : : {
166 : : // snapping internally
167 : 0 : QgsInternalGeometrySnapper snapper( tolerance, mode );
168 : 0 : long long processed = 0;
169 : 0 : QgsFeature f;
170 : 0 : while ( features.nextFeature( f ) )
171 : : {
172 : 0 : if ( feedback->isCanceled() )
173 : 0 : break;
174 : :
175 : 0 : if ( f.hasGeometry() )
176 : : {
177 : 0 : QgsFeature outFeature( f );
178 : 0 : outFeature.setGeometry( snapper.snapFeature( f ) );
179 : 0 : sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
180 : 0 : }
181 : : else
182 : : {
183 : 0 : sink->addFeature( f );
184 : : }
185 : 0 : processed += 1;
186 : 0 : feedback->setProgress( processed * step );
187 : : }
188 : 0 : }
189 : :
190 : 0 : QVariantMap outputs;
191 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), dest );
192 : 0 : return outputs;
193 : 0 : }
194 : :
195 : : ///@endcond
|