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