QGIS API Documentation  3.0.2-Girona (307d082)
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  std::unique_ptr< QgsProcessingFeatureSource > spokeSource( parameterAsSource( parameters, QStringLiteral( "SPOKES" ), context ) );
92  if ( !hubSource || !spokeSource )
93  throw QgsProcessingException( QObject::tr( "Could not load source layers" ) );
94 
95  QString fieldHubName = parameterAsString( parameters, QStringLiteral( "HUB_FIELD" ), context );
96  int fieldHubIndex = hubSource->fields().lookupField( fieldHubName );
97  const QStringList hubFieldsToCopy = parameterAsFields( parameters, QStringLiteral( "HUB_FIELDS" ), context );
98 
99  QString fieldSpokeName = parameterAsString( parameters, QStringLiteral( "SPOKE_FIELD" ), context );
100  int fieldSpokeIndex = spokeSource->fields().lookupField( fieldSpokeName );
101  const QStringList spokeFieldsToCopy = parameterAsFields( parameters, QStringLiteral( "SPOKE_FIELDS" ), context );
102 
103  if ( fieldHubIndex < 0 || fieldSpokeIndex < 0 )
104  throw QgsProcessingException( QObject::tr( "Invalid ID field" ) );
105 
106  QgsFields hubOutFields;
107  QgsAttributeList hubFieldIndices;
108  if ( hubFieldsToCopy.empty() )
109  {
110  hubOutFields = hubSource->fields();
111  for ( int i = 0; i < hubOutFields.count(); ++i )
112  {
113  hubFieldIndices << i;
114  }
115  }
116  else
117  {
118  for ( const QString &field : hubFieldsToCopy )
119  {
120  int index = hubSource->fields().lookupField( field );
121  if ( index >= 0 )
122  {
123  hubFieldIndices << index;
124  hubOutFields.append( hubSource->fields().at( index ) );
125  }
126  }
127  }
128 
129  QgsAttributeList hubFields2Fetch = hubFieldIndices;
130  hubFields2Fetch << fieldHubIndex;
131 
132  QgsFields spokeOutFields;
133  QgsAttributeList spokeFieldIndices;
134  if ( spokeFieldsToCopy.empty() )
135  {
136  spokeOutFields = spokeSource->fields();
137  for ( int i = 0; i < spokeOutFields.count(); ++i )
138  {
139  spokeFieldIndices << i;
140  }
141  }
142  else
143  {
144  for ( const QString &field : spokeFieldsToCopy )
145  {
146  int index = spokeSource->fields().lookupField( field );
147  if ( index >= 0 )
148  {
149  spokeFieldIndices << index;
150  spokeOutFields.append( spokeSource->fields().at( index ) );
151  }
152  }
153  }
154 
155  QgsAttributeList spokeFields2Fetch = spokeFieldIndices;
156  spokeFields2Fetch << fieldSpokeIndex;
157 
158 
159  QgsFields fields = QgsProcessingUtils::combineFields( hubOutFields, spokeOutFields );
160 
162  bool hasZ = false;
163  if ( QgsWkbTypes::hasZ( hubSource->wkbType() ) || QgsWkbTypes::hasZ( spokeSource->wkbType() ) )
164  {
165  outType = QgsWkbTypes::addZ( outType );
166  hasZ = true;
167  }
168  bool hasM = false;
169  if ( QgsWkbTypes::hasM( hubSource->wkbType() ) || QgsWkbTypes::hasM( spokeSource->wkbType() ) )
170  {
171  outType = QgsWkbTypes::addM( outType );
172  hasM = true;
173  }
174 
175  QString dest;
176  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields,
177  outType, hubSource->sourceCrs() ) );
178  if ( !sink )
179  throw QgsProcessingException( QObject::tr( "Could not create destination layer for OUTPUT" ) );;
180 
181  auto getPointFromFeature = [hasZ, hasM]( const QgsFeature & feature )->QgsPoint
182  {
183  QgsPoint p;
184  if ( feature.geometry().type() == QgsWkbTypes::PointGeometry && !feature.geometry().isMultipart() )
185  p = *static_cast< const QgsPoint *>( feature.geometry().constGet() );
186  else
187  p = *static_cast< const QgsPoint *>( feature.geometry().pointOnSurface().constGet() );
188  if ( hasZ && !p.is3D() )
189  p.addZValue( 0 );
190  if ( hasM && !p.isMeasure() )
191  p.addMValue( 0 );
192  return p;
193  };
194 
195  QgsFeatureIterator hubFeatures = hubSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( hubFields2Fetch ), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
196  double step = hubSource->featureCount() > 0 ? 100.0 / hubSource->featureCount() : 1;
197  int i = 0;
198  QgsFeature hubFeature;
199  while ( hubFeatures.nextFeature( hubFeature ) )
200  {
201  i++;
202  if ( feedback->isCanceled() )
203  {
204  break;
205  }
206 
207  feedback->setProgress( i * step );
208 
209  if ( !hubFeature.hasGeometry() )
210  continue;
211 
212  QgsPoint hubPoint = getPointFromFeature( hubFeature );
213 
214  // only keep selected attributes
215  QgsAttributes hubAttributes;
216  for ( int j = 0; j < hubFeature.attributes().count(); ++j )
217  {
218  if ( !hubFieldIndices.contains( j ) )
219  continue;
220  hubAttributes << hubFeature.attribute( j );
221  }
222 
223  QgsFeatureRequest spokeRequest = QgsFeatureRequest().setDestinationCrs( hubSource->sourceCrs(), context.transformContext() );
224  spokeRequest.setSubsetOfAttributes( spokeFields2Fetch );
225  spokeRequest.setFilterExpression( QgsExpression::createFieldEqualityExpression( fieldSpokeName, hubFeature.attribute( fieldHubIndex ) ) );
226 
227  QgsFeatureIterator spokeFeatures = spokeSource->getFeatures( spokeRequest, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
228  QgsFeature spokeFeature;
229  while ( spokeFeatures.nextFeature( spokeFeature ) )
230  {
231  if ( feedback->isCanceled() )
232  {
233  break;
234  }
235  if ( !spokeFeature.hasGeometry() )
236  continue;
237 
238  QgsPoint spokePoint = getPointFromFeature( spokeFeature );
239  QgsGeometry line( new QgsLineString( QVector< QgsPoint >() << hubPoint << spokePoint ) );
240 
241  QgsFeature outFeature;
242  QgsAttributes outAttributes = hubAttributes;
243 
244  // only keep selected attributes
245  QgsAttributes spokeAttributes;
246  for ( int j = 0; j < spokeFeature.attributes().count(); ++j )
247  {
248  if ( !spokeFieldIndices.contains( j ) )
249  continue;
250  spokeAttributes << spokeFeature.attribute( j );
251  }
252 
253  outAttributes.append( spokeAttributes );
254  outFeature.setAttributes( outAttributes );
255  outFeature.setGeometry( line );
256  sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
257  }
258  }
259 
260  QVariantMap outputs;
261  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
262  return outputs;
263 }
264 
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:485
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:111
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
Return 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
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:41
Vector line layers.
Definition: qgsprocessing.h:50
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:496
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