Nicer memory handling when registering label features

This commit is contained in:
Nyall Dawson 2021-05-31 09:38:45 +10:00
parent d4ac6fc347
commit cf8b96ad44
7 changed files with 145 additions and 102 deletions

View File

@ -505,17 +505,20 @@ vertically oriented text will be written to ``rotatedLabelX`` and ``rotatedLabel
%End %End
void registerFeature( const QgsFeature &f, QgsRenderContext &context ); void registerFeature( const QgsFeature &f, QgsRenderContext &context );
%Docstring %Docstring
Register a feature for labeling. Registers a feature for labeling.
:param f: feature to label :param f: feature to label
:param context: render context. The :py:class:`QgsExpressionContext` contained within the render context :param context: render context. The :py:class:`QgsExpressionContext` contained within the render context
must have already had the feature and fields sets prior to calling this method. must have already had the feature and fields sets prior to calling this method.
:param labelFeature: if using :py:class:`QgsLabelingEngine`, this will receive the label feature. Not available
in Python bindings. .. warning::
This method is designed for use by PyQGIS clients only. C++ code should use the
variant with additional arguments.
%End %End
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ); void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
%Docstring %Docstring
Read settings from a DOM element Read settings from a DOM element

View File

@ -1670,11 +1670,13 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, const QSt
#endif #endif
} }
void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **labelFeature, QgsGeometry obstacleGeometry, const QgsSymbol *symbol ) void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext &context )
{ {
// either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngine (labelFeature is set) registerFeatureWithDetails( f, context, QgsGeometry(), nullptr );
Q_ASSERT( labelFeature ); }
std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails( const QgsFeature &f, QgsRenderContext &context, QgsGeometry obstacleGeometry, const QgsSymbol *symbol )
{
QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
mCurFeat = &f; mCurFeat = &f;
@ -1687,9 +1689,12 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
{ {
if ( isObstacle ) if ( isObstacle )
{ {
registerObstacleFeature( f, context, labelFeature, obstacleGeometry ); return registerObstacleFeature( f, context, obstacleGeometry );
}
else
{
return nullptr;
} }
return;
} }
QgsFeature feature = f; QgsFeature feature = f;
@ -1720,7 +1725,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
context.expressionContext().setOriginalValueVariable( true ); context.expressionContext().setOriginalValueVariable( true );
if ( !mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Show, context.expressionContext(), true ) ) if ( !mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Show, context.expressionContext(), true ) )
{ {
return; return nullptr;
} }
} }
@ -1747,7 +1752,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() < maxScale ) if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() < maxScale )
{ {
return; return nullptr;
} }
// data defined min scale? // data defined min scale?
@ -1766,7 +1771,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() > minScale ) if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() > minScale )
{ {
return; return nullptr;
} }
} }
@ -1797,14 +1802,14 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
} }
if ( fontSize <= 0.0 ) if ( fontSize <= 0.0 )
{ {
return; return nullptr;
} }
int fontPixelSize = QgsTextRenderer::sizeToPixel( fontSize, context, fontunits, mFormat.sizeMapUnitScale() ); int fontPixelSize = QgsTextRenderer::sizeToPixel( fontSize, context, fontunits, mFormat.sizeMapUnitScale() );
// don't try to show font sizes less than 1 pixel (Qt complains) // don't try to show font sizes less than 1 pixel (Qt complains)
if ( fontPixelSize < 1 ) if ( fontPixelSize < 1 )
{ {
return; return nullptr;
} }
labelFont.setPixelSize( fontPixelSize ); labelFont.setPixelSize( fontPixelSize );
@ -1820,7 +1825,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel ) if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
{ {
return; return nullptr;
} }
} }
} }
@ -1851,14 +1856,14 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
if ( exp->hasParserError() ) if ( exp->hasParserError() )
{ {
QgsDebugMsgLevel( QStringLiteral( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 ); QgsDebugMsgLevel( QStringLiteral( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
return; return nullptr;
} }
QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer() QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
if ( exp->hasEvalError() ) if ( exp->hasEvalError() )
{ {
QgsDebugMsgLevel( QStringLiteral( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 ); QgsDebugMsgLevel( QStringLiteral( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
return; return nullptr;
} }
labelText = result.isNull() ? QString() : result.toString(); labelText = result.isNull() ? QString() : result.toString();
} }
@ -2012,7 +2017,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
QgsGeometry geom = feature.geometry(); QgsGeometry geom = feature.geometry();
if ( geom.isNull() ) if ( geom.isNull() )
{ {
return; return nullptr;
} }
// simplify? // simplify?
@ -2132,7 +2137,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
geom = QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : QgsGeometry(), lineSettings.mergeLines() ); geom = QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : QgsGeometry(), lineSettings.mergeLines() );
if ( geom.isEmpty() ) if ( geom.isEmpty() )
return; return nullptr;
} }
geos_geom_clone = QgsGeos::asGeos( geom ); geos_geom_clone = QgsGeos::asGeos( geom );
@ -2158,12 +2163,12 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
else else
{ {
if ( !checkMinimumSizeMM( context, geom, featureThinningSettings.minimumFeatureSize() ) ) if ( !checkMinimumSizeMM( context, geom, featureThinningSettings.minimumFeatureSize() ) )
return; return nullptr;
} }
} }
if ( !geos_geom_clone ) if ( !geos_geom_clone )
return; // invalid geometry return nullptr; // invalid geometry
// likelihood exists label will be registered with PAL and may be drawn // likelihood exists label will be registered with PAL and may be drawn
// check if max number of features to label (already registered with PAL) has been reached // check if max number of features to label (already registered with PAL) has been reached
@ -2172,11 +2177,11 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
{ {
if ( !featureThinningSettings.maximumNumberLabels() ) if ( !featureThinningSettings.maximumNumberLabels() )
{ {
return; return nullptr;
} }
if ( mFeatsRegPal >= featureThinningSettings.maximumNumberLabels() ) if ( mFeatsRegPal >= featureThinningSettings.maximumNumberLabels() )
{ {
return; return nullptr;
} }
int divNum = static_cast< int >( ( static_cast< double >( mFeaturesToLabel ) / featureThinningSettings.maximumNumberLabels() ) + 0.5 ); // NOLINT int divNum = static_cast< int >( ( static_cast< double >( mFeaturesToLabel ) / featureThinningSettings.maximumNumberLabels() ) + 0.5 ); // NOLINT
@ -2185,7 +2190,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
mFeatsSendingToPal += 1; mFeatsSendingToPal += 1;
if ( divNum && mFeatsSendingToPal % divNum ) if ( divNum && mFeatsSendingToPal % divNum )
{ {
return; return nullptr;
} }
} }
} }
@ -2531,39 +2536,38 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
} }
// feature to the layer // feature to the layer
QgsTextLabelFeature *lf = new QgsTextLabelFeature( feature.id(), std::move( geos_geom_clone ), QSizeF( labelX, labelY ) ); std::unique_ptr< QgsTextLabelFeature > labelFeature = std::make_unique< QgsTextLabelFeature>( feature.id(), std::move( geos_geom_clone ), QSizeF( labelX, labelY ) );
lf->setAnchorPosition( anchorPosition ); labelFeature->setAnchorPosition( anchorPosition );
lf->setFeature( feature ); labelFeature->setFeature( feature );
lf->setSymbol( symbol ); labelFeature->setSymbol( symbol );
lf->setDocument( doc ); labelFeature->setDocument( doc );
if ( !qgsDoubleNear( rotatedLabelX, 0.0 ) && !qgsDoubleNear( rotatedLabelY, 0.0 ) ) if ( !qgsDoubleNear( rotatedLabelX, 0.0 ) && !qgsDoubleNear( rotatedLabelY, 0.0 ) )
lf->setRotatedSize( QSizeF( rotatedLabelX, rotatedLabelY ) ); labelFeature->setRotatedSize( QSizeF( rotatedLabelX, rotatedLabelY ) );
mFeatsRegPal++; mFeatsRegPal++;
*labelFeature = lf; labelFeature->setHasFixedPosition( hasDataDefinedPosition );
( *labelFeature )->setHasFixedPosition( hasDataDefinedPosition ); labelFeature->setFixedPosition( QgsPointXY( xPos, yPos ) );
( *labelFeature )->setFixedPosition( QgsPointXY( xPos, yPos ) );
// use layer-level defined rotation, but not if position fixed // use layer-level defined rotation, but not if position fixed
( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !hasDataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) ); labelFeature->setHasFixedAngle( dataDefinedRotation || ( !hasDataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
( *labelFeature )->setFixedAngle( angle ); labelFeature->setFixedAngle( angle );
( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) ); labelFeature->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
( *labelFeature )->setPositionOffset( QgsPointXY( offsetX, offsetY ) ); labelFeature->setPositionOffset( QgsPointXY( offsetX, offsetY ) );
( *labelFeature )->setOffsetType( offsetType ); labelFeature->setOffsetType( offsetType );
( *labelFeature )->setAlwaysShow( alwaysShow ); labelFeature->setAlwaysShow( alwaysShow );
( *labelFeature )->setRepeatDistance( repeatDist ); labelFeature->setRepeatDistance( repeatDist );
( *labelFeature )->setLabelText( labelText ); labelFeature->setLabelText( labelText );
( *labelFeature )->setPermissibleZone( permissibleZone ); labelFeature->setPermissibleZone( permissibleZone );
( *labelFeature )->setOverrunDistance( overrunDistanceEval ); labelFeature->setOverrunDistance( overrunDistanceEval );
( *labelFeature )->setOverrunSmoothDistance( overrunSmoothDist ); labelFeature->setOverrunSmoothDistance( overrunSmoothDist );
( *labelFeature )->setLineAnchorPercent( lineSettings.lineAnchorPercent() ); labelFeature->setLineAnchorPercent( lineSettings.lineAnchorPercent() );
( *labelFeature )->setLineAnchorType( lineSettings.anchorType() ); labelFeature->setLineAnchorType( lineSettings.anchorType() );
( *labelFeature )->setLabelAllParts( labelAll ); labelFeature->setLabelAllParts( labelAll );
( *labelFeature )->setOriginalFeatureCrs( context.coordinateTransform().sourceCrs() ); labelFeature->setOriginalFeatureCrs( context.coordinateTransform().sourceCrs() );
( *labelFeature )->setMinimumSize( minimumSize ); labelFeature->setMinimumSize( minimumSize );
if ( geom.type() == QgsWkbTypes::PointGeometry && !obstacleGeometry.isNull() ) if ( geom.type() == QgsWkbTypes::PointGeometry && !obstacleGeometry.isNull() )
{ {
//register symbol size //register symbol size
( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry.boundingBox().width(), labelFeature->setSymbolSize( QSizeF( obstacleGeometry.boundingBox().width(),
obstacleGeometry.boundingBox().height() ) ); obstacleGeometry.boundingBox().height() ) );
} }
@ -2573,15 +2577,15 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
double bottomMargin = 1.0 + labelFontMetrics->descent(); double bottomMargin = 1.0 + labelFontMetrics->descent();
QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin ); QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin );
vm *= xform->mapUnitsPerPixel(); vm *= xform->mapUnitsPerPixel();
( *labelFeature )->setVisualMargin( vm ); labelFeature->setVisualMargin( vm );
// store the label's calculated font for later use during painting // store the label's calculated font for later use during painting
QgsDebugMsgLevel( QStringLiteral( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 ); QgsDebugMsgLevel( QStringLiteral( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
lf->setDefinedFont( labelFont ); labelFeature->setDefinedFont( labelFont );
lf->setFontMetrics( *labelFontMetrics ); labelFeature->setFontMetrics( *labelFontMetrics );
lf->setMaximumCharacterAngleInside( std::clamp( maxcharanglein, 20.0, 60.0 ) * M_PI / 180 ); labelFeature->setMaximumCharacterAngleInside( std::clamp( maxcharanglein, 20.0, 60.0 ) * M_PI / 180 );
lf->setMaximumCharacterAngleOutside( std::clamp( maxcharangleout, -95.0, -20.0 ) * M_PI / 180 ); labelFeature->setMaximumCharacterAngleOutside( std::clamp( maxcharangleout, -95.0, -20.0 ) * M_PI / 180 );
switch ( placement ) switch ( placement )
{ {
case QgsPalLayerSettings::AroundPoint: case QgsPalLayerSettings::AroundPoint:
@ -2596,7 +2600,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
case QgsPalLayerSettings::Curved: case QgsPalLayerSettings::Curved:
case QgsPalLayerSettings::PerimeterCurved: case QgsPalLayerSettings::PerimeterCurved:
lf->setTextMetrics( QgsTextLabelFeature::calculateTextMetrics( xform, *labelFontMetrics, labelFont.letterSpacing(), labelFont.wordSpacing(), labelText, format().allowHtmlFormatting() ? &doc : nullptr ) ); labelFeature->setTextMetrics( QgsTextLabelFeature::calculateTextMetrics( xform, *labelFontMetrics, labelFont.letterSpacing(), labelFont.wordSpacing(), labelText, format().allowHtmlFormatting() ? &doc : nullptr ) );
break; break;
} }
@ -2654,17 +2658,17 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
if ( !qgsDoubleNear( distance, 0.0 ) ) if ( !qgsDoubleNear( distance, 0.0 ) )
{ {
double d = ptOne.distance( ptZero ) * distance; double d = ptOne.distance( ptZero ) * distance;
( *labelFeature )->setDistLabel( d ); labelFeature->setDistLabel( d );
} }
if ( ddFixedQuad ) if ( ddFixedQuad )
{ {
( *labelFeature )->setHasFixedQuadrant( true ); labelFeature->setHasFixedQuadrant( true );
} }
( *labelFeature )->setArrangementFlags( lineSettings.placementFlags() ); labelFeature->setArrangementFlags( lineSettings.placementFlags() );
( *labelFeature )->setPolygonPlacementFlags( polygonPlacement ); labelFeature->setPolygonPlacementFlags( polygonPlacement );
// data defined z-index? // data defined z-index?
double z = zIndex; double z = zIndex;
@ -2673,7 +2677,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
context.expressionContext().setOriginalValueVariable( z ); context.expressionContext().setOriginalValueVariable( z );
z = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::ZIndex, context.expressionContext(), z ); z = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::ZIndex, context.expressionContext(), z );
} }
( *labelFeature )->setZIndex( z ); labelFeature->setZIndex( z );
// data defined priority? // data defined priority?
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Priority ) ) if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Priority ) )
@ -2688,7 +2692,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
{ {
priorityD = std::clamp( priorityD, 0.0, 10.0 ); priorityD = std::clamp( priorityD, 0.0, 10.0 );
priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0 priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
( *labelFeature )->setPriority( priorityD ); labelFeature->setPriority( priorityD );
} }
} }
} }
@ -2697,7 +2701,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
os.setIsObstacle( isObstacle ); os.setIsObstacle( isObstacle );
os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() ); os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
os.setObstacleGeometry( obstacleGeometry ); os.setObstacleGeometry( obstacleGeometry );
lf->setObstacleSettings( os ); labelFeature->setObstacleSettings( os );
QVector< QgsPalLayerSettings::PredefinedPointPosition > positionOrder = predefinedPositionOrder; QVector< QgsPalLayerSettings::PredefinedPointPosition > positionOrder = predefinedPositionOrder;
if ( positionOrder.isEmpty() ) if ( positionOrder.isEmpty() )
@ -2712,13 +2716,15 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( dataDefinedOrder ); positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( dataDefinedOrder );
} }
} }
( *labelFeature )->setPredefinedPositionOrder( positionOrder ); labelFeature->setPredefinedPositionOrder( positionOrder );
// add parameters for data defined labeling to label feature // add parameters for data defined labeling to label feature
lf->setDataDefinedValues( dataDefinedValues ); labelFeature->setDataDefinedValues( dataDefinedValues );
return labelFeature;
} }
void QgsPalLayerSettings::registerObstacleFeature( const QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **obstacleFeature, const QgsGeometry &obstacleGeometry ) std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerObstacleFeature( const QgsFeature &f, QgsRenderContext &context, const QgsGeometry &obstacleGeometry )
{ {
mCurFeat = &f; mCurFeat = &f;
@ -2734,14 +2740,14 @@ void QgsPalLayerSettings::registerObstacleFeature( const QgsFeature &f, QgsRende
if ( geom.isNull() ) if ( geom.isNull() )
{ {
return; return nullptr;
} }
// don't even try to register linestrings with only one vertex as an obstacle // don't even try to register linestrings with only one vertex as an obstacle
if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( geom.constGet() ) ) if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( geom.constGet() ) )
{ {
if ( ls->numPoints() < 2 ) if ( ls->numPoints() < 2 )
return; return nullptr;
} }
// simplify? // simplify?
@ -2765,18 +2771,19 @@ void QgsPalLayerSettings::registerObstacleFeature( const QgsFeature &f, QgsRende
geos_geom_clone = QgsGeos::asGeos( geom ); geos_geom_clone = QgsGeos::asGeos( geom );
if ( !geos_geom_clone ) if ( !geos_geom_clone )
return; // invalid geometry return nullptr; // invalid geometry
// feature to the layer // feature to the layer
*obstacleFeature = new QgsLabelFeature( f.id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) ); std::unique_ptr< QgsLabelFeature > obstacleFeature = std::make_unique< QgsLabelFeature >( f.id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) );
( *obstacleFeature )->setFeature( f ); obstacleFeature->setFeature( f );
QgsLabelObstacleSettings os = mObstacleSettings; QgsLabelObstacleSettings os = mObstacleSettings;
os.setIsObstacle( true ); os.setIsObstacle( true );
os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() ); os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
( *obstacleFeature )->setObstacleSettings( os ); obstacleFeature->setObstacleSettings( os );
mFeatsRegPal++; mFeatsRegPal++;
return obstacleFeature;
} }
bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType, bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,

