Skip to content

Commit

Permalink
Nicer memory handling when registering label features
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jun 21, 2021
1 parent d4ac6fc commit cf8b96a
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 102 deletions.
13 changes: 8 additions & 5 deletions python/core/auto_generated/labeling/qgspallabeling.sip.in
Expand Up @@ -504,18 +504,21 @@ If the text orientation is set to rotation-based, the spaced taken to render
vertically oriented text will be written to ``rotatedLabelX`` and ``rotatedLabelY`` .
%End

void registerFeature( const QgsFeature &f, QgsRenderContext &context );

void registerFeature( const QgsFeature &f, QgsRenderContext &context );
%Docstring
Register a feature for labeling.
Registers a feature for labeling.

:param f: feature to label
: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.
: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


void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
%Docstring
Read settings from a DOM element
Expand Down
145 changes: 76 additions & 69 deletions src/core/labeling/qgspallabeling.cpp
Expand Up @@ -1670,11 +1670,13 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, const QSt
#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)
Q_ASSERT( labelFeature );
registerFeatureWithDetails( f, context, QgsGeometry(), nullptr );
}

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
mCurFeat = &f;

Expand All @@ -1687,9 +1689,12 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
{
if ( isObstacle )
{
registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
return registerObstacleFeature( f, context, obstacleGeometry );
}
else
{
return nullptr;
}
return;
}

QgsFeature feature = f;
Expand Down Expand Up @@ -1720,7 +1725,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
context.expressionContext().setOriginalValueVariable( true );
if ( !mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Show, context.expressionContext(), true ) )
{
return;
return nullptr;
}
}

Expand All @@ -1747,7 +1752,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext

if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() < maxScale )
{
return;
return nullptr;
}

// data defined min scale?
Expand All @@ -1766,7 +1771,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext

if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() > minScale )
{
return;
return nullptr;
}
}

Expand Down Expand Up @@ -1797,14 +1802,14 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
}
if ( fontSize <= 0.0 )
{
return;
return nullptr;
}

int fontPixelSize = QgsTextRenderer::sizeToPixel( fontSize, context, fontunits, mFormat.sizeMapUnitScale() );
// don't try to show font sizes less than 1 pixel (Qt complains)
if ( fontPixelSize < 1 )
{
return;
return nullptr;
}
labelFont.setPixelSize( fontPixelSize );

Expand All @@ -1820,7 +1825,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext

if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
{
return;
return nullptr;
}
}
}
Expand Down Expand Up @@ -1851,14 +1856,14 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
if ( exp->hasParserError() )
{
QgsDebugMsgLevel( QStringLiteral( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
return;
return nullptr;
}

QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
if ( exp->hasEvalError() )
{
QgsDebugMsgLevel( QStringLiteral( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
return;
return nullptr;
}
labelText = result.isNull() ? QString() : result.toString();
}
Expand Down Expand Up @@ -2012,7 +2017,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
QgsGeometry geom = feature.geometry();
if ( geom.isNull() )
{
return;
return nullptr;
}

// simplify?
Expand Down Expand Up @@ -2132,7 +2137,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
geom = QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : QgsGeometry(), lineSettings.mergeLines() );

if ( geom.isEmpty() )
return;
return nullptr;
}
geos_geom_clone = QgsGeos::asGeos( geom );

Expand All @@ -2158,12 +2163,12 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
else
{
if ( !checkMinimumSizeMM( context, geom, featureThinningSettings.minimumFeatureSize() ) )
return;
return nullptr;
}
}

if ( !geos_geom_clone )
return; // invalid geometry
return nullptr; // invalid geometry

// 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
Expand All @@ -2172,11 +2177,11 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
{
if ( !featureThinningSettings.maximumNumberLabels() )
{
return;
return nullptr;
}
if ( mFeatsRegPal >= featureThinningSettings.maximumNumberLabels() )
{
return;
return nullptr;
}

int divNum = static_cast< int >( ( static_cast< double >( mFeaturesToLabel ) / featureThinningSettings.maximumNumberLabels() ) + 0.5 ); // NOLINT
Expand All @@ -2185,7 +2190,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
mFeatsSendingToPal += 1;
if ( divNum && mFeatsSendingToPal % divNum )
{
return;
return nullptr;
}
}
}
Expand Down Expand Up @@ -2531,40 +2536,39 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
}

