25using namespace Qt::StringLiterals;
29QString QgsDistanceMatrixAlgorithm::name()
const
31 return u
"distancematrix"_s;
34QString QgsDistanceMatrixAlgorithm::displayName()
const
36 return QObject::tr(
"Distance matrix" );
39QStringList QgsDistanceMatrixAlgorithm::tags()
const
41 return QObject::tr(
"point,distance,matrix,nearest,closest,summary" ).split(
',' );
44QString QgsDistanceMatrixAlgorithm::group()
const
46 return QObject::tr(
"Vector analysis" );
49QString QgsDistanceMatrixAlgorithm::groupId()
const
51 return u
"vectoranalysis"_s;
54QString QgsDistanceMatrixAlgorithm::shortHelpString()
const
57 "This algorithm creates a table containing a distance matrix, with "
58 "distances between all the points in a points layer.\n\n"
59 "The algorithm supports both Cartesian and ellipsoidal distance calculations.\n\n"
60 "Cartesian calculations are performed using a spatial index and offer "
61 "high performance, but can give inaccurate results when used with CRSs "
62 "with high distance distortion or where shortest distances accounting for "
63 "great circles is required.\n\n"
64 "If ellipsoidal calculations are enabled, the algorithm bypasses the spatial "
65 "index and provides accurate geodetic measurements at a considerable performance cost."
69QString QgsDistanceMatrixAlgorithm::shortDescription()
const
71 return QObject::tr(
"Creates a table containing a matrix of distances between all the points in a points layer." );
79QgsDistanceMatrixAlgorithm *QgsDistanceMatrixAlgorithm::createInstance()
const
81 return new QgsDistanceMatrixAlgorithm();
84void QgsDistanceMatrixAlgorithm::initAlgorithm(
const QVariantMap & )
86 auto inputParam = std::make_unique<QgsProcessingParameterFeatureSource>( u
"INPUT"_s, QObject::tr(
"Input point layer" ), QList<int>() <<
static_cast<int>(
Qgis::ProcessingSourceType::VectorPoint ) );
87 inputParam->setHelp( QObject::tr(
"The layer containing the points from which distances will be calculated." ) );
88 addParameter( inputParam.release() );
89 auto inputFieldParam = std::make_unique<QgsProcessingParameterField>( u
"INPUT_FIELD"_s, QObject::tr(
"Input unique ID field" ), QVariant(), u
"INPUT"_s );
90 inputFieldParam->setHelp( QObject::tr(
"A field of the input layer with unique values to identify each starting point in the output table." ) );
91 addParameter( inputFieldParam.release() );
92 auto targetParam = std::make_unique<QgsProcessingParameterFeatureSource>( u
"TARGET"_s, QObject::tr(
"Target point layer" ), QList<int>() <<
static_cast<int>(
Qgis::ProcessingSourceType::VectorPoint ) );
93 targetParam->setHelp( QObject::tr(
"The layer containing the points to which distances will be measured. If the same as the input layer, distances between points in the same layer are calculated." ) );
94 addParameter( targetParam.release() );
95 auto targetFieldParam = std::make_unique<QgsProcessingParameterField>( u
"TARGET_FIELD"_s, QObject::tr(
"Target unique ID field" ), QVariant(), u
"TARGET"_s );
96 targetFieldParam->setHelp( QObject::tr(
"A field of the target layer with unique values to identify each destination point in the output table." ) );
97 addParameter( targetFieldParam.release() );
98 auto matrixTypeParam = std::make_unique<QgsProcessingParameterEnum>(
100 QObject::tr(
"Output matrix type" ),
101 QStringList() << QObject::tr(
"Linear (N*k x 3) distance matrix" ) << QObject::tr(
"Standard (N x T) distance matrix" ) << QObject::tr(
"Summary distance matrix (mean, std. dev., min, max)" ),
105 matrixTypeParam->setHelp(
107 "The format of the output table. Linear for a list of point pairs and their distance; "
108 "standard for one row per input point with columns for each target and distance; "
109 "summary for statistics (mean, min, max, etc.) for each input point."
112 addParameter( matrixTypeParam.release() );
115 pointsParam->setHelp( QObject::tr(
"Limit the calculation to a specific number of the closest target points. If set to 0, distances to all target points will be calculated." ) );
116 addParameter( pointsParam.release() );
117 auto ellipsoidParam = std::make_unique<QgsProcessingParameterBoolean>( u
"USE_ELLIPSOID"_s, QObject::tr(
"Use ellipsoidal calculations" ),
true );
118 ellipsoidParam->setHelp(
119 QObject::tr(
"If checked, the algorithm will bypass the spatial index and calculate ellipsoidal distances for all point combinations. This will reduce performance but provide accurate results." )
121 addParameter( ellipsoidParam.release() );
127 mSource.reset( parameterAsSource( parameters, u
"INPUT"_s, context ) );
135 throw QgsProcessingException( QObject::tr(
"Input point layer is a MultiPoint layer - convert to single points before using this algorithm." ) );
138 mTarget.reset( parameterAsSource( parameters, u
"TARGET"_s, context ) );
146 throw QgsProcessingException( QObject::tr(
"Target point layer is a MultiPoint layer - convert to single points before using this algorithm." ) );
149 mSourceField = parameterAsString( parameters, u
"INPUT_FIELD"_s, context );
150 mTargetField = parameterAsString( parameters, u
"TARGET_FIELD"_s, context );
152 const int sourceFieldIndex = mSource->fields().lookupField( mSourceField );
153 if ( sourceFieldIndex < 0 )
158 const int targetFieldIndex = mTarget->fields().lookupField( mTargetField );
159 if ( targetFieldIndex < 0 )
164 mMatrixType =
static_cast<MatrixType
>( parameterAsEnum( parameters, u
"MATRIX_TYPE"_s, context ) );
165 mSameLayer = ( parameters.value( u
"INPUT"_s ) == parameters.value( u
"TARGET"_s ) );
167 mKPoints = parameterAsInt( parameters, u
"NEAREST_POINTS"_s, context );
170 mKPoints =
static_cast<int>( mTarget->featureCount() );
173 mUseEllipsoid = parameterAsBool( parameters, u
"USE_ELLIPSOID"_s, context );
182 switch ( mMatrixType )
186 return linearMatrixEllipsoid( parameters, context, feedback );
189 return regularMatrixEllipsoid( parameters, context, feedback );
194 switch ( mMatrixType )
198 return linearMatrixCartesian( parameters, context, feedback );
201 return regularMatrixCartesian( parameters, context, feedback );
211 long long nPoints = mSameLayer ? mKPoints + 1 : mKPoints;
213 const int sourceIndex = mSource->fields().lookupField( mSourceField );
214 const int targetIndex = mTarget->fields().lookupField( mTargetField );
217 QgsField inputIdField( mSource->fields().at( sourceIndex ) );
218 inputIdField.setName( u
"InputID"_s );
219 fields.
append( inputIdField );
221 if ( mMatrixType == Linear )
223 QgsField targetIdField( mTarget->fields().at( targetIndex ) );
224 targetIdField.setName( u
"TargetID"_s );
225 fields.
append( targetIdField );
226 fields.
append(
QgsField( u
"Distance"_s, QMetaType::Type::Double ) );
231 fields.
append(
QgsField( u
"STDDEV"_s, QMetaType::Type::Double ) );
239 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u
"OUTPUT"_s, context, dest, fields, outputWkbType, mSource->sourceCrs() ) );
246 QHash<QgsFeatureId, QVariant> targetIdCache;
247 double step = mTarget->featureCount() > 0 ? 50.0 /
static_cast<double>( mTarget->featureCount() ) : 1;
248 long long current = 0;
257 targetIdCache.insert( f.
id(), f.
attribute( targetIndex ) );
258 feedback->
setProgress(
static_cast<double>( current ) * step );
270 QVector<double> distancesList;
271 distancesList.reserve( nPoints );
274 step = mSource->featureCount() > 0 ? 50.0 /
static_cast<double>( mSource->featureCount() ) : 0;
285 distancesList.clear();
287 const QString sourceId = sourceFeature.
attribute( sourceIndex ).toString();
288 const QList<QgsFeatureId> nearestIds = index.nearestNeighbor( sourcePoint, nPoints );
290 if ( mMatrixType == Linear )
299 if ( mSameLayer && sourceFeature.
id() == targetId )
304 QgsPointXY targetPoint = index.geometry( targetId ).asPoint();
305 distance = da.measureLine( sourcePoint, targetPoint );
326 if ( mSameLayer && sourceFeature.
id() == targetId )
331 QgsPointXY targetPoint = index.geometry( targetId ).asPoint();
332 distancesList << da.measureLine( sourcePoint, targetPoint );
337 if ( distancesList.isEmpty() )
344 double sumSquares = 0;
345 double minDistance = std::numeric_limits<double>::max();
346 double maxDistance = std::numeric_limits<double>::lowest();
348 for (
const double d : std::as_const( distancesList ) )
352 minDistance = std::min( minDistance, d );
353 maxDistance = std::max( maxDistance, d );
356 const long long n = distancesList.size();
357 const double mean = sum /
static_cast<double>( n );
358 const double variance = std::max( 0.0, ( sumSquares /
static_cast<double>( n ) ) - ( mean * mean ) );
359 const double stdDev = std::sqrt( variance );
371 feedback->
setProgress( 50.0 +
static_cast<double>( current ) * step );
379 outputs.insert( u
"OUTPUT"_s, dest );
387 long long nPoints = mSameLayer ? mKPoints == mTarget->featureCount() ? mKPoints : mKPoints + 1 : mKPoints;
389 const int sourceIndex = mSource->fields().lookupField( mSourceField );
390 const int targetIndex = mTarget->fields().lookupField( mTargetField );
393 QgsField inputIdField( mSource->fields().at( sourceIndex ) );
394 inputIdField.setName( u
"InputID"_s );
395 fields.
append( inputIdField );
397 QMetaType::Type targetIdType = mTarget->fields().at( targetIndex ).type();
399 for (
long long i = 0; i < nPoints - 1; i++ )
401 fields.
append(
QgsField( u
"TargetID_%1"_s.arg( i + 1 ), targetIdType ) );
402 fields.
append(
QgsField( u
"Dist_%1"_s.arg( i + 1 ), QMetaType::Type::Double ) );
406 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u
"OUTPUT"_s, context, dest, fields, mSource->wkbType(), mSource->sourceCrs() ) );
413 QHash<QgsFeatureId, QVariant> targetIdCache;
414 double step = mTarget->featureCount() > 0 ? 50.0 /
static_cast<double>( mTarget->featureCount() ) : 1;
415 long long current = 0;
423 targetIdCache.insert( f.
id(), f.
attribute( targetIndex ) );
425 feedback->
setProgress(
static_cast<double>( current ) * step );
438 step = mSource->featureCount() > 0 ? 50.0 /
static_cast<double>( mSource->featureCount() ) : 0;
450 const QString sourceId = sourceFeature.
attribute( sourceIndex ).toString();
451 const QList<QgsFeatureId> nearestIds = index.nearestNeighbor( sourcePoint, nPoints );
454 attrs.reserve( 1 + nPoints * 2 );
463 if ( mSameLayer && sourceFeature.
id() == targetId )
468 QgsPointXY targetPoint = index.geometry( targetId ).asPoint();
469 distance = da.measureLine( sourcePoint, targetPoint );
470 attrs << targetIdCache.value( targetId ) << distance;
482 feedback->
setProgress( 50.0 +
static_cast<double>( current ) * step );
490 outputs.insert( u
"OUTPUT"_s, dest );
497 long long nPoints = mSameLayer ? mKPoints + 1 : mKPoints;
499 const int sourceIndex = mSource->fields().lookupField( mSourceField );
500 const int targetIndex = mTarget->fields().lookupField( mTargetField );
503 QgsField inputIdField( mSource->fields().at( sourceIndex ) );
504 inputIdField.setName( u
"InputID"_s );
505 fields.
append( inputIdField );
507 if ( mMatrixType == Linear )
509 QgsField targetIdField( mTarget->fields().at( targetIndex ) );
510 targetIdField.setName( u
"TargetID"_s );
511 fields.
append( targetIdField );
512 fields.
append(
QgsField( u
"Distance"_s, QMetaType::Type::Double ) );
517 fields.
append(
QgsField( u
"STDDEV"_s, QMetaType::Type::Double ) );
525 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u
"OUTPUT"_s, context, dest, fields, outputWkbType, mSource->sourceCrs() ) );
531 QHash<QgsFeatureId, QVariant> targetIdCache;
532 std::vector<std::pair<QgsFeatureId, QgsPointXY>> targetPointsCache;
533 targetPointsCache.reserve( mTarget->featureCount() );
535 double step = mTarget->featureCount() > 0 ? 50.0 /
static_cast<double>( mTarget->featureCount() ) : 1;
536 long long current = 0;
545 targetIdCache.insert( f.
id(), f.
attribute( targetIndex ) );
547 feedback->
setProgress(
static_cast<double>( current ) * step );
555 QVector<double> distancesList;
556 distancesList.reserve( nPoints );
559 step = mSource->featureCount() > 0 ? 50.0 /
static_cast<double>( mSource->featureCount() ) : 0;
570 distancesList.clear();
572 const QString sourceId = sourceFeature.
attribute( sourceIndex ).toString();
573 const std::vector<QgsDistanceUtils::NeighborResult> nearestPoints = QgsDistanceUtils::nearestNeighbors( sourcePoint, targetPointsCache, da, nPoints, feedback );
575 if ( mMatrixType == Linear )
577 for (
const auto &targetPoint : nearestPoints )
584 if ( mSameLayer && sourceFeature.
id() == targetPoint.featureId )
601 for (
const auto &targetPoint : nearestPoints )
608 if ( mSameLayer && sourceFeature.
id() == targetPoint.featureId )
613 distancesList << targetPoint.
distance;
618 if ( distancesList.isEmpty() )
625 double sumSquares = 0;
626 double minDistance = std::numeric_limits<double>::max();
627 double maxDistance = std::numeric_limits<double>::lowest();
629 for (
const double d : std::as_const( distancesList ) )
633 minDistance = std::min( minDistance, d );
634 maxDistance = std::max( maxDistance, d );
637 const long long n = distancesList.size();
638 const double mean = sum /
static_cast<double>( n );
639 const double variance = std::max( 0.0, ( sumSquares /
static_cast<double>( n ) ) - ( mean * mean ) );
640 const double stdDev = std::sqrt( variance );
652 feedback->
setProgress( 50.0 +
static_cast<double>( current ) * step );
660 outputs.insert( u
"OUTPUT"_s, dest );
668 long long nPoints = mSameLayer ? mKPoints == mTarget->featureCount() ? mKPoints : mKPoints + 1 : mKPoints;
670 const int sourceIndex = mSource->fields().lookupField( mSourceField );
671 const int targetIndex = mTarget->fields().lookupField( mTargetField );
674 QgsField inputIdField( mSource->fields().at( sourceIndex ) );
675 inputIdField.setName( u
"InputID"_s );
676 fields.
append( inputIdField );
678 QMetaType::Type targetIdType = mTarget->fields().at( targetIndex ).type();
680 for (
long long i = 0; i < nPoints - 1; i++ )
682 fields.
append(
QgsField( u
"TargetID_%1"_s.arg( i + 1 ), targetIdType ) );
683 fields.
append(
QgsField( u
"Dist_%1"_s.arg( i + 1 ), QMetaType::Type::Double ) );
687 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u
"OUTPUT"_s, context, dest, fields, mSource->wkbType(), mSource->sourceCrs() ) );
693 QHash<QgsFeatureId, QVariant> targetIdCache;
694 std::vector<std::pair<QgsFeatureId, QgsPointXY>> targetPointsCache;
695 targetPointsCache.reserve( mTarget->featureCount() );
697 double step = mTarget->featureCount() > 0 ? 50.0 /
static_cast<double>( mTarget->featureCount() ) : 1;
698 long long current = 0;
707 targetIdCache.insert( f.
id(), f.
attribute( targetIndex ) );
710 feedback->
setProgress(
static_cast<double>( current ) * step );
719 step = mSource->featureCount() > 0 ? 50.0 /
static_cast<double>( mSource->featureCount() ) : 0;
731 const QString sourceId = sourceFeature.
attribute( sourceIndex ).toString();
732 std::vector<QgsDistanceUtils::NeighborResult> nearestPoints = QgsDistanceUtils::nearestNeighbors( sourcePoint, targetPointsCache, da, nPoints, feedback );
735 attrs.reserve( 1 + nPoints * 2 );
737 for (
const auto &targetPoint : nearestPoints )
744 if ( mSameLayer && sourceFeature.
id() == targetPoint.featureId )
749 attrs << targetIdCache.value( targetPoint.featureId ) << targetPoint.
distance;
761 feedback->
setProgress( 50.0 +
static_cast<double>( current ) * step );
769 outputs.insert( u
"OUTPUT"_s, dest );
@ VectorPoint
Vector point layers.
@ RespectsEllipsoid
Algorithm respects the context's ellipsoid settings, and uses ellipsoidal based measurements.
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
WkbType
The WKB type describes the number of dimensions a geometry has.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
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).
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
@ 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...
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr)
Compute the unary union on a list of geometries.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QString ellipsoid() const
Returns the ellipsoid to use for distance and area calculations.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
void featureAddedToSink(const QString &output)
Reports that a feature was added to the the sink associated with the specified algorithm output.
void featureSinkFinalized(const QString &output)
Reports that a feature sink has been finalized.
A feature sink output for processing algorithms.
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features