View File

@ -823,22 +823,37 @@ class CORE_EXPORT QgsPalLayerSettings
#endif #endif
/** /**
* Register a feature for labeling. * Registers a feature for labeling.
* \param f feature to label * \param f feature to label
* \param context render context. The QgsExpressionContext contained within the render context * \param context render context. The QgsExpressionContext contained within the render context
* must have already had the feature and fields sets prior to calling this method. * must have already had the feature and fields sets prior to calling this method.
* \param labelFeature if using QgsLabelingEngine, this will receive the label feature. Not available *
* in Python bindings. * \warning This method is designed for use by PyQGIS clients only. C++ code should use the
* variant with additional arguments.
*/
void registerFeature( const QgsFeature &f, QgsRenderContext &context );
#ifndef SIP_RUN
/**
* Registers a feature for labeling.
* \param feature feature to label
* \param context render context. The QgsExpressionContext contained within the render context
* must have already had the feature and fields set prior to calling this method.
* \param obstacleGeometry optional obstacle geometry, if a different geometry to the feature's geometry * \param obstacleGeometry optional obstacle geometry, if a different geometry to the feature's geometry
* should be used as an obstacle for labels (e.g., if the feature has been rendered with an offset point * should be used as an obstacle for labels (e.g., if the feature has been rendered with an offset point
* symbol, the obstacle geometry should represent the bounds of the offset symbol). If not set, * symbol, the obstacle geometry should represent the bounds of the offset symbol). If not set,
* the feature's original geometry will be used as an obstacle for labels. Not available * the feature's original geometry will be used as an obstacle for labels.
* in Python bindings.
* \param symbol feature symbol to label (ownership is not transferred, and \a symbol must exist until the labeling is complete) * \param symbol feature symbol to label (ownership is not transferred, and \a symbol must exist until the labeling is complete)
*
* \returns QgsLabelFeature representing the registered feature, or NULLPTR if the feature will not be labeled
* in this context.
*
* \note Not available in Python bindings
*/ */
void registerFeature( const QgsFeature &f, QgsRenderContext &context, std::unique_ptr< QgsLabelFeature > registerFeatureWithDetails( const QgsFeature &feature, QgsRenderContext &context,
QgsLabelFeature **labelFeature SIP_PYARGREMOVE = nullptr, QgsGeometry obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr );
QgsGeometry obstacleGeometry SIP_PYARGREMOVE = QgsGeometry(), const QgsSymbol *symbol SIP_PYARGREMOVE = nullptr ); #endif
/** /**
* Read settings from a DOM element * Read settings from a DOM element
@ -1090,7 +1105,7 @@ class CORE_EXPORT QgsPalLayerSettings
/** /**
* Registers a feature as an obstacle only (no label rendered) * Registers a feature as an obstacle only (no label rendered)
*/ */
void registerObstacleFeature( const QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **obstacleFeature, const QgsGeometry &obstacleGeometry = QgsGeometry() ); std::unique_ptr< QgsLabelFeature > registerObstacleFeature( const QgsFeature &f, QgsRenderContext &context, const QgsGeometry &obstacleGeometry = QgsGeometry() );
QMap<Property, QVariant> dataDefinedValues; QMap<Property, QVariant> dataDefinedValues;

