QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsalgorithmsplitvectorlayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmsplitvectorlayer.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 
19 #include "qgsvectorfilewriter.h"
20 
22 
23 QString QgsSplitVectorLayerAlgorithm::name() const
24 {
25  return QStringLiteral( "splitvectorlayer" );
26 }
27 
28 QString QgsSplitVectorLayerAlgorithm::displayName() const
29 {
30  return QObject::tr( "Split vector layer" );
31 }
32 
33 QStringList QgsSplitVectorLayerAlgorithm::tags() const
34 {
35  return QObject::tr( "vector,split,field,unique" ).split( ',' );
36 }
37 
38 QString QgsSplitVectorLayerAlgorithm::group() const
39 {
40  return QObject::tr( "Vector general" );
41 }
42 
43 QString QgsSplitVectorLayerAlgorithm::groupId() const
44 {
45  return QStringLiteral( "vectorgeneral" );
46 }
47 
48 QString QgsSplitVectorLayerAlgorithm::shortHelpString() const
49 {
50  return QObject::tr( "Splits input vector layer into multiple layers by specified unique ID field." )
51  + QStringLiteral( "\n\n" )
52  + QObject::tr( "Each of the layers created in the output folder contains all features from "
53  "the input layer with the same value for the specified attribute. The number "
54  "of files generated is equal to the number of different values found for the "
55  "specified attribute." );
56 }
57 
58 QgsSplitVectorLayerAlgorithm *QgsSplitVectorLayerAlgorithm::createInstance() const
59 {
60  return new QgsSplitVectorLayerAlgorithm();
61 }
62 
63 void QgsSplitVectorLayerAlgorithm::initAlgorithm( const QVariantMap & )
64 {
65  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
66  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Unique ID field" ),
67  QVariant(), QStringLiteral( "INPUT" ) ) );
68 
69  const QStringList options = QgsVectorFileWriter::supportedFormatExtensions();
70  auto fileTypeParam = std::make_unique < QgsProcessingParameterEnum >( QStringLiteral( "FILE_TYPE" ), QObject::tr( "Output file type" ), options, false, QVariantList() << 0, true );
71  fileTypeParam->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
72  addParameter( fileTypeParam.release() );
73 
74  addParameter( new QgsProcessingParameterFolderDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output directory" ) ) );
75  addOutput( new QgsProcessingOutputMultipleLayers( QStringLiteral( "OUTPUT_LAYERS" ), QObject::tr( "Output layers" ) ) );
76 }
77 
78 QVariantMap QgsSplitVectorLayerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
79 {
80  std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
81  if ( !source )
82  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
83 
84  const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
85  const QString outputDir = parameterAsString( parameters, QStringLiteral( "OUTPUT" ), context );
86  QString outputFormat;
87  if ( parameters.value( QStringLiteral( "FILE_TYPE" ) ).isValid() )
88  {
89  const int idx = parameterAsEnum( parameters, QStringLiteral( "FILE_TYPE" ), context );
90  outputFormat = QgsVectorFileWriter::supportedFormatExtensions().at( idx );
91  }
92  else
93  {
94  outputFormat = context.preferredVectorFormat();
95  if ( !QgsVectorFileWriter::supportedFormatExtensions().contains( outputFormat, Qt::CaseInsensitive ) )
96  outputFormat = QStringLiteral( "gpkg" );
97  }
98 
99  if ( !QDir().mkpath( outputDir ) )
100  throw QgsProcessingException( QStringLiteral( "Failed to create output directory." ) );
101 
102  const QgsFields fields = source->fields();
103  const QgsCoordinateReferenceSystem crs = source->sourceCrs();
104  const QgsWkbTypes::Type geometryType = source->wkbType();
105  const int fieldIndex = fields.lookupField( fieldName );
106  const QSet< QVariant > uniqueValues = source->uniqueValues( fieldIndex );
107  const QString baseName = outputDir + QDir::separator() + fieldName;
108 
109  int current = 0;
110  const double step = uniqueValues.size() > 0 ? 100.0 / uniqueValues.size() : 1;
111 
112  int count = 0;
113  QgsFeature feat;
114  QStringList outputLayers;
115  std::unique_ptr< QgsFeatureSink > sink;
116 
117  for ( auto it = uniqueValues.constBegin(); it != uniqueValues.constEnd(); ++it )
118  {
119  if ( feedback->isCanceled() )
120  break;
121 
122  QString fileName;
123  if ( ( *it ).isNull() )
124  {
125  fileName = QStringLiteral( "%1_NULL.%2" ).arg( baseName ).arg( outputFormat );
126  }
127  else if ( ( *it ).toString().isEmpty() )
128  {
129  fileName = QStringLiteral( "%1_EMPTY.%2" ).arg( baseName ).arg( outputFormat );
130  }
131  else
132  {
133  fileName = QStringLiteral( "%1_%2.%3" ).arg( baseName ).arg( ( *it ).toString() ).arg( outputFormat );
134  }
135  feedback->pushInfo( QObject::tr( "Creating layer: %1" ).arg( fileName ) );
136 
137  sink.reset( QgsProcessingUtils::createFeatureSink( fileName, context, fields, geometryType, crs ) );
138  const QString expr = QgsExpression::createFieldEqualityExpression( fieldName, *it );
139  QgsFeatureIterator features = source->getFeatures( QgsFeatureRequest().setFilterExpression( expr ) );
140  count = 0;
141  while ( features.nextFeature( feat ) )
142  {
143  if ( feedback->isCanceled() )
144  break;
145 
146  if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
147  throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
148  count += 1;
149  }
150 
151  feedback->pushInfo( QObject::tr( "Added %n feature(s) to layer", nullptr, count ) );
152  outputLayers << fileName;
153 
154  current += 1;
155  feedback->setProgress( current * step );
156  }
157 
158  QVariantMap outputs;
159  outputs.insert( QStringLiteral( "OUTPUT" ), outputDir );
160  outputs.insert( QStringLiteral( "OUTPUT_LAYERS" ), outputLayers );
161  return outputs;
162 }
163 
This class represents a coordinate reference system (CRS).
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class 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:56
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
Container of fields for a vector layer.
Definition: qgsfields.h:45
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
Contains information about the context in which a processing algorithm is executed.
QString preferredVectorFormat() const
Returns the preferred vector format to use for vector outputs.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
A multi-layer output for processing algorithms which create map layers, when the number and nature of...
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer or feature source field parameter for processing algorithms.
A folder destination parameter, for specifying the destination path for a folder created by the algor...
static QgsFeatureSink * createFeatureSink(QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions=QVariantMap(), const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QgsRemappingSinkDefinition *remappingDefinition=nullptr)
Creates a feature sink ready for adding features.
static QStringList supportedFormatExtensions(VectorFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
const QgsCoordinateReferenceSystem & crs