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