Skip to content
Permalink
Browse files

[FEATURE][layouts] Use QgsTextRenderer for drawing map grid text in l…

…ayouts

Allows for grid annotations which use buffers, shadows, background shapes, etc!
  • Loading branch information
nyalldawson committed Jul 8, 2020
1 parent 573e46b commit ff14c6fdadca9011641e7b0f5ce50e54eaf8b8c8
@@ -565,32 +565,62 @@ Returns whether annotations are shown for the grid.
.. seealso:: :py:func:`setAnnotationEnabled`
%End

void setAnnotationFont( const QFont &font );
void setAnnotationTextFormat( const QgsTextFormat &format );
%Docstring
Sets the text ``format`` to use when rendering grid annotations.

.. seealso:: :py:func:`annotationTextFormat`

.. versionadded:: 3.16
%End

QgsTextFormat annotationTextFormat() const;
%Docstring
Returns the text format used when rendering grid annotations.

.. seealso:: :py:func:`setAnnotationTextFormat`

.. versionadded:: 3.16
%End

void setAnnotationFont( const QFont &font ) /Deprecated/;
%Docstring
Sets the ``font`` used for drawing grid annotations.

.. seealso:: :py:func:`annotationFont`

.. deprecated::
use setAnnotationTextFormat() instead
%End

QFont annotationFont() const;
QFont annotationFont() const /Deprecated/;
%Docstring
Returns the font used for drawing grid annotations.

.. seealso:: :py:func:`setAnnotationFont`

.. deprecated::
use annotationTextFormat() instead
%End

void setAnnotationFontColor( const QColor &color );
void setAnnotationFontColor( const QColor &color ) /Deprecated/;
%Docstring
Sets the font ``color`` used for drawing grid annotations.

.. seealso:: :py:func:`annotationFontColor`

.. deprecated::
use setAnnotationTextFormat() instead
%End

QColor annotationFontColor() const;
QColor annotationFontColor() const /Deprecated/;
%Docstring
Returns the font color used for drawing grid annotations.

.. seealso:: :py:func:`setAnnotationFontColor`

.. deprecated::
use annotationTextFormat() instead
%End

void setAnnotationPrecision( const int precision );
@@ -984,8 +984,21 @@ bool QgsCompositionConverter::readMapXml( QgsLayoutItemMap *layoutItem, const QD
mapGrid->setAnnotationFrameDistance( annotationElem.attribute( QStringLiteral( "frameDistance" ), QStringLiteral( "0" ) ).toDouble() );
QFont annotationFont;
annotationFont.fromString( annotationElem.attribute( QStringLiteral( "font" ), QString() ) );
mapGrid->setAnnotationFont( annotationFont );
mapGrid->setAnnotationFontColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );

QgsTextFormat annotationFormat = mapGrid->annotationTextFormat();
annotationFormat.setFont( annotationFont );
if ( annotationFont.pointSizeF() > 0 )
{
annotationFormat.setSize( annotationFont.pointSizeF() );
annotationFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
}
else if ( annotationFont.pixelSize() > 0 )
{
annotationFormat.setSize( annotationFont.pixelSize() );
annotationFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
}
annotationFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
mapGrid->setAnnotationTextFormat( annotationFormat );

mapGrid->setAnnotationPrecision( annotationElem.attribute( QStringLiteral( "precision" ), QStringLiteral( "3" ) ).toInt() );
}
@@ -35,6 +35,7 @@
#include "qgssettings.h"
#include "qgscoordinateformatter.h"
#include "qgsstyleentityvisitor.h"
#include "qgstextrenderer.h"

