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