View File

@ -38,10 +38,10 @@ bool QgsRuleBasedLabelProvider::prepare( QgsRenderContext &context, QSet<QString
return true; return true;
} }
void QgsRuleBasedLabelProvider::registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry, const QgsSymbol *symbol ) QList<QgsLabelFeature *> QgsRuleBasedLabelProvider::registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry, const QgsSymbol *symbol )
{ {
// will register the feature to relevant sub-providers // will register the feature to relevant sub-providers
mRules->rootRule()->registerFeature( feature, context, mSubProviders, obstacleGeometry, symbol ); return std::get< 1 >( mRules->rootRule()->registerFeature( feature, context, mSubProviders, obstacleGeometry, symbol ) );
} }
QList<QgsAbstractLabelProvider *> QgsRuleBasedLabelProvider::subProviders() QList<QgsAbstractLabelProvider *> QgsRuleBasedLabelProvider::subProviders()
@ -349,12 +349,13 @@ void QgsRuleBasedLabeling::Rule::prepare( QgsRenderContext &context, QSet<QStrin
} }
} }
QgsRuleBasedLabeling::Rule::RegisterResult QgsRuleBasedLabeling::Rule::registerFeature( const QgsFeature &feature, QgsRenderContext &context, QgsRuleBasedLabeling::RuleToProviderMap &subProviders, const QgsGeometry &obstacleGeometry, const QgsSymbol *symbol ) std::tuple< QgsRuleBasedLabeling::Rule::RegisterResult, QList< QgsLabelFeature * > > QgsRuleBasedLabeling::Rule::registerFeature( const QgsFeature &feature, QgsRenderContext &context, QgsRuleBasedLabeling::RuleToProviderMap &subProviders, const QgsGeometry &obstacleGeometry, const QgsSymbol *symbol )
{ {
QList< QgsLabelFeature * > labels;
if ( !isFilterOK( feature, context ) if ( !isFilterOK( feature, context )
|| !isScaleOK( context.rendererScale() ) ) || !isScaleOK( context.rendererScale() ) )
{ {
return Filtered; return { Filtered, labels };
} }
bool registered = false; bool registered = false;
@ -362,7 +363,7 @@ QgsRuleBasedLabeling::Rule::RegisterResult QgsRuleBasedLabeling::Rule::registerF
// do we have active subprovider for the rule? // do we have active subprovider for the rule?
if ( subProviders.contains( this ) && mIsActive ) if ( subProviders.contains( this ) && mIsActive )
{ {
subProviders[this]->registerFeature( feature, context, obstacleGeometry, symbol ); labels.append( subProviders[this]->registerFeature( feature, context, obstacleGeometry, symbol ) );
registered = true; registered = true;
} }
@ -374,7 +375,10 @@ QgsRuleBasedLabeling::Rule::RegisterResult QgsRuleBasedLabeling::Rule::registerF
// Don't process else rules yet // Don't process else rules yet
if ( !rule->isElse() ) if ( !rule->isElse() )
{ {
RegisterResult res = rule->registerFeature( feature, context, subProviders, obstacleGeometry ); RegisterResult res;
QList< QgsLabelFeature * > added;
std::tie( res, added ) = rule->registerFeature( feature, context, subProviders, obstacleGeometry );
labels.append( added );
// consider inactive items as "registered" so the else rule will ignore them // consider inactive items as "registered" so the else rule will ignore them
willRegisterSomething |= ( res == Registered || res == Inactive ); willRegisterSomething |= ( res == Registered || res == Inactive );
registered |= willRegisterSomething; registered |= willRegisterSomething;
@ -386,16 +390,20 @@ QgsRuleBasedLabeling::Rule::RegisterResult QgsRuleBasedLabeling::Rule::registerF
{ {
for ( Rule *rule : std::as_const( mElseRules ) ) for ( Rule *rule : std::as_const( mElseRules ) )
{ {
registered |= rule->registerFeature( feature, context, subProviders, obstacleGeometry, symbol ) != Filtered; RegisterResult res;
QList< QgsLabelFeature * > added;
std::tie( res, added ) = rule->registerFeature( feature, context, subProviders, obstacleGeometry, symbol ) ;
registered |= res != Filtered;
labels.append( added );
} }
} }
if ( !mIsActive ) if ( !mIsActive )
return Inactive; return { Inactive, labels };
else if ( registered ) else if ( registered )
return Registered; return { Registered, labels };
else else
return Filtered; return { Filtered, labels };
} }
bool QgsRuleBasedLabeling::Rule::isFilterOK( const QgsFeature &f, QgsRenderContext &context ) const bool QgsRuleBasedLabeling::Rule::isFilterOK( const QgsFeature &f, QgsRenderContext &context ) const

View File

@ -282,10 +282,15 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
void prepare( QgsRenderContext &context, QSet<QString> &attributeNames, RuleToProviderMap &subProviders ) SIP_SKIP; void prepare( QgsRenderContext &context, QSet<QString> &attributeNames, RuleToProviderMap &subProviders ) SIP_SKIP;
/** /**
* register individual features * Register individual features
*
* Returns result of registration, together with a list of all label features which
* were created as a result of registering \a feature. Ownership of these label features is not transferred
* (it has already been assigned to the label provider).
*
* \note not available in Python bindings * \note not available in Python bindings
*/ */
RegisterResult registerFeature( const QgsFeature &feature, QgsRenderContext &context, RuleToProviderMap &subProviders, const QgsGeometry &obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr ) SIP_SKIP; std::tuple< RegisterResult, QList< QgsLabelFeature * > > registerFeature( const QgsFeature &feature, QgsRenderContext &context, RuleToProviderMap &subProviders, const QgsGeometry &obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr ) SIP_SKIP;
/** /**
* Returns TRUE if this rule or any of its children requires advanced composition effects * Returns TRUE if this rule or any of its children requires advanced composition effects
@ -411,7 +416,7 @@ class CORE_EXPORT QgsRuleBasedLabelProvider : public QgsVectorLayerLabelProvider
bool prepare( QgsRenderContext &context, QSet<QString> &attributeNames ) override; bool prepare( QgsRenderContext &context, QSet<QString> &attributeNames ) override;
void registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr ) override; QList< QgsLabelFeature * > registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr ) override;
//! create a label provider //! create a label provider
virtual QgsVectorLayerLabelProvider *createProvider( QgsVectorLayer *layer, const QString &providerId, bool withFeatureLoop, const QgsPalLayerSettings *settings ); virtual QgsVectorLayerLabelProvider *createProvider( QgsVectorLayer *layer, const QString &providerId, bool withFeatureLoop, const QgsPalLayerSettings *settings );

View File

@ -190,13 +190,16 @@ QList<QgsLabelFeature *> QgsVectorLayerLabelProvider::labelFeatures( QgsRenderCo
return mLabels; return mLabels;
} }
void QgsVectorLayerLabelProvider::registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry, const QgsSymbol *symbol ) QList< QgsLabelFeature * > QgsVectorLayerLabelProvider::registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry, const QgsSymbol *symbol )
{ {
QgsLabelFeature *label = nullptr; std::unique_ptr< QgsLabelFeature > label = mSettings.registerFeatureWithDetails( feature, context, obstacleGeometry, symbol );
QList< QgsLabelFeature * > res;
mSettings.registerFeature( feature, context, &label, obstacleGeometry, symbol );
if ( label ) if ( label )
mLabels << label; {
res << label.get();
mLabels << label.release();
}
return res;
} }
QgsGeometry QgsVectorLayerLabelProvider::getPointObstacleGeometry( QgsFeature &fet, QgsRenderContext &context, const QgsSymbolList &symbols ) QgsGeometry QgsVectorLayerLabelProvider::getPointObstacleGeometry( QgsFeature &fet, QgsRenderContext &context, const QgsSymbolList &symbols )

View File

@ -88,8 +88,10 @@ class CORE_EXPORT QgsVectorLayerLabelProvider : public QgsAbstractLabelProvider
* symbol, the obstacle geometry should represent the bounds of the offset symbol). If not set, * symbol, the obstacle geometry should represent the bounds of the offset symbol). If not set,
* the feature's original geometry will be used as an obstacle for labels. * the feature's original geometry will be used as an obstacle for labels.
* \param symbol feature symbol to label (ownership is not transferred - the symbol must exist until after labeling is complete) * \param symbol feature symbol to label (ownership is not transferred - the symbol must exist until after labeling is complete)
* \returns a list of the newly generated label features. Ownership of these label features is not transferred
* (it has already been assigned to the label provider).
*/ */
virtual void registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr ); virtual QList< QgsLabelFeature * > registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr );
/** /**
* Returns the geometry for a point feature which should be used as an obstacle for labels. This * Returns the geometry for a point feature which should be used as an obstacle for labels. This