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