QGIS API Documentation 3.99.0-Master (09f76ad7019)
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
17
18#include <cmath>
19
20#include "pal/feature.h"
21#include "pal/labelposition.h"
23#include "qgslabelsink.h"
24#include "qgslayertree.h"
31#include "qgsscalecalculator.h"
32#include "qgstextlabelfeature.h"
33#include "qgsvectorlayer.h"
34
35#include <QPainter>
36#include <QString>
37
38using namespace Qt::StringLiterals;
39
41
42QString QgsExtractLabelsAlgorithm::name() const
43{
44 return u"extractlabels"_s;
45}
46
47QString QgsExtractLabelsAlgorithm::displayName() const
48{
49 return QObject::tr( "Extract labels" );
50}
51
52QStringList QgsExtractLabelsAlgorithm::tags() const
53{
54 return QObject::tr( "map themes,font,position" ).split( ',' );
55}
56
57Qgis::ProcessingAlgorithmFlags QgsExtractLabelsAlgorithm::flags() const
58{
60}
61
62QString QgsExtractLabelsAlgorithm::group() const
63{
64 return QObject::tr( "Cartography" );
65}
66
67QString QgsExtractLabelsAlgorithm::groupId() const
68{
69 return u"cartography"_s;
70}
71
72void QgsExtractLabelsAlgorithm::initAlgorithm( const QVariantMap & )
73{
74 addParameter( new QgsProcessingParameterExtent(
75 u"EXTENT"_s,
76 QObject::tr( "Map extent" )
77 ) );
78
79 addParameter( new QgsProcessingParameterScale(
80 u"SCALE"_s,
81 QObject::tr( "Map scale" )
82 ) );
83
84 auto mapThemeParameter = std::make_unique<QgsProcessingParameterMapTheme>(
85 u"MAP_THEME"_s,
86 QObject::tr( "Map theme" ),
87 QVariant(), true
88 );
89 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." ) );
90 addParameter( mapThemeParameter.release() );
91
92 addParameter( new QgsProcessingParameterBoolean(
93 u"INCLUDE_UNPLACED"_s,
94 QObject::tr( "Include unplaced labels" ),
95 QVariant( true )
96 ) );
97
98 auto dpiParameter = std::make_unique<QgsProcessingParameterNumber>(
99 u"DPI"_s,
100 QObject::tr( "Map resolution (in DPI)" ),
102 QVariant( 96.0 ), true
103 );
104 dpiParameter->setFlags( dpiParameter->flags() | Qgis::ProcessingParameterFlag::Advanced );
105 addParameter( dpiParameter.release() );
106
107 addParameter( new QgsProcessingParameterFeatureSink(
108 u"OUTPUT"_s,
109 QObject::tr( "Extracted labels" ),
111 ) );
112}
113
114QString QgsExtractLabelsAlgorithm::shortDescription() const
115{
116 return QObject::tr( "Converts map labels to a point layer with relevant details saved as attributes." );
117}
118
119Qgis::ProcessingAlgorithmDocumentationFlags QgsExtractLabelsAlgorithm::documentationFlags() const
120{
122}
123
124QString QgsExtractLabelsAlgorithm::shortHelpString() const
125{
126 return QObject::tr( "This algorithm extracts label information from a rendered map at a given extent and scale.\n\n"
127 "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"
128 "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." );
129}
130
131QgsExtractLabelsAlgorithm *QgsExtractLabelsAlgorithm::createInstance() const
132{
133 return new QgsExtractLabelsAlgorithm();
134}
135
136class ExtractLabelSink : public QgsLabelSink
137{
138 public:
139 ExtractLabelSink( const QMap<QString, QString> &mapLayerNames, QgsProcessingFeedback *feedback )
140 : mMapLayerNames( mapLayerNames )
141 , mFeedback( feedback )
142 {
143 }
144
145 void drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings ) override
146 {
147 processLabel( layerId, context, label, settings, false );
148 }
149
150 void drawUnplacedLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings ) override
151 {
152 processLabel( layerId, context, label, settings, true );
153 }
154
155 void processLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings, bool unplacedLabel )
156 {
157 if ( mFeedback->isCanceled() )
158 {
159 context.setRenderingStopped( true );
160 }
161
162 const QgsFeatureId fid = label->getFeaturePart()->featureId();
163 switch ( settings.placement )
164 {
167 {
168 if ( !mCurvedWarningPushed.contains( layerId ) )
169 {
170 mCurvedWarningPushed << layerId;
171 mFeedback->pushWarning( QObject::tr( "Curved placement not supported, skipping labels from layer %1" ).arg( mMapLayerNames.value( layerId ) ) );
172 }
173 return;
174 }
175
183 break;
184 }
185
186 QgsTextLabelFeature *labelFeature = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
187 if ( !labelFeature )
188 return;
189
190 QgsPalLayerSettings labelSettings( settings );
191 const QMap<QgsPalLayerSettings::Property, QVariant> &dataDefinedValues = labelFeature->dataDefinedValues();
192
193 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::MultiLineWrapChar ) )
194 {
195 labelSettings.wrapChar = dataDefinedValues.value( QgsPalLayerSettings::Property::MultiLineWrapChar ).toString();
196 }
197 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::AutoWrapLength ) )
198 {
199 labelSettings.autoWrapLength = dataDefinedValues.value( QgsPalLayerSettings::Property::AutoWrapLength ).toInt();
200 }
201 const QString labelText = QgsPalLabeling::splitToLines( labelFeature->text( -1 ), labelSettings.wrapChar, labelSettings.autoWrapLength, labelSettings.useMaxLineLengthForAutoWrap ).join( '\n' );
202
203 QString labelAlignment;
204 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::MultiLineAlignment ) )
205 {
206 labelSettings.multilineAlign = static_cast<Qgis::LabelMultiLineAlignment>( dataDefinedValues.value( QgsPalLayerSettings::Property::MultiLineAlignment ).toInt() );
207 }
208 switch ( labelSettings.multilineAlign )
209 {
211 labelAlignment = u"right"_s;
212 break;
213
215 labelAlignment = u"center"_s;
216 break;
217
219 labelAlignment = u"left"_s;
220 break;
221
223 labelAlignment = u"justify"_s;
224 break;
225
227 switch ( label->quadrant() )
228 {
232 labelAlignment = u"right"_s;
233 break;
234
238 labelAlignment = u"center"_s;
239 break;
240
244 labelAlignment = u"left"_s;
245 break;
246 }
247 break;
248 }
249
250 const double labelRotation = !qgsDoubleNear( label->getAlpha(), 0.0 )
251 ? -( label->getAlpha() * 180 / M_PI ) + 360
252 : 0.0;
253
254 const QFont font = labelFeature->definedFont();
255 const QString fontFamily = font.family();
256 const QString fontStyle = font.styleName();
257 const double fontSize = static_cast<double>( font.pixelSize() ) * 72 / context.painter()->device()->logicalDpiX();
258 const bool fontItalic = font.italic();
259 const bool fontBold = font.bold();
260 const bool fontUnderline = font.underline();
261 const double fontLetterSpacing = font.letterSpacing();
262 const double fontWordSpacing = font.wordSpacing();
263
264 QgsTextFormat format = labelSettings.format();
265 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::Size ) )
266 {
267 format.setSize( dataDefinedValues.value( QgsPalLayerSettings::Property::Size ).toDouble() );
268 }
269 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::Color ) )
270 {
271 format.setColor( dataDefinedValues.value( QgsPalLayerSettings::Property::Color ).value<QColor>() );
272 }
273 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::FontOpacity ) )
274 {
275 format.setOpacity( dataDefinedValues.value( QgsPalLayerSettings::Property::FontOpacity ).toDouble() / 100.0 );
276 }
277 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::MultiLineHeight ) )
278 {
279 format.setLineHeight( dataDefinedValues.value( QgsPalLayerSettings::Property::MultiLineHeight ).toDouble() );
280 }
281
282 const QString formatColor = format.color().name();
283 const double formatOpacity = format.opacity() * 100;
284 const double formatLineHeight = format.lineHeight();
285
286 QgsTextBufferSettings buffer = format.buffer();
287 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::BufferDraw ) )
288 {
289 buffer.setEnabled( dataDefinedValues.value( QgsPalLayerSettings::Property::BufferDraw ).toBool() );
290 }
291 const bool bufferDraw = buffer.enabled();
292 double bufferSize = 0.0;
293 QString bufferColor;
294 double bufferOpacity = 0.0;
295 if ( bufferDraw )
296 {
297 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::BufferSize ) )
298 {
299 buffer.setSize( dataDefinedValues.value( QgsPalLayerSettings::Property::BufferSize ).toDouble() );
300 }
301 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::BufferColor ) )
302 {
303 buffer.setColor( dataDefinedValues.value( QgsPalLayerSettings::Property::BufferColor ).value<QColor>() );
304 }
305 if ( dataDefinedValues.contains( QgsPalLayerSettings::Property::BufferOpacity ) )
306 {
307 buffer.setOpacity( dataDefinedValues.value( QgsPalLayerSettings::Property::BufferOpacity ).toDouble() / 100.0 );
308 }
309
310 bufferSize = buffer.sizeUnit() == Qgis::RenderUnit::Percentage
311 ? context.convertToPainterUnits( format.size(), format.sizeUnit(), format.sizeMapUnitScale() ) * buffer.size() / 100
312 : context.convertToPainterUnits( buffer.size(), buffer.sizeUnit(), buffer.sizeMapUnitScale() );
313 bufferSize = bufferSize * 72 / context.painter()->device()->logicalDpiX();
314 bufferColor = buffer.color().name();
315 bufferOpacity = buffer.opacity() * 100;
316 }
317
318 QgsAttributes attributes;
319 attributes << mMapLayerNames.value( layerId ) << fid
320 << labelText << label->getWidth() << label->getHeight() << labelRotation << unplacedLabel
321 << fontFamily << fontSize << fontItalic << fontBold << fontUnderline << fontStyle << fontLetterSpacing << fontWordSpacing
322 << labelAlignment << formatLineHeight << formatColor << formatOpacity
323 << bufferDraw << bufferSize << bufferColor << bufferOpacity;
324
325 double x = label->getX();
326 double y = label->getY();
327 QgsGeometry geometry( new QgsPoint( x, y ) );
328
329 QgsFeature feature;
330 feature.setAttributes( attributes );
331 feature.setGeometry( geometry );
332 features << feature;
333 }
334
335 QList<QgsFeature> features;
336
337 private:
338 QMap<QString, QString> mMapLayerNames;
339 QList<QString> mCurvedWarningPushed;
340
341 QgsProcessingFeedback *mFeedback = nullptr;
342};
343
344QVariantMap QgsExtractLabelsAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
345{
346 const QgsRectangle extent = parameterAsExtent( parameters, u"EXTENT"_s, context );
347 const double scale = parameterAsDouble( parameters, u"SCALE"_s, context );
348 if ( qgsDoubleNear( scale, 0.0 ) )
349 {
350 throw QgsProcessingException( QObject::tr( "Invalid scale value, a number greater than 0 is required" ) );
351 }
352 double dpi = parameterAsDouble( parameters, u"DPI"_s, context );
353 if ( qgsDoubleNear( dpi, 0.0 ) )
354 {
355 dpi = 96.0;
356 }
357
358 QgsScaleCalculator calculator;
359 calculator.setDpi( dpi );
360 calculator.setMapUnits( mCrs.mapUnits() );
361 const QSize imageSize = calculator.calculateImageSize( extent, scale ).toSize();
362
363 QgsFields fields;
364 fields.append( QgsField( u"Layer"_s, QMetaType::Type::QString, QString(), 0, 0 ) );
365 fields.append( QgsField( u"FeatureID"_s, QMetaType::Type::LongLong, QString(), 20 ) );
366 fields.append( QgsField( u"LabelText"_s, QMetaType::Type::QString, QString(), 0, 0 ) );
367 fields.append( QgsField( u"LabelWidth"_s, QMetaType::Type::Double, QString(), 20, 8 ) );
368 fields.append( QgsField( u"LabelHeight"_s, QMetaType::Type::Double, QString(), 20, 8 ) );
369 fields.append( QgsField( u"LabelRotation"_s, QMetaType::Type::Double, QString(), 20, 2 ) );
370 fields.append( QgsField( u"LabelUnplaced"_s, QMetaType::Type::Bool, QString(), 1, 0 ) );
371 fields.append( QgsField( u"Family"_s, QMetaType::Type::QString, QString(), 0, 0 ) );
372 fields.append( QgsField( u"Size"_s, QMetaType::Type::Double, QString(), 20, 4 ) );
373 fields.append( QgsField( u"Italic"_s, QMetaType::Type::Bool, QString(), 1, 0 ) );
374 fields.append( QgsField( u"Bold"_s, QMetaType::Type::Bool, QString(), 1, 0 ) );
375 fields.append( QgsField( u"Underline"_s, QMetaType::Type::Bool, QString(), 1, 0 ) );
376 fields.append( QgsField( u"FontStyle"_s, QMetaType::Type::QString, QString(), 0, 0 ) );
377 fields.append( QgsField( u"FontLetterSpacing"_s, QMetaType::Type::Double, QString(), 20, 4 ) );
378 fields.append( QgsField( u"FontWordSpacing"_s, QMetaType::Type::Double, QString(), 20, 4 ) );
379 fields.append( QgsField( u"MultiLineAlignment"_s, QMetaType::Type::QString, QString(), 0, 0 ) );
380 fields.append( QgsField( u"MultiLineHeight"_s, QMetaType::Type::Double, QString(), 20, 2 ) );
381 fields.append( QgsField( u"Color"_s, QMetaType::Type::QString, QString(), 7, 0 ) );
382 fields.append( QgsField( u"FontOpacity"_s, QMetaType::Type::Double, QString(), 20, 1 ) );
383 fields.append( QgsField( u"BufferDraw"_s, QMetaType::Type::Bool, QString(), 1, 0 ) );
384 fields.append( QgsField( u"BufferSize"_s, QMetaType::Type::Double, QString(), 20, 4 ) );
385 fields.append( QgsField( u"BufferColor"_s, QMetaType::Type::QString, QString(), 7, 0 ) );
386 fields.append( QgsField( u"BufferOpacity"_s, QMetaType::Type::Double, QString(), 20, 1 ) );
387
388 QString dest;
389 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, dest, fields, Qgis::WkbType::Point, mCrs, QgsFeatureSink::RegeneratePrimaryKey ) );
390 if ( !sink )
391 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
392
393 QgsMapSettings mapSettings;
394 mapSettings.setDestinationCrs( mCrs );
395 mapSettings.setExtent( extent );
396 mapSettings.setOutputSize( imageSize );
397 mapSettings.setOutputDpi( dpi );
398 mapSettings.setFlag( Qgis::MapSettingsFlag::DrawLabeling, true );
401 mapSettings.setLayers( mMapLayers );
402 mapSettings.setLayerStyleOverrides( mMapThemeStyleOverrides );
403 mapSettings.setLabelingEngineSettings( mLabelSettings );
404 mapSettings.setScaleMethod( mScaleMethod );
405
406 //build the expression context
407 QgsExpressionContext expressionContext;
408 expressionContext << QgsExpressionContextUtils::globalScope()
411 mapSettings.setExpressionContext( expressionContext );
412
413 QgsNullPaintDevice nullPaintDevice;
414 nullPaintDevice.setOutputSize( imageSize );
415 nullPaintDevice.setOutputDpi( static_cast<int>( std::round( dpi ) ) );
416 QPainter painter( &nullPaintDevice );
417
418 QgsMapRendererCustomPainterJob renderJob( mapSettings, &painter );
419 ExtractLabelSink labelSink( mMapLayerNames, feedback );
420 renderJob.setLabelSink( &labelSink );
421
422 feedback->pushInfo( QObject::tr( "Extracting labels" ) );
423
424 QgsProcessingMultiStepFeedback multiStepFeedback( 10, feedback );
425 multiStepFeedback.setCurrentStep( 0 );
426
427 QEventLoop loop;
428 QObject::connect( feedback, &QgsFeedback::canceled, &renderJob, &QgsMapRendererCustomPainterJob::cancel );
429 QObject::connect( &renderJob, &QgsMapRendererJob::renderingLayersFinished, feedback, [feedback]() { feedback->pushInfo( QObject::tr( "Calculating label placement" ) ); } );
430 int labelsCollectedFromLayers = 0;
431 QObject::connect( &renderJob, &QgsMapRendererJob::layerRenderingStarted, feedback, [this, &multiStepFeedback, &labelsCollectedFromLayers]( const QString &layerId ) {
432 multiStepFeedback.pushInfo( QObject::tr( "Collecting labelled features from %1" ).arg( mMapLayerNames.value( layerId ) ) );
433 multiStepFeedback.setProgress( 100.0 * static_cast<double>( labelsCollectedFromLayers ) / mMapLayers.size() );
434 labelsCollectedFromLayers++;
435 } );
436
437 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::labelRegistrationAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
438 multiStepFeedback.setCurrentStep( 1 );
439 multiStepFeedback.pushInfo( QObject::tr( "Registering labels" ) );
440 } );
441
442 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::providerRegistrationAboutToBegin, &multiStepFeedback, [this, &multiStepFeedback]( QgsAbstractLabelProvider *provider ) {
443 multiStepFeedback.setCurrentStep( 2 );
444 if ( !provider->layerId().isEmpty() )
445 {
446 multiStepFeedback.pushInfo( QObject::tr( "Adding labels from %1" ).arg( mMapLayerNames.value( provider->layerId() ) ) );
447 }
448 } );
449 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::candidateCreationAboutToBegin, &multiStepFeedback, [this, &multiStepFeedback]( QgsAbstractLabelProvider *provider ) {
450 multiStepFeedback.setCurrentStep( 3 );
451 if ( !provider->layerId().isEmpty() )
452 {
453 multiStepFeedback.pushInfo( QObject::tr( "Generating label placement candidates for %1" ).arg( mMapLayerNames.value( provider->layerId() ) ) );
454 }
455 } );
456 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::obstacleCostingAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
457 multiStepFeedback.setCurrentStep( 4 );
458 multiStepFeedback.setProgressText( QObject::tr( "Calculating obstacle costs" ) );
459 } );
460 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::calculatingConflictsAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
461 multiStepFeedback.setCurrentStep( 5 );
462 multiStepFeedback.setProgressText( QObject::tr( "Calculating label conflicts" ) );
463 } );
464 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::finalizingCandidatesAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
465 multiStepFeedback.setCurrentStep( 6 );
466 multiStepFeedback.setProgressText( QObject::tr( "Finalizing candidates" ) );
467 } );
468 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::reductionAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
469 multiStepFeedback.setCurrentStep( 7 );
470 multiStepFeedback.setProgressText( QObject::tr( "Reducing problem" ) );
471 } );
472 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::solvingPlacementAboutToBegin, &multiStepFeedback, [&multiStepFeedback]() {
473 multiStepFeedback.setCurrentStep( 8 );
474 multiStepFeedback.setProgressText( QObject::tr( "Determining optimal label placements" ) );
475 } );
476 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::solvingPlacementFinished, &multiStepFeedback, [&multiStepFeedback]() {
477 multiStepFeedback.setProgressText( QObject::tr( "Labeling complete" ) );
478 } );
479
480 QObject::connect( renderJob.labelingEngineFeedback(), &QgsLabelingEngineFeedback::progressChanged, &multiStepFeedback, [&multiStepFeedback]( double progress ) {
481 multiStepFeedback.setProgress( progress );
482 } );
483
484 QObject::connect( &renderJob, &QgsMapRendererJob::finished, &loop, [&loop]() { loop.exit(); } );
485 renderJob.start();
486 loop.exec();
487
488 qDeleteAll( mMapLayers );
489 mMapLayers.clear();
490
491 multiStepFeedback.setCurrentStep( 9 );
492 feedback->pushInfo( QObject::tr( "Writing %n label(s) to output layer", "", labelSink.features.count() ) );
493 const double step = !labelSink.features.empty() ? 100.0 / labelSink.features.count() : 1;
494 long long index = -1;
495 for ( QgsFeature &feature : labelSink.features )
496 {
497 index++;
498 multiStepFeedback.setProgress( step * index );
499 if ( feedback->isCanceled() )
500 break;
501
502 if ( !sink->addFeature( feature, QgsFeatureSink::FastInsert ) )
503 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
504 }
505 sink->finalize();
506 sink.reset();
507
508 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( dest, context ) ) )
509 {
510 vl->setRenderer( new QgsNullSymbolRenderer() );
511 if ( vl->renderer() )
512 {
513 vl->renderer()->setReferenceScale( scale );
514
515 QgsPalLayerSettings settings;
516 QgsPropertyCollection settingsProperties;
517
518 settings.fieldName = u"LabelText"_s;
519 settings.obstacleSettings().setIsObstacle( false );
524
525 QgsTextFormat textFormat;
526 textFormat.setSize( 9 );
528 textFormat.setColor( QColor( 0, 0, 0 ) );
529
530 QgsTextBufferSettings buffer = textFormat.buffer();
532
533 textFormat.setBuffer( buffer );
534 settings.setFormat( textFormat );
535
536 settingsProperties.setProperty( QgsPalLayerSettings::Property::Color, QgsProperty::fromExpression( u"if(\"LabelUnplaced\",'255,0,0',\"Color\")"_s ) );
537 settingsProperties.setProperty( QgsPalLayerSettings::Property::FontOpacity, QgsProperty::fromField( u"FontOpacity"_s ) );
541 settingsProperties.setProperty( QgsPalLayerSettings::Property::Underline, QgsProperty::fromField( u"Underline"_s ) );
543 settingsProperties.setProperty( QgsPalLayerSettings::Property::FontLetterSpacing, QgsProperty::fromField( u"FontLetterSpacing"_s ) );
544 settingsProperties.setProperty( QgsPalLayerSettings::Property::FontWordSpacing, QgsProperty::fromField( u"FontWordSpacing"_s ) );
545 settingsProperties.setProperty( QgsPalLayerSettings::Property::MultiLineAlignment, QgsProperty::fromField( u"MultiLineAlignment"_s ) );
546 settingsProperties.setProperty( QgsPalLayerSettings::Property::MultiLineHeight, QgsProperty::fromField( u"MultiLineHeight"_s ) );
547 settingsProperties.setProperty( QgsPalLayerSettings::Property::LabelRotation, QgsProperty::fromField( u"LabelRotation"_s ) );
548 settingsProperties.setProperty( QgsPalLayerSettings::Property::BufferDraw, QgsProperty::fromField( u"BufferDraw"_s ) );
549 settingsProperties.setProperty( QgsPalLayerSettings::Property::BufferSize, QgsProperty::fromField( u"BufferSize"_s ) );
550 settingsProperties.setProperty( QgsPalLayerSettings::Property::BufferColor, QgsProperty::fromField( u"BufferColor"_s ) );
551 settingsProperties.setProperty( QgsPalLayerSettings::Property::BufferOpacity, QgsProperty::fromField( u"BufferOpacity"_s ) );
552 settingsProperties.setProperty( QgsPalLayerSettings::Property::Show, QgsProperty::fromExpression( u"\"LabelUnplaced\"=false"_s ) );
553 settings.setDataDefinedProperties( settingsProperties );
554
556 vl->setLabeling( labeling );
557 vl->setLabelsEnabled( true );
558
559 QString errorMessage;
560 vl->saveStyleToDatabaseV2( QString(), QString(), true, QString(), errorMessage );
561 }
562 }
563
564 QVariantMap outputs;
565 outputs.insert( u"OUTPUT"_s, dest );
566 return outputs;
567}
568
569
570bool QgsExtractLabelsAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
571{
572 // Retrieve and clone layers
573 const QString mapTheme = parameterAsString( parameters, u"MAP_THEME"_s, context );
574 if ( !mapTheme.isEmpty() && context.project()->mapThemeCollection()->hasMapTheme( mapTheme ) )
575 {
576 const QList<QgsMapLayer *> constLayers = context.project()->mapThemeCollection()->mapThemeVisibleLayers( mapTheme );
577 for ( const QgsMapLayer *l : constLayers )
578 {
579 // only copy vector layers as other layer types aren't actors in the labeling process
580 if ( l->type() == Qgis::LayerType::Vector )
581 mMapLayers.push_back( l->clone() );
582 }
583 mMapThemeStyleOverrides = context.project()->mapThemeCollection()->mapThemeStyleOverrides( mapTheme );
584 }
585
586 if ( mMapLayers.isEmpty() )
587 {
588 QList<QgsMapLayer *> layers;
589 QgsLayerTree *root = context.project()->layerTreeRoot();
590 const QList<QgsLayerTreeLayer *> layerTreeLayers = root->findLayers();
591 layers.reserve( layerTreeLayers.size() );
592 for ( QgsLayerTreeLayer *nodeLayer : layerTreeLayers )
593 {
594 QgsMapLayer *layer = nodeLayer->layer();
595 if ( nodeLayer->isVisible() && root->layerOrder().contains( layer ) )
596 layers << layer;
597 }
598
599 for ( const QgsMapLayer *l : std::as_const( layers ) )
600 {
601 if ( l->type() == Qgis::LayerType::Vector )
602 mMapLayers.push_back( l->clone() );
603 }
604 }
605
606 for ( const QgsMapLayer *l : std::as_const( mMapLayers ) )
607 {
608 mMapLayerNames.insert( l->id(), l->name() );
609 }
610
611 mCrs = parameterAsExtentCrs( parameters, u"EXTENT"_s, context );
612 if ( !mCrs.isValid() )
613 mCrs = context.project()->crs();
614
615 bool includeUnplaced = parameterAsBoolean( parameters, u"INCLUDE_UNPLACED"_s, context );
616 mLabelSettings = context.project()->labelingEngineSettings();
617 mLabelSettings.setFlag( Qgis::LabelingFlag::DrawUnplacedLabels, includeUnplaced );
618 mLabelSettings.setFlag( Qgis::LabelingFlag::CollectUnplacedLabels, includeUnplaced );
619
620 mScaleMethod = context.project()->scaleMethod();
621
622 return true;
623}
624
625
@ VectorPoint
Vector point layers.
Definition qgis.h:3605
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition qgis.h:1227
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
Definition qgis.h:1229
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
Definition qgis.h:1226
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
Definition qgis.h:1228
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
Definition qgis.h:1231
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
Definition qgis.h:1232
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
Definition qgis.h:1230
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
Definition qgis.h:1233
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only.
Definition qgis.h:1234
@ AboveRight
Above right.
Definition qgis.h:1316
@ BelowLeft
Below left.
Definition qgis.h:1320
@ Above
Above center.
Definition qgis.h:1315
@ BelowRight
Below right.
Definition qgis.h:1322
@ Right
Right middle.
Definition qgis.h:1319
@ AboveLeft
Above left.
Definition qgis.h:1314
@ Below
Below center.
Definition qgis.h:1321
@ Over
Center middle.
Definition qgis.h:1318
@ CollectUnplacedLabels
Whether unplaced labels should be collected in the labeling results (regardless of whether they are b...
Definition qgis.h:2907
@ DrawUnplacedLabels
Whether to render unplaced labels as an indicator/warning for users.
Definition qgis.h:2906
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
Definition qgis.h:3690
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3680
LabelMultiLineAlignment
Text alignment for multi-line labels.
Definition qgis.h:1396
@ Center
Center align.
Definition qgis.h:1398
@ FollowPlacement
Alignment follows placement of label, e.g., labels to the left of a feature will be drawn with right ...
Definition qgis.h:1400
@ Vector
Vector layer.
Definition qgis.h:194
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5294
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5295
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3701
@ Point
Point.
Definition qgis.h:282
@ RequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
Definition qgis.h:3667
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3834
@ AllowOverlapIfRequired
Avoids overlapping labels when possible, but permit overlaps if labels for features cannot otherwise ...
Definition qgis.h:1188
@ Double
Double/float values.
Definition qgis.h:3875
@ UseRenderingOptimization
Enable vector simplification and other rendering optimizations.
Definition qgis.h:2780
@ DrawLabeling
Enable drawing of labels on top of the map.
Definition qgis.h:2779
@ SkipSymbolRendering
Disable symbol rendering while still drawing labels if enabled.
Definition qgis.h:2790
An abstract interface class for label providers.
Abstract base class - its implementations define different approaches to the labeling of a vector lay...
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:60
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:55
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:56
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:76
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:83
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.
Contains configuration for rendering maps.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setScaleMethod(Qgis::ScaleCalculationMethod method)
Sets the method to use for scale calculations for 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.
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...
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:122
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:119
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project's global labeling engine settings.
Qgis::ScaleCalculationMethod scaleMethod
Definition qgsproject.h:135
A grouped map of multiple QgsProperty objects, each referenced by an 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.
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.
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 dataset.
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Definition feature.cpp:169
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:6935
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features