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