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