QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgslayoutitemmapgrid.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemmapgrid.cpp
3 ----------------------
4 begin : October 2017
5 copyright : (C) 2017 by Marco Hugentobler, Nyall Dawson
6 email : marco dot hugentobler at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include <math.h>
21#include <memory>
22
23#include "qgscolorutils.h"
26#include "qgsexception.h"
29#include "qgsfontutils.h"
30#include "qgsgeometry.h"
31#include "qgslayout.h"
32#include "qgslayoutitemmap.h"
34#include "qgslayoututils.h"
35#include "qgslinesymbol.h"
36#include "qgslogger.h"
37#include "qgsmarkersymbol.h"
38#include "qgsreadwritecontext.h"
39#include "qgsrendercontext.h"
40#include "qgssettings.h"
42#include "qgssymbollayerutils.h"
43#include "qgstextrenderer.h"
44#include "qgsunittypes.h"
45
46#include <QPainter>
47#include <QPen>
48#include <QVector2D>
49
50#include "moc_qgslayoutitemmapgrid.cpp"
51
52#define MAX_GRID_LINES 1000 //maximum number of horizontal or vertical grid lines to draw
53
59
64
65void QgsLayoutItemMapGridStack::removeGrid( const QString &gridId )
66{
68}
69
70void QgsLayoutItemMapGridStack::moveGridUp( const QString &gridId )
71{
73}
74
75void QgsLayoutItemMapGridStack::moveGridDown( const QString &gridId )
76{
78}
79
81{
83 return qobject_cast<QgsLayoutItemMapGrid *>( item );
84}
85
87{
89 return qobject_cast<QgsLayoutItemMapGrid *>( item );
90}
91
92QList<QgsLayoutItemMapGrid *> QgsLayoutItemMapGridStack::asList() const // cppcheck-suppress duplInheritedMember
93{
94 QList< QgsLayoutItemMapGrid * > list;
96 {
97 if ( QgsLayoutItemMapGrid *grid = qobject_cast<QgsLayoutItemMapGrid *>( item ) )
98 {
99 list.append( grid );
100 }
101 }
102 return list;
103}
104
105QgsLayoutItemMapGrid &QgsLayoutItemMapGridStack::operator[]( int idx ) // cppcheck-suppress duplInheritedMember
106{
107 QgsLayoutItemMapItem *item = mItems.at( idx );
108 QgsLayoutItemMapGrid *grid = qobject_cast<QgsLayoutItemMapGrid *>( item );
109 return *grid;
110}
111
112bool QgsLayoutItemMapGridStack::readXml( const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context )
113{
114 removeItems();
115
116 //read grid stack
117 const QDomNodeList mapGridNodeList = elem.elementsByTagName( QStringLiteral( "ComposerMapGrid" ) );
118 for ( int i = 0; i < mapGridNodeList.size(); ++i )
119 {
120 const QDomElement mapGridElem = mapGridNodeList.at( i ).toElement();
121 QgsLayoutItemMapGrid *mapGrid = new QgsLayoutItemMapGrid( mapGridElem.attribute( QStringLiteral( "name" ) ), mMap );
122 mapGrid->readXml( mapGridElem, doc, context );
123 mItems.append( mapGrid );
124 }
125
126 return true;
127}
128
130{
131 double top = 0.0;
132 double right = 0.0;
133 double bottom = 0.0;
134 double left = 0.0;
135 calculateMaxGridExtension( top, right, bottom, left );
136 return std::max( std::max( std::max( top, right ), bottom ), left );
137}
138
139void QgsLayoutItemMapGridStack::calculateMaxGridExtension( double &top, double &right, double &bottom, double &left ) const
140{
141 top = 0.0;
142 right = 0.0;
143 bottom = 0.0;
144 left = 0.0;
145
147 {
148 if ( QgsLayoutItemMapGrid *grid = qobject_cast<QgsLayoutItemMapGrid *>( item ) )
149 {
150 double gridTop = 0.0;
151 double gridRight = 0.0;
152 double gridBottom = 0.0;
153 double gridLeft = 0.0;
154 grid->calculateMaxExtension( gridTop, gridRight, gridBottom, gridLeft );
155 top = std::max( top, gridTop );
156 right = std::max( right, gridRight );
157 bottom = std::max( bottom, gridBottom );
158 left = std::max( left, gridLeft );
159 }
160 }
161}
162
163
164//
165// QgsLayoutItemMapGrid
166//
167
169{
170 // returns a border as a vector2D for vector arithmetic
171 switch ( border )
172 {
174 return QVector2D( 0, 1 );
176 return QVector2D( -1, 0 );
178 return QVector2D( 0, -1 );
180 return QVector2D( 1, 0 );
181 }
182 return QVector2D();
183}
185{
186 // returns a border normal (towards center) as a vector2D for vector arithmetic
187 const QVector2D borderVector = borderToVector2D( border );
188 return QVector2D( borderVector.y(), -borderVector.x() );
189}
190
195{
196 //get default layout font from settings
197 const QgsSettings settings;
198 const QString defaultFontString = settings.value( QStringLiteral( "LayoutDesigner/defaultFont" ), QVariant(), QgsSettings::Gui ).toString();
199 if ( !defaultFontString.isEmpty() )
200 {
201 QFont font;
202 QgsFontUtils::setFontFamily( font, defaultFontString );
203 mAnnotationFormat.setFont( font );
204 }
205
206 createDefaultGridLineSymbol();
207 createDefaultGridMarkerSymbol();
208
209 connect( mMap, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemMapGrid::refreshDataDefinedProperties );
210 connect( mMap, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemMapGrid::refreshDataDefinedProperties );
211 connect( mMap, &QgsLayoutItemMap::crsChanged, this, [this]
212 {
213 if ( !mCRS.isValid() )
214 emit crsChanged();
215 } );
216}
217
219
220void QgsLayoutItemMapGrid::createDefaultGridLineSymbol()
221{
222 QVariantMap properties;
223 properties.insert( QStringLiteral( "color" ), QStringLiteral( "0,0,0,255" ) );
224 properties.insert( QStringLiteral( "width" ), QStringLiteral( "0.3" ) );
225 properties.insert( QStringLiteral( "capstyle" ), QStringLiteral( "flat" ) );
226 mGridLineSymbol = QgsLineSymbol::createSimple( properties );
227}
228
229void QgsLayoutItemMapGrid::createDefaultGridMarkerSymbol()
230{
231 QVariantMap properties;
232 properties.insert( QStringLiteral( "name" ), QStringLiteral( "circle" ) );
233 properties.insert( QStringLiteral( "size" ), QStringLiteral( "2.0" ) );
234 properties.insert( QStringLiteral( "color" ), QStringLiteral( "0,0,0,255" ) );
235 mGridMarkerSymbol = QgsMarkerSymbol::createSimple( properties );
236}
237
239{
240 if ( mGridLineSymbol )
241 {
242 mGridLineSymbol->setWidth( width );
243 }
244}
245
247{
248 if ( mGridLineSymbol )
249 {
250 mGridLineSymbol->setColor( c );
251 }
252}
253
254bool QgsLayoutItemMapGrid::writeXml( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const
255{
256 if ( elem.isNull() )
257 {
258 return false;
259 }
260
261 QDomElement mapGridElem = doc.createElement( QStringLiteral( "ComposerMapGrid" ) );
262 mapGridElem.setAttribute( QStringLiteral( "gridStyle" ), mGridStyle );
263 mapGridElem.setAttribute( QStringLiteral( "intervalX" ), qgsDoubleToString( mGridIntervalX ) );
264 mapGridElem.setAttribute( QStringLiteral( "intervalY" ), qgsDoubleToString( mGridIntervalY ) );
265 mapGridElem.setAttribute( QStringLiteral( "offsetX" ), qgsDoubleToString( mGridOffsetX ) );
266 mapGridElem.setAttribute( QStringLiteral( "offsetY" ), qgsDoubleToString( mGridOffsetY ) );
267 mapGridElem.setAttribute( QStringLiteral( "crossLength" ), qgsDoubleToString( mCrossLength ) );
268
269 QDomElement lineStyleElem = doc.createElement( QStringLiteral( "lineStyle" ) );
270 const QDomElement gridLineStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mGridLineSymbol.get(), doc, context );
271 lineStyleElem.appendChild( gridLineStyleElem );
272 mapGridElem.appendChild( lineStyleElem );
273
274 QDomElement markerStyleElem = doc.createElement( QStringLiteral( "markerStyle" ) );
275 const QDomElement gridMarkerStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mGridMarkerSymbol.get(), doc, context );
276 markerStyleElem.appendChild( gridMarkerStyleElem );
277 mapGridElem.appendChild( markerStyleElem );
278
279 mapGridElem.setAttribute( QStringLiteral( "gridFrameStyle" ), mGridFrameStyle );
280 mapGridElem.setAttribute( QStringLiteral( "gridFrameSideFlags" ), mGridFrameSides );
281 mapGridElem.setAttribute( QStringLiteral( "gridFrameWidth" ), qgsDoubleToString( mGridFrameWidth ) );
282 mapGridElem.setAttribute( QStringLiteral( "gridFrameMargin" ), qgsDoubleToString( mGridFrameMargin ) );
283 mapGridElem.setAttribute( QStringLiteral( "gridFramePenThickness" ), qgsDoubleToString( mGridFramePenThickness ) );
284 mapGridElem.setAttribute( QStringLiteral( "gridFramePenColor" ), QgsColorUtils::colorToString( mGridFramePenColor ) );
285 mapGridElem.setAttribute( QStringLiteral( "frameFillColor1" ), QgsColorUtils::colorToString( mGridFrameFillColor1 ) );
286 mapGridElem.setAttribute( QStringLiteral( "frameFillColor2" ), QgsColorUtils::colorToString( mGridFrameFillColor2 ) );
287 mapGridElem.setAttribute( QStringLiteral( "leftFrameDivisions" ), mLeftFrameDivisions );
288 mapGridElem.setAttribute( QStringLiteral( "rightFrameDivisions" ), mRightFrameDivisions );
289 mapGridElem.setAttribute( QStringLiteral( "topFrameDivisions" ), mTopFrameDivisions );
290 mapGridElem.setAttribute( QStringLiteral( "bottomFrameDivisions" ), mBottomFrameDivisions );
291 mapGridElem.setAttribute( QStringLiteral( "rotatedTicksLengthMode" ), mRotatedTicksLengthMode );
292 mapGridElem.setAttribute( QStringLiteral( "rotatedTicksEnabled" ), mRotatedTicksEnabled );
293 mapGridElem.setAttribute( QStringLiteral( "rotatedTicksMinimumAngle" ), QString::number( mRotatedTicksMinimumAngle ) );
294 mapGridElem.setAttribute( QStringLiteral( "rotatedTicksMarginToCorner" ), QString::number( mRotatedTicksMarginToCorner ) );
295 mapGridElem.setAttribute( QStringLiteral( "rotatedAnnotationsLengthMode" ), mRotatedAnnotationsLengthMode );
296 mapGridElem.setAttribute( QStringLiteral( "rotatedAnnotationsEnabled" ), mRotatedAnnotationsEnabled );
297 mapGridElem.setAttribute( QStringLiteral( "rotatedAnnotationsMinimumAngle" ), QString::number( mRotatedAnnotationsMinimumAngle ) );
298 mapGridElem.setAttribute( QStringLiteral( "rotatedAnnotationsMarginToCorner" ), QString::number( mRotatedAnnotationsMarginToCorner ) );
299 if ( mCRS.isValid() )
300 {
301 mCRS.writeXml( mapGridElem, doc );
302 }
303
304 mapGridElem.setAttribute( QStringLiteral( "annotationFormat" ), mGridAnnotationFormat );
305 mapGridElem.setAttribute( QStringLiteral( "showAnnotation" ), mShowGridAnnotation );
306 mapGridElem.setAttribute( QStringLiteral( "annotationExpression" ), mGridAnnotationExpressionString );
307 mapGridElem.setAttribute( QStringLiteral( "leftAnnotationDisplay" ), mLeftGridAnnotationDisplay );
308 mapGridElem.setAttribute( QStringLiteral( "rightAnnotationDisplay" ), mRightGridAnnotationDisplay );
309 mapGridElem.setAttribute( QStringLiteral( "topAnnotationDisplay" ), mTopGridAnnotationDisplay );
310 mapGridElem.setAttribute( QStringLiteral( "bottomAnnotationDisplay" ), mBottomGridAnnotationDisplay );
311 mapGridElem.setAttribute( QStringLiteral( "leftAnnotationPosition" ), mLeftGridAnnotationPosition );
312 mapGridElem.setAttribute( QStringLiteral( "rightAnnotationPosition" ), mRightGridAnnotationPosition );
313 mapGridElem.setAttribute( QStringLiteral( "topAnnotationPosition" ), mTopGridAnnotationPosition );
314 mapGridElem.setAttribute( QStringLiteral( "bottomAnnotationPosition" ), mBottomGridAnnotationPosition );
315 mapGridElem.setAttribute( QStringLiteral( "leftAnnotationDirection" ), mLeftGridAnnotationDirection );
316 mapGridElem.setAttribute( QStringLiteral( "rightAnnotationDirection" ), mRightGridAnnotationDirection );
317 mapGridElem.setAttribute( QStringLiteral( "topAnnotationDirection" ), mTopGridAnnotationDirection );
318 mapGridElem.setAttribute( QStringLiteral( "bottomAnnotationDirection" ), mBottomGridAnnotationDirection );
319 mapGridElem.setAttribute( QStringLiteral( "frameAnnotationDistance" ), QString::number( mAnnotationFrameDistance ) );
320 mapGridElem.appendChild( mAnnotationFormat.writeXml( doc, context ) );
321 mapGridElem.setAttribute( QStringLiteral( "annotationPrecision" ), mGridAnnotationPrecision );
322 mapGridElem.setAttribute( QStringLiteral( "unit" ), mGridUnit );
323 mapGridElem.setAttribute( QStringLiteral( "blendMode" ), mBlendMode );
324 mapGridElem.setAttribute( QStringLiteral( "minimumIntervalWidth" ), QString::number( mMinimumIntervalWidth ) );
325 mapGridElem.setAttribute( QStringLiteral( "maximumIntervalWidth" ), QString::number( mMaximumIntervalWidth ) );
326
327 const bool ok = QgsLayoutItemMapItem::writeXml( mapGridElem, doc, context );
328 elem.appendChild( mapGridElem );
329 return ok;
330}
331
332bool QgsLayoutItemMapGrid::readXml( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
333{
334 Q_UNUSED( doc )
335 if ( itemElem.isNull() )
336 {
337 return false;
338 }
339
340 const bool ok = QgsLayoutItemMapItem::readXml( itemElem, doc, context );
341
342 //grid
343 mGridStyle = QgsLayoutItemMapGrid::GridStyle( itemElem.attribute( QStringLiteral( "gridStyle" ), QStringLiteral( "0" ) ).toInt() );
344 mGridIntervalX = itemElem.attribute( QStringLiteral( "intervalX" ), QStringLiteral( "0" ) ).toDouble();
345 mGridIntervalY = itemElem.attribute( QStringLiteral( "intervalY" ), QStringLiteral( "0" ) ).toDouble();
346 mGridOffsetX = itemElem.attribute( QStringLiteral( "offsetX" ), QStringLiteral( "0" ) ).toDouble();
347 mGridOffsetY = itemElem.attribute( QStringLiteral( "offsetY" ), QStringLiteral( "0" ) ).toDouble();
348 mCrossLength = itemElem.attribute( QStringLiteral( "crossLength" ), QStringLiteral( "3" ) ).toDouble();
349 mGridFrameStyle = static_cast< QgsLayoutItemMapGrid::FrameStyle >( itemElem.attribute( QStringLiteral( "gridFrameStyle" ), QStringLiteral( "0" ) ).toInt() );
350 mGridFrameSides = static_cast< QgsLayoutItemMapGrid::FrameSideFlags >( itemElem.attribute( QStringLiteral( "gridFrameSideFlags" ), QStringLiteral( "15" ) ).toInt() );
351 mGridFrameWidth = itemElem.attribute( QStringLiteral( "gridFrameWidth" ), QStringLiteral( "2.0" ) ).toDouble();
352 mGridFrameMargin = itemElem.attribute( QStringLiteral( "gridFrameMargin" ), QStringLiteral( "0.0" ) ).toDouble();
353 mGridFramePenThickness = itemElem.attribute( QStringLiteral( "gridFramePenThickness" ), QStringLiteral( "0.3" ) ).toDouble();
354 mGridFramePenColor = QgsColorUtils::colorFromString( itemElem.attribute( QStringLiteral( "gridFramePenColor" ), QStringLiteral( "0,0,0" ) ) );
355 mGridFrameFillColor1 = QgsColorUtils::colorFromString( itemElem.attribute( QStringLiteral( "frameFillColor1" ), QStringLiteral( "255,255,255,255" ) ) );
356 mGridFrameFillColor2 = QgsColorUtils::colorFromString( itemElem.attribute( QStringLiteral( "frameFillColor2" ), QStringLiteral( "0,0,0,255" ) ) );
357 mLeftFrameDivisions = QgsLayoutItemMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "leftFrameDivisions" ), QStringLiteral( "0" ) ).toInt() );
358 mRightFrameDivisions = QgsLayoutItemMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "rightFrameDivisions" ), QStringLiteral( "0" ) ).toInt() );
359 mTopFrameDivisions = QgsLayoutItemMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "topFrameDivisions" ), QStringLiteral( "0" ) ).toInt() );
360 mBottomFrameDivisions = QgsLayoutItemMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "bottomFrameDivisions" ), QStringLiteral( "0" ) ).toInt() );
361 mRotatedTicksLengthMode = TickLengthMode( itemElem.attribute( QStringLiteral( "rotatedTicksLengthMode" ), QStringLiteral( "0" ) ).toInt() );
362 mRotatedTicksEnabled = itemElem.attribute( QStringLiteral( "rotatedTicksEnabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" );
363 mRotatedTicksMinimumAngle = itemElem.attribute( QStringLiteral( "rotatedTicksMinimumAngle" ), QStringLiteral( "0" ) ).toDouble();
364 mRotatedTicksMarginToCorner = itemElem.attribute( QStringLiteral( "rotatedTicksMarginToCorner" ), QStringLiteral( "0" ) ).toDouble();
365 mRotatedAnnotationsLengthMode = TickLengthMode( itemElem.attribute( QStringLiteral( "rotatedAnnotationsLengthMode" ), QStringLiteral( "0" ) ).toInt() );
366 mRotatedAnnotationsEnabled = itemElem.attribute( QStringLiteral( "rotatedAnnotationsEnabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" );
367 mRotatedAnnotationsMinimumAngle = itemElem.attribute( QStringLiteral( "rotatedAnnotationsMinimumAngle" ), QStringLiteral( "0" ) ).toDouble();
368 mRotatedAnnotationsMarginToCorner = itemElem.attribute( QStringLiteral( "rotatedAnnotationsMarginToCorner" ), QStringLiteral( "0" ) ).toDouble();
369
370 const QDomElement lineStyleElem = itemElem.firstChildElement( QStringLiteral( "lineStyle" ) );
371 if ( !lineStyleElem.isNull() )
372 {
373 const QDomElement symbolElem = lineStyleElem.firstChildElement( QStringLiteral( "symbol" ) );
374 if ( !symbolElem.isNull() )
375 {
376 mGridLineSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( symbolElem, context );
377 }
378 }
379 else
380 {
381 //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
382 mGridLineSymbol = QgsLineSymbol::createSimple( QVariantMap() );
383 mGridLineSymbol->setWidth( itemElem.attribute( QStringLiteral( "penWidth" ), QStringLiteral( "0" ) ).toDouble() );
384 mGridLineSymbol->setColor( QColor( itemElem.attribute( QStringLiteral( "penColorRed" ), QStringLiteral( "0" ) ).toInt(),
385 itemElem.attribute( QStringLiteral( "penColorGreen" ), QStringLiteral( "0" ) ).toInt(),
386 itemElem.attribute( QStringLiteral( "penColorBlue" ), QStringLiteral( "0" ) ).toInt() ) );
387 }
388
389 const QDomElement markerStyleElem = itemElem.firstChildElement( QStringLiteral( "markerStyle" ) );
390 if ( !markerStyleElem.isNull() )
391 {
392 const QDomElement symbolElem = markerStyleElem.firstChildElement( QStringLiteral( "symbol" ) );
393 if ( !symbolElem.isNull() )
394 {
395 mGridMarkerSymbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElem, context );
396 }
397 }
398
399 if ( !mCRS.readXml( itemElem ) )
401
402 mBlendMode = static_cast< QPainter::CompositionMode >( itemElem.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() );
403
404 //annotation
405 mShowGridAnnotation = ( itemElem.attribute( QStringLiteral( "showAnnotation" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
406 mGridAnnotationFormat = QgsLayoutItemMapGrid::AnnotationFormat( itemElem.attribute( QStringLiteral( "annotationFormat" ), QStringLiteral( "0" ) ).toInt() );
407 mGridAnnotationExpressionString = itemElem.attribute( QStringLiteral( "annotationExpression" ) );
408 mGridAnnotationExpression.reset();
409 mLeftGridAnnotationPosition = QgsLayoutItemMapGrid::AnnotationPosition( itemElem.attribute( QStringLiteral( "leftAnnotationPosition" ), QStringLiteral( "0" ) ).toInt() );
410 mRightGridAnnotationPosition = QgsLayoutItemMapGrid::AnnotationPosition( itemElem.attribute( QStringLiteral( "rightAnnotationPosition" ), QStringLiteral( "0" ) ).toInt() );
411 mTopGridAnnotationPosition = QgsLayoutItemMapGrid::AnnotationPosition( itemElem.attribute( QStringLiteral( "topAnnotationPosition" ), QStringLiteral( "0" ) ).toInt() );
412 mBottomGridAnnotationPosition = QgsLayoutItemMapGrid::AnnotationPosition( itemElem.attribute( QStringLiteral( "bottomAnnotationPosition" ), QStringLiteral( "0" ) ).toInt() );
413 mLeftGridAnnotationDisplay = QgsLayoutItemMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "leftAnnotationDisplay" ), QStringLiteral( "0" ) ).toInt() );
414 mRightGridAnnotationDisplay = QgsLayoutItemMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "rightAnnotationDisplay" ), QStringLiteral( "0" ) ).toInt() );
415 mTopGridAnnotationDisplay = QgsLayoutItemMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "topAnnotationDisplay" ), QStringLiteral( "0" ) ).toInt() );
416 mBottomGridAnnotationDisplay = QgsLayoutItemMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "bottomAnnotationDisplay" ), QStringLiteral( "0" ) ).toInt() );
417
418 mLeftGridAnnotationDirection = QgsLayoutItemMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "leftAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
419 mRightGridAnnotationDirection = QgsLayoutItemMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "rightAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
420 mTopGridAnnotationDirection = QgsLayoutItemMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "topAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
421 mBottomGridAnnotationDirection = QgsLayoutItemMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "bottomAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
422 mAnnotationFrameDistance = itemElem.attribute( QStringLiteral( "frameAnnotationDistance" ), QStringLiteral( "0" ) ).toDouble();
423
424 if ( !itemElem.firstChildElement( "text-style" ).isNull() )
425 {
426 mAnnotationFormat.readXml( itemElem, context );
427 }
428 else
429 {
430 QFont font;
431 if ( !QgsFontUtils::setFromXmlChildNode( font, itemElem, "annotationFontProperties" ) )
432 {
433 font.fromString( itemElem.attribute( "annotationFont", QString() ) );
434 }
435 mAnnotationFormat.setFont( font );
436 mAnnotationFormat.setSize( font.pointSizeF() );
437 mAnnotationFormat.setSizeUnit( Qgis::RenderUnit::Points );
438 mAnnotationFormat.setColor( QgsColorUtils::colorFromString( itemElem.attribute( "annotationFontColor", "0,0,0,255" ) ) );
439 }
440
441 mGridAnnotationPrecision = itemElem.attribute( QStringLiteral( "annotationPrecision" ), QStringLiteral( "3" ) ).toInt();
442 const int gridUnitInt = itemElem.attribute( QStringLiteral( "unit" ), QString::number( MapUnit ) ).toInt();
443 mGridUnit = ( gridUnitInt <= static_cast< int >( DynamicPageSizeBased ) ) ? static_cast< GridUnit >( gridUnitInt ) : MapUnit;
444 mMinimumIntervalWidth = itemElem.attribute( QStringLiteral( "minimumIntervalWidth" ), QStringLiteral( "50" ) ).toDouble();
445 mMaximumIntervalWidth = itemElem.attribute( QStringLiteral( "maximumIntervalWidth" ), QStringLiteral( "100" ) ).toDouble();
446
447 refreshDataDefinedProperties();
448 return ok;
449}
450
452{
453 if ( mCRS == crs )
454 return;
455
456 mCRS = crs;
457 mTransformDirty = true;
458 emit crsChanged();
459}
460
462{
463 return mBlendMode != QPainter::CompositionMode_SourceOver;
464}
465
466QPolygonF QgsLayoutItemMapGrid::scalePolygon( const QPolygonF &polygon, const double scale ) const
467{
468 const QTransform t = QTransform::fromScale( scale, scale );
469 return t.map( polygon );
470}
471
472void QgsLayoutItemMapGrid::drawGridCrsTransform( QgsRenderContext &context, double dotsPerMM, bool calculateLinesOnly ) const
473{
474 if ( !mMap || !mEvaluatedEnabled )
475 {
476 return;
477 }
478
479 //has map extent/scale changed?
480 const QPolygonF mapPolygon = mMap->transformedMapPolygon();
481 if ( mapPolygon != mPrevMapPolygon )
482 {
483 mTransformDirty = true;
484 mPrevMapPolygon = mapPolygon;
485 }
486
487 if ( mTransformDirty )
488 {
489 calculateCrsTransformLines();
490 }
491
492 //draw lines
493 if ( !calculateLinesOnly )
494 {
495 int countLongitudeLines = 0;
496 int countLatitudeLines = 0;
497 for ( const GridLine &line : mGridLines )
498 {
499 switch ( line.coordinateType )
500 {
501 case Longitude:
502 countLongitudeLines++;
503 break;
504 case Latitude:
505 countLatitudeLines++;
506 break;
507 }
508 }
509
510 int latitudeLineIndex = 0;
511 int longitudeLineIndex = 0;
512 if ( mGridStyle == QgsLayoutItemMapGrid::Solid )
513 {
514 QList< GridLine >::const_iterator gridIt = mGridLines.constBegin();
515 for ( ; gridIt != mGridLines.constEnd(); ++gridIt )
516 {
517 switch ( gridIt->coordinateType )
518 {
519 case Longitude:
520 longitudeLineIndex++;
521 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_count" ), countLongitudeLines, true ) );
522 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_index" ), longitudeLineIndex, true ) );
523 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), QStringLiteral( "x" ), true ) );
524 break;
525
526 case Latitude:
527 latitudeLineIndex++;
528 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_count" ), countLatitudeLines, true ) );
529 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_index" ), latitudeLineIndex, true ) );
530 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), QStringLiteral( "y" ), true ) );
531 break;
532 }
533 context.expressionContext().lastScope()->setVariable( QStringLiteral( "grid_number" ), gridIt->coordinate );
534 drawGridLine( scalePolygon( gridIt->line, dotsPerMM ), context );
535 }
536 }
537 else if ( mGridStyle == QgsLayoutItemMapGrid::Cross || mGridStyle == QgsLayoutItemMapGrid::Markers )
538 {
539 const double maxX = mMap->rect().width();
540 const double maxY = mMap->rect().height();
541
542 QList< QgsPointXY >::const_iterator intersectionIt = mTransformedIntersections.constBegin();
543 for ( ; intersectionIt != mTransformedIntersections.constEnd(); ++intersectionIt )
544 {
545 const double x = intersectionIt->x();
546 const double y = intersectionIt->y();
547 if ( mGridStyle == QgsLayoutItemMapGrid::Cross )
548 {
549 //ensure that crosses don't overshoot the map item bounds
550 const QLineF line1 = QLineF( x - mEvaluatedCrossLength, y, x + mEvaluatedCrossLength, y );
551 line1.p1().rx() = line1.p1().x() < 0 ? 0 : line1.p1().x();
552 line1.p2().rx() = line1.p2().x() > maxX ? maxX : line1.p2().x();
553 const QLineF line2 = QLineF( x, y - mEvaluatedCrossLength, x, y + mEvaluatedCrossLength );
554 line2.p1().ry() = line2.p1().y() < 0 ? 0 : line2.p1().y();
555 line2.p2().ry() = line2.p2().y() > maxY ? maxY : line2.p2().y();
556
557 //draw line using coordinates scaled to dots
558 drawGridLine( QLineF( line1.p1() * dotsPerMM, line1.p2() * dotsPerMM ), context );
559 drawGridLine( QLineF( line2.p1() * dotsPerMM, line2.p2() * dotsPerMM ), context );
560 }
561 else if ( mGridStyle == QgsLayoutItemMapGrid::Markers )
562 {
563 drawGridMarker( QPointF( x, y ) * dotsPerMM, context );
564 }
565 }
566 }
567 }
568}
569
570void QgsLayoutItemMapGrid::calculateCrsTransformLines() const
571{
572 QgsRectangle crsBoundingRect;
573 QgsCoordinateTransform inverseTr;
574 if ( crsGridParams( crsBoundingRect, inverseTr ) != 0 )
575 {
576 return;
577 }
578
579 // calculate grid lines
580 mGridLines.clear();
581 xGridLinesCrsTransform( crsBoundingRect, inverseTr );
582 yGridLinesCrsTransform( crsBoundingRect, inverseTr );
583
584 if ( mGridStyle == QgsLayoutItemMapGrid::Cross || mGridStyle == QgsLayoutItemMapGrid::Markers )
585 {
586 //cross or markers style - we also need to calculate intersections of lines
587
588 //first convert lines to QgsGeometry
589 QList< QgsGeometry > xLines;
590 QList< QgsGeometry > yLines;
591 QList< GridLine >::const_iterator gridIt = mGridLines.constBegin();
592 for ( ; gridIt != mGridLines.constEnd(); ++gridIt )
593 {
594
595 QgsPolylineXY line;
596 for ( int i = 0; i < gridIt->line.size(); ++i )
597 {
598 line.append( QgsPointXY( gridIt->line.at( i ).x(), gridIt->line.at( i ).y() ) );
599 }
600 if ( gridIt->coordinateType == AnnotationCoordinate::Longitude )
601 yLines << QgsGeometry::fromPolylineXY( line );
602 else if ( gridIt->coordinateType == AnnotationCoordinate::Latitude )
603 xLines << QgsGeometry::fromPolylineXY( line );
604 }
605
606 //now, loop through geometries and calculate intersection points
607 mTransformedIntersections.clear();
608 QList< QgsGeometry >::const_iterator yLineIt = yLines.constBegin();
609 for ( ; yLineIt != yLines.constEnd(); ++yLineIt )
610 {
611 QList< QgsGeometry >::const_iterator xLineIt = xLines.constBegin();
612 for ( ; xLineIt != xLines.constEnd(); ++xLineIt )
613 {
614 //look for intersections between lines
615 const QgsGeometry intersects = ( *yLineIt ).intersection( ( *xLineIt ) );
616 if ( intersects.isNull() )
617 continue;
618
619 //go through all intersections and draw grid markers/crosses
620 int i = 0;
621 QgsPointXY vertex = intersects.vertexAt( i );
622 while ( !vertex.isEmpty() )
623 {
624 mTransformedIntersections << vertex;
625 i = i + 1;
626 vertex = intersects.vertexAt( i );
627 }
628 }
629 }
630 }
631
632 mTransformDirty = false;
633}
634
635void QgsLayoutItemMapGrid::draw( QPainter *p )
636{
637 if ( !mMap || !mEvaluatedEnabled )
638 {
639 return;
640 }
641 QPaintDevice *paintDevice = p->device();
642 if ( !paintDevice )
643 {
644 return;
645 }
646
647 p->save();
648 p->setCompositionMode( mBlendMode );
649 p->setRenderHint( QPainter::Antialiasing, mMap->layout()->renderContext().flags() & Qgis::LayoutRenderFlag::Antialiasing );
650
651 const QRectF thisPaintRect = QRectF( 0, 0, mMap->rect().width(), mMap->rect().height() );
652 p->setClipRect( thisPaintRect );
653 if ( thisPaintRect != mPrevPaintRect )
654 {
655 //rect has changed, so need to recalculate transform
656 mTransformDirty = true;
657 mPrevPaintRect = thisPaintRect;
658 }
659
660 //setup painter scaling to dots so that raster symbology is drawn to scale
661 const double dotsPerMM = paintDevice->logicalDpiX() / 25.4;
662 p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); //scale painter from mm to dots
663
664 //setup render context
669 const QgsExpressionContext expressionContext = createExpressionContext();
670 context.setExpressionContext( expressionContext );
671
672 //is grid in a different crs than map?
673 switch ( mGridUnit )
674 {
675 case MapUnit:
677 if ( mCRS.isValid() && mCRS != mMap->crs() )
678 {
679 drawGridCrsTransform( context, dotsPerMM );
680 break;
681 }
682
683 [[fallthrough]];
684 case CM:
685 case MM:
686 drawGridNoTransform( context, dotsPerMM );
687 break;
688 }
689 p->restore();
690
691 p->setClipping( false );
692#ifdef Q_OS_MAC
693 //QPainter::setClipping(false) seems to be broken on OSX (#12747). So we hack around it by
694 //setting a larger clip rect
695 p->setClipRect( mMap->mapRectFromScene( mMap->sceneBoundingRect() ).adjusted( -10, -10, 10, 10 ) );
696#endif
697
698
699 if ( mGridFrameStyle != QgsLayoutItemMapGrid::NoFrame || mShowGridAnnotation )
700 updateGridLinesAnnotationsPositions();
701
702 if ( mGridFrameStyle != QgsLayoutItemMapGrid::NoFrame )
703 {
704 drawGridFrame( p );
705 }
706
707 if ( mShowGridAnnotation )
708 {
709 drawCoordinateAnnotations( context, context.expressionContext() );
710 }
711}
712
713void QgsLayoutItemMapGrid::updateGridLinesAnnotationsPositions() const
714{
715 QList< GridLine >::iterator it = mGridLines.begin();
716 for ( ; it != mGridLines.end(); ++it )
717 {
718 it->startAnnotation.border = borderForLineCoord( it->line.first(), it->coordinateType );
719 it->endAnnotation.border = borderForLineCoord( it->line.last(), it->coordinateType );
720 it->startAnnotation.position = QVector2D( it->line.first() );
721 it->endAnnotation.position = QVector2D( it->line.last() );
722 it->startAnnotation.vector = QVector2D( it->line.at( 1 ) - it->line.first() ).normalized();
723 it->endAnnotation.vector = QVector2D( it->line.at( it->line.count() - 2 ) - it->line.last() ).normalized();
724 const QVector2D normS = borderToNormal2D( it->startAnnotation.border );
725 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() );
726 const QVector2D normE = borderToNormal2D( it->endAnnotation.border );
727 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() );
728 }
729}
730
731void QgsLayoutItemMapGrid::drawGridNoTransform( QgsRenderContext &context, double dotsPerMM, bool calculateLinesOnly ) const
732{
733 //get line positions
734 mGridLines.clear();
735 yGridLines();
736 xGridLines();
737
738 if ( calculateLinesOnly )
739 return;
740
741 QList< GridLine >::const_iterator vIt = mGridLines.constBegin();
742 QList< GridLine >::const_iterator hIt = mGridLines.constBegin();
743
744 int countLongitudeLines = 0;
745 int countLatitudeLines = 0;
746 for ( const GridLine &line : mGridLines )
747 {
748 switch ( line.coordinateType )
749 {
750 case Longitude:
751 countLongitudeLines++;
752 break;
753 case Latitude:
754 countLatitudeLines++;
755 break;
756 }
757 }
758
759 int latitudeLineIndex = 0;
760 int longitudeLineIndex = 0;
761
762 //simple approach: draw vertical lines first, then horizontal ones
763 if ( mGridStyle == QgsLayoutItemMapGrid::Solid )
764 {
765 //we need to scale line coordinates to dots, rather than mm, since the painter has already been scaled to dots
766 //this is done by multiplying each line coordinate by dotsPerMM
767 QLineF line;
768 for ( ; vIt != mGridLines.constEnd(); ++vIt )
769 {
770 if ( vIt->coordinateType != AnnotationCoordinate::Longitude )
771 continue;
772 line = QLineF( vIt->line.first() * dotsPerMM, vIt->line.last() * dotsPerMM );
773
774 longitudeLineIndex++;
775 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_count" ), countLongitudeLines, true ) );
776 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_index" ), longitudeLineIndex, true ) );
777 context.expressionContext().lastScope()->setVariable( QStringLiteral( "grid_number" ), vIt->coordinate );
778 context.expressionContext().lastScope()->setVariable( QStringLiteral( "grid_axis" ), "x" );
779
780 drawGridLine( line, context );
781 }
782
783 for ( ; hIt != mGridLines.constEnd(); ++hIt )
784 {
785 if ( hIt->coordinateType != AnnotationCoordinate::Latitude )
786 continue;
787 line = QLineF( hIt->line.first() * dotsPerMM, hIt->line.last() * dotsPerMM );
788
789 latitudeLineIndex++;
790 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_count" ), countLatitudeLines, true ) );
791 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_index" ), latitudeLineIndex, true ) );
792 context.expressionContext().lastScope()->setVariable( QStringLiteral( "grid_number" ), hIt->coordinate );
793 context.expressionContext().lastScope()->setVariable( QStringLiteral( "grid_axis" ), "y" );
794
795 drawGridLine( line, context );
796 }
797 }
798 else if ( mGridStyle != QgsLayoutItemMapGrid::FrameAnnotationsOnly ) //cross or markers
799 {
800 QLineF l1, l2;
801 QPointF intersectionPoint, crossEnd1, crossEnd2;
802 for ( ; vIt != mGridLines.constEnd(); ++vIt )
803 {
804 if ( vIt->coordinateType != AnnotationCoordinate::Longitude )
805 continue;
806
807 l1 = QLineF( vIt->line.first(), vIt->line.last() );
808
809 //test for intersection with every horizontal line
810 hIt = mGridLines.constBegin();
811 for ( ; hIt != mGridLines.constEnd(); ++hIt )
812 {
813 if ( hIt->coordinateType != AnnotationCoordinate::Latitude )
814 continue;
815
816 l2 = QLineF( hIt->line.first(), hIt->line.last() );
817
818 if ( l2.intersects( l1, &intersectionPoint ) == QLineF::BoundedIntersection )
819 {
820 if ( mGridStyle == QgsLayoutItemMapGrid::Cross )
821 {
822 //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
823 crossEnd1 = ( ( intersectionPoint - l1.p1() ).manhattanLength() > 0.01 ) ?
824 QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, l1.p1(), mEvaluatedCrossLength ) : intersectionPoint;
825 crossEnd2 = ( ( intersectionPoint - l1.p2() ).manhattanLength() > 0.01 ) ?
826 QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, l1.p2(), mEvaluatedCrossLength ) : intersectionPoint;
827 //draw line using coordinates scaled to dots
828 drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
829 }
830 else if ( mGridStyle == QgsLayoutItemMapGrid::Markers )
831 {
832 drawGridMarker( intersectionPoint * dotsPerMM, context );
833 }
834 }
835 }
836 }
837 if ( mGridStyle == QgsLayoutItemMapGrid::Markers )
838 {
839 //markers mode, so we have no need to process horizontal lines (we've already
840 //drawn markers on the intersections between horizontal and vertical lines)
841 return;
842 }
843
844 hIt = mGridLines.constBegin();
845 for ( ; hIt != mGridLines.constEnd(); ++hIt )
846 {
847 if ( hIt->coordinateType != AnnotationCoordinate::Latitude )
848 continue;
849
850 l1 = QLineF( hIt->line.first(), hIt->line.last() );
851
852 vIt = mGridLines.constBegin();
853 for ( ; vIt != mGridLines.constEnd(); ++vIt )
854 {
855 if ( vIt->coordinateType != AnnotationCoordinate::Longitude )
856 continue;
857
858 l2 = QLineF( vIt->line.first(), vIt->line.last() );
859
860 if ( l2.intersects( l1, &intersectionPoint ) == QLineF::BoundedIntersection )
861 {
862 //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
863 crossEnd1 = ( ( intersectionPoint - l1.p1() ).manhattanLength() > 0.01 ) ?
864 QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, l1.p1(), mEvaluatedCrossLength ) : intersectionPoint;
865 crossEnd2 = ( ( intersectionPoint - l1.p2() ).manhattanLength() > 0.01 ) ?
866 QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, l1.p2(), mEvaluatedCrossLength ) : intersectionPoint;
867 //draw line using coordinates scaled to dots
868 drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
869 }
870 }
871 }
872 }
873}
874
875void QgsLayoutItemMapGrid::drawGridFrame( QPainter *p, GridExtension *extension ) const
876{
877 if ( p )
878 {
879 p->save();
880 p->setRenderHint( QPainter::Antialiasing, mMap->layout()->renderContext().flags() & Qgis::LayoutRenderFlag::Antialiasing );
881 }
882
883
884 switch ( mGridFrameStyle )
885 {
888 drawGridFrameZebra( p, extension );
889 break;
893 drawGridFrameTicks( p, extension );
894 break;
895
898 drawGridFrameLine( p, extension );
899 break;
900
902 break;
903 }
904
905 if ( p )
906 p->restore();
907}
908
909void QgsLayoutItemMapGrid::drawGridLine( const QLineF &line, QgsRenderContext &context ) const
910{
911 QPolygonF poly;
912 poly << line.p1() << line.p2();
913 drawGridLine( poly, context );
914}
915
916void QgsLayoutItemMapGrid::drawGridLine( const QPolygonF &line, QgsRenderContext &context ) const
917{
918 if ( !mMap || !mMap->layout() || !mGridLineSymbol )
919 {
920 return;
921 }
922
923 mGridLineSymbol->startRender( context );
924 mGridLineSymbol->renderPolyline( line, nullptr, context );
925 mGridLineSymbol->stopRender( context );
926}
927
928void QgsLayoutItemMapGrid::drawGridMarker( QPointF point, QgsRenderContext &context ) const
929{
930 if ( !mMap || !mMap->layout() || !mGridMarkerSymbol )
931 {
932 return;
933 }
934
935 mGridMarkerSymbol->startRender( context );
936 mGridMarkerSymbol->renderPoint( point, nullptr, context );
937 mGridMarkerSymbol->stopRender( context );
938}
939
940void QgsLayoutItemMapGrid::drawGridFrameZebra( QPainter *p, GridExtension *extension ) const
941{
943 {
944 drawGridFrameZebraBorder( p, QgsLayoutItemMapGrid::Left, extension ? &extension->left : nullptr );
945 }
947 {
948 drawGridFrameZebraBorder( p, QgsLayoutItemMapGrid::Right, extension ? &extension->right : nullptr );
949 }
951 {
952 drawGridFrameZebraBorder( p, QgsLayoutItemMapGrid::Top, extension ? &extension->top : nullptr );
953 }
955 {
956 drawGridFrameZebraBorder( p, QgsLayoutItemMapGrid::Bottom, extension ? &extension->bottom : nullptr );
957 }
958}
959
960void QgsLayoutItemMapGrid::drawGridFrameZebraBorder( QPainter *p, BorderSide border, double *extension ) const
961{
962 if ( !mMap )
963 {
964 return;
965 }
966
967 if ( extension )
968 {
969 *extension = mEvaluatedGridFrameMargin + mEvaluatedGridFrameWidth + mEvaluatedGridFrameLineThickness / 2.0;
970 return;
971 }
972
973 double currentCoord = 0.0;
974 bool color1 = false;
975 double x = 0;
976 double y = 0;
977 double width = 0;
978 double height = 0;
979
980 bool drawTLBox = false;
981 bool drawTRBox = false;
982 bool drawBLBox = false;
983 bool drawBRBox = false;
984
985 QMap< double, double > pos = QMap< double, double >();
986 QList< GridLine >::const_iterator it = mGridLines.constBegin();
987 for ( ; it != mGridLines.constEnd(); ++it )
988 {
989 // for first and last point of the line
990 for ( int i = 0 ; i < 2 ; ++i )
991 {
992 const GridLineAnnotation annot = ( i == 0 ) ? it->startAnnotation : it->endAnnotation;
993
994 // we skip if the point is on another border
995 if ( annot.border != border )
996 continue;
997
998 if ( ! shouldShowDivisionForSide( it->coordinateType, annot.border ) )
999 continue;
1000
1001 if ( border == QgsLayoutItemMapGrid::Left || border == QgsLayoutItemMapGrid::Right )
1002 pos.insert( annot.position.y(), it->coordinate );
1003 else
1004 pos.insert( annot.position.x(), it->coordinate );
1005 }
1006 }
1007
1008
1009 if ( border == QgsLayoutItemMapGrid::Left || border == QgsLayoutItemMapGrid::Right )
1010 {
1011 pos.insert( mMap->rect().height(), mMap->rect().height() );
1013 {
1014 drawBLBox = border == QgsLayoutItemMapGrid::Left;
1015 drawBRBox = border == QgsLayoutItemMapGrid::Right;
1016 }
1018 {
1019 drawTLBox = border == QgsLayoutItemMapGrid::Left;
1020 drawTRBox = border == QgsLayoutItemMapGrid::Right;
1021 }
1022 if ( !drawTLBox && border == QgsLayoutItemMapGrid::Left )
1023 color1 = true;
1024 }
1025 else if ( border == QgsLayoutItemMapGrid::Top || border == QgsLayoutItemMapGrid::Bottom )
1026 {
1027 pos.insert( mMap->rect().width(), mMap->rect().width() );
1028 }
1029
1030 //set pen to current frame pen
1031 QPen framePen = QPen( mGridFramePenColor );
1032 framePen.setWidthF( mEvaluatedGridFrameLineThickness );
1033 framePen.setJoinStyle( Qt::MiterJoin );
1034 p->setPen( framePen );
1035
1036 QMap< double, double >::const_iterator posIt = pos.constBegin();
1037 for ( ; posIt != pos.constEnd(); ++posIt )
1038 {
1039 p->setBrush( QBrush( color1 ? mGridFrameFillColor1 : mGridFrameFillColor2 ) );
1040 if ( border == QgsLayoutItemMapGrid::Left || border == QgsLayoutItemMapGrid::Right )
1041 {
1042 height = posIt.key() - currentCoord;
1043 width = mEvaluatedGridFrameWidth;
1044 x = ( border == QgsLayoutItemMapGrid::Left ) ? -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ) : mMap->rect().width() + mEvaluatedGridFrameMargin;
1045 y = currentCoord;
1046 }
1047 else //top or bottom
1048 {
1049 height = mEvaluatedGridFrameWidth;
1050 width = posIt.key() - currentCoord;
1051 x = currentCoord;
1052 y = ( border == QgsLayoutItemMapGrid::Top ) ? -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ) : mMap->rect().height() + mEvaluatedGridFrameMargin;
1053 }
1054 p->drawRect( QRectF( x, y, width, height ) );
1055 currentCoord = posIt.key();
1056 color1 = !color1;
1057 }
1058
1059 if ( mGridFrameStyle == ZebraNautical || qgsDoubleNear( mEvaluatedGridFrameMargin, 0.0 ) )
1060 {
1061 //draw corners
1062 width = height = ( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ) ;
1063 p->setBrush( QBrush( mGridFrameFillColor1 ) );
1064 if ( drawTLBox )
1065 p->drawRect( QRectF( -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ), -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ), width, height ) );
1066 if ( drawTRBox )
1067 p->drawRect( QRectF( mMap->rect().width(), -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ), width, height ) );
1068 if ( drawBLBox )
1069 p->drawRect( QRectF( -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ), mMap->rect().height(), width, height ) );
1070 if ( drawBRBox )
1071 p->drawRect( QRectF( mMap->rect().width(), mMap->rect().height(), width, height ) );
1072 }
1073}
1074
1075void QgsLayoutItemMapGrid::drawGridFrameTicks( QPainter *p, GridExtension *extension ) const
1076{
1077 if ( !mMap )
1078 {
1079 return;
1080 }
1081
1082 //set pen to current frame pen
1083 if ( p )
1084 {
1085 QPen framePen = QPen( mGridFramePenColor );
1086 framePen.setWidthF( mEvaluatedGridFrameLineThickness );
1087 framePen.setCapStyle( Qt::FlatCap );
1088 p->setBrush( Qt::NoBrush );
1089 p->setPen( framePen );
1090 }
1091
1092 QList< GridLine >::iterator it = mGridLines.begin();
1093 for ( ; it != mGridLines.end(); ++it )
1094 {
1095 // for first and last point of the line
1096 for ( int i = 0 ; i < 2 ; ++i )
1097 {
1098 const GridLineAnnotation annot = ( i == 0 ) ? it->startAnnotation : it->endAnnotation;
1099
1100 if ( ! shouldShowDivisionForSide( it->coordinateType, annot.border ) )
1101 continue;
1102
1103 // If the angle is below the threshold, we don't draw the annotation
1104 if ( abs( annot.angle ) / M_PI * 180.0 > 90.0 - mRotatedTicksMinimumAngle + 0.0001 )
1105 continue;
1106
1107 // Skip outwards facing annotations that are below mRotatedTicksMarginToCorner
1108 bool facingLeft;
1109 bool facingRight;
1110 if ( mGridFrameStyle == QgsLayoutItemMapGrid::InteriorExteriorTicks )
1111 {
1112 facingLeft = ( annot.angle != 0 );
1113 facingRight = ( annot.angle != 0 );
1114 }
1115 else if ( mGridFrameStyle == QgsLayoutItemMapGrid::ExteriorTicks )
1116 {
1117 facingLeft = ( annot.angle > 0 );
1118 facingRight = ( annot.angle < 0 );
1119 }
1120 else
1121 {
1122 facingLeft = ( annot.angle < 0 );
1123 facingRight = ( annot.angle > 0 );
1124 }
1125
1126 if ( annot.border == BorderSide::Top && ( ( facingLeft && annot.position.x() < mRotatedTicksMarginToCorner ) ||
1127 ( facingRight && annot.position.x() > mMap->rect().width() - mRotatedTicksMarginToCorner ) ) )
1128 continue;
1129 if ( annot.border == BorderSide::Bottom && ( ( facingLeft && annot.position.x() > mMap->rect().width() - mRotatedTicksMarginToCorner ) ||
1130 ( facingRight && annot.position.x() < mRotatedTicksMarginToCorner ) ) )
1131 continue;
1132 if ( annot.border == BorderSide::Left && ( ( facingLeft && annot.position.y() > mMap->rect().height() - mRotatedTicksMarginToCorner ) ||
1133 ( facingRight && annot.position.y() < mRotatedTicksMarginToCorner ) ) )
1134 continue;
1135 if ( annot.border == BorderSide::Right && ( ( facingLeft && annot.position.y() < mRotatedTicksMarginToCorner ) ||
1136 ( facingRight && annot.position.y() > mMap->rect().height() - mRotatedTicksMarginToCorner ) ) )
1137 continue;
1138
1139 const QVector2D normalVector = borderToNormal2D( annot.border );
1140 const QVector2D vector = ( mRotatedTicksEnabled ) ? annot.vector : normalVector;
1141
1142 double fA = mEvaluatedGridFrameMargin; // point near to frame
1143 double fB = mEvaluatedGridFrameMargin + mEvaluatedGridFrameWidth; // point far from frame
1144
1145 if ( mRotatedTicksEnabled && mRotatedTicksLengthMode == OrthogonalTicks )
1146 {
1147 fA /= QVector2D::dotProduct( vector, normalVector );
1148 fB /= QVector2D::dotProduct( vector, normalVector );
1149 }
1150
1151 // extents isn't computed accurately
1152 if ( extension )
1153 {
1154 if ( mGridFrameStyle != QgsLayoutItemMapGrid::InteriorTicks )
1155 extension->UpdateBorder( annot.border, fB );
1156 continue;
1157 }
1158
1159 QVector2D pA;
1160 QVector2D pB;
1161 if ( mGridFrameStyle == QgsLayoutItemMapGrid::InteriorTicks )
1162 {
1163 pA = annot.position + fA * vector;
1164 pB = annot.position + fB * vector;
1165 }
1166 else if ( mGridFrameStyle == QgsLayoutItemMapGrid::ExteriorTicks )
1167 {
1168 pA = annot.position - fA * vector;
1169 pB = annot.position - fB * vector;
1170 }
1171 else // InteriorExteriorTicks
1172 {
1173 pA = annot.position - fB * vector;
1174 pB = annot.position + ( fB - 2.0 * mEvaluatedGridFrameMargin ) * vector;
1175 }
1176 p->drawLine( QLineF( pA.toPointF(), pB.toPointF() ) );
1177
1178 }
1179 }
1180}
1181
1182void QgsLayoutItemMapGrid::drawGridFrameLine( QPainter *p, GridExtension *extension ) const
1183{
1184 if ( !mMap )
1185 {
1186 return;
1187 }
1188
1189 if ( p )
1190 {
1191 //set pen to current frame pen
1192 QPen framePen = QPen( mGridFramePenColor );
1193 framePen.setWidthF( mEvaluatedGridFrameLineThickness );
1194 framePen.setCapStyle( Qt::SquareCap );
1195 p->setBrush( Qt::NoBrush );
1196 p->setPen( framePen );
1197 }
1198
1199 const bool drawDiagonals = mGridFrameStyle == LineBorderNautical && !qgsDoubleNear( mEvaluatedGridFrameMargin, 0.0 );
1200
1202 {
1203 if ( extension )
1204 extension->UpdateBorder( QgsLayoutItemMapGrid::Left, mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 );
1205 else
1206 p->drawLine( QLineF( 0 - mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin, mMap->rect().height() + mEvaluatedGridFrameMargin ) );
1207 }
1208
1210 {
1211 if ( extension )
1212 extension->UpdateBorder( QgsLayoutItemMapGrid::Right, mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 );
1213 else
1214 p->drawLine( QLineF( mMap->rect().width() + mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin, mMap->rect().width() + mEvaluatedGridFrameMargin, mMap->rect().height() + mEvaluatedGridFrameMargin ) );
1215 }
1216
1218 {
1219 if ( extension )
1220 extension->UpdateBorder( QgsLayoutItemMapGrid::Top, mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 );
1221 else
1222 p->drawLine( QLineF( 0 - mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin, mMap->rect().width() + mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin ) );
1223 }
1224
1226 {
1227 if ( extension )
1228 extension->UpdateBorder( QgsLayoutItemMapGrid::Bottom, mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 );
1229 else
1230 p->drawLine( QLineF( 0 - mEvaluatedGridFrameMargin, mMap->rect().height() + mEvaluatedGridFrameMargin, mMap->rect().width() + mEvaluatedGridFrameMargin, mMap->rect().height() + mEvaluatedGridFrameMargin ) );
1231 }
1232
1233 if ( ! extension && drawDiagonals )
1234 {
1236 {
1237 //corner left-top
1238 const double X1 = 0 - mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0;
1239 const double Y1 = 0 - mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0;
1240 p->drawLine( QLineF( 0, 0, X1, Y1 ) );
1241 }
1243 {
1244 //corner right-bottom
1245 const double X1 = mMap->rect().width() + mEvaluatedGridFrameMargin - mEvaluatedGridFrameLineThickness / 2.0 ;
1246 const double Y1 = mMap->rect().height() + mEvaluatedGridFrameMargin - mEvaluatedGridFrameLineThickness / 2.0 ;
1247 p->drawLine( QLineF( mMap->rect().width(), mMap->rect().height(), X1, Y1 ) );
1248 }
1250 {
1251 //corner right-top
1252 const double X1 = mMap->rect().width() + mEvaluatedGridFrameMargin - mEvaluatedGridFrameLineThickness / 2.0 ;
1253 const double Y1 = 0 - mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 ;
1254 p->drawLine( QLineF( mMap->rect().width(), 0, X1, Y1 ) );
1255 }
1257 {
1258 //corner left-bottom
1259 const double X1 = 0 - mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 ;
1260 const double Y1 = mMap->rect().height() + mEvaluatedGridFrameMargin - mEvaluatedGridFrameLineThickness / 2.0 ;
1261 p->drawLine( QLineF( 0, mMap->rect().height(), X1, Y1 ) );
1262 }
1263 }
1264}
1265
1266void QgsLayoutItemMapGrid::drawCoordinateAnnotations( QgsRenderContext &context, QgsExpressionContext &expressionContext,
1267 GridExtension *extension ) const
1268{
1269 if ( mGridLines.empty() )
1270 return;
1271
1272 QString currentAnnotationString;
1273 QList< GridLine >::const_iterator it = mGridLines.constBegin();
1274
1275 QgsExpressionContextScope *gridScope = new QgsExpressionContextScope();
1276 QgsExpressionContextScopePopper scopePopper( expressionContext, gridScope );
1277
1278 bool geographic = false;
1279 if ( mCRS.isValid() )
1280 {
1281 geographic = mCRS.isGeographic();
1282 }
1283 else if ( mMap && mMap->layout() )
1284 {
1285 geographic = mMap->crs().isGeographic();
1286 }
1287
1288 const bool forceWrap = ( geographic && it->coordinateType == QgsLayoutItemMapGrid::Longitude &&
1289 ( mGridAnnotationFormat == QgsLayoutItemMapGrid::Decimal || mGridAnnotationFormat == QgsLayoutItemMapGrid::DecimalWithSuffix ) );
1290
1291 int countLongitudeLines = 0;
1292 int countLatitudeLines = 0;
1293 for ( const GridLine &line : mGridLines )
1294 {
1295 switch ( line.coordinateType )
1296 {
1297 case Longitude:
1298 countLongitudeLines++;
1299 break;
1300 case Latitude:
1301 countLatitudeLines++;
1302 break;
1303 }
1304 }
1305
1306 int latitudeLineIndex = 0;
1307 int longitudeLineIndex = 0;
1308 for ( ; it != mGridLines.constEnd(); ++it )
1309 {
1310 double value = it->coordinate;
1311 switch ( it->coordinateType )
1312 {
1313 case Longitude:
1314 longitudeLineIndex++;
1315 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_count" ), countLongitudeLines, true ) );
1316 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_index" ), longitudeLineIndex, true ) );
1317 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), QStringLiteral( "x" ), true ) );
1318 break;
1319
1320 case Latitude:
1321 latitudeLineIndex++;
1322 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_count" ), countLatitudeLines, true ) );
1323 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_index" ), latitudeLineIndex, true ) );
1324 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), QStringLiteral( "y" ), true ) );
1325 break;
1326 }
1327
1328 if ( forceWrap )
1329 {
1330 // wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W"
1331 const double wrappedX = std::fmod( value, 360.0 );
1332 if ( wrappedX > 180.0 )
1333 {
1334 value = wrappedX - 360.0;
1335 }
1336 else if ( wrappedX < -180.0 )
1337 {
1338 value = wrappedX + 360.0;
1339 }
1340 }
1341
1342 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_number" ), value, true ) );
1343
1344 if ( mDrawAnnotationProperty )
1345 {
1346 bool ok = false;
1347 const bool display = mDrawAnnotationProperty->valueAsBool( expressionContext, true, &ok );
1348 if ( ok && !display )
1349 continue;
1350 }
1351 currentAnnotationString = gridAnnotationString( it->coordinate, it->coordinateType, expressionContext, geographic );
1352 drawCoordinateAnnotation( context, it->startAnnotation, currentAnnotationString, it->coordinateType, extension );
1353 drawCoordinateAnnotation( context, it->endAnnotation, currentAnnotationString, it->coordinateType, extension );
1354 }
1355}
1356
1357void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QgsRenderContext &context, GridLineAnnotation annot, const QString &annotationString, const AnnotationCoordinate coordinateType, GridExtension *extension ) const
1358{
1359 if ( !mMap )
1360 {
1361 return;
1362 }
1363
1364 if ( ! shouldShowAnnotationForSide( coordinateType, annot.border ) )
1365 return;
1366
1367 const QgsLayoutItemMapGrid::BorderSide frameBorder = annot.border;
1368 double textWidth = QgsTextRenderer::textWidth( context, mAnnotationFormat, QStringList() << annotationString ) / context.convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
1369 if ( extension )
1370 textWidth *= 1.1; // little bit of extra padding when we are calculating the bounding rect, to account for antialiasing
1371
1372 //relevant for annotations is the height of digits
1373 const double textHeight = ( extension ? ( QgsTextRenderer::textHeight( context, mAnnotationFormat, QChar(), true ) )
1374 : ( QgsTextRenderer::textHeight( context, mAnnotationFormat, '0', false ) ) ) / context.convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
1375
1376 double xpos = annot.position.x();
1377 double ypos = annot.position.y();
1378 QPointF anchor = QPointF();
1379 int rotation = 0;
1380
1381 const AnnotationPosition anotPos = annotationPosition( frameBorder );
1382 const AnnotationDirection anotDir = annotationDirection( frameBorder );
1383
1384 // If the angle is below the threshold, we don't draw the annotation
1385 if ( abs( annot.angle ) / M_PI * 180.0 > 90.0 - mRotatedAnnotationsMinimumAngle + 0.0001 )
1386 return;
1387
1388 const QVector2D normalVector = borderToNormal2D( annot.border );
1389 const QVector2D vector = ( mRotatedAnnotationsEnabled ) ? annot.vector : normalVector;
1390
1391 // Distance to frame
1392 double f = mEvaluatedAnnotationFrameDistance;
1393
1394 // Adapt distance to frame using the frame width and line thickness into account
1395 const bool isOverTick = ( anotDir == QgsLayoutItemMapGrid::AboveTick || anotDir == QgsLayoutItemMapGrid::OnTick || anotDir == QgsLayoutItemMapGrid::UnderTick );
1396 const bool hasInteriorMargin = ! isOverTick && ( mGridFrameStyle == QgsLayoutItemMapGrid::InteriorTicks || mGridFrameStyle == QgsLayoutItemMapGrid::InteriorExteriorTicks );
1397 const bool hasExteriorMargin = ! isOverTick && ( mGridFrameStyle == QgsLayoutItemMapGrid::Zebra || mGridFrameStyle == QgsLayoutItemMapGrid::ExteriorTicks || mGridFrameStyle == QgsLayoutItemMapGrid::InteriorExteriorTicks || mGridFrameStyle == QgsLayoutItemMapGrid::ZebraNautical );
1398 const bool hasBorderWidth = ( mGridFrameStyle == QgsLayoutItemMapGrid::Zebra || mGridFrameStyle == QgsLayoutItemMapGrid::ZebraNautical || mGridFrameStyle == QgsLayoutItemMapGrid::LineBorder || mGridFrameStyle == QgsLayoutItemMapGrid::LineBorderNautical );
1399 if ( ( anotPos == QgsLayoutItemMapGrid::InsideMapFrame && hasInteriorMargin ) || ( anotPos == QgsLayoutItemMapGrid::OutsideMapFrame && hasExteriorMargin ) )
1400 f += mEvaluatedGridFrameWidth;
1401 if ( hasBorderWidth )
1402 f += mEvaluatedGridFrameLineThickness / 2.0;
1403
1405 f *= -1;
1406
1407 if ( mRotatedAnnotationsEnabled && mRotatedAnnotationsLengthMode == OrthogonalTicks )
1408 {
1409 f /= QVector2D::dotProduct( vector, normalVector );
1410 }
1411
1412 const QVector2D pos = annot.position + f * vector;
1413 xpos = pos.x();
1414 ypos = pos.y();
1415
1416 const bool outside = ( anotPos == QgsLayoutItemMapGrid::OutsideMapFrame );
1417
1418 if (
1420 anotDir == QgsLayoutItemMapGrid::OnTick ||
1422 )
1423 {
1424
1425 rotation = atan2( vector.y(), vector.x() ) / M_PI * 180;
1426
1427 if ( rotation <= -90 || rotation > 90 )
1428 {
1429 rotation += 180;
1430 anchor.setX( outside ? 0 : textWidth ); // left / right
1431 }
1432 else
1433 {
1434 anchor.setX( outside ? textWidth : 0 ); // right / left
1435 }
1436
1437 if ( anotDir == QgsLayoutItemMapGrid::AboveTick )
1438 anchor.setY( 0.5 * textHeight ); // bottom
1439 else if ( anotDir == QgsLayoutItemMapGrid::UnderTick )
1440 anchor.setY( -1.5 * textHeight ); // top
1441 else // OnTick
1442 anchor.setY( -0.5 * textHeight ); // middle
1443
1444 }
1445 else if ( anotDir == QgsLayoutItemMapGrid::Horizontal )
1446 {
1447 rotation = 0;
1448 anchor.setX( 0.5 * textWidth ); // center
1449 anchor.setY( -0.5 * textHeight ); // middle
1450 if ( frameBorder == QgsLayoutItemMapGrid::Top )
1451 anchor.setY( outside ? 0 : -textHeight ); // bottom / top
1452 else if ( frameBorder == QgsLayoutItemMapGrid::Right )
1453 anchor.setX( outside ? 0 : textWidth ); // left / right
1454 else if ( frameBorder == QgsLayoutItemMapGrid::Bottom )
1455 anchor.setY( outside ? -textHeight : 0 ); // top / bottom
1456 else if ( frameBorder == QgsLayoutItemMapGrid::Left )
1457 anchor.setX( outside ? textWidth : 0 ); // right / left
1458 }
1459 else if ( anotDir == QgsLayoutItemMapGrid::Vertical )
1460 {
1461 rotation = -90;
1462 anchor.setX( 0.5 * textWidth ); // center
1463 anchor.setY( -0.5 * textHeight ); // middle
1464 if ( frameBorder == QgsLayoutItemMapGrid::Top )
1465 anchor.setX( outside ? 0 : textWidth ); // left / right
1466 else if ( frameBorder == QgsLayoutItemMapGrid::Right )
1467 anchor.setY( outside ? -textHeight : 0 ); // top / bottom
1468 else if ( frameBorder == QgsLayoutItemMapGrid::Bottom )
1469 anchor.setX( outside ? textWidth : 0 ); // right / left
1470 else if ( frameBorder == QgsLayoutItemMapGrid::Left )
1471 anchor.setY( outside ? 0 : -textHeight ); // bottom / top
1472 }
1473 else if ( anotDir == QgsLayoutItemMapGrid::VerticalDescending )
1474 {
1475 rotation = 90;
1476 anchor.setX( 0.5 * textWidth ); // center
1477 anchor.setY( -0.5 * textHeight ); // middle
1478 if ( frameBorder == QgsLayoutItemMapGrid::Top )
1479 anchor.setX( outside ? textWidth : 0 ); // right / left
1480 else if ( frameBorder == QgsLayoutItemMapGrid::Right )
1481 anchor.setY( outside ? 0 : -textHeight ); // bottom / top
1482 else if ( frameBorder == QgsLayoutItemMapGrid::Bottom )
1483 anchor.setX( outside ? 0 : textWidth ); // left / right
1484 else if ( frameBorder == QgsLayoutItemMapGrid::Left )
1485 anchor.setY( outside ? -textHeight : 0 ); // top / bottom
1486 }
1487 else // ( anotDir == QgsLayoutItemMapGrid::BoundaryDirection )
1488 {
1489 const QVector2D borderVector = borderToVector2D( annot.border );
1490 rotation = atan2( borderVector.y(), borderVector.x() ) / M_PI * 180;
1491 anchor.setX( 0.5 * textWidth ); // center
1493 anchor.setY( -textHeight ); // top
1494 else
1495 anchor.setY( 0 ); // bottom
1496 }
1497
1498 // extents isn't computed accurately
1499 if ( extension && anotPos == QgsLayoutItemMapGrid::OutsideMapFrame )
1500 {
1501 extension->UpdateBorder( frameBorder, -f + textWidth );
1502 // We also add a general margin, can be useful for labels near corners
1503 extension->UpdateAll( textWidth / 2.0 );
1504 }
1505
1506 if ( extension || !context.painter() )
1507 return;
1508
1509 // Skip outwards facing annotations that are below mRotatedAnnotationsMarginToCorner
1510 bool facingLeft = ( annot.angle < 0 );
1511 bool facingRight = ( annot.angle > 0 );
1513 {
1514 facingLeft = !facingLeft;
1515 facingRight = !facingRight;
1516 }
1517 if ( annot.border == BorderSide::Top && ( ( facingLeft && annot.position.x() < mRotatedAnnotationsMarginToCorner ) ||
1518 ( facingRight && annot.position.x() > mMap->rect().width() - mRotatedAnnotationsMarginToCorner ) ) )
1519 return;
1520 if ( annot.border == BorderSide::Bottom && ( ( facingLeft && annot.position.x() > mMap->rect().width() - mRotatedAnnotationsMarginToCorner ) ||
1521 ( facingRight && annot.position.x() < mRotatedAnnotationsMarginToCorner ) ) )
1522 return;
1523 if ( annot.border == BorderSide::Left && ( ( facingLeft && annot.position.y() > mMap->rect().height() - mRotatedAnnotationsMarginToCorner ) ||
1524 ( facingRight && annot.position.y() < mRotatedAnnotationsMarginToCorner ) ) )
1525 return;
1526 if ( annot.border == BorderSide::Right && ( ( facingLeft && annot.position.y() < mRotatedAnnotationsMarginToCorner ) ||
1527 ( facingRight && annot.position.y() > mMap->rect().height() - mRotatedAnnotationsMarginToCorner ) ) )
1528 return;
1529
1530 const QgsScopedQPainterState painterState( context.painter() );
1531 context.painter()->translate( QPointF( xpos, ypos ) );
1532 context.painter()->rotate( rotation );
1533 context.painter()->translate( -anchor );
1534 const QgsScopedRenderContextScaleToPixels scale( context );
1535 QgsTextRenderer::drawText( QPointF( 0, 0 ), 0, Qgis::TextHorizontalAlignment::Left, annotationString.split( '\n' ), context, mAnnotationFormat );
1536}
1537
1538QString QgsLayoutItemMapGrid::gridAnnotationString( const double value, QgsLayoutItemMapGrid::AnnotationCoordinate coord, QgsExpressionContext &expressionContext, bool isGeographic ) const
1539{
1540 //check if we are using degrees (ie, geographic crs)
1541
1542 if ( mGridAnnotationFormat == QgsLayoutItemMapGrid::Decimal )
1543 {
1544 return QString::number( value, 'f', mGridAnnotationPrecision );
1545 }
1546 else if ( mGridAnnotationFormat == QgsLayoutItemMapGrid::DecimalWithSuffix )
1547 {
1548 QString hemisphere;
1549
1550 const double coordRounded = qgsRound( value, mGridAnnotationPrecision );
1551 if ( coord == QgsLayoutItemMapGrid::Longitude )
1552 {
1553 //don't use E/W suffixes if ambiguous (e.g., 180 degrees)
1554 if ( !isGeographic || ( coordRounded != 180.0 && coordRounded != 0.0 ) )
1555 {
1556 hemisphere = value < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
1557 }
1558 }
1559 else
1560 {
1561 //don't use N/S suffixes if ambiguous (e.g., 0 degrees)
1562 if ( !isGeographic || coordRounded != 0.0 )
1563 {
1564 hemisphere = value < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
1565 }
1566 }
1567 if ( isGeographic )
1568 {
1569 //insert degree symbol for geographic coordinates
1570 return QString::number( std::fabs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere;
1571 }
1572 else
1573 {
1574 return QString::number( std::fabs( value ), 'f', mGridAnnotationPrecision ) + hemisphere;
1575 }
1576 }
1577 else if ( mGridAnnotationFormat == CustomFormat )
1578 {
1579 if ( !mGridAnnotationExpression )
1580 {
1581 mGridAnnotationExpression = std::make_unique<QgsExpression>( mGridAnnotationExpressionString );
1582 mGridAnnotationExpression->prepare( &expressionContext );
1583 }
1584 return mGridAnnotationExpression->evaluate( &expressionContext ).toString();
1585 }
1586
1589 switch ( mGridAnnotationFormat )
1590 {
1591 case Decimal:
1592 case DecimalWithSuffix:
1593 case CustomFormat:
1594 break; // already handled above
1595
1596 case DegreeMinute:
1599 break;
1600
1601 case DegreeMinuteSecond:
1604 break;
1605
1609 break;
1610
1611 case DegreeMinutePadded:
1614 break;
1615
1619 break;
1620
1624 break;
1625 }
1626
1627 switch ( coord )
1628 {
1629 case Longitude:
1630 return QgsCoordinateFormatter::formatX( value, format, mGridAnnotationPrecision, flags );
1631
1632 case Latitude:
1633 return QgsCoordinateFormatter::formatY( value, format, mGridAnnotationPrecision, flags );
1634 }
1635
1636 return QString(); // no warnings
1637}
1638
1639int QgsLayoutItemMapGrid::xGridLines() const
1640{
1641 if ( !mMap || mEvaluatedIntervalY <= 0.0 )
1642 {
1643 return 1;
1644 }
1645
1646
1647 QPolygonF mapPolygon = mMap->transformedMapPolygon();
1648 QRectF mapBoundingRect = mapPolygon.boundingRect();
1649 double gridIntervalY = mEvaluatedIntervalY;
1650 double gridOffsetY = mEvaluatedOffsetY;
1651 double annotationScale = 1.0;
1652 switch ( mGridUnit )
1653 {
1654 case CM:
1655 case MM:
1656 {
1657 mapBoundingRect = mMap->rect();
1658 mapPolygon = QPolygonF( mMap->rect() );
1659 if ( mGridUnit == CM )
1660 {
1661 annotationScale = 0.1;
1662 gridIntervalY *= 10;
1663 gridOffsetY *= 10;
1664 }
1665 break;
1666 }
1667
1668 case MapUnit:
1670 break;
1671 }
1672
1673 //consider to round up to the next step in case the left boundary is > 0
1674 const double roundCorrection = mapBoundingRect.top() > gridOffsetY ? 1.0 : 0.0;
1675 double currentLevel = static_cast< int >( ( mapBoundingRect.top() - gridOffsetY ) / gridIntervalY + roundCorrection ) * gridIntervalY + gridOffsetY;
1676
1677 int gridLineCount = 0;
1678 if ( qgsDoubleNear( mMap->mapRotation(), 0.0 ) || ( mGridUnit != MapUnit && mGridUnit != DynamicPageSizeBased ) )
1679 {
1680 //no rotation. Do it 'the easy way'
1681
1682 double yCanvasCoord;
1683 while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1684 {
1685 yCanvasCoord = mMap->rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
1686 GridLine newLine;
1687 newLine.coordinate = currentLevel * annotationScale;
1688 newLine.coordinateType = AnnotationCoordinate::Latitude;
1689 newLine.line = QPolygonF() << QPointF( 0, yCanvasCoord ) << QPointF( mMap->rect().width(), yCanvasCoord );
1690 mGridLines.append( newLine );
1691 currentLevel += gridIntervalY;
1692 gridLineCount++;
1693 }
1694 return 0;
1695 }
1696
1697 //the four border lines
1698 QVector<QLineF> borderLines;
1699 borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1700 borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1701 borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1702 borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1703
1704 QVector<QPointF> intersectionList; //intersects between border lines and grid lines
1705
1706 while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1707 {
1708 intersectionList.clear();
1709 const QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
1710
1711 QVector<QLineF>::const_iterator it = borderLines.constBegin();
1712 for ( ; it != borderLines.constEnd(); ++it )
1713 {
1714 QPointF intersectionPoint;
1715 if ( it->intersects( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1716 {
1717 intersectionList.push_back( intersectionPoint );
1718 if ( intersectionList.size() >= 2 )
1719 {
1720 break; //we already have two intersections, skip further tests
1721 }
1722 }
1723 }
1724
1725 if ( intersectionList.size() >= 2 )
1726 {
1727 GridLine newLine;
1728 newLine.coordinate = currentLevel;
1729 newLine.coordinateType = AnnotationCoordinate::Latitude;
1730 newLine.line = QPolygonF() << mMap->mapToItemCoords( intersectionList.at( 0 ) ) << mMap->mapToItemCoords( intersectionList.at( 1 ) );
1731 mGridLines.append( newLine );
1732 gridLineCount++;
1733 }
1734 currentLevel += gridIntervalY;
1735 }
1736
1737
1738 return 0;
1739}
1740
1741int QgsLayoutItemMapGrid::yGridLines() const
1742{
1743 if ( !mMap || mEvaluatedIntervalX <= 0.0 )
1744 {
1745 return 1;
1746 }
1747
1748 QPolygonF mapPolygon = mMap->transformedMapPolygon();
1749 QRectF mapBoundingRect = mapPolygon.boundingRect();
1750 double gridIntervalX = mEvaluatedIntervalX;
1751 double gridOffsetX = mEvaluatedOffsetX;
1752 double annotationScale = 1.0;
1753 switch ( mGridUnit )
1754 {
1755 case CM:
1756 case MM:
1757 {
1758 mapBoundingRect = mMap->rect();
1759 mapPolygon = QPolygonF( mMap->rect() );
1760 if ( mGridUnit == CM )
1761 {
1762 annotationScale = 0.1;
1763 gridIntervalX *= 10;
1764 gridOffsetX *= 10;
1765 }
1766 break;
1767 }
1768
1769 case MapUnit:
1771 break;
1772 }
1773
1774 //consider to round up to the next step in case the left boundary is > 0
1775 const double roundCorrection = mapBoundingRect.left() > gridOffsetX ? 1.0 : 0.0;
1776 double currentLevel = static_cast< int >( ( mapBoundingRect.left() - gridOffsetX ) / gridIntervalX + roundCorrection ) * gridIntervalX + gridOffsetX;
1777
1778 int gridLineCount = 0;
1779 if ( qgsDoubleNear( mMap->mapRotation(), 0.0 ) || ( mGridUnit != MapUnit && mGridUnit != DynamicPageSizeBased ) )
1780 {
1781 //no rotation. Do it 'the easy way'
1782 double xCanvasCoord;
1783 while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1784 {
1785 xCanvasCoord = mMap->rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
1786
1787 GridLine newLine;
1788 newLine.coordinate = currentLevel * annotationScale;
1789 newLine.coordinateType = AnnotationCoordinate::Longitude;
1790 newLine.line = QPolygonF() << QPointF( xCanvasCoord, 0 ) << QPointF( xCanvasCoord, mMap->rect().height() );
1791 mGridLines.append( newLine );
1792 currentLevel += gridIntervalX;
1793 gridLineCount++;
1794 }
1795 return 0;
1796 }
1797
1798 //the four border lines
1799 QVector<QLineF> borderLines;
1800 borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1801 borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1802 borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1803 borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1804
1805 QVector<QPointF> intersectionList; //intersects between border lines and grid lines
1806
1807 while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1808 {
1809 intersectionList.clear();
1810 const QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
1811
1812 QVector<QLineF>::const_iterator it = borderLines.constBegin();
1813 for ( ; it != borderLines.constEnd(); ++it )
1814 {
1815 QPointF intersectionPoint;
1816 if ( it->intersects( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1817 {
1818 intersectionList.push_back( intersectionPoint );
1819 if ( intersectionList.size() >= 2 )
1820 {
1821 break; //we already have two intersections, skip further tests
1822 }
1823 }
1824 }
1825
1826 if ( intersectionList.size() >= 2 )
1827 {
1828 GridLine newLine;
1829 newLine.coordinate = currentLevel;
1830 newLine.coordinateType = AnnotationCoordinate::Longitude;
1831 newLine.line = QPolygonF() << mMap->mapToItemCoords( intersectionList.at( 0 ) ) << mMap->mapToItemCoords( intersectionList.at( 1 ) );
1832 mGridLines.append( newLine );
1833 gridLineCount++;
1834 }
1835 currentLevel += gridIntervalX;
1836 }
1837
1838 return 0;
1839}
1840
1841int QgsLayoutItemMapGrid::xGridLinesCrsTransform( const QgsRectangle &bbox, const QgsCoordinateTransform &t ) const
1842{
1843 if ( !mMap || mEvaluatedIntervalY <= 0.0 )
1844 {
1845 return 1;
1846 }
1847
1848 const double roundCorrection = bbox.yMaximum() > mEvaluatedOffsetY ? 1.0 : 0.0;
1849 double currentLevel = static_cast< int >( ( bbox.yMaximum() - mEvaluatedOffsetY ) / mEvaluatedIntervalY + roundCorrection ) * mEvaluatedIntervalY + mEvaluatedOffsetY;
1850
1851 const double minX = bbox.xMinimum();
1852 const double maxX = bbox.xMaximum();
1853 double step = ( maxX - minX ) / 20;
1854
1855 bool crosses180 = false;
1856 bool crossed180 = false;
1857 if ( mCRS.isGeographic() && ( minX > maxX ) )
1858 {
1859 //handle 180 degree longitude crossover
1860 crosses180 = true;
1861 step = ( maxX + 360.0 - minX ) / 20;
1862 }
1863
1864 if ( qgsDoubleNear( step, 0.0 ) )
1865 return 1;
1866
1867 int gridLineCount = 0;
1868 while ( currentLevel >= bbox.yMinimum() && gridLineCount < MAX_GRID_LINES )
1869 {
1870 QPolygonF gridLine;
1871 double currentX = minX;
1872 bool cont = true;
1873 while ( cont )
1874 {
1875 if ( ( !crosses180 || crossed180 ) && ( currentX > maxX ) )
1876 {
1877 cont = false;
1878 }
1879
1880 try
1881 {
1882 const QgsPointXY mapPoint = t.transform( currentX, currentLevel ); //transform back to map crs
1883 gridLine.append( mMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) ); //transform back to composer coords
1884 }
1885 catch ( QgsCsException &cse )
1886 {
1887 Q_UNUSED( cse )
1888 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
1889 }
1890
1891 currentX += step;
1892 if ( crosses180 && currentX > 180.0 )
1893 {
1894 currentX -= 360.0;
1895 crossed180 = true;
1896 }
1897 }
1898 crossed180 = false;
1899
1900 const QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mMap->rect() ) );
1901 QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1902 for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1903 {
1904 if ( !( *lineIt ).isEmpty() )
1905 {
1906 GridLine newLine;
1907 newLine.coordinate = currentLevel;
1908 newLine.coordinateType = AnnotationCoordinate::Latitude;
1909 newLine.line = QPolygonF( *lineIt );
1910 mGridLines.append( newLine );
1911 gridLineCount++;
1912 }
1913 }
1914 currentLevel -= mEvaluatedIntervalY;
1915 }
1916
1917 return 0;
1918}
1919
1920int QgsLayoutItemMapGrid::yGridLinesCrsTransform( const QgsRectangle &bbox, const QgsCoordinateTransform &t ) const
1921{
1922 if ( !mMap || mEvaluatedIntervalX <= 0.0 )
1923 {
1924 return 1;
1925 }
1926
1927 const double roundCorrection = bbox.xMinimum() > mEvaluatedOffsetX ? 1.0 : 0.0;
1928 double currentLevel = static_cast< int >( ( bbox.xMinimum() - mEvaluatedOffsetX ) / mEvaluatedIntervalX + roundCorrection ) * mEvaluatedIntervalX + mEvaluatedOffsetX;
1929
1930 const double minY = bbox.yMinimum();
1931 const double maxY = bbox.yMaximum();
1932 const double step = ( maxY - minY ) / 20;
1933
1934 if ( qgsDoubleNear( step, 0.0 ) )
1935 return 1;
1936
1937 bool crosses180 = false;
1938 bool crossed180 = false;
1939 if ( mCRS.isGeographic() && ( bbox.xMinimum() > bbox.xMaximum() ) )
1940 {
1941 //handle 180 degree longitude crossover
1942 crosses180 = true;
1943 }
1944
1945 int gridLineCount = 0;
1946 while ( ( currentLevel <= bbox.xMaximum() || ( crosses180 && !crossed180 ) ) && gridLineCount < MAX_GRID_LINES )
1947 {
1948 QPolygonF gridLine;
1949 double currentY = minY;
1950 bool cont = true;
1951 while ( cont )
1952 {
1953 if ( currentY > maxY )
1954 {
1955 cont = false;
1956 }
1957 try
1958 {
1959 //transform back to map crs
1960 const QgsPointXY mapPoint = t.transform( currentLevel, currentY );
1961 //transform back to composer coords
1962 gridLine.append( mMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) );
1963 }
1964 catch ( QgsCsException &cse )
1965 {
1966 Q_UNUSED( cse )
1967 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
1968 }
1969
1970 currentY += step;
1971 }
1972 //clip grid line to map polygon
1973 const QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mMap->rect() ) );
1974 QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1975 for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1976 {
1977 if ( !( *lineIt ).isEmpty() )
1978 {
1979 GridLine newLine;
1980 newLine.coordinate = currentLevel;
1981 newLine.coordinateType = AnnotationCoordinate::Longitude;
1982 newLine.line = QPolygonF( *lineIt );
1983 mGridLines.append( newLine );
1984 gridLineCount++;
1985 }
1986 }
1987 currentLevel += mEvaluatedIntervalX;
1988 if ( crosses180 && currentLevel > 180.0 )
1989 {
1990 currentLevel -= 360.0;
1991 crossed180 = true;
1992 }
1993 }
1994
1995 return 0;
1996}
1997
1998bool QgsLayoutItemMapGrid::shouldShowDivisionForSide( QgsLayoutItemMapGrid::AnnotationCoordinate coordinate, QgsLayoutItemMapGrid::BorderSide side ) const
1999{
2000 switch ( side )
2001 {
2003 return testFrameSideFlag( QgsLayoutItemMapGrid::FrameLeft ) && shouldShowForDisplayMode( coordinate, mEvaluatedLeftFrameDivisions );
2005 return testFrameSideFlag( QgsLayoutItemMapGrid::FrameRight ) && shouldShowForDisplayMode( coordinate, mEvaluatedRightFrameDivisions );
2007 return testFrameSideFlag( QgsLayoutItemMapGrid::FrameTop ) && shouldShowForDisplayMode( coordinate, mEvaluatedTopFrameDivisions );
2009 return testFrameSideFlag( QgsLayoutItemMapGrid::FrameBottom ) && shouldShowForDisplayMode( coordinate, mEvaluatedBottomFrameDivisions );
2010 }
2011 return false; // no warnings
2012}
2013
2014bool QgsLayoutItemMapGrid::shouldShowAnnotationForSide( QgsLayoutItemMapGrid::AnnotationCoordinate coordinate, QgsLayoutItemMapGrid::BorderSide side ) const
2015{
2016 switch ( side )
2017 {
2019 return shouldShowForDisplayMode( coordinate, mEvaluatedLeftGridAnnotationDisplay );
2021 return shouldShowForDisplayMode( coordinate, mEvaluatedRightGridAnnotationDisplay );
2023 return shouldShowForDisplayMode( coordinate, mEvaluatedTopGridAnnotationDisplay );
2025 return shouldShowForDisplayMode( coordinate, mEvaluatedBottomGridAnnotationDisplay );
2026 }
2027 return false; // no warnings
2028}
2029
2030bool QgsLayoutItemMapGrid::shouldShowForDisplayMode( QgsLayoutItemMapGrid::AnnotationCoordinate coordinate, QgsLayoutItemMapGrid::DisplayMode mode ) const
2031{
2032 return mode == QgsLayoutItemMapGrid::ShowAll
2035}
2036
2038{
2039 if ( ddValue.compare( QLatin1String( "x_only" ), Qt::CaseInsensitive ) == 0 )
2041 else if ( ddValue.compare( QLatin1String( "y_only" ), Qt::CaseInsensitive ) == 0 )
2043 else if ( ddValue.compare( QLatin1String( "disabled" ), Qt::CaseInsensitive ) == 0 )
2045 else if ( ddValue.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
2047 else
2048 return defValue;
2049}
2050
2051void QgsLayoutItemMapGrid::refreshDataDefinedProperties()
2052{
2053 const QgsExpressionContext context = createExpressionContext();
2054
2055 // if we are changing the grid interval or offset, then we also have to mark the transform as dirty
2056 mTransformDirty = mTransformDirty
2061
2062 mEvaluatedEnabled = mDataDefinedProperties.valueAsBool( QgsLayoutObject::DataDefinedProperty::MapGridEnabled, context, enabled() );
2063
2064 // suppress false positive clang tidy warning
2065 // NOLINTBEGIN(bugprone-branch-clone)
2067 {
2068 mDrawAnnotationProperty.reset( new QgsProperty( mDataDefinedProperties.property( QgsLayoutObject::DataDefinedProperty::MapGridDrawAnnotation ) ) );
2069 mDrawAnnotationProperty->prepare( context );
2070 }
2071 else
2072 {
2073 mDrawAnnotationProperty.reset();
2074 }
2075 // NOLINTEND(bugprone-branch-clone)
2076
2077 switch ( mGridUnit )
2078 {
2079 case MapUnit:
2080 case MM:
2081 case CM:
2082 {
2083 mEvaluatedIntervalX = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridIntervalX, context, mGridIntervalX );
2084 mEvaluatedIntervalY = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridIntervalY, context, mGridIntervalY );
2085 break;
2086 }
2087
2089 {
2090 if ( mMaximumIntervalWidth < mMinimumIntervalWidth )
2091 {
2092 mEvaluatedEnabled = false;
2093 }
2094 else
2095 {
2096 const double mapWidthMm = mLayout->renderContext().measurementConverter().convert( mMap->sizeWithUnits(), Qgis::LayoutUnit::Millimeters ).width();
2097 const double mapWidthMapUnits = mapWidth();
2098 const double minUnitsPerSeg = ( mMinimumIntervalWidth * mapWidthMapUnits ) / mapWidthMm;
2099 const double maxUnitsPerSeg = ( mMaximumIntervalWidth * mapWidthMapUnits ) / mapWidthMm;
2100 const double interval = QgsLayoutUtils::calculatePrettySize( minUnitsPerSeg, maxUnitsPerSeg );
2101 mEvaluatedIntervalX = interval;
2102 mEvaluatedIntervalY = interval;
2103 mTransformDirty = true;
2104 }
2105 break;
2106 }
2107 }
2108 mEvaluatedOffsetX = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridOffsetX, context, mGridOffsetX );
2109 mEvaluatedOffsetY = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridOffsetY, context, mGridOffsetY );
2110 mEvaluatedGridFrameWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridFrameSize, context, mGridFrameWidth );
2111 mEvaluatedGridFrameMargin = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridFrameMargin, context, mGridFrameMargin );
2112 mEvaluatedAnnotationFrameDistance = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridLabelDistance, context, mAnnotationFrameDistance );
2113 mEvaluatedCrossLength = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridCrossSize, context, mCrossLength );
2114 mEvaluatedGridFrameLineThickness = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridFrameLineThickness, context, mGridFramePenThickness );
2115 mEvaluatedLeftGridAnnotationDisplay = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridAnnotationDisplayLeft, context ), mLeftGridAnnotationDisplay );
2116 mEvaluatedRightGridAnnotationDisplay = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridAnnotationDisplayRight, context ), mRightGridAnnotationDisplay );
2117 mEvaluatedTopGridAnnotationDisplay = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridAnnotationDisplayTop, context ), mTopGridAnnotationDisplay );
2118 mEvaluatedBottomGridAnnotationDisplay = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridAnnotationDisplayBottom, context ), mBottomGridAnnotationDisplay );
2119 mEvaluatedLeftFrameDivisions = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridFrameDivisionsLeft, context ), mLeftFrameDivisions );
2120 mEvaluatedRightFrameDivisions = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridFrameDivisionsRight, context ), mRightFrameDivisions );
2121 mEvaluatedTopFrameDivisions = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridFrameDivisionsTop, context ), mTopFrameDivisions );
2122 mEvaluatedBottomFrameDivisions = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridFrameDivisionsBottom, context ), mBottomFrameDivisions );
2123}
2124
2125double QgsLayoutItemMapGrid::mapWidth() const
2126{
2127 if ( !mMap )
2128 {
2129 return 0.0;
2130 }
2131
2132 const QgsRectangle mapExtent = mMap->extent();
2133 const Qgis::DistanceUnit distanceUnit = mCRS.isValid() ? mCRS.mapUnits() : mMap->crs().mapUnits();
2134 if ( distanceUnit == Qgis::DistanceUnit::Unknown )
2135 {
2136 return mapExtent.width();
2137 }
2138 else
2139 {
2140 QgsDistanceArea da;
2141
2142 da.setSourceCrs( mMap->crs(), mLayout->project()->transformContext() );
2143 da.setEllipsoid( mLayout->project()->ellipsoid() );
2144
2146 double measure = 0;
2147 try
2148 {
2149 measure = da.measureLine( QgsPointXY( mapExtent.xMinimum(), mapExtent.yMinimum() ),
2150 QgsPointXY( mapExtent.xMaximum(), mapExtent.yMinimum() ) );
2151 measure /= QgsUnitTypes::fromUnitToUnitFactor( distanceUnit, units );
2152 }
2153 catch ( QgsCsException & )
2154 {
2155 // TODO report errors to user
2156 QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
2157 }
2158 return measure;
2159 }
2160}
2161
2162bool sortByDistance( QPair<qreal, QgsLayoutItemMapGrid::BorderSide> a, QPair<qreal, QgsLayoutItemMapGrid::BorderSide> b )
2163{
2164 return a.first < b.first;
2165}
2166
2167QgsLayoutItemMapGrid::BorderSide QgsLayoutItemMapGrid::borderForLineCoord( QPointF p, const AnnotationCoordinate coordinateType ) const
2168{
2169 if ( !mMap )
2170 {
2172 }
2173
2174 const double tolerance = std::max( mMap->frameEnabled() ? mMap->pen().widthF() : 0.0, 1.0 );
2175
2176 //check for corner coordinates
2177 if ( ( p.y() <= tolerance && p.x() <= tolerance ) // top left
2178 || ( p.y() <= tolerance && p.x() >= ( mMap->rect().width() - tolerance ) ) //top right
2179 || ( p.y() >= ( mMap->rect().height() - tolerance ) && p.x() <= tolerance ) //bottom left
2180 || ( p.y() >= ( mMap->rect().height() - tolerance ) && p.x() >= ( mMap->rect().width() - tolerance ) ) //bottom right
2181 )
2182 {
2183 //coordinate is in corner - fall back to preferred side for coordinate type
2184 if ( coordinateType == QgsLayoutItemMapGrid::Latitude )
2185 {
2186 if ( p.x() <= tolerance )
2187 {
2189 }
2190 else
2191 {
2193 }
2194 }
2195 else
2196 {
2197 if ( p.y() <= tolerance )
2198 {
2200 }
2201 else
2202 {
2204 }
2205 }
2206 }
2207
2208 //otherwise, guess side based on closest map side to point
2209 QList< QPair<qreal, QgsLayoutItemMapGrid::BorderSide > > distanceToSide;
2210 distanceToSide << qMakePair( p.x(), QgsLayoutItemMapGrid::Left );
2211 distanceToSide << qMakePair( mMap->rect().width() - p.x(), QgsLayoutItemMapGrid::Right );
2212 distanceToSide << qMakePair( p.y(), QgsLayoutItemMapGrid::Top );
2213 distanceToSide << qMakePair( mMap->rect().height() - p.y(), QgsLayoutItemMapGrid::Bottom );
2214
2215 std::sort( distanceToSide.begin(), distanceToSide.end(), sortByDistance );
2216 return distanceToSide.at( 0 ).second;
2217}
2218
2220{
2221 mGridLineSymbol.reset( symbol );
2222}
2223
2225{
2226 return mGridLineSymbol.get();
2227}
2228
2230{
2231 return mGridLineSymbol.get();
2232}
2233
2235{
2236 mGridMarkerSymbol.reset( symbol );
2237}
2238
2240{
2241 return mGridMarkerSymbol.get();
2242}
2243
2245{
2246 return mGridMarkerSymbol.get();
2247}
2248
2250{
2251 mAnnotationFormat.setFont( font );
2252 if ( font.pointSizeF() > 0 )
2253 {
2254 mAnnotationFormat.setSize( font.pointSizeF() );
2255 mAnnotationFormat.setSizeUnit( Qgis::RenderUnit::Points );
2256 }
2257 else if ( font.pixelSize() > 0 )
2258 {
2259 mAnnotationFormat.setSize( font.pixelSize() );
2260 mAnnotationFormat.setSizeUnit( Qgis::RenderUnit::Pixels );
2261 }
2262}
2263
2265{
2266 return mAnnotationFormat.toQFont();
2267}
2268
2270{
2271 mAnnotationFormat.setColor( color );
2272}
2273
2275{
2276 return mAnnotationFormat.color();
2277}
2278
2280{
2281 switch ( border )
2282 {
2284 mLeftGridAnnotationDisplay = display;
2285 break;
2287 mRightGridAnnotationDisplay = display;
2288 break;
2290 mTopGridAnnotationDisplay = display;
2291 break;
2293 mBottomGridAnnotationDisplay = display;
2294 break;
2295 }
2296
2297 refreshDataDefinedProperties();
2298
2299 if ( mMap )
2300 {
2301 mMap->updateBoundingRect();
2302 mMap->update();
2303 }
2304}
2305
2307{
2308 switch ( border )
2309 {
2311 return mLeftGridAnnotationDisplay;
2313 return mRightGridAnnotationDisplay;
2315 return mTopGridAnnotationDisplay;
2317 return mBottomGridAnnotationDisplay;
2318 }
2319 return mBottomGridAnnotationDisplay; // no warnings
2320}
2321
2323{
2324 double top = 0.0;
2325 double right = 0.0;
2326 double bottom = 0.0;
2327 double left = 0.0;
2328 calculateMaxExtension( top, right, bottom, left );
2329 return std::max( std::max( std::max( top, right ), bottom ), left );
2330}
2331
2332void QgsLayoutItemMapGrid::calculateMaxExtension( double &top, double &right, double &bottom, double &left ) const
2333{
2334 top = 0.0;
2335 right = 0.0;
2336 bottom = 0.0;
2337 left = 0.0;
2338
2339 if ( !mMap || !mEvaluatedEnabled )
2340 {
2341 return;
2342 }
2343
2344 //setup render context
2346 const QgsExpressionContext expressionContext = createExpressionContext();
2347 context.setExpressionContext( expressionContext );
2348
2349 GridExtension extension;
2350
2351 //collect grid lines
2352 switch ( mGridUnit )
2353 {
2354 case MapUnit:
2356 {
2357 if ( mCRS.isValid() && mCRS != mMap->crs() )
2358 {
2359 drawGridCrsTransform( context, 0, true );
2360 break;
2361 }
2362 }
2363 [[fallthrough]];
2364 case CM:
2365 case MM:
2366 drawGridNoTransform( context, 0, true );
2367 break;
2368 }
2369
2370 if ( mGridFrameStyle != QgsLayoutItemMapGrid::NoFrame || mShowGridAnnotation )
2371 updateGridLinesAnnotationsPositions();
2372
2373 if ( mGridFrameStyle != QgsLayoutItemMapGrid::NoFrame )
2374 {
2375 drawGridFrame( nullptr, &extension );
2376 }
2377
2378 if ( mShowGridAnnotation )
2379 {
2380 drawCoordinateAnnotations( context, context.expressionContext(), &extension );
2381 }
2382
2383 top = extension.top;
2384 right = extension.right;
2385 bottom = extension.bottom;
2386 left = extension.left;
2387}
2388
2390{
2392 refreshDataDefinedProperties();
2393}
2394
2396{
2397 if ( unit == mGridUnit )
2398 {
2399 return;
2400 }
2401 mGridUnit = unit;
2402 mTransformDirty = true;
2403}
2404
2405void QgsLayoutItemMapGrid::setIntervalX( const double interval )
2406{
2407 if ( qgsDoubleNear( interval, mGridIntervalX ) )
2408 {
2409 return;
2410 }
2411 mGridIntervalX = interval;
2412 mTransformDirty = true;
2413 refreshDataDefinedProperties();
2414}
2415
2416void QgsLayoutItemMapGrid::setIntervalY( const double interval )
2417{
2418 if ( qgsDoubleNear( interval, mGridIntervalY ) )
2419 {
2420 return;
2421 }
2422 mGridIntervalY = interval;
2423 mTransformDirty = true;
2424 refreshDataDefinedProperties();
2425}
2426
2427void QgsLayoutItemMapGrid::setOffsetX( const double offset )
2428{
2429 if ( qgsDoubleNear( offset, mGridOffsetX ) )
2430 {
2431 return;
2432 }
2433 mGridOffsetX = offset;
2434 mTransformDirty = true;
2435 refreshDataDefinedProperties();
2436}
2437
2438void QgsLayoutItemMapGrid::setOffsetY( const double offset )
2439{
2440 if ( qgsDoubleNear( offset, mGridOffsetY ) )
2441 {
2442 return;
2443 }
2444 mGridOffsetY = offset;
2445 mTransformDirty = true;
2446 refreshDataDefinedProperties();
2447}
2448
2450{
2451 if ( qgsDoubleNear( minWidth, mMinimumIntervalWidth ) )
2452 {
2453 return;
2454 }
2455 mMinimumIntervalWidth = minWidth;
2456 mTransformDirty = true;
2457 refreshDataDefinedProperties();
2458}
2459
2461{
2462 if ( qgsDoubleNear( maxWidth, mMaximumIntervalWidth ) )
2463 {
2464 return;
2465 }
2466 mMaximumIntervalWidth = maxWidth;
2467 mTransformDirty = true;
2468 refreshDataDefinedProperties();
2469}
2470
2472{
2473 if ( style == mGridStyle )
2474 {
2475 return;
2476 }
2477 mGridStyle = style;
2478 mTransformDirty = true;
2479}
2480
2481void QgsLayoutItemMapGrid::setCrossLength( const double length )
2482{
2483 mCrossLength = length;
2484 refreshDataDefinedProperties();
2485}
2486
2488{
2489 switch ( border )
2490 {
2492 mLeftGridAnnotationDirection = direction;
2493 break;
2495 mRightGridAnnotationDirection = direction;
2496 break;
2498 mTopGridAnnotationDirection = direction;
2499 break;
2501 mBottomGridAnnotationDirection = direction;
2502 break;
2503 }
2504
2505 if ( mMap )
2506 {
2507 mMap->updateBoundingRect();
2508 mMap->update();
2509 }
2510}
2511
2513{
2514 mGridFrameSides = flags;
2515}
2516
2518{
2519 if ( on )
2520 mGridFrameSides |= flag;
2521 else
2522 mGridFrameSides &= ~flag;
2523}
2524
2529
2531{
2533 context.appendScope( new QgsExpressionContextScope( tr( "Grid" ) ) );
2534 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_number" ), 0, true ) );
2535 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), "x", true ) );
2536 context.setHighlightedVariables( QStringList() << QStringLiteral( "grid_number" ) << QStringLiteral( "grid_axis" ) );
2537 return context;
2538}
2539
2541{
2542 if ( mGridLineSymbol )
2543 {
2544 QgsStyleSymbolEntity entity( mGridLineSymbol.get() );
2545 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, QStringLiteral( "grid" ), QObject::tr( "Grid" ) ) ) )
2546 return false;
2547 }
2548 if ( mGridMarkerSymbol )
2549 {
2550 QgsStyleSymbolEntity entity( mGridMarkerSymbol.get() );
2551 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, QStringLiteral( "grid" ), QObject::tr( "Grid" ) ) ) )
2552 return false;
2553 }
2554
2555 return true;
2556}
2557
2559{
2560 mTransformDirty = true;
2561 refreshDataDefinedProperties();
2562 mMap->updateBoundingRect();
2563 mMap->update();
2564}
2565
2567{
2568 return mGridFrameSides.testFlag( flag );
2569}
2570
2571void QgsLayoutItemMapGrid::setFrameWidth( const double width )
2572{
2573 mGridFrameWidth = width;
2574 refreshDataDefinedProperties();
2575}
2576
2577void QgsLayoutItemMapGrid::setFrameMargin( const double margin )
2578{
2579 mGridFrameMargin = margin;
2580 refreshDataDefinedProperties();
2581}
2582
2584{
2585 mGridFramePenThickness = width;
2586 refreshDataDefinedProperties();
2587}
2588
2590{
2591 mLeftGridAnnotationDirection = direction;
2592 mRightGridAnnotationDirection = direction;
2593 mTopGridAnnotationDirection = direction;
2594 mBottomGridAnnotationDirection = direction;
2595}
2596
2598{
2599 switch ( border )
2600 {
2602 mLeftGridAnnotationPosition = position;
2603 break;
2605 mRightGridAnnotationPosition = position;
2606 break;
2608 mTopGridAnnotationPosition = position;
2609 break;
2611 mBottomGridAnnotationPosition = position;
2612 break;
2613 }
2614
2615 if ( mMap )
2616 {
2617 mMap->updateBoundingRect();
2618 mMap->update();
2619 }
2620}
2621
2623{
2624 switch ( border )
2625 {
2627 return mLeftGridAnnotationPosition;
2629 return mRightGridAnnotationPosition;
2631 return mTopGridAnnotationPosition;
2633 return mBottomGridAnnotationPosition;
2634 }
2635 return mLeftGridAnnotationPosition; // no warnings
2636}
2637
2639{
2640 mAnnotationFrameDistance = distance;
2641 refreshDataDefinedProperties();
2642}
2643
2645{
2646 if ( !mMap )
2647 {
2648 return mLeftGridAnnotationDirection;
2649 }
2650
2651 switch ( border )
2652 {
2654 return mLeftGridAnnotationDirection;
2656 return mRightGridAnnotationDirection;
2658 return mTopGridAnnotationDirection;
2660 return mBottomGridAnnotationDirection;
2661 }
2662 return mLeftGridAnnotationDirection; // no warnings
2663}
2664
2665void QgsLayoutItemMapGrid::setAnnotationExpression( const QString &expression )
2666{
2667 mGridAnnotationExpressionString = expression;
2668 mGridAnnotationExpression.reset();
2669}
2670
2672{
2673 switch ( border )
2674 {
2676 mLeftFrameDivisions = divisions;
2677 break;
2679 mRightFrameDivisions = divisions;
2680 break;
2682 mTopFrameDivisions = divisions;
2683 break;
2685 mBottomFrameDivisions = divisions;
2686 break;
2687 }
2688
2689 refreshDataDefinedProperties();
2690
2691 if ( mMap )
2692 {
2693 mMap->update();
2694 }
2695}
2696
2698{
2699 switch ( border )
2700 {
2702 return mLeftFrameDivisions;
2704 return mRightFrameDivisions;
2706 return mTopFrameDivisions;
2708 return mBottomFrameDivisions;
2709 }
2710 return mLeftFrameDivisions; // no warnings
2711}
2712
2713int QgsLayoutItemMapGrid::crsGridParams( QgsRectangle &crsRect, QgsCoordinateTransform &inverseTransform ) const
2714{
2715 if ( !mMap )
2716 {
2717 return 1;
2718 }
2719
2720 try
2721 {
2722 const QgsCoordinateTransform tr( mMap->crs(), mCRS, mLayout->project() );
2723 QgsCoordinateTransform extentTransform = tr;
2724 extentTransform.setBallparkTransformsAreAppropriate( true );
2725 const QPolygonF mapPolygon = mMap->transformedMapPolygon();
2726 const QRectF mbr = mapPolygon.boundingRect();
2727 const QgsRectangle mapBoundingRect( mbr.left(), mbr.bottom(), mbr.right(), mbr.top() );
2728
2729
2730 if ( mCRS.isGeographic() )
2731 {
2732 //handle crossing the 180 degree longitude line
2733 QgsPointXY lowerLeft( mapBoundingRect.xMinimum(), mapBoundingRect.yMinimum() );
2734 QgsPointXY upperRight( mapBoundingRect.xMaximum(), mapBoundingRect.yMaximum() );
2735
2736 lowerLeft = tr.transform( lowerLeft.x(), lowerLeft.y() );
2737 upperRight = tr.transform( upperRight.x(), upperRight.y() );
2738
2739 if ( lowerLeft.x() > upperRight.x() )
2740 {
2741 //we've crossed the line
2742 crsRect = extentTransform.transformBoundingBox( mapBoundingRect, Qgis::TransformDirection::Forward, true );
2743 }
2744 else
2745 {
2746 //didn't cross the line
2747 crsRect = extentTransform.transformBoundingBox( mapBoundingRect );
2748 }
2749 }
2750 else
2751 {
2752 crsRect = extentTransform.transformBoundingBox( mapBoundingRect );
2753 }
2754
2755 inverseTransform = QgsCoordinateTransform( mCRS, mMap->crs(), mLayout->project() );
2756 }
2757 catch ( QgsCsException &cse )
2758 {
2759 Q_UNUSED( cse )
2760 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
2761 return 1;
2762 }
2763 return 0;
2764}
2765
2766QList<QPolygonF> QgsLayoutItemMapGrid::trimLinesToMap( const QPolygonF &line, const QgsRectangle &rect )
2767{
2768 const QgsGeometry lineGeom = QgsGeometry::fromQPolygonF( line );
2769 const QgsGeometry rectGeom = QgsGeometry::fromRect( rect );
2770
2771 const QgsGeometry intersected = lineGeom.intersection( rectGeom );
2772 const QVector<QgsGeometry> intersectedParts = intersected.asGeometryCollection();
2773
2774 QList<QPolygonF> trimmedLines;
2775 QVector<QgsGeometry>::const_iterator geomIt = intersectedParts.constBegin();
2776 for ( ; geomIt != intersectedParts.constEnd(); ++geomIt )
2777 {
2778 trimmedLines << ( *geomIt ).asQPolygonF();
2779 }
2780 return trimmedLines;
2781}
2782
2784{
2785 // grid
2786 setStyle( other->style() );
2787 setIntervalX( other->intervalX() );
2788 setIntervalY( other->intervalY() );
2789 setOffsetX( other->offsetX() );
2790 setOffsetY( other->offsetX() );
2791 setCrossLength( other->crossLength() );
2792 setFrameStyle( other->frameStyle() );
2794 setFrameWidth( other->frameWidth() );
2795 setFrameMargin( other->frameMargin() );
2796 setFramePenSize( other->framePenSize() );
2797 setFramePenColor( other->framePenColor() );
2800
2805
2814
2815 if ( other->lineSymbol() )
2816 {
2817 setLineSymbol( other->lineSymbol()->clone() );
2818 }
2819
2820 if ( other->markerSymbol() )
2821 {
2822 setMarkerSymbol( other->markerSymbol()->clone() );
2823 }
2824
2825 setCrs( other->crs() );
2826
2827 setBlendMode( other->blendMode() );
2828
2829 //annotation
2833
2848
2850 setUnits( other->units() );
2853
2855 refreshDataDefinedProperties();
2856}
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
Definition qgis.h:2704
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
Definition qgis.h:2705
@ Millimeters
Millimeters.
Definition qgis.h:5204
DistanceUnit
Units of distance.
Definition qgis.h:5013
@ Unknown
Unknown distance unit.
Definition qgis.h:5063
@ Millimeters
Millimeters.
Definition qgis.h:5184
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5188
@ Pixels
Pixels.
Definition qgis.h:5186
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
Definition qgis.h:2762
@ Forward
Forward transform (from source to destination).
Definition qgis.h:2672
@ Antialiasing
Use antialiasing when drawing items.
Definition qgis.h:5240
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Format
Available formats for displaying coordinates.
@ FormatDecimalDegrees
Decimal degrees, eg 30.7555 degrees.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Represents a coordinate reference system (CRS).
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
double measureLine(const QVector< QgsPointXY > &points) const
Measures the length of a line with multiple segments.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
Qgis::DistanceUnit lengthUnits() const
Returns the units of distance for length calculations made by this object.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
QString what() const
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user.
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QList< QgsLayoutItemMapGrid * > asList() const
Returns a list of QgsLayoutItemMapGrids contained by the stack.
void calculateMaxGridExtension(double &top, double &right, double &bottom, double &left) const
Calculates the maximum distance grids within the stack extend beyond the QgsLayoutItemMap's item rect...
void removeGrid(const QString &gridId)
Removes a grid with matching gridId from the stack and deletes the corresponding QgsLayoutItemMapGrid...
double maxGridExtension() const
Calculates the maximum distance grids within the stack extend beyond the QgsLayoutItemMap's item rect...
void addGrid(QgsLayoutItemMapGrid *grid)
Adds a new map grid to the stack and takes ownership of the grid.
QgsLayoutItemMapGrid * grid(const QString &gridId) const
Returns a reference to a grid with matching gridId within the stack.
bool readXml(const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets the item stack's state from a DOM document, where element is a DOM node corresponding to a 'Layo...
QgsLayoutItemMapGrid & operator[](int index)
Returns a reference to a grid at the specified index within the stack.
void moveGridUp(const QString &gridId)
Moves a grid with matching gridId up the stack, causing it to be rendered above other grids.
void moveGridDown(const QString &gridId)
Moves a grid with matching gridId down the stack, causing it to be rendered below other grids.
QgsLayoutItemMapGridStack(QgsLayoutItemMap *map)
Constructor for QgsLayoutItemMapGridStack, attached to the specified map.
An individual grid which is drawn above the map content in a QgsLayoutItemMap.
void setFrameSideFlags(QgsLayoutItemMapGrid::FrameSideFlags flags)
Sets flags for grid frame sides.
bool rotatedAnnotationsEnabled() const
Gets whether annotations rotation for rotated or reprojected grids is enabled.
QString annotationExpression() const
Returns the expression used for drawing grid annotations.
double rotatedTicksMarginToCorner() const
Gets the margin to corners (in canvas units) below which outwards facing ticks are not drawn.
GridStyle
Grid drawing style.
@ Markers
Draw markers at intersections of grid lines.
@ Cross
Draw line crosses at intersections of grid lines.
@ FrameAnnotationsOnly
No grid lines over the map, only draw frame and annotations.
void calculateMaxExtension(double &top, double &right, double &bottom, double &left) const
Calculates the maximum distance the grid extends beyond the QgsLayoutItemMap's item rect.
void setFrameFillColor2(const QColor &color)
Sets the second fill color used for the grid frame.
GridUnit
Unit for grid values.
@ CM
Grid units in centimeters.
@ MM
Grid units in millimeters.
@ DynamicPageSizeBased
Dynamically sized, based on a on-page size range.
@ MapUnit
Grid units follow map units.
bool writeXml(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores map item state in a DOM element, where element is the DOM element corresponding to a 'LayoutMa...
void refresh() override
Refreshes the object, causing a recalculation of any property overrides.
GridStyle style() const
Returns the grid's style, which controls how the grid is drawn over the map's contents.
void setFrameSideFlag(QgsLayoutItemMapGrid::FrameSideFlag flag, bool on=true)
Sets whether the grid frame is drawn for a certain side of the map item.
FrameSideFlag
Flags for controlling which side of the map a frame is drawn on.
@ FrameBottom
Bottom side of map.
@ FrameLeft
Left side of map.
@ FrameRight
Right side of map.
Q_DECL_DEPRECATED void setAnnotationFontColor(const QColor &color)
Sets the font color used for drawing grid annotations.
void setAnnotationFormat(const AnnotationFormat format)
Sets the format for drawing grid annotations.
double frameWidth() const
Gets the grid frame width in layout units.
void draw(QPainter *painter) override
Draws the item on to a destination painter.
double crossLength() const
Retrieves the length (in layout units) of the cross segments drawn for the grid.
void setIntervalY(double interval)
Sets the interval between grid lines in the y-direction.
void setRotatedTicksMinimumAngle(const double angle)
Sets the minimum angle (in degrees) below which ticks are not drawn.
Q_DECL_DEPRECATED QColor annotationFontColor() const
Returns the font color used for drawing grid annotations.
QPainter::CompositionMode blendMode() const
Retrieves the blending mode used for drawing the grid.
void setAnnotationEnabled(const bool enabled)
Sets whether annotations should be shown for the grid.
QgsTextFormat annotationTextFormat() const
Returns the text format used when rendering grid annotations.
AnnotationFormat annotationFormat() const
Returns the format for drawing grid annotations.
double framePenSize() const
Retrieves the width of the stroke drawn in the grid frame.
void setFramePenColor(const QColor &color)
Sets the color of the stroke drawn in the grid frame.
void setFramePenSize(const double width)
Sets the width of the stroke drawn in the grid frame.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
void setAnnotationDisplay(DisplayMode display, BorderSide border)
Sets what types of grid annotations should be drawn for a specified side of the map frame,...
void setRotatedTicksEnabled(const bool state)
Enable/disable ticks rotation for rotated or reprojected grids.
Q_DECL_DEPRECATED QFont annotationFont() const
Returns the font used for drawing grid annotations.
double maxExtension() const
Calculates the maximum distance the grid extends beyond the QgsLayoutItemMap's item rect (in layout u...
AnnotationPosition
Position for grid annotations.
@ InsideMapFrame
Draw annotations inside the map frame.
@ OutsideMapFrame
Draw annotations outside the map frame.
TickLengthMode rotatedAnnotationsLengthMode() const
Returns the annotation length calculation mode.
double rotatedTicksMinimumAngle() const
Gets the minimum angle (in degrees) below which ticks are not drawn.
void setAnnotationPosition(AnnotationPosition position, BorderSide side)
Sets the position for the grid annotations on a specified side of the map frame.
AnnotationPosition annotationPosition(BorderSide side) const
Returns the position for the grid annotations on a specified side of the map frame.
void setUnits(GridUnit unit)
Sets the unit to use for grid measurements such as the interval and offset for grid lines.
QgsLayoutItemMapGrid::FrameSideFlags frameSideFlags() const
Returns the flags which control which sides of the map item the grid frame is drawn on.
bool readXml(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets the map item state from a DOM document, where element is the DOM node corresponding to a 'Layout...
bool testFrameSideFlag(FrameSideFlag flag) const
Tests whether the grid frame should be drawn on a specified side of the map item.
void setFrameDivisions(DisplayMode divisions, BorderSide side)
Sets what type of grid divisions should be used for frames on a specified side of the map.
void setMinimumIntervalWidth(double width)
Sets the minimum width (in millimeters) for grid segments.
AnnotationCoordinate
Annotation coordinate type.
@ Latitude
Coordinate is a latitude value.
@ Longitude
Coordinate is a longitude value.
void setIntervalX(double interval)
Sets the interval between grid lines in the x-direction.
void setCrossLength(const double length)
Sets the length (in layout units) of the cross segments drawn for the grid.
void setRotatedTicksLengthMode(const TickLengthMode mode)
Sets the tick length calculation mode.
void setEnabled(bool enabled) override
Controls whether the item will be drawn.
DisplayMode
Display settings for grid annotations and frames.
@ LongitudeOnly
Show longitude/x annotations/divisions only.
@ ShowAll
Show both latitude and longitude annotations/divisions.
@ LatitudeOnly
Show latitude/y annotations/divisions only.
void setAnnotationFrameDistance(const double distance)
Sets the distance between the map frame and annotations.
void crsChanged()
Emitted whenever the grid's CRS is changed.
void setRotatedAnnotationsMinimumAngle(const double angle)
Sets the minimum angle (in degrees) below which annotations are not drawn.
void setFrameMargin(const double margin)
Sets the grid frame margin (in layout units).
double rotatedAnnotationsMinimumAngle() const
Gets the minimum angle (in degrees) below which annotations are not drawn.
void setAnnotationTextFormat(const QgsTextFormat &format)
Sets the text format to use when rendering grid annotations.
QgsLayoutItemMapGrid(const QString &name, QgsLayoutItemMap *map)
Constructor for QgsLayoutItemMapGrid.
~QgsLayoutItemMapGrid() override
void copyProperties(const QgsLayoutItemMapGrid *other)
Copies properties from specified map grid.
void setBlendMode(const QPainter::CompositionMode mode)
Sets the blending mode used for drawing the grid.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the marker symbol used for drawing grid points.
bool annotationEnabled() const
Returns whether annotations are shown for the grid.
void setMaximumIntervalWidth(double width)
Sets the maximum width (in millimeters) for grid segments.
void setFrameStyle(const FrameStyle style)
Sets the grid frame style.
QFlags< FrameSideFlag > FrameSideFlags
QColor framePenColor() const
Retrieves the color of the stroke drawn in the grid frame.
bool usesAdvancedEffects() const override
Returns true if the item is drawn using advanced effects, such as blend modes.
FrameStyle frameStyle() const
Returns the grid frame style.
Q_DECL_DEPRECATED void setAnnotationFont(const QFont &font)
Sets the font used for drawing grid annotations.
void setRotatedTicksMarginToCorner(const double margin)
Sets the margin to corners (in canvas units) below which outwards facing ticks are not drawn.
void setAnnotationPrecision(const int precision)
Sets the coordinate precision for grid annotations.
QColor frameFillColor2() const
Retrieves the second fill color for the grid frame.
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used for drawing grid lines.
QgsCoordinateReferenceSystem crs() const
Retrieves the CRS for the grid.
void setRotatedAnnotationsMarginToCorner(const double margin)
Sets the margin to corners (in canvas units) below which outwards facing annotations are not drawn.
double rotatedAnnotationsMarginToCorner() const
Gets the margin to corners (in canvas units) below which outwards facing annotations are not drawn.
void setRotatedAnnotationsLengthMode(const TickLengthMode mode)
Sets the annotation length calculation mode.
TickLengthMode
Tick length mode (useful for rotated grids).
@ OrthogonalTicks
Align ticks orthogonaly.
double offsetX() const
Returns the offset for grid lines in the x-direction.
bool rotatedTicksEnabled() const
Gets whether ticks rotation for rotated or reprojected grids is enabled.
AnnotationFormat
Format for displaying grid annotations.
@ DegreeMinuteSecondNoSuffix
Degree/minutes/seconds, use - for S/W coordinates.
@ DegreeMinuteSecondPadded
Degree/minutes/seconds, with minutes using leading zeros where required.
@ DegreeMinuteSecond
Degree/minutes/seconds, use NSEW suffix.
@ DecimalWithSuffix
Decimal degrees, use NSEW suffix.
@ DegreeMinute
Degree/minutes, use NSEW suffix.
@ DegreeMinuteNoSuffix
Degree/minutes, use - for S/W coordinates.
@ Decimal
Decimal degrees, use - for S/W coordinates.
@ DegreeMinutePadded
Degree/minutes, with minutes using leading zeros where required.
@ CustomFormat
Custom expression-based format.
DisplayMode frameDivisions(BorderSide side) const
Returns the type of grid divisions which are used for frames on a specified side of the map.
AnnotationDirection
Direction of grid annotations.
@ OnTick
Draw annotations parallel to tick (on the line).
@ Horizontal
Draw annotations horizontally.
@ Vertical
Draw annotations vertically, ascending.
@ AboveTick
Draw annotations parallel to tick (above the line).
@ UnderTick
Draw annotations parallel to tick (under the line).
@ VerticalDescending
Draw annotations vertically, descending.
double annotationFrameDistance() const
Returns the distance between the map frame and annotations.
double intervalY() const
Returns the interval between grid lines in the y-direction.
GridUnit units() const
Returns the units used for grid measurements such as the interval and offset for grid lines.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs for the grid.
double minimumIntervalWidth() const
Returns the minimum width (in millimeters) for grid segments.
void setOffsetY(double offset)
Sets the offset for grid lines in the y-direction.
const QgsMarkerSymbol * markerSymbol() const
Returns the marker symbol used for drawing grid points.
const QgsLineSymbol * lineSymbol() const
Returns the line symbol used for drawing grid lines.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QColor frameFillColor1() const
Retrieves the first fill color for the grid frame.
void setRotatedAnnotationsEnabled(const bool state)
Enable/disable annotations rotation for rotated or reprojected grids.
FrameStyle
Style for grid frame.
@ Zebra
Black/white pattern.
@ InteriorTicks
Tick markers drawn inside map frame.
@ LineBorder
Simple solid line frame.
@ InteriorExteriorTicks
Tick markers drawn both inside and outside the map frame.
@ LineBorderNautical
Simple solid line frame, with nautical style diagonals on corners.
@ ExteriorTicks
Tick markers drawn outside map frame.
@ NoFrame
Disable grid frame.
@ ZebraNautical
Black/white pattern, with nautical style diagonals on corners.
void setFrameWidth(const double width)
Sets the grid frame width (in layout units).
void setFrameFillColor1(const QColor &color)
Sets the first fill color used for the grid frame.
double frameMargin() const
Sets the grid frame Margin (in layout units).
DisplayMode annotationDisplay(BorderSide border) const
Returns the display mode for the grid annotations on a specified side of the map frame.
void setOffsetX(double offset)
Sets the offset for grid lines in the x-direction.
BorderSide
Border sides for annotations.
AnnotationDirection annotationDirection(BorderSide border) const
Returns the direction for drawing frame annotations, on the specified side of the map.
void setAnnotationDirection(AnnotationDirection direction, BorderSide side)
Sets the direction for drawing frame annotations for the specified map side.
void setGridLineColor(const QColor &color)
Sets the color of grid lines.
void setGridLineWidth(double width)
Sets the width of grid lines (in layout units).
int annotationPrecision() const
Returns the coordinate precision for grid annotations, which is the number of decimal places shown wh...
void setStyle(GridStyle style)
Sets the grid style, which controls how the grid is drawn over the map's contents.
double maximumIntervalWidth() const
Returns the maximum width (in millimeters) for grid segments.
void setAnnotationExpression(const QString &expression)
Sets the expression used for drawing grid annotations.
TickLengthMode rotatedTicksLengthMode() const
Returns the grid frame style.
double intervalX() const
Returns the interval between grid lines in the x-direction.
void addItem(QgsLayoutItemMapItem *item)
Adds a new map item to the stack and takes ownership of the item.
void removeItem(const QString &itemId)
Removes an item which matching itemId from the stack and deletes the corresponding QgsLayoutItemMapIt...
QgsLayoutItemMapItem * item(int index) const
Returns a reference to the item at the specified index within the stack.
void moveItemUp(const QString &itemId)
Moves an item which matching itemId up the stack, causing it to be rendered above other items.
void removeItems()
Clears the item stack and deletes all QgsLayoutItemMapItems contained by the stack.
QgsLayoutItemMapItemStack(QgsLayoutItemMap *map)
Constructor for QgsLayoutItemMapItemStack, attached to the specified map.
QList< QgsLayoutItemMapItem * > mItems
void moveItemDown(const QString &itemId)
Moves an item which matching itemId up the stack, causing it to be rendered above other items.
An item which is drawn inside a QgsLayoutItemMap, e.g., a grid or map overview.
QgsLayoutItemMap * mMap
Associated map.
virtual bool readXml(const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context)
Sets the map item state from a DOM document, where element is the DOM node corresponding to a 'Layout...
virtual bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Stores map item state in a DOM element, where element is the DOM element corresponding to a 'LayoutMa...
virtual void setEnabled(bool enabled)
Controls whether the item will be drawn.
QString name() const
Returns the friendly display name for the item.
QgsLayoutItemMapItem(const QString &name, QgsLayoutItemMap *map)
Constructor for QgsLayoutItemMapItem, attached to the specified map.
bool enabled() const
Returns whether the item will be drawn.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
const QgsLayoutItemMap * map() const
Returns the layout item map for the item.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
void crsChanged()
Emitted when the map's coordinate reference system is changed.
QgsPropertyCollection mDataDefinedProperties
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the object's property collection, used for data defined overrides.
QPointer< QgsLayout > mLayout
@ MapGridFrameDivisionsBottom
Map frame division display bottom.
@ MapGridFrameDivisionsTop
Map frame division display top.
@ MapGridAnnotationDisplayLeft
Map annotation display left.
@ MapGridFrameMargin
Map grid frame margin.
@ MapGridDrawAnnotation
Map annotation visibility (for individual annotations).
@ MapGridAnnotationDisplayRight
Map annotation display right.
@ MapGridAnnotationDisplayTop
Map annotation display top.
@ MapGridAnnotationDisplayBottom
Map annotation display bottom.
@ MapGridFrameDivisionsRight
Map frame division display right.
@ MapGridLabelDistance
Map grid label distance.
@ MapGridFrameLineThickness
Map grid frame line thickness.
@ MapGridFrameDivisionsLeft
Map frame division display left.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the objects's property collection, used for data defined overrides.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
static double calculatePrettySize(double minimumSize, double maximumSize)
Calculates a "pretty" size which falls between the range [minimumSize, maximumSize].
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
static std::unique_ptr< QgsLineSymbol > createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
A marker symbol type, for rendering Point and MultiPoint geometries.
static std::unique_ptr< QgsMarkerSymbol > createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
Contains information about the context of a rendering operation.
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.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setRasterizedRenderingPolicy(Qgis::RasterizedRenderingPolicy policy)
Sets the policy controlling when rasterisation of content during renders is permitted.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected).
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
Stores settings for use within QGIS.
Definition qgssettings.h:65
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition qgsstyle.h:1397
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QPointF pointOnLineWithDistance(QPointF startPoint, QPointF directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
void setColor(const QColor &color) const
Sets the color for the symbol.
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
static void drawText(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, Qgis::TextVerticalAlignment vAlignment=Qgis::TextVerticalAlignment::Top, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle)
Draws text within a rectangle using the specified settings.
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Point, QFontMetricsF *fontMetrics=nullptr, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), double maxLineWidth=0)
Returns the height of a text based on a given format.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:6524
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6648
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:61
QgsLayoutItemMapGrid::DisplayMode gridAnnotationDisplayModeFromDD(QString ddValue, QgsLayoutItemMapGrid::DisplayMode defValue)
#define MAX_GRID_LINES
bool sortByDistance(QPair< qreal, QgsLayoutItemMapGrid::BorderSide > a, QPair< qreal, QgsLayoutItemMapGrid::BorderSide > b)
QVector2D borderToNormal2D(QgsLayoutItemMapGrid::BorderSide border)
QVector2D borderToVector2D(QgsLayoutItemMapGrid::BorderSide border)
#define QgsDebugError(str)
Definition qgslogger.h:57
Single variable definition for use within a QgsExpressionContextScope.
Contains information relating to the style entity currently being visited.