QGIS API Documentation  2.99.0-Master (f867b65)
qgscomposermapgrid.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposermapgrid.cpp
3  ----------------------
4  begin : December 2013
5  copyright : (C) 2013 by Marco Hugentobler
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 
18 #include "qgscomposermapgrid.h"
19 #include "qgscomposerutils.h"
20 #include "qgsclipper.h"
21 #include "qgsgeometry.h"
22 #include "qgscomposermap.h"
23 #include "qgscomposition.h"
24 #include "qgsmapsettings.h"
25 #include "qgspathresolver.h"
26 #include "qgsreadwritecontext.h"
27 #include "qgsrendercontext.h"
28 #include "qgssymbollayerutils.h"
29 #include "qgssymbol.h"
31 #include "qgslogger.h"
32 #include "qgsfontutils.h"
33 #include "qgsexpressioncontext.h"
34 #include "qgsexception.h"
35 #include "qgssettings.h"
36 
37 #include <QPainter>
38 #include <QPen>
39 
40 #define MAX_GRID_LINES 1000 //maximum number of horizontal or vertical grid lines to draw
41 
44 {
45 
46 }
47 
49 {
51 }
52 
53 void QgsComposerMapGridStack::removeGrid( const QString &gridId )
54 {
56 }
57 
58 void QgsComposerMapGridStack::moveGridUp( const QString &gridId )
59 {
61 }
62 
63 void QgsComposerMapGridStack::moveGridDown( const QString &gridId )
64 {
66 }
67 
68 const QgsComposerMapGrid *QgsComposerMapGridStack::constGrid( const QString &gridId ) const
69 {
71  return dynamic_cast<const QgsComposerMapGrid *>( item );
72 }
73 
74 QgsComposerMapGrid *QgsComposerMapGridStack::grid( const QString &gridId ) const
75 {
77  return dynamic_cast<QgsComposerMapGrid *>( item );
78 }
79 
81 {
83  return dynamic_cast<QgsComposerMapGrid *>( item );
84 }
85 
86 QList<QgsComposerMapGrid *> QgsComposerMapGridStack::asList() const
87 {
88  QList< QgsComposerMapGrid * > list;
89  QList< QgsComposerMapItem * >::const_iterator it = mItems.begin();
90  for ( ; it != mItems.end(); ++it )
91  {
92  QgsComposerMapGrid *grid = dynamic_cast<QgsComposerMapGrid *>( *it );
93  if ( grid )
94  {
95  list.append( grid );
96  }
97  }
98  return list;
99 }
100 
102 {
103  QgsComposerMapItem *item = mItems.at( idx );
104  QgsComposerMapGrid *grid = dynamic_cast<QgsComposerMapGrid *>( item );
105  return *grid;
106 }
107 
108 bool QgsComposerMapGridStack::readXml( const QDomElement &elem, const QDomDocument &doc )
109 {
110  removeItems();
111 
112  //read grid stack
113  QDomNodeList mapGridNodeList = elem.elementsByTagName( QStringLiteral( "ComposerMapGrid" ) );
114  for ( int i = 0; i < mapGridNodeList.size(); ++i )
115  {
116  QDomElement mapGridElem = mapGridNodeList.at( i ).toElement();
117  QgsComposerMapGrid *mapGrid = new QgsComposerMapGrid( mapGridElem.attribute( QStringLiteral( "name" ) ), mComposerMap );
118  mapGrid->readXml( mapGridElem, doc );
119  mItems.append( mapGrid );
120  }
121 
122  return true;
123 }
124 
126 {
127  double top = 0.0;
128  double right = 0.0;
129  double bottom = 0.0;
130  double left = 0.0;
131  calculateMaxGridExtension( top, right, bottom, left );
132  return qMax( qMax( qMax( top, right ), bottom ), left );
133 }
134 
135 void QgsComposerMapGridStack::calculateMaxGridExtension( double &top, double &right, double &bottom, double &left ) const
136 {
137  top = 0.0;
138  right = 0.0;
139  bottom = 0.0;
140  left = 0.0;
141 
142  Q_FOREACH ( QgsComposerMapItem *item, mItems )
143  {
144  QgsComposerMapGrid *grid = dynamic_cast<QgsComposerMapGrid *>( item );
145  if ( grid )
146  {
147  double gridTop = 0.0;
148  double gridRight = 0.0;
149  double gridBottom = 0.0;
150  double gridLeft = 0.0;
151  grid->calculateMaxExtension( gridTop, gridRight, gridBottom, gridLeft );
152  top = qMax( top, gridTop );
153  right = qMax( right, gridRight );
154  bottom = qMax( bottom, gridBottom );
155  left = qMax( left, gridLeft );
156  }
157  }
158 }
159 
160 
161 //
162 // QgsComposerMapGrid
163 //
164 
165 
167  : QgsComposerMapItem( name, map )
168 {
169  init();
170 }
171 
173  : QgsComposerMapItem( QString(), nullptr )
174 {
175  init();
176 }
177 
178 void QgsComposerMapGrid::init()
179 {
180  mTransformDirty = true;
181  mGridStyle = QgsComposerMapGrid::Solid;
182  mGridIntervalX = 0.0;
183  mGridIntervalY = 0.0;
184  mGridOffsetX = 0.0;
185  mGridOffsetY = 0.0;
186  mGridAnnotationFontColor = Qt::black;
187  mGridAnnotationPrecision = 3;
188  mShowGridAnnotation = false;
189  mLeftGridAnnotationDisplay = QgsComposerMapGrid::ShowAll;
190  mRightGridAnnotationDisplay = QgsComposerMapGrid::ShowAll;
191  mTopGridAnnotationDisplay = QgsComposerMapGrid::ShowAll;
192  mBottomGridAnnotationDisplay = QgsComposerMapGrid::ShowAll;
193  mLeftGridAnnotationPosition = QgsComposerMapGrid::OutsideMapFrame;
194  mRightGridAnnotationPosition = QgsComposerMapGrid::OutsideMapFrame;
195  mTopGridAnnotationPosition = QgsComposerMapGrid::OutsideMapFrame;
196  mBottomGridAnnotationPosition = QgsComposerMapGrid::OutsideMapFrame;
197  mAnnotationFrameDistance = 1.0;
198  mLeftGridAnnotationDirection = QgsComposerMapGrid::Horizontal;
199  mRightGridAnnotationDirection = QgsComposerMapGrid::Horizontal;
200  mTopGridAnnotationDirection = QgsComposerMapGrid::Horizontal;
201  mBottomGridAnnotationDirection = QgsComposerMapGrid::Horizontal;
202  mGridAnnotationFormat = QgsComposerMapGrid::Decimal;
203  mGridFrameStyle = QgsComposerMapGrid::NoFrame;
206  mGridFrameWidth = 2.0;
207  mGridFramePenThickness = 0.3;
208  mGridFramePenColor = QColor( 0, 0, 0 );
209  mGridFrameFillColor1 = Qt::white;
210  mGridFrameFillColor2 = Qt::black;
211  mCrossLength = 3;
212  mLeftFrameDivisions = QgsComposerMapGrid::ShowAll;
213  mRightFrameDivisions = QgsComposerMapGrid::ShowAll;
214  mTopFrameDivisions = QgsComposerMapGrid::ShowAll;
215  mBottomFrameDivisions = QgsComposerMapGrid::ShowAll;
216  mGridLineSymbol = nullptr;
217  mGridMarkerSymbol = nullptr;
218  mGridUnit = MapUnit;
219  mBlendMode = QPainter::CompositionMode_SourceOver;
220 
221  //get default composer font from settings
222  QgsSettings settings;
223  QString defaultFontString = settings.value( QStringLiteral( "Composer/defaultFont" ) ).toString();
224  if ( !defaultFontString.isEmpty() )
225  {
226  mGridAnnotationFont.setFamily( defaultFontString );
227  }
228 
229  createDefaultGridLineSymbol();
230  createDefaultGridMarkerSymbol();
231 }
232 
234 {
235  delete mGridLineSymbol;
236  delete mGridMarkerSymbol;
237 }
238 
239 void QgsComposerMapGrid::createDefaultGridLineSymbol()
240 {
241  delete mGridLineSymbol;
242  QgsStringMap properties;
243  properties.insert( QStringLiteral( "color" ), QStringLiteral( "0,0,0,255" ) );
244  properties.insert( QStringLiteral( "width" ), QStringLiteral( "0.3" ) );
245  properties.insert( QStringLiteral( "capstyle" ), QStringLiteral( "flat" ) );
246  mGridLineSymbol = QgsLineSymbol::createSimple( properties );
247 }
248 
249 void QgsComposerMapGrid::createDefaultGridMarkerSymbol()
250 {
251  delete mGridMarkerSymbol;
252  QgsStringMap properties;
253  properties.insert( QStringLiteral( "name" ), QStringLiteral( "circle" ) );
254  properties.insert( QStringLiteral( "size" ), QStringLiteral( "2.0" ) );
255  properties.insert( QStringLiteral( "color" ), QStringLiteral( "0,0,0,255" ) );
256  mGridMarkerSymbol = QgsMarkerSymbol::createSimple( properties );
257 }
258 
259 void QgsComposerMapGrid::setGridLineWidth( const double width )
260 {
261  if ( mGridLineSymbol )
262  {
263  mGridLineSymbol->setWidth( width );
264  }
265 }
266 
268 {
269  if ( mGridLineSymbol )
270  {
271  mGridLineSymbol->setColor( c );
272  }
273 }
274 
275 bool QgsComposerMapGrid::writeXml( QDomElement &elem, QDomDocument &doc ) const
276 {
277  if ( elem.isNull() )
278  {
279  return false;
280  }
281 
282  QDomElement mapGridElem = doc.createElement( QStringLiteral( "ComposerMapGrid" ) );
283  mapGridElem.setAttribute( QStringLiteral( "gridStyle" ), mGridStyle );
284  mapGridElem.setAttribute( QStringLiteral( "intervalX" ), qgsDoubleToString( mGridIntervalX ) );
285  mapGridElem.setAttribute( QStringLiteral( "intervalY" ), qgsDoubleToString( mGridIntervalY ) );
286  mapGridElem.setAttribute( QStringLiteral( "offsetX" ), qgsDoubleToString( mGridOffsetX ) );
287  mapGridElem.setAttribute( QStringLiteral( "offsetY" ), qgsDoubleToString( mGridOffsetY ) );
288  mapGridElem.setAttribute( QStringLiteral( "crossLength" ), qgsDoubleToString( mCrossLength ) );
289 
290  QgsReadWriteContext context;
292 
293  QDomElement lineStyleElem = doc.createElement( QStringLiteral( "lineStyle" ) );
294  QDomElement gridLineStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mGridLineSymbol, doc, context );
295  lineStyleElem.appendChild( gridLineStyleElem );
296  mapGridElem.appendChild( lineStyleElem );
297 
298  QDomElement markerStyleElem = doc.createElement( QStringLiteral( "markerStyle" ) );
299  QDomElement gridMarkerStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mGridMarkerSymbol, doc, context );
300  markerStyleElem.appendChild( gridMarkerStyleElem );
301  mapGridElem.appendChild( markerStyleElem );
302 
303  mapGridElem.setAttribute( QStringLiteral( "gridFrameStyle" ), mGridFrameStyle );
304  mapGridElem.setAttribute( QStringLiteral( "gridFrameSideFlags" ), mGridFrameSides );
305  mapGridElem.setAttribute( QStringLiteral( "gridFrameWidth" ), qgsDoubleToString( mGridFrameWidth ) );
306  mapGridElem.setAttribute( QStringLiteral( "gridFramePenThickness" ), qgsDoubleToString( mGridFramePenThickness ) );
307  mapGridElem.setAttribute( QStringLiteral( "gridFramePenColor" ), QgsSymbolLayerUtils::encodeColor( mGridFramePenColor ) );
308  mapGridElem.setAttribute( QStringLiteral( "frameFillColor1" ), QgsSymbolLayerUtils::encodeColor( mGridFrameFillColor1 ) );
309  mapGridElem.setAttribute( QStringLiteral( "frameFillColor2" ), QgsSymbolLayerUtils::encodeColor( mGridFrameFillColor2 ) );
310  mapGridElem.setAttribute( QStringLiteral( "leftFrameDivisions" ), mLeftFrameDivisions );
311  mapGridElem.setAttribute( QStringLiteral( "rightFrameDivisions" ), mRightFrameDivisions );
312  mapGridElem.setAttribute( QStringLiteral( "topFrameDivisions" ), mTopFrameDivisions );
313  mapGridElem.setAttribute( QStringLiteral( "bottomFrameDivisions" ), mBottomFrameDivisions );
314  if ( mCRS.isValid() )
315  {
316  mCRS.writeXml( mapGridElem, doc );
317  }
318 
319  mapGridElem.setAttribute( QStringLiteral( "annotationFormat" ), mGridAnnotationFormat );
320  mapGridElem.setAttribute( QStringLiteral( "showAnnotation" ), mShowGridAnnotation );
321  mapGridElem.setAttribute( QStringLiteral( "annotationExpression" ), mGridAnnotationExpressionString );
322  mapGridElem.setAttribute( QStringLiteral( "leftAnnotationDisplay" ), mLeftGridAnnotationDisplay );
323  mapGridElem.setAttribute( QStringLiteral( "rightAnnotationDisplay" ), mRightGridAnnotationDisplay );
324  mapGridElem.setAttribute( QStringLiteral( "topAnnotationDisplay" ), mTopGridAnnotationDisplay );
325  mapGridElem.setAttribute( QStringLiteral( "bottomAnnotationDisplay" ), mBottomGridAnnotationDisplay );
326  mapGridElem.setAttribute( QStringLiteral( "leftAnnotationPosition" ), mLeftGridAnnotationPosition );
327  mapGridElem.setAttribute( QStringLiteral( "rightAnnotationPosition" ), mRightGridAnnotationPosition );
328  mapGridElem.setAttribute( QStringLiteral( "topAnnotationPosition" ), mTopGridAnnotationPosition );
329  mapGridElem.setAttribute( QStringLiteral( "bottomAnnotationPosition" ), mBottomGridAnnotationPosition );
330  mapGridElem.setAttribute( QStringLiteral( "leftAnnotationDirection" ), mLeftGridAnnotationDirection );
331  mapGridElem.setAttribute( QStringLiteral( "rightAnnotationDirection" ), mRightGridAnnotationDirection );
332  mapGridElem.setAttribute( QStringLiteral( "topAnnotationDirection" ), mTopGridAnnotationDirection );
333  mapGridElem.setAttribute( QStringLiteral( "bottomAnnotationDirection" ), mBottomGridAnnotationDirection );
334  mapGridElem.setAttribute( QStringLiteral( "frameAnnotationDistance" ), QString::number( mAnnotationFrameDistance ) );
335  mapGridElem.appendChild( QgsFontUtils::toXmlElement( mGridAnnotationFont, doc, QStringLiteral( "annotationFontProperties" ) ) );
336  mapGridElem.setAttribute( QStringLiteral( "annotationFontColor" ), QgsSymbolLayerUtils::encodeColor( mGridAnnotationFontColor ) );
337  mapGridElem.setAttribute( QStringLiteral( "annotationPrecision" ), mGridAnnotationPrecision );
338  mapGridElem.setAttribute( QStringLiteral( "unit" ), mGridUnit );
339  mapGridElem.setAttribute( QStringLiteral( "blendMode" ), mBlendMode );
340 
341  bool ok = QgsComposerMapItem::writeXml( mapGridElem, doc );
342  elem.appendChild( mapGridElem );
343  return ok;
344 }
345 
346 bool QgsComposerMapGrid::readXml( const QDomElement &itemElem, const QDomDocument &doc )
347 {
348  Q_UNUSED( doc );
349  if ( itemElem.isNull() )
350  {
351  return false;
352  }
353 
354  bool ok = QgsComposerMapItem::readXml( itemElem, doc );
355 
356  //grid
357  mGridStyle = QgsComposerMapGrid::GridStyle( itemElem.attribute( QStringLiteral( "gridStyle" ), QStringLiteral( "0" ) ).toInt() );
358  mGridIntervalX = itemElem.attribute( QStringLiteral( "intervalX" ), QStringLiteral( "0" ) ).toDouble();
359  mGridIntervalY = itemElem.attribute( QStringLiteral( "intervalY" ), QStringLiteral( "0" ) ).toDouble();
360  mGridOffsetX = itemElem.attribute( QStringLiteral( "offsetX" ), QStringLiteral( "0" ) ).toDouble();
361  mGridOffsetY = itemElem.attribute( QStringLiteral( "offsetY" ), QStringLiteral( "0" ) ).toDouble();
362  mCrossLength = itemElem.attribute( QStringLiteral( "crossLength" ), QStringLiteral( "3" ) ).toDouble();
363  mGridFrameStyle = static_cast< QgsComposerMapGrid::FrameStyle >( itemElem.attribute( QStringLiteral( "gridFrameStyle" ), QStringLiteral( "0" ) ).toInt() );
364  mGridFrameSides = static_cast< QgsComposerMapGrid::FrameSideFlags >( itemElem.attribute( QStringLiteral( "gridFrameSideFlags" ), QStringLiteral( "15" ) ).toInt() );
365  mGridFrameWidth = itemElem.attribute( QStringLiteral( "gridFrameWidth" ), QStringLiteral( "2.0" ) ).toDouble();
366  mGridFramePenThickness = itemElem.attribute( QStringLiteral( "gridFramePenThickness" ), QStringLiteral( "0.3" ) ).toDouble();
367  mGridFramePenColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "gridFramePenColor" ), QStringLiteral( "0,0,0" ) ) );
368  mGridFrameFillColor1 = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "frameFillColor1" ), QStringLiteral( "255,255,255,255" ) ) );
369  mGridFrameFillColor2 = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "frameFillColor2" ), QStringLiteral( "0,0,0,255" ) ) );
370  mLeftFrameDivisions = QgsComposerMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "leftFrameDivisions" ), QStringLiteral( "0" ) ).toInt() );
371  mRightFrameDivisions = QgsComposerMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "rightFrameDivisions" ), QStringLiteral( "0" ) ).toInt() );
372  mTopFrameDivisions = QgsComposerMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "topFrameDivisions" ), QStringLiteral( "0" ) ).toInt() );
373  mBottomFrameDivisions = QgsComposerMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "bottomFrameDivisions" ), QStringLiteral( "0" ) ).toInt() );
374 
375  QgsReadWriteContext context;
377 
378  QDomElement lineStyleElem = itemElem.firstChildElement( QStringLiteral( "lineStyle" ) );
379  if ( !lineStyleElem.isNull() )
380  {
381  QDomElement symbolElem = lineStyleElem.firstChildElement( QStringLiteral( "symbol" ) );
382  if ( !symbolElem.isNull() )
383  {
384  delete mGridLineSymbol;
385  mGridLineSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( symbolElem, context );
386  }
387  }
388  else
389  {
390  //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
391  mGridLineSymbol = QgsLineSymbol::createSimple( QgsStringMap() );
392  mGridLineSymbol->setWidth( itemElem.attribute( QStringLiteral( "penWidth" ), QStringLiteral( "0" ) ).toDouble() );
393  mGridLineSymbol->setColor( QColor( itemElem.attribute( QStringLiteral( "penColorRed" ), QStringLiteral( "0" ) ).toInt(),
394  itemElem.attribute( QStringLiteral( "penColorGreen" ), QStringLiteral( "0" ) ).toInt(),
395  itemElem.attribute( QStringLiteral( "penColorBlue" ), QStringLiteral( "0" ) ).toInt() ) );
396  }
397 
398  QDomElement markerStyleElem = itemElem.firstChildElement( QStringLiteral( "markerStyle" ) );
399  if ( !markerStyleElem.isNull() )
400  {
401  QDomElement symbolElem = markerStyleElem.firstChildElement( QStringLiteral( "symbol" ) );
402  if ( !symbolElem.isNull() )
403  {
404  delete mGridMarkerSymbol;
405  mGridMarkerSymbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElem, context );
406  }
407  }
408 
409  if ( !mCRS.readXml( itemElem ) )
411 
412  mBlendMode = static_cast< QPainter::CompositionMode >( itemElem.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() );
413 
414  //annotation
415  mShowGridAnnotation = ( itemElem.attribute( QStringLiteral( "showAnnotation" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
416  mGridAnnotationFormat = QgsComposerMapGrid::AnnotationFormat( itemElem.attribute( QStringLiteral( "annotationFormat" ), QStringLiteral( "0" ) ).toInt() );
417  mGridAnnotationExpressionString = itemElem.attribute( QStringLiteral( "annotationExpression" ) );
418  mGridAnnotationExpression.reset();
419  mLeftGridAnnotationPosition = QgsComposerMapGrid::AnnotationPosition( itemElem.attribute( QStringLiteral( "leftAnnotationPosition" ), QStringLiteral( "0" ) ).toInt() );
420  mRightGridAnnotationPosition = QgsComposerMapGrid::AnnotationPosition( itemElem.attribute( QStringLiteral( "rightAnnotationPosition" ), QStringLiteral( "0" ) ).toInt() );
421  mTopGridAnnotationPosition = QgsComposerMapGrid::AnnotationPosition( itemElem.attribute( QStringLiteral( "topAnnotationPosition" ), QStringLiteral( "0" ) ).toInt() );
422  mBottomGridAnnotationPosition = QgsComposerMapGrid::AnnotationPosition( itemElem.attribute( QStringLiteral( "bottomAnnotationPosition" ), QStringLiteral( "0" ) ).toInt() );
423  mLeftGridAnnotationDisplay = QgsComposerMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "leftAnnotationDisplay" ), QStringLiteral( "0" ) ).toInt() );
424  mRightGridAnnotationDisplay = QgsComposerMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "rightAnnotationDisplay" ), QStringLiteral( "0" ) ).toInt() );
425  mTopGridAnnotationDisplay = QgsComposerMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "topAnnotationDisplay" ), QStringLiteral( "0" ) ).toInt() );
426  mBottomGridAnnotationDisplay = QgsComposerMapGrid::DisplayMode( itemElem.attribute( QStringLiteral( "bottomAnnotationDisplay" ), QStringLiteral( "0" ) ).toInt() );
427 
428  mLeftGridAnnotationDirection = QgsComposerMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "leftAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
429  mRightGridAnnotationDirection = QgsComposerMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "rightAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
430  mTopGridAnnotationDirection = QgsComposerMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "topAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
431  mBottomGridAnnotationDirection = QgsComposerMapGrid::AnnotationDirection( itemElem.attribute( QStringLiteral( "bottomAnnotationDirection" ), QStringLiteral( "0" ) ).toInt() );
432  mAnnotationFrameDistance = itemElem.attribute( QStringLiteral( "frameAnnotationDistance" ), QStringLiteral( "0" ) ).toDouble();
433  if ( !QgsFontUtils::setFromXmlChildNode( mGridAnnotationFont, itemElem, QStringLiteral( "annotationFontProperties" ) ) )
434  {
435  mGridAnnotationFont.fromString( itemElem.attribute( QStringLiteral( "annotationFont" ), QLatin1String( "" ) ) );
436  }
437  mGridAnnotationFontColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "annotationFontColor" ), QStringLiteral( "0,0,0,255" ) ) );
438  mGridAnnotationPrecision = itemElem.attribute( QStringLiteral( "annotationPrecision" ), QStringLiteral( "3" ) ).toInt();
439  int gridUnitInt = itemElem.attribute( QStringLiteral( "unit" ), QString::number( MapUnit ) ).toInt();
440  mGridUnit = ( gridUnitInt <= static_cast< int >( CM ) ) ? static_cast< GridUnit >( gridUnitInt ) : MapUnit;
441  return ok;
442 }
443 
445 {
446  mCRS = crs;
447  mTransformDirty = true;
448 }
449 
451 {
452  return mBlendMode == QPainter::CompositionMode_SourceOver;
453 }
454 
455 QPolygonF QgsComposerMapGrid::scalePolygon( const QPolygonF &polygon, const double scale ) const
456 {
457  QTransform t = QTransform::fromScale( scale, scale );
458  return t.map( polygon );
459 }
460 
461 void QgsComposerMapGrid::drawGridCrsTransform( QgsRenderContext &context, double dotsPerMM, QList< QPair< double, QLineF > > &horizontalLines,
462  QList< QPair< double, QLineF > > &verticalLines, bool calculateLinesOnly )
463 {
464  if ( !mComposerMap || !mEnabled )
465  {
466  return;
467  }
468 
469  //has map extent/scale changed?
470  QPolygonF mapPolygon = mComposerMap->transformedMapPolygon();
471  if ( mapPolygon != mPrevMapPolygon )
472  {
473  mTransformDirty = true;
474  mPrevMapPolygon = mapPolygon;
475  }
476 
477  if ( mTransformDirty )
478  {
479  calculateCrsTransformLines();
480  }
481 
482  //draw lines
483  if ( !calculateLinesOnly )
484  {
485  if ( mGridStyle == QgsComposerMapGrid::Solid )
486  {
487  QList< QPair< double, QPolygonF > >::const_iterator xGridIt = mTransformedXLines.constBegin();
488  for ( ; xGridIt != mTransformedXLines.constEnd(); ++xGridIt )
489  {
490  drawGridLine( scalePolygon( xGridIt->second, dotsPerMM ), context );
491  }
492 
493  QList< QPair< double, QPolygonF > >::const_iterator yGridIt = mTransformedYLines.constBegin();
494  for ( ; yGridIt != mTransformedYLines.constEnd(); ++yGridIt )
495  {
496  drawGridLine( scalePolygon( yGridIt->second, dotsPerMM ), context );
497  }
498  }
499  else if ( mGridStyle == QgsComposerMapGrid::Cross || mGridStyle == QgsComposerMapGrid::Markers )
500  {
501  double maxX = mComposerMap->rect().width();
502  double maxY = mComposerMap->rect().height();
503 
504  QList< QgsPointXY >::const_iterator intersectionIt = mTransformedIntersections.constBegin();
505  for ( ; intersectionIt != mTransformedIntersections.constEnd(); ++intersectionIt )
506  {
507  double x = intersectionIt->x();
508  double y = intersectionIt->y();
509  if ( mGridStyle == QgsComposerMapGrid::Cross )
510  {
511  //ensure that crosses don't overshoot the map item bounds
512  QLineF line1 = QLineF( x - mCrossLength, y, x + mCrossLength, y );
513  line1.p1().rx() = line1.p1().x() < 0 ? 0 : line1.p1().x();
514  line1.p2().rx() = line1.p2().x() > maxX ? maxX : line1.p2().x();
515  QLineF line2 = QLineF( x, y - mCrossLength, x, y + mCrossLength );
516  line2.p1().ry() = line2.p1().y() < 0 ? 0 : line2.p1().y();
517  line2.p2().ry() = line2.p2().y() > maxY ? maxY : line2.p2().y();
518 
519  //draw line using coordinates scaled to dots
520  drawGridLine( QLineF( line1.p1() * dotsPerMM, line1.p2() * dotsPerMM ), context );
521  drawGridLine( QLineF( line2.p1() * dotsPerMM, line2.p2() * dotsPerMM ), context );
522  }
523  else if ( mGridStyle == QgsComposerMapGrid::Markers )
524  {
525  drawGridMarker( QPointF( x, y ) * dotsPerMM, context );
526  }
527  }
528  }
529  }
530 
531  //convert QPolygonF to QLineF to draw grid frames and annotations
532  QList< QPair< double, QPolygonF > >::const_iterator yGridLineIt = mTransformedYLines.constBegin();
533  for ( ; yGridLineIt != mTransformedYLines.constEnd(); ++yGridLineIt )
534  {
535  verticalLines.push_back( qMakePair( yGridLineIt->first, QLineF( yGridLineIt->second.first(), yGridLineIt->second.last() ) ) );
536  }
537  QList< QPair< double, QPolygonF > >::const_iterator xGridLineIt = mTransformedXLines.constBegin();
538  for ( ; xGridLineIt != mTransformedXLines.constEnd(); ++xGridLineIt )
539  {
540  horizontalLines.push_back( qMakePair( xGridLineIt->first, QLineF( xGridLineIt->second.first(), xGridLineIt->second.last() ) ) );
541  }
542 }
543 
544 void QgsComposerMapGrid::calculateCrsTransformLines()
545 {
546  QgsRectangle crsBoundingRect;
547  QgsCoordinateTransform inverseTr;
548  if ( crsGridParams( crsBoundingRect, inverseTr ) != 0 )
549  {
550  return;
551  }
552 
553  //calculate x grid lines
554  mTransformedXLines.clear();
555  xGridLinesCrsTransform( crsBoundingRect, inverseTr, mTransformedXLines );
556 
557  //calculate y grid lines
558  mTransformedYLines.clear();
559  yGridLinesCrsTransform( crsBoundingRect, inverseTr, mTransformedYLines );
560 
561  if ( mGridStyle == QgsComposerMapGrid::Cross || mGridStyle == QgsComposerMapGrid::Markers )
562  {
563  //cross or markers style - we also need to calculate intersections of lines
564 
565  //first convert lines to QgsGeometry
566  QList< QgsGeometry > yLines;
567  QList< QPair< double, QPolygonF > >::const_iterator yGridIt = mTransformedYLines.constBegin();
568  for ( ; yGridIt != mTransformedYLines.constEnd(); ++yGridIt )
569  {
570  QgsPolyline yLine;
571  for ( int i = 0; i < ( *yGridIt ).second.size(); ++i )
572  {
573  yLine.append( QgsPointXY( ( *yGridIt ).second.at( i ).x(), ( *yGridIt ).second.at( i ).y() ) );
574  }
575  yLines << QgsGeometry::fromPolyline( yLine );
576  }
577  QList< QgsGeometry > xLines;
578  QList< QPair< double, QPolygonF > >::const_iterator xGridIt = mTransformedXLines.constBegin();
579  for ( ; xGridIt != mTransformedXLines.constEnd(); ++xGridIt )
580  {
581  QgsPolyline xLine;
582  for ( int i = 0; i < ( *xGridIt ).second.size(); ++i )
583  {
584  xLine.append( QgsPointXY( ( *xGridIt ).second.at( i ).x(), ( *xGridIt ).second.at( i ).y() ) );
585  }
586  xLines << QgsGeometry::fromPolyline( xLine );
587  }
588 
589  //now, loop through geometries and calculate intersection points
590  mTransformedIntersections.clear();
591  QList< QgsGeometry >::const_iterator yLineIt = yLines.constBegin();
592  for ( ; yLineIt != yLines.constEnd(); ++yLineIt )
593  {
594  QList< QgsGeometry >::const_iterator xLineIt = xLines.constBegin();
595  for ( ; xLineIt != xLines.constEnd(); ++xLineIt )
596  {
597  //look for intersections between lines
598  QgsGeometry intersects = ( *yLineIt ).intersection( ( *xLineIt ) );
599  if ( intersects.isNull() )
600  continue;
601 
602  //go through all intersections and draw grid markers/crosses
603  int i = 0;
604  QgsPointXY vertex = intersects.vertexAt( i );
605  while ( vertex != QgsPointXY( 0, 0 ) )
606  {
607  mTransformedIntersections << vertex;
608  i = i + 1;
609  vertex = intersects.vertexAt( i );
610  }
611  }
612  }
613  }
614 
615  mTransformDirty = false;
616 }
617 
618 void QgsComposerMapGrid::draw( QPainter *p )
619 {
620  if ( !mComposerMap || !mEnabled )
621  {
622  return;
623  }
624  QPaintDevice *paintDevice = p->device();
625  if ( !paintDevice )
626  {
627  return;
628  }
629 
630  p->save();
631  p->setCompositionMode( mBlendMode );
632  p->setRenderHint( QPainter::Antialiasing );
633 
634  QRectF thisPaintRect = QRectF( 0, 0, mComposerMap->rect().width(), mComposerMap->rect().height() );
635  p->setClipRect( thisPaintRect );
636  if ( thisPaintRect != mPrevPaintRect )
637  {
638  //rect has changed, so need to recalculate transform
639  mTransformDirty = true;
640  mPrevPaintRect = thisPaintRect;
641  }
642 
643  //setup painter scaling to dots so that raster symbology is drawn to scale
644  double dotsPerMM = paintDevice->logicalDpiX() / 25.4;
645  p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); //scale painter from mm to dots
646 
647  //setup render context
649  context.setForceVectorOutput( true );
650  QgsExpressionContext expressionContext = createExpressionContext();
651  context.setExpressionContext( expressionContext );
652 
653  QList< QPair< double, QLineF > > verticalLines;
654  QList< QPair< double, QLineF > > horizontalLines;
655 
656  //is grid in a different crs than map?
657  if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != mComposerMap->crs() )
658  {
659  drawGridCrsTransform( context, dotsPerMM, horizontalLines, verticalLines );
660  }
661  else
662  {
663  drawGridNoTransform( context, dotsPerMM, horizontalLines, verticalLines );
664  }
665 
666  p->restore();
667 
668  p->setClipping( false );
669 #ifdef Q_OS_MAC
670  //QPainter::setClipping(false) seems to be broken on OSX (#12747). So we hack around it by
671  //setting a larger clip rect
672  p->setClipRect( mComposerMap->mapRectFromScene( mComposerMap->sceneBoundingRect() ).adjusted( -10, -10, 10, 10 ) );
673 #endif
674 
675  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame )
676  {
677  drawGridFrame( p, horizontalLines, verticalLines );
678  }
679 
680  if ( mShowGridAnnotation )
681  {
682  drawCoordinateAnnotations( p, horizontalLines, verticalLines, context.expressionContext() );
683  }
684 }
685 
686 void QgsComposerMapGrid::drawGridNoTransform( QgsRenderContext &context, double dotsPerMM, QList< QPair< double, QLineF > > &horizontalLines,
687  QList< QPair< double, QLineF > > &verticalLines, bool calculateLinesOnly ) const
688 {
689  //get line positions
690  yGridLines( verticalLines );
691  xGridLines( horizontalLines );
692 
693  if ( calculateLinesOnly )
694  return;
695 
696  QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
697  QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
698 
699  //simple approach: draw vertical lines first, then horizontal ones
700  if ( mGridStyle == QgsComposerMapGrid::Solid )
701  {
702  //we need to scale line coordinates to dots, rather than mm, since the painter has already been scaled to dots
703  //this is done by multiplying each line coordinate by dotsPerMM
704  QLineF line;
705  for ( ; vIt != verticalLines.constEnd(); ++vIt )
706  {
707  line = QLineF( vIt->second.p1() * dotsPerMM, vIt->second.p2() * dotsPerMM );
708  drawGridLine( line, context );
709  }
710 
711  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
712  {
713  line = QLineF( hIt->second.p1() * dotsPerMM, hIt->second.p2() * dotsPerMM );
714  drawGridLine( line, context );
715  }
716  }
717  else if ( mGridStyle != QgsComposerMapGrid::FrameAnnotationsOnly ) //cross or markers
718  {
719  QPointF intersectionPoint, crossEnd1, crossEnd2;
720  for ( ; vIt != verticalLines.constEnd(); ++vIt )
721  {
722  //test for intersection with every horizontal line
723  hIt = horizontalLines.constBegin();
724  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
725  {
726  if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
727  {
728  if ( mGridStyle == QgsComposerMapGrid::Cross )
729  {
730  //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
731  crossEnd1 = ( ( intersectionPoint - vIt->second.p1() ).manhattanLength() > 0.01 ) ?
732  QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength ) : intersectionPoint;
733  crossEnd2 = ( ( intersectionPoint - vIt->second.p2() ).manhattanLength() > 0.01 ) ?
734  QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength ) : intersectionPoint;
735  //draw line using coordinates scaled to dots
736  drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
737  }
738  else if ( mGridStyle == QgsComposerMapGrid::Markers )
739  {
740  drawGridMarker( intersectionPoint * dotsPerMM, context );
741  }
742  }
743  }
744  }
745  if ( mGridStyle == QgsComposerMapGrid::Markers )
746  {
747  //markers mode, so we have no need to process horizontal lines (we've already
748  //drawn markers on the intersections between horizontal and vertical lines)
749  return;
750  }
751 
752  hIt = horizontalLines.constBegin();
753  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
754  {
755  vIt = verticalLines.constBegin();
756  for ( ; vIt != verticalLines.constEnd(); ++vIt )
757  {
758  if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
759  {
760  //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
761  crossEnd1 = ( ( intersectionPoint - hIt->second.p1() ).manhattanLength() > 0.01 ) ?
762  QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength ) : intersectionPoint;
763  crossEnd2 = ( ( intersectionPoint - hIt->second.p2() ).manhattanLength() > 0.01 ) ?
764  QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength ) : intersectionPoint;
765  //draw line using coordinates scaled to dots
766  drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
767  }
768  }
769  }
770  }
771 }
772 
773 void QgsComposerMapGrid::drawGridFrame( QPainter *p, const QList< QPair< double, QLineF > > &hLines, const QList< QPair< double, QLineF > > &vLines, GridExtension *extension ) const
774 {
775  if ( p )
776  {
777  p->save();
778  p->setRenderHint( QPainter::Antialiasing );
779  }
780 
781  //Sort the coordinate positions for each side
782  QMap< double, double > leftGridFrame;
783  QMap< double, double > rightGridFrame;
784  QMap< double, double > topGridFrame;
785  QMap< double, double > bottomGridFrame;
786 
787  sortGridLinesOnBorders( hLines, vLines, leftGridFrame, rightGridFrame, topGridFrame, bottomGridFrame );
788 
790  {
791  drawGridFrameBorder( p, leftGridFrame, QgsComposerMapGrid::Left, extension ? &extension->left : nullptr );
792  }
794  {
795  drawGridFrameBorder( p, rightGridFrame, QgsComposerMapGrid::Right, extension ? &extension->right : nullptr );
796  }
798  {
799  drawGridFrameBorder( p, topGridFrame, QgsComposerMapGrid::Top, extension ? &extension->top : nullptr );
800  }
802  {
803  drawGridFrameBorder( p, bottomGridFrame, QgsComposerMapGrid::Bottom, extension ? &extension->bottom : nullptr );
804  }
805  if ( p )
806  p->restore();
807 }
808 
809 void QgsComposerMapGrid::drawGridLine( const QLineF &line, QgsRenderContext &context ) const
810 {
811  QPolygonF poly;
812  poly << line.p1() << line.p2();
813  drawGridLine( poly, context );
814 }
815 
816 void QgsComposerMapGrid::drawGridLine( const QPolygonF &line, QgsRenderContext &context ) const
817 {
818  if ( !mComposerMap || !mComposerMap->composition() || !mGridLineSymbol )
819  {
820  return;
821  }
822 
823  mGridLineSymbol->startRender( context );
824  mGridLineSymbol->renderPolyline( line, nullptr, context );
825  mGridLineSymbol->stopRender( context );
826 }
827 
828 void QgsComposerMapGrid::drawGridMarker( QPointF point, QgsRenderContext &context ) const
829 {
830  if ( !mComposerMap || !mComposerMap->composition() || !mGridMarkerSymbol )
831  {
832  return;
833  }
834 
835  mGridMarkerSymbol->startRender( context );
836  mGridMarkerSymbol->renderPoint( point, nullptr, context );
837  mGridMarkerSymbol->stopRender( context );
838 }
839 
840 void QgsComposerMapGrid::drawGridFrameBorder( QPainter *p, const QMap< double, double > &borderPos, QgsComposerMapGrid::BorderSide border, double *extension ) const
841 {
842  if ( !mComposerMap )
843  {
844  return;
845  }
846 
847  switch ( mGridFrameStyle )
848  {
850  drawGridFrameZebraBorder( p, borderPos, border, extension );
851  break;
855  drawGridFrameTicks( p, borderPos, border, extension );
856  break;
857 
859  drawGridFrameLineBorder( p, border, extension );
860  break;
861 
863  break;
864  }
865 
866 }
867 
868 void QgsComposerMapGrid::drawGridFrameZebraBorder( QPainter *p, const QMap< double, double > &borderPos, QgsComposerMapGrid::BorderSide border, double *extension ) const
869 {
870  if ( !mComposerMap )
871  {
872  return;
873  }
874 
875  if ( extension )
876  {
877  *extension = mGridFrameWidth + mGridFramePenThickness / 2.0;
878  return;
879  }
880 
881  QMap< double, double > pos = borderPos;
882 
883  double currentCoord = 0;
885  {
886  currentCoord = - mGridFrameWidth;
887  pos.insert( 0, 0 );
888  }
890  {
891  currentCoord = - mGridFrameWidth;
892  pos.insert( 0, 0 );
893  }
894  bool color1 = true;
895  double x = 0;
896  double y = 0;
897  double width = 0;
898  double height = 0;
899 
900  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
901  {
902  pos.insert( mComposerMap->rect().height(), mComposerMap->rect().height() );
904  {
905  pos.insert( mComposerMap->rect().height() + mGridFrameWidth, mComposerMap->rect().height() + mGridFrameWidth );
906  }
907  }
908  else if ( border == QgsComposerMapGrid::Top || border == QgsComposerMapGrid::Bottom )
909  {
910  pos.insert( mComposerMap->rect().width(), mComposerMap->rect().width() );
912  {
913  pos.insert( mComposerMap->rect().width() + mGridFrameWidth, mComposerMap->rect().width() + mGridFrameWidth );
914  }
915  }
916 
917  //set pen to current frame pen
918  QPen framePen = QPen( mGridFramePenColor );
919  framePen.setWidthF( mGridFramePenThickness );
920  framePen.setJoinStyle( Qt::MiterJoin );
921  p->setPen( framePen );
922 
923  QMap< double, double >::const_iterator posIt = pos.constBegin();
924  for ( ; posIt != pos.constEnd(); ++posIt )
925  {
926  p->setBrush( QBrush( color1 ? mGridFrameFillColor1 : mGridFrameFillColor2 ) );
927  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
928  {
929  height = posIt.key() - currentCoord;
930  width = mGridFrameWidth;
931  x = ( border == QgsComposerMapGrid::Left ) ? -mGridFrameWidth : mComposerMap->rect().width();
932  y = currentCoord;
933  }
934  else //top or bottom
935  {
936  height = mGridFrameWidth;
937  width = posIt.key() - currentCoord;
938  x = currentCoord;
939  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height();
940  }
941  p->drawRect( QRectF( x, y, width, height ) );
942  currentCoord = posIt.key();
943  color1 = !color1;
944  }
945 }
946 
947 void QgsComposerMapGrid::drawGridFrameTicks( QPainter *p, const QMap< double, double > &borderPos, QgsComposerMapGrid::BorderSide border, double *extension ) const
948 {
949  if ( !mComposerMap )
950  {
951  return;
952  }
953 
954  if ( extension )
955  {
956  if ( mGridFrameStyle != QgsComposerMapGrid::InteriorTicks )
957  *extension = mGridFrameWidth;
958  return;
959  }
960 
961  double x = 0;
962  double y = 0;
963  double width = 0;
964  double height = 0;
965 
966  //set pen to current frame pen
967  QPen framePen = QPen( mGridFramePenColor );
968  framePen.setWidthF( mGridFramePenThickness );
969  framePen.setCapStyle( Qt::FlatCap );
970  p->setBrush( Qt::NoBrush );
971  p->setPen( framePen );
972 
973  QMap< double, double >::const_iterator posIt = borderPos.constBegin();
974  for ( ; posIt != borderPos.constEnd(); ++posIt )
975  {
976  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
977  {
978  y = posIt.key();
979  height = 0;
980  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
981  {
982  width = mGridFrameWidth;
983  x = ( border == QgsComposerMapGrid::Left ) ? 0 : mComposerMap->rect().width() - mGridFrameWidth;
984  }
985  else if ( mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
986  {
987  width = mGridFrameWidth;
988  x = ( border == QgsComposerMapGrid::Left ) ? - mGridFrameWidth : mComposerMap->rect().width();
989  }
990  else if ( mGridFrameStyle == QgsComposerMapGrid::InteriorExteriorTicks )
991  {
992  width = mGridFrameWidth * 2;
993  x = ( border == QgsComposerMapGrid::Left ) ? - mGridFrameWidth : mComposerMap->rect().width() - mGridFrameWidth;
994  }
995  }
996  else //top or bottom
997  {
998  x = posIt.key();
999  width = 0;
1000  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1001  {
1002  height = mGridFrameWidth;
1003  y = ( border == QgsComposerMapGrid::Top ) ? 0 : mComposerMap->rect().height() - mGridFrameWidth;
1004  }
1005  else if ( mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1006  {
1007  height = mGridFrameWidth;
1008  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height();
1009  }
1010  else if ( mGridFrameStyle == QgsComposerMapGrid::InteriorExteriorTicks )
1011  {
1012  height = mGridFrameWidth * 2;
1013  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height() - mGridFrameWidth;
1014  }
1015  }
1016  p->drawLine( QLineF( x, y, x + width, y + height ) );
1017  }
1018 }
1019 
1020 void QgsComposerMapGrid::drawGridFrameLineBorder( QPainter *p, QgsComposerMapGrid::BorderSide border, double *extension ) const
1021 {
1022  if ( !mComposerMap )
1023  {
1024  return;
1025  }
1026 
1027  if ( extension )
1028  {
1029  *extension = mGridFramePenThickness / 2.0;
1030  return;
1031  }
1032 
1033  //set pen to current frame pen
1034  QPen framePen = QPen( mGridFramePenColor );
1035  framePen.setWidthF( mGridFramePenThickness );
1036  framePen.setCapStyle( Qt::SquareCap );
1037  p->setBrush( Qt::NoBrush );
1038  p->setPen( framePen );
1039 
1040  switch ( border )
1041  {
1043  p->drawLine( QLineF( 0, 0, 0, mComposerMap->rect().height() ) );
1044  break;
1046  p->drawLine( QLineF( mComposerMap->rect().width(), 0, mComposerMap->rect().width(), mComposerMap->rect().height() ) );
1047  break;
1049  p->drawLine( QLineF( 0, 0, mComposerMap->rect().width(), 0 ) );
1050  break;
1052  p->drawLine( QLineF( 0, mComposerMap->rect().height(), mComposerMap->rect().width(), mComposerMap->rect().height() ) );
1053  break;
1054  }
1055 }
1056 
1057 void QgsComposerMapGrid::drawCoordinateAnnotations( QPainter *p, const QList< QPair< double, QLineF > > &hLines, const QList< QPair< double, QLineF > > &vLines, QgsExpressionContext &expressionContext,
1058  GridExtension *extension ) const
1059 {
1060  QString currentAnnotationString;
1061  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
1062  for ( ; it != hLines.constEnd(); ++it )
1063  {
1064  currentAnnotationString = gridAnnotationString( it->first, QgsComposerMapGrid::Latitude, expressionContext );
1065  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsComposerMapGrid::Latitude, extension );
1066  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsComposerMapGrid::Latitude, extension );
1067  }
1068 
1069  it = vLines.constBegin();
1070  for ( ; it != vLines.constEnd(); ++it )
1071  {
1072  currentAnnotationString = gridAnnotationString( it->first, QgsComposerMapGrid::Longitude, expressionContext );
1073  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsComposerMapGrid::Longitude, extension );
1074  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsComposerMapGrid::Longitude, extension );
1075  }
1076 }
1077 
1078 void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, const QString &annotationString, const AnnotationCoordinate coordinateType, GridExtension *extension ) const
1079 {
1080  if ( !mComposerMap )
1081  {
1082  return;
1083  }
1084  QgsComposerMapGrid::BorderSide frameBorder = borderForLineCoord( pos, coordinateType );
1085  double textWidth = QgsComposerUtils::textWidthMM( mGridAnnotationFont, annotationString );
1086  //relevant for annotations is the height of digits
1087  double textHeight = extension ? QgsComposerUtils::fontAscentMM( mGridAnnotationFont )
1088  : QgsComposerUtils::fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
1089  double xpos = pos.x();
1090  double ypos = pos.y();
1091  int rotation = 0;
1092 
1093  double gridFrameDistance = 0;
1094  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame && mGridFrameStyle != QgsComposerMapGrid::LineBorder )
1095  {
1096  gridFrameDistance = mGridFrameWidth;
1097  }
1098  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::LineBorder )
1099  {
1100  gridFrameDistance += ( mGridFramePenThickness / 2.0 );
1101  }
1102 
1103  if ( frameBorder == QgsComposerMapGrid::Left )
1104  {
1105  if ( mLeftGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1106  ( coordinateType == Longitude && mLeftGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1107  ( coordinateType == Latitude && mLeftGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1108  {
1109  return;
1110  }
1112  {
1113  gridFrameDistance = 0;
1114  }
1115 
1116  if ( mLeftGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1117  {
1118  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1119  {
1120  gridFrameDistance = 0;
1121  }
1122  if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::Vertical || mLeftGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1123  {
1124  xpos += textHeight + mAnnotationFrameDistance + gridFrameDistance;
1125  ypos += textWidth / 2.0;
1126  rotation = 270;
1127  }
1128  else if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1129  {
1130  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1131  ypos -= textWidth / 2.0;
1132  rotation = 90;
1133  }
1134  else
1135  {
1136  xpos += mAnnotationFrameDistance + gridFrameDistance;
1137  ypos += textHeight / 2.0;
1138  }
1139  }
1140  else if ( mLeftGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //Outside map frame
1141  {
1142  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1143  {
1144  gridFrameDistance = 0;
1145  }
1146  if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::Vertical || mLeftGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1147  {
1148  xpos -= ( mAnnotationFrameDistance + gridFrameDistance );
1149  ypos += textWidth / 2.0;
1150  rotation = 270;
1151  if ( extension )
1152  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1153  }
1154  else if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1155  {
1156  xpos -= textHeight + mAnnotationFrameDistance + gridFrameDistance;
1157  ypos -= textWidth / 2.0;
1158  rotation = 90;
1159  if ( extension )
1160  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1161  }
1162  else
1163  {
1164  xpos -= ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
1165  ypos += textHeight / 2.0;
1166  if ( extension )
1167  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1168  }
1169  }
1170  else
1171  {
1172  return;
1173  }
1174  }
1175  else if ( frameBorder == QgsComposerMapGrid::Right )
1176  {
1177  if ( mRightGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1178  ( coordinateType == Longitude && mRightGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1179  ( coordinateType == Latitude && mRightGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1180  {
1181  return;
1182  }
1184  {
1185  gridFrameDistance = 0;
1186  }
1187 
1188  if ( mRightGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1189  {
1190  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1191  {
1192  gridFrameDistance = 0;
1193  }
1194  if ( mRightGridAnnotationDirection == QgsComposerMapGrid::Vertical )
1195  {
1196  xpos -= mAnnotationFrameDistance + gridFrameDistance;
1197  ypos += textWidth / 2.0;
1198  rotation = 270;
1199  }
1200  else if ( mRightGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending || mRightGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1201  {
1202  xpos -= textHeight + mAnnotationFrameDistance + gridFrameDistance;
1203  ypos -= textWidth / 2.0;
1204  rotation = 90;
1205  }
1206  else
1207  {
1208  xpos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1209  ypos += textHeight / 2.0;
1210  }
1211  }
1212  else if ( mRightGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame )//OutsideMapFrame
1213  {
1214  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1215  {
1216  gridFrameDistance = 0;
1217  }
1218  if ( mRightGridAnnotationDirection == QgsComposerMapGrid::Vertical )
1219  {
1220  xpos += ( textHeight + mAnnotationFrameDistance + gridFrameDistance );
1221  ypos += textWidth / 2.0;
1222  rotation = 270;
1223  if ( extension )
1224  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1225  }
1226  else if ( mRightGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending || mRightGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1227  {
1228  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1229  ypos -= textWidth / 2.0;
1230  rotation = 90;
1231  if ( extension )
1232  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1233  }
1234  else //Horizontal
1235  {
1236  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1237  ypos += textHeight / 2.0;
1238  if ( extension )
1239  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1240  }
1241  }
1242  else
1243  {
1244  return;
1245  }
1246  }
1247  else if ( frameBorder == QgsComposerMapGrid::Bottom )
1248  {
1249  if ( mBottomGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1250  ( coordinateType == Longitude && mBottomGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1251  ( coordinateType == Latitude && mBottomGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1252  {
1253  return;
1254  }
1256  {
1257  gridFrameDistance = 0;
1258  }
1259 
1260  if ( mBottomGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1261  {
1262  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1263  {
1264  gridFrameDistance = 0;
1265  }
1266  if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mBottomGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1267  {
1268  ypos -= mAnnotationFrameDistance + gridFrameDistance;
1269  xpos -= textWidth / 2.0;
1270  }
1271  else if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1272  {
1273  xpos -= textHeight / 2.0;
1274  ypos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1275  rotation = 90;
1276  }
1277  else //Vertical
1278  {
1279  xpos += textHeight / 2.0;
1280  ypos -= mAnnotationFrameDistance + gridFrameDistance;
1281  rotation = 270;
1282  }
1283  }
1284  else if ( mBottomGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //OutsideMapFrame
1285  {
1286  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1287  {
1288  gridFrameDistance = 0;
1289  }
1290  if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mBottomGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1291  {
1292  ypos += ( mAnnotationFrameDistance + textHeight + gridFrameDistance );
1293  xpos -= textWidth / 2.0;
1294  if ( extension )
1295  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1296  }
1297  else if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1298  {
1299  xpos -= textHeight / 2.0;
1300  ypos += gridFrameDistance + mAnnotationFrameDistance;
1301  rotation = 90;
1302  if ( extension )
1303  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1304  }
1305  else //Vertical
1306  {
1307  xpos += textHeight / 2.0;
1308  ypos += ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
1309  rotation = 270;
1310  if ( extension )
1311  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1312  }
1313  }
1314  else
1315  {
1316  return;
1317  }
1318  }
1319  else //top
1320  {
1321  if ( mTopGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1322  ( coordinateType == Longitude && mTopGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1323  ( coordinateType == Latitude && mTopGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1324  {
1325  return;
1326  }
1328  {
1329  gridFrameDistance = 0;
1330  }
1331 
1332  if ( mTopGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1333  {
1334  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1335  {
1336  gridFrameDistance = 0;
1337  }
1338  if ( mTopGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mTopGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1339  {
1340  xpos -= textWidth / 2.0;
1341  ypos += textHeight + mAnnotationFrameDistance + gridFrameDistance;
1342  }
1343  else if ( mTopGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1344  {
1345  xpos -= textHeight / 2.0;
1346  ypos += mAnnotationFrameDistance + gridFrameDistance;
1347  rotation = 90;
1348  }
1349  else //Vertical
1350  {
1351  xpos += textHeight / 2.0;
1352  ypos += textWidth + mAnnotationFrameDistance + gridFrameDistance;
1353  rotation = 270;
1354  }
1355  }
1356  else if ( mTopGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //OutsideMapFrame
1357  {
1358  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1359  {
1360  gridFrameDistance = 0;
1361  }
1362  if ( mTopGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mTopGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1363  {
1364  xpos -= textWidth / 2.0;
1365  ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
1366  if ( extension )
1367  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1368  }
1369  else if ( mTopGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1370  {
1371  xpos -= textHeight / 2.0;
1372  ypos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1373  rotation = 90;
1374  if ( extension )
1375  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1376  }
1377  else //Vertical
1378  {
1379  xpos += textHeight / 2.0;
1380  ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
1381  rotation = 270;
1382  if ( extension )
1383  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1384  }
1385  }
1386  else
1387  {
1388  return;
1389  }
1390  }
1391 
1392  if ( extension || !p )
1393  return;
1394 
1395  drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
1396 }
1397 
1398 void QgsComposerMapGrid::drawAnnotation( QPainter *p, QPointF pos, int rotation, const QString &annotationText ) const
1399 {
1400  if ( !mComposerMap )
1401  {
1402  return;
1403  }
1404 
1405  p->save();
1406  p->translate( pos );
1407  p->rotate( rotation );
1408  QgsComposerUtils::drawText( p, QPointF( 0, 0 ), annotationText, mGridAnnotationFont, mGridAnnotationFontColor );
1409  p->restore();
1410 }
1411 
1412 QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGrid::AnnotationCoordinate coord, QgsExpressionContext &expressionContext ) const
1413 {
1414  //check if we are using degrees (ie, geographic crs)
1415  bool geographic = false;
1416  if ( mCRS.isValid() && mCRS.isGeographic() )
1417  {
1418  geographic = true;
1419  }
1420  else if ( mComposerMap && mComposerMap->composition() )
1421  {
1422  geographic = mComposerMap->crs().isGeographic();
1423  }
1424 
1425  if ( geographic && coord == QgsComposerMapGrid::Longitude &&
1426  ( mGridAnnotationFormat == QgsComposerMapGrid::Decimal || mGridAnnotationFormat == QgsComposerMapGrid::DecimalWithSuffix ) )
1427  {
1428  // wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W"
1429  double wrappedX = fmod( value, 360.0 );
1430  if ( wrappedX > 180.0 )
1431  {
1432  value = wrappedX - 360.0;
1433  }
1434  else if ( wrappedX < -180.0 )
1435  {
1436  value = wrappedX + 360.0;
1437  }
1438  }
1439 
1440  if ( mGridAnnotationFormat == QgsComposerMapGrid::Decimal )
1441  {
1442  return QString::number( value, 'f', mGridAnnotationPrecision );
1443  }
1444  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DecimalWithSuffix )
1445  {
1446  QString hemisphere;
1447 
1448  double coordRounded = qRound( value * pow( 10.0, mGridAnnotationPrecision ) ) / pow( 10.0, mGridAnnotationPrecision );
1449  if ( coord == QgsComposerMapGrid::Longitude )
1450  {
1451  //don't use E/W suffixes if ambiguous (e.g., 180 degrees)
1452  if ( !geographic || ( coordRounded != 180.0 && coordRounded != 0.0 ) )
1453  {
1454  hemisphere = value < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
1455  }
1456  }
1457  else
1458  {
1459  //don't use N/S suffixes if ambiguous (e.g., 0 degrees)
1460  if ( !geographic || coordRounded != 0.0 )
1461  {
1462  hemisphere = value < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
1463  }
1464  }
1465  if ( geographic )
1466  {
1467  //insert degree symbol for geographic coordinates
1468  return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere;
1469  }
1470  else
1471  {
1472  return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + hemisphere;
1473  }
1474  }
1475  else if ( mGridAnnotationFormat == CustomFormat )
1476  {
1477  expressionContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_number" ), value, true ) );
1478  expressionContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), coord == QgsComposerMapGrid::Longitude ? "x" : "y", true ) );
1479  if ( !mGridAnnotationExpression )
1480  {
1481  mGridAnnotationExpression.reset( new QgsExpression( mGridAnnotationExpressionString ) );
1482  mGridAnnotationExpression->prepare( &expressionContext );
1483  }
1484  return mGridAnnotationExpression->evaluate( &expressionContext ).toString();
1485  }
1486 
1487  QgsPointXY p;
1488  p.setX( coord == QgsComposerMapGrid::Longitude ? value : 0 );
1489  p.setY( coord == QgsComposerMapGrid::Longitude ? 0 : value );
1490 
1491  QString annotationString;
1492  if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinute )
1493  {
1494  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision );
1495  }
1496  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteNoSuffix )
1497  {
1498  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision, false );
1499  }
1500  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinutePadded )
1501  {
1502  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision, true, true );
1503  }
1504  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecond )
1505  {
1506  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision );
1507  }
1508  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecondNoSuffix )
1509  {
1510  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision, false );
1511  }
1512  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecondPadded )
1513  {
1514  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision, true, true );
1515  }
1516 
1517  QStringList split = annotationString.split( ',' );
1518  if ( coord == QgsComposerMapGrid::Longitude )
1519  {
1520  return split.at( 0 );
1521  }
1522  else
1523  {
1524  if ( split.size() < 2 )
1525  {
1526  return QLatin1String( "" );
1527  }
1528  return split.at( 1 );
1529  }
1530 }
1531 
1532 int QgsComposerMapGrid::xGridLines( QList< QPair< double, QLineF > > &lines ) const
1533 {
1534  lines.clear();
1535  if ( !mComposerMap || mGridIntervalY <= 0.0 )
1536  {
1537  return 1;
1538  }
1539 
1540 
1541  QPolygonF mapPolygon = mComposerMap->transformedMapPolygon();
1542  QRectF mapBoundingRect = mapPolygon.boundingRect();
1543  double gridIntervalY = mGridIntervalY;
1544  double gridOffsetY = mGridOffsetY;
1545  double annotationScale = 1.0;
1546  if ( mGridUnit != MapUnit )
1547  {
1548  mapBoundingRect = mComposerMap->rect();
1549  mapPolygon = QPolygonF( mComposerMap->rect() );
1550  if ( mGridUnit == CM )
1551  {
1552  annotationScale = 0.1;
1553  gridIntervalY *= 10;
1554  gridOffsetY *= 10;
1555  }
1556  }
1557 
1558  //consider to round up to the next step in case the left boundary is > 0
1559  double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
1560  double currentLevel = static_cast< int >( ( mapBoundingRect.top() - gridOffsetY ) / gridIntervalY + roundCorrection ) * gridIntervalY + gridOffsetY;
1561 
1562  int gridLineCount = 0;
1563  if ( qgsDoubleNear( mComposerMap->mapRotation(), 0.0 ) || mGridUnit != MapUnit )
1564  {
1565  //no rotation. Do it 'the easy way'
1566 
1567  double yCanvasCoord;
1568  while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1569  {
1570  yCanvasCoord = mComposerMap->rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
1571  lines.push_back( qMakePair( currentLevel * annotationScale, QLineF( 0, yCanvasCoord, mComposerMap->rect().width(), yCanvasCoord ) ) );
1572  currentLevel += gridIntervalY;
1573  gridLineCount++;
1574  }
1575  return 0;
1576  }
1577 
1578  //the four border lines
1579  QVector<QLineF> borderLines;
1580  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1581  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1582  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1583  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1584 
1585  QVector<QPointF> intersectionList; //intersects between border lines and grid lines
1586 
1587  while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1588  {
1589  intersectionList.clear();
1590  QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
1591 
1592  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1593  for ( ; it != borderLines.constEnd(); ++it )
1594  {
1595  QPointF intersectionPoint;
1596  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1597  {
1598  intersectionList.push_back( intersectionPoint );
1599  if ( intersectionList.size() >= 2 )
1600  {
1601  break; //we already have two intersections, skip further tests
1602  }
1603  }
1604  }
1605 
1606  if ( intersectionList.size() >= 2 )
1607  {
1608  lines.push_back( qMakePair( currentLevel, QLineF( mComposerMap->mapToItemCoords( intersectionList.at( 0 ) ), mComposerMap->mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1609  gridLineCount++;
1610  }
1611  currentLevel += gridIntervalY;
1612  }
1613 
1614 
1615  return 0;
1616 }
1617 
1618 int QgsComposerMapGrid::yGridLines( QList< QPair< double, QLineF > > &lines ) const
1619 {
1620  lines.clear();
1621  if ( !mComposerMap || mGridIntervalX <= 0.0 )
1622  {
1623  return 1;
1624  }
1625 
1626  QPolygonF mapPolygon = mComposerMap->transformedMapPolygon();
1627  QRectF mapBoundingRect = mapPolygon.boundingRect();
1628  double gridIntervalX = mGridIntervalX;
1629  double gridOffsetX = mGridOffsetX;
1630  double annotationScale = 1.0;
1631  if ( mGridUnit != MapUnit )
1632  {
1633  mapBoundingRect = mComposerMap->rect();
1634  mapPolygon = QPolygonF( mComposerMap->rect() );
1635  if ( mGridUnit == CM )
1636  {
1637  annotationScale = 0.1;
1638  gridIntervalX *= 10;
1639  gridOffsetX *= 10;
1640  }
1641  }
1642 
1643  //consider to round up to the next step in case the left boundary is > 0
1644  double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
1645  double currentLevel = static_cast< int >( ( mapBoundingRect.left() - gridOffsetX ) / gridIntervalX + roundCorrection ) * gridIntervalX + gridOffsetX;
1646 
1647  int gridLineCount = 0;
1648  if ( qgsDoubleNear( mComposerMap->mapRotation(), 0.0 ) || mGridUnit != MapUnit )
1649  {
1650  //no rotation. Do it 'the easy way'
1651  double xCanvasCoord;
1652  while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1653  {
1654  xCanvasCoord = mComposerMap->rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
1655  lines.push_back( qMakePair( currentLevel * annotationScale, QLineF( xCanvasCoord, 0, xCanvasCoord, mComposerMap->rect().height() ) ) );
1656  currentLevel += gridIntervalX;
1657  gridLineCount++;
1658  }
1659  return 0;
1660  }
1661 
1662  //the four border lines
1663  QVector<QLineF> borderLines;
1664  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1665  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1666  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1667  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1668 
1669  QVector<QPointF> intersectionList; //intersects between border lines and grid lines
1670 
1671  while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1672  {
1673  intersectionList.clear();
1674  QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
1675 
1676  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1677  for ( ; it != borderLines.constEnd(); ++it )
1678  {
1679  QPointF intersectionPoint;
1680  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1681  {
1682  intersectionList.push_back( intersectionPoint );
1683  if ( intersectionList.size() >= 2 )
1684  {
1685  break; //we already have two intersections, skip further tests
1686  }
1687  }
1688  }
1689 
1690  if ( intersectionList.size() >= 2 )
1691  {
1692  lines.push_back( qMakePair( currentLevel, QLineF( mComposerMap->mapToItemCoords( intersectionList.at( 0 ) ), mComposerMap->mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1693  gridLineCount++;
1694  }
1695  currentLevel += gridIntervalX;
1696  }
1697 
1698  return 0;
1699 }
1700 
1701 int QgsComposerMapGrid::xGridLinesCrsTransform( const QgsRectangle &bbox, const QgsCoordinateTransform &t, QList< QPair< double, QPolygonF > > &lines ) const
1702 {
1703  lines.clear();
1704  if ( !mComposerMap || mGridIntervalY <= 0.0 )
1705  {
1706  return 1;
1707  }
1708 
1709  double roundCorrection = bbox.yMaximum() > 0 ? 1.0 : 0.0;
1710  double currentLevel = static_cast< int >( ( bbox.yMaximum() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY;
1711 
1712  double minX = bbox.xMinimum();
1713  double maxX = bbox.xMaximum();
1714  double step = ( maxX - minX ) / 20;
1715 
1716  bool crosses180 = false;
1717  bool crossed180 = false;
1718  if ( mCRS.isGeographic() && ( minX > maxX ) )
1719  {
1720  //handle 180 degree longitude crossover
1721  crosses180 = true;
1722  step = ( maxX + 360.0 - minX ) / 20;
1723  }
1724 
1725  if ( qgsDoubleNear( step, 0.0 ) )
1726  return 1;
1727 
1728  int gridLineCount = 0;
1729  while ( currentLevel >= bbox.yMinimum() && gridLineCount < MAX_GRID_LINES )
1730  {
1731  QPolygonF gridLine;
1732  double currentX = minX;
1733  bool cont = true;
1734  while ( cont )
1735  {
1736  if ( ( !crosses180 || crossed180 ) && ( currentX > maxX ) )
1737  {
1738  cont = false;
1739  }
1740 
1741  try
1742  {
1743  QgsPointXY mapPoint = t.transform( currentX, currentLevel ); //transform back to map crs
1744  gridLine.append( mComposerMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) ); //transform back to composer coords
1745  }
1746  catch ( QgsCsException &cse )
1747  {
1748  Q_UNUSED( cse );
1749  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
1750  }
1751 
1752  currentX += step;
1753  if ( crosses180 && currentX > 180.0 )
1754  {
1755  currentX -= 360.0;
1756  crossed180 = true;
1757  }
1758  }
1759  crossed180 = false;
1760 
1761  QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
1762  QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1763  for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1764  {
1765  if ( !( *lineIt ).isEmpty() )
1766  {
1767  lines.append( qMakePair( currentLevel, *lineIt ) );
1768  gridLineCount++;
1769  }
1770  }
1771  currentLevel -= mGridIntervalY;
1772  }
1773 
1774  return 0;
1775 }
1776 
1777 int QgsComposerMapGrid::yGridLinesCrsTransform( const QgsRectangle &bbox, const QgsCoordinateTransform &t, QList< QPair< double, QPolygonF > > &lines ) const
1778 {
1779  lines.clear();
1780  if ( !mComposerMap || mGridIntervalX <= 0.0 )
1781  {
1782  return 1;
1783  }
1784 
1785  double roundCorrection = bbox.xMinimum() > 0 ? 1.0 : 0.0;
1786  double currentLevel = static_cast< int >( ( bbox.xMinimum() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
1787 
1788  double minY = bbox.yMinimum();
1789  double maxY = bbox.yMaximum();
1790  double step = ( maxY - minY ) / 20;
1791 
1792  if ( qgsDoubleNear( step, 0.0 ) )
1793  return 1;
1794 
1795  bool crosses180 = false;
1796  bool crossed180 = false;
1797  if ( mCRS.isGeographic() && ( bbox.xMinimum() > bbox.xMaximum() ) )
1798  {
1799  //handle 180 degree longitude crossover
1800  crosses180 = true;
1801  }
1802 
1803  int gridLineCount = 0;
1804  while ( ( currentLevel <= bbox.xMaximum() || ( crosses180 && !crossed180 ) ) && gridLineCount < MAX_GRID_LINES )
1805  {
1806  QPolygonF gridLine;
1807  double currentY = minY;
1808  bool cont = true;
1809  while ( cont )
1810  {
1811  if ( currentY > maxY )
1812  {
1813  cont = false;
1814  }
1815  try
1816  {
1817  //transform back to map crs
1818  QgsPointXY mapPoint = t.transform( currentLevel, currentY );
1819  //transform back to composer coords
1820  gridLine.append( mComposerMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) );
1821  }
1822  catch ( QgsCsException &cse )
1823  {
1824  Q_UNUSED( cse );
1825  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
1826  }
1827 
1828  currentY += step;
1829  }
1830  //clip grid line to map polygon
1831  QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
1832  QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1833  for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1834  {
1835  if ( !( *lineIt ).isEmpty() )
1836  {
1837  lines.append( qMakePair( currentLevel, *lineIt ) );
1838  gridLineCount++;
1839  }
1840  }
1841  currentLevel += mGridIntervalX;
1842  if ( crosses180 && currentLevel > 180.0 )
1843  {
1844  currentLevel -= 360.0;
1845  crossed180 = true;
1846  }
1847  }
1848 
1849  return 0;
1850 }
1851 
1852 void QgsComposerMapGrid::sortGridLinesOnBorders( const QList< QPair< double, QLineF > > &hLines, const QList< QPair< double, QLineF > > &vLines, QMap< double, double > &leftFrameEntries,
1853  QMap< double, double > &rightFrameEntries, QMap< double, double > &topFrameEntries, QMap< double, double > &bottomFrameEntries ) const
1854 {
1855  QList< QgsMapAnnotation > borderPositions;
1856  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
1857  for ( ; it != hLines.constEnd(); ++it )
1858  {
1859  QgsMapAnnotation p1;
1860  p1.coordinate = it->first;
1861  p1.itemPosition = it->second.p1();
1862  p1.coordinateType = QgsComposerMapGrid::Latitude;
1863  borderPositions << p1;
1864 
1865  QgsMapAnnotation p2;
1866  p2.coordinate = it->first;
1867  p2.itemPosition = it->second.p2();
1868  p2.coordinateType = QgsComposerMapGrid::Latitude;
1869  borderPositions << p2;
1870  }
1871  it = vLines.constBegin();
1872  for ( ; it != vLines.constEnd(); ++it )
1873  {
1874  QgsMapAnnotation p1;
1875  p1.coordinate = it->first;
1876  p1.itemPosition = it->second.p1();
1877  p1.coordinateType = QgsComposerMapGrid::Longitude;
1878  borderPositions << p1;
1879 
1880  QgsMapAnnotation p2;
1881  p2.coordinate = it->first;
1882  p2.itemPosition = it->second.p2();
1883  p2.coordinateType = QgsComposerMapGrid::Longitude;
1884  borderPositions << p2;
1885  }
1886 
1887  QList< QgsMapAnnotation >::const_iterator bIt = borderPositions.constBegin();
1888  for ( ; bIt != borderPositions.constEnd(); ++bIt )
1889  {
1890  QgsComposerMapGrid::BorderSide frameBorder = borderForLineCoord( bIt->itemPosition, bIt->coordinateType );
1891  if ( frameBorder == QgsComposerMapGrid::Left && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Left ) )
1892  {
1893  leftFrameEntries.insert( bIt->itemPosition.y(), bIt->coordinate );
1894  }
1895  else if ( frameBorder == QgsComposerMapGrid::Right && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Right ) )
1896  {
1897  rightFrameEntries.insert( bIt->itemPosition.y(), bIt->coordinate );
1898  }
1899  else if ( frameBorder == QgsComposerMapGrid::Top && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Top ) )
1900  {
1901  topFrameEntries.insert( bIt->itemPosition.x(), bIt->coordinate );
1902  }
1903  else if ( frameBorder == QgsComposerMapGrid::Bottom && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Bottom ) )
1904  {
1905  bottomFrameEntries.insert( bIt->itemPosition.x(), bIt->coordinate );
1906  }
1907  }
1908 }
1909 
1910 bool QgsComposerMapGrid::shouldShowDivisionForSide( QgsComposerMapGrid::AnnotationCoordinate coordinate, QgsComposerMapGrid::BorderSide side ) const
1911 {
1912  switch ( side )
1913  {
1915  return shouldShowDivisionForDisplayMode( coordinate, mLeftFrameDivisions );
1917  return shouldShowDivisionForDisplayMode( coordinate, mRightFrameDivisions );
1919  return shouldShowDivisionForDisplayMode( coordinate, mTopFrameDivisions );
1921  default: //prevent warnings
1922  return shouldShowDivisionForDisplayMode( coordinate, mBottomFrameDivisions );
1923  }
1924 }
1925 
1926 bool QgsComposerMapGrid::shouldShowDivisionForDisplayMode( QgsComposerMapGrid::AnnotationCoordinate coordinate, QgsComposerMapGrid::DisplayMode mode ) const
1927 {
1928  return mode == QgsComposerMapGrid::ShowAll
1929  || ( mode == QgsComposerMapGrid::LatitudeOnly && coordinate == QgsComposerMapGrid::Latitude )
1930  || ( mode == QgsComposerMapGrid::LongitudeOnly && coordinate == QgsComposerMapGrid::Longitude );
1931 }
1932 
1933 bool sortByDistance( QPair<qreal, QgsComposerMapGrid::BorderSide> a, QPair<qreal, QgsComposerMapGrid::BorderSide> b )
1934 {
1935  return a.first < b.first;
1936 }
1937 
1938 QgsComposerMapGrid::BorderSide QgsComposerMapGrid::borderForLineCoord( QPointF p, const AnnotationCoordinate coordinateType ) const
1939 {
1940  if ( !mComposerMap )
1941  {
1942  return QgsComposerMapGrid::Left;
1943  }
1944 
1945  double tolerance = qMax( mComposerMap->hasFrame() ? mComposerMap->pen().widthF() : 0.0, 1.0 );
1946 
1947  //check for corner coordinates
1948  if ( ( p.y() <= tolerance && p.x() <= tolerance ) // top left
1949  || ( p.y() <= tolerance && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //top right
1950  || ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() <= tolerance ) //bottom left
1951  || ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //bottom right
1952  )
1953  {
1954  //coordinate is in corner - fall back to preferred side for coordinate type
1955  if ( coordinateType == QgsComposerMapGrid::Latitude )
1956  {
1957  if ( p.x() <= tolerance )
1958  {
1959  return QgsComposerMapGrid::Left;
1960  }
1961  else
1962  {
1964  }
1965  }
1966  else
1967  {
1968  if ( p.y() <= tolerance )
1969  {
1970  return QgsComposerMapGrid::Top;
1971  }
1972  else
1973  {
1975  }
1976  }
1977  }
1978 
1979  //otherwise, guess side based on closest map side to point
1980  QList< QPair<qreal, QgsComposerMapGrid::BorderSide > > distanceToSide;
1981  distanceToSide << qMakePair( p.x(), QgsComposerMapGrid::Left );
1982  distanceToSide << qMakePair( mComposerMap->rect().width() - p.x(), QgsComposerMapGrid::Right );
1983  distanceToSide << qMakePair( p.y(), QgsComposerMapGrid::Top );
1984  distanceToSide << qMakePair( mComposerMap->rect().height() - p.y(), QgsComposerMapGrid::Bottom );
1985 
1986  std::sort( distanceToSide.begin(), distanceToSide.end(), sortByDistance );
1987  return distanceToSide.at( 0 ).second;
1988 }
1989 
1991 {
1992  delete mGridLineSymbol;
1993  mGridLineSymbol = symbol;
1994 }
1995 
1997 {
1998  delete mGridMarkerSymbol;
1999  mGridMarkerSymbol = symbol;
2000 }
2001 
2003 {
2004  switch ( border )
2005  {
2007  mLeftGridAnnotationDisplay = display;
2008  break;
2010  mRightGridAnnotationDisplay = display;
2011  break;
2013  mTopGridAnnotationDisplay = display;
2014  break;
2016  mBottomGridAnnotationDisplay = display;
2017  break;
2018  default:
2019  return;
2020  }
2021 
2022  if ( mComposerMap )
2023  {
2025  mComposerMap->update();
2026  }
2027 }
2028 
2030 {
2031  switch ( border )
2032  {
2034  return mLeftGridAnnotationDisplay;
2036  return mRightGridAnnotationDisplay;
2038  return mTopGridAnnotationDisplay;
2040  default:
2041  return mBottomGridAnnotationDisplay;
2042  }
2043 }
2044 
2046 {
2047  double top = 0.0;
2048  double right = 0.0;
2049  double bottom = 0.0;
2050  double left = 0.0;
2051  calculateMaxExtension( top, right, bottom, left );
2052  return qMax( qMax( qMax( top, right ), bottom ), left );
2053 }
2054 
2055 void QgsComposerMapGrid::calculateMaxExtension( double &top, double &right, double &bottom, double &left )
2056 {
2057  top = 0.0;
2058  right = 0.0;
2059  bottom = 0.0;
2060  left = 0.0;
2061 
2062  if ( !mComposerMap || !mEnabled )
2063  {
2064  return;
2065  }
2066 
2067  //setup render context
2069  QgsExpressionContext expressionContext = createExpressionContext();
2070  context.setExpressionContext( expressionContext );
2071 
2072  GridExtension extension;
2073 
2074  //collect grid lines
2075  QList< QPair< double, QLineF > > verticalLines;
2076  QList< QPair< double, QLineF > > horizontalLines;
2077  if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != mComposerMap->crs() )
2078  {
2079  drawGridCrsTransform( context, 0, horizontalLines, verticalLines, false );
2080  }
2081  else
2082  {
2083  drawGridNoTransform( context, 0, horizontalLines, verticalLines, false );
2084  }
2085 
2086  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame )
2087  {
2088  drawGridFrame( nullptr, horizontalLines, verticalLines, &extension );
2089  }
2090 
2091  if ( mShowGridAnnotation )
2092  {
2093  drawCoordinateAnnotations( nullptr, horizontalLines, verticalLines, context.expressionContext(), &extension );
2094  }
2095 
2096  top = extension.top;
2097  right = extension.right;
2098  bottom = extension.bottom;
2099  left = extension.left;
2100 }
2101 
2103 {
2104  if ( unit == mGridUnit )
2105  {
2106  return;
2107  }
2108  mGridUnit = unit;
2109  mTransformDirty = true;
2110 }
2111 
2112 void QgsComposerMapGrid::setIntervalX( const double interval )
2113 {
2114  if ( qgsDoubleNear( interval, mGridIntervalX ) )
2115  {
2116  return;
2117  }
2118  mGridIntervalX = interval;
2119  mTransformDirty = true;
2120 }
2121 
2122 void QgsComposerMapGrid::setIntervalY( const double interval )
2123 {
2124  if ( qgsDoubleNear( interval, mGridIntervalY ) )
2125  {
2126  return;
2127  }
2128  mGridIntervalY = interval;
2129  mTransformDirty = true;
2130 }
2131 
2132 void QgsComposerMapGrid::setOffsetX( const double offset )
2133 {
2134  if ( qgsDoubleNear( offset, mGridOffsetX ) )
2135  {
2136  return;
2137  }
2138  mGridOffsetX = offset;
2139  mTransformDirty = true;
2140 }
2141 
2142 void QgsComposerMapGrid::setOffsetY( const double offset )
2143 {
2144  if ( qgsDoubleNear( offset, mGridOffsetY ) )
2145  {
2146  return;
2147  }
2148  mGridOffsetY = offset;
2149  mTransformDirty = true;
2150 }
2151 
2153 {
2154  if ( style == mGridStyle )
2155  {
2156  return;
2157  }
2158  mGridStyle = style;
2159  mTransformDirty = true;
2160 }
2161 
2163 {
2164  switch ( border )
2165  {
2167  mLeftGridAnnotationDirection = direction;
2168  break;
2170  mRightGridAnnotationDirection = direction;
2171  break;
2173  mTopGridAnnotationDirection = direction;
2174  break;
2176  mBottomGridAnnotationDirection = direction;
2177  break;
2178  default:
2179  return;
2180  }
2181 
2182  if ( mComposerMap )
2183  {
2185  mComposerMap->update();
2186  }
2187 }
2188 
2189 void QgsComposerMapGrid::setFrameSideFlags( FrameSideFlags flags )
2190 {
2191  mGridFrameSides = flags;
2192 }
2193 
2195 {
2196  if ( on )
2197  mGridFrameSides |= flag;
2198  else
2199  mGridFrameSides &= ~flag;
2200 }
2201 
2202 QgsComposerMapGrid::FrameSideFlags QgsComposerMapGrid::frameSideFlags() const
2203 {
2204  return mGridFrameSides;
2205 }
2206 
2208 {
2210  context.appendScope( new QgsExpressionContextScope( tr( "Grid" ) ) );
2211  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_number" ), 0, true ) );
2212  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), "x", true ) );
2213  context.setHighlightedVariables( QStringList() << QStringLiteral( "grid_number" ) << QStringLiteral( "grid_axis" ) );
2214  return context;
2215 }
2216 
2218 {
2219  return mGridFrameSides.testFlag( flag );
2220 }
2221 
2223 {
2224  mLeftGridAnnotationDirection = direction;
2225  mRightGridAnnotationDirection = direction;
2226  mTopGridAnnotationDirection = direction;
2227  mBottomGridAnnotationDirection = direction;
2228 }
2229 
2231 {
2232  switch ( border )
2233  {
2235  mLeftGridAnnotationPosition = position;
2236  break;
2238  mRightGridAnnotationPosition = position;
2239  break;
2241  mTopGridAnnotationPosition = position;
2242  break;
2244  mBottomGridAnnotationPosition = position;
2245  break;
2246  default:
2247  return;
2248  }
2249 
2250  if ( mComposerMap )
2251  {
2253  mComposerMap->update();
2254  }
2255 }
2256 
2258 {
2259  switch ( border )
2260  {
2262  return mLeftGridAnnotationPosition;
2264  return mRightGridAnnotationPosition;
2266  return mTopGridAnnotationPosition;
2268  default:
2269  return mBottomGridAnnotationPosition;
2270  }
2271 }
2272 
2274 {
2275  if ( !mComposerMap )
2276  {
2277  return mLeftGridAnnotationDirection;
2278  }
2279 
2280  switch ( border )
2281  {
2283  return mLeftGridAnnotationDirection;
2285  return mRightGridAnnotationDirection;
2287  return mTopGridAnnotationDirection;
2289  default:
2290  return mBottomGridAnnotationDirection;
2291  }
2292 }
2293 
2295 {
2296  switch ( border )
2297  {
2299  mLeftFrameDivisions = divisions;
2300  break;
2302  mRightFrameDivisions = divisions;
2303  break;
2305  mTopFrameDivisions = divisions;
2306  break;
2308  mBottomFrameDivisions = divisions;
2309  break;
2310  default:
2311  return;
2312  }
2313 
2314  if ( mComposerMap )
2315  {
2316  mComposerMap->update();
2317  }
2318 }
2319 
2321 {
2322  switch ( border )
2323  {
2325  return mLeftFrameDivisions;
2327  return mRightFrameDivisions;
2329  return mTopFrameDivisions;
2331  default:
2332  return mBottomFrameDivisions;
2333  }
2334 }
2335 
2336 int QgsComposerMapGrid::crsGridParams( QgsRectangle &crsRect, QgsCoordinateTransform &inverseTransform ) const
2337 {
2338  if ( !mComposerMap )
2339  {
2340  return 1;
2341  }
2342 
2343  try
2344  {
2345  QgsCoordinateTransform tr( mComposerMap->crs(), mCRS );
2346  QPolygonF mapPolygon = mComposerMap->transformedMapPolygon();
2347  QRectF mbr = mapPolygon.boundingRect();
2348  QgsRectangle mapBoundingRect( mbr.left(), mbr.bottom(), mbr.right(), mbr.top() );
2349 
2350 
2351  if ( mCRS.isGeographic() )
2352  {
2353  //handle crossing the 180 degree longitude line
2354  QgsPointXY lowerLeft( mapBoundingRect.xMinimum(), mapBoundingRect.yMinimum() );
2355  QgsPointXY upperRight( mapBoundingRect.xMaximum(), mapBoundingRect.yMaximum() );
2356 
2357  lowerLeft = tr.transform( lowerLeft.x(), lowerLeft.y() );
2358  upperRight = tr.transform( upperRight.x(), upperRight.y() );
2359 
2360  if ( lowerLeft.x() > upperRight.x() )
2361  {
2362  //we've crossed the line
2363  crsRect = tr.transformBoundingBox( mapBoundingRect, QgsCoordinateTransform::ForwardTransform, true );
2364  }
2365  else
2366  {
2367  //didn't cross the line
2368  crsRect = tr.transformBoundingBox( mapBoundingRect );
2369  }
2370  }
2371  else
2372  {
2373  crsRect = tr.transformBoundingBox( mapBoundingRect );
2374  }
2375 
2376  inverseTransform.setSourceCrs( mCRS );
2377  inverseTransform.setDestinationCrs( mComposerMap->crs() );
2378  }
2379  catch ( QgsCsException &cse )
2380  {
2381  Q_UNUSED( cse );
2382  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
2383  return 1;
2384  }
2385  return 0;
2386 }
2387 
2388 QList<QPolygonF> QgsComposerMapGrid::trimLinesToMap( const QPolygonF &line, const QgsRectangle &rect )
2389 {
2390  QgsGeometry lineGeom = QgsGeometry::fromQPolygonF( line );
2391  QgsGeometry rectGeom = QgsGeometry::fromRect( rect );
2392 
2393  QgsGeometry intersected = lineGeom.intersection( rectGeom );
2394  QList<QgsGeometry> intersectedParts = intersected.asGeometryCollection();
2395 
2396  QList<QPolygonF> trimmedLines;
2397  QList<QgsGeometry>::const_iterator geomIt = intersectedParts.constBegin();
2398  for ( ; geomIt != intersectedParts.constEnd(); ++geomIt )
2399  {
2400  trimmedLines << ( *geomIt ).asQPolygonF();
2401  }
2402  return trimmedLines;
2403 }
QgsComposerMapGrid(const QString &name, QgsComposerMap *map)
Constructor for QgsComposerMapGrid.
QgsComposerMapItem * item(const QString &itemId) const
Returns a reference to an item within the stack.
void setStyle(const GridStyle style)
Sets the grid style, which controls how the grid is drawn over the map&#39;s contents.
void setForceVectorOutput(bool force)
void addGrid(QgsComposerMapGrid *grid)
Adds a new map grid to the stack and takes ownership of the grid.
The class is used as a container of context for various read/write operations on other objects...
void draw(QPainter *painter) override
Draws a grid.
Single variable definition for use within a QgsExpressionContextScope.
A rectangle specified with double values.
Definition: qgsrectangle.h:38
void calculateMaxGridExtension(double &top, double &right, double &bottom, double &left) const
Calculates the maximum distance grids within the stack extend beyond the QgsComposerMap&#39;s item rect...
void setAnnotationDirection(const AnnotationDirection direction, const BorderSide border)
Sets the direction for drawing frame annotations.
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
virtual bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets map item state from a DOM document.
static QgsGeometry fromPolyline(const QgsPolyline &polyline)
Creates a new geometry from a QgsPolyline object.
virtual QgsExpressionContext createExpressionContext() const
Creates an expression context relating to the objects&#39; current state.
virtual QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the objects&#39; current state.
GridStyle
Grid drawing style.
const QgsComposerMapGrid * constGrid(const QString &gridId) const
Returns a const reference to a grid within the stack.
void addItem(QgsComposerMapItem *item)
Adds a new map item to the stack and takes ownership of the item.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
void setOffsetY(const double offset)
Sets the offset for grid lines in the y-direction.
Draw line crosses at intersections of grid lines.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:54
Draw annotations horizontally.
bool readXml(const QDomElement &elem, const QDomDocument &doc) override
Sets the grid stack&#39;s state from a DOM document.
Degree/minutes, with minutes using leading zeros where required.
static QgsLineSymbol * createSimple(const QgsStringMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties. ...
Definition: qgssymbol.cpp:1070
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
double y
Definition: qgspointxy.h:47
FrameStyle
Style for grid frame.
A class to represent a 2D point.
Definition: qgspointxy.h:42
QgsComposerMapItem(const QString &name, QgsComposerMap *map)
Constructor for QgsComposerMapItem.
QList< QgsComposerMapItem *> mItems
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Definition: qgssymbol.cpp:1656
Simple solid line frame.
virtual bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores map item state in DOM element.
static void drawText(QPainter *painter, QPointF pos, const QString &text, const QFont &font, const QColor &color=QColor())
Draws text on a painter at a specific position, taking care of composer specific issues (calculation ...
Draw annotations outside the map frame.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:96
double maxExtension()
Calculates the maximum distance the grid extends beyond the QgsComposerMap&#39;s item rect...
static double fontAscentMM(const QFont &font)
Calculate font ascent in millimeters, including workarounds for QT font rendering issues...
Tick markers drawn outside map frame.
Degree/minutes, use NSEW suffix.
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
AnnotationDirection
Direction of grid annotations.
AnnotationPosition annotationPosition(const BorderSide border) const
Gets the position for the grid annotations on a specified side of the map frame.
void removeGrid(const QString &gridId)
Removes a grid from the stack and deletes the corresponding QgsComposerMapGrid.
void setGridLineColor(const QColor &color)
Sets color of grid lines.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:347
void setAnnotationDisplay(const DisplayMode display, const BorderSide border)
Sets what types of grid annotations should be drawn for a specified side of the map frame...
QString toDegreesMinutesSeconds(int precision, const bool useSuffix=true, const bool padded=false) const
Return a string representation as degrees minutes seconds.
Definition: qgspointxy.cpp:61
AnnotationFormat
Format for displaying grid annotations.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:210
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
An item which is drawn inside a QgsComposerMap, e.g., a grid or map overview.
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:384
bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores grid state in DOM element.
void calculateMaxExtension(double &top, double &right, double &bottom, double &left)
Calculates the maximum distance the grid extends beyond the QgsComposerMap&#39;s item rect...
QVector< QgsPointXY > QgsPolyline
Polyline is represented as a vector of points.
Definition: qgsgeometry.h:48
Show latitude/y annotations/divisions only.
QString what() const
Definition: qgsexception.h:48
void setFrameSideFlag(const FrameSideFlag flag, bool on=true)
Sets whether the grid frame is drawn for a certain side of the map item.
static QString encodeColor(const QColor &color)
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...
void renderPoint(QPointF point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Definition: qgssymbol.cpp:1447
Black/white pattern.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
static double fontHeightCharacterMM(const QFont &font, QChar character)
Calculate font height in millimeters of a single character, including workarounds for QT font renderi...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void updateBoundingRect()
Updates the bounding rect of this item. Call this function before doing any changes related to annota...
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
#define MAX_GRID_LINES
bool mEnabled
True if item is to be displayed on map.
Show both latitude and longitude annotations/divisions.
QgsPathResolver pathResolver() const
Return path resolver object with considering whether the project uses absolute or relative paths and ...
void setWidth(double width)
Definition: qgssymbol.cpp:1523
void removeItem(const QString &itemId)
Removes an item from the stack and deletes the corresponding QgsComposerMapItem.
const QgsComposerMapItem * constItem(const QString &itemId) const
Returns a const reference to an item within the stack.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:198
QList< QgsGeometry > asGeometryCollection() const
Return contents of the geometry as a list of geometries.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
DisplayMode
Display settings for grid annotations and frames.
void moveItemUp(const QString &itemId)
Moves an item up the stack, causing it to be rendered above other items.
AnnotationPosition
Position for grid annotations.
void setY(double y)
Sets the y value of the point.
Definition: qgspointxy.h:108
void setUnits(const GridUnit unit)
Sets the units to use for grid measurements such as the interval and offset for grid lines...
No grid lines over the map, only draw frame and annotations.
GridStyle style() const
Gets the grid&#39;s style, which controls how the grid is drawn over the map&#39;s contents.
Annotations follow the boundary direction.
FrameSideFlags frameSideFlags() const
Returns the flags which control which sides of the map item the grid frame is drawn on...
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.
FrameSideFlag
Flags for controlling which side of the map a frame is drawn on.
Decimal degrees, use NSEW suffix.
An individual grid which is drawn above the map content in a QgsComposerMap.
QgsComposerMapGrid & operator[](int idx)
Returns a reference to a grid within the stack.
bool isGeographic() const
Returns whether the CRS is a geographic CRS (using lat/lon coordinates)
QgsComposerMap * mComposerMap
Associated composer map.
AnnotationDirection annotationDirection(const BorderSide border) const
Gets the direction for drawing frame annotations.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the CRS for the grid.
bool usesAdvancedEffects() const override
Returns true if the item is drawn using advanced effects, such as blend modes.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setGridLineWidth(const double width)
Sets width of grid lines.
Draw annotations inside the map frame.
QgsComposerMapGridStack(QgsComposerMap *map)
Constructor for QgsComposerMapGridStack.
Object representing map window.
void setX(double x)
Sets the x value of the point.
Definition: qgspointxy.h:100
void moveItemDown(const QString &itemId)
Moves an item up the stack, causing it to be rendered above other items.
double x
Definition: qgspointxy.h:46
void setFrameSideFlags(QgsComposerMapGrid::FrameSideFlags flags)
Sets flags for grid frame sides.
Coordinate is a longitude value.
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used for drawing grid lines.
QString toDegreesMinutes(int precision, const bool useSuffix=true, const bool padded=false) const
Return a string representation as degrees minutes.
Definition: qgspointxy.cpp:175
BorderSide
Border sides for annotations.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the marker symbol used for drawing grid points.
bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets grid state from a DOM document.
void setAnnotationPosition(const AnnotationPosition position, const BorderSide border)
Sets the position for the grid annotations on a specified side of the map frame.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:106
Degree/minutes/seconds, use NSEW suffix.
QgsExpressionContext & expressionContext()
Gets the expression context.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:91
QPointF mapToItemCoords(QPointF mapCoords) const
Transforms map coordinates to item coordinates (considering rotation and move offset) ...
static double textWidthMM(const QFont &font, const QString &text)
Calculate font width in millimeters for a string, including workarounds for QT font rendering issues...
QgsComposition * mComposition
Degree/minutes/seconds, with minutes using leading zeros where required.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Contains information about the context of a rendering operation.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
DisplayMode frameDivisions(const BorderSide border) const
Gets the type of grid divisions which are used for frames on a specified side of the map...
const QgsComposition * composition() const
Returns the composition the item is attached to.
QList< QgsComposerMapGrid *> asList() const
Returns a list of QgsComposerMapGrids contained by the stack.
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const
Transform the point from the source CRS to the destination CRS.
QgsProject * project() const
The project associated with the composition.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
A collection of map items which are drawn above the map content in a QgsComposerMap.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Draw annotations vertically, descending.
This class represents a coordinate reference system (CRS).
DisplayMode annotationDisplay(const BorderSide border) const
Gets the display mode for the grid annotations on a specified side of the map frame.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
bool sortByDistance(QPair< qreal, QgsComposerMapGrid::BorderSide > a, QPair< qreal, QgsComposerMapGrid::BorderSide > b)
static QDomElement saveSymbol(const QString &symbolName, QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
void setIntervalY(const double interval)
Sets the interval between grid lines in the y-direction.
Class for doing transforms between two map coordinate systems.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:96
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
Tick markers drawn both inside and outside the map frame.
void setFrameDivisions(const DisplayMode divisions, const BorderSide border)
Sets what type of grid divisions should be used for frames on a specified side of the map...
void moveGridUp(const QString &gridId)
Moves a grid up the stack, causing it to be rendered above other grids.
Degree/minutes, use - for S/W coordinates.
Custom expression-based format.
double maxGridExtension() const
Calculates the maximum distance grids within the stack extend beyond the QgsComposerMap&#39;s item rect...
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user...
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:101
Transform from source to destination CRS.
bool hasFrame() const
Whether this item has a frame or not.
static QgsMarkerSymbol * createSimple(const QgsStringMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Definition: qgssymbol.cpp:1059
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:64
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
bool testFrameSideFlag(const FrameSideFlag flag) const
Tests whether the grid frame should be drawn on a specified side of the map item. ...
Draw markers at intersections of grid lines.
void moveGridDown(const QString &gridId)
Moves a grid down the stack, causing it to be rendered below other grids.
void removeItems()
Clears the item stack and deletes all QgsComposerMapItems contained by the stack. ...
double mapRotation(QgsComposerObject::PropertyValueType valueType=QgsComposerObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the composer item, in degrees clockwise...
Draw annotations vertically, ascending.
Decimal degrees, use - for S/W coordinates.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
Degree/minutes/seconds, use - for S/W coordinates.
static QgsRenderContext createRenderContextForComposition(QgsComposition *composition, QPainter *painter)
Creates a render context suitable for the specified composition and painter destination.
Grid units in centimeters.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:406
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
GridUnit
Unit for grid values.
void setIntervalX(const double interval)
Sets the interval between grid lines in the x-direction.
Show longitude/x annotations/divisions only.
QgsCoordinateReferenceSystem crs() const
Retrieves the CRS for the grid.
QgsComposerMapGrid * grid(const QString &gridId) const
Returns a reference to a grid within the stack.
QPolygonF transformedMapPolygon() const
Returns extent that considers rotation and shift with mOffsetX / mOffsetY.
Grid units follow map units.
void setOffsetX(const double offset)
Sets the offset for grid lines in the x-direction.
AnnotationCoordinate
Annotation coordinate type.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
static QColor decodeColor(const QString &str)
Tick markers drawn inside map frame.
QgsComposerMap * mComposerMap
void setColor(const QColor &color)
Definition: qgssymbol.cpp:428
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Coordinate is a latitude value.