Skip to content

Commit

Permalink
Merge pull request #38524 from olivierdalang/rotated_ticks_followup
Browse files Browse the repository at this point in the history
[followup] Support for rotated ticks/annotation
  • Loading branch information
m-kuhn committed Sep 10, 2020
2 parents 8b7f600 + 27bfb9e commit 509c5e1
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 14 deletions.
60 changes: 60 additions & 0 deletions python/core/auto_generated/layout/qgslayoutitemmapgrid.sip.in
Expand Up @@ -864,83 +864,143 @@ framePenSize method.
Enable/disable ticks rotation for rotated or reprojected grids.

.. seealso:: :py:func:`rotatedTicksEnabled`

.. versionadded:: 3.16
%End

double rotatedTicksEnabled() const;
%Docstring
Gets whether ticks rotation for rotated or reprojected grids is enabled.

.. seealso:: :py:func:`setRotatedTicksEnabled`

.. versionadded:: 3.16
%End

void setRotatedTicksLengthMode( const TickLengthMode mode );
%Docstring
Sets the tick length calculation mode.

.. seealso:: :py:func:`rotatedTicksLengthMode`

.. versionadded:: 3.16
%End

TickLengthMode rotatedTicksLengthMode() const;
%Docstring
Returns the grid frame style.

.. seealso:: :py:func:`setRotatedTicksLengthMode`

.. versionadded:: 3.16
%End

void setRotatedTicksMinimumAngle( const double angle );
%Docstring
Sets the ``minimum`` angle (in degrees) below which ticks are not drawn.

.. seealso:: :py:func:`rotatedTicksMinimumAngle`

.. versionadded:: 3.16
%End

double rotatedTicksMinimumAngle() const;
%Docstring
Gets the ``minimum`` angle (in degrees) below which ticks are not drawn.

.. seealso:: :py:func:`setRotatedTicksMinimumAngle`

.. versionadded:: 3.16
%End

void setRotatedTicksMarginToCorner( const double margin );
%Docstring
Sets the ``margin`` to corners (in canvas units) below which outwards facing ticks are not drawn.

.. seealso:: :py:func:`rotatedTicksMarginToCorner`

.. versionadded:: 3.16
%End

double rotatedTicksMarginToCorner() const;
%Docstring
Gets the ``margin`` to corners (in canvas units) below which outwards facing ticks are not drawn.

.. seealso:: :py:func:`setRotatedTicksMarginToCorner`

.. versionadded:: 3.16
%End

void setRotatedAnnotationsEnabled( const bool state );
%Docstring
Enable/disable annotations rotation for rotated or reprojected grids.

.. seealso:: :py:func:`rotatedAnnotationsEnabled`

.. versionadded:: 3.16
%End

double rotatedAnnotationsEnabled() const;
%Docstring
Gets whether annotations rotation for rotated or reprojected grids is enabled.

.. seealso:: :py:func:`setRotatedAnnotationsEnabled`

.. versionadded:: 3.16
%End

void setRotatedAnnotationsLengthMode( const TickLengthMode mode );
%Docstring
Sets the annotation length calculation mode.

.. seealso:: :py:func:`rotatedAnnotationsLengthMode`

.. versionadded:: 3.16
%End

TickLengthMode rotatedAnnotationsLengthMode() const;
%Docstring
Returns the grid frame style.

.. seealso:: :py:func:`setRotatedAnnotationsLengthMode`

.. versionadded:: 3.16
%End

void setRotatedAnnotationsMinimumAngle( const double angle );
%Docstring
Sets the ``minimum`` angle (in degrees) below which annotated are not drawn.

.. seealso:: :py:func:`rotatedAnnotationsMinimumAngle`

.. versionadded:: 3.16
%End

double rotatedAnnotationsMinimumAngle() const;
%Docstring
Gets the ``minimum`` angle (in degrees) below which annotated are not drawn.

.. seealso:: :py:func:`setRotatedAnnotationsMinimumAngle`