#include <QPainter>
#include <QPen>
@@ -166,7 +167,9 @@ QgsLayoutItemMapGrid::QgsLayoutItemMapGrid( const QString &name, QgsLayoutItemMa
QString defaultFontString = settings.value( QStringLiteral( "LayoutDesigner/defaultFont" ), QVariant(), QgsSettings::Gui ).toString();
if ( !defaultFontString.isEmpty() )
{
mGridAnnotationFont.setFamily( defaultFontString );
QFont font;
font.setFamily( defaultFontString );
mAnnotationFormat.setFont( font );
}

createDefaultGridLineSymbol();
@@ -268,8 +271,7 @@ bool QgsLayoutItemMapGrid::writeXml( QDomElement &elem, QDomDocument &doc, const
mapGridElem.setAttribute( QStringLiteral( "topAnnotationDirection" ), mTopGridAnnotationDirection );
mapGridElem.setAttribute( QStringLiteral( "bottomAnnotationDirection" ), mBottomGridAnnotationDirection );
mapGridElem.setAttribute( QStringLiteral( "frameAnnotationDistance" ), QString::number( mAnnotationFrameDistance ) );
mapGridElem.appendChild( QgsFontUtils::toXmlElement( mGridAnnotationFont, doc, QStringLiteral( "annotationFontProperties" ) ) );
mapGridElem.setAttribute( QStringLiteral( "annotationFontColor" ), QgsSymbolLayerUtils::encodeColor( mGridAnnotationFontColor ) );
mapGridElem.appendChild( mAnnotationFormat.writeXml( doc, context ) );
mapGridElem.setAttribute( QStringLiteral( "annotationPrecision" ), mGridAnnotationPrecision );
mapGridElem.setAttribute( QStringLiteral( "unit" ), mGridUnit );
mapGridElem.setAttribute( QStringLiteral( "blendMode" ), mBlendMode );
@@ -364,11 +366,24 @@ bool QgsLayoutItemMapGrid::readXml( const QDomElement &itemElem, const QDomDocum
mTopGridAnnotationDirection = QgsLayoutItemMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "topAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
mBottomGridAnnotationDirection = QgsLayoutItemMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "bottomAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
mAnnotationFrameDistance = itemElem.attribute( QStringLiteral( "frameAnnotationDistance" ), QStringLiteral( "0" ) ).toDouble();
if ( !QgsFontUtils::setFromXmlChildNode( mGridAnnotationFont, itemElem, QStringLiteral( "annotationFontProperties" ) ) )

if ( !itemElem.firstChildElement( "text-style" ).isNull() )
{
mAnnotationFormat.readXml( itemElem, context );
}
else
{
mGridAnnotationFont.fromString( itemElem.attribute( QStringLiteral( "annotationFont" ), QString() ) );
QFont font;
if ( !QgsFontUtils::setFromXmlChildNode( font, itemElem, "annotationFontProperties" ) )
{
font.fromString( itemElem.attribute( "annotationFont", QString() ) );
}
mAnnotationFormat.setFont( font );
mAnnotationFormat.setSize( font.pointSizeF() );
mAnnotationFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
mAnnotationFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( "annotationFontColor", "0,0,0,255" ) ) );
}
mGridAnnotationFontColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "annotationFontColor" ), QStringLiteral( "0,0,0,255" ) ) );

mGridAnnotationPrecision = itemElem.attribute( QStringLiteral( "annotationPrecision" ), QStringLiteral( "3" ) ).toInt();
int gridUnitInt = itemElem.attribute( QStringLiteral( "unit" ), QString::number( MapUnit ) ).toInt();
mGridUnit = ( gridUnitInt <= static_cast< int >( DynamicPageSizeBased ) ) ? static_cast< GridUnit >( gridUnitInt ) : MapUnit;
@@ -585,6 +600,7 @@ void QgsLayoutItemMapGrid::draw( QPainter *p )
//setup render context
QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, p );
context.setForceVectorOutput( true );
context.setFlag( QgsRenderContext::ApplyScalingWorkaroundForTextRendering, true );
QgsExpressionContext expressionContext = createExpressionContext();
context.setExpressionContext( expressionContext );

@@ -624,7 +640,7 @@ void QgsLayoutItemMapGrid::draw( QPainter *p )

if ( mShowGridAnnotation )
{
drawCoordinateAnnotations( p, horizontalLines, verticalLines, context.expressionContext() );
drawCoordinateAnnotations( context, horizontalLines, verticalLines, context.expressionContext() );
}
}

@@ -1045,39 +1061,40 @@ void QgsLayoutItemMapGrid::drawGridFrameLineBorder( QPainter *p, QgsLayoutItemMa
}
}

void QgsLayoutItemMapGrid::drawCoordinateAnnotations( QPainter *p, const QList< QPair< double, QLineF > > &hLines, const QList< QPair< double, QLineF > > &vLines, QgsExpressionContext &expressionContext,
void QgsLayoutItemMapGrid::drawCoordinateAnnotations( QgsRenderContext &context, const QList< QPair< double, QLineF > > &hLines, const QList< QPair< double, QLineF > > &vLines, QgsExpressionContext &expressionContext,
GridExtension *extension ) const
{
QString currentAnnotationString;
QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
for ( ; it != hLines.constEnd(); ++it )
{
currentAnnotationString = gridAnnotationString( it->first, QgsLayoutItemMapGrid::Latitude, expressionContext );
drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsLayoutItemMapGrid::Latitude, extension );
drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsLayoutItemMapGrid::Latitude, extension );
drawCoordinateAnnotation( context, it->second.p1(), currentAnnotationString, QgsLayoutItemMapGrid::Latitude, extension );
drawCoordinateAnnotation( context, it->second.p2(), currentAnnotationString, QgsLayoutItemMapGrid::Latitude, extension );
}

