QGIS API Documentation 3.99.0-Master (357b655ed83)
Loading...
Searching...
No Matches
qgsalgorithmshortestline.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmshortestline.cpp
3 ---------------------
4 begin : September 2021
5 copyright : (C) 2020 by Matteo Ghetta, 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:This feature was originally developed in Python by: Matteo Ghetta, August 2021
19
21
22#include "qgsdistancearea.h"
23#include "qgsspatialindex.h"
24
25#include <QString>
26
27using namespace Qt::StringLiterals;
28
30
31QString QgsShortestLineAlgorithm::name() const
32{
33 return u"shortestline"_s;
34}
35
36QString QgsShortestLineAlgorithm::displayName() const
37{
38 return QObject::tr( "Shortest line between features" );
39}
40
41QStringList QgsShortestLineAlgorithm::tags() const
42{
43 return QObject::tr( "distance,shortest,minimum,nearest,closest,proximity" ).split( ',' );
44}
45
46QString QgsShortestLineAlgorithm::group() const
47{
48 return QObject::tr( "Vector analysis" );
49}
50
51QString QgsShortestLineAlgorithm::groupId() const
52{
53 return u"vectoranalysis"_s;
54}
55
56QString QgsShortestLineAlgorithm::shortDescription() const
57{
58 return QObject::tr( "Calculates the shortest lines between features in source and destination layers." );
59}
60
61QString QgsShortestLineAlgorithm::shortHelpString() const
62{
63 return QObject::tr( "This algorithm creates a line layer as the "
64 "shortest line between the source and the destination layer. "
65 "By default only the first nearest feature of the "
66 "destination layer is taken into account. "
67 "The n-nearest neighboring features number can be specified.\n\n"
68 "If a maximum distance is specified, then only "
69 "features which are closer than this distance will "
70 "be considered.\n\nThe output features will contain all the "
71 "source layer attributes, all the attributes from the n-nearest "
72 "feature and the additional field of the distance.\n\n"
73 "This algorithm uses purely Cartesian calculations for distance, "
74 "and does not consider geodetic or ellipsoid properties when "
75 "determining feature proximity. The measurement and output coordinate "
76 "system is based on the coordinate system of the source layer."
77 );
78}
79
80QgsShortestLineAlgorithm *QgsShortestLineAlgorithm::createInstance() const
81{
82 return new QgsShortestLineAlgorithm();
83}
84
85void QgsShortestLineAlgorithm::initAlgorithm( const QVariantMap & )
86{
87 addParameter( new QgsProcessingParameterFeatureSource( u"SOURCE"_s, QObject::tr( "Source layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) );
88 addParameter( new QgsProcessingParameterFeatureSource( u"DESTINATION"_s, QObject::tr( "Destination layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) );
89 addParameter( new QgsProcessingParameterEnum( u"METHOD"_s, QObject::tr( "Method" ), QStringList() << "Distance to Nearest Point on feature" << "Distance to Feature Centroid", false, 0 ) );
90 addParameter( new QgsProcessingParameterNumber( u"NEIGHBORS"_s, QObject::tr( "Maximum number of neighbors" ), Qgis::ProcessingNumberParameterType::Integer, 1, false, 1 ) );
91 addParameter( new QgsProcessingParameterDistance( u"DISTANCE"_s, QObject::tr( "Maximum distance" ), QVariant(), QString( "SOURCE" ), true ) );
92 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Shortest lines" ), Qgis::ProcessingSourceType::VectorLine ) );
93}
94
95bool QgsShortestLineAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
96{
97 mSource.reset( parameterAsSource( parameters, u"SOURCE"_s, context ) );
98 if ( !mSource )
99 throw QgsProcessingException( invalidSourceError( parameters, u"SOURCE"_s ) );
100
101 mDestination.reset( parameterAsSource( parameters, u"DESTINATION"_s, context ) );
102 if ( !mDestination )
103 throw QgsProcessingException( invalidSourceError( parameters, u"DESTINATION"_s ) );
104
105 mMethod = parameterAsInt( parameters, u"METHOD"_s, context );
106
107 mKNeighbors = parameterAsInt( parameters, u"NEIGHBORS"_s, context );
108
109 mMaxDistance = parameterAsDouble( parameters, u"DISTANCE"_s, context ); //defaults to zero if not set
110
111 return true;
112}
113
114QVariantMap QgsShortestLineAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
115{
116 if ( mKNeighbors > mDestination->featureCount() )
117 mKNeighbors = mDestination->featureCount();
118
119 QgsFields fields = QgsProcessingUtils::combineFields( mSource->fields(), mDestination->fields() );
120
121 QgsFields newFields;
122 newFields.append( QgsField( u"distance"_s, QMetaType::Type::Double ) );
123 fields = QgsProcessingUtils::combineFields( fields, newFields );
124
125 QString dest;
126 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, dest, fields, Qgis::WkbType::MultiLineString, mSource->sourceCrs() ) );
127 if ( !sink )
128 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
129
130 const QgsFeatureIterator destinationIterator = mDestination->getFeatures( QgsFeatureRequest().setDestinationCrs( mSource->sourceCrs(), context.transformContext() ) );
131 QHash<QgsFeatureId, QgsAttributes> destinationAttributeCache;
132 double step = mDestination->featureCount() > 0 ? 50.0 / mDestination->featureCount() : 1;
133 int i = 0;
134 const QgsSpatialIndex idx( destinationIterator, [&]( const QgsFeature &f ) -> bool {
135 i++;
136 if ( feedback-> isCanceled() )
137 return false;
138
139 feedback->setProgress( i * step );
140
141 destinationAttributeCache.insert( f.id(), f.attributes() );
142
144
145 step = mSource->featureCount() > 0 ? 50.0 / mSource->featureCount() : 1;
146 QgsFeatureIterator sourceIterator = mSource->getFeatures();
147
149 da.setSourceCrs( mSource->sourceCrs(), context.transformContext() );
150
151 QgsFeature sourceFeature;
152 while ( sourceIterator.nextFeature( sourceFeature ) )
153 {
154 if ( feedback->isCanceled() )
155 break;
156
157 const QgsGeometry sourceGeom = sourceFeature.geometry();
158 QgsFeatureIds nearestIds = qgis::listToSet( idx.nearestNeighbor( sourceGeom, mKNeighbors, mMaxDistance ) );
159
160 for ( const QgsFeatureId id : nearestIds )
161 {
162 QgsGeometry destinationGeom = idx.geometry( id );
163 if ( mMethod == 1 )
164 {
165 destinationGeom = idx.geometry( id ).centroid();
166 }
167
168 const QgsGeometry shortestLine = sourceGeom.shortestLine( destinationGeom );
169 double dist = 0;
170 try
171 {
172 dist = da.measureLength( shortestLine );
173 }
174 catch ( QgsCsException & )
175 {
176 throw QgsProcessingException( QObject::tr( "An error occurred while calculating shortest line length" ) );
177 }
178
179 QgsFeature f;
180 QgsAttributes attrs = sourceFeature.attributes();
181 attrs << destinationAttributeCache.value( id ) << dist;
182
183 f.setAttributes( attrs );
184 f.setGeometry( shortestLine );
185 if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
186 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
187 }
188
189 i++;
190 feedback->setProgress( i * step );
191 }
192
193 sink->finalize();
194
195 QVariantMap outputs;
196 outputs.insert( u"OUTPUT"_s, dest );
197 return outputs;
198}
199
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3604
@ VectorLine
Vector line layers.
Definition qgis.h:3606
@ MultiLineString
MultiLineString.
Definition qgis.h:287
A vector of attributes.
Custom exception class for Coordinate Reference System related exceptions.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsAttributes attributes
Definition qgsfeature.h:69
QgsFeatureId id
Definition qgsfeature.h:68
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:71
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:63
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:76
A geometry is the spatial representation of a feature.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
A double numeric parameter for distance values.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A numeric parameter for processing algorithms.
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features