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