QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
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
54Qgis::ProcessingAlgorithmFlags QgsExtractLabelsAlgorithm::flags() const
55{
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
76 addParameter( new QgsProcessingParameterScale(
77 QStringLiteral( "SCALE" ),
78 QObject::tr( "Map scale" )
79 ) );
80
81 std::unique_ptr<QgsProcessingParameterMapTheme> mapThemeParameter = std::make_unique<QgsProcessingParameterMapTheme>(
82 QStringLiteral( "MAP_THEME" ),
83 QObject::tr( "Map theme" ),
84 QVariant(), true
85 );
86 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." ) );
87 addParameter( mapThemeParameter.release() );
88
89 addParameter( new QgsProcessingParameterBoolean(
90 QStringLiteral( "INCLUDE_UNPLACED" ),
91 QObject::tr( "Include unplaced labels" ),
92 QVariant( true )
93 ) );
94
95 std::unique_ptr<QgsProcessingParameterNumber> dpiParameter = std::make_unique<QgsProcessingParameterNumber>(
96 QStringLiteral( "DPI" ),
97 QObject::tr( "Map resolution (in DPI)" ),
99 QVariant( 96.0 ), true
100 );
101 dpiParameter->setFlags( dpiParameter->flags() | Qgis::ProcessingParameterFlag::Advanced );
102 addParameter( dpiParameter.release() );
103
104 addParameter( new QgsProcessingParameterFeatureSink(
105 QStringLiteral( "OUTPUT" ),
106 QObject::tr( "Extracted labels" ),
108 ) );
109}
110
111QString QgsExtractLabelsAlgorithm::shortDescription() const
112{
113 return QObject::tr( "Converts map labels to a point layer with relevant details saved as attributes." );
114}
115
116Qgis::ProcessingAlgorithmDocumentationFlags QgsExtractLabelsAlgorithm::documentationFlags() const
117{
119}
120
121QString QgsExtractLabelsAlgorithm::shortHelpString() const
122{
123 return QObject::tr( "This algorithm extracts label information from a rendered map at a given extent and scale.\n\n"
124 "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"
125 "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." );
126}
127
128QgsExtractLabelsAlgorithm *QgsExtractLabelsAlgorithm::createInstance() const
129{
130 return new QgsExtractLabelsAlgorithm();
131}
132
133class ExtractLabelSink : public QgsLabelSink
134{
135 public:
136 ExtractLabelSink( const QMap<QString, QString> &mapLayerNames, QgsProcessingFeedback *feedback )
137 : mMapLayerNames( mapLayerNames )
138 , mFeedback( feedback )
139 {
140 }
141
142 void drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings ) override
143 {
144 processLabel( layerId, context, label, settings, false );
145 }
146
147 void drawUnplacedLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings ) override
148 {
149 processLabel( layerId, context, label, settings, true );
150 }
151
152 void processLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings, bool unplacedLabel )
153 {
154 if ( mFeedback->isCanceled() )
155 {
156 context.setRenderingStopped( true );
157 }
158
159 const QgsFeatureId fid = label->getFeaturePart()->featureId();
160 switch ( settings.placement )
161 {
164 {
165 if ( !mCurvedWarningPushed.contains( layerId ) )
166 {
167 mCurvedWarningPushed << layerId;
168 mFeedback->pushWarning( QObject::tr( "Curved placement not supported, skipping labels from layer %1" ).arg( mMapLayerNames.value( layerId ) ) );
169 }
170 return;
171 }
172
180 break;
181 }
182
183 QgsTextLabelFeature *labelFeature = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
184 if ( !labelFeature )
185 return;
186
187 QgsPalLayerSettings labelSettings( settings );
188 const QMap<QgsPalLayerSettings::Property, QVariant> &dataDefinedValues = labelFeature->dataDefinedValues();
189
190 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::MultiLineWrapChar ) )
191 {
192 labelSettings.wrapChar = dataDefinedValues.value( QgsPalLayerSettings::Property::MultiLineWrapChar ).toString();
193 }
194 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::AutoWrapLength ) )
195 {
196 labelSettings.autoWrapLength = dataDefinedValues.value( QgsPalLayerSettings::Property::AutoWrapLength ).toInt();
197 }
198 const QString labelText = QgsPalLabeling::splitToLines( labelFeature->text( -1 ), labelSettings.wrapChar, labelSettings.autoWrapLength, labelSettings.useMaxLineLengthForAutoWrap ).join( '\n' );
199
200 QString labelAlignment;
201 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::MultiLineAlignment ) )
202 {
203 labelSettings.multilineAlign = static_cast<Qgis::LabelMultiLineAlignment>( dataDefinedValues.value( QgsPalLayerSettings::Property::MultiLineAlignment ).toInt() );
204 }
205 switch ( labelSettings.multilineAlign )
206 {
208 labelAlignment = QStringLiteral( "right" );
209 break;
210
212 labelAlignment = QStringLiteral( "center" );
213 break;
214
216 labelAlignment = QStringLiteral( "left" );
217 break;
218
220 labelAlignment = QStringLiteral( "justify" );
221 break;
222
224 switch ( label->quadrant() )
225 {
229 labelAlignment = QStringLiteral( "right" );
230 break;
231
235 labelAlignment = QStringLiteral( "center" );
236 break;
237
241 labelAlignment = QStringLiteral( "left" );
242 break;
243 }
244 break;
245 }
246
247 const double labelRotation = !qgsDoubleNear( label->getAlpha(), 0.0 )
248 ? -( label->getAlpha() * 180 / M_PI ) + 360
249 : 0.0;
250
251 const QFont font = labelFeature->definedFont();
252 const QString fontFamily = font.family();
253 const QString fontStyle = font.styleName();
254 const double fontSize = static_cast<double>( font.pixelSize() ) * 72 / context.painter()->device()->logicalDpiX();
255 const bool fontItalic = font.italic();
256 const bool fontBold = font.bold();
257 const bool fontUnderline = font.underline();
258 const double fontLetterSpacing = font.letterSpacing();
259 const double fontWordSpacing = font.wordSpacing();
260
261 QgsTextFormat format = labelSettings.format();
262 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::Size ) )
263 {
264 format.setSize( dataDefinedValues.value( QgsPalLayerSettings::Property::Size ).toDouble() );
265 }
266 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::Color ) )
267 {
268 format.setColor( dataDefinedValues.value( QgsPalLayerSettings::Property::Color ).value<QColor>() );
269 }
270 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::FontOpacity ) )
271 {
272 format.setOpacity( dataDefinedValues.value( QgsPalLayerSettings::Property::FontOpacity ).toDouble() / 100.0 );
273 }
274 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::MultiLineHeight ) )
275 {
276 format.setLineHeight( dataDefinedValues.value( QgsPalLayerSettings::Property::MultiLineHeight ).toDouble() );
277 }
278
279 const QString formatColor = format.color().name();
280 const double formatOpacity = format.opacity() * 100;
281 const double formatLineHeight = format.lineHeight();
282
283 QgsTextBufferSettings buffer = format.buffer();
284 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::BufferDraw ) )
285 {
286 buffer.setEnabled( dataDefinedValues.value( QgsPalLayerSettings::Property::BufferDraw ).toBool() );
287 }
288 const bool bufferDraw = buffer.enabled();
289 double bufferSize = 0.0;
290 QString bufferColor;
291 double bufferOpacity = 0.0;
292 if ( bufferDraw )
293 {
294 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::BufferSize ) )
295 {
296 buffer.setSize( dataDefinedValues.value( QgsPalLayerSettings::Property::BufferSize ).toDouble() );
297 }
298 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::BufferColor ) )
299 {
300 buffer.setColor( dataDefinedValues.value( QgsPalLayerSettings::Property::BufferColor ).value<QColor>() );
301 }
302 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::BufferOpacity ) )
303 {
304 buffer.setOpacity( dataDefinedValues.value( QgsPalLayerSettings::Property::BufferOpacity ).toDouble() / 100.0 );
305 }
306
307 bufferSize = buffer.sizeUnit() == Qgis::RenderUnit::Percentage
308 ? context.convertToPainterUnits( format.size(), format.sizeUnit(), format.sizeMapUnitScale() ) * buffer.size() / 100
309 : context.convertToPainterUnits( buffer.size(), buffer.sizeUnit(), buffer.sizeMapUnitScale() );
310 bufferSize = bufferSize * 72 / context.painter()->device()->logicalDpiX();
311 bufferColor = buffer.color().name();
312 bufferOpacity = buffer.opacity() * 100;
313 }
314
315 QgsAttributes attributes;
316 attributes << mMapLayerNames.value( layerId ) << fid
317 << labelText << label->getWidth() << label->getHeight() << labelRotation << unplacedLabel
318 << fontFamily << fontSize << fontItalic << fontBold << fontUnderline << fontStyle << fontLetterSpacing << fontWordSpacing
319 << labelAlignment << formatLineHeight << formatColor << formatOpacity
320 << bufferDraw << bufferSize << bufferColor << bufferOpacity;
321
322 double x = label->getX();
323 double y = label->getY();
324 QgsGeometry geometry( new QgsPoint( x, y ) );
325
326 QgsFeature feature;
327 feature.setAttributes( attributes );
328 feature.setGeometry( geometry );
329 features << feature;
330 }
331
332 QList<QgsFeature> features;
333
334 private:
335 QMap<QString, QString> mMapLayerNames;
336 QList<QString> mCurvedWarningPushed;
337
338 QgsProcessingFeedback *mFeedback = nullptr;
339};
340
341QVariantMap QgsExtractLabelsAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
342{
343 const QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context );
344 const double scale = parameterAsDouble( parameters, QStringLiteral( "SCALE" ), context );
345 if ( qgsDoubleNear( scale, 0.0 ) )
346 {
347 throw QgsProcessingException( QObject::tr( "Invalid scale value, a number greater than 0 is required" ) );
348 }
349 double dpi = parameterAsDouble( parameters, QStringLiteral( "DPI" ), context );
350 if ( qgsDoubleNear( dpi, 0.0 ) )
351 {
352 dpi = 96.0;
353 }
354
355 QgsScaleCalculator calculator;
356 calculator.setDpi( dpi );
357 calculator.setMapUnits( mCrs.mapUnits() );
358 const QSize imageSize = calculator.calculateImageSize( extent, scale ).toSize();
359
360 QgsFields fields;
361 fields.append( QgsField( QStringLiteral( "Layer" ), QMetaType::Type::QString, QString(), 0, 0 ) );
362 fields.append( QgsField( QStringLiteral( "FeatureID" ), QMetaType::Type::LongLong, QString(), 20 ) );
363 fields.append( QgsField( QStringLiteral( "LabelText" ), QMetaType::Type::QString, QString(), 0, 0 ) );
364 fields.append( QgsField( QStringLiteral( "LabelWidth" ), QMetaType::Type::Double, QString(), 20, 8 ) );
365 fields.append( QgsField( QStringLiteral( "LabelHeight" ), QMetaType::Type::Double, QString(), 20, 8 ) );
366 fields.append( QgsField( QStringLiteral( "LabelRotation" ), QMetaType::Type::Double, QString(), 20, 2 ) );
367 fields.append( QgsField( QStringLiteral( "LabelUnplaced" ), QMetaType::Type::Bool, QString(), 1, 0 ) );
368 fields.append( QgsField( QStringLiteral( "Family" ), QMetaType::Type::QString, QString(), 0, 0 ) );
369 fields.append( QgsField( QStringLiteral( "Size" ), QMetaType::Type::Double, QString(), 20, 4 ) );
370 fields.append( QgsField( QStringLiteral( "Italic" ), QMetaType::Type::Bool, QString(), 1, 0 ) );
371 fields.append( QgsField( QStringLiteral( "Bold" ), QMetaType::Type::Bool, QString(), 1, 0 ) );
372 fields.append( QgsField( QStringLiteral( "Underline" ), QMetaType::Type::Bool, QString(), 1, 0 ) );
373 fields.append( QgsField( QStringLiteral( "FontStyle" ), QMetaType::Type::QString, QString(), 0, 0 ) );
374 fields.append( QgsField( QStringLiteral( "FontLetterSpacing" ), QMetaType::Type::Double, QString(), 20, 4 ) );
375 fields.append( QgsField( QStringLiteral( "FontWordSpacing" ), QMetaType::Type::Double, QString(), 20, 4 ) );
376 fields.append( QgsField( QStringLiteral( "MultiLineAlignment" ), QMetaType::Type::QString, QString(), 0, 0 ) );
377 fields.append( QgsField( QStringLiteral( "MultiLineHeight" ), QMetaType::Type::Double, QString(), 20, 2 ) );
378 fields.append( QgsField( QStringLiteral( "Color" ), QMetaType::Type::QString, QString(), 7, 0 ) );
379 fields.append( QgsField( QStringLiteral( "FontOpacity" ), QMetaType::Type::Double, QString(), 20, 1 ) );
380 fields.append( QgsField( QStringLiteral( "BufferDraw" ), QMetaType::Type::Bool, QString(), 1, 0 ) );
381 fields.append( QgsField( QStringLiteral( "BufferSize" ), QMetaType::Type::Double, QString(), 20, 4 ) );
382 fields.append( QgsField( QStringLiteral( "BufferColor" ), QMetaType::Type::QString, QString(), 7, 0 ) );
383 fields.append( QgsField( QStringLiteral( "BufferOpacity" ), QMetaType::Type::Double, QString(), 20, 1 ) );
384
385 QString dest;
386 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, Qgis::WkbType::Point, mCrs, QgsFeatureSink::RegeneratePrimaryKey ) );
387 if ( !sink )
388 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
389
390 QgsMapSettings mapSettings;
391 mapSettings.setDestinationCrs( mCrs );
392 mapSettings.setExtent( extent );
393 mapSettings.setOutputSize( imageSize );
394 mapSettings.setOutputDpi( dpi );
395 mapSettings.setFlag( Qgis::MapSettingsFlag::DrawLabeling, true );
398 mapSettings.setLayers( mMapLayers );
399 mapSettings.setLayerStyleOverrides( mMapThemeStyleOverrides );
400 mapSettings.setLabelingEngineSettings( mLabelSettings );
401
402 //build the expression context
403 QgsExpressionContext expressionContext;
404 expressionContext << QgsExpressionContextUtils::globalScope()
407 mapSettings.setExpressionContext( expressionContext );
408
409 QgsNullPaintDevice nullPaintDevice;
410 nullPaintDevice.setOutputSize( imageSize );
411 nullPaintDevice.setOutputDpi( static_cast<int>( std::round( dpi ) ) );
412 QPainter painter( &nullPaintDevice );
413
414 QgsMapRendererCustomPainterJob renderJob( mapSettings, &painter );
415 ExtractLabelSink labelSink( mMapLayerNames, feedback );
416 renderJob.setLabelSink( &labelSink );
417
418 feedback->pushInfo( QObject::tr( "Extracting labels" ) );
419
420 QgsProcessingMultiStepFeedback multiStepFeedback( 10, feedback );
421 multiStepFeedback.setCurrentStep( 0 );
422
423 QEventLoop loop;
424 QObject::connect( feedback, &QgsFeedback::canceled, &renderJob, &QgsMapRendererCustomPainterJob::cancel );
425 QObject::connect( &renderJob, &QgsMapRendererJob::renderingLayersFinished, feedback, [feedback]() { feedback->pushInfo( QObject::tr( "Calculating label placement" ) ); } );
426 int labelsCollectedFromLayers = 0;
427 QObject::connect( &renderJob, &QgsMapRendererJob::layerRenderingStarted, feedback, [this, &multiStepFeedback, &labelsCollectedFromLayers]( const QString &layerId ) {
428 multiStepFeedback.pushInfo( QObject::tr( "Collecting labelled features from %1" ).arg( mMapLayerNames.value( layerId ) ) );
429 multiStepFeedback.setProgress( 100.0 * static_cast<double>( labelsCollectedFromLayers ) / mMapLayers.size() );
430 labelsCollectedFromLayers++;
431 } );
432
433 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::labelRegistrationAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
434 multiStepFeedback.setCurrentStep( 1 );
435 multiStepFeedback.pushInfo( QObject::tr( "Registering labels" ) );
436 } );
437
438 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::providerRegistrationAboutToBegin, &multiStepFeedback, [this, &multiStepFeedback]( QgsAbstractLabelProvider *provider ) {
439 multiStepFeedback.setCurrentStep( 2 );
440 if ( !provider->layerId().isEmpty() )
441 {
442 multiStepFeedback.pushInfo( QObject::tr( "Adding labels from %1" ).arg( mMapLayerNames.value( provider->layerId() ) ) );
443 }
444 } );
445 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::candidateCreationAboutToBegin, &multiStepFeedback, [this, &multiStepFeedback]( QgsAbstractLabelProvider *provider ) {
446 multiStepFeedback.setCurrentStep( 3 );
447 if ( !provider->layerId().isEmpty() )
448 {
449 multiStepFeedback.pushInfo( QObject::tr( "Generating label placement candidates for %1" ).arg( mMapLayerNames.value( provider->layerId() ) ) );
450 }
451 } );
452 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::obstacleCostingAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
453 multiStepFeedback.setCurrentStep( 4 );
454 multiStepFeedback.setProgressText( QObject::tr( "Calculating obstacle costs" ) );
455 } );
456 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::calculatingConflictsAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
457 multiStepFeedback.setCurrentStep( 5 );
458 multiStepFeedback.setProgressText( QObject::tr( "Calculating label conflicts" ) );
459 } );
460 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::finalizingCandidatesAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
461 multiStepFeedback.setCurrentStep( 6 );
462 multiStepFeedback.setProgressText( QObject::tr( "Finalizing candidates" ) );
463 } );
464 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::reductionAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
465 multiStepFeedback.setCurrentStep( 7 );
466 multiStepFeedback.setProgressText( QObject::tr( "Reducing problem" ) );
467 } );
468 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::solvingPlacementAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
469 multiStepFeedback.setCurrentStep( 8 );
470 multiStepFeedback.setProgressText( QObject::tr( "Determining optimal label placements" ) );
471 } );
472 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::solvingPlacementFinished, &multiStepFeedback, [&multiStepFeedback]() {
473 multiStepFeedback.setProgressText( QObject::tr( "Labeling complete" ) );
474 } );
475
476 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::progressChanged, &multiStepFeedback, [&multiStepFeedback]( double progress ) {
477 multiStepFeedback.setProgress( progress );
478 } );
479
480 QObject::connect( &renderJob, &QgsMapRendererJob::finished, &loop, [&loop]() { loop.exit(); } );
481 renderJob.start();
482 loop.exec();
483
484 qDeleteAll( mMapLayers );
485 mMapLayers.clear();
486
487 multiStepFeedback.setCurrentStep( 9 );
488 feedback->pushInfo( QObject::tr( "Writing %n label(s) to output layer", "", labelSink.features.count() ) );
489 const double step = !labelSink.features.empty() ? 100.0 / labelSink.features.count() : 1;
490 long long index = -1;
491 for ( QgsFeature &feature : labelSink.features )
492 {
493 index++;
494 multiStepFeedback.setProgress( step * index );
495 if ( feedback->isCanceled() )
496 break;
497
498 if ( !sink->addFeature( feature, QgsFeatureSink::FastInsert ) )
499 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
500 }
501 sink->finalize();
502 sink.reset();
503
504 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( dest, context ) ) )
505 {
506 vl->setRenderer( new QgsNullSymbolRenderer() );
507 if ( vl->renderer() )
508 {
509 vl->renderer()->setReferenceScale( scale );
510
511 QgsPalLayerSettings settings;
512 QgsPropertyCollection settingsProperties;
513
514 settings.fieldName = QStringLiteral( "LabelText" );
515 settings.obstacleSettings().setIsObstacle( false );
520
521 QgsTextFormat textFormat;
522 textFormat.setSize( 9 );
524 textFormat.setColor( QColor( 0, 0, 0 ) );
525
526 QgsTextBufferSettings buffer = textFormat.buffer();
528
529 textFormat.setBuffer( buffer );
530 settings.setFormat( textFormat );
531
532 settingsProperties.setProperty( QgsPalLayerSettings::Property::Color, QgsProperty::fromExpression( QStringLiteral( "if(\"LabelUnplaced\",'255,0,0',\"Color\")" ) ) );
533 settingsProperties.setProperty( QgsPalLayerSettings::Property::FontOpacity, QgsProperty::fromField( QStringLiteral( "FontOpacity" ) ) );
534 settingsProperties.setProperty( QgsPalLayerSettings::Property::Family, QgsProperty::fromField( QStringLiteral( "Family" ) ) );
535 settingsProperties.setProperty( QgsPalLayerSettings::Property::Italic, QgsProperty::fromField( QStringLiteral( "Italic" ) ) );
536 settingsProperties.setProperty( QgsPalLayerSettings::Property::Bold, QgsProperty::fromField( QStringLiteral( "Bold" ) ) );
537 settingsProperties.setProperty( QgsPalLayerSettings::Property::Underline, QgsProperty::fromField( QStringLiteral( "Underline" ) ) );
538 settingsProperties.setProperty( QgsPalLayerSettings::Property::Size, QgsProperty::fromField( QStringLiteral( "Size" ) ) );
539 settingsProperties.setProperty( QgsPalLayerSettings::Property::FontLetterSpacing, QgsProperty::fromField( QStringLiteral( "FontLetterSpacing" ) ) );
540 settingsProperties.setProperty( QgsPalLayerSettings::Property::FontWordSpacing, QgsProperty::fromField( QStringLiteral( "FontWordSpacing" ) ) );
541 settingsProperties.setProperty( QgsPalLayerSettings::Property::MultiLineAlignment, QgsProperty::fromField( QStringLiteral( "MultiLineAlignment" ) ) );
542 settingsProperties.setProperty( QgsPalLayerSettings::Property::MultiLineHeight, QgsProperty::fromField( QStringLiteral( "MultiLineHeight" ) ) );
543 settingsProperties.setProperty( QgsPalLayerSettings::Property::LabelRotation, QgsProperty::fromField( QStringLiteral( "LabelRotation" ) ) );
544 settingsProperties.setProperty( QgsPalLayerSettings::Property::BufferDraw, QgsProperty::fromField( QStringLiteral( "BufferDraw" ) ) );
545 settingsProperties.setProperty( QgsPalLayerSettings::Property::BufferSize, QgsProperty::fromField( QStringLiteral( "BufferSize" ) ) );
546 settingsProperties.setProperty( QgsPalLayerSettings::Property::BufferColor, QgsProperty::fromField( QStringLiteral( "BufferColor" ) ) );
547 settingsProperties.setProperty( QgsPalLayerSettings::Property::BufferOpacity, QgsProperty::fromField( QStringLiteral( "BufferOpacity" ) ) );
548 settingsProperties.setProperty( QgsPalLayerSettings::Property::Show, QgsProperty::fromExpression( QStringLiteral( "\"LabelUnplaced\"=false" ) ) );
549 settings.setDataDefinedProperties( settingsProperties );
550
552 vl->setLabeling( labeling );
553 vl->setLabelsEnabled( true );
554
555 QString errorMessage;
556 vl->saveStyleToDatabase( QString(), QString(), true, QString(), errorMessage );
557 }
558 }
559
560 QVariantMap outputs;
561 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
562 return outputs;
563}
564
565
566bool QgsExtractLabelsAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
567{
568 // Retrieve and clone layers
569 const QString mapTheme = parameterAsString( parameters, QStringLiteral( "MAP_THEME" ), context );
570 if ( !mapTheme.isEmpty() && context.project()->mapThemeCollection()->hasMapTheme( mapTheme ) )
571 {
572 const QList<QgsMapLayer *> constLayers = context.project()->mapThemeCollection()->mapThemeVisibleLayers( mapTheme );
573 for ( const QgsMapLayer *l : constLayers )
574 {
575 // only copy vector layers as other layer types aren't actors in the labeling process
576 if ( l->type() == Qgis::LayerType::Vector )
577 mMapLayers.push_back( l->clone() );
578 }
579 mMapThemeStyleOverrides = context.project()->mapThemeCollection()->mapThemeStyleOverrides( mapTheme );
580 }
581
582 if ( mMapLayers.isEmpty() )
583 {
584 QList<QgsMapLayer *> layers;
585 QgsLayerTree *root = context.project()->layerTreeRoot();
586 const QList<QgsLayerTreeLayer *> layerTreeLayers = root->findLayers();
587 layers.reserve( layerTreeLayers.size() );
588 for ( QgsLayerTreeLayer *nodeLayer : layerTreeLayers )
589 {
590 QgsMapLayer *layer = nodeLayer->layer();
591 if ( nodeLayer->isVisible() && root->layerOrder().contains( layer ) )
592 layers << layer;
593 }
594
595 for ( const QgsMapLayer *l : std::as_const( layers ) )
596 {
597 if ( l->type() == Qgis::LayerType::Vector )
598 mMapLayers.push_back( l->clone() );
599 }
600 }
601
602 for ( const QgsMapLayer *l : std::as_const( mMapLayers ) )
603 {
604 mMapLayerNames.insert( l->id(), l->name() );
605 }
606
607 mCrs = parameterAsExtentCrs( parameters, QStringLiteral( "EXTENT" ), context );
608 if ( !mCrs.isValid() )
609 mCrs = context.project()->crs();
610
611 bool includeUnplaced = parameterAsBoolean( parameters, QStringLiteral( "INCLUDE_UNPLACED" ), context );
612 mLabelSettings = context.project()->labelingEngineSettings();
613 mLabelSettings.setFlag( Qgis::LabelingFlag::DrawUnplacedLabels, includeUnplaced );
614 mLabelSettings.setFlag( Qgis::LabelingFlag::CollectUnplacedLabels, includeUnplaced );
615
616 return true;
617}
618
619
@ VectorPoint
Vector point layers.
@ 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.
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only.
@ 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.
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3410
LabelMultiLineAlignment
Text alignment for multi-line labels.
Definition qgis.h:1269
@ FollowPlacement
Alignment follows placement of label, e.g., labels to the left of a feature will be drawn with right ...
@ Vector
Vector layer.
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
@ Points
Points (e.g., for font sizes)
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3430
@ RequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
@ 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.
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.
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:58
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
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.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:70
A geometry is the spatial representation of a feature.
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.
void setQuadrant(Qgis::LabelQuadrantPosition quadrant)
Sets the quadrant in which to offset labels from the point.
Abstract base class that can be used to intercept rendered labels from a labeling / rendering job.
virtual void drawUnplacedLabel(const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings)
The drawLabel method is called for each unplaced label.
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.
void setFlag(Qgis::LabelingFlag 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.
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:76
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, which draws no symbols for features by default, but allows for labeling and dia...
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.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
@ LabelRotation
Label rotation.
@ Italic
Use italic style.
@ BufferOpacity
Buffer opacity.
@ FontLetterSpacing
Letter spacing.
QString fieldName
Name of field (or an expression) to use for label text.
const QgsLabelPointSettings & pointSettings() const
Returns the label point settings, which contain settings related to how the label engine places and f...
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
virtual Qgis::ProcessingAlgorithmFlags 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.
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.
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, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
QgsMapThemeCollection * mapThemeCollection
Definition qgsproject.h:115
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:112
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.
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...
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
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(Qgis::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.
Qgis::RenderUnit sizeUnit() const
Returns the units for the buffer size.
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 setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the buffer size.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
QColor color() const
Returns the color of the buffer.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
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.
void setSizeUnit(Qgis::RenderUnit unit)
Sets 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.
Qgis::RenderUnit sizeUnit() const
Returns 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.
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:166
QgsLabelFeature * feature()
Returns the parent feature.
Definition feature.h:94
LabelPosition is a candidate feature label position.
double getAlpha() const
Returns the angle to rotate text (in radians).
double getHeight() const
double getWidth() const
Qgis::LabelQuadrantPosition quadrant() const
Returns the quadrant associated with this label position.
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.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6066
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features