it = vLines.constBegin();
for ( ; it != vLines.constEnd(); ++it )
{
currentAnnotationString = gridAnnotationString( it->first, QgsLayoutItemMapGrid::Longitude, expressionContext );
drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsLayoutItemMapGrid::Longitude, extension );
drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsLayoutItemMapGrid::Longitude, extension );
drawCoordinateAnnotation( context, it->second.p1(), currentAnnotationString, QgsLayoutItemMapGrid::Longitude, extension );
drawCoordinateAnnotation( context, it->second.p2(), currentAnnotationString, QgsLayoutItemMapGrid::Longitude, extension );
}
}

void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, const QString &annotationString, const AnnotationCoordinate coordinateType, GridExtension *extension ) const
void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QgsRenderContext &context, QPointF pos, const QString &annotationString, const AnnotationCoordinate coordinateType, GridExtension *extension ) const
{
if ( !mMap )
{
return;
}

QgsLayoutItemMapGrid::BorderSide frameBorder = borderForLineCoord( pos, coordinateType );
double textWidth = QgsLayoutUtils::textWidthMM( mGridAnnotationFont, annotationString );
double textWidth = QgsTextRenderer::textWidth( context, mAnnotationFormat, QStringList() << annotationString ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
//relevant for annotations is the height of digits
double textHeight = extension ? QgsLayoutUtils::fontAscentMM( mGridAnnotationFont )
: QgsLayoutUtils::fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
const QFontMetricsF metrics = QgsTextRenderer::fontMetrics( context, mAnnotationFormat, QgsTextRenderer::FONT_WORKAROUND_SCALE );
double textHeight = ( extension ? ( QgsTextRenderer::textHeight( context, mAnnotationFormat, QChar(), true ) )
: ( QgsTextRenderer::textHeight( context, mAnnotationFormat, '0', false ) ) ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
double xpos = pos.x();
double ypos = pos.y();
int rotation = 0;
@@ -1398,23 +1415,25 @@ void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, c
}
}

if ( extension || !p )
if ( extension || !context.painter() )
return;

drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
drawAnnotation( context, QPointF( xpos, ypos ), rotation, annotationString );
}

void QgsLayoutItemMapGrid::drawAnnotation( QPainter *p, QPointF pos, int rotation, const QString &annotationText ) const
void QgsLayoutItemMapGrid::drawAnnotation( QgsRenderContext &context, QPointF pos, int rotation, const QString &annotationText ) const
{
if ( !mMap )
{
return;
}

QgsScopedQPainterState painterState( p );
p->translate( pos );
p->rotate( rotation );
QgsLayoutUtils::drawText( p, QPointF( 0, 0 ), annotationText, mGridAnnotationFont, mGridAnnotationFontColor );
QgsScopedQPainterState painterState( context.painter() );
context.painter()->translate( pos );
context.painter()->rotate( rotation );

QgsScopedRenderContextScaleToPixels scale( context );
QgsTextRenderer::drawText( QPointF( 0, 0 ), 0, QgsTextRenderer::AlignLeft, QStringList() << annotationText, context, mAnnotationFormat );
}

QString QgsLayoutItemMapGrid::gridAnnotationString( double value, QgsLayoutItemMapGrid::AnnotationCoordinate coord, QgsExpressionContext &expressionContext ) const
@@ -2122,6 +2141,36 @@ QgsMarkerSymbol *QgsLayoutItemMapGrid::markerSymbol()
return mGridMarkerSymbol.get();
}

void QgsLayoutItemMapGrid::setAnnotationFont( const QFont &font )
{
mAnnotationFormat.setFont( font );
if ( font.pointSizeF() > 0 )
{
mAnnotationFormat.setSize( font.pointSizeF() );
mAnnotationFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
}
else if ( font.pixelSize() > 0 )
{
mAnnotationFormat.setSize( font.pixelSize() );
mAnnotationFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
}
}

QFont QgsLayoutItemMapGrid::annotationFont() const
{
return mAnnotationFormat.toQFont();
}

void QgsLayoutItemMapGrid::setAnnotationFontColor( const QColor &color )
{
mAnnotationFormat.setColor( color );
}

QColor QgsLayoutItemMapGrid::annotationFontColor() const
{
return mAnnotationFormat.color();
}

void QgsLayoutItemMapGrid::setAnnotationDisplay( const QgsLayoutItemMapGrid::DisplayMode display, const QgsLayoutItemMapGrid::BorderSide border )
{
switch ( border )
@@ -2220,7 +2269,7 @@ void QgsLayoutItemMapGrid::calculateMaxExtension( double &top, double &right, do

if ( mShowGridAnnotation )
{
drawCoordinateAnnotations( nullptr, horizontalLines, verticalLines, context.expressionContext(), &extension );
drawCoordinateAnnotations( context, horizontalLines, verticalLines, context.expressionContext(), &extension );
}

top = extension.top;

0 comments on commit ff14c6f

Please sign in to comment.
You can’t perform that action at this time.