// feature to the layer
QgsTextLabelFeature *lf = new QgsTextLabelFeature( feature.id(), std::move( geos_geom_clone ), QSizeF( labelX, labelY ) );
lf->setAnchorPosition( anchorPosition );
lf->setFeature( feature );
lf->setSymbol( symbol );
lf->setDocument( doc );
std::unique_ptr< QgsTextLabelFeature > labelFeature = std::make_unique< QgsTextLabelFeature>( feature.id(), std::move( geos_geom_clone ), QSizeF( labelX, labelY ) );
labelFeature->setAnchorPosition( anchorPosition );
labelFeature->setFeature( feature );
labelFeature->setSymbol( symbol );
labelFeature->setDocument( doc );
if ( !qgsDoubleNear( rotatedLabelX, 0.0 ) && !qgsDoubleNear( rotatedLabelY, 0.0 ) )
lf->setRotatedSize( QSizeF( rotatedLabelX, rotatedLabelY ) );
labelFeature->setRotatedSize( QSizeF( rotatedLabelX, rotatedLabelY ) );
mFeatsRegPal++;

*labelFeature = lf;
( *labelFeature )->setHasFixedPosition( hasDataDefinedPosition );
( *labelFeature )->setFixedPosition( QgsPointXY( xPos, yPos ) );
labelFeature->setHasFixedPosition( hasDataDefinedPosition );
labelFeature->setFixedPosition( QgsPointXY( xPos, yPos ) );
// use layer-level defined rotation, but not if position fixed
( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !hasDataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
( *labelFeature )->setFixedAngle( angle );
( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
( *labelFeature )->setPositionOffset( QgsPointXY( offsetX, offsetY ) );
( *labelFeature )->setOffsetType( offsetType );
( *labelFeature )->setAlwaysShow( alwaysShow );
( *labelFeature )->setRepeatDistance( repeatDist );
( *labelFeature )->setLabelText( labelText );
( *labelFeature )->setPermissibleZone( permissibleZone );
( *labelFeature )->setOverrunDistance( overrunDistanceEval );
( *labelFeature )->setOverrunSmoothDistance( overrunSmoothDist );
( *labelFeature )->setLineAnchorPercent( lineSettings.lineAnchorPercent() );
( *labelFeature )->setLineAnchorType( lineSettings.anchorType() );
( *labelFeature )->setLabelAllParts( labelAll );
( *labelFeature )->setOriginalFeatureCrs( context.coordinateTransform().sourceCrs() );
( *labelFeature )->setMinimumSize( minimumSize );
labelFeature->setHasFixedAngle( dataDefinedRotation || ( !hasDataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
labelFeature->setFixedAngle( angle );
labelFeature->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
labelFeature->setPositionOffset( QgsPointXY( offsetX, offsetY ) );
labelFeature->setOffsetType( offsetType );
labelFeature->setAlwaysShow( alwaysShow );
labelFeature->setRepeatDistance( repeatDist );
labelFeature->setLabelText( labelText );
labelFeature->setPermissibleZone( permissibleZone );
labelFeature->setOverrunDistance( overrunDistanceEval );
labelFeature->setOverrunSmoothDistance( overrunSmoothDist );
labelFeature->setLineAnchorPercent( lineSettings.lineAnchorPercent() );
labelFeature->setLineAnchorType( lineSettings.anchorType() );
labelFeature->setLabelAllParts( labelAll );
labelFeature->setOriginalFeatureCrs( context.coordinateTransform().sourceCrs() );
labelFeature->setMinimumSize( minimumSize );
if ( geom.type() == QgsWkbTypes::PointGeometry && !obstacleGeometry.isNull() )
{
//register symbol size
( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry.boundingBox().width(),
obstacleGeometry.boundingBox().height() ) );
labelFeature->setSymbolSize( QSizeF( obstacleGeometry.boundingBox().width(),
obstacleGeometry.boundingBox().height() ) );
}

//set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
Expand All @@ -2573,15 +2577,15 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
double bottomMargin = 1.0 + labelFontMetrics->descent();
QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin );
vm *= xform->mapUnitsPerPixel();
( *labelFeature )->setVisualMargin( vm );
labelFeature->setVisualMargin( vm );

// 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 );
lf->setDefinedFont( labelFont );
lf->setFontMetrics( *labelFontMetrics );
labelFeature->setDefinedFont( labelFont );
labelFeature->setFontMetrics( *labelFontMetrics );

lf->setMaximumCharacterAngleInside( std::clamp( maxcharanglein, 20.0, 60.0 ) * M_PI / 180 );
lf->setMaximumCharacterAngleOutside( std::clamp( maxcharangleout, -95.0, -20.0 ) * M_PI / 180 );
labelFeature->setMaximumCharacterAngleInside( std::clamp( maxcharanglein, 20.0, 60.0 ) * M_PI / 180 );
labelFeature->setMaximumCharacterAngleOutside( std::clamp( maxcharangleout, -95.0, -20.0 ) * M_PI / 180 );
switch ( placement )
{
case QgsPalLayerSettings::AroundPoint:
Expand All @@ -2596,7 +2600,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext

case QgsPalLayerSettings::Curved:
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;
}

Expand Down Expand Up @@ -2654,17 +2658,17 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
if ( !qgsDoubleNear( distance, 0.0 ) )
{
double d = ptOne.distance( ptZero ) * distance;
( *labelFeature )->setDistLabel( d );
labelFeature->setDistLabel( d );
}

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?
double z = zIndex;
Expand All @@ -2673,7 +2677,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
context.expressionContext().setOriginalValueVariable( z );
z = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::ZIndex, context.expressionContext(), z );
}
( *labelFeature )->setZIndex( z );
labelFeature->setZIndex( z );

