25 QgsCategorizeUsingStyleAlgorithm::QgsCategorizeUsingStyleAlgorithm() =
default;
27 QgsCategorizeUsingStyleAlgorithm::~QgsCategorizeUsingStyleAlgorithm() =
default;
29 void QgsCategorizeUsingStyleAlgorithm::initAlgorithm(
const QVariantMap & )
33 addParameter(
new QgsProcessingParameterExpression( QStringLiteral(
"FIELD" ), QObject::tr(
"Categorize using expression" ), QVariant(), QStringLiteral(
"INPUT" ) ) );
36 addParameter(
new QgsProcessingParameterBoolean( QStringLiteral(
"CASE_SENSITIVE" ), QObject::tr(
"Use case-sensitive match to symbol names" ),
false ) );
37 addParameter(
new QgsProcessingParameterBoolean( QStringLiteral(
"TOLERANT" ), QObject::tr(
"Ignore non-alphanumeric characters while matching" ),
false ) );
41 std::unique_ptr< QgsProcessingParameterFeatureSink > failCategories = qgis::make_unique< QgsProcessingParameterFeatureSink >( QStringLiteral(
"NON_MATCHING_CATEGORIES" ), QObject::tr(
"Non-matching categories" ),
45 addParameter( failCategories.release() );
47 std::unique_ptr< QgsProcessingParameterFeatureSink > failSymbols = qgis::make_unique< QgsProcessingParameterFeatureSink >( QStringLiteral(
"NON_MATCHING_SYMBOLS" ), QObject::tr(
"Non-matching symbol names" ),
50 addParameter( failSymbols.release() );
53 QString QgsCategorizeUsingStyleAlgorithm::name()
const 55 return QStringLiteral(
"categorizeusingstyle" );
58 QString QgsCategorizeUsingStyleAlgorithm::displayName()
const 60 return QObject::tr(
"Create categorized renderer from styles" );
63 QStringList QgsCategorizeUsingStyleAlgorithm::tags()
const 65 return QObject::tr(
"file,database,symbols,names,category,categories" ).split(
',' );
68 QString QgsCategorizeUsingStyleAlgorithm::group()
const 70 return QObject::tr(
"Cartography" );
73 QString QgsCategorizeUsingStyleAlgorithm::groupId()
const 75 return QStringLiteral(
"cartography" );
78 QString QgsCategorizeUsingStyleAlgorithm::shortHelpString()
const 80 return QObject::tr(
"Sets a vector layer's renderer to a categorized renderer using matching symbols from a style database. If no " 81 "style file is specified, symbols from the user's current style library are used instead.\n\n" 82 "The specified expression (or field name) is used to create categories for the renderer. A category will be " 83 "created for each unique value within the layer.\n\n" 84 "Each category is individually matched to the symbols which exist within the specified QGIS XML style database. Whenever " 85 "a matching symbol name is found, the category's symbol will be set to this matched symbol.\n\n" 86 "The matching is case-insensitive by default, but can be made case-sensitive if required.\n\n" 87 "Optionally, non-alphanumeric characters in both the category value and symbol name can be ignored " 88 "while performing the match. This allows for greater tolerance when matching categories to symbols.\n\n" 89 "If desired, tables can also be output containing lists of the categories which could not be matched " 90 "to symbols, and symbols which were not matched to categories." 94 QString QgsCategorizeUsingStyleAlgorithm::shortDescription()
const 96 return QObject::tr(
"Sets a vector layer's renderer to a categorized renderer using symbols from a style database." );
99 QgsCategorizeUsingStyleAlgorithm *QgsCategorizeUsingStyleAlgorithm::createInstance()
const 101 return new QgsCategorizeUsingStyleAlgorithm();
108 SetCategorizedRendererPostProcessor( std::unique_ptr< QgsCategorizedSymbolRenderer > renderer )
109 : mRenderer( std::move( renderer ) )
114 if (
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
117 vl->setRenderer( mRenderer.release() );
118 vl->triggerRepaint();
124 std::unique_ptr<QgsCategorizedSymbolRenderer> mRenderer;
131 QgsVectorLayer *layer = parameterAsVectorLayer( parameters, QStringLiteral(
"INPUT" ), context );
135 mField = parameterAsString( parameters, QStringLiteral(
"FIELD" ), context );
137 mLayerId = layer->
id();
138 mLayerName = layer->
name();
140 mLayerFields = layer->
fields();
147 mExpression.prepare( &mExpressionContext );
151 if ( !mExpression.needsGeometry() )
161 const QString styleFile = parameterAsFile( parameters, QStringLiteral(
"STYLE" ), context );
162 const bool caseSensitive = parameterAsBool( parameters, QStringLiteral(
"CASE_SENSITIVE" ), context );
163 const bool tolerant = parameterAsBool( parameters, QStringLiteral(
"TOLERANT" ), context );
166 std::unique_ptr< QgsStyle >importedStyle;
167 if ( !styleFile.isEmpty() )
169 importedStyle = qgis::make_unique< QgsStyle >();
170 if ( !importedStyle->importXml( styleFile ) )
172 throw QgsProcessingException( QObject::tr(
"An error occurred while reading style file: %1" ).arg( importedStyle->errorString() ) );
174 style = importedStyle.get();
182 nonMatchingCategoryFields.
append(
QgsField( QStringLiteral(
"category" ), QVariant::String ) );
183 QString nonMatchingCategoriesDest;
184 std::unique_ptr< QgsFeatureSink > nonMatchingCategoriesSink( parameterAsSink( parameters, QStringLiteral(
"NON_MATCHING_CATEGORIES" ), context, nonMatchingCategoriesDest, nonMatchingCategoryFields,
QgsWkbTypes::NoGeometry ) );
185 if ( !nonMatchingCategoriesSink && parameters.contains( QStringLiteral(
"NON_MATCHING_CATEGORIES" ) ) && parameters.value( QStringLiteral(
"NON_MATCHING_CATEGORIES" ) ).isValid() )
186 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral(
"NON_MATCHING_CATEGORIES" ) ) );
189 nonMatchingSymbolFields.
append(
QgsField( QStringLiteral(
"name" ), QVariant::String ) );
190 QString nonMatchingSymbolsDest;
191 std::unique_ptr< QgsFeatureSink > nonMatchingSymbolsSink( parameterAsSink( parameters, QStringLiteral(
"NON_MATCHING_SYMBOLS" ), context, nonMatchingSymbolsDest, nonMatchingSymbolFields,
QgsWkbTypes::NoGeometry ) );
192 if ( !nonMatchingSymbolsSink && parameters.contains( QStringLiteral(
"NON_MATCHING_SYMBOLS" ) ) && parameters.value( QStringLiteral(
"NON_MATCHING_SYMBOLS" ) ).isValid() )
195 QSet<QVariant> uniqueVals;
197 while ( mIterator.nextFeature( feature ) )
199 mExpressionContext.setFeature( feature );
200 QVariant value = mExpression.evaluate( &mExpressionContext );
201 if ( uniqueVals.contains( value ) )
206 QVariantList sortedUniqueVals = uniqueVals.toList();
207 std::sort( sortedUniqueVals.begin(), sortedUniqueVals.end() );
210 cats.reserve( uniqueVals.count() );
212 for (
const QVariant &val : qgis::as_const( sortedUniqueVals ) )
217 mRenderer = qgis::make_unique< QgsCategorizedSymbolRenderer >( mField, cats );
223 QVariantList unmatchedCategories;
224 QStringList unmatchedSymbols;
225 const int matched = mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols, caseSensitive, tolerant );
229 feedback->
pushInfo( QObject::tr(
"Matched %1 categories to symbols from file." ).arg( matched ) );
233 feedback->
reportError( QObject::tr(
"No categories could be matched to symbols in file." ) );
236 if ( !unmatchedCategories.empty() )
238 feedback->
pushInfo( QObject::tr(
"\n%1 categories could not be matched:" ).arg( unmatchedCategories.count() ) );
239 std::sort( unmatchedCategories.begin(), unmatchedCategories.end() );
240 for (
const QVariant &cat : qgis::as_const( unmatchedCategories ) )
242 feedback->
pushInfo( QStringLiteral(
"∙ “%1”" ).arg( cat.toString() ) );
243 if ( nonMatchingCategoriesSink )
252 if ( !unmatchedSymbols.empty() )
254 feedback->
pushInfo( QObject::tr(
"\n%1 symbols in style were not matched:" ).arg( unmatchedSymbols.count() ) );
255 std::sort( unmatchedSymbols.begin(), unmatchedSymbols.end() );
256 for (
const QString &name : qgis::as_const( unmatchedSymbols ) )
258 feedback->
pushInfo( QStringLiteral(
"∙ “%1”" ).arg( name ) );
259 if ( nonMatchingSymbolsSink )
272 results.insert( QStringLiteral(
"OUTPUT" ), mLayerId );
273 if ( nonMatchingCategoriesSink )
274 results.insert( QStringLiteral(
"NON_MATCHING_CATEGORIES" ), nonMatchingCategoriesDest );
275 if ( nonMatchingSymbolsSink )
276 results.insert( QStringLiteral(
"NON_MATCHING_SYMBOLS" ), nonMatchingSymbolsDest );
A boolean parameter for processing algorithms.
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsProject * project() const
Returns the project in which the algorithm is being executed.
An input file or folder parameter for processing algorithms.
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
Base class for all map layer types.
Base class for providing feedback from a processing algorithm.
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
An interface for layer post-processing handlers for execution following a processing algorithm operat...
An expression parameter for processing algorithms.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
void setPostProcessor(QgsProcessingLayerPostProcessorInterface *processor)
Sets the layer post-processor.
Container of fields for a vector layer.
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
A vector layer output for processing algorithms.
static QgsStyle * defaultStyle()
Returns default application-wide style.
QList< QgsRendererCategory > QgsCategoryList
SymbolType
Type of the symbol.
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Parameter is a single file.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Custom exception class for processing related exceptions.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false) ...
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
A vector layer (with or without geometry) parameter for processing algorithms.
Encapsulate a field in an attribute table or data source.
Details for layers to load into projects.
void addLayerToLoadOnCompletion(const QString &layer, const QgsProcessingContext::LayerDetails &details)
Adds a layer to load (by ID or datasource) into the canvas upon completion of the algorithm or model...
QgsProcessingContext::LayerDetails & layerToLoadOnCompletionDetails(const QString &layer)
Returns a reference to the details for a given layer which is loaded on completion of the algorithm o...
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
virtual void postProcessLayer(QgsMapLayer *layer, QgsProcessingContext &context, QgsProcessingFeedback *feedback)=0
Post-processes the specified layer, following successful execution of a processing algorithm...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
Contains information about the context in which a processing algorithm is executed.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.