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