QGIS API Documentation 3.99.0-Master (c03dd32cbdd)
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 "qgstextdocument.h"
45#include "qgstextrenderer.h"
46#include "qgsunittypes.h"
47
48#include <QPainter>
49#include <QPen>
50#include <QString>
51#include <QVector2D>
52
53#include "moc_qgslayoutitemmapgrid.cpp"
54
55using namespace Qt::StringLiterals;
56
57#define MAX_GRID_LINES 1000 //maximum number of horizontal or vertical grid lines to draw
58
64
69
70void QgsLayoutItemMapGridStack::removeGrid( const QString &gridId )
71{
73}
74
75void QgsLayoutItemMapGridStack::moveGridUp( const QString &gridId )
76{
78}
79
80void QgsLayoutItemMapGridStack::moveGridDown( const QString &gridId )
81{
83}
84
86{
88 return qobject_cast<QgsLayoutItemMapGrid *>( item );
89}
90
92{
94 return qobject_cast<QgsLayoutItemMapGrid *>( item );
95}
96
97QList<QgsLayoutItemMapGrid *> QgsLayoutItemMapGridStack::asList() const // cppcheck-suppress duplInheritedMember
98{
99 QList< QgsLayoutItemMapGrid * > list;
101 {
102 if ( QgsLayoutItemMapGrid *grid = qobject_cast<QgsLayoutItemMapGrid *>( item ) )
103 {
104 list.append( grid );
105 }
106 }
107 return list;
108}
109
110QgsLayoutItemMapGrid &QgsLayoutItemMapGridStack::operator[]( int idx ) // cppcheck-suppress duplInheritedMember
111{
112 QgsLayoutItemMapItem *item = mItems.at( idx );
113 QgsLayoutItemMapGrid *grid = qobject_cast<QgsLayoutItemMapGrid *>( item );
114 return *grid;
115}
116
117bool QgsLayoutItemMapGridStack::readXml( const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context )
118{
119 removeItems();
120
121 //read grid stack
122 const QDomNodeList mapGridNodeList = elem.elementsByTagName( u"ComposerMapGrid"_s );
123 for ( int i = 0; i < mapGridNodeList.size(); ++i )
124 {
125 const QDomElement mapGridElem = mapGridNodeList.at( i ).toElement();
126 QgsLayoutItemMapGrid *mapGrid = new QgsLayoutItemMapGrid( mapGridElem.attribute( u"name"_s ), mMap );
127 mapGrid->readXml( mapGridElem, doc, context );
128 mItems.append( mapGrid );
129 }
130
131 return true;
132}
133
135{
136 double top = 0.0;
137 double right = 0.0;
138 double bottom = 0.0;
139 double left = 0.0;
140 calculateMaxGridExtension( top, right, bottom, left );
141 return std::max( std::max( std::max( top, right ), bottom ), left );
142}
143
144void QgsLayoutItemMapGridStack::calculateMaxGridExtension( double &top, double &right, double &bottom, double &left ) const
145{
146 top = 0.0;
147 right = 0.0;
148 bottom = 0.0;
149 left = 0.0;
150
152 {
153 if ( QgsLayoutItemMapGrid *grid = qobject_cast<QgsLayoutItemMapGrid *>( item ) )
154 {
155 double gridTop = 0.0;
156 double gridRight = 0.0;
157 double gridBottom = 0.0;
158 double gridLeft = 0.0;
159 grid->calculateMaxExtension( gridTop, gridRight, gridBottom, gridLeft );
160 top = std::max( top, gridTop );
161 right = std::max( right, gridRight );
162 bottom = std::max( bottom, gridBottom );
163 left = std::max( left, gridLeft );
164 }
165 }
166}
167
168
169//
170// QgsLayoutItemMapGrid
171//
172
174{
175 // returns a border as a vector2D for vector arithmetic
176 switch ( border )
177 {
179 return QVector2D( 0, 1 );
181 return QVector2D( -1, 0 );
183 return QVector2D( 0, -1 );
185 return QVector2D( 1, 0 );
186 }
187 return QVector2D();
188}
190{
191 // returns a border normal (towards center) as a vector2D for vector arithmetic
192 const QVector2D borderVector = borderToVector2D( border );
193 return QVector2D( borderVector.y(), -borderVector.x() );
194}
195
198 , mGridFrameSides( Qgis::MapGridFrameSideFlag::Left | Qgis::MapGridFrameSideFlag::Right |
199 Qgis::MapGridFrameSideFlag::Top | Qgis::MapGridFrameSideFlag::Bottom )
200{
201 //get default layout font from settings
202 const QgsSettings settings;
203 const QString defaultFontString = settings.value( u"LayoutDesigner/defaultFont"_s, QVariant(), QgsSettings::Gui ).toString();
204 if ( !defaultFontString.isEmpty() )
205 {
206 QFont font;
207 QgsFontUtils::setFontFamily( font, defaultFontString );
208 mAnnotationFormat.setFont( font );
209 }
210
211 createDefaultGridLineSymbol();
212 createDefaultGridMarkerSymbol();
213
214 connect( mMap, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemMapGrid::refreshDataDefinedProperties );
215 connect( mMap, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemMapGrid::refreshDataDefinedProperties );
216 connect( mMap, &QgsLayoutItemMap::crsChanged, this, [this]
217 {
218 if ( !mCRS.isValid() )
219 emit crsChanged();
220 } );
221}
222
224
225void QgsLayoutItemMapGrid::createDefaultGridLineSymbol()
226{
227 QVariantMap properties;
228 properties.insert( u"color"_s, u"0,0,0,255"_s );
229 properties.insert( u"width"_s, u"0.3"_s );
230 properties.insert( u"capstyle"_s, u"flat"_s );
231 mGridLineSymbol = QgsLineSymbol::createSimple( properties );
232}
233
234void QgsLayoutItemMapGrid::createDefaultGridMarkerSymbol()
235{
236 QVariantMap properties;
237 properties.insert( u"name"_s, u"circle"_s );
238 properties.insert( u"size"_s, u"2.0"_s );
239 properties.insert( u"color"_s, u"0,0,0,255"_s );
240 mGridMarkerSymbol = QgsMarkerSymbol::createSimple( properties );
241}
242
244{
245 if ( mGridLineSymbol )
246 {
247 mGridLineSymbol->setWidth( width );
248 }
249}
250
252{
253 if ( mGridLineSymbol )
254 {
255 mGridLineSymbol->setColor( c );
256 }
257}
258
259bool QgsLayoutItemMapGrid::writeXml( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const
260{
261 if ( elem.isNull() )
262 {
263 return false;
264 }
265
266 QDomElement mapGridElem = doc.createElement( u"ComposerMapGrid"_s );
267 mapGridElem.setAttribute( u"gridStyle"_s, static_cast< int >( mGridStyle ) );
268 mapGridElem.setAttribute( u"intervalX"_s, qgsDoubleToString( mGridIntervalX ) );
269 mapGridElem.setAttribute( u"intervalY"_s, qgsDoubleToString( mGridIntervalY ) );
270 mapGridElem.setAttribute( u"offsetX"_s, qgsDoubleToString( mGridOffsetX ) );
271 mapGridElem.setAttribute( u"offsetY"_s, qgsDoubleToString( mGridOffsetY ) );
272 mapGridElem.setAttribute( u"crossLength"_s, qgsDoubleToString( mCrossLength ) );
273
274 QDomElement lineStyleElem = doc.createElement( u"lineStyle"_s );
275 const QDomElement gridLineStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mGridLineSymbol.get(), doc, context );
276 lineStyleElem.appendChild( gridLineStyleElem );
277 mapGridElem.appendChild( lineStyleElem );
278
279 QDomElement markerStyleElem = doc.createElement( u"markerStyle"_s );
280 const QDomElement gridMarkerStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mGridMarkerSymbol.get(), doc, context );
281 markerStyleElem.appendChild( gridMarkerStyleElem );
282 mapGridElem.appendChild( markerStyleElem );
283
284 mapGridElem.setAttribute( u"gridFrameStyle"_s, static_cast< int >( mGridFrameStyle ) );
285 mapGridElem.setAttribute( u"gridFrameSideFlags"_s, mGridFrameSides );
286 mapGridElem.setAttribute( u"gridFrameWidth"_s, qgsDoubleToString( mGridFrameWidth ) );
287 mapGridElem.setAttribute( u"gridFrameMargin"_s, qgsDoubleToString( mGridFrameMargin ) );
288 mapGridElem.setAttribute( u"gridFramePenThickness"_s, qgsDoubleToString( mGridFramePenThickness ) );
289 mapGridElem.setAttribute( u"gridFramePenColor"_s, QgsColorUtils::colorToString( mGridFramePenColor ) );
290 mapGridElem.setAttribute( u"frameFillColor1"_s, QgsColorUtils::colorToString( mGridFrameFillColor1 ) );
291 mapGridElem.setAttribute( u"frameFillColor2"_s, QgsColorUtils::colorToString( mGridFrameFillColor2 ) );
292 mapGridElem.setAttribute( u"leftFrameDivisions"_s, static_cast< int >( mLeftFrameDivisions ) );
293 mapGridElem.setAttribute( u"rightFrameDivisions"_s, static_cast< int >( mRightFrameDivisions ) );
294 mapGridElem.setAttribute( u"topFrameDivisions"_s, static_cast< int >( mTopFrameDivisions ) );
295 mapGridElem.setAttribute( u"bottomFrameDivisions"_s, static_cast< int >( mBottomFrameDivisions ) );
296 mapGridElem.setAttribute( u"rotatedTicksLengthMode"_s, static_cast< int >( mRotatedTicksLengthMode ) );
297 mapGridElem.setAttribute( u"rotatedTicksEnabled"_s, mRotatedTicksEnabled );
298 mapGridElem.setAttribute( u"rotatedTicksMinimumAngle"_s, QString::number( mRotatedTicksMinimumAngle ) );
299 mapGridElem.setAttribute( u"rotatedTicksMarginToCorner"_s, QString::number( mRotatedTicksMarginToCorner ) );
300 mapGridElem.setAttribute( u"rotatedAnnotationsLengthMode"_s, static_cast< int >( mRotatedAnnotationsLengthMode ) );
301 mapGridElem.setAttribute( u"rotatedAnnotationsEnabled"_s, mRotatedAnnotationsEnabled );
302 mapGridElem.setAttribute( u"rotatedAnnotationsMinimumAngle"_s, QString::number( mRotatedAnnotationsMinimumAngle ) );
303 mapGridElem.setAttribute( u"rotatedAnnotationsMarginToCorner"_s, QString::number( mRotatedAnnotationsMarginToCorner ) );
304 if ( mCRS.isValid() )
305 {
306 mCRS.writeXml( mapGridElem, doc );
307 }
308
309 mapGridElem.setAttribute( u"annotationFormat"_s, static_cast< int >( mGridAnnotationFormat ) );
310 mapGridElem.setAttribute( u"showAnnotation"_s, mShowGridAnnotation );
311 mapGridElem.setAttribute( u"annotationExpression"_s, mGridAnnotationExpressionString );
312 mapGridElem.setAttribute( u"leftAnnotationDisplay"_s, static_cast< int >( mLeftGridAnnotationDisplay ) );
313 mapGridElem.setAttribute( u"rightAnnotationDisplay"_s, static_cast< int >( mRightGridAnnotationDisplay ) );
314 mapGridElem.setAttribute( u"topAnnotationDisplay"_s, static_cast< int >( mTopGridAnnotationDisplay ) );
315 mapGridElem.setAttribute( u"bottomAnnotationDisplay"_s, static_cast< int >( mBottomGridAnnotationDisplay ) );
316 mapGridElem.setAttribute( u"leftAnnotationPosition"_s, static_cast< int >( mLeftGridAnnotationPosition ) );
317 mapGridElem.setAttribute( u"rightAnnotationPosition"_s, static_cast< int >( mRightGridAnnotationPosition ) );
318 mapGridElem.setAttribute( u"topAnnotationPosition"_s, static_cast< int >( mTopGridAnnotationPosition ) );
319 mapGridElem.setAttribute( u"bottomAnnotationPosition"_s, static_cast< int >( mBottomGridAnnotationPosition ) );
320 mapGridElem.setAttribute( u"leftAnnotationDirection"_s, static_cast< int >( mLeftGridAnnotationDirection ) );
321 mapGridElem.setAttribute( u"rightAnnotationDirection"_s, static_cast< int >( mRightGridAnnotationDirection ) );
322 mapGridElem.setAttribute( u"topAnnotationDirection"_s, static_cast< int >( mTopGridAnnotationDirection ) );
323 mapGridElem.setAttribute( u"bottomAnnotationDirection"_s, static_cast< int >( mBottomGridAnnotationDirection ) );
324 mapGridElem.setAttribute( u"frameAnnotationDistance"_s, QString::number( mAnnotationFrameDistance ) );
325 mapGridElem.appendChild( mAnnotationFormat.writeXml( doc, context ) );
326 mapGridElem.setAttribute( u"annotationPrecision"_s, mGridAnnotationPrecision );
327 mapGridElem.setAttribute( u"unit"_s, static_cast< int >( mGridUnit ) );
328 mapGridElem.setAttribute( u"blendMode"_s, mBlendMode );
329 mapGridElem.setAttribute( u"minimumIntervalWidth"_s, QString::number( mMinimumIntervalWidth ) );
330 mapGridElem.setAttribute( u"maximumIntervalWidth"_s, QString::number( mMaximumIntervalWidth ) );
331
333 {
334 mapGridElem.setAttribute( u"halign"_s, qgsEnumValueToKey( mHAlign ) );
335 }
336
337 const bool ok = QgsLayoutItemMapItem::writeXml( mapGridElem, doc, context );
338 elem.appendChild( mapGridElem );
339 return ok;
340}
341
342bool QgsLayoutItemMapGrid::readXml( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
343{
344 Q_UNUSED( doc )
345 if ( itemElem.isNull() )
346 {
347 return false;
348 }
349
350 const bool ok = QgsLayoutItemMapItem::readXml( itemElem, doc, context );
351
352 //grid
353 mGridStyle = static_cast< Qgis::MapGridStyle >( itemElem.attribute( u"gridStyle"_s, u"0"_s ).toInt() );
354 mGridIntervalX = itemElem.attribute( u"intervalX"_s, u"0"_s ).toDouble();
355 mGridIntervalY = itemElem.attribute( u"intervalY"_s, u"0"_s ).toDouble();
356 mGridOffsetX = itemElem.attribute( u"offsetX"_s, u"0"_s ).toDouble();
357 mGridOffsetY = itemElem.attribute( u"offsetY"_s, u"0"_s ).toDouble();
358 mCrossLength = itemElem.attribute( u"crossLength"_s, u"3"_s ).toDouble();
359 mGridFrameStyle = static_cast< Qgis::MapGridFrameStyle >( itemElem.attribute( u"gridFrameStyle"_s, u"0"_s ).toInt() );
360 mGridFrameSides = static_cast< Qgis::MapGridFrameSideFlags >( itemElem.attribute( u"gridFrameSideFlags"_s, u"15"_s ).toInt() );
361 mGridFrameWidth = itemElem.attribute( u"gridFrameWidth"_s, u"2.0"_s ).toDouble();
362 mGridFrameMargin = itemElem.attribute( u"gridFrameMargin"_s, u"0.0"_s ).toDouble();
363 mGridFramePenThickness = itemElem.attribute( u"gridFramePenThickness"_s, u"0.3"_s ).toDouble();
364 mGridFramePenColor = QgsColorUtils::colorFromString( itemElem.attribute( u"gridFramePenColor"_s, u"0,0,0"_s ) );
365 mGridFrameFillColor1 = QgsColorUtils::colorFromString( itemElem.attribute( u"frameFillColor1"_s, u"255,255,255,255"_s ) );
366 mGridFrameFillColor2 = QgsColorUtils::colorFromString( itemElem.attribute( u"frameFillColor2"_s, u"0,0,0,255"_s ) );
367 mLeftFrameDivisions = static_cast< Qgis::MapGridComponentVisibility >( itemElem.attribute( u"leftFrameDivisions"_s, u"0"_s ).toInt() );
368 mRightFrameDivisions = static_cast< Qgis::MapGridComponentVisibility >( itemElem.attribute( u"rightFrameDivisions"_s, u"0"_s ).toInt() );
369 mTopFrameDivisions = static_cast< Qgis::MapGridComponentVisibility >( itemElem.attribute( u"topFrameDivisions"_s, u"0"_s ).toInt() );
370 mBottomFrameDivisions = static_cast< Qgis::MapGridComponentVisibility >( itemElem.attribute( u"bottomFrameDivisions"_s, u"0"_s ).toInt() );
371 mRotatedTicksLengthMode = static_cast< Qgis::MapGridTickLengthMode >( itemElem.attribute( u"rotatedTicksLengthMode"_s, u"0"_s ).toInt() );
372 mRotatedTicksEnabled = itemElem.attribute( u"rotatedTicksEnabled"_s, u"0"_s ) != "0"_L1;
373 mRotatedTicksMinimumAngle = itemElem.attribute( u"rotatedTicksMinimumAngle"_s, u"0"_s ).toDouble();
374 mRotatedTicksMarginToCorner = itemElem.attribute( u"rotatedTicksMarginToCorner"_s, u"0"_s ).toDouble();
375 mRotatedAnnotationsLengthMode = static_cast< Qgis::MapGridTickLengthMode >( itemElem.attribute( u"rotatedAnnotationsLengthMode"_s, u"0"_s ).toInt() );
376 mRotatedAnnotationsEnabled = itemElem.attribute( u"rotatedAnnotationsEnabled"_s, u"0"_s ) != "0"_L1;
377 mRotatedAnnotationsMinimumAngle = itemElem.attribute( u"rotatedAnnotationsMinimumAngle"_s, u"0"_s ).toDouble();
378 mRotatedAnnotationsMarginToCorner = itemElem.attribute( u"rotatedAnnotationsMarginToCorner"_s, u"0"_s ).toDouble();
379
380 const QDomElement lineStyleElem = itemElem.firstChildElement( u"lineStyle"_s );
381 if ( !lineStyleElem.isNull() )
382 {
383 const QDomElement symbolElem = lineStyleElem.firstChildElement( u"symbol"_s );
384 if ( !symbolElem.isNull() )
385 {
386 mGridLineSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( symbolElem, context );
387 }
388 }
389 else
390 {
391 //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
392 mGridLineSymbol = QgsLineSymbol::createSimple( QVariantMap() );
393 mGridLineSymbol->setWidth( itemElem.attribute( u"penWidth"_s, u"0"_s ).toDouble() );
394 mGridLineSymbol->setColor( QColor( itemElem.attribute( u"penColorRed"_s, u"0"_s ).toInt(),
395 itemElem.attribute( u"penColorGreen"_s, u"0"_s ).toInt(),
396 itemElem.attribute( u"penColorBlue"_s, u"0"_s ).toInt() ) );
397 }
398
399 const QDomElement markerStyleElem = itemElem.firstChildElement( u"markerStyle"_s );
400 if ( !markerStyleElem.isNull() )
401 {
402 const QDomElement symbolElem = markerStyleElem.firstChildElement( u"symbol"_s );
403 if ( !symbolElem.isNull() )
404 {
405 mGridMarkerSymbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElem, context );
406 }
407 }
408
409 if ( !mCRS.readXml( itemElem ) )
411
412 mBlendMode = static_cast< QPainter::CompositionMode >( itemElem.attribute( u"blendMode"_s, u"0"_s ).toUInt() );
413
414 //annotation
415 mShowGridAnnotation = ( itemElem.attribute( u"showAnnotation"_s, u"0"_s ) != "0"_L1 );
416 mGridAnnotationFormat = static_cast< Qgis::MapGridAnnotationFormat >( itemElem.attribute( u"annotationFormat"_s, u"0"_s ).toInt() );
417 mGridAnnotationExpressionString = itemElem.attribute( u"annotationExpression"_s );
418 mGridAnnotationExpression.reset();
419 mLeftGridAnnotationPosition = static_cast< Qgis::MapGridAnnotationPosition >( itemElem.attribute( u"leftAnnotationPosition"_s, u"0"_s ).toInt() );
420 mRightGridAnnotationPosition = static_cast< Qgis::MapGridAnnotationPosition >( itemElem.attribute( u"rightAnnotationPosition"_s, u"0"_s ).toInt() );
421 mTopGridAnnotationPosition = static_cast< Qgis::MapGridAnnotationPosition >( itemElem.attribute( u"topAnnotationPosition"_s, u"0"_s ).toInt() );
422 mBottomGridAnnotationPosition = static_cast< Qgis::MapGridAnnotationPosition >( itemElem.attribute( u"bottomAnnotationPosition"_s, u"0"_s ).toInt() );
423 mLeftGridAnnotationDisplay = static_cast<Qgis::MapGridComponentVisibility >( itemElem.attribute( u"leftAnnotationDisplay"_s, u"0"_s ).toInt() );
424 mRightGridAnnotationDisplay = static_cast<Qgis::MapGridComponentVisibility >( itemElem.attribute( u"rightAnnotationDisplay"_s, u"0"_s ).toInt() );
425 mTopGridAnnotationDisplay = static_cast<Qgis::MapGridComponentVisibility >( itemElem.attribute( u"topAnnotationDisplay"_s, u"0"_s ).toInt() );
426 mBottomGridAnnotationDisplay = static_cast<Qgis::MapGridComponentVisibility >( itemElem.attribute( u"bottomAnnotationDisplay"_s, u"0"_s ).toInt() );
427
428 mLeftGridAnnotationDirection = static_cast<Qgis::MapGridAnnotationDirection >( itemElem.attribute( u"leftAnnotationDirection"_s, u"0"_s ).toInt() );
429 mRightGridAnnotationDirection = static_cast<Qgis::MapGridAnnotationDirection >( itemElem.attribute( u"rightAnnotationDirection"_s, u"0"_s ).toInt() );
430 mTopGridAnnotationDirection = static_cast<Qgis::MapGridAnnotationDirection >( itemElem.attribute( u"topAnnotationDirection"_s, u"0"_s ).toInt() );
431 mBottomGridAnnotationDirection = static_cast<Qgis::MapGridAnnotationDirection >( itemElem.attribute( u"bottomAnnotationDirection"_s, u"0"_s ).toInt() );
432 mAnnotationFrameDistance = itemElem.attribute( u"frameAnnotationDistance"_s, u"0"_s ).toDouble();
433
434 if ( !itemElem.firstChildElement( "text-style" ).isNull() )
435 {
436 mAnnotationFormat.readXml( itemElem, context );
437 }
438 else
439 {
440 QFont font;
441 if ( !QgsFontUtils::setFromXmlChildNode( font, itemElem, "annotationFontProperties" ) )
442 {
443 font.fromString( itemElem.attribute( "annotationFont", QString() ) );
444 }
445 mAnnotationFormat.setFont( font );
446 mAnnotationFormat.setSize( font.pointSizeF() );
447 mAnnotationFormat.setSizeUnit( Qgis::RenderUnit::Points );
448 mAnnotationFormat.setColor( QgsColorUtils::colorFromString( itemElem.attribute( "annotationFontColor", "0,0,0,255" ) ) );
449 }
450
451 mGridAnnotationPrecision = itemElem.attribute( u"annotationPrecision"_s, u"3"_s ).toInt();
452 const int gridUnitInt = itemElem.attribute( u"unit"_s, QString::number( static_cast< int >( Qgis::MapGridUnit::MapUnits ) ) ).toInt();
453 mGridUnit = ( gridUnitInt <= static_cast< int >( Qgis::MapGridUnit::DynamicPageSizeBased ) ) ? static_cast< Qgis::MapGridUnit >( gridUnitInt ) : Qgis::MapGridUnit::MapUnits;
454 mMinimumIntervalWidth = itemElem.attribute( u"minimumIntervalWidth"_s, u"50"_s ).toDouble();
455 mMaximumIntervalWidth = itemElem.attribute( u"maximumIntervalWidth"_s, u"100"_s ).toDouble();
456
457 mHAlign = qgsEnumKeyToValue( itemElem.attribute( u"halign"_s ), Qgis::TextHorizontalAlignment::Center );
458
459 refreshDataDefinedProperties();
460 return ok;
461}
462
464{
465 if ( mCRS == crs )
466 return;
467
468 mCRS = crs;
469 mTransformDirty = true;
470 emit crsChanged();
471}
472
474{
475 return mBlendMode != QPainter::CompositionMode_SourceOver;
476}
477
478QPolygonF QgsLayoutItemMapGrid::scalePolygon( const QPolygonF &polygon, const double scale ) const
479{
480 const QTransform t = QTransform::fromScale( scale, scale );
481 return t.map( polygon );
482}
483
484void QgsLayoutItemMapGrid::drawGridCrsTransform( QgsRenderContext &context, double dotsPerMM, bool calculateLinesOnly ) const
485{
486 if ( !mMap || !mEvaluatedEnabled )
487 {
488 return;
489 }
490
491 //has map extent/scale changed?
492 const QPolygonF mapPolygon = mMap->transformedMapPolygon();
493 if ( mapPolygon != mPrevMapPolygon )
494 {
495 mTransformDirty = true;
496 mPrevMapPolygon = mapPolygon;
497 }
498
499 if ( mTransformDirty )
500 {
501 calculateCrsTransformLines();
502 }
503
504 //draw lines
505 if ( !calculateLinesOnly )
506 {
507 int countLongitudeLines = 0;
508 int countLatitudeLines = 0;
509 for ( const GridLine &line : mGridLines )
510 {
511 switch ( line.coordinateType )
512 {
514 countLongitudeLines++;
515 break;
517 countLatitudeLines++;
518 break;
519 }
520 }
521
522 int latitudeLineIndex = 0;
523 int longitudeLineIndex = 0;
524 if ( mGridStyle == Qgis::MapGridStyle::Lines )
525 {
526 QList< GridLine >::const_iterator gridIt = mGridLines.constBegin();
527 for ( ; gridIt != mGridLines.constEnd(); ++gridIt )
528 {
529 switch ( gridIt->coordinateType )
530 {
532 longitudeLineIndex++;
533 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_count"_s, countLongitudeLines, true ) );
534 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_index"_s, longitudeLineIndex, true ) );
535 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_axis"_s, u"x"_s, true ) );
536 break;
537
539 latitudeLineIndex++;
540 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_count"_s, countLatitudeLines, true ) );
541 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_index"_s, latitudeLineIndex, true ) );
542 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_axis"_s, u"y"_s, true ) );
543 break;
544 }
545 context.expressionContext().lastScope()->setVariable( u"grid_number"_s, gridIt->coordinate );
546 drawGridLine( scalePolygon( gridIt->line, dotsPerMM ), context );
547 }
548 }
549 else if ( mGridStyle == Qgis::MapGridStyle::LineCrosses || mGridStyle == Qgis::MapGridStyle::Markers )
550 {
551 const double maxX = mMap->rect().width();
552 const double maxY = mMap->rect().height();
553
554 QList< QgsPointXY >::const_iterator intersectionIt = mTransformedIntersections.constBegin();
555 for ( ; intersectionIt != mTransformedIntersections.constEnd(); ++intersectionIt )
556 {
557 const double x = intersectionIt->x();
558 const double y = intersectionIt->y();
559 if ( mGridStyle == Qgis::MapGridStyle::LineCrosses )
560 {
561 //ensure that crosses don't overshoot the map item bounds
562 const QLineF line1 = QLineF( x - mEvaluatedCrossLength, y, x + mEvaluatedCrossLength, y );
563 line1.p1().rx() = line1.p1().x() < 0 ? 0 : line1.p1().x();
564 line1.p2().rx() = line1.p2().x() > maxX ? maxX : line1.p2().x();
565 const QLineF line2 = QLineF( x, y - mEvaluatedCrossLength, x, y + mEvaluatedCrossLength );
566 line2.p1().ry() = line2.p1().y() < 0 ? 0 : line2.p1().y();
567 line2.p2().ry() = line2.p2().y() > maxY ? maxY : line2.p2().y();
568
569 //draw line using coordinates scaled to dots
570 drawGridLine( QLineF( line1.p1() * dotsPerMM, line1.p2() * dotsPerMM ), context );
571 drawGridLine( QLineF( line2.p1() * dotsPerMM, line2.p2() * dotsPerMM ), context );
572 }
573 else if ( mGridStyle == Qgis::MapGridStyle::Markers )
574 {
575 drawGridMarker( QPointF( x, y ) * dotsPerMM, context );
576 }
577 }
578 }
579 }
580}
581
582void QgsLayoutItemMapGrid::calculateCrsTransformLines() const
583{
584 QgsRectangle crsBoundingRect;
585 QgsCoordinateTransform inverseTr;
586 if ( crsGridParams( crsBoundingRect, inverseTr ) != 0 )
587 {
588 return;
589 }
590
591 // calculate grid lines
592 mGridLines.clear();
593 xGridLinesCrsTransform( crsBoundingRect, inverseTr );
594 yGridLinesCrsTransform( crsBoundingRect, inverseTr );
595
596 if ( mGridStyle == Qgis::MapGridStyle::LineCrosses || mGridStyle == Qgis::MapGridStyle::Markers )
597 {
598 //cross or markers style - we also need to calculate intersections of lines
599
600 //first convert lines to QgsGeometry
601 QList< QgsGeometry > xLines;
602 QList< QgsGeometry > yLines;
603 QList< GridLine >::const_iterator gridIt = mGridLines.constBegin();
604 for ( ; gridIt != mGridLines.constEnd(); ++gridIt )
605 {
606
607 QgsPolylineXY line;
608 for ( int i = 0; i < gridIt->line.size(); ++i )
609 {
610 line.append( QgsPointXY( gridIt->line.at( i ).x(), gridIt->line.at( i ).y() ) );
611 }
612 if ( gridIt->coordinateType == Qgis::MapGridAnnotationType::Longitude )
613 yLines << QgsGeometry::fromPolylineXY( line );
614 else if ( gridIt->coordinateType == Qgis::MapGridAnnotationType::Latitude )
615 xLines << QgsGeometry::fromPolylineXY( line );
616 }
617
618 //now, loop through geometries and calculate intersection points
619 mTransformedIntersections.clear();
620 QList< QgsGeometry >::const_iterator yLineIt = yLines.constBegin();
621 for ( ; yLineIt != yLines.constEnd(); ++yLineIt )
622 {
623 QList< QgsGeometry >::const_iterator xLineIt = xLines.constBegin();
624 for ( ; xLineIt != xLines.constEnd(); ++xLineIt )
625 {
626 //look for intersections between lines
627 const QgsGeometry intersects = ( *yLineIt ).intersection( ( *xLineIt ) );
628 if ( intersects.isNull() )
629 continue;
630
631 //go through all intersections and draw grid markers/crosses
632 int i = 0;
633 QgsPointXY vertex = intersects.vertexAt( i );
634 while ( !vertex.isEmpty() )
635 {
636 mTransformedIntersections << vertex;
637 i = i + 1;
638 vertex = intersects.vertexAt( i );
639 }
640 }
641 }
642 }
643
644 mTransformDirty = false;
645}
646
647void QgsLayoutItemMapGrid::draw( QPainter *p )
648{
649 if ( !mMap || !mEvaluatedEnabled )
650 {
651 return;
652 }
653 QPaintDevice *paintDevice = p->device();
654 if ( !paintDevice )
655 {
656 return;
657 }
658
659 p->save();
660 p->setCompositionMode( mBlendMode );
661 p->setRenderHint( QPainter::Antialiasing, mMap->layout()->renderContext().flags() & Qgis::LayoutRenderFlag::Antialiasing );
662
663 const QRectF thisPaintRect = QRectF( 0, 0, mMap->rect().width(), mMap->rect().height() );
664 p->setClipRect( thisPaintRect );
665 if ( thisPaintRect != mPrevPaintRect )
666 {
667 //rect has changed, so need to recalculate transform
668 mTransformDirty = true;
669 mPrevPaintRect = thisPaintRect;
670 }
671
672 //setup painter scaling to dots so that raster symbology is drawn to scale
673 const double dotsPerMM = paintDevice->logicalDpiX() / 25.4;
674 p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); //scale painter from mm to dots
675
676 //setup render context
681 const QgsExpressionContext expressionContext = createExpressionContext();
682 context.setExpressionContext( expressionContext );
683
684 //is grid in a different crs than map?
685 switch ( mGridUnit )
686 {
689 if ( mCRS.isValid() && mCRS != mMap->crs() )
690 {
691 drawGridCrsTransform( context, dotsPerMM );
692 break;
693 }
694
695 [[fallthrough]];
698 drawGridNoTransform( context, dotsPerMM );
699 break;
700 }
701 p->restore();
702
703 p->setClipping( false );
704#ifdef Q_OS_MAC
705 //QPainter::setClipping(false) seems to be broken on OSX (#12747). So we hack around it by
706 //setting a larger clip rect
707 p->setClipRect( mMap->mapRectFromScene( mMap->sceneBoundingRect() ).adjusted( -10, -10, 10, 10 ) );
708#endif
709
710
711 if ( mGridFrameStyle != Qgis::MapGridFrameStyle::NoFrame || mShowGridAnnotation )
712 updateGridLinesAnnotationsPositions();
713
714 if ( mGridFrameStyle != Qgis::MapGridFrameStyle::NoFrame )
715 {
716 drawGridFrame( p );
717 }
718
719 if ( mShowGridAnnotation )
720 {
721 drawCoordinateAnnotations( context, context.expressionContext() );
722 }
723}
724
725void QgsLayoutItemMapGrid::updateGridLinesAnnotationsPositions() const
726{
727 QList< GridLine >::iterator it = mGridLines.begin();
728 for ( ; it != mGridLines.end(); ++it )
729 {
730 it->startAnnotation.border = borderForLineCoord( it->line.first(), it->coordinateType );
731 it->endAnnotation.border = borderForLineCoord( it->line.last(), it->coordinateType );
732 it->startAnnotation.position = QVector2D( it->line.first() );
733 it->endAnnotation.position = QVector2D( it->line.last() );
734 it->startAnnotation.vector = QVector2D( it->line.at( 1 ) - it->line.first() ).normalized();
735 it->endAnnotation.vector = QVector2D( it->line.at( it->line.count() - 2 ) - it->line.last() ).normalized();
736 const QVector2D normS = borderToNormal2D( it->startAnnotation.border );
737 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() );
738 const QVector2D normE = borderToNormal2D( it->endAnnotation.border );
739 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() );
740 }
741}
742
743void QgsLayoutItemMapGrid::drawGridNoTransform( QgsRenderContext &context, double dotsPerMM, bool calculateLinesOnly ) const
744{
745 //get line positions
746 mGridLines.clear();
747 yGridLines();
748 xGridLines();
749
750 if ( calculateLinesOnly || mGridLines.empty() )
751 return;
752
753 QList< GridLine >::const_iterator vIt = mGridLines.constBegin();
754 QList< GridLine >::const_iterator hIt = mGridLines.constBegin();
755
756 int countLongitudeLines = 0;
757 int countLatitudeLines = 0;
758 for ( const GridLine &line : mGridLines )
759 {
760 switch ( line.coordinateType )
761 {
763 countLongitudeLines++;
764 break;
766 countLatitudeLines++;
767 break;
768 }
769 }
770
771 int latitudeLineIndex = 0;
772 int longitudeLineIndex = 0;
773
774 //simple approach: draw vertical lines first, then horizontal ones
775 if ( mGridStyle == Qgis::MapGridStyle::Lines )
776 {
777 //we need to scale line coordinates to dots, rather than mm, since the painter has already been scaled to dots
778 //this is done by multiplying each line coordinate by dotsPerMM
779 QLineF line;
780 for ( ; vIt != mGridLines.constEnd(); ++vIt )
781 {
782 if ( vIt->coordinateType != Qgis::MapGridAnnotationType::Longitude )
783 continue;
784 line = QLineF( vIt->line.first() * dotsPerMM, vIt->line.last() * dotsPerMM );
785
786 longitudeLineIndex++;
787 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_count"_s, countLongitudeLines, true ) );
788 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_index"_s, longitudeLineIndex, true ) );
789 context.expressionContext().lastScope()->setVariable( u"grid_number"_s, vIt->coordinate );
790 context.expressionContext().lastScope()->setVariable( u"grid_axis"_s, "x" );
791
792 drawGridLine( line, context );
793 }
794
795 for ( ; hIt != mGridLines.constEnd(); ++hIt )
796 {
797 if ( hIt->coordinateType != Qgis::MapGridAnnotationType::Latitude )
798 continue;
799 line = QLineF( hIt->line.first() * dotsPerMM, hIt->line.last() * dotsPerMM );
800
801 latitudeLineIndex++;
802 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_count"_s, countLatitudeLines, true ) );
803 context.expressionContext().lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_index"_s, latitudeLineIndex, true ) );
804 context.expressionContext().lastScope()->setVariable( u"grid_number"_s, hIt->coordinate );
805 context.expressionContext().lastScope()->setVariable( u"grid_axis"_s, "y" );
806
807 drawGridLine( line, context );
808 }
809 }
810 else if ( mGridStyle != Qgis::MapGridStyle::FrameAndAnnotationsOnly ) //cross or markers
811 {
812 QLineF l1, l2;
813 QPointF intersectionPoint, crossEnd1, crossEnd2;
814 for ( ; vIt != mGridLines.constEnd(); ++vIt )
815 {
816 if ( vIt->coordinateType != Qgis::MapGridAnnotationType::Longitude )
817 continue;
818
819 l1 = QLineF( vIt->line.first(), vIt->line.last() );
820
821 //test for intersection with every horizontal line
822 hIt = mGridLines.constBegin();
823 for ( ; hIt != mGridLines.constEnd(); ++hIt )
824 {
825 if ( hIt->coordinateType != Qgis::MapGridAnnotationType::Latitude )
826 continue;
827
828 l2 = QLineF( hIt->line.first(), hIt->line.last() );
829
830 if ( l2.intersects( l1, &intersectionPoint ) == QLineF::BoundedIntersection )
831 {
832 if ( mGridStyle == Qgis::MapGridStyle::LineCrosses )
833 {
834 //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
835 crossEnd1 = ( ( intersectionPoint - l1.p1() ).manhattanLength() > 0.01 ) ?
836 QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, l1.p1(), mEvaluatedCrossLength ) : intersectionPoint;
837 crossEnd2 = ( ( intersectionPoint - l1.p2() ).manhattanLength() > 0.01 ) ?
838 QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, l1.p2(), mEvaluatedCrossLength ) : intersectionPoint;
839 //draw line using coordinates scaled to dots
840 drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
841 }
842 else if ( mGridStyle == Qgis::MapGridStyle::Markers )
843 {
844 drawGridMarker( intersectionPoint * dotsPerMM, context );
845 }
846 }
847 }
848 }
849 if ( mGridStyle == Qgis::MapGridStyle::Markers )
850 {
851 //markers mode, so we have no need to process horizontal lines (we've already
852 //drawn markers on the intersections between horizontal and vertical lines)
853 return;
854 }
855
856 hIt = mGridLines.constBegin();
857 for ( ; hIt != mGridLines.constEnd(); ++hIt )
858 {
859 if ( hIt->coordinateType != Qgis::MapGridAnnotationType::Latitude )
860 continue;
861
862 l1 = QLineF( hIt->line.first(), hIt->line.last() );
863
864 vIt = mGridLines.constBegin();
865 for ( ; vIt != mGridLines.constEnd(); ++vIt )
866 {
867 if ( vIt->coordinateType != Qgis::MapGridAnnotationType::Longitude )
868 continue;
869
870 l2 = QLineF( vIt->line.first(), vIt->line.last() );
871
872 if ( l2.intersects( l1, &intersectionPoint ) == QLineF::BoundedIntersection )
873 {
874 //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
875 crossEnd1 = ( ( intersectionPoint - l1.p1() ).manhattanLength() > 0.01 ) ?
876 QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, l1.p1(), mEvaluatedCrossLength ) : intersectionPoint;
877 crossEnd2 = ( ( intersectionPoint - l1.p2() ).manhattanLength() > 0.01 ) ?
878 QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, l1.p2(), mEvaluatedCrossLength ) : intersectionPoint;
879 //draw line using coordinates scaled to dots
880 drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
881 }
882 }
883 }
884 }
885}
886
887void QgsLayoutItemMapGrid::drawGridFrame( QPainter *p, GridExtension *extension ) const
888{
889 if ( p )
890 {
891 p->save();
892 p->setRenderHint( QPainter::Antialiasing, mMap->layout()->renderContext().flags() & Qgis::LayoutRenderFlag::Antialiasing );
893 }
894
895
896 switch ( mGridFrameStyle )
897 {
900 drawGridFrameZebra( p, extension );
901 break;
905 drawGridFrameTicks( p, extension );
906 break;
907
910 drawGridFrameLine( p, extension );
911 break;
912
914 break;
915 }
916
917 if ( p )
918 p->restore();
919}
920
921void QgsLayoutItemMapGrid::drawGridLine( const QLineF &line, QgsRenderContext &context ) const
922{
923 QPolygonF poly;
924 poly << line.p1() << line.p2();
925 drawGridLine( poly, context );
926}
927
928void QgsLayoutItemMapGrid::drawGridLine( const QPolygonF &line, QgsRenderContext &context ) const
929{
930 if ( !mMap || !mMap->layout() || !mGridLineSymbol )
931 {
932 return;
933 }
934
935 mGridLineSymbol->startRender( context );
936 mGridLineSymbol->renderPolyline( line, nullptr, context );
937 mGridLineSymbol->stopRender( context );
938}
939
940void QgsLayoutItemMapGrid::drawGridMarker( QPointF point, QgsRenderContext &context ) const
941{
942 if ( !mMap || !mMap->layout() || !mGridMarkerSymbol )
943 {
944 return;
945 }
946
947 mGridMarkerSymbol->startRender( context );
948 mGridMarkerSymbol->renderPoint( point, nullptr, context );
949 mGridMarkerSymbol->stopRender( context );
950}
951
952void QgsLayoutItemMapGrid::drawGridFrameZebra( QPainter *p, GridExtension *extension ) const
953{
955 {
956 drawGridFrameZebraBorder( p, Qgis::MapGridBorderSide::Left, extension ? &extension->left : nullptr );
957 }
959 {
960 drawGridFrameZebraBorder( p, Qgis::MapGridBorderSide::Right, extension ? &extension->right : nullptr );
961 }
963 {
964 drawGridFrameZebraBorder( p, Qgis::MapGridBorderSide::Top, extension ? &extension->top : nullptr );
965 }
967 {
968 drawGridFrameZebraBorder( p, Qgis::MapGridBorderSide::Bottom, extension ? &extension->bottom : nullptr );
969 }
970}
971
972void QgsLayoutItemMapGrid::drawGridFrameZebraBorder( QPainter *p, Qgis::MapGridBorderSide border, double *extension ) const
973{
974 if ( !mMap )
975 {
976 return;
977 }
978
979 if ( extension )
980 {
981 *extension = mEvaluatedGridFrameMargin + mEvaluatedGridFrameWidth + mEvaluatedGridFrameLineThickness / 2.0;
982 return;
983 }
984
985 double currentCoord = 0.0;
986 bool color1 = false;
987 double x = 0;
988 double y = 0;
989 double width = 0;
990 double height = 0;
991
992 bool drawTLBox = false;
993 bool drawTRBox = false;
994 bool drawBLBox = false;
995 bool drawBRBox = false;
996
997 QMap< double, double > pos = QMap< double, double >();
998 QList< GridLine >::const_iterator it = mGridLines.constBegin();
999 for ( ; it != mGridLines.constEnd(); ++it )
1000 {
1001 // for first and last point of the line
1002 for ( int i = 0 ; i < 2 ; ++i )
1003 {
1004 const GridLineAnnotation annot = ( i == 0 ) ? it->startAnnotation : it->endAnnotation;
1005
1006 // we skip if the point is on another border
1007 if ( annot.border != border )
1008 continue;
1009
1010 if ( ! shouldShowDivisionForSide( it->coordinateType, annot.border ) )
1011 continue;
1012
1014 pos.insert( annot.position.y(), it->coordinate );
1015 else
1016 pos.insert( annot.position.x(), it->coordinate );
1017 }
1018 }
1019
1020
1022 {
1023 pos.insert( mMap->rect().height(), mMap->rect().height() );
1025 {
1026 drawBLBox = border == Qgis::MapGridBorderSide::Left;
1027 drawBRBox = border == Qgis::MapGridBorderSide::Right;
1028 }
1030 {
1031 drawTLBox = border == Qgis::MapGridBorderSide::Left;
1032 drawTRBox = border == Qgis::MapGridBorderSide::Right;
1033 }
1034 if ( !drawTLBox && border == Qgis::MapGridBorderSide::Left )
1035 color1 = true;
1036 }
1037 else if ( border == Qgis::MapGridBorderSide::Top || border == Qgis::MapGridBorderSide::Bottom )
1038 {
1039 pos.insert( mMap->rect().width(), mMap->rect().width() );
1040 }
1041
1042 //set pen to current frame pen
1043 QPen framePen = QPen( mGridFramePenColor );
1044 framePen.setWidthF( mEvaluatedGridFrameLineThickness );
1045 framePen.setJoinStyle( Qt::MiterJoin );
1046 p->setPen( framePen );
1047
1048 QMap< double, double >::const_iterator posIt = pos.constBegin();
1049 for ( ; posIt != pos.constEnd(); ++posIt )
1050 {
1051 p->setBrush( QBrush( color1 ? mGridFrameFillColor1 : mGridFrameFillColor2 ) );
1053 {
1054 height = posIt.key() - currentCoord;
1055 width = mEvaluatedGridFrameWidth;
1056 x = ( border == Qgis::MapGridBorderSide::Left ) ? -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ) : mMap->rect().width() + mEvaluatedGridFrameMargin;
1057 y = currentCoord;
1058 }
1059 else //top or bottom
1060 {
1061 height = mEvaluatedGridFrameWidth;
1062 width = posIt.key() - currentCoord;
1063 x = currentCoord;
1064 y = ( border == Qgis::MapGridBorderSide::Top ) ? -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ) : mMap->rect().height() + mEvaluatedGridFrameMargin;
1065 }
1066 p->drawRect( QRectF( x, y, width, height ) );
1067 currentCoord = posIt.key();
1068 color1 = !color1;
1069 }
1070
1071 if ( mGridFrameStyle == Qgis::MapGridFrameStyle::ZebraNautical || qgsDoubleNear( mEvaluatedGridFrameMargin, 0.0 ) )
1072 {
1073 //draw corners
1074 width = height = ( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ) ;
1075 p->setBrush( QBrush( mGridFrameFillColor1 ) );
1076 if ( drawTLBox )
1077 p->drawRect( QRectF( -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ), -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ), width, height ) );
1078 if ( drawTRBox )
1079 p->drawRect( QRectF( mMap->rect().width(), -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ), width, height ) );
1080 if ( drawBLBox )
1081 p->drawRect( QRectF( -( mEvaluatedGridFrameWidth + mEvaluatedGridFrameMargin ), mMap->rect().height(), width, height ) );
1082 if ( drawBRBox )
1083 p->drawRect( QRectF( mMap->rect().width(), mMap->rect().height(), width, height ) );
1084 }
1085}
1086
1087void QgsLayoutItemMapGrid::drawGridFrameTicks( QPainter *p, GridExtension *extension ) const
1088{
1089 if ( !mMap )
1090 {
1091 return;
1092 }
1093
1094 //set pen to current frame pen
1095 if ( p )
1096 {
1097 QPen framePen = QPen( mGridFramePenColor );
1098 framePen.setWidthF( mEvaluatedGridFrameLineThickness );
1099 framePen.setCapStyle( Qt::FlatCap );
1100 p->setBrush( Qt::NoBrush );
1101 p->setPen( framePen );
1102 }
1103
1104 QList< GridLine >::iterator it = mGridLines.begin();
1105 for ( ; it != mGridLines.end(); ++it )
1106 {
1107 // for first and last point of the line
1108 for ( int i = 0 ; i < 2 ; ++i )
1109 {
1110 const GridLineAnnotation annot = ( i == 0 ) ? it->startAnnotation : it->endAnnotation;
1111
1112 if ( ! shouldShowDivisionForSide( it->coordinateType, annot.border ) )
1113 continue;
1114
1115 // If the angle is below the threshold, we don't draw the annotation
1116 if ( abs( annot.angle ) / M_PI * 180.0 > 90.0 - mRotatedTicksMinimumAngle + 0.0001 )
1117 continue;
1118
1119 // Skip outwards facing annotations that are below mRotatedTicksMarginToCorner
1120 bool facingLeft;
1121 bool facingRight;
1122 if ( mGridFrameStyle == Qgis::MapGridFrameStyle::InteriorExteriorTicks )
1123 {
1124 facingLeft = ( annot.angle != 0 );
1125 facingRight = ( annot.angle != 0 );
1126 }
1127 else if ( mGridFrameStyle == Qgis::MapGridFrameStyle::ExteriorTicks )
1128 {
1129 facingLeft = ( annot.angle > 0 );
1130 facingRight = ( annot.angle < 0 );
1131 }
1132 else
1133 {
1134 facingLeft = ( annot.angle < 0 );
1135 facingRight = ( annot.angle > 0 );
1136 }
1137
1138 if ( annot.border == Qgis::MapGridBorderSide::Top && ( ( facingLeft && annot.position.x() < mRotatedTicksMarginToCorner ) ||
1139 ( facingRight && annot.position.x() > mMap->rect().width() - mRotatedTicksMarginToCorner ) ) )
1140 continue;
1141 if ( annot.border == Qgis::MapGridBorderSide::Bottom && ( ( facingLeft && annot.position.x() > mMap->rect().width() - mRotatedTicksMarginToCorner ) ||
1142 ( facingRight && annot.position.x() < mRotatedTicksMarginToCorner ) ) )
1143 continue;
1144 if ( annot.border == Qgis::MapGridBorderSide::Left && ( ( facingLeft && annot.position.y() > mMap->rect().height() - mRotatedTicksMarginToCorner ) ||
1145 ( facingRight && annot.position.y() < mRotatedTicksMarginToCorner ) ) )
1146 continue;
1147 if ( annot.border == Qgis::MapGridBorderSide::Right && ( ( facingLeft && annot.position.y() < mRotatedTicksMarginToCorner ) ||
1148 ( facingRight && annot.position.y() > mMap->rect().height() - mRotatedTicksMarginToCorner ) ) )
1149 continue;
1150
1151 const QVector2D normalVector = borderToNormal2D( annot.border );
1152 const QVector2D vector = ( mRotatedTicksEnabled ) ? annot.vector : normalVector;
1153
1154 double fA = mEvaluatedGridFrameMargin; // point near to frame
1155 double fB = mEvaluatedGridFrameMargin + mEvaluatedGridFrameWidth; // point far from frame
1156
1157 if ( mRotatedTicksEnabled && mRotatedTicksLengthMode == Qgis::MapGridTickLengthMode::OrthogonalTicks )
1158 {
1159 fA /= QVector2D::dotProduct( vector, normalVector );
1160 fB /= QVector2D::dotProduct( vector, normalVector );
1161 }
1162
1163 // extents isn't computed accurately
1164 if ( extension )
1165 {
1166 if ( mGridFrameStyle != Qgis::MapGridFrameStyle::InteriorTicks )
1167 extension->UpdateBorder( annot.border, fB );
1168 continue;
1169 }
1170
1171 QVector2D pA;
1172 QVector2D pB;
1173 if ( mGridFrameStyle == Qgis::MapGridFrameStyle::InteriorTicks )
1174 {
1175 pA = annot.position + static_cast< float >( fA ) * vector;
1176 pB = annot.position + static_cast< float >( fB ) * vector;
1177 }
1178 else if ( mGridFrameStyle == Qgis::MapGridFrameStyle::ExteriorTicks )
1179 {
1180 pA = annot.position - static_cast< float >( fA ) * vector;
1181 pB = annot.position - static_cast< float >( fB ) * vector;
1182 }
1183 else // InteriorExteriorTicks
1184 {
1185 pA = annot.position - static_cast< float >( fB ) * vector;
1186 pB = annot.position + static_cast< float >( fB - 2.0 * mEvaluatedGridFrameMargin ) * vector;
1187 }
1188 p->drawLine( QLineF( pA.toPointF(), pB.toPointF() ) );
1189
1190 }
1191 }
1192}
1193
1194void QgsLayoutItemMapGrid::drawGridFrameLine( QPainter *p, GridExtension *extension ) const
1195{
1196 if ( !mMap )
1197 {
1198 return;
1199 }
1200
1201 if ( p )
1202 {
1203 //set pen to current frame pen
1204 QPen framePen = QPen( mGridFramePenColor );
1205 framePen.setWidthF( mEvaluatedGridFrameLineThickness );
1206 framePen.setCapStyle( Qt::SquareCap );
1207 p->setBrush( Qt::NoBrush );
1208 p->setPen( framePen );
1209 }
1210
1211 const bool drawDiagonals = mGridFrameStyle == Qgis::MapGridFrameStyle::LineBorderNautical && !qgsDoubleNear( mEvaluatedGridFrameMargin, 0.0 );
1212
1214 {
1215 if ( extension )
1216 extension->UpdateBorder( Qgis::MapGridBorderSide::Left, mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 );
1217 else
1218 p->drawLine( QLineF( 0 - mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin, mMap->rect().height() + mEvaluatedGridFrameMargin ) );
1219 }
1220
1222 {
1223 if ( extension )
1224 extension->UpdateBorder( Qgis::MapGridBorderSide::Right, mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 );
1225 else
1226 p->drawLine( QLineF( mMap->rect().width() + mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin, mMap->rect().width() + mEvaluatedGridFrameMargin, mMap->rect().height() + mEvaluatedGridFrameMargin ) );
1227 }
1228
1230 {
1231 if ( extension )
1232 extension->UpdateBorder( Qgis::MapGridBorderSide::Top, mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 );
1233 else
1234 p->drawLine( QLineF( 0 - mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin, mMap->rect().width() + mEvaluatedGridFrameMargin, 0 - mEvaluatedGridFrameMargin ) );
1235 }
1236
1238 {
1239 if ( extension )
1240 extension->UpdateBorder( Qgis::MapGridBorderSide::Bottom, mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 );
1241 else
1242 p->drawLine( QLineF( 0 - mEvaluatedGridFrameMargin, mMap->rect().height() + mEvaluatedGridFrameMargin, mMap->rect().width() + mEvaluatedGridFrameMargin, mMap->rect().height() + mEvaluatedGridFrameMargin ) );
1243 }
1244
1245 if ( ! extension && drawDiagonals )
1246 {
1248 {
1249 //corner left-top
1250 const double X1 = 0 - mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0;
1251 const double Y1 = 0 - mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0;
1252 p->drawLine( QLineF( 0, 0, X1, Y1 ) );
1253 }
1255 {
1256 //corner right-bottom
1257 const double X1 = mMap->rect().width() + mEvaluatedGridFrameMargin - mEvaluatedGridFrameLineThickness / 2.0 ;
1258 const double Y1 = mMap->rect().height() + mEvaluatedGridFrameMargin - mEvaluatedGridFrameLineThickness / 2.0 ;
1259 p->drawLine( QLineF( mMap->rect().width(), mMap->rect().height(), X1, Y1 ) );
1260 }
1262 {
1263 //corner right-top
1264 const double X1 = mMap->rect().width() + mEvaluatedGridFrameMargin - mEvaluatedGridFrameLineThickness / 2.0 ;
1265 const double Y1 = 0 - mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 ;
1266 p->drawLine( QLineF( mMap->rect().width(), 0, X1, Y1 ) );
1267 }
1269 {
1270 //corner left-bottom
1271 const double X1 = 0 - mEvaluatedGridFrameMargin + mEvaluatedGridFrameLineThickness / 2.0 ;
1272 const double Y1 = mMap->rect().height() + mEvaluatedGridFrameMargin - mEvaluatedGridFrameLineThickness / 2.0 ;
1273 p->drawLine( QLineF( 0, mMap->rect().height(), X1, Y1 ) );
1274 }
1275 }
1276}
1277
1278void QgsLayoutItemMapGrid::drawCoordinateAnnotations( QgsRenderContext &context, QgsExpressionContext &expressionContext,
1279 GridExtension *extension ) const
1280{
1281 if ( mGridLines.empty() )
1282 return;
1283
1284 QString currentAnnotationString;
1285 QList< GridLine >::const_iterator it = mGridLines.constBegin();
1286
1287 QgsExpressionContextScope *gridScope = new QgsExpressionContextScope();
1288 QgsExpressionContextScopePopper scopePopper( expressionContext, gridScope );
1289
1290 bool geographic = false;
1291 if ( mCRS.isValid() )
1292 {
1293 geographic = mCRS.isGeographic();
1294 }
1295 else if ( mMap && mMap->layout() )
1296 {
1297 geographic = mMap->crs().isGeographic();
1298 }
1299
1300 const bool forceWrap = ( geographic && it->coordinateType == Qgis::MapGridAnnotationType::Longitude &&
1301 ( mGridAnnotationFormat == Qgis::MapGridAnnotationFormat::Decimal || mGridAnnotationFormat == Qgis::MapGridAnnotationFormat::DecimalWithSuffix ) );
1302
1303 int countLongitudeLines = 0;
1304 int countLatitudeLines = 0;
1305 for ( const GridLine &line : mGridLines )
1306 {
1307 switch ( line.coordinateType )
1308 {
1310 countLongitudeLines++;
1311 break;
1313 countLatitudeLines++;
1314 break;
1315 }
1316 }
1317
1318 int latitudeLineIndex = 0;
1319 int longitudeLineIndex = 0;
1320 for ( ; it != mGridLines.constEnd(); ++it )
1321 {
1322 double value = it->coordinate;
1323 switch ( it->coordinateType )
1324 {
1326 longitudeLineIndex++;
1327 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_count"_s, countLongitudeLines, true ) );
1328 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_index"_s, longitudeLineIndex, true ) );
1329 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_axis"_s, u"x"_s, true ) );
1330 break;
1331
1333 latitudeLineIndex++;
1334 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_count"_s, countLatitudeLines, true ) );
1335 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_index"_s, latitudeLineIndex, true ) );
1336 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_axis"_s, u"y"_s, true ) );
1337 break;
1338 }
1339
1340 if ( forceWrap )
1341 {
1342 // wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W"
1343 const double wrappedX = std::fmod( value, 360.0 );
1344 if ( wrappedX > 180.0 )
1345 {
1346 value = wrappedX - 360.0;
1347 }
1348 else if ( wrappedX < -180.0 )
1349 {
1350 value = wrappedX + 360.0;
1351 }
1352 }
1353
1354 gridScope->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_number"_s, value, true ) );
1355
1356 if ( mDrawAnnotationProperty )
1357 {
1358 bool ok = false;
1359 const bool display = mDrawAnnotationProperty->valueAsBool( expressionContext, true, &ok );
1360 if ( ok && !display )
1361 continue;
1362 }
1363 currentAnnotationString = gridAnnotationString( it->coordinate, it->coordinateType, expressionContext, geographic );
1364 drawCoordinateAnnotation( context, it->startAnnotation, currentAnnotationString, it->coordinateType, extension );
1365 drawCoordinateAnnotation( context, it->endAnnotation, currentAnnotationString, it->coordinateType, extension );
1366 }
1367}
1368
1369void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QgsRenderContext &context, GridLineAnnotation annot, const QString &annotationString, const Qgis::MapGridAnnotationType coordinateType, GridExtension *extension ) const
1370{
1371 if ( !mMap )
1372 {
1373 return;
1374 }
1375
1376 if ( ! shouldShowAnnotationForSide( coordinateType, annot.border ) )
1377 return;
1378
1379 // painter is in MM, scale to dots
1380 std::unique_ptr< QgsScopedQPainterState > painterState;
1381 double dotsPerMM = 1;
1382
1383 if ( context.painter() && context.painter()->device() )
1384 {
1385 painterState = std::make_unique< QgsScopedQPainterState >( context.painter() );
1386 dotsPerMM = context.painter()->device()->logicalDpiX() / 25.4;
1387 context.painter()->scale( 1 / dotsPerMM, 1 / dotsPerMM ); //scale painter from mm to dots
1388 }
1389
1390 const Qgis::MapGridBorderSide frameBorder = annot.border;
1391
1392 const QgsTextDocument doc = QgsTextDocument::fromTextAndFormat( annotationString.split( '\n' ), mAnnotationFormat );
1393 const double textScaleFactor = QgsTextRenderer::calculateScaleFactorForFormat( context, mAnnotationFormat );
1394 const QgsTextDocumentMetrics documentMetrics = QgsTextDocumentMetrics::calculateMetrics( doc, mAnnotationFormat, context, textScaleFactor );
1395 const QSizeF sizePainterUnits = documentMetrics.documentSize( Qgis::TextLayoutMode::Point, Qgis::TextOrientation::Horizontal );
1396 const double painterUnitsToMM = 1 / context.convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
1397
1398 double textWidthPainterUnits = sizePainterUnits.width();
1399 if ( extension )
1400 textWidthPainterUnits *= 1.1; // little bit of extra padding when we are calculating the bounding rect, to account for antialiasing
1401
1402 const double textWidthMM = textWidthPainterUnits * painterUnitsToMM ;
1403
1404 double textHeightPainterUnits = 0;
1405 if ( extension || doc.size() > 1 )
1406 {
1407 textHeightPainterUnits = sizePainterUnits.height();
1408 }
1409 else
1410 {
1411 // special logic for single line annotations -- using fixed digit height only.
1412 // kept for pixel-perfect compatibility with existing renders prior to proper support for
1413 // multi-line annotation labels
1414 textHeightPainterUnits = QgsTextRenderer::textHeight( context, mAnnotationFormat, '0', false );
1415 }
1416 const double textHeightMM = textHeightPainterUnits * painterUnitsToMM;
1417
1418 const Qgis::MapGridAnnotationPosition anotPos = annotationPosition( frameBorder );
1419 const Qgis::MapGridAnnotationDirection anotDir = annotationDirection( frameBorder );
1420
1421 // If the angle is below the threshold, we don't draw the annotation
1422 if ( abs( annot.angle ) / M_PI * 180.0 > 90.0 - mRotatedAnnotationsMinimumAngle + 0.0001 )
1423 return;
1424
1425 const QVector2D normalVector = borderToNormal2D( annot.border );
1426 const QVector2D vector = ( mRotatedAnnotationsEnabled ) ? annot.vector : normalVector;
1427
1428 // Distance to frame
1429 double distanceToFrameMM = mEvaluatedAnnotationFrameDistance;
1430
1431 // Adapt distance to frame using the frame width and line thickness into account
1433 const bool hasInteriorMargin = ! isOverTick && ( mGridFrameStyle == Qgis::MapGridFrameStyle::InteriorTicks || mGridFrameStyle == Qgis::MapGridFrameStyle::InteriorExteriorTicks );
1434 const bool hasExteriorMargin = ! isOverTick && ( mGridFrameStyle == Qgis::MapGridFrameStyle::Zebra || mGridFrameStyle == Qgis::MapGridFrameStyle::ExteriorTicks || mGridFrameStyle == Qgis::MapGridFrameStyle::InteriorExteriorTicks || mGridFrameStyle == Qgis::MapGridFrameStyle::ZebraNautical );
1435 const bool hasBorderWidth = ( mGridFrameStyle == Qgis::MapGridFrameStyle::Zebra || mGridFrameStyle == Qgis::MapGridFrameStyle::ZebraNautical || mGridFrameStyle == Qgis::MapGridFrameStyle::LineBorder || mGridFrameStyle == Qgis::MapGridFrameStyle::LineBorderNautical );
1436 if ( ( anotPos == Qgis::MapGridAnnotationPosition::InsideMapFrame && hasInteriorMargin ) || ( anotPos == Qgis::MapGridAnnotationPosition::OutsideMapFrame && hasExteriorMargin ) )
1437 distanceToFrameMM += mEvaluatedGridFrameWidth;
1438 if ( hasBorderWidth )
1439 distanceToFrameMM += mEvaluatedGridFrameLineThickness / 2.0;
1440
1442 distanceToFrameMM *= -1;
1443
1444 if ( mRotatedAnnotationsEnabled && mRotatedAnnotationsLengthMode == Qgis::MapGridTickLengthMode::OrthogonalTicks )
1445 {
1446 distanceToFrameMM /= QVector2D::dotProduct( vector, normalVector );
1447 }
1448
1449 QPointF annotationPositionMM = ( annot.position + static_cast< float >( distanceToFrameMM ) * vector ).toPointF();
1450
1451 const bool outside = ( anotPos == Qgis::MapGridAnnotationPosition::OutsideMapFrame );
1452
1453 QPointF anchorMM;
1454 double rotation = 0;
1455
1456 if (
1460 )
1461 {
1462 rotation = atan2( vector.y(), vector.x() ) / M_PI * 180;
1463
1464 if ( rotation <= -90 || rotation > 90 )
1465 {
1466 rotation += 180;
1467 anchorMM.setX( outside ? 0 : textWidthMM ); // left / right
1468 }
1469 else
1470 {
1471 anchorMM.setX( outside ? textWidthMM : 0 ); // right / left
1472 }
1473
1475 anchorMM.setY( 0.5 * textHeightMM ); // bottom
1476 else if ( anotDir == Qgis::MapGridAnnotationDirection::UnderTick )
1477 anchorMM.setY( -1.5 * textHeightMM ); // top
1478 else // OnTick
1479 anchorMM.setY( -0.5 * textHeightMM ); // middle
1480
1481 }
1483 {
1484 rotation = 0;
1485 anchorMM.setX( 0.5 * textWidthMM ); // center
1486 anchorMM.setY( -0.5 * textHeightMM ); // middle
1487 if ( frameBorder == Qgis::MapGridBorderSide::Top )
1488 anchorMM.setY( outside ? 0 : -textHeightMM ); // bottom / top
1489 else if ( frameBorder == Qgis::MapGridBorderSide::Right )
1490 anchorMM.setX( outside ? 0 : textWidthMM ); // left / right
1491 else if ( frameBorder == Qgis::MapGridBorderSide::Bottom )
1492 anchorMM.setY( outside ? -textHeightMM : 0 ); // top / bottom
1493 else if ( frameBorder == Qgis::MapGridBorderSide::Left )
1494 anchorMM.setX( outside ? textWidthMM : 0 ); // right / left
1495 }
1496 else if ( anotDir == Qgis::MapGridAnnotationDirection::Vertical )
1497 {
1498 rotation = -90;
1499 anchorMM.setX( 0.5 * textWidthMM ); // center
1500 anchorMM.setY( -0.5 * textHeightMM ); // middle
1501 if ( frameBorder == Qgis::MapGridBorderSide::Top )
1502 anchorMM.setX( outside ? 0 : textWidthMM ); // left / right
1503 else if ( frameBorder == Qgis::MapGridBorderSide::Right )
1504 anchorMM.setY( outside ? -textHeightMM : 0 ); // top / bottom
1505 else if ( frameBorder == Qgis::MapGridBorderSide::Bottom )
1506 anchorMM.setX( outside ? textWidthMM : 0 ); // right / left
1507 else if ( frameBorder == Qgis::MapGridBorderSide::Left )
1508 anchorMM.setY( outside ? 0 : -textHeightMM ); // bottom / top
1509 }
1511 {
1512 rotation = 90;
1513 anchorMM.setX( 0.5 * textWidthMM ); // center
1514 anchorMM.setY( -0.5 * textHeightMM ); // middle
1515 if ( frameBorder == Qgis::MapGridBorderSide::Top )
1516 anchorMM.setX( outside ? textWidthMM : 0 ); // right / left
1517 else if ( frameBorder == Qgis::MapGridBorderSide::Right )
1518 anchorMM.setY( outside ? 0 : -textHeightMM ); // bottom / top
1519 else if ( frameBorder == Qgis::MapGridBorderSide::Bottom )
1520 anchorMM.setX( outside ? 0 : textWidthMM ); // left / right
1521 else if ( frameBorder == Qgis::MapGridBorderSide::Left )
1522 anchorMM.setY( outside ? -textHeightMM : 0 ); // top / bottom
1523 }
1524 else // ( anotDir == QgsLayoutItemMapGrid::BoundaryDirection )
1525 {
1526 const QVector2D borderVector = borderToVector2D( annot.border );
1527 rotation = atan2( borderVector.y(), borderVector.x() ) / M_PI * 180;
1528 anchorMM.setX( 0.5 * textWidthMM ); // center
1530 anchorMM.setY( -textHeightMM ); // top
1531 else
1532 anchorMM.setY( 0 ); // bottom
1533 }
1534
1535 // extents isn't computed accurately
1536 if ( extension && anotPos == Qgis::MapGridAnnotationPosition::OutsideMapFrame )
1537 {
1538 extension->UpdateBorder( frameBorder, -distanceToFrameMM + std::max( textHeightMM, textWidthMM ) );
1539 // We also add a general margin, can be useful for labels near corners
1540 extension->UpdateAll( std::max( textHeightMM, textWidthMM ) / 2.0 );
1541 }
1542
1543 if ( extension || !context.painter() )
1544 return;
1545
1546 // Skip outwards facing annotations that are below mRotatedAnnotationsMarginToCorner
1547 bool facingLeft = ( annot.angle < 0 );
1548 bool facingRight = ( annot.angle > 0 );
1550 {
1551 facingLeft = !facingLeft;
1552 facingRight = !facingRight;
1553 }
1554 if ( annot.border == Qgis::MapGridBorderSide::Top && ( ( facingLeft && annot.position.x() < mRotatedAnnotationsMarginToCorner ) ||
1555 ( facingRight && annot.position.x() > mMap->rect().width() - mRotatedAnnotationsMarginToCorner ) ) )
1556 return;
1557 if ( annot.border == Qgis::MapGridBorderSide::Bottom && ( ( facingLeft && annot.position.x() > mMap->rect().width() - mRotatedAnnotationsMarginToCorner ) ||
1558 ( facingRight && annot.position.x() < mRotatedAnnotationsMarginToCorner ) ) )
1559 return;
1560 if ( annot.border == Qgis::MapGridBorderSide::Left && ( ( facingLeft && annot.position.y() > mMap->rect().height() - mRotatedAnnotationsMarginToCorner ) ||
1561 ( facingRight && annot.position.y() < mRotatedAnnotationsMarginToCorner ) ) )
1562 return;
1563 if ( annot.border == Qgis::MapGridBorderSide::Right && ( ( facingLeft && annot.position.y() < mRotatedAnnotationsMarginToCorner ) ||
1564 ( facingRight && annot.position.y() > mMap->rect().height() - mRotatedAnnotationsMarginToCorner ) ) )
1565 return;
1566
1567 // adjust to account for text alignment -- for left/right borders the alignment
1568 // affects multiline text ONLY, but for top/bottom it also controls the
1569 // annotation placement with respect to the corresponding grid line
1570 Qgis::TextHorizontalAlignment textAlignment = mHAlign;
1571 QPointF textPos( 0, 0 );
1572 switch ( annot.border )
1573 {
1576 {
1577 switch ( anotDir )
1578 {
1583 switch ( mHAlign )
1584 {
1587 break;
1589 textPos.setX( textWidthMM / ( 2 * painterUnitsToMM ) );
1590 break;
1592 textPos.setX( textWidthMM / painterUnitsToMM );
1593 break;
1594 }
1595 break;
1596
1598 textPos.setX( textWidthMM / ( 2 * painterUnitsToMM ) );
1599 switch ( mHAlign )
1600 {
1604 break;
1606 break;
1609 break;
1610 }
1611 break;
1612
1615 switch ( mHAlign )
1616 {
1620 textPos.setX( textWidthMM / ( 2 * painterUnitsToMM ) );
1621 break;
1623 textPos.setX( textWidthMM / ( 2 * painterUnitsToMM ) );
1624 break;
1627 textPos.setX( textWidthMM / ( 2 * painterUnitsToMM ) );
1628 break;
1629 }
1630 break;
1631 }
1632 break;
1633 }
1634
1637 {
1638 switch ( anotDir )
1639 {
1641 {
1642 textPos.setX( textWidthMM / ( 2 * painterUnitsToMM ) );
1643 switch ( mHAlign )
1644 {
1648 break;
1650 break;
1653 break;
1654 }
1655 break;
1656 }
1659 {
1660 switch ( mHAlign )
1661 {
1665 break;
1667 textPos.setX( textWidthMM / ( 2 * painterUnitsToMM ) );
1668 break;
1670 textPos.setX( textWidthMM / painterUnitsToMM );
1672 break;
1673 }
1674 break;
1675 }
1676
1681 {
1682 textPos.setX( textWidthMM / ( 2 * painterUnitsToMM ) );
1683 switch ( mHAlign )
1684 {
1688 break;
1690 break;
1693 break;
1694 }
1695 break;
1696 }
1697 }
1698
1699 break;
1700 }
1701 }
1702
1703 context.painter()->translate( QPointF( annotationPositionMM.x(), annotationPositionMM.y() ) / painterUnitsToMM );
1704 context.painter()->rotate( rotation );
1705 context.painter()->translate( -anchorMM / painterUnitsToMM );
1706
1707 QgsTextRenderer::drawDocument( textPos, mAnnotationFormat, doc, documentMetrics, context,
1708 textAlignment,
1710}
1711
1712QString QgsLayoutItemMapGrid::gridAnnotationString( const double value, Qgis::MapGridAnnotationType coord, QgsExpressionContext &expressionContext, bool isGeographic ) const
1713{
1714 //check if we are using degrees (ie, geographic crs)
1715
1716 if ( mGridAnnotationFormat == Qgis::MapGridAnnotationFormat::Decimal )
1717 {
1718 return QString::number( value, 'f', mGridAnnotationPrecision );
1719 }
1720 else if ( mGridAnnotationFormat == Qgis::MapGridAnnotationFormat::DecimalWithSuffix )
1721 {
1722 QString hemisphere;
1723
1724 const double coordRounded = qgsRound( value, mGridAnnotationPrecision );
1726 {
1727 //don't use E/W suffixes if ambiguous (e.g., 180 degrees)
1728 if ( !isGeographic || ( coordRounded != 180.0 && coordRounded != 0.0 ) )
1729 {
1730 hemisphere = value < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
1731 }
1732 }
1733 else
1734 {
1735 //don't use N/S suffixes if ambiguous (e.g., 0 degrees)
1736 if ( !isGeographic || coordRounded != 0.0 )
1737 {
1738 hemisphere = value < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
1739 }
1740 }
1741 if ( isGeographic )
1742 {
1743 //insert degree symbol for geographic coordinates
1744 return QString::number( std::fabs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere;
1745 }
1746 else
1747 {
1748 return QString::number( std::fabs( value ), 'f', mGridAnnotationPrecision ) + hemisphere;
1749 }
1750 }
1751 else if ( mGridAnnotationFormat == Qgis::MapGridAnnotationFormat::CustomFormat )
1752 {
1753 if ( !mGridAnnotationExpression )
1754 {
1755 mGridAnnotationExpression = std::make_unique<QgsExpression>( mGridAnnotationExpressionString );
1756 mGridAnnotationExpression->prepare( &expressionContext );
1757 }
1758 return mGridAnnotationExpression->evaluate( &expressionContext ).toString();
1759 }
1760
1763 switch ( mGridAnnotationFormat )
1764 {
1768 break; // already handled above
1769
1773 break;
1774
1778 break;
1779
1783 break;
1784
1788 break;
1789
1793 break;
1794
1798 break;
1799 }
1800
1801 switch ( coord )
1802 {
1804 return QgsCoordinateFormatter::formatX( value, format, mGridAnnotationPrecision, flags );
1805
1807 return QgsCoordinateFormatter::formatY( value, format, mGridAnnotationPrecision, flags );
1808 }
1809
1810 return QString(); // no warnings
1811}
1812
1813int QgsLayoutItemMapGrid::xGridLines() const
1814{
1815 if ( !mMap || mEvaluatedIntervalY <= 0.0 )
1816 {
1817 return 1;
1818 }
1819
1820
1821 QPolygonF mapPolygon = mMap->transformedMapPolygon();
1822 QRectF mapBoundingRect = mapPolygon.boundingRect();
1823 double gridIntervalY = mEvaluatedIntervalY;
1824 double gridOffsetY = mEvaluatedOffsetY;
1825 double annotationScale = 1.0;
1826 switch ( mGridUnit )
1827 {
1830 {
1831 mapBoundingRect = mMap->rect();
1832 mapPolygon = QPolygonF( mMap->rect() );
1833 if ( mGridUnit == Qgis::MapGridUnit::Centimeters )
1834 {
1835 annotationScale = 0.1;
1836 gridIntervalY *= 10;
1837 gridOffsetY *= 10;
1838 }
1839 break;
1840 }
1841
1844 break;
1845 }
1846
1847 //consider to round up to the next step in case the left boundary is > 0
1848 const double roundCorrection = mapBoundingRect.top() > gridOffsetY ? 1.0 : 0.0;
1849 double currentLevel = static_cast< int >( ( mapBoundingRect.top() - gridOffsetY ) / gridIntervalY + roundCorrection ) * gridIntervalY + gridOffsetY;
1850
1851 int gridLineCount = 0;
1852 if ( qgsDoubleNear( mMap->mapRotation(), 0.0 ) || ( mGridUnit != Qgis::MapGridUnit::MapUnits && mGridUnit != Qgis::MapGridUnit::DynamicPageSizeBased ) )
1853 {
1854 //no rotation. Do it 'the easy way'
1855
1856 double yCanvasCoord;
1857 while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1858 {
1859 yCanvasCoord = mMap->rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
1860 GridLine newLine;
1861 newLine.coordinate = currentLevel * annotationScale;
1862 newLine.coordinateType = Qgis::MapGridAnnotationType::Latitude;
1863 newLine.line = QPolygonF() << QPointF( 0, yCanvasCoord ) << QPointF( mMap->rect().width(), yCanvasCoord );
1864 mGridLines.append( newLine );
1865 currentLevel += gridIntervalY;
1866 gridLineCount++;
1867 }
1868 return 0;
1869 }
1870
1871 //the four border lines
1872 QVector<QLineF> borderLines;
1873 borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1874 borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1875 borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1876 borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1877
1878 QVector<QPointF> intersectionList; //intersects between border lines and grid lines
1879
1880 while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1881 {
1882 intersectionList.clear();
1883 const QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
1884
1885 QVector<QLineF>::const_iterator it = borderLines.constBegin();
1886 for ( ; it != borderLines.constEnd(); ++it )
1887 {
1888 QPointF intersectionPoint;
1889 if ( it->intersects( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1890 {
1891 intersectionList.push_back( intersectionPoint );
1892 if ( intersectionList.size() >= 2 )
1893 {
1894 break; //we already have two intersections, skip further tests
1895 }
1896 }
1897 }
1898
1899 if ( intersectionList.size() >= 2 )
1900 {
1901 GridLine newLine;
1902 newLine.coordinate = currentLevel;
1903 newLine.coordinateType = Qgis::MapGridAnnotationType::Latitude;
1904 newLine.line = QPolygonF() << mMap->mapToItemCoords( intersectionList.at( 0 ) ) << mMap->mapToItemCoords( intersectionList.at( 1 ) );
1905 mGridLines.append( newLine );
1906 gridLineCount++;
1907 }
1908 currentLevel += gridIntervalY;
1909 }
1910
1911
1912 return 0;
1913}
1914
1915int QgsLayoutItemMapGrid::yGridLines() const
1916{
1917 if ( !mMap || mEvaluatedIntervalX <= 0.0 )
1918 {
1919 return 1;
1920 }
1921
1922 QPolygonF mapPolygon = mMap->transformedMapPolygon();
1923 QRectF mapBoundingRect = mapPolygon.boundingRect();
1924 double gridIntervalX = mEvaluatedIntervalX;
1925 double gridOffsetX = mEvaluatedOffsetX;
1926 double annotationScale = 1.0;
1927 switch ( mGridUnit )
1928 {
1931 {
1932 mapBoundingRect = mMap->rect();
1933 mapPolygon = QPolygonF( mMap->rect() );
1934 if ( mGridUnit == Qgis::MapGridUnit::Centimeters )
1935 {
1936 annotationScale = 0.1;
1937 gridIntervalX *= 10;
1938 gridOffsetX *= 10;
1939 }
1940 break;
1941 }
1942
1945 break;
1946 }
1947
1948 //consider to round up to the next step in case the left boundary is > 0
1949 const double roundCorrection = mapBoundingRect.left() > gridOffsetX ? 1.0 : 0.0;
1950 double currentLevel = static_cast< int >( ( mapBoundingRect.left() - gridOffsetX ) / gridIntervalX + roundCorrection ) * gridIntervalX + gridOffsetX;
1951
1952 int gridLineCount = 0;
1953 if ( qgsDoubleNear( mMap->mapRotation(), 0.0 ) || ( mGridUnit != Qgis::MapGridUnit::MapUnits && mGridUnit != Qgis::MapGridUnit::DynamicPageSizeBased ) )
1954 {
1955 //no rotation. Do it 'the easy way'
1956 double xCanvasCoord;
1957 while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1958 {
1959 xCanvasCoord = mMap->rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
1960
1961 GridLine newLine;
1962 newLine.coordinate = currentLevel * annotationScale;
1963 newLine.coordinateType = Qgis::MapGridAnnotationType::Longitude;
1964 newLine.line = QPolygonF() << QPointF( xCanvasCoord, 0 ) << QPointF( xCanvasCoord, mMap->rect().height() );
1965 mGridLines.append( newLine );
1966 currentLevel += gridIntervalX;
1967 gridLineCount++;
1968 }
1969 return 0;
1970 }
1971
1972 //the four border lines
1973 QVector<QLineF> borderLines;
1974 borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1975 borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1976 borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1977 borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1978
1979 QVector<QPointF> intersectionList; //intersects between border lines and grid lines
1980
1981 while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1982 {
1983 intersectionList.clear();
1984 const QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
1985
1986 QVector<QLineF>::const_iterator it = borderLines.constBegin();
1987 for ( ; it != borderLines.constEnd(); ++it )
1988 {
1989 QPointF intersectionPoint;
1990 if ( it->intersects( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1991 {
1992 intersectionList.push_back( intersectionPoint );
1993 if ( intersectionList.size() >= 2 )
1994 {
1995 break; //we already have two intersections, skip further tests
1996 }
1997 }
1998 }
1999
2000 if ( intersectionList.size() >= 2 )
2001 {
2002 GridLine newLine;
2003 newLine.coordinate = currentLevel;
2004 newLine.coordinateType = Qgis::MapGridAnnotationType::Longitude;
2005 newLine.line = QPolygonF() << mMap->mapToItemCoords( intersectionList.at( 0 ) ) << mMap->mapToItemCoords( intersectionList.at( 1 ) );
2006 mGridLines.append( newLine );
2007 gridLineCount++;
2008 }
2009 currentLevel += gridIntervalX;
2010 }
2011
2012 return 0;
2013}
2014
2015int QgsLayoutItemMapGrid::xGridLinesCrsTransform( const QgsRectangle &bbox, const QgsCoordinateTransform &t ) const
2016{
2017 if ( !mMap || mEvaluatedIntervalY <= 0.0 )
2018 {
2019 return 1;
2020 }
2021
2022 const double roundCorrection = bbox.yMaximum() > mEvaluatedOffsetY ? 1.0 : 0.0;
2023 double currentLevel = static_cast< int >( ( bbox.yMaximum() - mEvaluatedOffsetY ) / mEvaluatedIntervalY + roundCorrection ) * mEvaluatedIntervalY + mEvaluatedOffsetY;
2024
2025 const double minX = bbox.xMinimum();
2026 const double maxX = bbox.xMaximum();
2027 double step = ( maxX - minX ) / 20;
2028
2029 bool crosses180 = false;
2030 bool crossed180 = false;
2031 if ( mCRS.isGeographic() && ( minX > maxX ) )
2032 {
2033 //handle 180 degree longitude crossover
2034 crosses180 = true;
2035 step = ( maxX + 360.0 - minX ) / 20;
2036 }
2037
2038 if ( qgsDoubleNear( step, 0.0 ) )
2039 return 1;
2040
2041 int gridLineCount = 0;
2042 while ( currentLevel >= bbox.yMinimum() && gridLineCount < MAX_GRID_LINES )
2043 {
2044 QPolygonF gridLine;
2045 double currentX = minX;
2046 bool cont = true;
2047 while ( cont )
2048 {
2049 if ( ( !crosses180 || crossed180 ) && ( currentX > maxX ) )
2050 {
2051 cont = false;
2052 }
2053
2054 try
2055 {
2056 const QgsPointXY mapPoint = t.transform( currentX, currentLevel ); //transform back to map crs
2057 gridLine.append( mMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) ); //transform back to composer coords
2058 }
2059 catch ( QgsCsException &cse )
2060 {
2061 Q_UNUSED( cse )
2062 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
2063 }
2064
2065 currentX += step;
2066 if ( crosses180 && currentX > 180.0 )
2067 {
2068 currentX -= 360.0;
2069 crossed180 = true;
2070 }
2071 }
2072 crossed180 = false;
2073
2074 const QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mMap->rect() ) );
2075 QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
2076 for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
2077 {
2078 if ( !( *lineIt ).isEmpty() )
2079 {
2080 GridLine newLine;
2081 newLine.coordinate = currentLevel;
2082 newLine.coordinateType = Qgis::MapGridAnnotationType::Latitude;
2083 newLine.line = QPolygonF( *lineIt );
2084 mGridLines.append( newLine );
2085 gridLineCount++;
2086 }
2087 }
2088 currentLevel -= mEvaluatedIntervalY;
2089 }
2090
2091 return 0;
2092}
2093
2094int QgsLayoutItemMapGrid::yGridLinesCrsTransform( const QgsRectangle &bbox, const QgsCoordinateTransform &t ) const
2095{
2096 if ( !mMap || mEvaluatedIntervalX <= 0.0 )
2097 {
2098 return 1;
2099 }
2100
2101 const double roundCorrection = bbox.xMinimum() > mEvaluatedOffsetX ? 1.0 : 0.0;
2102 double currentLevel = static_cast< int >( ( bbox.xMinimum() - mEvaluatedOffsetX ) / mEvaluatedIntervalX + roundCorrection ) * mEvaluatedIntervalX + mEvaluatedOffsetX;
2103
2104 const double minY = bbox.yMinimum();
2105 const double maxY = bbox.yMaximum();
2106 const double step = ( maxY - minY ) / 20;
2107
2108 if ( qgsDoubleNear( step, 0.0 ) )
2109 return 1;
2110
2111 bool crosses180 = false;
2112 bool crossed180 = false;
2113 if ( mCRS.isGeographic() && ( bbox.xMinimum() > bbox.xMaximum() ) )
2114 {
2115 //handle 180 degree longitude crossover
2116 crosses180 = true;
2117 }
2118
2119 int gridLineCount = 0;
2120 while ( ( currentLevel <= bbox.xMaximum() || ( crosses180 && !crossed180 ) ) && gridLineCount < MAX_GRID_LINES )
2121 {
2122 QPolygonF gridLine;
2123 double currentY = minY;
2124 bool cont = true;
2125 while ( cont )
2126 {
2127 if ( currentY > maxY )
2128 {
2129 cont = false;
2130 }
2131 try
2132 {
2133 //transform back to map crs
2134 const QgsPointXY mapPoint = t.transform( currentLevel, currentY );
2135 //transform back to composer coords
2136 gridLine.append( mMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) );
2137 }
2138 catch ( QgsCsException &cse )
2139 {
2140 Q_UNUSED( cse )
2141 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
2142 }
2143
2144 currentY += step;
2145 }
2146 //clip grid line to map polygon
2147 const QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mMap->rect() ) );
2148 QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
2149 for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
2150 {
2151 if ( !( *lineIt ).isEmpty() )
2152 {
2153 GridLine newLine;
2154 newLine.coordinate = currentLevel;
2155 newLine.coordinateType = Qgis::MapGridAnnotationType::Longitude;
2156 newLine.line = QPolygonF( *lineIt );
2157 mGridLines.append( newLine );
2158 gridLineCount++;
2159 }
2160 }
2161 currentLevel += mEvaluatedIntervalX;
2162 if ( crosses180 && currentLevel > 180.0 )
2163 {
2164 currentLevel -= 360.0;
2165 crossed180 = true;
2166 }
2167 }
2168
2169 return 0;
2170}
2171
2172bool QgsLayoutItemMapGrid::shouldShowDivisionForSide( Qgis::MapGridAnnotationType coordinate, Qgis::MapGridBorderSide side ) const
2173{
2174 switch ( side )
2175 {
2177 return testFrameSideFlag( Qgis::MapGridFrameSideFlag::Left ) && shouldShowForDisplayMode( coordinate, mEvaluatedLeftFrameDivisions );
2179 return testFrameSideFlag( Qgis::MapGridFrameSideFlag::Right ) && shouldShowForDisplayMode( coordinate, mEvaluatedRightFrameDivisions );
2181 return testFrameSideFlag( Qgis::MapGridFrameSideFlag::Top ) && shouldShowForDisplayMode( coordinate, mEvaluatedTopFrameDivisions );
2183 return testFrameSideFlag( Qgis::MapGridFrameSideFlag::Bottom ) && shouldShowForDisplayMode( coordinate, mEvaluatedBottomFrameDivisions );
2184 }
2185 return false; // no warnings
2186}
2187
2188bool QgsLayoutItemMapGrid::shouldShowAnnotationForSide( Qgis::MapGridAnnotationType coordinate, Qgis::MapGridBorderSide side ) const
2189{
2190 switch ( side )
2191 {
2193 return shouldShowForDisplayMode( coordinate, mEvaluatedLeftGridAnnotationDisplay );
2195 return shouldShowForDisplayMode( coordinate, mEvaluatedRightGridAnnotationDisplay );
2197 return shouldShowForDisplayMode( coordinate, mEvaluatedTopGridAnnotationDisplay );
2199 return shouldShowForDisplayMode( coordinate, mEvaluatedBottomGridAnnotationDisplay );
2200 }
2201 return false; // no warnings
2202}
2203
2204bool QgsLayoutItemMapGrid::shouldShowForDisplayMode( Qgis::MapGridAnnotationType coordinate, Qgis::MapGridComponentVisibility mode ) const
2205{
2209}
2210
2212{
2213 if ( ddValue.compare( "x_only"_L1, Qt::CaseInsensitive ) == 0 )
2215 else if ( ddValue.compare( "y_only"_L1, Qt::CaseInsensitive ) == 0 )
2217 else if ( ddValue.compare( "disabled"_L1, Qt::CaseInsensitive ) == 0 )
2219 else if ( ddValue.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
2221 else
2222 return defValue;
2223}
2224
2225void QgsLayoutItemMapGrid::refreshDataDefinedProperties()
2226{
2227 const QgsExpressionContext context = createExpressionContext();
2228
2229 // if we are changing the grid interval or offset, then we also have to mark the transform as dirty
2230 mTransformDirty = mTransformDirty
2235
2236 mEvaluatedEnabled = mDataDefinedProperties.valueAsBool( QgsLayoutObject::DataDefinedProperty::MapGridEnabled, context, enabled() );
2237
2239 {
2240 mDrawAnnotationProperty.reset( new QgsProperty( mDataDefinedProperties.property( QgsLayoutObject::DataDefinedProperty::MapGridDrawAnnotation ) ) );
2241 mDrawAnnotationProperty->prepare( context );
2242 }
2243 else
2244 {
2245 mDrawAnnotationProperty.reset();
2246 }
2247
2248 switch ( mGridUnit )
2249 {
2253 {
2254 mEvaluatedIntervalX = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridIntervalX, context, mGridIntervalX );
2255 mEvaluatedIntervalY = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridIntervalY, context, mGridIntervalY );
2256 break;
2257 }
2258
2260 {
2261 if ( mMaximumIntervalWidth < mMinimumIntervalWidth )
2262 {
2263 mEvaluatedEnabled = false;
2264 }
2265 else
2266 {
2267 const double mapWidthMm = mLayout->renderContext().measurementConverter().convert( mMap->sizeWithUnits(), Qgis::LayoutUnit::Millimeters ).width();
2268 const double mapWidthMapUnits = mapWidth();
2269 const double minUnitsPerSeg = ( mMinimumIntervalWidth * mapWidthMapUnits ) / mapWidthMm;
2270 const double maxUnitsPerSeg = ( mMaximumIntervalWidth * mapWidthMapUnits ) / mapWidthMm;
2271 const double interval = QgsLayoutUtils::calculatePrettySize( minUnitsPerSeg, maxUnitsPerSeg );
2272 mEvaluatedIntervalX = interval;
2273 mEvaluatedIntervalY = interval;
2274 mTransformDirty = true;
2275 }
2276 break;
2277 }
2278 }
2279 mEvaluatedOffsetX = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridOffsetX, context, mGridOffsetX );
2280 mEvaluatedOffsetY = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridOffsetY, context, mGridOffsetY );
2281 mEvaluatedGridFrameWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridFrameSize, context, mGridFrameWidth );
2282 mEvaluatedGridFrameMargin = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridFrameMargin, context, mGridFrameMargin );
2283 mEvaluatedAnnotationFrameDistance = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridLabelDistance, context, mAnnotationFrameDistance );
2284 mEvaluatedCrossLength = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridCrossSize, context, mCrossLength );
2285 mEvaluatedGridFrameLineThickness = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::DataDefinedProperty::MapGridFrameLineThickness, context, mGridFramePenThickness );
2286 mEvaluatedLeftGridAnnotationDisplay = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridAnnotationDisplayLeft, context ), mLeftGridAnnotationDisplay );
2287 mEvaluatedRightGridAnnotationDisplay = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridAnnotationDisplayRight, context ), mRightGridAnnotationDisplay );
2288 mEvaluatedTopGridAnnotationDisplay = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridAnnotationDisplayTop, context ), mTopGridAnnotationDisplay );
2289 mEvaluatedBottomGridAnnotationDisplay = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridAnnotationDisplayBottom, context ), mBottomGridAnnotationDisplay );
2290 mEvaluatedLeftFrameDivisions = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridFrameDivisionsLeft, context ), mLeftFrameDivisions );
2291 mEvaluatedRightFrameDivisions = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridFrameDivisionsRight, context ), mRightFrameDivisions );
2292 mEvaluatedTopFrameDivisions = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridFrameDivisionsTop, context ), mTopFrameDivisions );
2293 mEvaluatedBottomFrameDivisions = gridAnnotationDisplayModeFromDD( mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::MapGridFrameDivisionsBottom, context ), mBottomFrameDivisions );
2294}
2295
2296double QgsLayoutItemMapGrid::mapWidth() const
2297{
2298 if ( !mMap )
2299 {
2300 return 0.0;
2301 }
2302
2303 const QgsRectangle mapExtent = mMap->extent();
2304 const Qgis::DistanceUnit distanceUnit = mCRS.isValid() ? mCRS.mapUnits() : mMap->crs().mapUnits();
2305 if ( distanceUnit == Qgis::DistanceUnit::Unknown )
2306 {
2307 return mapExtent.width();
2308 }
2309 else
2310 {
2311 QgsDistanceArea da;
2312
2313 da.setSourceCrs( mMap->crs(), mLayout->project()->transformContext() );
2314 da.setEllipsoid( mLayout->project()->ellipsoid() );
2315
2317 double measure = 0;
2318 try
2319 {
2320 measure = da.measureLine( QgsPointXY( mapExtent.xMinimum(), mapExtent.yMinimum() ),
2321 QgsPointXY( mapExtent.xMaximum(), mapExtent.yMinimum() ) );
2322 measure /= QgsUnitTypes::fromUnitToUnitFactor( distanceUnit, units );
2323 }
2324 catch ( QgsCsException & )
2325 {
2326 // TODO report errors to user
2327 QgsDebugError( u"An error occurred while calculating length"_s );
2328 }
2329 return measure;
2330 }
2331}
2332
2333bool sortByDistance( QPair<qreal, Qgis::MapGridBorderSide> a, QPair<qreal, Qgis::MapGridBorderSide> b )
2334{
2335 return a.first < b.first;
2336}
2337
2338Qgis::MapGridBorderSide QgsLayoutItemMapGrid::borderForLineCoord( QPointF p, const Qgis::MapGridAnnotationType coordinateType ) const
2339{
2340 if ( !mMap )
2341 {
2343 }
2344
2345 const double tolerance = std::max( mMap->frameEnabled() ? mMap->pen().widthF() : 0.0, 1.0 );
2346
2347 //check for corner coordinates
2348 if ( ( p.y() <= tolerance && p.x() <= tolerance ) // top left
2349 || ( p.y() <= tolerance && p.x() >= ( mMap->rect().width() - tolerance ) ) //top right
2350 || ( p.y() >= ( mMap->rect().height() - tolerance ) && p.x() <= tolerance ) //bottom left
2351 || ( p.y() >= ( mMap->rect().height() - tolerance ) && p.x() >= ( mMap->rect().width() - tolerance ) ) //bottom right
2352 )
2353 {
2354 //coordinate is in corner - fall back to preferred side for coordinate type
2355 if ( coordinateType == Qgis::MapGridAnnotationType::Latitude )
2356 {
2357 if ( p.x() <= tolerance )
2358 {
2360 }
2361 else
2362 {
2364 }
2365 }
2366 else
2367 {
2368 if ( p.y() <= tolerance )
2369 {
2371 }
2372 else
2373 {
2375 }
2376 }
2377 }
2378
2379 //otherwise, guess side based on closest map side to point
2380 QList< QPair<qreal, Qgis::MapGridBorderSide > > distanceToSide;
2381 distanceToSide << qMakePair( p.x(), Qgis::MapGridBorderSide::Left );
2382 distanceToSide << qMakePair( mMap->rect().width() - p.x(), Qgis::MapGridBorderSide::Right );
2383 distanceToSide << qMakePair( p.y(), Qgis::MapGridBorderSide::Top );
2384 distanceToSide << qMakePair( mMap->rect().height() - p.y(), Qgis::MapGridBorderSide::Bottom );
2385
2386 std::sort( distanceToSide.begin(), distanceToSide.end(), sortByDistance );
2387 return distanceToSide.at( 0 ).second;
2388}
2389
2391{
2392 mGridLineSymbol.reset( symbol );
2393}
2394
2396{
2397 return mGridLineSymbol.get();
2398}
2399
2401{
2402 return mGridLineSymbol.get();
2403}
2404
2406{
2407 mGridMarkerSymbol.reset( symbol );
2408}
2409
2411{
2412 return mGridMarkerSymbol.get();
2413}
2414
2416{
2417 return mGridMarkerSymbol.get();
2418}
2419
2421{
2422 mAnnotationFormat.setFont( font );
2423 if ( font.pointSizeF() > 0 )
2424 {
2425 mAnnotationFormat.setSize( font.pointSizeF() );
2426 mAnnotationFormat.setSizeUnit( Qgis::RenderUnit::Points );
2427 }
2428 else if ( font.pixelSize() > 0 )
2429 {
2430 mAnnotationFormat.setSize( font.pixelSize() );
2431 mAnnotationFormat.setSizeUnit( Qgis::RenderUnit::Pixels );
2432 }
2433}
2434
2436{
2437 return mAnnotationFormat.toQFont();
2438}
2439
2441{
2442 mAnnotationFormat.setColor( color );
2443}
2444
2446{
2447 return mAnnotationFormat.color();
2448}
2449
2451{
2452 switch ( border )
2453 {
2455 mLeftGridAnnotationDisplay = display;
2456 break;
2458 mRightGridAnnotationDisplay = display;
2459 break;
2461 mTopGridAnnotationDisplay = display;
2462 break;
2464 mBottomGridAnnotationDisplay = display;
2465 break;
2466 }
2467
2468 refreshDataDefinedProperties();
2469
2470 if ( mMap )
2471 {
2472 mMap->updateBoundingRect();
2473 mMap->update();
2474 }
2475}
2476
2478{
2479 switch ( border )
2480 {
2482 return mLeftGridAnnotationDisplay;
2484 return mRightGridAnnotationDisplay;
2486 return mTopGridAnnotationDisplay;
2488 return mBottomGridAnnotationDisplay;
2489 }
2490 return mBottomGridAnnotationDisplay; // no warnings
2491}
2492
2494{
2495 double top = 0.0;
2496 double right = 0.0;
2497 double bottom = 0.0;
2498 double left = 0.0;
2499 calculateMaxExtension( top, right, bottom, left );
2500 return std::max( std::max( std::max( top, right ), bottom ), left );
2501}
2502
2503void QgsLayoutItemMapGrid::calculateMaxExtension( double &top, double &right, double &bottom, double &left ) const
2504{
2505 top = 0.0;
2506 right = 0.0;
2507 bottom = 0.0;
2508 left = 0.0;
2509
2510 if ( !mMap || !mEvaluatedEnabled )
2511 {
2512 return;
2513 }
2514
2515 //setup render context
2517 const QgsExpressionContext expressionContext = createExpressionContext();
2518 context.setExpressionContext( expressionContext );
2519
2520 GridExtension extension;
2521
2522 //collect grid lines
2523 switch ( mGridUnit )
2524 {
2527 {
2528 if ( mCRS.isValid() && mCRS != mMap->crs() )
2529 {
2530 drawGridCrsTransform( context, 0, true );
2531 break;
2532 }
2533 }
2534 [[fallthrough]];
2537 drawGridNoTransform( context, 0, true );
2538 break;
2539 }
2540
2541 if ( mGridFrameStyle != Qgis::MapGridFrameStyle::NoFrame || mShowGridAnnotation )
2542 updateGridLinesAnnotationsPositions();
2543
2544 if ( mGridFrameStyle != Qgis::MapGridFrameStyle::NoFrame )
2545 {
2546 drawGridFrame( nullptr, &extension );
2547 }
2548
2549 if ( mShowGridAnnotation )
2550 {
2551 drawCoordinateAnnotations( context, context.expressionContext(), &extension );
2552 }
2553
2554 top = extension.top;
2555 right = extension.right;
2556 bottom = extension.bottom;
2557 left = extension.left;
2558}
2559
2561{
2563 refreshDataDefinedProperties();
2564}
2565
2567{
2568 if ( unit == mGridUnit )
2569 {
2570 return;
2571 }
2572 mGridUnit = unit;
2573 mTransformDirty = true;
2574}
2575
2576void QgsLayoutItemMapGrid::setIntervalX( const double interval )
2577{
2578 if ( qgsDoubleNear( interval, mGridIntervalX ) )
2579 {
2580 return;
2581 }
2582 mGridIntervalX = interval;
2583 mTransformDirty = true;
2584 refreshDataDefinedProperties();
2585}
2586
2587void QgsLayoutItemMapGrid::setIntervalY( const double interval )
2588{
2589 if ( qgsDoubleNear( interval, mGridIntervalY ) )
2590 {
2591 return;
2592 }
2593 mGridIntervalY = interval;
2594 mTransformDirty = true;
2595 refreshDataDefinedProperties();
2596}
2597
2598void QgsLayoutItemMapGrid::setOffsetX( const double offset )
2599{
2600 if ( qgsDoubleNear( offset, mGridOffsetX ) )
2601 {
2602 return;
2603 }
2604 mGridOffsetX = offset;
2605 mTransformDirty = true;
2606 refreshDataDefinedProperties();
2607}
2608
2609void QgsLayoutItemMapGrid::setOffsetY( const double offset )
2610{
2611 if ( qgsDoubleNear( offset, mGridOffsetY ) )
2612 {
2613 return;
2614 }
2615 mGridOffsetY = offset;
2616 mTransformDirty = true;
2617 refreshDataDefinedProperties();
2618}
2619
2621{
2622 if ( qgsDoubleNear( minWidth, mMinimumIntervalWidth ) )
2623 {
2624 return;
2625 }
2626 mMinimumIntervalWidth = minWidth;
2627 mTransformDirty = true;
2628 refreshDataDefinedProperties();
2629}
2630
2632{
2633 if ( qgsDoubleNear( maxWidth, mMaximumIntervalWidth ) )
2634 {
2635 return;
2636 }
2637 mMaximumIntervalWidth = maxWidth;
2638 mTransformDirty = true;
2639 refreshDataDefinedProperties();
2640}
2641
2643{
2644 if ( style == mGridStyle )
2645 {
2646 return;
2647 }
2648 mGridStyle = style;
2649 mTransformDirty = true;
2650}
2651
2652void QgsLayoutItemMapGrid::setCrossLength( const double length )
2653{
2654 mCrossLength = length;
2655 refreshDataDefinedProperties();
2656}
2657
2659{
2660 switch ( border )
2661 {
2663 mLeftGridAnnotationDirection = direction;
2664 break;
2666 mRightGridAnnotationDirection = direction;
2667 break;
2669 mTopGridAnnotationDirection = direction;
2670 break;
2672 mBottomGridAnnotationDirection = direction;
2673 break;
2674 }
2675
2676 if ( mMap )
2677 {
2678 mMap->updateBoundingRect();
2679 mMap->update();
2680 }
2681}
2682
2684{
2685 mGridFrameSides = flags;
2686}
2687
2689{
2690 mGridFrameSides.setFlag( flag, on );
2691}
2692
2694{
2695 return mGridFrameSides;
2696}
2697
2699{
2701 context.appendScope( new QgsExpressionContextScope( tr( "Grid" ) ) );
2702 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_number"_s, 0, true ) );
2703 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"grid_axis"_s, "x", true ) );
2704 context.setHighlightedVariables( QStringList() << u"grid_number"_s << u"grid_axis"_s );
2705 return context;
2706}
2707
2709{
2710 if ( mGridLineSymbol )
2711 {
2712 QgsStyleSymbolEntity entity( mGridLineSymbol.get() );
2713 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, u"grid"_s, QObject::tr( "Grid" ) ) ) )
2714 return false;
2715 }
2716 if ( mGridMarkerSymbol )
2717 {
2718 QgsStyleSymbolEntity entity( mGridMarkerSymbol.get() );
2719 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, u"grid"_s, QObject::tr( "Grid" ) ) ) )
2720 return false;
2721 }
2722
2723 return true;
2724}
2725
2727{
2728 mTransformDirty = true;
2729 refreshDataDefinedProperties();
2730 mMap->updateBoundingRect();
2731 mMap->update();
2732}
2733
2735{
2736 return mGridFrameSides.testFlag( flag );
2737}
2738
2739void QgsLayoutItemMapGrid::setFrameWidth( const double width )
2740{
2741 mGridFrameWidth = width;
2742 refreshDataDefinedProperties();
2743}
2744
2745void QgsLayoutItemMapGrid::setFrameMargin( const double margin )
2746{
2747 mGridFrameMargin = margin;
2748 refreshDataDefinedProperties();
2749}
2750
2752{
2753 mGridFramePenThickness = width;
2754 refreshDataDefinedProperties();
2755}
2756
2761
2763{
2764 mHAlign = alignment;
2765}
2766
2768{
2769 mLeftGridAnnotationDirection = direction;
2770 mRightGridAnnotationDirection = direction;
2771 mTopGridAnnotationDirection = direction;
2772 mBottomGridAnnotationDirection = direction;
2773}
2774
2776{
2777 switch ( border )
2778 {
2780 mLeftGridAnnotationPosition = position;
2781 break;
2783 mRightGridAnnotationPosition = position;
2784 break;
2786 mTopGridAnnotationPosition = position;
2787 break;
2789 mBottomGridAnnotationPosition = position;
2790 break;
2791 }
2792
2793 if ( mMap )
2794 {
2795 mMap->updateBoundingRect();
2796 mMap->update();
2797 }
2798}
2799
2801{
2802 switch ( border )
2803 {
2805 return mLeftGridAnnotationPosition;
2807 return mRightGridAnnotationPosition;
2809 return mTopGridAnnotationPosition;
2811 return mBottomGridAnnotationPosition;
2812 }
2813 return mLeftGridAnnotationPosition; // no warnings
2814}
2815
2817{
2818 mAnnotationFrameDistance = distance;
2819 refreshDataDefinedProperties();
2820}
2821
2823{
2824 if ( !mMap )
2825 {
2826 return mLeftGridAnnotationDirection;
2827 }
2828
2829 switch ( border )
2830 {
2832 return mLeftGridAnnotationDirection;
2834 return mRightGridAnnotationDirection;
2836 return mTopGridAnnotationDirection;
2838 return mBottomGridAnnotationDirection;
2839 }
2840 return mLeftGridAnnotationDirection; // no warnings
2841}
2842
2843void QgsLayoutItemMapGrid::setAnnotationExpression( const QString &expression )
2844{
2845 mGridAnnotationExpressionString = expression;
2846 mGridAnnotationExpression.reset();
2847}
2848
2850{
2851 switch ( border )
2852 {
2854 mLeftFrameDivisions = divisions;
2855 break;
2857 mRightFrameDivisions = divisions;
2858 break;
2860 mTopFrameDivisions = divisions;
2861 break;
2863 mBottomFrameDivisions = divisions;
2864 break;
2865 }
2866
2867 refreshDataDefinedProperties();
2868
2869 if ( mMap )
2870 {
2871 mMap->update();
2872 }
2873}
2874
2876{
2877 switch ( border )
2878 {
2880 return mLeftFrameDivisions;
2882 return mRightFrameDivisions;
2884 return mTopFrameDivisions;
2886 return mBottomFrameDivisions;
2887 }
2888 return mLeftFrameDivisions; // no warnings
2889}
2890
2891int QgsLayoutItemMapGrid::crsGridParams( QgsRectangle &crsRect, QgsCoordinateTransform &inverseTransform ) const
2892{
2893 if ( !mMap )
2894 {
2895 return 1;
2896 }
2897
2898 try
2899 {
2900 const QgsCoordinateTransform tr( mMap->crs(), mCRS, mLayout->project() );
2901 QgsCoordinateTransform extentTransform = tr;
2902 extentTransform.setBallparkTransformsAreAppropriate( true );
2903 const QPolygonF mapPolygon = mMap->transformedMapPolygon();
2904 const QRectF mbr = mapPolygon.boundingRect();
2905 const QgsRectangle mapBoundingRect( mbr.left(), mbr.bottom(), mbr.right(), mbr.top() );
2906
2907
2908 if ( mCRS.isGeographic() )
2909 {
2910 //handle crossing the 180 degree longitude line
2911 QgsPointXY lowerLeft( mapBoundingRect.xMinimum(), mapBoundingRect.yMinimum() );
2912 QgsPointXY upperRight( mapBoundingRect.xMaximum(), mapBoundingRect.yMaximum() );
2913
2914 lowerLeft = tr.transform( lowerLeft.x(), lowerLeft.y() );
2915 upperRight = tr.transform( upperRight.x(), upperRight.y() );
2916
2917 if ( lowerLeft.x() > upperRight.x() )
2918 {
2919 //we've crossed the line
2920 crsRect = extentTransform.transformBoundingBox( mapBoundingRect, Qgis::TransformDirection::Forward, true );
2921 }
2922 else
2923 {
2924 //didn't cross the line
2925 crsRect = extentTransform.transformBoundingBox( mapBoundingRect );
2926 }
2927 }
2928 else
2929 {
2930 crsRect = extentTransform.transformBoundingBox( mapBoundingRect );
2931 }
2932
2933 inverseTransform = QgsCoordinateTransform( mCRS, mMap->crs(), mLayout->project() );
2934 }
2935 catch ( QgsCsException &cse )
2936 {
2937 Q_UNUSED( cse )
2938 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
2939 return 1;
2940 }
2941 return 0;
2942}
2943
2944QList<QPolygonF> QgsLayoutItemMapGrid::trimLinesToMap( const QPolygonF &line, const QgsRectangle &rect )
2945{
2946 const QgsGeometry lineGeom = QgsGeometry::fromQPolygonF( line );
2947 const QgsGeometry rectGeom = QgsGeometry::fromRect( rect );
2948
2949 const QgsGeometry intersected = lineGeom.intersection( rectGeom );
2950 const QVector<QgsGeometry> intersectedParts = intersected.asGeometryCollection();
2951
2952 QList<QPolygonF> trimmedLines;
2953 QVector<QgsGeometry>::const_iterator geomIt = intersectedParts.constBegin();
2954 for ( ; geomIt != intersectedParts.constEnd(); ++geomIt )
2955 {
2956 trimmedLines << ( *geomIt ).asQPolygonF();
2957 }
2958 return trimmedLines;
2959}
2960
2962{
2963 // grid
2964 setStyle( other->style() );
2965 setIntervalX( other->intervalX() );
2966 setIntervalY( other->intervalY() );
2967 setOffsetX( other->offsetX() );
2968 setOffsetY( other->offsetX() );
2969 setCrossLength( other->crossLength() );
2970 setFrameStyle( other->frameStyle() );
2972 setFrameWidth( other->frameWidth() );
2973 setFrameMargin( other->frameMargin() );
2974 setFramePenSize( other->framePenSize() );
2975 setFramePenColor( other->framePenColor() );
2978
2983
2992
2993 if ( other->lineSymbol() )
2994 {
2995 setLineSymbol( other->lineSymbol()->clone() );
2996 }
2997
2998 if ( other->markerSymbol() )
2999 {
3000 setMarkerSymbol( other->markerSymbol()->clone() );
3001 }
3002
3003 setCrs( other->crs() );
3004
3005 setBlendMode( other->blendMode() );
3006
3007 //annotation
3011
3026
3028 setUnits( other->units() );
3031
3033 refreshDataDefinedProperties();
3034}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:59
MapGridTickLengthMode
Map grid tick length mode (useful for rotated grids).
Definition qgis.h:5607
@ OrthogonalTicks
Align ticks orthogonaly.
Definition qgis.h:5608
QFlags< MapGridFrameSideFlag > MapGridFrameSideFlags
Flags for controlling which side of the map a frame is drawn on.
Definition qgis.h:5637
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
Definition qgis.h:2764
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
Definition qgis.h:2765
@ Millimeters
Millimeters.
Definition qgis.h:5313
MapGridAnnotationPosition
Position for map grid annotations.
Definition qgis.h:5517
@ InsideMapFrame
Draw annotations inside the map frame.
Definition qgis.h:5518
@ OutsideMapFrame
Draw annotations outside the map frame.
Definition qgis.h:5519
MapGridUnit
Units for map grid values.
Definition qgis.h:5469
@ Centimeters
Grid units in centimeters.
Definition qgis.h:5472
@ Millimeters
Grid units in millimeters.
Definition qgis.h:5471
@ DynamicPageSizeBased
Dynamically sized, based on a on-page size range.
Definition qgis.h:5473
@ MapUnits
Grid units follow map units.
Definition qgis.h:5470
DistanceUnit
Units of distance.
Definition qgis.h:5122
@ Unknown
Unknown distance unit.
Definition qgis.h:5172
MapGridBorderSide
Border sides for map grid annotations.
Definition qgis.h:5571
@ Bottom
Bottom border.
Definition qgis.h:5574
@ Right
Right border.
Definition qgis.h:5573
@ Left
Left border.
Definition qgis.h:5572
@ Top
Top border.
Definition qgis.h:5575
MapGridFrameSideFlag
Flags for controlling which side of the map a frame is drawn on.
Definition qgis.h:5622
@ Bottom
Bottom side of map.
Definition qgis.h:5626
@ Right
Right side of map.
Definition qgis.h:5624
@ Left
Left side of map.
Definition qgis.h:5623
@ Top
Top side of map.
Definition qgis.h:5625
@ Point
Text at point of origin layout mode.
Definition qgis.h:2963
MapGridComponentVisibility
Visibility display settings for map grid annotations and frames.
Definition qgis.h:5501
@ ShowAll
Show both latitude and longitude annotations/divisions.
Definition qgis.h:5502
@ LongitudeOnly
Show longitude/x annotations/divisions only.
Definition qgis.h:5504
@ LatitudeOnly
Show latitude/y annotations/divisions only.
Definition qgis.h:5503
@ HideAll
No annotations.
Definition qgis.h:5505
@ Horizontal
Horizontally oriented text.
Definition qgis.h:2947
MapGridStyle
Map grid drawing styles.
Definition qgis.h:5485
@ LineCrosses
Draw line crosses at intersections of grid lines.
Definition qgis.h:5487
@ Markers
Draw markers at intersections of grid lines.
Definition qgis.h:5488
@ Lines
Draw lines for grid.
Definition qgis.h:5486
@ FrameAndAnnotationsOnly
No grid lines over the map, only draw frame and annotations.
Definition qgis.h:5489
@ Millimeters
Millimeters.
Definition qgis.h:5293
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5297
@ Pixels
Pixels.
Definition qgis.h:5295
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
Definition qgis.h:2822
MapGridAnnotationType
Annotation coordinate type.
Definition qgis.h:5648
@ Latitude
Coordinate is a latitude value.
Definition qgis.h:5650
@ Longitude
Coordinate is a longitude value.
Definition qgis.h:5649
MapGridFrameStyle
Style for map grid frames.
Definition qgis.h:5587
@ LineBorderNautical
Simple solid line frame, with nautical style diagonals on corners.
Definition qgis.h:5594
@ ZebraNautical
Black/white pattern, with nautical style diagonals on corners.
Definition qgis.h:5595
@ InteriorExteriorTicks
Tick markers drawn both inside and outside the map frame.
Definition qgis.h:5592
@ NoFrame
Disable grid frame.
Definition qgis.h:5588
@ ExteriorTicks
Tick markers drawn outside map frame.
Definition qgis.h:5591
@ Zebra
Black/white pattern.
Definition qgis.h:5589
@ LineBorder
Simple solid line frame.
Definition qgis.h:5593
@ InteriorTicks
Tick markers drawn inside map frame.
Definition qgis.h:5590
MapGridAnnotationDirection
Direction of grid annotations.
Definition qgis.h:5531
@ Vertical
Draw annotations vertically, ascending.
Definition qgis.h:5533
@ UnderTick
Draw annotations parallel to tick (under the line).
Definition qgis.h:5538
@ VerticalDescending
Draw annotations vertically, descending.
Definition qgis.h:5534
@ BoundaryDirection
Annotations follow the boundary direction.
Definition qgis.h:5535
@ AboveTick
Draw annotations parallel to tick (above the line).
Definition qgis.h:5536
@ OnTick
Draw annotations parallel to tick (on the line).
Definition qgis.h:5537
@ Horizontal
Draw annotations horizontally.
Definition qgis.h:5532
TextHorizontalAlignment
Text horizontal alignment.
Definition qgis.h:3002
@ Justify
Justify align.
Definition qgis.h:3006
@ Center
Center align.
Definition qgis.h:3004
MapGridAnnotationFormat
Format for displaying map grid annotations.
Definition qgis.h:5550
@ DegreeMinute
Degree/minutes, use NSEW suffix.
Definition qgis.h:5552
@ DegreeMinuteSecond
Degree/minutes/seconds, use NSEW suffix.
Definition qgis.h:5553
@ DegreeMinuteNoSuffix
Degree/minutes, use - for S/W coordinates.
Definition qgis.h:5555
@ CustomFormat
Custom expression-based format.
Definition qgis.h:5559
@ Decimal
Decimal degrees, use - for S/W coordinates.
Definition qgis.h:5551
@ DegreeMinuteSecondNoSuffix
Degree/minutes/seconds, use - for S/W coordinates.
Definition qgis.h:5557
@ DegreeMinutePadded
Degree/minutes, with minutes using leading zeros where required.
Definition qgis.h:5556
@ DegreeMinuteSecondPadded
Degree/minutes/seconds, with minutes using leading zeros where required.
Definition qgis.h:5558
@ DecimalWithSuffix
Decimal degrees, use NSEW suffix.
Definition qgis.h:5554
@ Forward
Forward transform (from source to destination).
Definition qgis.h:2732
@ Antialiasing
Use antialiasing when drawing items.
Definition qgis.h:5349
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(Qgis::MapGridFrameSideFlags flags)
Sets flags for grid frame sides.
void setRotatedTicksLengthMode(const Qgis::MapGridTickLengthMode mode)
Sets the tick length calculation mode.
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.
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.
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.
void setAnnotationDisplay(Qgis::MapGridComponentVisibility display, Qgis::MapGridBorderSide border)
Sets what types of grid annotations should be drawn for a specified side of the map frame,...
void setFrameStyle(const Qgis::MapGridFrameStyle style)
Sets the grid frame style.
void setUnits(Qgis::MapGridUnit unit)
Sets the unit to use for grid measurements such as the interval and offset for grid lines.
Q_DECL_DEPRECATED void setAnnotationFontColor(const QColor &color)
Sets the font color used 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.
void setStyle(Qgis::MapGridStyle style)
Sets the grid style, which controls how the grid is drawn over the map's contents.
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 setRotatedTicksEnabled(const bool state)
Enable/disable ticks rotation for rotated or reprojected grids.
Qgis::MapGridUnit units() const
Returns the units used for grid measurements such as the interval and offset for grid lines.
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...
void setFrameDivisions(Qgis::MapGridComponentVisibility divisions, Qgis::MapGridBorderSide side)
Sets what type of grid divisions should be used for frames on a specified side of the map.
Qgis::MapGridStyle style() const
Returns the grid's style, which controls how the grid is drawn over the map's contents.
double rotatedTicksMinimumAngle() const
Gets the minimum angle (in degrees) below which ticks are not drawn.
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...
Qgis::MapGridTickLengthMode rotatedAnnotationsLengthMode() const
Returns the annotation length calculation mode.
Qgis::MapGridFrameStyle frameStyle() const
Returns the grid frame style.
void setMinimumIntervalWidth(double width)
Sets the minimum width (in millimeters) for grid segments.
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 setAnnotationDirection(Qgis::MapGridAnnotationDirection direction, Qgis::MapGridBorderSide side)
Sets the direction for drawing frame annotations for the specified map side.
void setEnabled(bool enabled) override
Controls whether the item will be drawn.
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.
Qgis::MapGridTickLengthMode rotatedTicksLengthMode() const
Returns the grid frame style.
~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.
Qgis::MapGridComponentVisibility frameDivisions(Qgis::MapGridBorderSide side) const
Returns the type of grid divisions which are used for frames on a specified side of the map.
Qgis::MapGridFrameSideFlags frameSideFlags() const
Returns the flags which control which sides of the map item the grid frame is drawn on.
void setAnnotationPosition(Qgis::MapGridAnnotationPosition position, Qgis::MapGridBorderSide side)
Sets the position for the grid annotations on a specified side of the map frame.
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.
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 setRotatedAnnotationsLengthMode(const Qgis::MapGridTickLengthMode mode)
Sets the annotation length calculation mode.
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.
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.
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.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs for the grid.
bool testFrameSideFlag(Qgis::MapGridFrameSideFlag flag) const
Tests whether the grid frame should be drawn on a specified side of the map item.
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.
void setAnnotationFormat(const Qgis::MapGridAnnotationFormat format)
Sets the format for drawing grid annotations.
void setFrameWidth(const double width)
Sets the grid frame width (in layout units).
Qgis::MapGridComponentVisibility annotationDisplay(Qgis::MapGridBorderSide border) const
Returns the display mode for the grid annotations on a specified side of the map frame.
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).
void setOffsetX(double offset)
Sets the offset for grid lines in the x-direction.
Qgis::MapGridAnnotationFormat annotationFormat() const
Returns the format for drawing grid annotations.
void setFrameSideFlag(Qgis::MapGridFrameSideFlag flag, bool on=true)
Sets whether the grid frame is drawn for a certain side of the map item.
Qgis::MapGridAnnotationDirection annotationDirection(Qgis::MapGridBorderSide border) const
Returns the direction for drawing frame annotations, on the specified side of the map.
Qgis::MapGridAnnotationPosition annotationPosition(Qgis::MapGridBorderSide side) const
Returns the position for the grid annotations on a specified side of the map frame.
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...
double maximumIntervalWidth() const
Returns the maximum width (in millimeters) for grid segments.
Qgis::TextHorizontalAlignment horizontalAlignment() const
Returns the horizontal alignment for annotation text.
void setHorizontalAlignment(Qgis::TextHorizontalAlignment alignment)
Sets the horizontal alignment for annotation text.
void setAnnotationExpression(const QString &expression)
Sets the expression used for drawing grid annotations.
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:66
double x
Definition qgspointxy.h:65
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:244
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:68
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:1398
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.
QSizeF documentSize(Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation) const
Returns the overall size of the document.
static QgsTextDocumentMetrics calculateMetrics(const QgsTextDocument &document, const QgsTextFormat &format, const QgsRenderContext &context, double scaleFactor=1.0, const QgsTextDocumentRenderContext &documentContext=QgsTextDocumentRenderContext())
Returns precalculated text metrics for a text document, when rendered using the given base format and...
int size() const
Returns the number of blocks in the document.
static QgsTextDocument fromTextAndFormat(const QStringList &lines, const QgsTextFormat &format)
Constructor for QgsTextDocument consisting of a set of lines, respecting settings from a text format.
static void drawDocument(const QRectF &rect, const QgsTextFormat &format, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, QgsRenderContext &context, Qgis::TextHorizontalAlignment horizontalAlignment=Qgis::TextHorizontalAlignment::Left, Qgis::TextVerticalAlignment verticalAlignment=Qgis::TextVerticalAlignment::Top, double rotation=0, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags())
Draws a text document within a rectangle using the specified settings.
static double calculateScaleFactorForFormat(const QgsRenderContext &context, const QgsTextFormat &format)
Returns the scale factor used for upscaling font sizes and downscaling destination painter devices.
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
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:7147
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:6854
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7128
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6978
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6937
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:63
#define MAX_GRID_LINES
QVector2D borderToVector2D(Qgis::MapGridBorderSide border)
bool sortByDistance(QPair< qreal, Qgis::MapGridBorderSide > a, QPair< qreal, Qgis::MapGridBorderSide > b)
Qgis::MapGridComponentVisibility gridAnnotationDisplayModeFromDD(QString ddValue, Qgis::MapGridComponentVisibility defValue)
QVector2D borderToNormal2D(Qgis::MapGridBorderSide border)
#define QgsDebugError(str)
Definition qgslogger.h:59
Single variable definition for use within a QgsExpressionContextScope.
Contains information relating to the style entity currently being visited.