QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsalgorithmjoinwithlines.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmjoinwithlines.cpp
3  ---------------------
4  begin : April 2017
5  copyright : (C) 2017 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 "qgslinestring.h"
20 
22 
23 QString QgsJoinWithLinesAlgorithm::name() const
24 {
25  return QStringLiteral( "hublines" );
26 }
27 
28 QString QgsJoinWithLinesAlgorithm::displayName() const
29 {
30  return QObject::tr( "Join by lines (hub lines)" );
31 }
32 
33 QStringList QgsJoinWithLinesAlgorithm::tags() const
34 {
35  return QObject::tr( "join,connect,lines,points,hub,spoke" ).split( ',' );
36 }
37 
38 QString QgsJoinWithLinesAlgorithm::group() const
39 {
40  return QObject::tr( "Vector analysis" );
41 }
42 
43 QString QgsJoinWithLinesAlgorithm::groupId() const
44 {
45  return QStringLiteral( "vectoranalysis" );
46 }
47 
48 void QgsJoinWithLinesAlgorithm::initAlgorithm( const QVariantMap & )
49 {
50  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "HUBS" ),
51  QObject::tr( "Hub layer" ) ) );
52  addParameter( new QgsProcessingParameterField( QStringLiteral( "HUB_FIELD" ),
53  QObject::tr( "Hub ID field" ), QVariant(), QStringLiteral( "HUBS" ) ) );
54 
55  addParameter( new QgsProcessingParameterField( QStringLiteral( "HUB_FIELDS" ),
56  QObject::tr( "Hub layer fields to copy (leave empty to copy all fields)" ),
57  QVariant(), QStringLiteral( "HUBS" ), QgsProcessingParameterField::Any,
58  true, true ) );
59 
60  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "SPOKES" ),
61  QObject::tr( "Spoke layer" ) ) );
62  addParameter( new QgsProcessingParameterField( QStringLiteral( "SPOKE_FIELD" ),
63  QObject::tr( "Spoke ID field" ), QVariant(), QStringLiteral( "SPOKES" ) ) );
64 
65  addParameter( new QgsProcessingParameterField( QStringLiteral( "SPOKE_FIELDS" ),
66  QObject::tr( "Spoke layer fields to copy (leave empty to copy all fields)" ),
67  QVariant(), QStringLiteral( "SPOKES" ), QgsProcessingParameterField::Any,
68  true, true ) );
69 
70  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Hub lines" ), QgsProcessing::TypeVectorLine ) );
71 }
72 
73 QString QgsJoinWithLinesAlgorithm::shortHelpString() const
74 {
75  return QObject::tr( "This algorithm creates hub and spoke diagrams by connecting lines from points on the Spoke layer to matching points in the Hub layer.\n\n"
76  "Determination of which hub goes with each point is based on a match between the Hub ID field on the hub points and the Spoke ID field on the spoke points.\n\n"
77  "If input layers are not point layers, a point on the surface of the geometries will be taken as the connecting location." );
78 }
79 
80 QgsJoinWithLinesAlgorithm *QgsJoinWithLinesAlgorithm::createInstance() const
81 {
82  return new QgsJoinWithLinesAlgorithm();
83 }
84 
85 QVariantMap QgsJoinWithLinesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
86 {
87  if ( parameters.value( QStringLiteral( "SPOKES" ) ) == parameters.value( QStringLiteral( "HUBS" ) ) )
88  throw QgsProcessingException( QObject::tr( "Same layer given for both hubs and spokes" ) );
89 
90  std::unique_ptr< QgsProcessingFeatureSource > hubSource( parameterAsSource( parameters, QStringLiteral( "HUBS" ), context ) );
91  if ( !hubSource )
92  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "HUBS" ) ) );
93 
94  std::unique_ptr< QgsProcessingFeatureSource > spokeSource( parameterAsSource( parameters, QStringLiteral( "SPOKES" ), context ) );
95  if ( !hubSource || !spokeSource )
96  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "SPOKES" ) ) );
97 
98  QString fieldHubName = parameterAsString( parameters, QStringLiteral( "HUB_FIELD" ), context );
99  int fieldHubIndex = hubSource->fields().lookupField( fieldHubName );
100  const QStringList hubFieldsToCopy = parameterAsFields( parameters, QStringLiteral( "HUB_FIELDS" ), context );
101 
102  QString fieldSpokeName = parameterAsString( parameters, QStringLiteral( "SPOKE_FIELD" ), context );
103  int fieldSpokeIndex = spokeSource->fields().lookupField( fieldSpokeName );
104  const QStringList spokeFieldsToCopy = parameterAsFields( parameters, QStringLiteral( "SPOKE_FIELDS" ), context );
105 
106  if ( fieldHubIndex < 0 || fieldSpokeIndex < 0 )
107  throw QgsProcessingException( QObject::tr( "Invalid ID field" ) );
108 
109  QgsFields hubOutFields;
110  QgsAttributeList hubFieldIndices;
111  if ( hubFieldsToCopy.empty() )
112  {
113  hubOutFields = hubSource->fields();
114  for ( int i = 0; i < hubOutFields.count(); ++i )
115  {
116  hubFieldIndices << i;
117  }
118  }
119  else
120  {
121  for ( const QString &field : hubFieldsToCopy )
122  {
123  int index = hubSource->fields().lookupField( field );
124  if ( index >= 0 )
125  {
126  hubFieldIndices << index;
127  hubOutFields.append( hubSource->fields().at( index ) );
128  }
129  }
130  }
131 
132  QgsAttributeList hubFields2Fetch = hubFieldIndices;
133  hubFields2Fetch << fieldHubIndex;
134 
135  QgsFields spokeOutFields;
136  QgsAttributeList spokeFieldIndices;
137  if ( spokeFieldsToCopy.empty() )
138  {
139  spokeOutFields = spokeSource->fields();
140  for ( int i = 0; i < spokeOutFields.count(); ++i )
141  {
142  spokeFieldIndices << i;
143  }
144  }
145  else
146  {
147  for ( const QString &field : spokeFieldsToCopy )
148  {
149  int index = spokeSource->fields().lookupField( field );
150  if ( index >= 0 )
151  {
152  spokeFieldIndices << index;
153  spokeOutFields.append( spokeSource->fields().at( index ) );
154  }
155  }
156  }
157 
158  QgsAttributeList spokeFields2Fetch = spokeFieldIndices;
159  spokeFields2Fetch << fieldSpokeIndex;
160 
161 
162  QgsFields fields = QgsProcessingUtils::combineFields( hubOutFields, spokeOutFields );
163 
165  bool hasZ = false;
166  if ( QgsWkbTypes::hasZ( hubSource->wkbType() ) || QgsWkbTypes::hasZ( spokeSource->wkbType() ) )
167  {
168  outType = QgsWkbTypes::addZ( outType );
169  hasZ = true;
170  }
171  bool hasM = false;
172  if ( QgsWkbTypes::hasM( hubSource->wkbType() ) || QgsWkbTypes::hasM( spokeSource->wkbType() ) )
173  {
174  outType = QgsWkbTypes::addM( outType );
175  hasM = true;
176  }
177 
178  QString dest;
179  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields,
180  outType, hubSource->sourceCrs() ) );
181  if ( !sink )
182  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
183 
184  auto getPointFromFeature = [hasZ, hasM]( const QgsFeature & feature )->QgsPoint
185  {
186  QgsPoint p;
187  if ( feature.geometry().type() == QgsWkbTypes::PointGeometry && !feature.geometry().isMultipart() )
188  p = *static_cast< const QgsPoint *>( feature.geometry().constGet() );
189  else
190  p = *static_cast< const QgsPoint *>( feature.geometry().pointOnSurface().constGet() );
191  if ( hasZ && !p.is3D() )
192  p.addZValue( 0 );
193  if ( hasM && !p.isMeasure() )
194  p.addMValue( 0 );
195  return p;
196  };
197 
198  QgsFeatureIterator hubFeatures = hubSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( hubFields2Fetch ), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
199  double step = hubSource->featureCount() > 0 ? 100.0 / hubSource->featureCount() : 1;
200  int i = 0;
201  QgsFeature hubFeature;
202  while ( hubFeatures.nextFeature( hubFeature ) )
203  {
204  i++;
205  if ( feedback->isCanceled() )
206  {
207  break;
208  }
209 
210  feedback->setProgress( i * step );
211 
212  if ( !hubFeature.hasGeometry() )
213  continue;
214 
215  QgsPoint hubPoint = getPointFromFeature( hubFeature );
216 
217  // only keep selected attributes
218  QgsAttributes hubAttributes;
219  for ( int j = 0; j < hubFeature.attributes().count(); ++j )
220  {
221  if ( !hubFieldIndices.contains( j ) )
222  continue;
223  hubAttributes << hubFeature.attribute( j );
224  }
225 
226  QgsFeatureRequest spokeRequest = QgsFeatureRequest().setDestinationCrs( hubSource->sourceCrs(), context.transformContext() );
227  spokeRequest.setSubsetOfAttributes( spokeFields2Fetch );
228  spokeRequest.setFilterExpression( QgsExpression::createFieldEqualityExpression( fieldSpokeName, hubFeature.attribute( fieldHubIndex ) ) );
229 
230  QgsFeatureIterator spokeFeatures = spokeSource->getFeatures( spokeRequest, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
231  QgsFeature spokeFeature;
232  while ( spokeFeatures.nextFeature( spokeFeature ) )
233  {
234  if ( feedback->isCanceled() )
235  {
236  break;
237  }
238  if ( !spokeFeature.hasGeometry() )
239  continue;
240 
241  QgsPoint spokePoint = getPointFromFeature( spokeFeature );
242  QgsGeometry line( new QgsLineString( QVector< QgsPoint >() << hubPoint << spokePoint ) );
243 
244  QgsFeature outFeature;
245  QgsAttributes outAttributes = hubAttributes;
246 
247  // only keep selected attributes
248  QgsAttributes spokeAttributes;
249  for ( int j = 0; j < spokeFeature.attributes().count(); ++j )
250  {
251  if ( !spokeFieldIndices.contains( j ) )
252  continue;
253  spokeAttributes << spokeFeature.attribute( j );
254  }
255 
256  outAttributes.append( spokeAttributes );
257  outFeature.setAttributes( outAttributes );
258  outFeature.setGeometry( line );
259  sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
260  }
261  }
262 
263  QVariantMap outputs;
264  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
265  return outputs;
266 }
267 
bool isMeasure() const
Returns true if the geometry contains m values.
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:299
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature&#39;s geometries.
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 providing feedback from a processing algorithm.
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
A vector layer or feature source field parameter for processing algorithms.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:469
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
int count() const
Returns number of items.
Definition: qgsfields.cpp:115
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:768
A feature sink output for processing algorithms.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:889
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB)
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:864
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfields.cpp:59
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
An input feature source (such as vector layers) parameter for processing algorithms.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
Vector line layers.
Definition: qgsprocessing.h:49
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:480
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:818
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
A vector of attributes.
Definition: qgsattributes.h:58
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
Contains information about the context in which a processing algorithm is executed.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
QgsAttributes attributes
Definition: qgsfeature.h:72