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