QGIS API Documentation  2.13.0-Master
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()->setVariable( "grid_number", value );
1506  expressionContext.lastScope()->setVariable( "grid_axis", coord == QgsComposerMapGrid::Longitude ? "x" : "y" );
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  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
1777  }
1778 
1779  currentX += step;
1780  if ( crosses180 && currentX > 180.0 )
1781  {
1782  currentX -= 360.0;
1783  crossed180 = true;
1784  }
1785  }
1786  crossed180 = false;
1787 
1788  QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
1789  QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1790  for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1791  {
1792  if ( !( *lineIt ).isEmpty() )
1793  {
1794  lines.append( qMakePair( currentLevel, *lineIt ) );
1795  gridLineCount++;
1796  }
1797  }
1798  currentLevel -= mGridIntervalY;
1799  }
1800 
1801  return 0;
1802 }
1803 
1804 int QgsComposerMapGrid::yGridLinesCRSTransform( const QgsRectangle& bbox, const QgsCoordinateTransform& t, QList< QPair< double, QPolygonF > >& lines ) const
1805 {
1806  lines.clear();
1807  if ( !mComposerMap || mGridIntervalX <= 0.0 )
1808  {
1809  return 1;
1810  }
1811 
1812  double roundCorrection = bbox.xMinimum() > 0 ? 1.0 : 0.0;
1813  double currentLevel = static_cast< int >(( bbox.xMinimum() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
1814 
1815  double minY = bbox.yMinimum();
1816  double maxY = bbox.yMaximum();
1817  double step = ( maxY - minY ) / 20;
1818 
1819  if ( qgsDoubleNear( step, 0.0 ) )
1820  return 1;
1821 
1822  bool crosses180 = false;
1823  bool crossed180 = false;
1824  if ( mCRS.geographicFlag() && ( bbox.xMinimum() > bbox.xMaximum() ) )
1825  {
1826  //handle 180 degree longitude crossover
1827  crosses180 = true;
1828  }
1829 
1830  int gridLineCount = 0;
1831  while (( currentLevel <= bbox.xMaximum() || ( crosses180 && !crossed180 ) ) && gridLineCount < MAX_GRID_LINES )
1832  {
1833  QPolygonF gridLine;
1834  double currentY = minY;
1835  bool cont = true;
1836  while ( cont )
1837  {
1838  if ( currentY > maxY )
1839  {
1840  cont = false;
1841  }
1842  try
1843  {
1844  //transform back to map crs
1845  QgsPoint mapPoint = t.transform( currentLevel, currentY );
1846  //transform back to composer coords
1847  gridLine.append( mComposerMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) );
1848  }
1849  catch ( QgsCsException & cse )
1850  {
1851  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
1852  }
1853 
1854  currentY += step;
1855  }
1856  //clip grid line to map polygon
1857  QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
1858  QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1859  for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1860  {
1861  if ( !( *lineIt ).isEmpty() )
1862  {
1863  lines.append( qMakePair( currentLevel, *lineIt ) );
1864  gridLineCount++;
1865  }
1866  }
1867  currentLevel += mGridIntervalX;
1868  if ( crosses180 && currentLevel > 180.0 )
1869  {
1870  currentLevel -= 360.0;
1871  crossed180 = true;
1872  }
1873  }
1874 
1875  return 0;
1876 }
1877 
1878 void QgsComposerMapGrid::sortGridLinesOnBorders( const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines, QMap< double, double >& leftFrameEntries,
1879  QMap< double, double >& rightFrameEntries, QMap< double, double >& topFrameEntries, QMap< double, double >& bottomFrameEntries ) const
1880 {
1881  QList< QgsMapAnnotation > borderPositions;
1882  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
1883  for ( ; it != hLines.constEnd(); ++it )
1884  {
1885  QgsMapAnnotation p1;
1886  p1.coordinate = it->first;
1887  p1.itemPosition = it->second.p1();
1888  p1.coordinateType = QgsComposerMapGrid::Latitude;
1889  borderPositions << p1;
1890 
1891  QgsMapAnnotation p2;
1892  p2.coordinate = it->first;
1893  p2.itemPosition = it->second.p2();
1894  p2.coordinateType = QgsComposerMapGrid::Latitude;
1895  borderPositions << p2;
1896  }
1897  it = vLines.constBegin();
1898  for ( ; it != vLines.constEnd(); ++it )
1899  {
1900  QgsMapAnnotation p1;
1901  p1.coordinate = it->first;
1902  p1.itemPosition = it->second.p1();
1903  p1.coordinateType = QgsComposerMapGrid::Longitude;
1904  borderPositions << p1;
1905 
1906  QgsMapAnnotation p2;
1907  p2.coordinate = it->first;
1908  p2.itemPosition = it->second.p2();
1909  p2.coordinateType = QgsComposerMapGrid::Longitude;
1910  borderPositions << p2;
1911  }
1912 
1913  QList< QgsMapAnnotation >::const_iterator bIt = borderPositions.constBegin();
1914  for ( ; bIt != borderPositions.constEnd(); ++bIt )
1915  {
1916  QgsComposerMapGrid::BorderSide frameBorder = borderForLineCoord( bIt->itemPosition, bIt->coordinateType );
1917  if ( frameBorder == QgsComposerMapGrid::Left && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Left ) )
1918  {
1919  leftFrameEntries.insert( bIt->itemPosition.y(), bIt->coordinate );
1920  }
1921  else if ( frameBorder == QgsComposerMapGrid::Right && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Right ) )
1922  {
1923  rightFrameEntries.insert( bIt->itemPosition.y(), bIt->coordinate );
1924  }
1925  else if ( frameBorder == QgsComposerMapGrid::Top && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Top ) )
1926  {
1927  topFrameEntries.insert( bIt->itemPosition.x(), bIt->coordinate );
1928  }
1929  else if ( frameBorder == QgsComposerMapGrid::Bottom && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Bottom ) )
1930  {
1931  bottomFrameEntries.insert( bIt->itemPosition.x(), bIt->coordinate );
1932  }
1933  }
1934 }
1935 
1936 bool QgsComposerMapGrid::shouldShowDivisionForSide( QgsComposerMapGrid::AnnotationCoordinate coordinate, QgsComposerMapGrid::BorderSide side ) const
1937 {
1938  switch ( side )
1939  {
1941  return shouldShowDivisionForDisplayMode( coordinate, mLeftFrameDivisions );
1943  return shouldShowDivisionForDisplayMode( coordinate, mRightFrameDivisions );
1945  return shouldShowDivisionForDisplayMode( coordinate, mTopFrameDivisions );
1947  default: //prevent warnings
1948  return shouldShowDivisionForDisplayMode( coordinate, mBottomFrameDivisions );
1949  }
1950 }
1951 
1952 bool QgsComposerMapGrid::shouldShowDivisionForDisplayMode( QgsComposerMapGrid::AnnotationCoordinate coordinate, QgsComposerMapGrid::DisplayMode mode ) const
1953 {
1954  return mode == QgsComposerMapGrid::ShowAll
1955  || ( mode == QgsComposerMapGrid::LatitudeOnly && coordinate == QgsComposerMapGrid::Latitude )
1956  || ( mode == QgsComposerMapGrid::LongitudeOnly && coordinate == QgsComposerMapGrid::Longitude );
1957 }
1958 
1960 {
1961  return a.first < b.first;
1962 }
1963 
1964 QgsComposerMapGrid::BorderSide QgsComposerMapGrid::borderForLineCoord( QPointF p, const AnnotationCoordinate coordinateType ) const
1965 {
1966  if ( !mComposerMap )
1967  {
1968  return QgsComposerMapGrid::Left;
1969  }
1970 
1971  double tolerance = qMax( mComposerMap->hasFrame() ? mComposerMap->pen().widthF() : 0.0, 1.0 );
1972 
1973  //check for corner coordinates
1974  if (( p.y() <= tolerance && p.x() <= tolerance ) // top left
1975  || ( p.y() <= tolerance && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //top right
1976  || ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() <= tolerance ) //bottom left
1977  || ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //bottom right
1978  )
1979  {
1980  //coordinate is in corner - fall back to preferred side for coordinate type
1981  if ( coordinateType == QgsComposerMapGrid::Latitude )
1982  {
1983  if ( p.x() <= tolerance )
1984  {
1985  return QgsComposerMapGrid::Left;
1986  }
1987  else
1988  {
1990  }
1991  }
1992  else
1993  {
1994  if ( p.y() <= tolerance )
1995  {
1996  return QgsComposerMapGrid::Top;
1997  }
1998  else
1999  {
2001  }
2002  }
2003  }
2004 
2005  //otherwise, guess side based on closest map side to point
2007  distanceToSide << qMakePair( p.x(), QgsComposerMapGrid::Left );
2008  distanceToSide << qMakePair( mComposerMap->rect().width() - p.x(), QgsComposerMapGrid::Right );
2009  distanceToSide << qMakePair( p.y(), QgsComposerMapGrid::Top );
2010  distanceToSide << qMakePair( mComposerMap->rect().height() - p.y(), QgsComposerMapGrid::Bottom );
2011 
2012  qSort( distanceToSide.begin(), distanceToSide.end(), sortByDistance );
2013  return distanceToSide.at( 0 ).second;
2014 }
2015 
2017 {
2018  delete mGridLineSymbol;
2019  mGridLineSymbol = symbol;
2020 }
2021 
2023 {
2024  delete mGridMarkerSymbol;
2025  mGridMarkerSymbol = symbol;
2026 }
2027 
2029 {
2030  switch ( border )
2031  {
2033  mLeftGridAnnotationDisplay = display;
2034  break;
2036  mRightGridAnnotationDisplay = display;
2037  break;
2039  mTopGridAnnotationDisplay = display;
2040  break;
2042  mBottomGridAnnotationDisplay = display;
2043  break;
2044  default:
2045  return;
2046  }
2047 
2048  if ( mComposerMap )
2049  {
2051  mComposerMap->update();
2052  }
2053 }
2054 
2056 {
2057  switch ( border )
2058  {
2060  return mLeftGridAnnotationDisplay;
2062  return mRightGridAnnotationDisplay;
2064  return mTopGridAnnotationDisplay;
2066  default:
2067  return mBottomGridAnnotationDisplay;
2068  }
2069 }
2070 
2072 {
2073  double top = 0.0;
2074  double right = 0.0;
2075  double bottom = 0.0;
2076  double left = 0.0;
2077  calculateMaxExtension( top, right, bottom, left );
2078  return qMax( qMax( qMax( top, right ), bottom ), left );
2079 }
2080 
2081 void QgsComposerMapGrid::calculateMaxExtension( double& top, double& right, double& bottom, double& left )
2082 {
2083  top = 0.0;
2084  right = 0.0;
2085  bottom = 0.0;
2086  left = 0.0;
2087 
2088  if ( !mComposerMap || !mEnabled )
2089  {
2090  return;
2091  }
2092 
2093  //setup render context
2096  QgsExpressionContext* expressionContext = createExpressionContext();
2097  context.setExpressionContext( *expressionContext );
2098  delete expressionContext;
2099 
2100  GridExtension extension;
2101 
2102  //collect grid lines
2103  QList< QPair< double, QLineF > > verticalLines;
2104  QList< QPair< double, QLineF > > horizontalLines;
2105  if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != ms.destinationCrs() )
2106  {
2107  drawGridCRSTransform( context, 0, horizontalLines, verticalLines, false );
2108  }
2109  else
2110  {
2111  drawGridNoTransform( context, 0, horizontalLines, verticalLines, false );
2112  }
2113 
2114  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame )
2115  {
2116  drawGridFrame( nullptr, horizontalLines, verticalLines, &extension );
2117  }
2118 
2119  if ( mShowGridAnnotation )
2120  {
2121  drawCoordinateAnnotations( nullptr, horizontalLines, verticalLines, context.expressionContext(), &extension );
2122  }
2123 
2124  top = extension.top;
2125  right = extension.right;
2126  bottom = extension.bottom;
2127  left = extension.left;
2128 }
2129 
2131 {
2132  if ( unit == mGridUnit )
2133  {
2134  return;
2135  }
2136  mGridUnit = unit;
2137  mTransformDirty = true;
2138 }
2139 
2140 void QgsComposerMapGrid::setIntervalX( const double interval )
2141 {
2142  if ( qgsDoubleNear( interval, mGridIntervalX ) )
2143  {
2144  return;
2145  }
2146  mGridIntervalX = interval;
2147  mTransformDirty = true;
2148 }
2149 
2150 void QgsComposerMapGrid::setIntervalY( const double interval )
2151 {
2152  if ( qgsDoubleNear( interval, mGridIntervalY ) )
2153  {
2154  return;
2155  }
2156  mGridIntervalY = interval;
2157  mTransformDirty = true;
2158 }
2159 
2160 void QgsComposerMapGrid::setOffsetX( const double offset )
2161 {
2162  if ( qgsDoubleNear( offset, mGridOffsetX ) )
2163  {
2164  return;
2165  }
2166  mGridOffsetX = offset;
2167  mTransformDirty = true;
2168 }
2169 
2170 void QgsComposerMapGrid::setOffsetY( const double offset )
2171 {
2172  if ( qgsDoubleNear( offset, mGridOffsetY ) )
2173  {
2174  return;
2175  }
2176  mGridOffsetY = offset;
2177  mTransformDirty = true;
2178 }
2179 
2181 {
2182  if ( style == mGridStyle )
2183  {
2184  return;
2185  }
2186  mGridStyle = style;
2187  mTransformDirty = true;
2188 }
2189 
2191 {
2192  switch ( border )
2193  {
2195  mLeftGridAnnotationDirection = direction;
2196  break;
2198  mRightGridAnnotationDirection = direction;
2199  break;
2201  mTopGridAnnotationDirection = direction;
2202  break;
2204  mBottomGridAnnotationDirection = direction;
2205  break;
2206  default:
2207  return;
2208  }
2209 
2210  if ( mComposerMap )
2211  {
2213  mComposerMap->update();
2214  }
2215 }
2216 
2217 void QgsComposerMapGrid::setFrameSideFlags( const FrameSideFlags& flags )
2218 {
2219  mGridFrameSides = flags;
2220 }
2221 
2223 {
2224  if ( on )
2225  mGridFrameSides |= flag;
2226  else
2227  mGridFrameSides &= ~flag;
2228 }
2229 
2230 QgsComposerMapGrid::FrameSideFlags QgsComposerMapGrid::frameSideFlags() const
2231 {
2232  return mGridFrameSides;
2233 }
2234 
2236 {
2238  context->appendScope( new QgsExpressionContextScope( tr( "Grid" ) ) );
2239  context->lastScope()->setVariable( "grid_number", 0 );
2240  context->lastScope()->setVariable( "grid_axis", "x" );
2241  context->setHighlightedVariables( QStringList() << "grid_number" << "grid_axis" );
2242  return context;
2243 }
2244 
2246 {
2247  return mGridFrameSides.testFlag( flag );
2248 }
2249 
2251 {
2252  mLeftGridAnnotationDirection = direction;
2253  mRightGridAnnotationDirection = direction;
2254  mTopGridAnnotationDirection = direction;
2255  mBottomGridAnnotationDirection = direction;
2256 }
2257 
2259 {
2260  switch ( border )
2261  {
2263  mLeftGridAnnotationPosition = position;
2264  break;
2266  mRightGridAnnotationPosition = position;
2267  break;
2269  mTopGridAnnotationPosition = position;
2270  break;
2272  mBottomGridAnnotationPosition = position;
2273  break;
2274  default:
2275  return;
2276  }
2277 
2278  if ( mComposerMap )
2279  {
2281  mComposerMap->update();
2282  }
2283 }
2284 
2286 {
2287  switch ( border )
2288  {
2290  return mLeftGridAnnotationPosition;
2292  return mRightGridAnnotationPosition;
2294  return mTopGridAnnotationPosition;
2296  default:
2297  return mBottomGridAnnotationPosition;
2298  }
2299 }
2300 
2302 {
2303  if ( !mComposerMap )
2304  {
2305  return mLeftGridAnnotationDirection;
2306  }
2307 
2308  switch ( border )
2309  {
2311  return mLeftGridAnnotationDirection;
2313  return mRightGridAnnotationDirection;
2315  return mTopGridAnnotationDirection;
2317  default:
2318  return mBottomGridAnnotationDirection;
2319  }
2320 }
2321 
2323 {
2324  switch ( border )
2325  {
2327  mLeftFrameDivisions = divisions;
2328  break;
2330  mRightFrameDivisions = divisions;
2331  break;
2333  mTopFrameDivisions = divisions;
2334  break;
2336  mBottomFrameDivisions = divisions;
2337  break;
2338  default:
2339  return;
2340  }
2341 
2342  if ( mComposerMap )
2343  {
2344  mComposerMap->update();
2345  }
2346 }
2347 
2349 {
2350  switch ( border )
2351  {
2353  return mLeftFrameDivisions;
2355  return mRightFrameDivisions;
2357  return mTopFrameDivisions;
2359  default:
2360  return mBottomFrameDivisions;
2361  }
2362 }
2363 
2364 int QgsComposerMapGrid::crsGridParams( QgsRectangle& crsRect, QgsCoordinateTransform& inverseTransform ) const
2365 {
2366  if ( !mComposerMap )
2367  {
2368  return 1;
2369  }
2370 
2371  try
2372  {
2375  QRectF mbr = mapPolygon.boundingRect();
2376  QgsRectangle mapBoundingRect( mbr.left(), mbr.bottom(), mbr.right(), mbr.top() );
2377 
2378 
2379  if ( mCRS.geographicFlag() )
2380  {
2381  //handle crossing the 180 degree longitude line
2382  QgsPoint lowerLeft( mapBoundingRect.xMinimum(), mapBoundingRect.yMinimum() );
2383  QgsPoint upperRight( mapBoundingRect.xMaximum(), mapBoundingRect.yMaximum() );
2384 
2385  lowerLeft = tr.transform( lowerLeft.x(), lowerLeft.y() );
2386  upperRight = tr.transform( upperRight.x(), upperRight.y() );
2387 
2388  if ( lowerLeft.x() > upperRight.x() )
2389  {
2390  //we've crossed the line
2391  crsRect = tr.transformBoundingBox( mapBoundingRect, QgsCoordinateTransform::ForwardTransform, true );
2392  }
2393  else
2394  {
2395  //didn't cross the line
2396  crsRect = tr.transformBoundingBox( mapBoundingRect );
2397  }
2398  }
2399  else
2400  {
2401  crsRect = tr.transformBoundingBox( mapBoundingRect );
2402  }
2403 
2404  inverseTransform.setSourceCrs( mCRS );
2405  inverseTransform.setDestCRS( mComposerMap->composition()->mapSettings().destinationCrs() );
2406  }
2407  catch ( QgsCsException & cse )
2408  {
2409  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
2410  return 1;
2411  }
2412  return 0;
2413 }
2414 
2415 QList<QPolygonF> QgsComposerMapGrid::trimLinesToMap( const QPolygonF& line, const QgsRectangle& rect )
2416 {
2417  QgsGeometry* lineGeom = QgsGeometry::fromQPolygonF( line );
2418  QgsGeometry* rectGeom = QgsGeometry::fromRect( rect );
2419 
2420  QgsGeometry* intersected = lineGeom->intersection( rectGeom );
2421  QList<QgsGeometry*> intersectedParts = intersected->asGeometryCollection();
2422 
2423  QList<QPolygonF> trimmedLines;
2424  QList<QgsGeometry*>::const_iterator geomIt = intersectedParts.constBegin();
2425  for ( ; geomIt != intersectedParts.constEnd(); ++geomIt )
2426  {
2427  trimmedLines << ( *geomIt )->asQPolygonF();
2428  }
2429 
2430  qDeleteAll( intersectedParts );
2431  intersectedParts.clear();
2432  delete intersected;
2433  delete lineGeom;
2434  delete rectGeom;
2435  return trimmedLines;
2436 }
QgsComposerMapGrid(const QString &name, QgsComposerMap *map)
Constructor for QgsComposerMapGrid.
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
static unsigned index
QTransform fromScale(qreal sx, qreal sy)
A rectangle specified with double values.
Definition: qgsrectangle.h:35
QPointF mapToItemCoords(QPointF mapCoords) const
Transforms map coordinates to item coordinates (considering rotation and move offset) ...
static QgsMarkerSymbolV2 * createSimple(const QgsStringMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
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.
QgsComposerMapGrid * grid(const QString &gridId) const
Returns a 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
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:196
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setOutputDpi(int dpi)
Set DPI used for conversion between real world units (e.g. mm) and pixels.
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)
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
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
QList< QgsGeometry * > asGeometryCollection() const
Return contents of the geometry as a list of geometries.
const T & at(int i) const
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
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...
QgsPoint transform(const QgsPoint &p, TransformDirection direction=ForwardTransform) const
Transform the point from Source Coordinate System to Destination Coordinate System If the direction i...
QPolygonF transformedMapPolygon() const
Returns extent that considers rotation and shift with mOffsetX / mOffsetY.
void rotate(qreal angle)
void drawLine(const QLineF &line)
AnnotationDirection
Direction of grid annotations.
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:384
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
void setVariable(const QString &name, const QVariant &value)
Convenience method for setting a variable in the context scope by name and value. ...
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
void update(const QRectF &rect)
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
An item which is drawn inside a QgsComposerMap, eg a grid or map overview.
AnnotationPosition annotationPosition(const BorderSide border) const
Gets the position for the grid annotations on a specified side of the map frame.
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
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.
const QgsComposerMapItem * constItem(const QString &itemId) const
Returns a const reference to an item within the stack.
QDomElement toElement() const
bool testFrameSideFlag(const FrameSideFlag flag) const
Tests whether the grid frame should be drawn on a specified side of the map item. ...
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()
virtual QgsExpressionContext * createExpressionContext() const
Creates an expression context relating to the objects&#39; current state.
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)
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:201
const Key & key() const
const QgsComposerMapGrid * constGrid(const QString &gridId) const
Returns a const reference to a grid within the stack.
bool mEnabled
True if item is to be displayed on map.
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:186
DisplayMode frameDivisions(const BorderSide border) const
Gets the type of grid divisions which are used for frames on a specified side of the 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
const QgsComposition * composition() const
Returns the composition the item is attached to.
void removeItem(const QString &itemId)
Removes an item from the stack and deletes the corresponding QgsComposerMapItem.
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
int toInt(bool *ok, int base) const
QString qgsDoubleToString(double a, int precision=17)
Definition: qgis.h:274
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.
FrameSideFlags frameSideFlags() const
Returns the flags which control which sides of the map item the grid frame is drawn on...
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
void setWidthF(qreal width)
void setBrush(const QBrush &brush)
void setPainter(QPainter *p)
GridStyle style() const
Gets the grid&#39;s style, which controls how the grid is drawn over the map&#39;s contents.
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.
T & first()
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...
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.
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.
double mapRotation(QgsComposerObject::PropertyValueType valueType=QgsComposerObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the composer item.
A class to represent a point.
Definition: qgspoint.h:65
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets grid state from a DOM document.
QgsComposerMapGridStack(QgsComposerMap *map)
Constructor for QgsComposerMapGridStack.
QgsComposerMapItem * item(const QString &itemId) const
Returns a reference to an item within the stack.
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()
QgsRectangle * currentMapExtent()
Returns a pointer to the current map extent, which is either the original user specified extent or th...
QList< QgsComposerMapGrid * > asList() const
Returns a list of QgsComposerMapGrids contained by the stack.
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:105
BorderSide
Border sides for annotations.
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:113
QgsExpressionContext & expressionContext()
Gets the expression context.
bool isNull() const
DisplayMode annotationDisplay(const BorderSide border) const
Gets the display mode for the grid annotations on a specified side of the map frame.
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...
QgsGeometry * intersection(const QgsGeometry *geometry) const
Returns a geometry representing the points shared by this geometry and other.
void setDestCRS(const QgsCoordinateReferenceSystem &theCRS)
bool isValid() const
Returns whether this CRS is correctly initialised and usable.
QString what() const
Definition: qgsexception.h:36
const T & at(int i) const
QVariant value(const QString &key, const QVariant &defaultValue) const
const_iterator constBegin() const
bool writeXML(QDomNode &theNode, QDomDocument &theDoc) const
Stores state to the given Dom node in the given document.
Contains information about the context of a rendering operation.
QRectF boundingRect() const
qreal width() const
void stopRender(QgsRenderContext &context)
void setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
QString toDegreesMinutes(int thePrecision, const bool useSuffix=true, const bool padded=false) const
Return a string representation as degrees minutes.
Definition: qgspoint.cpp:256
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.
bool hasFrame() const
Whether this item has a frame or not.
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 setExtent(const QgsRectangle &rect)
Set coordinates of the rectangle which should be rendered.
void setMarkerSymbol(QgsMarkerSymbolV2 *symbol)
Sets the marker symbol used for drawing grid points.
qreal & rx()
qreal & ry()
bool sortByDistance(QPair< qreal, QgsComposerMapGrid::BorderSide > a, QPair< qreal, QgsComposerMapGrid::BorderSide > b)
void setIntervalY(const double interval)
Sets the interval between grid lines in the y-direction.
Class for doing transforms between two map coordinate systems.
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)
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.
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
virtual bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets map item state from a DOM document.
qreal height() const
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user...
static QColor decodeColor(const QString &str)
iterator insert(const Key &key, const T &value)
QString toDegreesMinutesSeconds(int thePrecision, const bool useSuffix=true, const bool padded=false) const
Return a string representation as degrees minutes seconds.
Definition: qgspoint.cpp:142
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.
int size() const
const_iterator constEnd() const
double maxGridExtension() const
Calculates the maximum distance grids within the stack extend beyond the QgsComposerMap&#39;s item rect...
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. ...
virtual bool writeXML(QDomElement &elem, QDomDocument &doc) const override
Stores map item state in DOM element.
AnnotationDirection annotationDirection(const BorderSide border) const
Gets the direction for drawing frame annotations.
bool geographicFlag() const
Returns whether the CRS is a geographic CRS.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:191
QString toString() const
QgsCoordinateReferenceSystem crs() const
Retrieves the CRS for the grid.
GridUnit
Unit for grid values.
void setIntervalX(const double interval)
Sets the interval between grid lines in the x-direction.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
iterator begin()
void setOffsetX(const double offset)
Sets the offset for grid lines in the x-direction.
AnnotationCoordinate
Annotation coordinate type.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QDomNode at(int index) const
QRectF rect() const
QgsComposerMap * mComposerMap
uint toUInt(bool *ok, int base) const