QGIS API Documentation  2.99.0-Master (90ae728)
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.isNull() )
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* paintDevice = p->device();
616  if ( !paintDevice )
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 = paintDevice->logicalDpiX() / 25.4;
636  p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); //scale painter from mm to dots
637 
638  //setup render context
640  context.setForceVectorOutput( true );
641  QgsExpressionContext expressionContext = createExpressionContext();
642  context.setExpressionContext( expressionContext );
643 
644  QList< QPair< double, QLineF > > verticalLines;
645  QList< QPair< double, QLineF > > horizontalLines;
646 
647  //is grid in a different crs than map?
648  if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != mComposerMap->crs() )
649  {
650  drawGridCrsTransform( context, dotsPerMM, horizontalLines, verticalLines );
651  }
652  else
653  {
654  drawGridNoTransform( context, dotsPerMM, horizontalLines, verticalLines );
655  }
656 
657  p->restore();
658 
659  p->setClipping( false );
660 #ifdef Q_OS_MAC
661  //QPainter::setClipping(false) seems to be broken on OSX (#12747). So we hack around it by
662  //setting a larger clip rect
663  p->setClipRect( mComposerMap->mapRectFromScene( mComposerMap->sceneBoundingRect() ).adjusted( -10, -10, 10, 10 ) );
664 #endif
665 
666  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame )
667  {
668  drawGridFrame( p, horizontalLines, verticalLines );
669  }
670 
671  if ( mShowGridAnnotation )
672  {
673  drawCoordinateAnnotations( p, horizontalLines, verticalLines, context.expressionContext() );
674  }
675 }
676 
677 void QgsComposerMapGrid::drawGridNoTransform( QgsRenderContext &context, double dotsPerMM, QList< QPair< double, QLineF > > &horizontalLines,
678  QList< QPair< double, QLineF > > &verticalLines, bool calculateLinesOnly ) const
679 {
680  //get line positions
681  yGridLines( verticalLines );
682  xGridLines( horizontalLines );
683 
684  if ( calculateLinesOnly )
685  return;
686 
687  QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
688  QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
689 
690  //simple approach: draw vertical lines first, then horizontal ones
691  if ( mGridStyle == QgsComposerMapGrid::Solid )
692  {
693  //we need to scale line coordinates to dots, rather than mm, since the painter has already been scaled to dots
694  //this is done by multiplying each line coordinate by dotsPerMM
695  QLineF line;
696  for ( ; vIt != verticalLines.constEnd(); ++vIt )
697  {
698  line = QLineF( vIt->second.p1() * dotsPerMM, vIt->second.p2() * dotsPerMM );
699  drawGridLine( line, context );
700  }
701 
702  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
703  {
704  line = QLineF( hIt->second.p1() * dotsPerMM, hIt->second.p2() * dotsPerMM );
705  drawGridLine( line, context );
706  }
707  }
708  else if ( mGridStyle != QgsComposerMapGrid::FrameAnnotationsOnly ) //cross or markers
709  {
710  QPointF intersectionPoint, crossEnd1, crossEnd2;
711  for ( ; vIt != verticalLines.constEnd(); ++vIt )
712  {
713  //test for intersection with every horizontal line
714  hIt = horizontalLines.constBegin();
715  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
716  {
717  if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
718  {
719  if ( mGridStyle == QgsComposerMapGrid::Cross )
720  {
721  //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
722  crossEnd1 = (( intersectionPoint - vIt->second.p1() ).manhattanLength() > 0.01 ) ?
723  QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength ) : intersectionPoint;
724  crossEnd2 = (( intersectionPoint - vIt->second.p2() ).manhattanLength() > 0.01 ) ?
725  QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength ) : intersectionPoint;
726  //draw line using coordinates scaled to dots
727  drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
728  }
729  else if ( mGridStyle == QgsComposerMapGrid::Markers )
730  {
731  drawGridMarker( intersectionPoint * dotsPerMM, context );
732  }
733  }
734  }
735  }
736  if ( mGridStyle == QgsComposerMapGrid::Markers )
737  {
738  //markers mode, so we have no need to process horizontal lines (we've already
739  //drawn markers on the intersections between horizontal and vertical lines)
740  return;
741  }
742 
743  hIt = horizontalLines.constBegin();
744  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
745  {
746  vIt = verticalLines.constBegin();
747  for ( ; vIt != verticalLines.constEnd(); ++vIt )
748  {
749  if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
750  {
751  //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
752  crossEnd1 = (( intersectionPoint - hIt->second.p1() ).manhattanLength() > 0.01 ) ?
753  QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength ) : intersectionPoint;
754  crossEnd2 = (( intersectionPoint - hIt->second.p2() ).manhattanLength() > 0.01 ) ?
755  QgsSymbolLayerUtils::pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength ) : intersectionPoint;
756  //draw line using coordinates scaled to dots
757  drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
758  }
759  }
760  }
761  }
762 }
763 
764 void QgsComposerMapGrid::drawGridFrame( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines, GridExtension* extension ) const
765 {
766  if ( p )
767  {
768  p->save();
769  p->setRenderHint( QPainter::Antialiasing );
770  }
771 
772  //Sort the coordinate positions for each side
773  QMap< double, double > leftGridFrame;
774  QMap< double, double > rightGridFrame;
775  QMap< double, double > topGridFrame;
776  QMap< double, double > bottomGridFrame;
777 
778  sortGridLinesOnBorders( hLines, vLines, leftGridFrame, rightGridFrame, topGridFrame, bottomGridFrame );
779 
781  {
782  drawGridFrameBorder( p, leftGridFrame, QgsComposerMapGrid::Left, extension ? &extension->left : nullptr );
783  }
785  {
786  drawGridFrameBorder( p, rightGridFrame, QgsComposerMapGrid::Right, extension ? &extension->right : nullptr );
787  }
789  {
790  drawGridFrameBorder( p, topGridFrame, QgsComposerMapGrid::Top, extension ? &extension->top : nullptr );
791  }
793  {
794  drawGridFrameBorder( p, bottomGridFrame, QgsComposerMapGrid::Bottom, extension ? &extension->bottom : nullptr );
795  }
796  if ( p )
797  p->restore();
798 }
799 
800 void QgsComposerMapGrid::drawGridLine( const QLineF& line, QgsRenderContext& context ) const
801 {
802  QPolygonF poly;
803  poly << line.p1() << line.p2();
804  drawGridLine( poly, context );
805 }
806 
807 void QgsComposerMapGrid::drawGridLine( const QPolygonF& line, QgsRenderContext& context ) const
808 {
809  if ( !mComposerMap || !mComposerMap->composition() || !mGridLineSymbol )
810  {
811  return;
812  }
813 
814  mGridLineSymbol->startRender( context );
815  mGridLineSymbol->renderPolyline( line, nullptr, context );
816  mGridLineSymbol->stopRender( context );
817 }
818 
819 void QgsComposerMapGrid::drawGridMarker( QPointF point, QgsRenderContext& context ) const
820 {
821  if ( !mComposerMap || !mComposerMap->composition() || !mGridMarkerSymbol )
822  {
823  return;
824  }
825 
826  mGridMarkerSymbol->startRender( context );
827  mGridMarkerSymbol->renderPoint( point, nullptr, context );
828  mGridMarkerSymbol->stopRender( context );
829 }
830 
831 void QgsComposerMapGrid::drawGridFrameBorder( QPainter* p, const QMap< double, double >& borderPos, QgsComposerMapGrid::BorderSide border, double* extension ) const
832 {
833  if ( !mComposerMap )
834  {
835  return;
836  }
837 
838  switch ( mGridFrameStyle )
839  {
841  drawGridFrameZebraBorder( p, borderPos, border, extension );
842  break;
846  drawGridFrameTicks( p, borderPos, border, extension );
847  break;
848 
850  drawGridFrameLineBorder( p, border, extension );
851  break;
852 
854  break;
855  }
856 
857 }
858 
859 void QgsComposerMapGrid::drawGridFrameZebraBorder( QPainter* p, const QMap< double, double >& borderPos, QgsComposerMapGrid::BorderSide border, double* extension ) const
860 {
861  if ( !mComposerMap )
862  {
863  return;
864  }
865 
866  if ( extension )
867  {
868  *extension = mGridFrameWidth + mGridFramePenThickness / 2.0;
869  return;
870  }
871 
872  QMap< double, double > pos = borderPos;
873 
874  double currentCoord = 0;
876  {
877  currentCoord = - mGridFrameWidth;
878  pos.insert( 0, 0 );
879  }
881  {
882  currentCoord = - mGridFrameWidth;
883  pos.insert( 0, 0 );
884  }
885  bool color1 = true;
886  double x = 0;
887  double y = 0;
888  double width = 0;
889  double height = 0;
890 
891  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
892  {
893  pos.insert( mComposerMap->rect().height(), mComposerMap->rect().height() );
895  {
896  pos.insert( mComposerMap->rect().height() + mGridFrameWidth, mComposerMap->rect().height() + mGridFrameWidth );
897  }
898  }
899  else if ( border == QgsComposerMapGrid::Top || border == QgsComposerMapGrid::Bottom )
900  {
901  pos.insert( mComposerMap->rect().width(), mComposerMap->rect().width() );
903  {
904  pos.insert( mComposerMap->rect().width() + mGridFrameWidth, mComposerMap->rect().width() + mGridFrameWidth );
905  }
906  }
907 
908  //set pen to current frame pen
909  QPen framePen = QPen( mGridFramePenColor );
910  framePen.setWidthF( mGridFramePenThickness );
911  framePen.setJoinStyle( Qt::MiterJoin );
912  p->setPen( framePen );
913 
914  QMap< double, double >::const_iterator posIt = pos.constBegin();
915  for ( ; posIt != pos.constEnd(); ++posIt )
916  {
917  p->setBrush( QBrush( color1 ? mGridFrameFillColor1 : mGridFrameFillColor2 ) );
918  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
919  {
920  height = posIt.key() - currentCoord;
921  width = mGridFrameWidth;
922  x = ( border == QgsComposerMapGrid::Left ) ? -mGridFrameWidth : mComposerMap->rect().width();
923  y = currentCoord;
924  }
925  else //top or bottom
926  {
927  height = mGridFrameWidth;
928  width = posIt.key() - currentCoord;
929  x = currentCoord;
930  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height();
931  }
932  p->drawRect( QRectF( x, y, width, height ) );
933  currentCoord = posIt.key();
934  color1 = !color1;
935  }
936 }
937 
938 void QgsComposerMapGrid::drawGridFrameTicks( QPainter* p, const QMap< double, double >& borderPos, QgsComposerMapGrid::BorderSide border, double* extension ) const
939 {
940  if ( !mComposerMap )
941  {
942  return;
943  }
944 
945  if ( extension )
946  {
947  if ( mGridFrameStyle != QgsComposerMapGrid::InteriorTicks )
948  *extension = mGridFrameWidth;
949  return;
950  }
951 
952  double x = 0;
953  double y = 0;
954  double width = 0;
955  double height = 0;
956 
957  //set pen to current frame pen
958  QPen framePen = QPen( mGridFramePenColor );
959  framePen.setWidthF( mGridFramePenThickness );
960  framePen.setCapStyle( Qt::FlatCap );
961  p->setBrush( Qt::NoBrush );
962  p->setPen( framePen );
963 
964  QMap< double, double >::const_iterator posIt = borderPos.constBegin();
965  for ( ; posIt != borderPos.constEnd(); ++posIt )
966  {
967  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
968  {
969  y = posIt.key();
970  height = 0;
971  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
972  {
973  width = mGridFrameWidth;
974  x = ( border == QgsComposerMapGrid::Left ) ? 0 : mComposerMap->rect().width() - mGridFrameWidth;
975  }
976  else if ( mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
977  {
978  width = mGridFrameWidth;
979  x = ( border == QgsComposerMapGrid::Left ) ? - mGridFrameWidth : mComposerMap->rect().width();
980  }
981  else if ( mGridFrameStyle == QgsComposerMapGrid::InteriorExteriorTicks )
982  {
983  width = mGridFrameWidth * 2;
984  x = ( border == QgsComposerMapGrid::Left ) ? - mGridFrameWidth : mComposerMap->rect().width() - mGridFrameWidth;
985  }
986  }
987  else //top or bottom
988  {
989  x = posIt.key();
990  width = 0;
991  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
992  {
993  height = mGridFrameWidth;
994  y = ( border == QgsComposerMapGrid::Top ) ? 0 : mComposerMap->rect().height() - mGridFrameWidth;
995  }
996  else if ( mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
997  {
998  height = mGridFrameWidth;
999  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height();
1000  }
1001  else if ( mGridFrameStyle == QgsComposerMapGrid::InteriorExteriorTicks )
1002  {
1003  height = mGridFrameWidth * 2;
1004  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height() - mGridFrameWidth;
1005  }
1006  }
1007  p->drawLine( QLineF( x, y, x + width, y + height ) );
1008  }
1009 }
1010 
1011 void QgsComposerMapGrid::drawGridFrameLineBorder( QPainter* p, QgsComposerMapGrid::BorderSide border, double* extension ) const
1012 {
1013  if ( !mComposerMap )
1014  {
1015  return;
1016  }
1017 
1018  if ( extension )
1019  {
1020  *extension = mGridFramePenThickness / 2.0;
1021  return;
1022  }
1023 
1024  //set pen to current frame pen
1025  QPen framePen = QPen( mGridFramePenColor );
1026  framePen.setWidthF( mGridFramePenThickness );
1027  framePen.setCapStyle( Qt::SquareCap );
1028  p->setBrush( Qt::NoBrush );
1029  p->setPen( framePen );
1030 
1031  switch ( border )
1032  {
1034  p->drawLine( QLineF( 0, 0, 0, mComposerMap->rect().height() ) );
1035  break;
1037  p->drawLine( QLineF( mComposerMap->rect().width(), 0, mComposerMap->rect().width(), mComposerMap->rect().height() ) );
1038  break;
1040  p->drawLine( QLineF( 0, 0, mComposerMap->rect().width(), 0 ) );
1041  break;
1043  p->drawLine( QLineF( 0, mComposerMap->rect().height(), mComposerMap->rect().width(), mComposerMap->rect().height() ) );
1044  break;
1045  }
1046 }
1047 
1048 void QgsComposerMapGrid::drawCoordinateAnnotations( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines, QgsExpressionContext &expressionContext,
1049  GridExtension* extension ) const
1050 {
1051  QString currentAnnotationString;
1052  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
1053  for ( ; it != hLines.constEnd(); ++it )
1054  {
1055  currentAnnotationString = gridAnnotationString( it->first, QgsComposerMapGrid::Latitude, expressionContext );
1056  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsComposerMapGrid::Latitude, extension );
1057  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsComposerMapGrid::Latitude, extension );
1058  }
1059 
1060  it = vLines.constBegin();
1061  for ( ; it != vLines.constEnd(); ++it )
1062  {
1063  currentAnnotationString = gridAnnotationString( it->first, QgsComposerMapGrid::Longitude, expressionContext );
1064  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsComposerMapGrid::Longitude, extension );
1065  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsComposerMapGrid::Longitude, extension );
1066  }
1067 }
1068 
1069 void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter* p, QPointF pos, const QString& annotationString, const AnnotationCoordinate coordinateType, GridExtension* extension ) const
1070 {
1071  if ( !mComposerMap )
1072  {
1073  return;
1074  }
1075  QgsComposerMapGrid::BorderSide frameBorder = borderForLineCoord( pos, coordinateType );
1076  double textWidth = QgsComposerUtils::textWidthMM( mGridAnnotationFont, annotationString );
1077  //relevant for annotations is the height of digits
1078  double textHeight = extension ? QgsComposerUtils::fontAscentMM( mGridAnnotationFont )
1079  : QgsComposerUtils::fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
1080  double xpos = pos.x();
1081  double ypos = pos.y();
1082  int rotation = 0;
1083 
1084  double gridFrameDistance = 0;
1085  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame && mGridFrameStyle != QgsComposerMapGrid::LineBorder )
1086  {
1087  gridFrameDistance = mGridFrameWidth;
1088  }
1089  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::LineBorder )
1090  {
1091  gridFrameDistance += ( mGridFramePenThickness / 2.0 );
1092  }
1093 
1094  if ( frameBorder == QgsComposerMapGrid::Left )
1095  {
1096  if ( mLeftGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1097  ( coordinateType == Longitude && mLeftGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1098  ( coordinateType == Latitude && mLeftGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1099  {
1100  return;
1101  }
1103  {
1104  gridFrameDistance = 0;
1105  }
1106 
1107  if ( mLeftGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1108  {
1109  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1110  {
1111  gridFrameDistance = 0;
1112  }
1113  if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::Vertical || mLeftGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1114  {
1115  xpos += textHeight + mAnnotationFrameDistance + gridFrameDistance;
1116  ypos += textWidth / 2.0;
1117  rotation = 270;
1118  }
1119  else if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1120  {
1121  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1122  ypos -= textWidth / 2.0;
1123  rotation = 90;
1124  }
1125  else
1126  {
1127  xpos += mAnnotationFrameDistance + gridFrameDistance;
1128  ypos += textHeight / 2.0;
1129  }
1130  }
1131  else if ( mLeftGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //Outside map frame
1132  {
1133  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1134  {
1135  gridFrameDistance = 0;
1136  }
1137  if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::Vertical || mLeftGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1138  {
1139  xpos -= ( mAnnotationFrameDistance + gridFrameDistance );
1140  ypos += textWidth / 2.0;
1141  rotation = 270;
1142  if ( extension )
1143  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1144  }
1145  else if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1146  {
1147  xpos -= textHeight + mAnnotationFrameDistance + gridFrameDistance;
1148  ypos -= textWidth / 2.0;
1149  rotation = 90;
1150  if ( extension )
1151  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1152  }
1153  else
1154  {
1155  xpos -= ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
1156  ypos += textHeight / 2.0;
1157  if ( extension )
1158  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1159  }
1160  }
1161  else
1162  {
1163  return;
1164  }
1165  }
1166  else if ( frameBorder == QgsComposerMapGrid::Right )
1167  {
1168  if ( mRightGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1169  ( coordinateType == Longitude && mRightGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1170  ( coordinateType == Latitude && mRightGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1171  {
1172  return;
1173  }
1175  {
1176  gridFrameDistance = 0;
1177  }
1178 
1179  if ( mRightGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1180  {
1181  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1182  {
1183  gridFrameDistance = 0;
1184  }
1185  if ( mRightGridAnnotationDirection == QgsComposerMapGrid::Vertical )
1186  {
1187  xpos -= mAnnotationFrameDistance + gridFrameDistance;
1188  ypos += textWidth / 2.0;
1189  rotation = 270;
1190  }
1191  else if ( mRightGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending || mRightGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1192  {
1193  xpos -= textHeight + mAnnotationFrameDistance + gridFrameDistance;
1194  ypos -= textWidth / 2.0;
1195  rotation = 90;
1196  }
1197  else
1198  {
1199  xpos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1200  ypos += textHeight / 2.0;
1201  }
1202  }
1203  else if ( mRightGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame )//OutsideMapFrame
1204  {
1205  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1206  {
1207  gridFrameDistance = 0;
1208  }
1209  if ( mRightGridAnnotationDirection == QgsComposerMapGrid::Vertical )
1210  {
1211  xpos += ( textHeight + mAnnotationFrameDistance + gridFrameDistance );
1212  ypos += textWidth / 2.0;
1213  rotation = 270;
1214  if ( extension )
1215  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1216  }
1217  else if ( mRightGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending || mRightGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1218  {
1219  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1220  ypos -= textWidth / 2.0;
1221  rotation = 90;
1222  if ( extension )
1223  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1224  }
1225  else //Horizontal
1226  {
1227  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1228  ypos += textHeight / 2.0;
1229  if ( extension )
1230  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1231  }
1232  }
1233  else
1234  {
1235  return;
1236  }
1237  }
1238  else if ( frameBorder == QgsComposerMapGrid::Bottom )
1239  {
1240  if ( mBottomGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1241  ( coordinateType == Longitude && mBottomGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1242  ( coordinateType == Latitude && mBottomGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1243  {
1244  return;
1245  }
1247  {
1248  gridFrameDistance = 0;
1249  }
1250 
1251  if ( mBottomGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1252  {
1253  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1254  {
1255  gridFrameDistance = 0;
1256  }
1257  if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mBottomGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1258  {
1259  ypos -= mAnnotationFrameDistance + gridFrameDistance;
1260  xpos -= textWidth / 2.0;
1261  }
1262  else if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1263  {
1264  xpos -= textHeight / 2.0;
1265  ypos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1266  rotation = 90;
1267  }
1268  else //Vertical
1269  {
1270  xpos += textHeight / 2.0;
1271  ypos -= mAnnotationFrameDistance + gridFrameDistance;
1272  rotation = 270;
1273  }
1274  }
1275  else if ( mBottomGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //OutsideMapFrame
1276  {
1277  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1278  {
1279  gridFrameDistance = 0;
1280  }
1281  if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mBottomGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1282  {
1283  ypos += ( mAnnotationFrameDistance + textHeight + gridFrameDistance );
1284  xpos -= textWidth / 2.0;
1285  if ( extension )
1286  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1287  }
1288  else if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1289  {
1290  xpos -= textHeight / 2.0;
1291  ypos += gridFrameDistance + mAnnotationFrameDistance;
1292  rotation = 90;
1293  if ( extension )
1294  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1295  }
1296  else //Vertical
1297  {
1298  xpos += textHeight / 2.0;
1299  ypos += ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
1300  rotation = 270;
1301  if ( extension )
1302  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1303  }
1304  }
1305  else
1306  {
1307  return;
1308  }
1309  }
1310  else //top
1311  {
1312  if ( mTopGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1313  ( coordinateType == Longitude && mTopGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1314  ( coordinateType == Latitude && mTopGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1315  {
1316  return;
1317  }
1319  {
1320  gridFrameDistance = 0;
1321  }
1322 
1323  if ( mTopGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1324  {
1325  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1326  {
1327  gridFrameDistance = 0;
1328  }
1329  if ( mTopGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mTopGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1330  {
1331  xpos -= textWidth / 2.0;
1332  ypos += textHeight + mAnnotationFrameDistance + gridFrameDistance;
1333  }
1334  else if ( mTopGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1335  {
1336  xpos -= textHeight / 2.0;
1337  ypos += mAnnotationFrameDistance + gridFrameDistance;
1338  rotation = 90;
1339  }
1340  else //Vertical
1341  {
1342  xpos += textHeight / 2.0;
1343  ypos += textWidth + mAnnotationFrameDistance + gridFrameDistance;
1344  rotation = 270;
1345  }
1346  }
1347  else if ( mTopGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //OutsideMapFrame
1348  {
1349  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1350  {
1351  gridFrameDistance = 0;
1352  }
1353  if ( mTopGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mTopGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1354  {
1355  xpos -= textWidth / 2.0;
1356  ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
1357  if ( extension )
1358  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1359  }
1360  else if ( mTopGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1361  {
1362  xpos -= textHeight / 2.0;
1363  ypos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1364  rotation = 90;
1365  if ( extension )
1366  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1367  }
1368  else //Vertical
1369  {
1370  xpos += textHeight / 2.0;
1371  ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
1372  rotation = 270;
1373  if ( extension )
1374  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1375  }
1376  }
1377  else
1378  {
1379  return;
1380  }
1381  }
1382 
1383  if ( extension || !p )
1384  return;
1385 
1386  drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
1387 }
1388 
1389 void QgsComposerMapGrid::drawAnnotation( QPainter* p, QPointF pos, int rotation, const QString& annotationText ) const
1390 {
1391  if ( !mComposerMap )
1392  {
1393  return;
1394  }
1395 
1396  p->save();
1397  p->translate( pos );
1398  p->rotate( rotation );
1399  QgsComposerUtils::drawText( p, QPointF( 0, 0 ), annotationText, mGridAnnotationFont, mGridAnnotationFontColor );
1400  p->restore();
1401 }
1402 
1403 QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGrid::AnnotationCoordinate coord, QgsExpressionContext &expressionContext ) const
1404 {
1405  //check if we are using degrees (ie, geographic crs)
1406  bool geographic = false;
1407  if ( mCRS.isValid() && mCRS.isGeographic() )
1408  {
1409  geographic = true;
1410  }
1411  else if ( mComposerMap && mComposerMap->composition() )
1412  {
1413  geographic = mComposerMap->crs().isGeographic();
1414  }
1415 
1416  if ( geographic && coord == QgsComposerMapGrid::Longitude &&
1417  ( mGridAnnotationFormat == QgsComposerMapGrid::Decimal || mGridAnnotationFormat == QgsComposerMapGrid::DecimalWithSuffix ) )
1418  {
1419  // wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W"
1420  double wrappedX = fmod( value, 360.0 );
1421  if ( wrappedX > 180.0 )
1422  {
1423  value = wrappedX - 360.0;
1424  }
1425  else if ( wrappedX < -180.0 )
1426  {
1427  value = wrappedX + 360.0;
1428  }
1429  }
1430 
1431  if ( mGridAnnotationFormat == QgsComposerMapGrid::Decimal )
1432  {
1433  return QString::number( value, 'f', mGridAnnotationPrecision );
1434  }
1435  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DecimalWithSuffix )
1436  {
1437  QString hemisphere;
1438 
1439  double coordRounded = qRound( value * pow( 10.0, mGridAnnotationPrecision ) ) / pow( 10.0, mGridAnnotationPrecision );
1440  if ( coord == QgsComposerMapGrid::Longitude )
1441  {
1442  //don't use E/W suffixes if ambiguous (e.g., 180 degrees)
1443  if ( !geographic || ( coordRounded != 180.0 && coordRounded != 0.0 ) )
1444  {
1445  hemisphere = value < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
1446  }
1447  }
1448  else
1449  {
1450  //don't use N/S suffixes if ambiguous (e.g., 0 degrees)
1451  if ( !geographic || coordRounded != 0.0 )
1452  {
1453  hemisphere = value < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
1454  }
1455  }
1456  if ( geographic )
1457  {
1458  //insert degree symbol for geographic coordinates
1459  return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere;
1460  }
1461  else
1462  {
1463  return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + hemisphere;
1464  }
1465  }
1466  else if ( mGridAnnotationFormat == CustomFormat )
1467  {
1468  expressionContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_number" ), value, true ) );
1469  expressionContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), coord == QgsComposerMapGrid::Longitude ? "x" : "y", true ) );
1470  if ( !mGridAnnotationExpression )
1471  {
1472  mGridAnnotationExpression.reset( new QgsExpression( mGridAnnotationExpressionString ) );
1473  mGridAnnotationExpression->prepare( &expressionContext );
1474  }
1475  return mGridAnnotationExpression->evaluate( &expressionContext ).toString();
1476  }
1477 
1478  QgsPoint p;
1479  p.setX( coord == QgsComposerMapGrid::Longitude ? value : 0 );
1480  p.setY( coord == QgsComposerMapGrid::Longitude ? 0 : value );
1481 
1482  QString annotationString;
1483  if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinute )
1484  {
1485  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision );
1486  }
1487  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteNoSuffix )
1488  {
1489  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision, false );
1490  }
1491  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinutePadded )
1492  {
1493  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision, true, true );
1494  }
1495  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecond )
1496  {
1497  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision );
1498  }
1499  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecondNoSuffix )
1500  {
1501  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision, false );
1502  }
1503  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecondPadded )
1504  {
1505  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision, true, true );
1506  }
1507 
1508  QStringList split = annotationString.split( ',' );
1509  if ( coord == QgsComposerMapGrid::Longitude )
1510  {
1511  return split.at( 0 );
1512  }
1513  else
1514  {
1515  if ( split.size() < 2 )
1516  {
1517  return QLatin1String( "" );
1518  }
1519  return split.at( 1 );
1520  }
1521 }
1522 
1523 int QgsComposerMapGrid::xGridLines( QList< QPair< double, QLineF > >& lines ) const
1524 {
1525  lines.clear();
1526  if ( !mComposerMap || mGridIntervalY <= 0.0 )
1527  {
1528  return 1;
1529  }
1530 
1531 
1532  QPolygonF mapPolygon = mComposerMap->transformedMapPolygon();
1533  QRectF mapBoundingRect = mapPolygon.boundingRect();
1534  double gridIntervalY = mGridIntervalY;
1535  double gridOffsetY = mGridOffsetY;
1536  double annotationScale = 1.0;
1537  if ( mGridUnit != MapUnit )
1538  {
1539  mapBoundingRect = mComposerMap->rect();
1540  mapPolygon = QPolygonF( mComposerMap->rect() );
1541  if ( mGridUnit == CM )
1542  {
1543  annotationScale = 0.1;
1544  gridIntervalY *= 10;
1545  gridOffsetY *= 10;
1546  }
1547  }
1548 
1549  //consider to round up to the next step in case the left boundary is > 0
1550  double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
1551  double currentLevel = static_cast< int >(( mapBoundingRect.top() - gridOffsetY ) / gridIntervalY + roundCorrection ) * gridIntervalY + gridOffsetY;
1552 
1553  int gridLineCount = 0;
1554  if ( qgsDoubleNear( mComposerMap->mapRotation(), 0.0 ) || mGridUnit != MapUnit )
1555  {
1556  //no rotation. Do it 'the easy way'
1557 
1558  double yCanvasCoord;
1559  while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1560  {
1561  yCanvasCoord = mComposerMap->rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
1562  lines.push_back( qMakePair( currentLevel * annotationScale, QLineF( 0, yCanvasCoord, mComposerMap->rect().width(), yCanvasCoord ) ) );
1563  currentLevel += gridIntervalY;
1564  gridLineCount++;
1565  }
1566  return 0;
1567  }
1568 
1569  //the four border lines
1570  QVector<QLineF> borderLines;
1571  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1572  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1573  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1574  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1575 
1576  QVector<QPointF> intersectionList; //intersects between border lines and grid lines
1577 
1578  while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1579  {
1580  intersectionList.clear();
1581  QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
1582 
1583  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1584  for ( ; it != borderLines.constEnd(); ++it )
1585  {
1586  QPointF intersectionPoint;
1587  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1588  {
1589  intersectionList.push_back( intersectionPoint );
1590  if ( intersectionList.size() >= 2 )
1591  {
1592  break; //we already have two intersections, skip further tests
1593  }
1594  }
1595  }
1596 
1597  if ( intersectionList.size() >= 2 )
1598  {
1599  lines.push_back( qMakePair( currentLevel, QLineF( mComposerMap->mapToItemCoords( intersectionList.at( 0 ) ), mComposerMap->mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1600  gridLineCount++;
1601  }
1602  currentLevel += gridIntervalY;
1603  }
1604 
1605 
1606  return 0;
1607 }
1608 
1609 int QgsComposerMapGrid::yGridLines( QList< QPair< double, QLineF > >& lines ) const
1610 {
1611  lines.clear();
1612  if ( !mComposerMap || mGridIntervalX <= 0.0 )
1613  {
1614  return 1;
1615  }
1616 
1617  QPolygonF mapPolygon = mComposerMap->transformedMapPolygon();
1618  QRectF mapBoundingRect = mapPolygon.boundingRect();
1619  double gridIntervalX = mGridIntervalX;
1620  double gridOffsetX = mGridOffsetX;
1621  double annotationScale = 1.0;
1622  if ( mGridUnit != MapUnit )
1623  {
1624  mapBoundingRect = mComposerMap->rect();
1625  mapPolygon = QPolygonF( mComposerMap->rect() );
1626  if ( mGridUnit == CM )
1627  {
1628  annotationScale = 0.1;
1629  gridIntervalX *= 10;
1630  gridOffsetX *= 10;
1631  }
1632  }
1633 
1634  //consider to round up to the next step in case the left boundary is > 0
1635  double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
1636  double currentLevel = static_cast< int >(( mapBoundingRect.left() - gridOffsetX ) / gridIntervalX + roundCorrection ) * gridIntervalX + gridOffsetX;
1637 
1638  int gridLineCount = 0;
1639  if ( qgsDoubleNear( mComposerMap->mapRotation(), 0.0 ) || mGridUnit != MapUnit )
1640  {
1641  //no rotation. Do it 'the easy way'
1642  double xCanvasCoord;
1643  while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1644  {
1645  xCanvasCoord = mComposerMap->rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
1646  lines.push_back( qMakePair( currentLevel * annotationScale, QLineF( xCanvasCoord, 0, xCanvasCoord, mComposerMap->rect().height() ) ) );
1647  currentLevel += gridIntervalX;
1648  gridLineCount++;
1649  }
1650  return 0;
1651  }
1652 
1653  //the four border lines
1654  QVector<QLineF> borderLines;
1655  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1656  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1657  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1658  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1659 
1660  QVector<QPointF> intersectionList; //intersects between border lines and grid lines
1661 
1662  while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1663  {
1664  intersectionList.clear();
1665  QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
1666 
1667  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1668  for ( ; it != borderLines.constEnd(); ++it )
1669  {
1670  QPointF intersectionPoint;
1671  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1672  {
1673  intersectionList.push_back( intersectionPoint );
1674  if ( intersectionList.size() >= 2 )
1675  {
1676  break; //we already have two intersections, skip further tests
1677  }
1678  }
1679  }
1680 
1681  if ( intersectionList.size() >= 2 )
1682  {
1683  lines.push_back( qMakePair( currentLevel, QLineF( mComposerMap->mapToItemCoords( intersectionList.at( 0 ) ), mComposerMap->mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1684  gridLineCount++;
1685  }
1686  currentLevel += gridIntervalX;
1687  }
1688 
1689  return 0;
1690 }
1691 
1692 int QgsComposerMapGrid::xGridLinesCrsTransform( const QgsRectangle& bbox, const QgsCoordinateTransform& t, QList< QPair< double, QPolygonF > >& lines ) const
1693 {
1694  lines.clear();
1695  if ( !mComposerMap || mGridIntervalY <= 0.0 )
1696  {
1697  return 1;
1698  }
1699 
1700  double roundCorrection = bbox.yMaximum() > 0 ? 1.0 : 0.0;
1701  double currentLevel = static_cast< int >(( bbox.yMaximum() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY;
1702 
1703  double minX = bbox.xMinimum();
1704  double maxX = bbox.xMaximum();
1705  double step = ( maxX - minX ) / 20;
1706 
1707  bool crosses180 = false;
1708  bool crossed180 = false;
1709  if ( mCRS.isGeographic() && ( minX > maxX ) )
1710  {
1711  //handle 180 degree longitude crossover
1712  crosses180 = true;
1713  step = ( maxX + 360.0 - minX ) / 20;
1714  }
1715 
1716  if ( qgsDoubleNear( step, 0.0 ) )
1717  return 1;
1718 
1719  int gridLineCount = 0;
1720  while ( currentLevel >= bbox.yMinimum() && gridLineCount < MAX_GRID_LINES )
1721  {
1722  QPolygonF gridLine;
1723  double currentX = minX;
1724  bool cont = true;
1725  while ( cont )
1726  {
1727  if (( !crosses180 || crossed180 ) && ( currentX > maxX ) )
1728  {
1729  cont = false;
1730  }
1731 
1732  try
1733  {
1734  QgsPoint mapPoint = t.transform( currentX, currentLevel ); //transform back to map crs
1735  gridLine.append( mComposerMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) ); //transform back to composer coords
1736  }
1737  catch ( QgsCsException & cse )
1738  {
1739  Q_UNUSED( cse );
1740  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
1741  }
1742 
1743  currentX += step;
1744  if ( crosses180 && currentX > 180.0 )
1745  {
1746  currentX -= 360.0;
1747  crossed180 = true;
1748  }
1749  }
1750  crossed180 = false;
1751 
1752  QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
1753  QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1754  for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1755  {
1756  if ( !( *lineIt ).isEmpty() )
1757  {
1758  lines.append( qMakePair( currentLevel, *lineIt ) );
1759  gridLineCount++;
1760  }
1761  }
1762  currentLevel -= mGridIntervalY;
1763  }
1764 
1765  return 0;
1766 }
1767 
1768 int QgsComposerMapGrid::yGridLinesCrsTransform( const QgsRectangle& bbox, const QgsCoordinateTransform& t, QList< QPair< double, QPolygonF > >& lines ) const
1769 {
1770  lines.clear();
1771  if ( !mComposerMap || mGridIntervalX <= 0.0 )
1772  {
1773  return 1;
1774  }
1775 
1776  double roundCorrection = bbox.xMinimum() > 0 ? 1.0 : 0.0;
1777  double currentLevel = static_cast< int >(( bbox.xMinimum() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
1778 
1779  double minY = bbox.yMinimum();
1780  double maxY = bbox.yMaximum();
1781  double step = ( maxY - minY ) / 20;
1782 
1783  if ( qgsDoubleNear( step, 0.0 ) )
1784  return 1;
1785 
1786  bool crosses180 = false;
1787  bool crossed180 = false;
1788  if ( mCRS.isGeographic() && ( bbox.xMinimum() > bbox.xMaximum() ) )
1789  {
1790  //handle 180 degree longitude crossover
1791  crosses180 = true;
1792  }
1793 
1794  int gridLineCount = 0;
1795  while (( currentLevel <= bbox.xMaximum() || ( crosses180 && !crossed180 ) ) && gridLineCount < MAX_GRID_LINES )
1796  {
1797  QPolygonF gridLine;
1798  double currentY = minY;
1799  bool cont = true;
1800  while ( cont )
1801  {
1802  if ( currentY > maxY )
1803  {
1804  cont = false;
1805  }
1806  try
1807  {
1808  //transform back to map crs
1809  QgsPoint mapPoint = t.transform( currentLevel, currentY );
1810  //transform back to composer coords
1811  gridLine.append( mComposerMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) );
1812  }
1813  catch ( QgsCsException & cse )
1814  {
1815  Q_UNUSED( cse );
1816  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
1817  }
1818 
1819  currentY += step;
1820  }
1821  //clip grid line to map polygon
1822  QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
1823  QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1824  for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1825  {
1826  if ( !( *lineIt ).isEmpty() )
1827  {
1828  lines.append( qMakePair( currentLevel, *lineIt ) );
1829  gridLineCount++;
1830  }
1831  }
1832  currentLevel += mGridIntervalX;
1833  if ( crosses180 && currentLevel > 180.0 )
1834  {
1835  currentLevel -= 360.0;
1836  crossed180 = true;
1837  }
1838  }
1839 
1840  return 0;
1841 }
1842 
1843 void QgsComposerMapGrid::sortGridLinesOnBorders( const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines, QMap< double, double >& leftFrameEntries,
1844  QMap< double, double >& rightFrameEntries, QMap< double, double >& topFrameEntries, QMap< double, double >& bottomFrameEntries ) const
1845 {
1846  QList< QgsMapAnnotation > borderPositions;
1847  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
1848  for ( ; it != hLines.constEnd(); ++it )
1849  {
1850  QgsMapAnnotation p1;
1851  p1.coordinate = it->first;
1852  p1.itemPosition = it->second.p1();
1853  p1.coordinateType = QgsComposerMapGrid::Latitude;
1854  borderPositions << p1;
1855 
1856  QgsMapAnnotation p2;
1857  p2.coordinate = it->first;
1858  p2.itemPosition = it->second.p2();
1859  p2.coordinateType = QgsComposerMapGrid::Latitude;
1860  borderPositions << p2;
1861  }
1862  it = vLines.constBegin();
1863  for ( ; it != vLines.constEnd(); ++it )
1864  {
1865  QgsMapAnnotation p1;
1866  p1.coordinate = it->first;
1867  p1.itemPosition = it->second.p1();
1868  p1.coordinateType = QgsComposerMapGrid::Longitude;
1869  borderPositions << p1;
1870 
1871  QgsMapAnnotation p2;
1872  p2.coordinate = it->first;
1873  p2.itemPosition = it->second.p2();
1874  p2.coordinateType = QgsComposerMapGrid::Longitude;
1875  borderPositions << p2;
1876  }
1877 
1878  QList< QgsMapAnnotation >::const_iterator bIt = borderPositions.constBegin();
1879  for ( ; bIt != borderPositions.constEnd(); ++bIt )
1880  {
1881  QgsComposerMapGrid::BorderSide frameBorder = borderForLineCoord( bIt->itemPosition, bIt->coordinateType );
1882  if ( frameBorder == QgsComposerMapGrid::Left && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Left ) )
1883  {
1884  leftFrameEntries.insert( bIt->itemPosition.y(), bIt->coordinate );
1885  }
1886  else if ( frameBorder == QgsComposerMapGrid::Right && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Right ) )
1887  {
1888  rightFrameEntries.insert( bIt->itemPosition.y(), bIt->coordinate );
1889  }
1890  else if ( frameBorder == QgsComposerMapGrid::Top && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Top ) )
1891  {
1892  topFrameEntries.insert( bIt->itemPosition.x(), bIt->coordinate );
1893  }
1894  else if ( frameBorder == QgsComposerMapGrid::Bottom && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Bottom ) )
1895  {
1896  bottomFrameEntries.insert( bIt->itemPosition.x(), bIt->coordinate );
1897  }
1898  }
1899 }
1900 
1901 bool QgsComposerMapGrid::shouldShowDivisionForSide( QgsComposerMapGrid::AnnotationCoordinate coordinate, QgsComposerMapGrid::BorderSide side ) const
1902 {
1903  switch ( side )
1904  {
1906  return shouldShowDivisionForDisplayMode( coordinate, mLeftFrameDivisions );
1908  return shouldShowDivisionForDisplayMode( coordinate, mRightFrameDivisions );
1910  return shouldShowDivisionForDisplayMode( coordinate, mTopFrameDivisions );
1912  default: //prevent warnings
1913  return shouldShowDivisionForDisplayMode( coordinate, mBottomFrameDivisions );
1914  }
1915 }
1916 
1917 bool QgsComposerMapGrid::shouldShowDivisionForDisplayMode( QgsComposerMapGrid::AnnotationCoordinate coordinate, QgsComposerMapGrid::DisplayMode mode ) const
1918 {
1919  return mode == QgsComposerMapGrid::ShowAll
1920  || ( mode == QgsComposerMapGrid::LatitudeOnly && coordinate == QgsComposerMapGrid::Latitude )
1921  || ( mode == QgsComposerMapGrid::LongitudeOnly && coordinate == QgsComposerMapGrid::Longitude );
1922 }
1923 
1924 bool sortByDistance( QPair<qreal , QgsComposerMapGrid::BorderSide> a, QPair<qreal , QgsComposerMapGrid::BorderSide> b )
1925 {
1926  return a.first < b.first;
1927 }
1928 
1929 QgsComposerMapGrid::BorderSide QgsComposerMapGrid::borderForLineCoord( QPointF p, const AnnotationCoordinate coordinateType ) const
1930 {
1931  if ( !mComposerMap )
1932  {
1933  return QgsComposerMapGrid::Left;
1934  }
1935 
1936  double tolerance = qMax( mComposerMap->hasFrame() ? mComposerMap->pen().widthF() : 0.0, 1.0 );
1937 
1938  //check for corner coordinates
1939  if (( p.y() <= tolerance && p.x() <= tolerance ) // top left
1940  || ( p.y() <= tolerance && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //top right
1941  || ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() <= tolerance ) //bottom left
1942  || ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //bottom right
1943  )
1944  {
1945  //coordinate is in corner - fall back to preferred side for coordinate type
1946  if ( coordinateType == QgsComposerMapGrid::Latitude )
1947  {
1948  if ( p.x() <= tolerance )
1949  {
1950  return QgsComposerMapGrid::Left;
1951  }
1952  else
1953  {
1955  }
1956  }
1957  else
1958  {
1959  if ( p.y() <= tolerance )
1960  {
1961  return QgsComposerMapGrid::Top;
1962  }
1963  else
1964  {
1966  }
1967  }
1968  }
1969 
1970  //otherwise, guess side based on closest map side to point
1971  QList< QPair<qreal, QgsComposerMapGrid::BorderSide > > distanceToSide;
1972  distanceToSide << qMakePair( p.x(), QgsComposerMapGrid::Left );
1973  distanceToSide << qMakePair( mComposerMap->rect().width() - p.x(), QgsComposerMapGrid::Right );
1974  distanceToSide << qMakePair( p.y(), QgsComposerMapGrid::Top );
1975  distanceToSide << qMakePair( mComposerMap->rect().height() - p.y(), QgsComposerMapGrid::Bottom );
1976 
1977  std::sort( distanceToSide.begin(), distanceToSide.end(), sortByDistance );
1978  return distanceToSide.at( 0 ).second;
1979 }
1980 
1982 {
1983  delete mGridLineSymbol;
1984  mGridLineSymbol = symbol;
1985 }
1986 
1988 {
1989  delete mGridMarkerSymbol;
1990  mGridMarkerSymbol = symbol;
1991 }
1992 
1994 {
1995  switch ( border )
1996  {
1998  mLeftGridAnnotationDisplay = display;
1999  break;
2001  mRightGridAnnotationDisplay = display;
2002  break;
2004  mTopGridAnnotationDisplay = display;
2005  break;
2007  mBottomGridAnnotationDisplay = display;
2008  break;
2009  default:
2010  return;
2011  }
2012 
2013  if ( mComposerMap )
2014  {
2016  mComposerMap->update();
2017  }
2018 }
2019 
2021 {
2022  switch ( border )
2023  {
2025  return mLeftGridAnnotationDisplay;
2027  return mRightGridAnnotationDisplay;
2029  return mTopGridAnnotationDisplay;
2031  default:
2032  return mBottomGridAnnotationDisplay;
2033  }
2034 }
2035 
2037 {
2038  double top = 0.0;
2039  double right = 0.0;
2040  double bottom = 0.0;
2041  double left = 0.0;
2042  calculateMaxExtension( top, right, bottom, left );
2043  return qMax( qMax( qMax( top, right ), bottom ), left );
2044 }
2045 
2046 void QgsComposerMapGrid::calculateMaxExtension( double& top, double& right, double& bottom, double& left )
2047 {
2048  top = 0.0;
2049  right = 0.0;
2050  bottom = 0.0;
2051  left = 0.0;
2052 
2053  if ( !mComposerMap || !mEnabled )
2054  {
2055  return;
2056  }
2057 
2058  //setup render context
2060  QgsExpressionContext expressionContext = createExpressionContext();
2061  context.setExpressionContext( expressionContext );
2062 
2063  GridExtension extension;
2064 
2065  //collect grid lines
2066  QList< QPair< double, QLineF > > verticalLines;
2067  QList< QPair< double, QLineF > > horizontalLines;
2068  if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != mComposerMap->crs() )
2069  {
2070  drawGridCrsTransform( context, 0, horizontalLines, verticalLines, false );
2071  }
2072  else
2073  {
2074  drawGridNoTransform( context, 0, horizontalLines, verticalLines, false );
2075  }
2076 
2077  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame )
2078  {
2079  drawGridFrame( nullptr, horizontalLines, verticalLines, &extension );
2080  }
2081 
2082  if ( mShowGridAnnotation )
2083  {
2084  drawCoordinateAnnotations( nullptr, horizontalLines, verticalLines, context.expressionContext(), &extension );
2085  }
2086 
2087  top = extension.top;
2088  right = extension.right;
2089  bottom = extension.bottom;
2090  left = extension.left;
2091 }
2092 
2094 {
2095  if ( unit == mGridUnit )
2096  {
2097  return;
2098  }
2099  mGridUnit = unit;
2100  mTransformDirty = true;
2101 }
2102 
2103 void QgsComposerMapGrid::setIntervalX( const double interval )
2104 {
2105  if ( qgsDoubleNear( interval, mGridIntervalX ) )
2106  {
2107  return;
2108  }
2109  mGridIntervalX = interval;
2110  mTransformDirty = true;
2111 }
2112 
2113 void QgsComposerMapGrid::setIntervalY( const double interval )
2114 {
2115  if ( qgsDoubleNear( interval, mGridIntervalY ) )
2116  {
2117  return;
2118  }
2119  mGridIntervalY = interval;
2120  mTransformDirty = true;
2121 }
2122 
2123 void QgsComposerMapGrid::setOffsetX( const double offset )
2124 {
2125  if ( qgsDoubleNear( offset, mGridOffsetX ) )
2126  {
2127  return;
2128  }
2129  mGridOffsetX = offset;
2130  mTransformDirty = true;
2131 }
2132 
2133 void QgsComposerMapGrid::setOffsetY( const double offset )
2134 {
2135  if ( qgsDoubleNear( offset, mGridOffsetY ) )
2136  {
2137  return;
2138  }
2139  mGridOffsetY = offset;
2140  mTransformDirty = true;
2141 }
2142 
2144 {
2145  if ( style == mGridStyle )
2146  {
2147  return;
2148  }
2149  mGridStyle = style;
2150  mTransformDirty = true;
2151 }
2152 
2154 {
2155  switch ( border )
2156  {
2158  mLeftGridAnnotationDirection = direction;
2159  break;
2161  mRightGridAnnotationDirection = direction;
2162  break;
2164  mTopGridAnnotationDirection = direction;
2165  break;
2167  mBottomGridAnnotationDirection = direction;
2168  break;
2169  default:
2170  return;
2171  }
2172 
2173  if ( mComposerMap )
2174  {
2176  mComposerMap->update();
2177  }
2178 }
2179 
2180 void QgsComposerMapGrid::setFrameSideFlags( FrameSideFlags flags )
2181 {
2182  mGridFrameSides = flags;
2183 }
2184 
2186 {
2187  if ( on )
2188  mGridFrameSides |= flag;
2189  else
2190  mGridFrameSides &= ~flag;
2191 }
2192 
2193 QgsComposerMapGrid::FrameSideFlags QgsComposerMapGrid::frameSideFlags() const
2194 {
2195  return mGridFrameSides;
2196 }
2197 
2199 {
2201  context.appendScope( new QgsExpressionContextScope( tr( "Grid" ) ) );
2202  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_number" ), 0, true ) );
2203  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "grid_axis" ), "x", true ) );
2204  context.setHighlightedVariables( QStringList() << QStringLiteral( "grid_number" ) << QStringLiteral( "grid_axis" ) );
2205  return context;
2206 }
2207 
2209 {
2210  return mGridFrameSides.testFlag( flag );
2211 }
2212 
2214 {
2215  mLeftGridAnnotationDirection = direction;
2216  mRightGridAnnotationDirection = direction;
2217  mTopGridAnnotationDirection = direction;
2218  mBottomGridAnnotationDirection = direction;
2219 }
2220 
2222 {
2223  switch ( border )
2224  {
2226  mLeftGridAnnotationPosition = position;
2227  break;
2229  mRightGridAnnotationPosition = position;
2230  break;
2232  mTopGridAnnotationPosition = position;
2233  break;
2235  mBottomGridAnnotationPosition = position;
2236  break;
2237  default:
2238  return;
2239  }
2240 
2241  if ( mComposerMap )
2242  {
2244  mComposerMap->update();
2245  }
2246 }
2247 
2249 {
2250  switch ( border )
2251  {
2253  return mLeftGridAnnotationPosition;
2255  return mRightGridAnnotationPosition;
2257  return mTopGridAnnotationPosition;
2259  default:
2260  return mBottomGridAnnotationPosition;
2261  }
2262 }
2263 
2265 {
2266  if ( !mComposerMap )
2267  {
2268  return mLeftGridAnnotationDirection;
2269  }
2270 
2271  switch ( border )
2272  {
2274  return mLeftGridAnnotationDirection;
2276  return mRightGridAnnotationDirection;
2278  return mTopGridAnnotationDirection;
2280  default:
2281  return mBottomGridAnnotationDirection;
2282  }
2283 }
2284 
2286 {
2287  switch ( border )
2288  {
2290  mLeftFrameDivisions = divisions;
2291  break;
2293  mRightFrameDivisions = divisions;
2294  break;
2296  mTopFrameDivisions = divisions;
2297  break;
2299  mBottomFrameDivisions = divisions;
2300  break;
2301  default:
2302  return;
2303  }
2304 
2305  if ( mComposerMap )
2306  {
2307  mComposerMap->update();
2308  }
2309 }
2310 
2312 {
2313  switch ( border )
2314  {
2316  return mLeftFrameDivisions;
2318  return mRightFrameDivisions;
2320  return mTopFrameDivisions;
2322  default:
2323  return mBottomFrameDivisions;
2324  }
2325 }
2326 
2327 int QgsComposerMapGrid::crsGridParams( QgsRectangle& crsRect, QgsCoordinateTransform& inverseTransform ) const
2328 {
2329  if ( !mComposerMap )
2330  {
2331  return 1;
2332  }
2333 
2334  try
2335  {
2336  QgsCoordinateTransform tr( mComposerMap->crs(), mCRS );
2337  QPolygonF mapPolygon = mComposerMap->transformedMapPolygon();
2338  QRectF mbr = mapPolygon.boundingRect();
2339  QgsRectangle mapBoundingRect( mbr.left(), mbr.bottom(), mbr.right(), mbr.top() );
2340 
2341 
2342  if ( mCRS.isGeographic() )
2343  {
2344  //handle crossing the 180 degree longitude line
2345  QgsPoint lowerLeft( mapBoundingRect.xMinimum(), mapBoundingRect.yMinimum() );
2346  QgsPoint upperRight( mapBoundingRect.xMaximum(), mapBoundingRect.yMaximum() );
2347 
2348  lowerLeft = tr.transform( lowerLeft.x(), lowerLeft.y() );
2349  upperRight = tr.transform( upperRight.x(), upperRight.y() );
2350 
2351  if ( lowerLeft.x() > upperRight.x() )
2352  {
2353  //we've crossed the line
2354  crsRect = tr.transformBoundingBox( mapBoundingRect, QgsCoordinateTransform::ForwardTransform, true );
2355  }
2356  else
2357  {
2358  //didn't cross the line
2359  crsRect = tr.transformBoundingBox( mapBoundingRect );
2360  }
2361  }
2362  else
2363  {
2364  crsRect = tr.transformBoundingBox( mapBoundingRect );
2365  }
2366 
2367  inverseTransform.setSourceCrs( mCRS );
2368  inverseTransform.setDestinationCrs( mComposerMap->crs() );
2369  }
2370  catch ( QgsCsException & cse )
2371  {
2372  Q_UNUSED( cse );
2373  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
2374  return 1;
2375  }
2376  return 0;
2377 }
2378 
2379 QList<QPolygonF> QgsComposerMapGrid::trimLinesToMap( const QPolygonF& line, const QgsRectangle& rect )
2380 {
2381  QgsGeometry lineGeom = QgsGeometry::fromQPolygonF( line );
2382  QgsGeometry rectGeom = QgsGeometry::fromRect( rect );
2383 
2384  QgsGeometry intersected = lineGeom.intersection( rectGeom );
2385  QList<QgsGeometry> intersectedParts = intersected.asGeometryCollection();
2386 
2387  QList<QPolygonF> trimmedLines;
2388  QList<QgsGeometry>::const_iterator geomIt = intersectedParts.constBegin();
2389  for ( ; geomIt != intersectedParts.constEnd(); ++geomIt )
2390  {
2391  trimmedLines << ( *geomIt ).asQPolygonF();
2392  }
2393  return trimmedLines;
2394 }
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...
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.
QString toDegreesMinutesSeconds(int precision, const bool useSuffix=true, const bool padded=false) const
Return a string representation as degrees minutes seconds.
Definition: qgspoint.cpp:179
static QgsGeometry fromPolyline(const QgsPolyline &polyline)
Creates a new geometry from a QgsPolyline object.
virtual QgsExpressionContext createExpressionContext() const
Creates an expression context relating to the objects&#39; current state.
virtual QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the objects&#39; current state.
GridStyle
Grid drawing style.
const QgsComposerMapGrid * constGrid(const QString &gridId) const
Returns a const reference to a grid within the stack.
void addItem(QgsComposerMapItem *item)
Adds a new map item to the stack and takes ownership of the item.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry)...
void setOffsetY(const double offset)
Sets the offset for grid lines in the y-direction.
Draw line crosses at intersections of grid lines.
static QDomElement saveSymbol(const QString &symbolName, QgsSymbol *symbol, QDomDocument &doc)
Draw annotations horizontally.
bool readXml(const QDomElement &elem, const QDomDocument &doc) override
Sets the grid stack&#39;s state from a DOM document.
Degree/minutes, with minutes using leading zeros where required.
static QgsLineSymbol * createSimple(const QgsStringMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties. ...
Definition: qgssymbol.cpp:1041
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
FrameStyle
Style for grid frame.
QVector< QgsPoint > QgsPolyline
Polyline is represented as a vector of points.
Definition: qgsgeometry.h:47
QgsComposerMapItem(const QString &name, QgsComposerMap *map)
Constructor for QgsComposerMapItem.
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Definition: qgssymbol.cpp:1627
Simple solid line frame.
virtual bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores map item state in DOM element.
static void drawText(QPainter *painter, QPointF pos, const QString &text, const QFont &font, const QColor &color=QColor())
Draws text on a painter at a specific position, taking care of composer specific issues (calculation ...
Draw annotations outside the map frame.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:79
double maxExtension()
Calculates the maximum distance the grid extends beyond the QgsComposerMap&#39;s item rect...
static double fontAscentMM(const QFont &font)
Calculate font ascent in millimeters, including workarounds for QT font rendering issues...
Tick markers drawn outside map frame.
Degree/minutes, use NSEW suffix.
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
AnnotationDirection
Direction of grid annotations.
AnnotationPosition annotationPosition(const BorderSide border) const
Gets the position for the grid annotations on a specified side of the map frame.
void removeGrid(const QString &gridId)
Removes a grid from the stack and deletes the corresponding QgsComposerMapGrid.
void setGridLineColor(const QColor &color)
Sets color of grid lines.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:330
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:198
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
An item which is drawn inside a QgsComposerMap, e.g., a grid or map overview.
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:378
bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores grid state in DOM element.
void calculateMaxExtension(double &top, double &right, double &bottom, double &left)
Calculates the maximum distance the grid extends beyond the QgsComposerMap&#39;s item rect...
Show latitude/y annotations/divisions only.
QString what() const
Definition: qgsexception.h:38
void setFrameSideFlag(const FrameSideFlag flag, bool on=true)
Sets whether the grid frame is drawn for a certain side of the map item.
static QString encodeColor(const QColor &color)
static QPointF pointOnLineWithDistance(QPointF startPoint, QPointF directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
void renderPoint(QPointF point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Definition: qgssymbol.cpp:1418
Black/white pattern.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
static double fontHeightCharacterMM(const QFont &font, QChar character)
Calculate font height in millimeters of a single character, including workarounds for QT font renderi...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void updateBoundingRect()
Updates the bounding rect of this item. Call this function before doing any changes related to annota...
QString toDegreesMinutes(int precision, const bool useSuffix=true, const bool padded=false) const
Return a string representation as degrees minutes.
Definition: qgspoint.cpp:293
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:1494
void removeItem(const QString &itemId)
Removes an item from the stack and deletes the corresponding QgsComposerMapItem.
const QgsComposerMapItem * constItem(const QString &itemId) const
Returns a const reference to an item within the stack.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:186
QList< QgsGeometry > asGeometryCollection() const
Return contents of the geometry as a list of geometries.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
DisplayMode
Display settings for grid annotations and frames.
void moveItemUp(const QString &itemId)
Moves an item up the stack, causing it to be rendered above other items.
AnnotationPosition
Position for grid annotations.
void setUnits(const GridUnit unit)
Sets the units to use for grid measurements such as the interval and offset for grid lines...
No grid lines over the map, only draw frame and annotations.
GridStyle style() const
Gets the grid&#39;s style, which controls how the grid is drawn over the map&#39;s contents.
Annotations follow the boundary direction.
FrameSideFlags frameSideFlags() const
Returns the flags which control which sides of the map item the grid frame is drawn on...
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
FrameSideFlag
Flags for controlling which side of the map a frame is drawn on.
Decimal degrees, use NSEW suffix.
An individual grid which is drawn above the map content in a QgsComposerMap.
QgsComposerMapGrid & operator[](int idx)
Returns a reference to a grid within the stack.
bool isGeographic() const
Returns whether the CRS is a geographic CRS (using lat/lon coordinates)
QgsComposerMap * mComposerMap
Associated composer map.
AnnotationDirection annotationDirection(const BorderSide border) const
Gets the direction for drawing frame annotations.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the CRS for the grid.
bool usesAdvancedEffects() const override
Returns true if the item is drawn using advanced effects, such as blend modes.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setGridLineWidth(const double width)
Sets width of grid lines.
A class to represent a point.
Definition: qgspoint.h:143
Draw annotations inside the map frame.
QgsComposerMapGridStack(QgsComposerMap *map)
Constructor for QgsComposerMapGridStack.
Object representing map window.
void moveItemDown(const QString &itemId)
Moves an item up the stack, causing it to be rendered above other items.
void setFrameSideFlags(QgsComposerMapGrid::FrameSideFlags flags)
Sets flags for grid frame sides.
Coordinate is a longitude value.
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used for drawing grid lines.
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h: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...
QgsComposition * mComposition
Degree/minutes/seconds, with minutes using leading zeros where required.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Contains information about the context of a rendering operation.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
DisplayMode frameDivisions(const BorderSide border) const
Gets the type of grid divisions which are used for frames on a specified side of the map...
const QgsComposition * composition() const
Returns the composition the item is attached to.
QList< QgsComposerMapGrid *> asList() const
Returns a list of QgsComposerMapGrids contained by the stack.
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
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
Tick markers drawn both inside and outside the map frame.
void setFrameDivisions(const DisplayMode divisions, const BorderSide border)
Sets what type of grid divisions should be used for frames on a specified side of the map...
void moveGridUp(const QString &gridId)
Moves a grid up the stack, causing it to be rendered above other grids.
Degree/minutes, use - for S/W coordinates.
Custom expression-based format.
double maxGridExtension() const
Calculates the maximum distance grids within the stack extend beyond the QgsComposerMap&#39;s item rect...
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user...
double yMaximum() const
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:1030
Custom exception class for Coordinate Reference System related exceptions.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
bool testFrameSideFlag(const FrameSideFlag flag) const
Tests whether the grid frame should be drawn on a specified side of the map item. ...
Draw markers at intersections of grid lines.
void moveGridDown(const QString &gridId)
Moves a grid down the stack, causing it to be rendered below other grids.
void removeItems()
Clears the item stack and deletes all QgsComposerMapItems contained by the stack. ...
double mapRotation(QgsComposerObject::PropertyValueType valueType=QgsComposerObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the composer item.
Draw annotations vertically, ascending.
Decimal degrees, use - for S/W coordinates.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
Degree/minutes/seconds, use - for S/W coordinates.
static QgsRenderContext createRenderContextForComposition(QgsComposition *composition, QPainter *painter)
Creates a render context suitable for the specified composition and painter destination.
Grid units in centimeters.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:399
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
GridUnit
Unit for grid values.
void setIntervalX(const double interval)
Sets the interval between grid lines in the x-direction.
Show longitude/x annotations/divisions only.
QgsCoordinateReferenceSystem crs() const
Retrieves the CRS for the grid.
QgsComposerMapGrid * grid(const QString &gridId) const
Returns a reference to a grid within the stack.
QPolygonF transformedMapPolygon() const
Returns extent that considers rotation and shift with mOffsetX / mOffsetY.
Grid units follow map units.
void setOffsetX(const double offset)
Sets the offset for grid lines in the x-direction.
AnnotationCoordinate
Annotation coordinate type.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
static QColor decodeColor(const QString &str)
Tick markers drawn inside map frame.
QgsComposerMap * mComposerMap
void setColor(const QColor &color)
Definition: qgssymbol.cpp:419
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Coordinate is a latitude value.
double x
Definition: qgspoint.h:147