// data defined priority?
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Priority ) )
Expand All @@ -2688,7 +2692,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
{
priorityD = std::clamp( priorityD, 0.0, 10.0 );
priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
( *labelFeature )->setPriority( priorityD );
labelFeature->setPriority( priorityD );
}
}
}
Expand All @@ -2697,7 +2701,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
os.setIsObstacle( isObstacle );
os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
os.setObstacleGeometry( obstacleGeometry );
lf->setObstacleSettings( os );
labelFeature->setObstacleSettings( os );

QVector< QgsPalLayerSettings::PredefinedPointPosition > positionOrder = predefinedPositionOrder;
if ( positionOrder.isEmpty() )
Expand All @@ -2712,13 +2716,15 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( dataDefinedOrder );
}
}
( *labelFeature )->setPredefinedPositionOrder( positionOrder );
labelFeature->setPredefinedPositionOrder( positionOrder );

// 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;

Expand All @@ -2734,14 +2740,14 @@ void QgsPalLayerSettings::registerObstacleFeature( const QgsFeature &f, QgsRende

if ( geom.isNull() )
{
return;
return nullptr;
}

// 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 ( ls->numPoints() < 2 )
return;
return nullptr;
}

// simplify?
Expand All @@ -2765,18 +2771,19 @@ void QgsPalLayerSettings::registerObstacleFeature( const QgsFeature &f, QgsRende
geos_geom_clone = QgsGeos::asGeos( geom );

if ( !geos_geom_clone )
return; // invalid geometry
return nullptr; // invalid geometry

// feature to the layer
*obstacleFeature = new QgsLabelFeature( f.id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) );
( *obstacleFeature )->setFeature( f );
std::unique_ptr< QgsLabelFeature > obstacleFeature = std::make_unique< QgsLabelFeature >( f.id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) );
obstacleFeature->setFeature( f );

QgsLabelObstacleSettings os = mObstacleSettings;
os.setIsObstacle( true );
os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
( *obstacleFeature )->setObstacleSettings( os );
obstacleFeature->setObstacleSettings( os );

mFeatsRegPal++;
return obstacleFeature;
}

bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
Expand Down

0 comments on commit cf8b96a

Please sign in to comment.