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