QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsalgorithmextractlabels.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmextractlabels.cpp - QgsExtractLabelsAlgorithm
3 ---------------------
4 begin : 30.12.2021
5 copyright : (C) 2021 by Mathieu Pellerin
6 email : nirvn dot asia at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
22#include "qgslabelsink.h"
23#include "qgslayertree.h"
24#include "qgsvectorlayer.h"
25#include "qgsscalecalculator.h"
26#include "qgstextlabelfeature.h"
29
30#include "pal/feature.h"
31#include "pal/labelposition.h"
32
33#include <QPainter>
34
35#include <cmath>
36
38
39QString QgsExtractLabelsAlgorithm::name() const
40{
41 return QStringLiteral( "extractlabels" );
42}
43
44QString QgsExtractLabelsAlgorithm::displayName() const
45{
46 return QObject::tr( "Extract labels" );
47}
48
49QStringList QgsExtractLabelsAlgorithm::tags() const
50{
51 return QObject::tr( "map themes,font,position" ).split( ',' );
52}
53
54QgsProcessingAlgorithm::Flags QgsExtractLabelsAlgorithm::flags() const
55{
56 return QgsProcessingAlgorithm::flags() | FlagRequiresProject;
57}
58
59QString QgsExtractLabelsAlgorithm::group() const
60{
61 return QObject::tr( "Cartography" );
62}
63
64QString QgsExtractLabelsAlgorithm::groupId() const
65{
66 return QStringLiteral( "cartography" );
67}
68
69void QgsExtractLabelsAlgorithm::initAlgorithm( const QVariantMap & )
70{
71 addParameter( new QgsProcessingParameterExtent(
72 QStringLiteral( "EXTENT" ),
73 QObject::tr( "Map extent" ) ) );
74
75 addParameter( new QgsProcessingParameterScale(
76 QStringLiteral( "SCALE" ),
77 QObject::tr( "Map scale" ) ) );
78
79 std::unique_ptr<QgsProcessingParameterMapTheme> mapThemeParameter = std::make_unique<QgsProcessingParameterMapTheme>(
80 QStringLiteral( "MAP_THEME" ),
81 QObject::tr( "Map theme" ),
82 QVariant(), true );
83 mapThemeParameter->setHelp( QObject::tr( "This parameter is optional. When left unset, the algorithm will fallback to extracting labels from all currently visible layers in the project." ) );
84 addParameter( mapThemeParameter.release() );
85
86 addParameter( new QgsProcessingParameterBoolean(
87 QStringLiteral( "INCLUDE_UNPLACED" ),
88 QObject::tr( "Include unplaced labels" ),
89 QVariant( true ), true ) );
90
91 std::unique_ptr<QgsProcessingParameterNumber> dpiParameter = std::make_unique<QgsProcessingParameterNumber>(
92 QStringLiteral( "DPI" ),
93 QObject::tr( "Map resolution (in DPI)" ),
95 QVariant( 96.0 ), true );
96 dpiParameter->setFlags( dpiParameter->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
97 addParameter( dpiParameter.release() );
98
99 addParameter( new QgsProcessingParameterFeatureSink(
100 QStringLiteral( "OUTPUT" ),
101 QObject::tr( "Extracted labels" ),
103}
104
105QString QgsExtractLabelsAlgorithm::shortDescription() const
106{
107 return QObject::tr( "Converts map labels to a point layer with relevant details saved as attributes." );
108}
109
110QString QgsExtractLabelsAlgorithm::shortHelpString() const
111{
112 return QObject::tr( "This algorithm extracts label information from a rendered map at a given extent and scale.\n\n"
113 "If a map theme is provided, the rendered map will match the visibility and symbology of that theme. If left blank, all visible layers from the project will be used.\n\n"
114 "Extracted label information include: position (served as point geometries), the associated layer name and feature ID, label text, rotation (in degree, clockwise), multiline alignment, and font details." );
115}
116
117QgsExtractLabelsAlgorithm *QgsExtractLabelsAlgorithm::createInstance() const
118{
119 return new QgsExtractLabelsAlgorithm();
120}
121
122class ExtractLabelSink : public QgsLabelSink
123{
124 public:
125 ExtractLabelSink( const QMap<QString, QString> &mapLayerNames, QgsProcessingFeedback *feedback )
126 : mMapLayerNames( mapLayerNames )
127 , mFeedback( feedback )
128 {
129 }
130
131 void drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings ) override
132 {
133 processLabel( layerId, context, label, settings, false );
134 }
135
136 void drawUnplacedLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings ) override
137 {
138 processLabel( layerId, context, label, settings, true );
139 }
140
141 void processLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings, bool unplacedLabel )
142 {
143 if ( mFeedback->isCanceled() )
144 {
145 context.setRenderingStopped( true );
146 }
147
148 const QgsFeatureId fid = label->getFeaturePart()->featureId();
149 if ( settings.placement == Qgis::LabelPlacement::Curved ||
151 {
152 if ( !mCurvedWarningPushed.contains( layerId ) )
153 {
154 mCurvedWarningPushed << layerId;
155 mFeedback->pushWarning( QObject::tr( "Curved placement not supported, skipping labels from layer %1" ).arg( mMapLayerNames.value( layerId ) ) );
156 }
157 return;
158 }
159
160 QgsTextLabelFeature *labelFeature = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
161 if ( !labelFeature )
162 return;
163
164 QgsPalLayerSettings labelSettings( settings );
165 const QMap< QgsPalLayerSettings::Property, QVariant > &dataDefinedValues = labelFeature->dataDefinedValues();
166
167 if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
168 {
169 labelSettings.wrapChar = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
170 }
171 if ( dataDefinedValues.contains( QgsPalLayerSettings::AutoWrapLength ) )
172 {
173 labelSettings.autoWrapLength = dataDefinedValues.value( QgsPalLayerSettings::AutoWrapLength ).toInt();
174 }
175 const QString labelText = QgsPalLabeling::splitToLines( labelFeature->text( -1 ),
176 labelSettings.wrapChar,
177 labelSettings.autoWrapLength,
178 labelSettings.useMaxLineLengthForAutoWrap ).join( '\n' );
179
180 QString labelAlignment;
181 if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
182 {
183 labelSettings.multilineAlign = static_cast< Qgis::LabelMultiLineAlignment >( dataDefinedValues.value( QgsPalLayerSettings::MultiLineAlignment ).toInt() );
184 }
185 switch ( labelSettings.multilineAlign )
186 {
187 case Qgis::LabelMultiLineAlignment::Right:
188 labelAlignment = QStringLiteral( "right" );
189 break;
190
191 case Qgis::LabelMultiLineAlignment::Center:
192 labelAlignment = QStringLiteral( "center" );
193 break;
194
195 case Qgis::LabelMultiLineAlignment::Left:
196 labelAlignment = QStringLiteral( "left" );
197 break;
198
199 case Qgis::LabelMultiLineAlignment::Justify:
200 labelAlignment = QStringLiteral( "justify" );
201 break;
202
203 case Qgis::LabelMultiLineAlignment::FollowPlacement:
204 switch ( label->getQuadrant() )
205 {
209 labelAlignment = QStringLiteral( "right" );
210 break;
211
215 labelAlignment = QStringLiteral( "center" );
216 break;
217
221 labelAlignment = QStringLiteral( "left" );
222 break;
223 }
224 break;
225 }
226
227 const double labelRotation = !qgsDoubleNear( label->getAlpha(), 0.0 )
228 ? -( label->getAlpha() * 180 / M_PI ) + 360
229 : 0.0;
230
231 const QFont font = labelFeature->definedFont();
232 const QString fontFamily = font.family();
233 const QString fontStyle = font.styleName();
234 const double fontSize = static_cast<double>( font.pixelSize() ) * 72 / context.painter()->device()->logicalDpiX();
235 const bool fontItalic = font.italic();
236 const bool fontBold = font.bold();
237 const bool fontUnderline = font.underline();
238 const double fontLetterSpacing = font.letterSpacing();
239 const double fontWordSpacing = font.wordSpacing();
240
241 QgsTextFormat format = labelSettings.format();
242 if ( dataDefinedValues.contains( QgsPalLayerSettings::Size ) )
243 {
244 format.setSize( dataDefinedValues.value( QgsPalLayerSettings::Size ).toDouble() );
245 }
246 if ( dataDefinedValues.contains( QgsPalLayerSettings::Color ) )
247 {
248 format.setColor( dataDefinedValues.value( QgsPalLayerSettings::Color ).value<QColor>() );
249 }
250 if ( dataDefinedValues.contains( QgsPalLayerSettings::FontOpacity ) )
251 {
252 format.setOpacity( dataDefinedValues.value( QgsPalLayerSettings::FontOpacity ).toDouble() / 100.0 );
253 }
254 if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
255 {
256 format.setLineHeight( dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble() );
257 }
258
259 const QString formatColor = format.color().name();
260 const double formatOpacity = format.opacity() * 100;
261 const double formatLineHeight = format.lineHeight();
262
263 QgsTextBufferSettings buffer = format.buffer();
264 if ( dataDefinedValues.contains( QgsPalLayerSettings::BufferDraw ) )
265 {
266 buffer.setEnabled( dataDefinedValues.value( QgsPalLayerSettings::BufferDraw ).toBool() );
267 }
268 const bool bufferDraw = buffer.enabled();
269 double bufferSize = 0.0;
270 QString bufferColor;
271 double bufferOpacity = 0.0;
272 if ( bufferDraw )
273 {
274 if ( dataDefinedValues.contains( QgsPalLayerSettings::BufferSize ) )
275 {
276 buffer.setSize( dataDefinedValues.value( QgsPalLayerSettings::BufferSize ).toDouble() );
277 }
278 if ( dataDefinedValues.contains( QgsPalLayerSettings::BufferColor ) )
279 {
280 buffer.setColor( dataDefinedValues.value( QgsPalLayerSettings::BufferColor ).value<QColor>() );
281 }
282 if ( dataDefinedValues.contains( QgsPalLayerSettings::BufferOpacity ) )
283 {
284 buffer.setOpacity( dataDefinedValues.value( QgsPalLayerSettings::BufferOpacity ).toDouble() / 100.0 );
285 }
286
287 bufferSize = buffer.sizeUnit() == QgsUnitTypes::RenderPercentage
288 ? context.convertToPainterUnits( format.size(), format.sizeUnit(), format.sizeMapUnitScale() ) * buffer.size() / 100
289 : context.convertToPainterUnits( buffer.size(), buffer.sizeUnit(), buffer.sizeMapUnitScale() );
290 bufferSize = bufferSize * 72 / context.painter()->device()->logicalDpiX();
291 bufferColor = buffer.color().name();
292 bufferOpacity = buffer.opacity() * 100;
293 }
294
295 QgsAttributes attributes;
296 attributes << mMapLayerNames.value( layerId ) << fid
297 << labelText << label->getWidth() << label->getHeight() << labelRotation << unplacedLabel
298 << fontFamily << fontSize << fontItalic << fontBold << fontUnderline << fontStyle << fontLetterSpacing << fontWordSpacing
299 << labelAlignment << formatLineHeight << formatColor << formatOpacity
300 << bufferDraw << bufferSize << bufferColor << bufferOpacity;
301
302 double x = label->getX();
303 double y = label->getY();
304 QgsGeometry geometry( new QgsPoint( x, y ) );
305
306 QgsFeature feature;
307 feature.setAttributes( attributes );
308 feature.setGeometry( geometry );
309 features << feature;
310 }
311
312 QList<QgsFeature> features;
313
314 private:
315
316 QMap<QString, QString> mMapLayerNames;
317 QList<QString> mCurvedWarningPushed;
318
319 QgsProcessingFeedback *mFeedback = nullptr;
320};
321
322QVariantMap QgsExtractLabelsAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
323{
324 const QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context );
325 const double scale = parameterAsDouble( parameters, QStringLiteral( "SCALE" ), context );
326 if ( qgsDoubleNear( scale, 0.0 ) )
327 {
328 throw QgsProcessingException( QObject::tr( "Invalid scale value, a number greater than 0 is required" ) );
329 }
330 double dpi = parameterAsDouble( parameters, QStringLiteral( "DPI" ), context );
331 if ( qgsDoubleNear( dpi, 0.0 ) )
332 {
333 dpi = 96.0;
334 }
335
336 QgsScaleCalculator calculator;
337 calculator.setDpi( dpi );
338 calculator.setMapUnits( mCrs.mapUnits() );
339 const QSize imageSize = calculator.calculateImageSize( extent, scale ).toSize();
340
341 QgsFields fields;
342 fields.append( QgsField( QStringLiteral( "Layer" ), QVariant::String, QString(), 0, 0 ) );
343 fields.append( QgsField( QStringLiteral( "FeatureID" ), QVariant::LongLong, QString(), 20 ) );
344 fields.append( QgsField( QStringLiteral( "LabelText" ), QVariant::String, QString(), 0, 0 ) );
345 fields.append( QgsField( QStringLiteral( "LabelWidth" ), QVariant::Double, QString(), 20, 8 ) );
346 fields.append( QgsField( QStringLiteral( "LabelHeight" ), QVariant::Double, QString(), 20, 8 ) );
347 fields.append( QgsField( QStringLiteral( "LabelRotation" ), QVariant::Double, QString(), 20, 2 ) );
348 fields.append( QgsField( QStringLiteral( "LabelUnplaced" ), QVariant::Bool, QString(), 1, 0 ) );
349 fields.append( QgsField( QStringLiteral( "Family" ), QVariant::String, QString(), 0, 0 ) );
350 fields.append( QgsField( QStringLiteral( "Size" ), QVariant::Double, QString(), 20, 4 ) );
351 fields.append( QgsField( QStringLiteral( "Italic" ), QVariant::Bool, QString(), 1, 0 ) );
352 fields.append( QgsField( QStringLiteral( "Bold" ), QVariant::Bool, QString(), 1, 0 ) );
353 fields.append( QgsField( QStringLiteral( "Underline" ), QVariant::Bool, QString(), 1, 0 ) );
354 fields.append( QgsField( QStringLiteral( "FontStyle" ), QVariant::String, QString(), 0, 0 ) );
355 fields.append( QgsField( QStringLiteral( "FontLetterSpacing" ), QVariant::Double, QString(), 20, 4 ) );
356 fields.append( QgsField( QStringLiteral( "FontWordSpacing" ), QVariant::Double, QString(), 20, 4 ) );
357 fields.append( QgsField( QStringLiteral( "MultiLineAlignment" ), QVariant::String, QString(), 0, 0 ) );
358 fields.append( QgsField( QStringLiteral( "MultiLineHeight" ), QVariant::Double, QString(), 20, 2 ) );
359 fields.append( QgsField( QStringLiteral( "Color" ), QVariant::String, QString(), 7, 0 ) );
360 fields.append( QgsField( QStringLiteral( "FontOpacity" ), QVariant::Double, QString(), 20, 1 ) );
361 fields.append( QgsField( QStringLiteral( "BufferDraw" ), QVariant::Bool, QString(), 1, 0 ) );
362 fields.append( QgsField( QStringLiteral( "BufferSize" ), QVariant::Double, QString(), 20, 4 ) );
363 fields.append( QgsField( QStringLiteral( "BufferColor" ), QVariant::String, QString(), 7, 0 ) );
364 fields.append( QgsField( QStringLiteral( "BufferOpacity" ), QVariant::Double, QString(), 20, 1 ) );
365
366 QString dest;
367 std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::Point, mCrs, QgsFeatureSink::RegeneratePrimaryKey ) );
368 if ( !sink )
369 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
370
371 QgsMapSettings mapSettings;
372 mapSettings.setDestinationCrs( mCrs );
373 mapSettings.setExtent( extent );
374 mapSettings.setOutputSize( imageSize );
375 mapSettings.setOutputDpi( dpi );
376 mapSettings.setFlag( Qgis::MapSettingsFlag::DrawLabeling, true );
379 mapSettings.setLayers( mMapLayers );
380 mapSettings.setLayerStyleOverrides( mMapThemeStyleOverrides );
381 mapSettings.setLabelingEngineSettings( mLabelSettings );
382
383 //build the expression context
384 QgsExpressionContext expressionContext;
385 expressionContext << QgsExpressionContextUtils::globalScope()
388 mapSettings.setExpressionContext( expressionContext );
389
390 QgsNullPaintDevice nullPaintDevice;
391 nullPaintDevice.setOutputSize( imageSize );
392 nullPaintDevice.setOutputDpi( static_cast< int >( std::round( dpi ) ) );
393 QPainter painter( &nullPaintDevice );
394
395 QgsMapRendererCustomPainterJob renderJob( mapSettings, &painter );
396 ExtractLabelSink labelSink( mMapLayerNames, feedback );
397 renderJob.setLabelSink( &labelSink );
398
399 feedback->pushInfo( QObject::tr( "Extracting labels" ) );
400
401 QgsProcessingMultiStepFeedback multiStepFeedback( 10, feedback );
402 multiStepFeedback.setCurrentStep( 0 );
403
404 QEventLoop loop;
405 QObject::connect( feedback, &QgsFeedback::canceled, &renderJob, &QgsMapRendererCustomPainterJob::cancel );
406 QObject::connect( &renderJob, &QgsMapRendererJob::renderingLayersFinished, feedback, [feedback]() { feedback->pushInfo( QObject::tr( "Calculating label placement" ) ); } );
407 int labelsCollectedFromLayers = 0;
408 QObject::connect( &renderJob, &QgsMapRendererJob::layerRenderingStarted, feedback, [this, &multiStepFeedback, &labelsCollectedFromLayers]( const QString & layerId )
409 {
410 multiStepFeedback.pushInfo( QObject::tr( "Collecting labelled features from %1" ).arg( mMapLayerNames.value( layerId ) ) );
411 multiStepFeedback.setProgress( 100.0 * static_cast< double >( labelsCollectedFromLayers ) / mMapLayers.size() );
412 labelsCollectedFromLayers++;
413 } );
414
415 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::labelRegistrationAboutToBegin, &multiStepFeedback, [&multiStepFeedback]()
416 {
417 multiStepFeedback.setCurrentStep( 1 );
418 multiStepFeedback.pushInfo( QObject::tr( "Registering labels" ) );
419 } );
420
421 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::providerRegistrationAboutToBegin, &multiStepFeedback, [this, &multiStepFeedback]( QgsAbstractLabelProvider * provider )
422 {
423 multiStepFeedback.setCurrentStep( 2 );
424 if ( !provider->layerId().isEmpty() )
425 {
426 multiStepFeedback.pushInfo( QObject::tr( "Adding labels from %1" ).arg( mMapLayerNames.value( provider->layerId() ) ) );
427 }
428 } );
429 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::candidateCreationAboutToBegin, &multiStepFeedback, [this, &multiStepFeedback]( QgsAbstractLabelProvider * provider )
430 {
431 multiStepFeedback.setCurrentStep( 3 );
432 if ( !provider->layerId().isEmpty() )
433 {
434 multiStepFeedback.pushInfo( QObject::tr( "Generating label placement candidates for %1" ).arg( mMapLayerNames.value( provider->layerId() ) ) );
435 }
436 } );
437 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::obstacleCostingAboutToBegin, &multiStepFeedback, [&multiStepFeedback]()
438 {
439 multiStepFeedback.setCurrentStep( 4 );
440 multiStepFeedback.setProgressText( QObject::tr( "Calculating obstacle costs" ) );
441 } );
442 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::calculatingConflictsAboutToBegin, &multiStepFeedback, [&multiStepFeedback]()
443 {
444 multiStepFeedback.setCurrentStep( 5 );
445 multiStepFeedback.setProgressText( QObject::tr( "Calculating label conflicts" ) );
446 } );
447 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::finalizingCandidatesAboutToBegin, &multiStepFeedback, [&multiStepFeedback]()
448 {
449 multiStepFeedback.setCurrentStep( 6 );
450 multiStepFeedback.setProgressText( QObject::tr( "Finalizing candidates" ) );
451 } );
452 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::reductionAboutToBegin, &multiStepFeedback, [&multiStepFeedback]()
453 {
454 multiStepFeedback.setCurrentStep( 7 );
455 multiStepFeedback.setProgressText( QObject::tr( "Reducing problem" ) );
456 } );
457 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::solvingPlacementAboutToBegin, &multiStepFeedback, [&multiStepFeedback]()
458 {
459 multiStepFeedback.setCurrentStep( 8 );
460 multiStepFeedback.setProgressText( QObject::tr( "Determining optimal label placements" ) );
461 } );
462 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::solvingPlacementFinished, &multiStepFeedback, [&multiStepFeedback]()
463 {
464 multiStepFeedback.setProgressText( QObject::tr( "Labeling complete" ) );
465 } );
466
467 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::progressChanged, &multiStepFeedback, [&multiStepFeedback]( double progress )
468 {
469 multiStepFeedback.setProgress( progress );
470 } );
471
472 QObject::connect( &renderJob, &QgsMapRendererJob::finished, &loop, [&loop]() { loop.exit(); } );
473 renderJob.start();
474 loop.exec();
475
476 qDeleteAll( mMapLayers );
477 mMapLayers.clear();
478
479 multiStepFeedback.setCurrentStep( 9 );
480 feedback->pushInfo( QObject::tr( "Writing %n label(s) to output layer", "", labelSink.features.count() ) );
481 const double step = !labelSink.features.empty() ? 100.0 / labelSink.features.count() : 1;
482 long long index = -1;
483 for ( QgsFeature &feature : labelSink.features )
484 {
485 index++;
486 multiStepFeedback.setProgress( step * index );
487 if ( feedback->isCanceled() )
488 break;
489
490 if ( !sink->addFeature( feature, QgsFeatureSink::FastInsert ) )
491 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
492 }
493 sink.reset();
494
495 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( QgsProcessingUtils::mapLayerFromString( dest, context ) ) )
496 {
497 vl->setRenderer( new QgsNullSymbolRenderer() );
498 if ( vl->renderer() )
499 {
500 vl->renderer()->setReferenceScale( scale );
501
502 QgsPalLayerSettings settings;
503 QgsPropertyCollection settingsProperties;
504
505 settings.fieldName = QStringLiteral( "LabelText" );
506 settings.obstacleSettings().setIsObstacle( false );
508 settings.quadOffset = Qgis::LabelQuadrantPosition::AboveRight;
511
512 QgsTextFormat textFormat;
513 textFormat.setSize( 9 );
515 textFormat.setColor( QColor( 0, 0, 0 ) );
516
517 QgsTextBufferSettings buffer = textFormat.buffer();
519
520 textFormat.setBuffer( buffer );
521 settings.setFormat( textFormat );
522
523 settingsProperties.setProperty( QgsPalLayerSettings::Color, QgsProperty::fromExpression( QStringLiteral( "if(\"LabelUnplaced\",'255,0,0',\"Color\")" ) ) );
524 settingsProperties.setProperty( QgsPalLayerSettings::FontOpacity, QgsProperty::fromField( QStringLiteral( "FontOpacity" ) ) );
525 settingsProperties.setProperty( QgsPalLayerSettings::Family, QgsProperty::fromField( QStringLiteral( "Family" ) ) );
526 settingsProperties.setProperty( QgsPalLayerSettings::Italic, QgsProperty::fromField( QStringLiteral( "Italic" ) ) );
527 settingsProperties.setProperty( QgsPalLayerSettings::Bold, QgsProperty::fromField( QStringLiteral( "Bold" ) ) );
528 settingsProperties.setProperty( QgsPalLayerSettings::Underline, QgsProperty::fromField( QStringLiteral( "Underline" ) ) );
529 settingsProperties.setProperty( QgsPalLayerSettings::Size, QgsProperty::fromField( QStringLiteral( "Size" ) ) );
530 settingsProperties.setProperty( QgsPalLayerSettings::FontLetterSpacing, QgsProperty::fromField( QStringLiteral( "FontLetterSpacing" ) ) );
531 settingsProperties.setProperty( QgsPalLayerSettings::FontWordSpacing, QgsProperty::fromField( QStringLiteral( "FontWordSpacing" ) ) );
532 settingsProperties.setProperty( QgsPalLayerSettings::MultiLineAlignment, QgsProperty::fromField( QStringLiteral( "MultiLineAlignment" ) ) );
533 settingsProperties.setProperty( QgsPalLayerSettings::MultiLineHeight, QgsProperty::fromField( QStringLiteral( "MultiLineHeight" ) ) );
534 settingsProperties.setProperty( QgsPalLayerSettings::LabelRotation, QgsProperty::fromField( QStringLiteral( "LabelRotation" ) ) );
535 settingsProperties.setProperty( QgsPalLayerSettings::BufferDraw, QgsProperty::fromField( QStringLiteral( "BufferDraw" ) ) );
536 settingsProperties.setProperty( QgsPalLayerSettings::BufferSize, QgsProperty::fromField( QStringLiteral( "BufferSize" ) ) );
537 settingsProperties.setProperty( QgsPalLayerSettings::BufferColor, QgsProperty::fromField( QStringLiteral( "BufferColor" ) ) );
538 settingsProperties.setProperty( QgsPalLayerSettings::BufferOpacity, QgsProperty::fromField( QStringLiteral( "BufferOpacity" ) ) );
539 settingsProperties.setProperty( QgsPalLayerSettings::Show, QgsProperty::fromExpression( QStringLiteral( "\"LabelUnplaced\"=false" ) ) );
540 settings.setDataDefinedProperties( settingsProperties );
541
543 vl->setLabeling( labeling );
544 vl->setLabelsEnabled( true );
545
546 QString errorMessage;
547 vl->saveStyleToDatabase( QString(), QString(), true, QString(), errorMessage );
548 }
549 }
550
551 QVariantMap outputs;
552 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
553 return outputs;
554}
555
556
557bool QgsExtractLabelsAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
558{
559 // Retrieve and clone layers
560 const QString mapTheme = parameterAsString( parameters, QStringLiteral( "MAP_THEME" ), context );
561 if ( !mapTheme.isEmpty() && context.project()->mapThemeCollection()->hasMapTheme( mapTheme ) )
562 {
563 const QList<QgsMapLayer *> constLayers = context.project()->mapThemeCollection()->mapThemeVisibleLayers( mapTheme );
564 for ( const QgsMapLayer *l : constLayers )
565 {
566 // only copy vector layers as other layer types aren't actors in the labeling process
567 if ( l->type() == QgsMapLayerType::VectorLayer )
568 mMapLayers.push_back( l->clone() );
569 }
570 mMapThemeStyleOverrides = context.project()->mapThemeCollection( )->mapThemeStyleOverrides( mapTheme );
571 }
572
573 if ( mMapLayers.isEmpty() )
574 {
575 QList<QgsMapLayer *> layers;
576 QgsLayerTree *root = context.project()->layerTreeRoot();
577 const QList<QgsLayerTreeLayer *> layerTreeLayers = root->findLayers();
578 layers.reserve( layerTreeLayers.size() );
579 for ( QgsLayerTreeLayer *nodeLayer : layerTreeLayers )
580 {
581 QgsMapLayer *layer = nodeLayer->layer();
582 if ( nodeLayer->isVisible() && root->layerOrder().contains( layer ) )
583 layers << layer;
584 }
585
586 for ( const QgsMapLayer *l : std::as_const( layers ) )
587 {
588 if ( l->type() == QgsMapLayerType::VectorLayer )
589 mMapLayers.push_back( l->clone() );
590 }
591 }
592
593 for ( const QgsMapLayer *l : std::as_const( mMapLayers ) )
594 {
595 mMapLayerNames.insert( l->id(), l->name() );
596 }
597
598 mCrs = parameterAsExtentCrs( parameters, QStringLiteral( "EXTENT" ), context );
599 if ( !mCrs.isValid() )
600 mCrs = context.project()->crs();
601
602 bool includeUnplaced = parameterAsBoolean( parameters, QStringLiteral( "INCLUDE_UNPLACED" ), context );
603 mLabelSettings = context.project()->labelingEngineSettings();
604 mLabelSettings.setFlag( QgsLabelingEngineSettings::DrawUnplacedLabels, includeUnplaced );
605 mLabelSettings.setFlag( QgsLabelingEngineSettings::CollectUnplacedLabels, includeUnplaced );
606
607 return true;
608}
609
610
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
LabelMultiLineAlignment
Text alignment for multi-line labels.
Definition: qgis.h:658
@ AllowOverlapIfRequired
Avoids overlapping labels when possible, but permit overlaps if labels for features cannot otherwise ...
@ UseRenderingOptimization
Enable vector simplification and other rendering optimizations.
@ DrawLabeling
Enable drawing of labels on top of the map.
@ SkipSymbolRendering
Disable symbol rendering while still drawing labels if enabled (since QGIS 3.24)
The QgsAbstractLabelProvider class is an interface class.
Abstract base class - its implementations define different approaches to the labeling of a vector lay...
A vector of attributes.
Definition: qgsattributes.h:59
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:160
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
void canceled()
Internal routines can connect to this signal if they use event loop.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
Container of fields for a vector layer.
Definition: qgsfields.h:45
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)
Definition: qgsfields.cpp:59
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
void setIsObstacle(bool isObstacle)
Sets whether features are obstacles to labels of other layers.
void setOverlapHandling(Qgis::LabelOverlapHandling handling)
Sets the technique used to handle overlapping labels.
void setAllowDegradedPlacement(bool allow)
Sets whether labels can be placed in inferior fallback positions if they cannot otherwise be placed.
Abstract base class that can be used to intercept rendered labels from a labeling / rendering job.
Definition: qgslabelsink.h:38
virtual void drawUnplacedLabel(const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings)
The drawLabel method is called for each unplaced label.
Definition: qgslabelsink.h:57
virtual void drawLabel(const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings)=0
The drawLabel method is called for each label that is being drawn.
void obstacleCostingAboutToBegin()
Emitted when the obstacle costing is about to begin.
void solvingPlacementAboutToBegin()
Emitted when the problem solving step is about to begin.
void calculatingConflictsAboutToBegin()
Emitted when the conflict handling step is about to begin.
void reductionAboutToBegin()
Emitted when the candidate reduction step is about to begin.
void labelRegistrationAboutToBegin()
Emitted when the label registration is about to begin.
void solvingPlacementFinished()
Emitted when the problem solving step is finished.
void finalizingCandidatesAboutToBegin()
Emitted when the label candidates are about to be finalized.
void candidateCreationAboutToBegin(QgsAbstractLabelProvider *provider)
Emitted when the label candidate creation is about to begin for a provider.
void providerRegistrationAboutToBegin(QgsAbstractLabelProvider *provider)
Emitted when the label registration is about to begin for a provider.
@ CollectUnplacedLabels
Whether unplaced labels should be collected in the labeling results (regardless of whether they are b...
@ DrawUnplacedLabels
Whether to render unplaced labels as an indicator/warning for users.
void setFlag(Flag f, bool enabled=true)
Sets whether a particual flag is enabled.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Layer tree node points to a map layer.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
Job implementation that renders everything sequentially using a custom painter.
void cancel() override
Stop the rendering job - does not return until the job has terminated.
void renderingLayersFinished()
Emitted when the layers are rendered.
void finished()
emitted when asynchronous rendering is finished (or canceled).
void layerRenderingStarted(const QString &layerId)
Emitted just before rendering starts for a particular layer.
The QgsMapSettings class contains configuration for rendering of the map.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the map of map layer style overrides (key: layer ID, value: style name) where a different style ...
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
void setFlag(Qgis::MapSettingsFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
QList< QgsMapLayer * > mapThemeVisibleLayers(const QString &name) const
Returns the list of layers that are visible for the specified map theme.
QMap< QString, QString > mapThemeStyleOverrides(const QString &name)
Gets layer style overrides (for QgsMapSettings) of the visible layers for given map theme.
Null painter device that can be used for map renderer jobs which use custom painters.
void setOutputSize(const QSize &size)
Sets the size of the device in pixels.
void setOutputDpi(const int dpi)
Sets the dpi of the device.
Null symbol renderer.
static QStringList splitToLines(const QString &text, const QString &wrapCharacter, int autoWrapLength=0, bool useMaxLineLengthWhenAutoWrapping=true)
Splits a text string to a list of separate lines, using a specified wrap character (wrapCharacter).
Contains settings for how a map layer will be labeled.
const QgsLabelObstacleSettings & obstacleSettings() const
Returns the label obstacle settings.
const QgsLabelPlacementSettings & placementSettings() const
Returns the label placement settings.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
Qgis::LabelPlacement placement
Label placement mode.
Qgis::LabelQuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
@ LabelRotation
Label rotation.
@ Underline
Use underline.
@ FontLetterSpacing
Letter spacing.
@ Bold
Use bold style.
@ BufferOpacity
Buffer opacity.
@ Italic
Use italic style.
@ FontWordSpacing
Word spacing.
@ Family
Font family.
@ FontOpacity
Text opacity.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
QString fieldName
Name of field (or an expression) to use for label text.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
virtual Flags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Contains information about the context in which a processing algorithm is executed.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
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.
Processing feedback object for multi-step operations.
A boolean parameter for processing algorithms.
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
A rectangular map extent parameter for processing algorithms.
A feature sink output for processing algorithms.
A double numeric parameter for map scale values.
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType)
Interprets a string as a map layer within the supplied context.
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:49
QgsMapThemeCollection * mapThemeCollection
Definition: qgsproject.h:112
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:109
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project's global labeling engine settings.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Contains information about the context of a rendering operation.
void setRenderingStopped(bool stopped)
Sets whether the rendering operation has been stopped and any ongoing rendering should be canceled im...
QPainter * painter()
Returns the destination QPainter for the render operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
Calculates scale for a given combination of canvas size, map extent, and monitor dpi.
void setDpi(double dpi)
Sets the dpi (dots per inch) for the output resolution, to be used in scale calculations.
void setMapUnits(QgsUnitTypes::DistanceUnit mapUnits)
Set the map units.
QSizeF calculateImageSize(const QgsRectangle &mapExtent, double scale) const
Calculate the image size in pixel (physical) units.
Container for settings relating to a text buffer.
double size() const
Returns the size of the buffer.
void setColor(const QColor &color)
Sets the color for the buffer.
void setOpacity(double opacity)
Sets the buffer opacity.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
bool enabled() const
Returns whether the buffer is enabled.
double opacity() const
Returns the buffer opacity.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
QColor color() const
Returns the color of the buffer.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
double lineHeight() const
Returns the line height for text.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
void setOpacity(double opacity)
Sets the text's opacity.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text's buffer settings.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
double opacity() const
Returns the text's opacity.
double size() const
Returns the size for rendered text.
QColor color() const
Returns the color that text will be rendered in.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
void setLineHeight(double height)
Sets the line height for text.
Class that adds extra information to QgsLabelFeature for text labels.
QFont definedFont() const
Font to be used for rendering.
const QMap< QgsPalLayerSettings::Property, QVariant > & dataDefinedValues() const
Gets data-defined values.
QString text(int partId) const
Returns the text component corresponding to a specified label part.
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:172
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:173
Basic implementation of the labeling interface.
Represents a vector layer which manages a vector based data sets.
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Definition: feature.cpp:164
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:94
LabelPosition is a candidate feature label position.
Definition: labelposition.h:56
double getAlpha() const
Returns the angle to rotate text (in rad).
double getHeight() const
Quadrant getQuadrant() const
double getWidth() const
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
double getX(int i=0) const
Returns the down-left x coordinate.
double getY(int i=0) const
Returns the down-left y coordinate.
@ VectorLayer
Vector layer.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28