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