.. versionadded:: 3.16
%End

void setRotatedAnnotationsMarginToCorner( const double margin );
%Docstring
Sets the ``margin`` to corners (in canvas units) below which outwards facing ticks are not drawn.

.. seealso:: :py:func:`rotatedAnnotationsMarginToCorner`

.. versionadded:: 3.16
%End

double rotatedAnnotationsMarginToCorner() const;
%Docstring
Gets the ``margin`` to corners (in canvas units) below which outwards facing ticks are not drawn.

.. seealso:: :py:func:`setRotatedAnnotationsMarginToCorner`

.. versionadded:: 3.16
%End

void setFrameMargin( const double margin );
Expand Down
67 changes: 61 additions & 6 deletions src/core/layout/qgslayoutitemmapgrid.cpp
Expand Up @@ -278,9 +278,11 @@ bool QgsLayoutItemMapGrid::writeXml( QDomElement &elem, QDomDocument &doc, const
mapGridElem.setAttribute( QStringLiteral( "rotatedTicksLengthMode" ), mRotatedTicksLengthMode );
mapGridElem.setAttribute( QStringLiteral( "rotatedTicksEnabled" ), mRotatedTicksEnabled );
mapGridElem.setAttribute( QStringLiteral( "rotatedTicksMinimumAngle" ), QString::number( mRotatedTicksMinimumAngle ) );
mapGridElem.setAttribute( QStringLiteral( "rotatedTicksMarginToCorner" ), QString::number( mRotatedTicksMarginToCorner ) );
mapGridElem.setAttribute( QStringLiteral( "rotatedAnnotationsLengthMode" ), mRotatedAnnotationsLengthMode );
mapGridElem.setAttribute( QStringLiteral( "rotatedAnnotationsEnabled" ), mRotatedAnnotationsEnabled );
mapGridElem.setAttribute( QStringLiteral( "rotatedAnnotationsMinimumAngle" ), QString::number( mRotatedAnnotationsMinimumAngle ) );
mapGridElem.setAttribute( QStringLiteral( "rotatedAnnotationsMarginToCorner" ), QString::number( mRotatedAnnotationsMarginToCorner ) );
if ( mCRS.isValid() )
{
mCRS.writeXml( mapGridElem, doc );
Expand Down Expand Up @@ -346,9 +348,11 @@ bool QgsLayoutItemMapGrid::readXml( const QDomElement &itemElem, const QDomDocum
mRotatedTicksLengthMode = TickLengthMode( itemElem.attribute( QStringLiteral( "rotatedTicksLengthMode" ), QStringLiteral( "0" ) ).toInt() );
mRotatedTicksEnabled = itemElem.attribute( QStringLiteral( "rotatedTicksEnabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" );
mRotatedTicksMinimumAngle = itemElem.attribute( QStringLiteral( "rotatedTicksMinimumAngle" ), QStringLiteral( "0" ) ).toDouble();
mRotatedTicksMarginToCorner = itemElem.attribute( QStringLiteral( "rotatedTicksMarginToCorner" ), QStringLiteral( "0" ) ).toDouble();
mRotatedAnnotationsLengthMode = TickLengthMode( itemElem.attribute( QStringLiteral( "rotatedAnnotationsLengthMode" ), QStringLiteral( "0" ) ).toInt() );
mRotatedAnnotationsEnabled = itemElem.attribute( QStringLiteral( "rotatedAnnotationsEnabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" );
mRotatedAnnotationsMinimumAngle = itemElem.attribute( QStringLiteral( "rotatedAnnotationsMinimumAngle" ), QStringLiteral( "0" ) ).toDouble();
mRotatedAnnotationsMarginToCorner = itemElem.attribute( QStringLiteral( "rotatedAnnotationsMarginToCorner" ), QStringLiteral( "0" ) ).toDouble();

QDomElement lineStyleElem = itemElem.firstChildElement( QStringLiteral( "lineStyle" ) );
if ( !lineStyleElem.isNull() )
Expand Down Expand Up @@ -666,9 +670,9 @@ void QgsLayoutItemMapGrid::updateGridLinesAnnotationsPositions() const
it->startAnnotation.vector = QVector2D( it->line.at( 1 ) - it->line.first() ).normalized();
it->endAnnotation.vector = QVector2D( it->line.at( it->line.count() - 2 ) - it->line.last() ).normalized();
QVector2D normS = borderToNormal2D( it->startAnnotation.border );
it->startAnnotation.angle = abs( M_PI / 2.0 - acos( QVector2D::dotProduct( it->startAnnotation.vector, normS ) / ( it->startAnnotation.vector.length() * normS.length() ) ) );
it->startAnnotation.angle = atan2( it->startAnnotation.vector.x() * normS.y() - it->startAnnotation.vector.y() * normS.x(), it->startAnnotation.vector.x() * normS.x() + it->startAnnotation.vector.y() * normS.y() );
QVector2D normE = borderToNormal2D( it->endAnnotation.border );
it->endAnnotation.angle = abs( M_PI / 2.0 - acos( QVector2D::dotProduct( it->endAnnotation.vector, normE ) / ( it->endAnnotation.vector.length() * normE.length() ) ) );
it->endAnnotation.angle = atan2( it->endAnnotation.vector.x() * normE.y() - it->endAnnotation.vector.y() * normE.x(), it->endAnnotation.vector.x() * normE.x() + it->endAnnotation.vector.y() * normE.y() );
}
}

Expand Down Expand Up @@ -1013,7 +1017,39 @@ void QgsLayoutItemMapGrid::drawGridFrameTicks( QPainter *p, GridExtension *exten
continue;

// If the angle is below the threshold, we don't draw the annotation
if ( annot.angle < mRotatedTicksMinimumAngle * M_PI / 180.0 )
if ( abs( annot.angle ) / M_PI * 180.0 > 90.0 - mRotatedTicksMinimumAngle )
continue;

// Skip outwards facing annotations that are below mRotatedTicksMarginToCorner
bool facingLeft;
bool facingRight;
if ( mGridFrameStyle == QgsLayoutItemMapGrid::InteriorExteriorTicks )
{
facingLeft = ( annot.angle != 0 );
facingRight = ( annot.angle != 0 );
}
else if ( mGridFrameStyle == QgsLayoutItemMapGrid::ExteriorTicks )
{
facingLeft = ( annot.angle > 0 );
facingRight = ( annot.angle < 0 );
}
else
{
facingLeft = ( annot.angle < 0 );
facingRight = ( annot.angle > 0 );
}

if ( annot.border == BorderSide::Top && ( ( facingLeft && annot.position.x() < mRotatedTicksMarginToCorner ) ||
( facingRight && annot.position.x() > mMap->rect().width() - mRotatedTicksMarginToCorner ) ) )
continue;
if ( annot.border == BorderSide::Bottom && ( ( facingLeft && annot.position.x() > mMap->rect().width() - mRotatedTicksMarginToCorner ) ||
( facingRight && annot.position.x() < mRotatedTicksMarginToCorner ) ) )
continue;
if ( annot.border == BorderSide::Left && ( ( facingLeft && annot.position.y() > mMap->rect().height() - mRotatedTicksMarginToCorner ) ||
( facingRight && annot.position.y() < mRotatedTicksMarginToCorner ) ) )
continue;
if ( annot.border == BorderSide::Right && ( ( facingLeft && annot.position.y() < mRotatedTicksMarginToCorner ) ||
( facingRight && annot.position.y() > mMap->rect().height() - mRotatedTicksMarginToCorner ) ) )
continue;

QVector2D normalVector = borderToNormal2D( annot.border );
Expand Down Expand Up @@ -1179,9 +1215,8 @@ void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QgsRenderContext &context,
AnnotationPosition anotPos = annotationPosition( frameBorder );
AnnotationDirection anotDir = annotationDirection( frameBorder );


// If the angle is below the threshold, we don't draw the annotation
if ( annot.angle < mRotatedAnnotationsMinimumAngle * M_PI / 180.0 )
if ( abs( annot.angle ) / M_PI * 180.0 > 90.0 - mRotatedAnnotationsMinimumAngle )
return;

QVector2D normalVector = borderToNormal2D( annot.border );
Expand Down Expand Up @@ -1302,10 +1337,30 @@ void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QgsRenderContext &context,
extension->UpdateAll( textWidth / 2.0 );
}


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

// Skip outwards facing annotations that are below mRotatedAnnotationsMarginToCorner
bool facingLeft = ( annot.angle < 0 );
bool facingRight = ( annot.angle > 0 );
if ( anotPos == QgsLayoutItemMapGrid::OutsideMapFrame )
{
facingLeft = !facingLeft;
facingRight = !facingRight;
}
if ( annot.border == BorderSide::Top && ( ( facingLeft && xpos < mRotatedAnnotationsMarginToCorner ) ||
( facingRight && xpos > mMap->rect().width() - mRotatedAnnotationsMarginToCorner ) ) )
return;
if ( annot.border == BorderSide::Bottom && ( ( facingLeft && xpos > mMap->rect().width() - mRotatedAnnotationsMarginToCorner ) ||
( facingRight && xpos < mRotatedAnnotationsMarginToCorner ) ) )
return;
if ( annot.border == BorderSide::Left && ( ( facingLeft && ypos > mMap->rect().height() - mRotatedAnnotationsMarginToCorner ) ||
( facingRight && ypos < mRotatedAnnotationsMarginToCorner ) ) )
return;
if ( annot.border == BorderSide::Right && ( ( facingLeft && ypos < mRotatedAnnotationsMarginToCorner ) ||
( facingRight && ypos > mMap->rect().height() - mRotatedAnnotationsMarginToCorner ) ) )
return;

QgsScopedQPainterState painterState( context.painter() );
context.painter()->translate( QPointF( xpos, ypos ) );
context.painter()->rotate( rotation );
Expand Down
52 changes: 47 additions & 5 deletions src/core/layout/qgslayoutitemmapgrid.h
Expand Up @@ -813,75 +813,115 @@ class CORE_EXPORT QgsLayoutItemMapGrid : public QgsLayoutItemMapItem
/**
* Enable/disable ticks rotation for rotated or reprojected grids.
* \see rotatedTicksEnabled()
* \since QGIS 3.16
*/
void setRotatedTicksEnabled( const bool state ) { mRotatedTicksEnabled = state; }

/**
* Gets whether ticks rotation for rotated or reprojected grids is enabled.
* \see setRotatedTicksEnabled()
* \since QGIS 3.16
*/
double rotatedTicksEnabled() const { return mRotatedTicksEnabled; }

/**
* Sets the tick length calculation mode.
* \see rotatedTicksLengthMode()
*/
* Sets the tick length calculation mode.
* \see rotatedTicksLengthMode()
* \since QGIS 3.16
*/
void setRotatedTicksLengthMode( const TickLengthMode mode ) { mRotatedTicksLengthMode = mode; }

/**
* Returns the grid frame style.
* \see setRotatedTicksLengthMode()
* \since QGIS 3.16
*/
TickLengthMode rotatedTicksLengthMode() const { return mRotatedTicksLengthMode; }

/**
* Sets the \a minimum angle (in degrees) below which ticks are not drawn.
* \see rotatedTicksMinimumAngle()
* \since QGIS 3.16
*/
void setRotatedTicksMinimumAngle( const double angle ) { mRotatedTicksMinimumAngle = angle; }

/**
* Gets the \a minimum angle (in degrees) below which ticks are not drawn.
* \see setRotatedTicksMinimumAngle()
* \since QGIS 3.16
*/
double rotatedTicksMinimumAngle() const { return mRotatedTicksMinimumAngle; }

/**
* Sets the \a margin to corners (in canvas units) below which outwards facing ticks are not drawn.
* \see rotatedTicksMarginToCorner()
* \since QGIS 3.16
*/
void setRotatedTicksMarginToCorner( const double margin ) { mRotatedTicksMarginToCorner = margin; }

/**
* Gets the \a margin to corners (in canvas units) below which outwards facing ticks are not drawn.
* \see setRotatedTicksMarginToCorner()
* \since QGIS 3.16
*/
double rotatedTicksMarginToCorner() const { return mRotatedTicksMarginToCorner; }

/**
* Enable/disable annotations rotation for rotated or reprojected grids.
* \see rotatedAnnotationsEnabled()
* \since QGIS 3.16
*/
void setRotatedAnnotationsEnabled( const bool state ) { mRotatedAnnotationsEnabled = state; }

/**
* Gets whether annotations rotation for rotated or reprojected grids is enabled.
* \see setRotatedAnnotationsEnabled()
* \since QGIS 3.16
*/
double rotatedAnnotationsEnabled() const { return mRotatedAnnotationsEnabled; }

/**
* Sets the annotation length calculation mode.
* \see rotatedAnnotationsLengthMode()
* Sets the annotation length calculation mode.
* \see rotatedAnnotationsLengthMode()
* \since QGIS 3.16
*/
void setRotatedAnnotationsLengthMode( const TickLengthMode mode ) { mRotatedAnnotationsLengthMode = mode; }

/**
* Returns the grid frame style.
* \see setRotatedAnnotationsLengthMode()
* \since QGIS 3.16
*/
TickLengthMode rotatedAnnotationsLengthMode() const { return mRotatedAnnotationsLengthMode; }

/**
* Sets the \a minimum angle (in degrees) below which annotated are not drawn.
* \see rotatedAnnotationsMinimumAngle()
* \since QGIS 3.16
*/
void setRotatedAnnotationsMinimumAngle( const double angle ) { mRotatedAnnotationsMinimumAngle = angle; }

/**
* Gets the \a minimum angle (in degrees) below which annotated are not drawn.
* \see setRotatedAnnotationsMinimumAngle()
* \since QGIS 3.16
*/
double rotatedAnnotationsMinimumAngle() const { return mRotatedAnnotationsMinimumAngle; }

/**
* Sets the \a margin to corners (in canvas units) below which outwards facing ticks are not drawn.
* \see rotatedAnnotationsMarginToCorner()
* \since QGIS 3.16
*/
void setRotatedAnnotationsMarginToCorner( const double margin ) { mRotatedAnnotationsMarginToCorner = margin; }

/**
* Gets the \a margin to corners (in canvas units) below which outwards facing ticks are not drawn.
* \see setRotatedAnnotationsMarginToCorner()
* \since QGIS 3.16
*/
double rotatedAnnotationsMarginToCorner() const { return mRotatedAnnotationsMarginToCorner; }

/**
* Sets the grid frame margin (in layout units).
* This property controls distance between the map frame and the grid frame.
Expand Down Expand Up @@ -1103,9 +1143,11 @@ class CORE_EXPORT QgsLayoutItemMapGrid : public QgsLayoutItemMapItem
bool mRotatedTicksEnabled = false;
TickLengthMode mRotatedTicksLengthMode = QgsLayoutItemMapGrid::OrthogonalTicks;
double mRotatedTicksMinimumAngle = 0.0;
double mRotatedTicksMarginToCorner = 0.0;
bool mRotatedAnnotationsEnabled = false;
TickLengthMode mRotatedAnnotationsLengthMode = QgsLayoutItemMapGrid::OrthogonalTicks;
double mRotatedAnnotationsMinimumAngle = 0.0;
double mRotatedAnnotationsMarginToCorner = 0.0;

double mMinimumIntervalWidth = 50;
double mMaximumIntervalWidth = 100;
Expand Down

0 comments on commit 509c5e1

Please sign in to comment.