QGIS API Documentation  2.12.0-Lyon
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 {
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 
172 QgsComposerMapGrid::QgsComposerMapGrid()
173  : QgsComposerMapItem( QString(), 0 )
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 = 0;
217  mGridMarkerSymbol = 0;
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 = ( QgsComposerMapGrid::FrameStyle )itemElem.attribute( "gridFrameStyle", "0" ).toInt();
361  mGridFrameSides = ( 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 
404  QDomElement crsElem = itemElem.firstChildElement( "spatialrefsys" );
405  if ( !crsElem.isNull() )
406  {
407  mCRS.readXML( const_cast<QDomElement&>( itemElem ) ); //better would be to change argument in QgsCoordinateReferenceSystem::readXML to const
408  }
409  else
410  {
412  }
413  mBlendMode = ( QPainter::CompositionMode )( itemElem.attribute( "blendMode", "0" ).toUInt() );
414 
415  //annotation
416  mShowGridAnnotation = ( itemElem.attribute( "showAnnotation", "0" ) != "0" );
417  mGridAnnotationFormat = QgsComposerMapGrid::AnnotationFormat( itemElem.attribute( "annotationFormat", "0" ).toInt() );
418  mGridAnnotationExpressionString = itemElem.attribute( "annotationExpression" );
419  mGridAnnotationExpression.reset();
420  mLeftGridAnnotationPosition = QgsComposerMapGrid::AnnotationPosition( itemElem.attribute( "leftAnnotationPosition", "0" ).toInt() );
421  mRightGridAnnotationPosition = QgsComposerMapGrid::AnnotationPosition( itemElem.attribute( "rightAnnotationPosition", "0" ).toInt() );
422  mTopGridAnnotationPosition = QgsComposerMapGrid::AnnotationPosition( itemElem.attribute( "topAnnotationPosition", "0" ).toInt() );
423  mBottomGridAnnotationPosition = QgsComposerMapGrid::AnnotationPosition( itemElem.attribute( "bottomAnnotationPosition", "0" ).toInt() );
424  mLeftGridAnnotationDisplay = QgsComposerMapGrid::DisplayMode( itemElem.attribute( "leftAnnotationDisplay", "0" ).toInt() );
425  mRightGridAnnotationDisplay = QgsComposerMapGrid::DisplayMode( itemElem.attribute( "rightAnnotationDisplay", "0" ).toInt() );
426  mTopGridAnnotationDisplay = QgsComposerMapGrid::DisplayMode( itemElem.attribute( "topAnnotationDisplay", "0" ).toInt() );
427  mBottomGridAnnotationDisplay = QgsComposerMapGrid::DisplayMode( itemElem.attribute( "bottomAnnotationDisplay", "0" ).toInt() );
428  //upgrade pre-2.7 projects
429  if ( mLeftGridAnnotationPosition == QgsComposerMapGrid::Disabled )
430  {
431  mLeftGridAnnotationDisplay = QgsComposerMapGrid::HideAll;
432  mLeftGridAnnotationPosition = QgsComposerMapGrid::OutsideMapFrame;
433  }
434  if ( mRightGridAnnotationPosition == QgsComposerMapGrid::Disabled )
435  {
436  mRightGridAnnotationDisplay = QgsComposerMapGrid::HideAll;
437  mRightGridAnnotationPosition = QgsComposerMapGrid::OutsideMapFrame;
438  }
439  if ( mTopGridAnnotationPosition == QgsComposerMapGrid::Disabled )
440  {
441  mTopGridAnnotationDisplay = QgsComposerMapGrid::HideAll;
442  mTopGridAnnotationPosition = QgsComposerMapGrid::OutsideMapFrame;
443  }
444  if ( mBottomGridAnnotationPosition == QgsComposerMapGrid::Disabled )
445  {
446  mBottomGridAnnotationDisplay = QgsComposerMapGrid::HideAll;
447  mBottomGridAnnotationPosition = QgsComposerMapGrid::OutsideMapFrame;
448  }
449 
450  mLeftGridAnnotationDirection = QgsComposerMapGrid::AnnotationDirection( itemElem.attribute( "leftAnnotationDirection", "0" ).toInt() );
451  mRightGridAnnotationDirection = QgsComposerMapGrid::AnnotationDirection( itemElem.attribute( "rightAnnotationDirection", "0" ).toInt() );
452  mTopGridAnnotationDirection = QgsComposerMapGrid::AnnotationDirection( itemElem.attribute( "topAnnotationDirection", "0" ).toInt() );
453  mBottomGridAnnotationDirection = QgsComposerMapGrid::AnnotationDirection( itemElem.attribute( "bottomAnnotationDirection", "0" ).toInt() );
454  mAnnotationFrameDistance = itemElem.attribute( "frameAnnotationDistance", "0" ).toDouble();
455  if ( !QgsFontUtils::setFromXmlChildNode( mGridAnnotationFont, itemElem, "annotationFontProperties" ) )
456  {
457  mGridAnnotationFont.fromString( itemElem.attribute( "annotationFont", "" ) );
458  }
459  mGridAnnotationFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "annotationFontColor", "0,0,0,255" ) );
460  mGridAnnotationPrecision = itemElem.attribute( "annotationPrecision", "3" ).toInt();
461  int gridUnitInt = itemElem.attribute( "unit", QString::number( MapUnit ) ).toInt();
462  mGridUnit = ( gridUnitInt <= ( int )CM ) ? ( GridUnit )gridUnitInt : MapUnit;
463  return ok;
464 }
465 
467 {
468  mCRS = crs;
469  mTransformDirty = true;
470 }
471 
473 {
474  return mBlendMode == QPainter::CompositionMode_SourceOver;
475 }
476 
477 QPolygonF QgsComposerMapGrid::scalePolygon( const QPolygonF &polygon, const double scale ) const
478 {
479  QTransform t = QTransform::fromScale( scale, scale );
480  return t.map( polygon );
481 }
482 
483 void QgsComposerMapGrid::drawGridCRSTransform( QgsRenderContext &context, double dotsPerMM, QList< QPair< double, QLineF > > &horizontalLines,
484  QList< QPair< double, QLineF > > &verticalLines, bool calculateLinesOnly )
485 {
486  if ( !mComposerMap || !mEnabled )
487  {
488  return;
489  }
490 
491  //has map extent/scale changed?
493  if ( mapPolygon != mPrevMapPolygon )
494  {
495  mTransformDirty = true;
496  mPrevMapPolygon = mapPolygon;
497  }
498 
499  if ( mTransformDirty )
500  {
501  calculateCRSTransformLines();
502  }
503 
504  //draw lines
505  if ( !calculateLinesOnly )
506  {
507  if ( mGridStyle == QgsComposerMapGrid::Solid )
508  {
509  QList< QPair< double, QPolygonF > >::const_iterator xGridIt = mTransformedXLines.constBegin();
510  for ( ; xGridIt != mTransformedXLines.constEnd(); ++xGridIt )
511  {
512  drawGridLine( scalePolygon( xGridIt->second, dotsPerMM ), context );
513  }
514 
515  QList< QPair< double, QPolygonF > >::const_iterator yGridIt = mTransformedYLines.constBegin();
516  for ( ; yGridIt != mTransformedYLines.constEnd(); ++yGridIt )
517  {
518  drawGridLine( scalePolygon( yGridIt->second, dotsPerMM ), context );
519  }
520  }
521  else if ( mGridStyle == QgsComposerMapGrid::Cross || mGridStyle == QgsComposerMapGrid::Markers )
522  {
523  double maxX = mComposerMap->rect().width();
524  double maxY = mComposerMap->rect().height();
525 
526  QList< QgsPoint >::const_iterator intersectionIt = mTransformedIntersections.constBegin();
527  for ( ; intersectionIt != mTransformedIntersections.constEnd(); ++intersectionIt )
528  {
529  double x = intersectionIt->x();
530  double y = intersectionIt->y();
531  if ( mGridStyle == QgsComposerMapGrid::Cross )
532  {
533  //ensure that crosses don't overshoot the map item bounds
534  QLineF line1 = QLineF( x - mCrossLength, y, x + mCrossLength, y );
535  line1.p1().rx() = line1.p1().x() < 0 ? 0 : line1.p1().x();
536  line1.p2().rx() = line1.p2().x() > maxX ? maxX : line1.p2().x();
537  QLineF line2 = QLineF( x, y - mCrossLength, x, y + mCrossLength );
538  line2.p1().ry() = line2.p1().y() < 0 ? 0 : line2.p1().y();
539  line2.p2().ry() = line2.p2().y() > maxY ? maxY : line2.p2().y();
540 
541  //draw line using coordinates scaled to dots
542  drawGridLine( QLineF( line1.p1() * dotsPerMM, line1.p2() * dotsPerMM ), context );
543  drawGridLine( QLineF( line2.p1() * dotsPerMM, line2.p2() * dotsPerMM ), context );
544  }
545  else if ( mGridStyle == QgsComposerMapGrid::Markers )
546  {
547  drawGridMarker( QPointF( x, y ) * dotsPerMM, context );
548  }
549  }
550  }
551  }
552 
553  //convert QPolygonF to QLineF to draw grid frames and annotations
554  QList< QPair< double, QPolygonF > >::const_iterator yGridLineIt = mTransformedYLines.constBegin();
555  for ( ; yGridLineIt != mTransformedYLines.constEnd(); ++yGridLineIt )
556  {
557  verticalLines.push_back( qMakePair( yGridLineIt->first, QLineF( yGridLineIt->second.first(), yGridLineIt->second.last() ) ) );
558  }
559  QList< QPair< double, QPolygonF > >::const_iterator xGridLineIt = mTransformedXLines.constBegin();
560  for ( ; xGridLineIt != mTransformedXLines.constEnd(); ++xGridLineIt )
561  {
562  horizontalLines.push_back( qMakePair( xGridLineIt->first, QLineF( xGridLineIt->second.first(), xGridLineIt->second.last() ) ) );
563  }
564 }
565 
566 void QgsComposerMapGrid::calculateCRSTransformLines()
567 {
568  QgsRectangle crsBoundingRect;
569  QgsCoordinateTransform inverseTr;
570  if ( crsGridParams( crsBoundingRect, inverseTr ) != 0 )
571  {
572  return;
573  }
574 
575  //calculate x grid lines
576  mTransformedXLines.clear();
577  xGridLinesCRSTransform( crsBoundingRect, inverseTr, mTransformedXLines );
578 
579  //calculate y grid lines
580  mTransformedYLines.clear();
581  yGridLinesCRSTransform( crsBoundingRect, inverseTr, mTransformedYLines );
582 
583  if ( mGridStyle == QgsComposerMapGrid::Cross || mGridStyle == QgsComposerMapGrid::Markers )
584  {
585  //cross or markers style - we also need to calculate intersections of lines
586 
587  //first convert lines to QgsGeometry
588  QList< QgsGeometry* > yLines;
589  QList< QPair< double, QPolygonF > >::const_iterator yGridIt = mTransformedYLines.constBegin();
590  for ( ; yGridIt != mTransformedYLines.constEnd(); ++yGridIt )
591  {
592  QgsPolyline yLine;
593  for ( int i = 0; i < ( *yGridIt ).second.size(); ++i )
594  {
595  yLine.append( QgsPoint(( *yGridIt ).second.at( i ).x(), ( *yGridIt ).second.at( i ).y() ) );
596  }
597  yLines << QgsGeometry::fromPolyline( yLine );
598  }
599  QList< QgsGeometry* > xLines;
600  QList< QPair< double, QPolygonF > >::const_iterator xGridIt = mTransformedXLines.constBegin();
601  for ( ; xGridIt != mTransformedXLines.constEnd(); ++xGridIt )
602  {
603  QgsPolyline xLine;
604  for ( int i = 0; i < ( *xGridIt ).second.size(); ++i )
605  {
606  xLine.append( QgsPoint(( *xGridIt ).second.at( i ).x(), ( *xGridIt ).second.at( i ).y() ) );
607  }
608  xLines << QgsGeometry::fromPolyline( xLine );
609  }
610 
611  //now, loop through geometries and calculate intersection points
612  mTransformedIntersections.clear();
614  for ( ; yLineIt != yLines.constEnd(); ++yLineIt )
615  {
617  for ( ; xLineIt != xLines.constEnd(); ++xLineIt )
618  {
619  //look for intersections between lines
620  QgsGeometry* intersects = ( *yLineIt )->intersection(( *xLineIt ) );
621  if ( !intersects )
622  continue;
623 
624  //go through all intersections and draw grid markers/crosses
625  int i = 0;
626  QgsPoint vertex = intersects->vertexAt( i );
627  while ( vertex != QgsPoint( 0, 0 ) )
628  {
629  mTransformedIntersections << vertex;
630  i = i + 1;
631  vertex = intersects->vertexAt( i );
632  }
633  delete intersects;
634  }
635  }
636  //clean up
637  qDeleteAll( yLines );
638  yLines.clear();
639  qDeleteAll( xLines );
640  xLines.clear();
641  }
642 
643  mTransformDirty = false;
644 }
645 
647 {
648  if ( !mComposerMap || !mEnabled )
649  {
650  return;
651  }
652  QPaintDevice* thePaintDevice = p->device();
653  if ( !thePaintDevice )
654  {
655  return;
656  }
657 
658  p->save();
659  p->setCompositionMode( mBlendMode );
660  p->setRenderHint( QPainter::Antialiasing );
661 
662  QRectF thisPaintRect = QRectF( 0, 0, mComposerMap->rect().width(), mComposerMap->rect().height() );
663  p->setClipRect( thisPaintRect );
664  if ( thisPaintRect != mPrevPaintRect )
665  {
666  //rect has changed, so need to recalculate transform
667  mTransformDirty = true;
668  mPrevPaintRect = thisPaintRect;
669  }
670 
671  //setup painter scaling to dots so that raster symbology is drawn to scale
672  double dotsPerMM = thePaintDevice->logicalDpiX() / 25.4;
673  p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); //scale painter from mm to dots
674 
675  //setup render context
677  //context units should be in dots
678  ms.setOutputSize( QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM ).toSize() );
680  ms.setOutputDpi( p->device()->logicalDpiX() );
682  context.setForceVectorOutput( true );
683  context.setPainter( p );
684  QgsExpressionContext* expressionContext = createExpressionContext();
685  context.setExpressionContext( *expressionContext );
686  delete expressionContext;
687 
688  QList< QPair< double, QLineF > > verticalLines;
689  QList< QPair< double, QLineF > > horizontalLines;
690 
691  //is grid in a different crs than map?
692  if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != ms.destinationCrs() )
693  {
694  drawGridCRSTransform( context, dotsPerMM, horizontalLines, verticalLines );
695  }
696  else
697  {
698  drawGridNoTransform( context, dotsPerMM, horizontalLines, verticalLines );
699  }
700 
701  p->restore();
702 
703  p->setClipping( false );
704 #ifdef Q_OS_MAC
705  //QPainter::setClipping(false) seems to be broken on OSX (#12747). So we hack around it by
706  //setting a larger clip rect
707  p->setClipRect( mComposerMap->mapRectFromScene( mComposerMap->sceneBoundingRect() ).adjusted( -10, -10, 10, 10 ) );
708 #endif
709 
710  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame )
711  {
712  drawGridFrame( p, horizontalLines, verticalLines );
713  }
714 
715  if ( mShowGridAnnotation )
716  {
717  drawCoordinateAnnotations( p, horizontalLines, verticalLines, context.expressionContext() );
718  }
719 }
720 
721 void QgsComposerMapGrid::drawGridNoTransform( QgsRenderContext &context, double dotsPerMM, QList< QPair< double, QLineF > > &horizontalLines,
722  QList< QPair< double, QLineF > > &verticalLines, bool calculateLinesOnly ) const
723 {
724  //get line positions
725  yGridLines( verticalLines );
726  xGridLines( horizontalLines );
727 
728  if ( calculateLinesOnly )
729  return;
730 
731  QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
732  QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
733 
734  //simple approach: draw vertical lines first, then horizontal ones
735  if ( mGridStyle == QgsComposerMapGrid::Solid )
736  {
737  //we need to scale line coordinates to dots, rather than mm, since the painter has already been scaled to dots
738  //this is done by multiplying each line coordinate by dotsPerMM
739  QLineF line;
740  for ( ; vIt != verticalLines.constEnd(); ++vIt )
741  {
742  line = QLineF( vIt->second.p1() * dotsPerMM, vIt->second.p2() * dotsPerMM );
743  drawGridLine( line, context );
744  }
745 
746  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
747  {
748  line = QLineF( hIt->second.p1() * dotsPerMM, hIt->second.p2() * dotsPerMM );
749  drawGridLine( line, context );
750  }
751  }
752  else if ( mGridStyle != QgsComposerMapGrid::FrameAnnotationsOnly ) //cross or markers
753  {
754  QPointF intersectionPoint, crossEnd1, crossEnd2;
755  for ( ; vIt != verticalLines.constEnd(); ++vIt )
756  {
757  //test for intersection with every horizontal line
758  hIt = horizontalLines.constBegin();
759  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
760  {
761  if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
762  {
763  if ( mGridStyle == QgsComposerMapGrid::Cross )
764  {
765  //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
766  crossEnd1 = (( intersectionPoint - vIt->second.p1() ).manhattanLength() > 0.01 ) ?
767  QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength ) : intersectionPoint;
768  crossEnd2 = (( intersectionPoint - vIt->second.p2() ).manhattanLength() > 0.01 ) ?
769  QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength ) : intersectionPoint;
770  //draw line using coordinates scaled to dots
771  drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
772  }
773  else if ( mGridStyle == QgsComposerMapGrid::Markers )
774  {
775  drawGridMarker( intersectionPoint * dotsPerMM, context );
776  }
777  }
778  }
779  }
780  if ( mGridStyle == QgsComposerMapGrid::Markers )
781  {
782  //markers mode, so we have no need to process horizontal lines (we've already
783  //drawn markers on the intersections between horizontal and vertical lines)
784  return;
785  }
786 
787  hIt = horizontalLines.constBegin();
788  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
789  {
790  vIt = verticalLines.constBegin();
791  for ( ; vIt != verticalLines.constEnd(); ++vIt )
792  {
793  if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
794  {
795  //apply a threshold to avoid calculate point if the two points are very close together (can lead to artifacts)
796  crossEnd1 = (( intersectionPoint - hIt->second.p1() ).manhattanLength() > 0.01 ) ?
797  QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength ) : intersectionPoint;
798  crossEnd2 = (( intersectionPoint - hIt->second.p2() ).manhattanLength() > 0.01 ) ?
799  QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength ) : intersectionPoint;
800  //draw line using coordinates scaled to dots
801  drawGridLine( QLineF( crossEnd1 * dotsPerMM, crossEnd2 * dotsPerMM ), context );
802  }
803  }
804  }
805  }
806 }
807 
808 void QgsComposerMapGrid::drawGridFrame( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines, GridExtension* extension ) const
809 {
810  if ( p )
811  {
812  p->save();
813  p->setRenderHint( QPainter::Antialiasing );
814  }
815 
816  //Sort the coordinate positions for each side
817  QMap< double, double > leftGridFrame;
818  QMap< double, double > rightGridFrame;
819  QMap< double, double > topGridFrame;
820  QMap< double, double > bottomGridFrame;
821 
822  sortGridLinesOnBorders( hLines, vLines, leftGridFrame, rightGridFrame, topGridFrame, bottomGridFrame );
823 
825  {
826  drawGridFrameBorder( p, leftGridFrame, QgsComposerMapGrid::Left, extension ? &extension->left : 0 );
827  }
829  {
830  drawGridFrameBorder( p, rightGridFrame, QgsComposerMapGrid::Right, extension ? &extension->right : 0 );
831  }
833  {
834  drawGridFrameBorder( p, topGridFrame, QgsComposerMapGrid::Top, extension ? &extension->top : 0 );
835  }
837  {
838  drawGridFrameBorder( p, bottomGridFrame, QgsComposerMapGrid::Bottom, extension ? &extension->bottom : 0 );
839  }
840  if ( p )
841  p->restore();
842 }
843 
844 void QgsComposerMapGrid::drawGridLine( const QLineF& line, QgsRenderContext& context ) const
845 {
846  QPolygonF poly;
847  poly << line.p1() << line.p2();
848  drawGridLine( poly, context );
849 }
850 
851 void QgsComposerMapGrid::drawGridLine( const QPolygonF& line, QgsRenderContext& context ) const
852 {
853  if ( !mComposerMap || !mComposerMap->composition() || !mGridLineSymbol )
854  {
855  return;
856  }
857 
858  mGridLineSymbol->startRender( context );
859  mGridLineSymbol->renderPolyline( line, 0, context );
860  mGridLineSymbol->stopRender( context );
861 }
862 
863 void QgsComposerMapGrid::drawGridMarker( const QPointF& point, QgsRenderContext& context ) const
864 {
865  if ( !mComposerMap || !mComposerMap->composition() || !mGridMarkerSymbol )
866  {
867  return;
868  }
869 
870  mGridMarkerSymbol->startRender( context );
871  mGridMarkerSymbol->renderPoint( point, 0, context );
872  mGridMarkerSymbol->stopRender( context );
873 }
874 
875 void QgsComposerMapGrid::drawGridFrameBorder( QPainter* p, const QMap< double, double >& borderPos, QgsComposerMapGrid::BorderSide border, double* extension ) const
876 {
877  if ( !mComposerMap )
878  {
879  return;
880  }
881 
882  switch ( mGridFrameStyle )
883  {
885  drawGridFrameZebraBorder( p, borderPos, border, extension );
886  break;
890  drawGridFrameTicks( p, borderPos, border, extension );
891  break;
892 
894  drawGridFrameLineBorder( p, border, extension );
895  break;
896 
898  break;
899  }
900 
901 }
902 
903 void QgsComposerMapGrid::drawGridFrameZebraBorder( QPainter* p, const QMap< double, double >& borderPos, QgsComposerMapGrid::BorderSide border, double* extension ) const
904 {
905  if ( !mComposerMap )
906  {
907  return;
908  }
909 
910  if ( extension )
911  {
912  *extension = mGridFrameWidth + mGridFramePenThickness / 2.0;
913  return;
914  }
915 
916  QMap< double, double > pos = borderPos;
917 
918  double currentCoord = 0;
920  {
921  currentCoord = - mGridFrameWidth;
922  pos.insert( 0, 0 );
923  }
925  {
926  currentCoord = - mGridFrameWidth;
927  pos.insert( 0, 0 );
928  }
929  bool color1 = true;
930  double x = 0;
931  double y = 0;
932  double width = 0;
933  double height = 0;
934 
935  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
936  {
939  {
940  pos.insert( mComposerMap->rect().height() + mGridFrameWidth, mComposerMap->rect().height() + mGridFrameWidth );
941  }
942  }
943  else if ( border == QgsComposerMapGrid::Top || border == QgsComposerMapGrid::Bottom )
944  {
945  pos.insert( mComposerMap->rect().width(), mComposerMap->rect().width() );
947  {
948  pos.insert( mComposerMap->rect().width() + mGridFrameWidth, mComposerMap->rect().width() + mGridFrameWidth );
949  }
950  }
951 
952  //set pen to current frame pen
953  QPen framePen = QPen( mGridFramePenColor );
954  framePen.setWidthF( mGridFramePenThickness );
955  framePen.setJoinStyle( Qt::MiterJoin );
956  p->setPen( framePen );
957 
959  for ( ; posIt != pos.constEnd(); ++posIt )
960  {
961  p->setBrush( QBrush( color1 ? mGridFrameFillColor1 : mGridFrameFillColor2 ) );
962  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
963  {
964  height = posIt.key() - currentCoord;
965  width = mGridFrameWidth;
966  x = ( border == QgsComposerMapGrid::Left ) ? -mGridFrameWidth : mComposerMap->rect().width();
967  y = currentCoord;
968  }
969  else //top or bottom
970  {
971  height = mGridFrameWidth;
972  width = posIt.key() - currentCoord;
973  x = currentCoord;
974  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height();
975  }
976  p->drawRect( QRectF( x, y, width, height ) );
977  currentCoord = posIt.key();
978  color1 = !color1;
979  }
980 }
981 
982 void QgsComposerMapGrid::drawGridFrameTicks( QPainter* p, const QMap< double, double >& borderPos, QgsComposerMapGrid::BorderSide border, double* extension ) const
983 {
984  if ( !mComposerMap )
985  {
986  return;
987  }
988 
989  if ( extension )
990  {
991  if ( mGridFrameStyle != QgsComposerMapGrid::InteriorTicks )
992  *extension = mGridFrameWidth;
993  return;
994  }
995 
996  double x = 0;
997  double y = 0;
998  double width = 0;
999  double height = 0;
1000 
1001  //set pen to current frame pen
1002  QPen framePen = QPen( mGridFramePenColor );
1003  framePen.setWidthF( mGridFramePenThickness );
1004  framePen.setCapStyle( Qt::FlatCap );
1005  p->setBrush( Qt::NoBrush );
1006  p->setPen( framePen );
1007 
1009  for ( ; posIt != borderPos.constEnd(); ++posIt )
1010  {
1011  if ( border == QgsComposerMapGrid::Left || border == QgsComposerMapGrid::Right )
1012  {
1013  y = posIt.key();
1014  height = 0;
1015  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1016  {
1017  width = mGridFrameWidth;
1018  x = ( border == QgsComposerMapGrid::Left ) ? 0 : mComposerMap->rect().width() - mGridFrameWidth;
1019  }
1020  else if ( mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1021  {
1022  width = mGridFrameWidth;
1023  x = ( border == QgsComposerMapGrid::Left ) ? - mGridFrameWidth : mComposerMap->rect().width();
1024  }
1025  else if ( mGridFrameStyle == QgsComposerMapGrid::InteriorExteriorTicks )
1026  {
1027  width = mGridFrameWidth * 2;
1028  x = ( border == QgsComposerMapGrid::Left ) ? - mGridFrameWidth : mComposerMap->rect().width() - mGridFrameWidth;
1029  }
1030  }
1031  else //top or bottom
1032  {
1033  x = posIt.key();
1034  width = 0;
1035  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1036  {
1037  height = mGridFrameWidth;
1038  y = ( border == QgsComposerMapGrid::Top ) ? 0 : mComposerMap->rect().height() - mGridFrameWidth;
1039  }
1040  else if ( mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1041  {
1042  height = mGridFrameWidth;
1043  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height();
1044  }
1045  else if ( mGridFrameStyle == QgsComposerMapGrid::InteriorExteriorTicks )
1046  {
1047  height = mGridFrameWidth * 2;
1048  y = ( border == QgsComposerMapGrid::Top ) ? -mGridFrameWidth : mComposerMap->rect().height() - mGridFrameWidth;
1049  }
1050  }
1051  p->drawLine( QLineF( x, y, x + width, y + height ) );
1052  }
1053 }
1054 
1055 void QgsComposerMapGrid::drawGridFrameLineBorder( QPainter* p, QgsComposerMapGrid::BorderSide border, double* extension ) const
1056 {
1057  if ( !mComposerMap )
1058  {
1059  return;
1060  }
1061 
1062  if ( extension )
1063  {
1064  *extension = mGridFramePenThickness / 2.0;
1065  return;
1066  }
1067 
1068  //set pen to current frame pen
1069  QPen framePen = QPen( mGridFramePenColor );
1070  framePen.setWidthF( mGridFramePenThickness );
1071  framePen.setCapStyle( Qt::SquareCap );
1072  p->setBrush( Qt::NoBrush );
1073  p->setPen( framePen );
1074 
1075  switch ( border )
1076  {
1078  p->drawLine( QLineF( 0, 0, 0, mComposerMap->rect().height() ) );
1079  break;
1082  break;
1084  p->drawLine( QLineF( 0, 0, mComposerMap->rect().width(), 0 ) );
1085  break;
1088  break;
1089  }
1090 }
1091 
1092 void QgsComposerMapGrid::drawCoordinateAnnotations( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines, QgsExpressionContext &expressionContext,
1093  GridExtension* extension ) const
1094 {
1095  QString currentAnnotationString;
1096  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
1097  for ( ; it != hLines.constEnd(); ++it )
1098  {
1099  currentAnnotationString = gridAnnotationString( it->first, QgsComposerMapGrid::Latitude, expressionContext );
1100  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsComposerMapGrid::Latitude, extension );
1101  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsComposerMapGrid::Latitude, extension );
1102  }
1103 
1104  it = vLines.constBegin();
1105  for ( ; it != vLines.constEnd(); ++it )
1106  {
1107  currentAnnotationString = gridAnnotationString( it->first, QgsComposerMapGrid::Longitude, expressionContext );
1108  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString, QgsComposerMapGrid::Longitude, extension );
1109  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString, QgsComposerMapGrid::Longitude, extension );
1110  }
1111 }
1112 
1113 void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter* p, const QPointF& pos, QString annotationString, const AnnotationCoordinate coordinateType, GridExtension* extension ) const
1114 {
1115  if ( !mComposerMap )
1116  {
1117  return;
1118  }
1119  QgsComposerMapGrid::BorderSide frameBorder = borderForLineCoord( pos, coordinateType );
1120  double textWidth = QgsComposerUtils::textWidthMM( mGridAnnotationFont, annotationString );
1121  //relevant for annotations is the height of digits
1122  double textHeight = extension ? QgsComposerUtils::fontAscentMM( mGridAnnotationFont )
1123  : QgsComposerUtils::fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
1124  double xpos = pos.x();
1125  double ypos = pos.y();
1126  int rotation = 0;
1127 
1128  double gridFrameDistance = 0;
1129  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame && mGridFrameStyle != QgsComposerMapGrid::LineBorder )
1130  {
1131  gridFrameDistance = mGridFrameWidth;
1132  }
1133  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::LineBorder )
1134  {
1135  gridFrameDistance += ( mGridFramePenThickness / 2.0 );
1136  }
1137 
1138  if ( frameBorder == QgsComposerMapGrid::Left )
1139  {
1140  if ( mLeftGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1141  ( coordinateType == Longitude && mLeftGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1142  ( coordinateType == Latitude && mLeftGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1143  {
1144  return;
1145  }
1147  {
1148  gridFrameDistance = 0;
1149  }
1150 
1151  if ( mLeftGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1152  {
1153  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1154  {
1155  gridFrameDistance = 0;
1156  }
1157  if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::Vertical || mLeftGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1158  {
1159  xpos += textHeight + mAnnotationFrameDistance + gridFrameDistance;
1160  ypos += textWidth / 2.0;
1161  rotation = 270;
1162  }
1163  else if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1164  {
1165  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1166  ypos -= textWidth / 2.0;
1167  rotation = 90;
1168  }
1169  else
1170  {
1171  xpos += mAnnotationFrameDistance + gridFrameDistance;
1172  ypos += textHeight / 2.0;
1173  }
1174  }
1175  else if ( mLeftGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //Outside map frame
1176  {
1177  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1178  {
1179  gridFrameDistance = 0;
1180  }
1181  if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::Vertical || mLeftGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1182  {
1183  xpos -= ( mAnnotationFrameDistance + gridFrameDistance );
1184  ypos += textWidth / 2.0;
1185  rotation = 270;
1186  if ( extension )
1187  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1188  }
1189  else if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1190  {
1191  xpos -= textHeight + mAnnotationFrameDistance + gridFrameDistance;
1192  ypos -= textWidth / 2.0;
1193  rotation = 90;
1194  if ( extension )
1195  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1196  }
1197  else
1198  {
1199  xpos -= ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
1200  ypos += textHeight / 2.0;
1201  if ( extension )
1202  extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1203  }
1204  }
1205  else
1206  {
1207  return;
1208  }
1209  }
1210  else if ( frameBorder == QgsComposerMapGrid::Right )
1211  {
1212  if ( mRightGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1213  ( coordinateType == Longitude && mRightGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1214  ( coordinateType == Latitude && mRightGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1215  {
1216  return;
1217  }
1219  {
1220  gridFrameDistance = 0;
1221  }
1222 
1223  if ( mRightGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1224  {
1225  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1226  {
1227  gridFrameDistance = 0;
1228  }
1229  if ( mRightGridAnnotationDirection == QgsComposerMapGrid::Vertical )
1230  {
1231  xpos -= mAnnotationFrameDistance + gridFrameDistance;
1232  ypos += textWidth / 2.0;
1233  rotation = 270;
1234  }
1235  else if ( mRightGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending || mRightGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1236  {
1237  xpos -= textHeight + mAnnotationFrameDistance + gridFrameDistance;
1238  ypos -= textWidth / 2.0;
1239  rotation = 90;
1240  }
1241  else
1242  {
1243  xpos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1244  ypos += textHeight / 2.0;
1245  }
1246  }
1247  else if ( mRightGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame )//OutsideMapFrame
1248  {
1249  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1250  {
1251  gridFrameDistance = 0;
1252  }
1253  if ( mRightGridAnnotationDirection == QgsComposerMapGrid::Vertical )
1254  {
1255  xpos += ( textHeight + mAnnotationFrameDistance + gridFrameDistance );
1256  ypos += textWidth / 2.0;
1257  rotation = 270;
1258  if ( extension )
1259  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1260  }
1261  else if ( mRightGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending || mRightGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1262  {
1263  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1264  ypos -= textWidth / 2.0;
1265  rotation = 90;
1266  if ( extension )
1267  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1268  }
1269  else //Horizontal
1270  {
1271  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1272  ypos += textHeight / 2.0;
1273  if ( extension )
1274  extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1275  }
1276  }
1277  else
1278  {
1279  return;
1280  }
1281  }
1282  else if ( frameBorder == QgsComposerMapGrid::Bottom )
1283  {
1284  if ( mBottomGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1285  ( coordinateType == Longitude && mBottomGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1286  ( coordinateType == Latitude && mBottomGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1287  {
1288  return;
1289  }
1291  {
1292  gridFrameDistance = 0;
1293  }
1294 
1295  if ( mBottomGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1296  {
1297  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1298  {
1299  gridFrameDistance = 0;
1300  }
1301  if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mBottomGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1302  {
1303  ypos -= mAnnotationFrameDistance + gridFrameDistance;
1304  xpos -= textWidth / 2.0;
1305  }
1306  else if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1307  {
1308  xpos -= textHeight / 2.0;
1309  ypos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1310  rotation = 90;
1311  }
1312  else //Vertical
1313  {
1314  xpos += textHeight / 2.0;
1315  ypos -= mAnnotationFrameDistance + gridFrameDistance;
1316  rotation = 270;
1317  }
1318  }
1319  else if ( mBottomGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //OutsideMapFrame
1320  {
1321  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1322  {
1323  gridFrameDistance = 0;
1324  }
1325  if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mBottomGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1326  {
1327  ypos += ( mAnnotationFrameDistance + textHeight + gridFrameDistance );
1328  xpos -= textWidth / 2.0;
1329  if ( extension )
1330  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1331  }
1332  else if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1333  {
1334  xpos -= textHeight / 2.0;
1335  ypos += gridFrameDistance + mAnnotationFrameDistance;
1336  rotation = 90;
1337  if ( extension )
1338  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1339  }
1340  else //Vertical
1341  {
1342  xpos += textHeight / 2.0;
1343  ypos += ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
1344  rotation = 270;
1345  if ( extension )
1346  extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1347  }
1348  }
1349  else
1350  {
1351  return;
1352  }
1353  }
1354  else //top
1355  {
1356  if ( mTopGridAnnotationDisplay == QgsComposerMapGrid::HideAll ||
1357  ( coordinateType == Longitude && mTopGridAnnotationDisplay == QgsComposerMapGrid::LatitudeOnly ) ||
1358  ( coordinateType == Latitude && mTopGridAnnotationDisplay == QgsComposerMapGrid::LongitudeOnly ) )
1359  {
1360  return;
1361  }
1363  {
1364  gridFrameDistance = 0;
1365  }
1366 
1367  if ( mTopGridAnnotationPosition == QgsComposerMapGrid::InsideMapFrame )
1368  {
1369  if ( mGridFrameStyle == QgsComposerMapGrid::Zebra || mGridFrameStyle == QgsComposerMapGrid::ExteriorTicks )
1370  {
1371  gridFrameDistance = 0;
1372  }
1373  if ( mTopGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mTopGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1374  {
1375  xpos -= textWidth / 2.0;
1376  ypos += textHeight + mAnnotationFrameDistance + gridFrameDistance;
1377  }
1378  else if ( mTopGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1379  {
1380  xpos -= textHeight / 2.0;
1381  ypos += mAnnotationFrameDistance + gridFrameDistance;
1382  rotation = 90;
1383  }
1384  else //Vertical
1385  {
1386  xpos += textHeight / 2.0;
1387  ypos += textWidth + mAnnotationFrameDistance + gridFrameDistance;
1388  rotation = 270;
1389  }
1390  }
1391  else if ( mTopGridAnnotationPosition == QgsComposerMapGrid::OutsideMapFrame ) //OutsideMapFrame
1392  {
1393  if ( mGridFrameStyle == QgsComposerMapGrid::InteriorTicks )
1394  {
1395  gridFrameDistance = 0;
1396  }
1397  if ( mTopGridAnnotationDirection == QgsComposerMapGrid::Horizontal || mTopGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection )
1398  {
1399  xpos -= textWidth / 2.0;
1400  ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
1401  if ( extension )
1402  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1403  }
1404  else if ( mTopGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending )
1405  {
1406  xpos -= textHeight / 2.0;
1407  ypos -= textWidth + mAnnotationFrameDistance + gridFrameDistance;
1408  rotation = 90;
1409  if ( extension )
1410  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1411  }
1412  else //Vertical
1413  {
1414  xpos += textHeight / 2.0;
1415  ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
1416  rotation = 270;
1417  if ( extension )
1418  extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth );
1419  }
1420  }
1421  else
1422  {
1423  return;
1424  }
1425  }
1426 
1427  if ( extension || !p )
1428  return;
1429 
1430  drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
1431 }
1432 
1433 void QgsComposerMapGrid::drawAnnotation( QPainter* p, const QPointF& pos, int rotation, const QString& annotationText ) const
1434 {
1435  if ( !mComposerMap )
1436  {
1437  return;
1438  }
1439 
1440  p->save();
1441  p->translate( pos );
1442  p->rotate( rotation );
1443  QgsComposerUtils::drawText( p, QPointF( 0, 0 ), annotationText, mGridAnnotationFont, mGridAnnotationFontColor );
1444  p->restore();
1445 }
1446 
1447 QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGrid::AnnotationCoordinate coord, QgsExpressionContext &expressionContext ) const
1448 {
1449  //check if we are using degrees (ie, geographic crs)
1450  bool geographic = false;
1451  if ( mCRS.isValid() && mCRS.geographicFlag() )
1452  {
1453  geographic = true;
1454  }
1455  else if ( mComposerMap && mComposerMap->composition() )
1456  {
1458  }
1459 
1460  if ( geographic && coord == QgsComposerMapGrid::Longitude &&
1461  ( mGridAnnotationFormat == QgsComposerMapGrid::Decimal || mGridAnnotationFormat == QgsComposerMapGrid::DecimalWithSuffix ) )
1462  {
1463  // wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
1464  double wrappedX = fmod( value, 360.0 );
1465  if ( wrappedX > 180.0 )
1466  {
1467  value = wrappedX - 360.0;
1468  }
1469  else if ( wrappedX < -180.0 )
1470  {
1471  value = wrappedX + 360.0;
1472  }
1473  }
1474 
1475  if ( mGridAnnotationFormat == QgsComposerMapGrid::Decimal )
1476  {
1477  return QString::number( value, 'f', mGridAnnotationPrecision );
1478  }
1479  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DecimalWithSuffix )
1480  {
1481  QString hemisphere;
1482 
1483  double coordRounded = qRound( value * pow( 10.0, mGridAnnotationPrecision ) ) / pow( 10.0, mGridAnnotationPrecision );
1484  if ( coord == QgsComposerMapGrid::Longitude )
1485  {
1486  //don't use E/W suffixes if ambiguous (eg 180 degrees)
1487  if ( !geographic || ( coordRounded != 180.0 && coordRounded != 0.0 ) )
1488  {
1489  hemisphere = value < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
1490  }
1491  }
1492  else
1493  {
1494  //don't use N/S suffixes if ambiguous (eg 0 degrees)
1495  if ( !geographic || coordRounded != 0.0 )
1496  {
1497  hemisphere = value < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
1498  }
1499  }
1500  if ( geographic )
1501  {
1502  //insert degree symbol for geographic coordinates
1503  return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere;
1504  }
1505  else
1506  {
1507  return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + hemisphere;
1508  }
1509  }
1510  else if ( mGridAnnotationFormat == CustomFormat )
1511  {
1512  expressionContext.lastScope()->setVariable( "grid_number", value );
1513  expressionContext.lastScope()->setVariable( "grid_axis", coord == QgsComposerMapGrid::Longitude ? "x" : "y" );
1514  if ( !mGridAnnotationExpression.data() )
1515  {
1516  mGridAnnotationExpression.reset( new QgsExpression( mGridAnnotationExpressionString ) );
1517  mGridAnnotationExpression->prepare( &expressionContext );
1518  }
1519  return mGridAnnotationExpression->evaluate( &expressionContext ).toString();
1520  }
1521 
1522  QgsPoint p;
1523  p.setX( coord == QgsComposerMapGrid::Longitude ? value : 0 );
1524  p.setY( coord == QgsComposerMapGrid::Longitude ? 0 : value );
1525 
1526  QString annotationString;
1527  if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinute )
1528  {
1529  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision );
1530  }
1531  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteNoSuffix )
1532  {
1533  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision, false );
1534  }
1535  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinutePadded )
1536  {
1537  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision, true, true );
1538  }
1539  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecond )
1540  {
1541  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision );
1542  }
1543  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecondNoSuffix )
1544  {
1545  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision, false );
1546  }
1547  else if ( mGridAnnotationFormat == QgsComposerMapGrid::DegreeMinuteSecondPadded )
1548  {
1549  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision, true, true );
1550  }
1551 
1552  QStringList split = annotationString.split( "," );
1553  if ( coord == QgsComposerMapGrid::Longitude )
1554  {
1555  return split.at( 0 );
1556  }
1557  else
1558  {
1559  if ( split.size() < 2 )
1560  {
1561  return "";
1562  }
1563  return split.at( 1 );
1564  }
1565 }
1566 
1567 int QgsComposerMapGrid::xGridLines( QList< QPair< double, QLineF > >& lines ) const
1568 {
1569  lines.clear();
1570  if ( !mComposerMap || mGridIntervalY <= 0.0 )
1571  {
1572  return 1;
1573  }
1574 
1575 
1577  QRectF mapBoundingRect = mapPolygon.boundingRect();
1578  double gridIntervalY = mGridIntervalY;
1579  double gridOffsetY = mGridOffsetY;
1580  double annotationScale = 1.0;
1581  if ( mGridUnit != MapUnit )
1582  {
1583  mapBoundingRect = mComposerMap->rect();
1584  mapPolygon = QPolygonF( mComposerMap->rect() );
1585  if ( mGridUnit == CM )
1586  {
1587  annotationScale = 0.1;
1588  gridIntervalY *= 10; gridOffsetY *= 10;
1589  }
1590  }
1591 
1592  //consider to round up to the next step in case the left boundary is > 0
1593  double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
1594  double currentLevel = ( int )(( mapBoundingRect.top() - gridOffsetY ) / gridIntervalY + roundCorrection ) * gridIntervalY + gridOffsetY;
1595 
1596  int gridLineCount = 0;
1597  if ( qgsDoubleNear( mComposerMap->mapRotation(), 0.0 ) || mGridUnit != MapUnit )
1598  {
1599  //no rotation. Do it 'the easy way'
1600 
1601  double yCanvasCoord;
1602  while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1603  {
1604  yCanvasCoord = mComposerMap->rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
1605  lines.push_back( qMakePair( currentLevel * annotationScale, QLineF( 0, yCanvasCoord, mComposerMap->rect().width(), yCanvasCoord ) ) );
1606  currentLevel += gridIntervalY;
1607  gridLineCount++;
1608  }
1609  return 0;
1610  }
1611 
1612  //the four border lines
1613  QVector<QLineF> borderLines;
1614  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1615  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1616  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1617  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1618 
1619  QList<QPointF> intersectionList; //intersects between border lines and grid lines
1620 
1621  while ( currentLevel <= mapBoundingRect.bottom() && gridLineCount < MAX_GRID_LINES )
1622  {
1623  intersectionList.clear();
1624  QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
1625 
1626  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1627  for ( ; it != borderLines.constEnd(); ++it )
1628  {
1629  QPointF intersectionPoint;
1630  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1631  {
1632  intersectionList.push_back( intersectionPoint );
1633  if ( intersectionList.size() >= 2 )
1634  {
1635  break; //we already have two intersections, skip further tests
1636  }
1637  }
1638  }
1639 
1640  if ( intersectionList.size() >= 2 )
1641  {
1642  lines.push_back( qMakePair( currentLevel, QLineF( mComposerMap->mapToItemCoords( intersectionList.at( 0 ) ), mComposerMap->mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1643  gridLineCount++;
1644  }
1645  currentLevel += gridIntervalY;
1646  }
1647 
1648 
1649  return 0;
1650 }
1651 
1652 int QgsComposerMapGrid::yGridLines( QList< QPair< double, QLineF > >& lines ) const
1653 {
1654  lines.clear();
1655  if ( !mComposerMap || mGridIntervalX <= 0.0 )
1656  {
1657  return 1;
1658  }
1659 
1661  QRectF mapBoundingRect = mapPolygon.boundingRect();
1662  double gridIntervalX = mGridIntervalX;
1663  double gridOffsetX = mGridOffsetX;
1664  double annotationScale = 1.0;
1665  if ( mGridUnit != MapUnit )
1666  {
1667  mapBoundingRect = mComposerMap->rect();
1668  mapPolygon = QPolygonF( mComposerMap->rect() );
1669  if ( mGridUnit == CM )
1670  {
1671  annotationScale = 0.1;
1672  gridIntervalX *= 10; gridOffsetX *= 10;
1673  }
1674  }
1675 
1676  //consider to round up to the next step in case the left boundary is > 0
1677  double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
1678  double currentLevel = ( int )(( mapBoundingRect.left() - gridOffsetX ) / gridIntervalX + roundCorrection ) * gridIntervalX + gridOffsetX;
1679 
1680  int gridLineCount = 0;
1681  if ( qgsDoubleNear( mComposerMap->mapRotation(), 0.0 ) || mGridUnit != MapUnit )
1682  {
1683  //no rotation. Do it 'the easy way'
1684  double xCanvasCoord;
1685  while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1686  {
1687  xCanvasCoord = mComposerMap->rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
1688  lines.push_back( qMakePair( currentLevel * annotationScale, QLineF( xCanvasCoord, 0, xCanvasCoord, mComposerMap->rect().height() ) ) );
1689  currentLevel += gridIntervalX;
1690  gridLineCount++;
1691  }
1692  return 0;
1693  }
1694 
1695  //the four border lines
1696  QVector<QLineF> borderLines;
1697  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1698  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1699  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1700  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1701 
1702  QList<QPointF> intersectionList; //intersects between border lines and grid lines
1703 
1704  while ( currentLevel <= mapBoundingRect.right() && gridLineCount < MAX_GRID_LINES )
1705  {
1706  intersectionList.clear();
1707  QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
1708 
1709  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1710  for ( ; it != borderLines.constEnd(); ++it )
1711  {
1712  QPointF intersectionPoint;
1713  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1714  {
1715  intersectionList.push_back( intersectionPoint );
1716  if ( intersectionList.size() >= 2 )
1717  {
1718  break; //we already have two intersections, skip further tests
1719  }
1720  }
1721  }
1722 
1723  if ( intersectionList.size() >= 2 )
1724  {
1725  lines.push_back( qMakePair( currentLevel, QLineF( mComposerMap->mapToItemCoords( intersectionList.at( 0 ) ), mComposerMap->mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1726  gridLineCount++;
1727  }
1728  currentLevel += gridIntervalX;
1729  }
1730 
1731  return 0;
1732 }
1733 
1734 int QgsComposerMapGrid::xGridLinesCRSTransform( const QgsRectangle& bbox, const QgsCoordinateTransform& t, QList< QPair< double, QPolygonF > >& lines ) const
1735 {
1736  lines.clear();
1737  if ( !mComposerMap || mGridIntervalY <= 0.0 )
1738  {
1739  return 1;
1740  }
1741 
1742  double roundCorrection = bbox.yMaximum() > 0 ? 1.0 : 0.0;
1743  double currentLevel = ( int )(( bbox.yMaximum() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY;
1744 
1745  double minX = bbox.xMinimum();
1746  double maxX = bbox.xMaximum();
1747  double step = ( maxX - minX ) / 20;
1748 
1749  bool crosses180 = false;
1750  bool crossed180 = false;
1751  if ( mCRS.geographicFlag() && ( minX > maxX ) )
1752  {
1753  //handle 180 degree longitude crossover
1754  crosses180 = true;
1755  step = ( maxX + 360.0 - minX ) / 20;
1756  }
1757 
1758  if ( step == 0 )
1759  return 1;
1760 
1761  int gridLineCount = 0;
1762  while ( currentLevel >= bbox.yMinimum() && gridLineCount < MAX_GRID_LINES )
1763  {
1764  QPolygonF gridLine;
1765  double currentX = minX;
1766  bool cont = true;
1767  while ( cont )
1768  {
1769  if (( !crosses180 || crossed180 ) && ( currentX > maxX ) )
1770  {
1771  cont = false;
1772  }
1773 
1774  try
1775  {
1776  QgsPoint mapPoint = t.transform( currentX, currentLevel ); //transform back to map crs
1777  gridLine.append( mComposerMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) ); //transform back to composer coords
1778  }
1779  catch ( QgsCsException & cse )
1780  {
1781  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
1782  }
1783 
1784  currentX += step;
1785  if ( crosses180 && currentX > 180.0 )
1786  {
1787  currentX -= 360.0;
1788  crossed180 = true;
1789  }
1790  }
1791  crossed180 = false;
1792 
1793  QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
1794  QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1795  for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1796  {
1797  if (( *lineIt ).size() > 0 )
1798  {
1799  lines.append( qMakePair( currentLevel, *lineIt ) );
1800  gridLineCount++;
1801  }
1802  }
1803  currentLevel -= mGridIntervalY;
1804  }
1805 
1806  return 0;
1807 }
1808 
1809 int QgsComposerMapGrid::yGridLinesCRSTransform( const QgsRectangle& bbox, const QgsCoordinateTransform& t, QList< QPair< double, QPolygonF > >& lines ) const
1810 {
1811  lines.clear();
1812  if ( !mComposerMap || mGridIntervalX <= 0.0 )
1813  {
1814  return 1;
1815  }
1816 
1817  double roundCorrection = bbox.xMinimum() > 0 ? 1.0 : 0.0;
1818  double currentLevel = ( int )(( bbox.xMinimum() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
1819 
1820  double minY = bbox.yMinimum();
1821  double maxY = bbox.yMaximum();
1822  double step = ( maxY - minY ) / 20;
1823 
1824  if ( step == 0 )
1825  return 1;
1826 
1827  bool crosses180 = false;
1828  bool crossed180 = false;
1829  if ( mCRS.geographicFlag() && ( bbox.xMinimum() > bbox.xMaximum() ) )
1830  {
1831  //handle 180 degree longitude crossover
1832  crosses180 = true;
1833  }
1834 
1835  int gridLineCount = 0;
1836  while (( currentLevel <= bbox.xMaximum() || ( crosses180 && !crossed180 ) ) && gridLineCount < MAX_GRID_LINES )
1837  {
1838  QPolygonF gridLine;
1839  double currentY = minY;
1840  bool cont = true;
1841  while ( cont )
1842  {
1843  if ( currentY > maxY )
1844  {
1845  cont = false;
1846  }
1847  try
1848  {
1849  //transform back to map crs
1850  QgsPoint mapPoint = t.transform( currentLevel, currentY );
1851  //transform back to composer coords
1852  gridLine.append( mComposerMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) );
1853  }
1854  catch ( QgsCsException & cse )
1855  {
1856  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
1857  }
1858 
1859  currentY += step;
1860  }
1861  //clip grid line to map polygon
1862  QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
1863  QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
1864  for ( ; lineIt != lineSegments.constEnd(); ++lineIt )
1865  {
1866  if (( *lineIt ).size() > 0 )
1867  {
1868  lines.append( qMakePair( currentLevel, *lineIt ) );
1869  gridLineCount++;
1870  }
1871  }
1872  currentLevel += mGridIntervalX;
1873  if ( crosses180 && currentLevel > 180.0 )
1874  {
1875  currentLevel -= 360.0;
1876  crossed180 = true;
1877  }
1878  }
1879 
1880  return 0;
1881 }
1882 
1883 void QgsComposerMapGrid::sortGridLinesOnBorders( const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines, QMap< double, double >& leftFrameEntries,
1884  QMap< double, double >& rightFrameEntries, QMap< double, double >& topFrameEntries, QMap< double, double >& bottomFrameEntries ) const
1885 {
1886  QList< QgsMapAnnotation > borderPositions;
1887  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
1888  for ( ; it != hLines.constEnd(); ++it )
1889  {
1890  QgsMapAnnotation p1;
1891  p1.coordinate = it->first;
1892  p1.itemPosition = it->second.p1();
1893  p1.coordinateType = QgsComposerMapGrid::Latitude;
1894  borderPositions << p1;
1895 
1896  QgsMapAnnotation p2;
1897  p2.coordinate = it->first;
1898  p2.itemPosition = it->second.p2();
1899  p2.coordinateType = QgsComposerMapGrid::Latitude;
1900  borderPositions << p2;
1901  }
1902  it = vLines.constBegin();
1903  for ( ; it != vLines.constEnd(); ++it )
1904  {
1905  QgsMapAnnotation p1;
1906  p1.coordinate = it->first;
1907  p1.itemPosition = it->second.p1();
1908  p1.coordinateType = QgsComposerMapGrid::Longitude;
1909  borderPositions << p1;
1910 
1911  QgsMapAnnotation p2;
1912  p2.coordinate = it->first;
1913  p2.itemPosition = it->second.p2();
1914  p2.coordinateType = QgsComposerMapGrid::Longitude;
1915  borderPositions << p2;
1916  }
1917 
1918  QList< QgsMapAnnotation >::const_iterator bIt = borderPositions.constBegin();
1919  for ( ; bIt != borderPositions.constEnd(); ++bIt )
1920  {
1921  QgsComposerMapGrid::BorderSide frameBorder = borderForLineCoord( bIt->itemPosition, bIt->coordinateType );
1922  if ( frameBorder == QgsComposerMapGrid::Left && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Left ) )
1923  {
1924  leftFrameEntries.insert( bIt->itemPosition.y(), bIt->coordinate );
1925  }
1926  else if ( frameBorder == QgsComposerMapGrid::Right && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Right ) )
1927  {
1928  rightFrameEntries.insert( bIt->itemPosition.y(), bIt->coordinate );
1929  }
1930  else if ( frameBorder == QgsComposerMapGrid::Top && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Top ) )
1931  {
1932  topFrameEntries.insert( bIt->itemPosition.x(), bIt->coordinate );
1933  }
1934  else if ( frameBorder == QgsComposerMapGrid::Bottom && shouldShowDivisionForSide( bIt->coordinateType, QgsComposerMapGrid::Bottom ) )
1935  {
1936  bottomFrameEntries.insert( bIt->itemPosition.x(), bIt->coordinate );
1937  }
1938  }
1939 }
1940 
1941 bool QgsComposerMapGrid::shouldShowDivisionForSide( const QgsComposerMapGrid::AnnotationCoordinate& coordinate, const QgsComposerMapGrid::BorderSide& side ) const
1942 {
1943  switch ( side )
1944  {
1946  return shouldShowDivisionForDisplayMode( coordinate, mLeftFrameDivisions );
1948  return shouldShowDivisionForDisplayMode( coordinate, mRightFrameDivisions );
1950  return shouldShowDivisionForDisplayMode( coordinate, mTopFrameDivisions );
1952  default: //prevent warnings
1953  return shouldShowDivisionForDisplayMode( coordinate, mBottomFrameDivisions );
1954  }
1955 }
1956 
1957 bool QgsComposerMapGrid::shouldShowDivisionForDisplayMode( const QgsComposerMapGrid::AnnotationCoordinate& coordinate, const QgsComposerMapGrid::DisplayMode& mode ) const
1958 {
1959  return mode == QgsComposerMapGrid::ShowAll
1960  || ( mode == QgsComposerMapGrid::LatitudeOnly && coordinate == QgsComposerMapGrid::Latitude )
1961  || ( mode == QgsComposerMapGrid::LongitudeOnly && coordinate == QgsComposerMapGrid::Longitude );
1962 }
1963 
1965 {
1966  return a.first < b.first;
1967 }
1968 
1969 QgsComposerMapGrid::BorderSide QgsComposerMapGrid::borderForLineCoord( const QPointF& p, const AnnotationCoordinate coordinateType ) const
1970 {
1971  if ( !mComposerMap )
1972  {
1973  return QgsComposerMapGrid::Left;
1974  }
1975 
1976  double tolerance = qMax( mComposerMap->hasFrame() ? mComposerMap->pen().widthF() : 0.0, 1.0 );
1977 
1978  //check for corner coordinates
1979  if (( p.y() <= tolerance && p.x() <= tolerance ) // top left
1980  || ( p.y() <= tolerance && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //top right
1981  || ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() <= tolerance ) //bottom left
1982  || ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //bottom right
1983  )
1984  {
1985  //coordinate is in corner - fall back to preferred side for coordinate type
1986  if ( coordinateType == QgsComposerMapGrid::Latitude )
1987  {
1988  if ( p.x() <= tolerance )
1989  {
1990  return QgsComposerMapGrid::Left;
1991  }
1992  else
1993  {
1995  }
1996  }
1997  else
1998  {
1999  if ( p.y() <= tolerance )
2000  {
2001  return QgsComposerMapGrid::Top;
2002  }
2003  else
2004  {
2006  }
2007  }
2008  }
2009 
2010  //otherwise, guess side based on closest map side to point
2012  distanceToSide << qMakePair( p.x(), QgsComposerMapGrid::Left );
2013  distanceToSide << qMakePair( mComposerMap->rect().width() - p.x(), QgsComposerMapGrid::Right );
2014  distanceToSide << qMakePair( p.y(), QgsComposerMapGrid::Top );
2015  distanceToSide << qMakePair( mComposerMap->rect().height() - p.y(), QgsComposerMapGrid::Bottom );
2016 
2017  qSort( distanceToSide.begin(), distanceToSide.end(), sortByDistance );
2018  return distanceToSide.at( 0 ).second;
2019 }
2020 
2022 {
2023  delete mGridLineSymbol;
2024  mGridLineSymbol = symbol;
2025 }
2026 
2028 {
2029  delete mGridMarkerSymbol;
2030  mGridMarkerSymbol = symbol;
2031 }
2032 
2034 {
2035  switch ( border )
2036  {
2038  mLeftGridAnnotationDisplay = display;
2039  break;
2041  mRightGridAnnotationDisplay = display;
2042  break;
2044  mTopGridAnnotationDisplay = display;
2045  break;
2047  mBottomGridAnnotationDisplay = display;
2048  break;
2049  default:
2050  return;
2051  }
2052 
2053  if ( mComposerMap )
2054  {
2056  mComposerMap->update();
2057  }
2058 }
2059 
2061 {
2062  switch ( border )
2063  {
2065  return mLeftGridAnnotationDisplay;
2066  break;
2068  return mRightGridAnnotationDisplay;
2069  break;
2071  return mTopGridAnnotationDisplay;
2072  break;
2074  default:
2075  return mBottomGridAnnotationDisplay;
2076  break;
2077  }
2078 }
2079 
2081 {
2082  double top = 0.0;
2083  double right = 0.0;
2084  double bottom = 0.0;
2085  double left = 0.0;
2086  calculateMaxExtension( top, right, bottom, left );
2087  return qMax( qMax( qMax( top, right ), bottom ), left );
2088 }
2089 
2090 void QgsComposerMapGrid::calculateMaxExtension( double& top, double& right, double& bottom, double& left )
2091 {
2092  top = 0.0;
2093  right = 0.0;
2094  bottom = 0.0;
2095  left = 0.0;
2096 
2097  if ( !mComposerMap || !mEnabled )
2098  {
2099  return;
2100  }
2101 
2102  //setup render context
2105  QgsExpressionContext* expressionContext = createExpressionContext();
2106  context.setExpressionContext( *expressionContext );
2107  delete expressionContext;
2108 
2109  GridExtension extension;
2110 
2111  //collect grid lines
2112  QList< QPair< double, QLineF > > verticalLines;
2113  QList< QPair< double, QLineF > > horizontalLines;
2114  if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != ms.destinationCrs() )
2115  {
2116  drawGridCRSTransform( context, 0, horizontalLines, verticalLines, false );
2117  }
2118  else
2119  {
2120  drawGridNoTransform( context, 0, horizontalLines, verticalLines, false );
2121  }
2122 
2123  if ( mGridFrameStyle != QgsComposerMapGrid::NoFrame )
2124  {
2125  drawGridFrame( 0, horizontalLines, verticalLines, &extension );
2126  }
2127 
2128  if ( mShowGridAnnotation )
2129  {
2130  drawCoordinateAnnotations( 0, horizontalLines, verticalLines, context.expressionContext(), &extension );
2131  }
2132 
2133  top = extension.top;
2134  right = extension.right;
2135  bottom = extension.bottom;
2136  left = extension.left;
2137 }
2138 
2140 {
2141  if ( unit == mGridUnit )
2142  {
2143  return;
2144  }
2145  mGridUnit = unit;
2146  mTransformDirty = true;
2147 }
2148 
2149 void QgsComposerMapGrid::setIntervalX( const double interval )
2150 {
2151  if ( interval == mGridIntervalX )
2152  {
2153  return;
2154  }
2155  mGridIntervalX = interval;
2156  mTransformDirty = true;
2157 }
2158 
2159 void QgsComposerMapGrid::setIntervalY( const double interval )
2160 {
2161  if ( interval == mGridIntervalY )
2162  {
2163  return;
2164  }
2165  mGridIntervalY = interval;
2166  mTransformDirty = true;
2167 }
2168 
2169 void QgsComposerMapGrid::setOffsetX( const double offset )
2170 {
2171  if ( offset == mGridOffsetX )
2172  {
2173  return;
2174  }
2175  mGridOffsetX = offset;
2176  mTransformDirty = true;
2177 }
2178 
2179 void QgsComposerMapGrid::setOffsetY( const double offset )
2180 {
2181  if ( offset == mGridOffsetY )
2182  {
2183  return;
2184  }
2185  mGridOffsetY = offset;
2186  mTransformDirty = true;
2187 }
2188 
2190 {
2191  if ( style == mGridStyle )
2192  {
2193  return;
2194  }
2195  mGridStyle = style;
2196  mTransformDirty = true;
2197 }
2198 
2200 {
2201  switch ( border )
2202  {
2204  mLeftGridAnnotationDirection = direction;
2205  break;
2207  mRightGridAnnotationDirection = direction;
2208  break;
2210  mTopGridAnnotationDirection = direction;
2211  break;
2213  mBottomGridAnnotationDirection = direction;
2214  break;
2215  default:
2216  return;
2217  break;
2218  }
2219 
2220  if ( mComposerMap )
2221  {
2223  mComposerMap->update();
2224  }
2225 }
2226 
2227 void QgsComposerMapGrid::setFrameSideFlags( const FrameSideFlags& flags )
2228 {
2229  mGridFrameSides = flags;
2230 }
2231 
2233 {
2234  if ( on )
2235  mGridFrameSides |= flag;
2236  else
2237  mGridFrameSides &= ~flag;
2238 }
2239 
2240 QgsComposerMapGrid::FrameSideFlags QgsComposerMapGrid::frameSideFlags() const
2241 {
2242  return mGridFrameSides;
2243 }
2244 
2246 {
2248  context->appendScope( new QgsExpressionContextScope( tr( "Grid" ) ) );
2249  context->lastScope()->setVariable( "grid_number", 0 );
2250  context->lastScope()->setVariable( "grid_axis", "x" );
2251  context->setHighlightedVariables( QStringList() << "grid_number" << "grid_axis" );
2252  return context;
2253 }
2254 
2256 {
2257  return mGridFrameSides.testFlag( flag );
2258 }
2259 
2261 {
2262  mLeftGridAnnotationDirection = direction;
2263  mRightGridAnnotationDirection = direction;
2264  mTopGridAnnotationDirection = direction;
2265  mBottomGridAnnotationDirection = direction;
2266 }
2267 
2269 {
2270  switch ( border )
2271  {
2273  mLeftGridAnnotationPosition = position;
2274  break;
2276  mRightGridAnnotationPosition = position;
2277  break;
2279  mTopGridAnnotationPosition = position;
2280  break;
2282  mBottomGridAnnotationPosition = position;
2283  break;
2284  default:
2285  return;
2286  }
2287 
2288  if ( mComposerMap )
2289  {
2291  mComposerMap->update();
2292  }
2293 }
2294 
2296 {
2297  switch ( border )
2298  {
2300  return mLeftGridAnnotationPosition;
2301  break;
2303  return mRightGridAnnotationPosition;
2304  break;
2306  return mTopGridAnnotationPosition;
2307  break;
2309  default:
2310  return mBottomGridAnnotationPosition;
2311  break;
2312  }
2313 }
2314 
2316 {
2317  if ( !mComposerMap )
2318  {
2319  return mLeftGridAnnotationDirection;
2320  }
2321 
2322  switch ( border )
2323  {
2325  return mLeftGridAnnotationDirection;
2326  break;
2328  return mRightGridAnnotationDirection;
2329  break;
2331  return mTopGridAnnotationDirection;
2332  break;
2334  default:
2335  return mBottomGridAnnotationDirection;
2336  break;
2337  }
2338 }
2339 
2341 {
2342  switch ( border )
2343  {
2345  mLeftFrameDivisions = divisions;
2346  break;
2348  mRightFrameDivisions = divisions;
2349  break;
2351  mTopFrameDivisions = divisions;
2352  break;
2354  mBottomFrameDivisions = divisions;
2355  break;
2356  default:
2357  return;
2358  }
2359 
2360  if ( mComposerMap )
2361  {
2362  mComposerMap->update();
2363  }
2364 }
2365 
2367 {
2368  switch ( border )
2369  {
2371  return mLeftFrameDivisions;
2372  break;
2374  return mRightFrameDivisions;
2375  break;
2377  return mTopFrameDivisions;
2378  break;
2380  default:
2381  return mBottomFrameDivisions;
2382  break;
2383  }
2384 }
2385 
2386 int QgsComposerMapGrid::crsGridParams( QgsRectangle& crsRect, QgsCoordinateTransform& inverseTransform ) const
2387 {
2388  if ( !mComposerMap )
2389  {
2390  return 1;
2391  }
2392 
2393  try
2394  {
2397  QRectF mbr = mapPolygon.boundingRect();
2398  QgsRectangle mapBoundingRect( mbr.left(), mbr.bottom(), mbr.right(), mbr.top() );
2399 
2400 
2401  if ( mCRS.geographicFlag() )
2402  {
2403  //handle crossing the 180 degree longitude line
2404  QgsPoint lowerLeft( mapBoundingRect.xMinimum(), mapBoundingRect.yMinimum() );
2405  QgsPoint upperRight( mapBoundingRect.xMaximum(), mapBoundingRect.yMaximum() );
2406 
2407  lowerLeft = tr.transform( lowerLeft.x(), lowerLeft.y() );
2408  upperRight = tr.transform( upperRight.x(), upperRight.y() );
2409 
2410  if ( lowerLeft.x() > upperRight.x() )
2411  {
2412  //we've crossed the line
2413  crsRect = tr.transformBoundingBox( mapBoundingRect, QgsCoordinateTransform::ForwardTransform, true );
2414  }
2415  else
2416  {
2417  //didn't cross the line
2418  crsRect = tr.transformBoundingBox( mapBoundingRect );
2419  }
2420  }
2421  else
2422  {
2423  crsRect = tr.transformBoundingBox( mapBoundingRect );
2424  }
2425 
2426  inverseTransform.setSourceCrs( mCRS );
2427  inverseTransform.setDestCRS( mComposerMap->composition()->mapSettings().destinationCrs() );
2428  }
2429  catch ( QgsCsException & cse )
2430  {
2431  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
2432  return 1;
2433  }
2434  return 0;
2435 }
2436 
2437 QList<QPolygonF> QgsComposerMapGrid::trimLinesToMap( const QPolygonF& line, const QgsRectangle& rect )
2438 {
2439  QgsGeometry* lineGeom = QgsGeometry::fromQPolygonF( line );
2440  QgsGeometry* rectGeom = QgsGeometry::fromRect( rect );
2441 
2442  QgsGeometry* intersected = lineGeom->intersection( rectGeom );
2443  QList<QgsGeometry*> intersectedParts = intersected->asGeometryCollection();
2444 
2445  QList<QPolygonF> trimmedLines;
2446  QList<QgsGeometry*>::const_iterator geomIt = intersectedParts.constBegin();
2447  for ( ; geomIt != intersectedParts.constEnd(); ++geomIt )
2448  {
2449  trimmedLines << ( *geomIt )->asQPolygonF();
2450  }
2451 
2452  qDeleteAll( intersectedParts );
2453  intersectedParts.clear();
2454  delete intersected;
2455  delete lineGeom;
2456  delete rectGeom;
2457  return trimmedLines;
2458 }
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").
Definition: qgsexpression.h:92
void setStyle(const GridStyle style)
Sets the grid style, which controls how the grid is drawn over the map'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
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.
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)
QRectF sceneBoundingRect() const
void save()
static void drawText(QPainter *painter, const 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 ...
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'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:367
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:268
void update(const QRectF &rect)
double x() const
Get the x value of the point.
Definition: qgspoint.h:126
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'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. ...
void setCapStyle(Qt::PenCapStyle style)
qreal bottom() const
void drawRect(const QRectF &rectangle)
void setColor(const QColor &color)
virtual QgsExpressionContext * createExpressionContext() const
Creates an expression context relating to the objects's current state.
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)
QPointF p1() const
QPointF p2() const
void updateBoundingRect()
Updates the bounding rect of this item.
#define MAX_GRID_LINES
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.
void startRender(QgsRenderContext &context, const QgsFields *fields=0)
static QgsLineSymbolV2 * createSimple(const QgsStringMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties. ...
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
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'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's style, which controls how the grid is drawn over the map'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'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.
void renderPoint(const QPointF &point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
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:63
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's current state.
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:103
BorderSide
Border sides for annotations.
QString qgsDoubleToString(const double &a, const int &precision=17)
Definition: qgis.h:257
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:111
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
Find out whether this CRS is correctly initialised and usable.
QString what() const
Definition: qgsexception.h:35
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)
void setOutputSize(const QSize &size)
Set the size of the resulting map image.
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()
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 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:134
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
bool readXML(QDomNode &theNode)
Restores state from the given Dom node.
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'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. ...
QPointF mapToItemCoords(const QPointF &mapCoords) const
Transforms map coordinates to item coordinates (considering rotation and move offset) ...
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.
static QPointF pointOnLineWithDistance(const QPointF &startPoint, const QPointF &directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
bool geographicFlag() const
Get this Geographic? flag.
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.
bool sortByDistance(const QPair< qreal, QgsComposerMapGrid::BorderSide > &a, const QPair< qreal, QgsComposerMapGrid::BorderSide > &b)
QDomNode at(int index) const
QRectF rect() const
Utilities for compositions.
QgsComposerMap * mComposerMap
uint toUInt(bool *ok, int base) const