QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposermap.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposermap.cpp
3  -------------------
4  begin : January 2005
5  copyright : (C) 2005 by Radim Blazek
6  email : [email protected]
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 "qgscomposermap.h"
19 #include "qgscomposition.h"
20 #include "qgscoordinatetransform.h"
21 #include "qgslogger.h"
22 #include "qgsmaprenderer.h"
23 #include "qgsmaplayerregistry.h"
24 #include "qgsmaptopixel.h"
25 #include "qgsproject.h"
26 #include "qgsrasterlayer.h"
27 #include "qgsrendercontext.h"
28 #include "qgsscalecalculator.h"
29 #include "qgsvectorlayer.h"
30 #include "qgspallabeling.h"
31 
32 #include "qgslabel.h"
33 #include "qgslabelattributes.h"
34 #include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance
35 
36 #include <QGraphicsScene>
37 #include <QGraphicsView>
38 #include <QPainter>
39 #include <QSettings>
40 #include <cmath>
41 
42 QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
43  : QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ),
44  mOverviewFrameMapId( -1 ), mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
45  mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationFontColor( QColor( 0, 0, 0 ) ),
46  mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), mGridBlendMode( QPainter::CompositionMode_SourceOver ),
47  mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ),
48  mTopGridAnnotationPosition( OutsideMapFrame ), mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ),
49  mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ), mTopGridAnnotationDirection( Horizontal ),
50  mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 ),
51  mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true )
52 {
55  mGridLineSymbol = 0;
58 
59  mId = 0;
60  assignFreeId();
61 
64  mCurrentRectangle = rect();
65 
66  // Cache
67  mCacheUpdated = false;
68  mDrawing = false;
69 
70  //Offset
71  mXOffset = 0.0;
72  mYOffset = 0.0;
73 
75 
76  //calculate mExtent based on width/height ratio and map canvas extent
77  if ( mMapRenderer )
78  {
80  }
81  setSceneRect( QRectF( x, y, width, height ) );
82  setToolTip( tr( "Map %1" ).arg( mId ) );
83 
85 }
86 
88  : QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 ),
89  mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
90  mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationFontColor( QColor( 0, 0, 0 ) ),
91  mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), mGridBlendMode( QPainter::CompositionMode_SourceOver ),
92  mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ),
93  mTopGridAnnotationPosition( OutsideMapFrame ), mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ),
94  mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ), mTopGridAnnotationDirection( Horizontal ),
95  mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 ), mCrossLength( 3 ),
96  mMapCanvas( 0 ), mDrawCanvasItems( true )
97 {
99  mGridLineSymbol = 0;
101 
102  //Offset
103  mXOffset = 0.0;
104  mYOffset = 0.0;
105 
107 
110  mId = mComposition->composerMapItems().size();
112  mCurrentRectangle = rect();
113 
114  setToolTip( tr( "Map %1" ).arg( mId ) );
115 
117 }
118 
120 {
122  delete mGridLineSymbol;
123 }
124 
125 /* This function is called by paint() and cache() to render the map. It does not override any functions
126 from QGraphicsItem. */
127 void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSizeF& size, double dpi, double* forceWidthScale )
128 {
129  if ( !painter )
130  {
131  return;
132  }
133 
134  if ( !mMapRenderer )
135  {
136  return;
137  }
138 
139  QgsMapRenderer theMapRenderer;
140  theMapRenderer.setExtent( extent );
141  theMapRenderer.setOutputSize( size, dpi );
142  if ( mMapRenderer->labelingEngine() )
143  theMapRenderer.setLabelingEngine( mMapRenderer->labelingEngine()->clone() );
144 
145  //use stored layer set or read current set from main canvas
146  if ( mKeepLayerSet )
147  {
148  theMapRenderer.setLayerSet( mLayerSet );
149  }
150  else
151  {
152  theMapRenderer.setLayerSet( mMapRenderer->layerSet() );
153  }
154  theMapRenderer.setDestinationCrs( mMapRenderer->destinationCrs() );
156 
157  //set antialiasing if enabled in options
158  QSettings settings;
159  // Changed to enable anti aliased rendering by default as of QGIS 1.7
160  if ( settings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
161  {
162  painter->setRenderHint( QPainter::Antialiasing );
163  }
164 
165  QgsRenderContext* theRendererContext = theMapRenderer.rendererContext();
166  if ( theRendererContext )
167  {
168  theRendererContext->setDrawEditingInformation( false );
169  theRendererContext->setRenderingStopped( false );
170  }
171 
172  // force vector output (no caching of marker images etc.)
173  theRendererContext->setForceVectorOutput( true );
174 
175  // make the renderer respect the composition's useAdvancedEffects flag
176  theRendererContext->setUseAdvancedEffects( mComposition->useAdvancedEffects() );
177 
178  //force composer map scale for scale dependent visibility
179  double bk_scale = theMapRenderer.scale();
180  theMapRenderer.setScale( scale() );
181 
182  //layer caching (as QImages) cannot be done for composer prints
183  QSettings s;
184  bool bkLayerCaching = s.value( "/qgis/enable_render_caching", false ).toBool();
185  s.setValue( "/qgis/enable_render_caching", false );
186 
187  if ( forceWidthScale ) //force wysiwyg line widths / marker sizes
188  {
189  theMapRenderer.render( painter, forceWidthScale );
190  }
191  else
192  {
193  theMapRenderer.render( painter );
194  }
195  s.setValue( "/qgis/enable_render_caching", bkLayerCaching );
196 
197  theMapRenderer.setScale( bk_scale );
198 }
199 
201 {
202  if ( mPreviewMode == Rectangle )
203  {
204  return;
205  }
206 
207  if ( mDrawing )
208  {
209  return;
210  }
211 
212  mDrawing = true;
213 
214  //in case of rotation, we need to request a larger rectangle and create a larger cache image
215  QgsRectangle requestExtent;
216  requestedExtent( requestExtent );
217 
218  double horizontalVScaleFactor = horizontalViewScaleFactor();
219  if ( horizontalVScaleFactor < 0 )
220  {
221  horizontalVScaleFactor = mLastValidViewScaleFactor;
222  }
223 
224  int w = requestExtent.width() * mapUnitsToMM() * horizontalVScaleFactor;
225  int h = requestExtent.height() * mapUnitsToMM() * horizontalVScaleFactor;
226 
227  if ( w > 5000 ) //limit size of image for better performance
228  {
229  w = 5000;
230  }
231 
232  if ( h > 5000 )
233  {
234  h = 5000;
235  }
236 
237  double forcedWidthScaleFactor = w / requestExtent.width() / mapUnitsToMM();
238 
239  mCacheImage = QImage( w, h, QImage::Format_ARGB32 );
240 
241  if ( hasBackground() )
242  {
243  //Initially fill image with specified background color. This ensures that layers with blend modes will
244  //preview correctly
245  mCacheImage.fill( backgroundColor().rgba() );
246  }
247  else
248  {
249  //no background, but start with empty fill to avoid artifacts
250  mCacheImage.fill( QColor( 255, 255, 255, 0 ).rgba() );
251  }
252 
253  double mapUnitsPerPixel = mExtent.width() / w;
254 
255  // WARNING: ymax in QgsMapToPixel is device height!!!
256  QgsMapToPixel transform( mapUnitsPerPixel, h, requestExtent.yMinimum(), requestExtent.xMinimum() );
257 
258  QPainter p( &mCacheImage );
259 
260  draw( &p, requestExtent, QSizeF( w, h ), mCacheImage.logicalDpiX(), &forcedWidthScaleFactor );
261  p.end();
262  mCacheUpdated = true;
263 
264  mDrawing = false;
265 }
266 
267 void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
268 {
269  Q_UNUSED( pWidget );
270 
271  if ( !mComposition || !painter )
272  {
273  return;
274  }
275 
276  QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
277  painter->save();
278  painter->setClipRect( thisPaintRect );
279 
281  {
282  // Fill with background color
283  drawBackground( painter );
284  QFont messageFont( "", 12 );
285  painter->setFont( messageFont );
286  painter->setPen( QColor( 0, 0, 0 ) );
287  painter->drawText( thisPaintRect, tr( "Map will be printed here" ) );
288  }
290  {
291  //draw cached pixmap. This function does not call cache() any more because
292  //Qt 4.4.0 and 4.4.1 have problems with recursive paintings
293  //QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
294  //client functions
295 
296  //Background color is already included in cached image, so no need to draw
297 
298  QgsRectangle requestRectangle;
299  requestedExtent( requestRectangle );
300  double horizontalVScaleFactor = horizontalViewScaleFactor();
301  if ( horizontalVScaleFactor < 0 )
302  {
303  horizontalVScaleFactor = mLastValidViewScaleFactor;
304  }
305 
306  double imagePixelWidth = mExtent.width() / requestRectangle.width() * mCacheImage.width() ; //how many pixels of the image are for the map extent?
307  double scale = rect().width() / imagePixelWidth;
308  QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
309 
310  //shift such that rotation point is at 0/0 point in the coordinate system
311  double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
312  double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
313 
314  //shift such that top left point of the extent at point 0/0 in item coordinate system
315  double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
316  double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
317 
318  painter->save();
319 
320  painter->translate( mXOffset, mYOffset );
321  painter->translate( xTopLeftShift, yTopLeftShift );
322  painter->rotate( mRotation );
323  painter->translate( xShiftMM, -yShiftMM );
324  painter->scale( scale, scale );
325  painter->drawImage( 0, 0, mCacheImage );
326 
327  //restore rotation
328  painter->restore();
329 
330  //draw canvas items
331  drawCanvasItems( painter, itemStyle );
332  }
333  else if ( mComposition->plotStyle() == QgsComposition::Print ||
335  {
336  if ( mDrawing )
337  {
338  return;
339  }
340 
341  mDrawing = true;
342  QPaintDevice* thePaintDevice = painter->device();
343  if ( !thePaintDevice )
344  {
345  return;
346  }
347 
348  // Fill with background color
349  drawBackground( painter );
350 
351  QgsRectangle requestRectangle;
352  requestedExtent( requestRectangle );
353 
354  QSizeF theSize( requestRectangle.width() * mapUnitsToMM(), requestRectangle.height() * mapUnitsToMM() );
355  QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
356 
357  //shift such that rotation point is at 0/0 point in the coordinate system
358  double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
359  double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
360 
361  //shift such that top left point of the extent at point 0/0 in item coordinate system
362  double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
363  double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
364  painter->save();
365  painter->translate( mXOffset, mYOffset );
366  painter->translate( xTopLeftShift, yTopLeftShift );
367  painter->rotate( mRotation );
368  painter->translate( xShiftMM, -yShiftMM );
369  draw( painter, requestRectangle, theSize, 25.4 ); //scene coordinates seem to be in mm
370 
371  //restore rotation
372  painter->restore();
373 
374  //draw canvas items
375  drawCanvasItems( painter, itemStyle );
376 
377  mDrawing = false;
378  }
379 
380  painter->setClipRect( thisPaintRect , Qt::NoClip );
381 
382  if ( mGridEnabled )
383  {
384  drawGrid( painter );
385  }
386  drawFrame( painter );
387  if ( isSelected() )
388  {
389  drawSelectionBoxes( painter );
390  }
391 
392  if ( mOverviewFrameMapId != -1 )
393  {
394  drawOverviewMapExtent( painter );
395  }
396 
397  painter->restore();
398 }
399 
401 {
402  syncLayerSet(); //layer list may have changed
403  mCacheUpdated = false;
404  cache();
405  QGraphicsRectItem::update();
406 }
407 
409 {
410  if ( mPreviewMode == Render )
411  {
413  }
414 }
415 
417 {
418  mCacheUpdated = u;
419 }
420 
421 double QgsComposerMap::scale() const
422 {
423  QgsScaleCalculator calculator;
424  calculator.setMapUnits( mMapRenderer->mapUnits() );
425  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
426  return calculator.calculate( mExtent, rect().width() );
427 }
428 
429 void QgsComposerMap::resize( double dx, double dy )
430 {
431  //setRect
432  QRectF currentRect = rect();
433  QRectF newSceneRect = QRectF( transform().dx(), transform().dy(), currentRect.width() + dx, currentRect.height() + dy );
434  setSceneRect( newSceneRect );
435  updateItem();
436 }
437 
438 void QgsComposerMap::moveContent( double dx, double dy )
439 {
440  if ( !mDrawing )
441  {
442  transformShift( dx, dy );
447  cache();
448  update();
449  emit itemChanged();
450  emit extentChanged();
451  }
452 }
453 
454 void QgsComposerMap::zoomContent( int delta, double x, double y )
455 {
456  if ( mDrawing )
457  {
458  return;
459  }
460 
461  QSettings settings;
462 
463  //read zoom mode
464  //0: zoom, 1: zoom and recenter, 2: zoom to cursor, 3: nothing
465  int zoomMode = settings.value( "/qgis/wheel_action", 2 ).toInt();
466  if ( zoomMode == 3 ) //do nothing
467  {
468  return;
469  }
470 
471  double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
472 
473  //find out new center point
474  double centerX = ( mExtent.xMaximum() + mExtent.xMinimum() ) / 2;
475  double centerY = ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2;
476 
477  if ( zoomMode != 0 )
478  {
479  //find out map coordinates of mouse position
480  double mapMouseX = mExtent.xMinimum() + ( x / rect().width() ) * ( mExtent.xMaximum() - mExtent.xMinimum() );
481  double mapMouseY = mExtent.yMinimum() + ( 1 - ( y / rect().height() ) ) * ( mExtent.yMaximum() - mExtent.yMinimum() );
482  if ( zoomMode == 1 ) //zoom and recenter
483  {
484  centerX = mapMouseX;
485  centerY = mapMouseY;
486  }
487  else if ( zoomMode == 2 ) //zoom to cursor
488  {
489  centerX = mapMouseX + ( centerX - mapMouseX ) * ( 1.0 / zoomFactor );
490  centerY = mapMouseY + ( centerY - mapMouseY ) * ( 1.0 / zoomFactor );
491  }
492  }
493 
494  double newIntervalX, newIntervalY;
495 
496  if ( delta > 0 )
497  {
498  newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) / zoomFactor;
499  newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) / zoomFactor;
500  }
501  else if ( delta < 0 )
502  {
503  newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) * zoomFactor;
504  newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) * zoomFactor;
505  }
506  else //no need to zoom
507  {
508  return;
509  }
510 
511  mExtent.setXMaximum( centerX + newIntervalX / 2 );
512  mExtent.setXMinimum( centerX - newIntervalX / 2 );
513  mExtent.setYMaximum( centerY + newIntervalY / 2 );
514  mExtent.setYMinimum( centerY - newIntervalY / 2 );
515 
516  cache();
517  update();
518  emit itemChanged();
519  emit extentChanged();
520 }
521 
522 void QgsComposerMap::setSceneRect( const QRectF& rectangle )
523 {
524  double w = rectangle.width();
525  double h = rectangle.height();
526  //prepareGeometryChange();
527 
528  QgsComposerItem::setSceneRect( rectangle );
529 
530  //QGraphicsRectItem::update();
531  double newHeight = mExtent.width() * h / w ;
533  mCacheUpdated = false;
534 
536  update();
537  emit itemChanged();
538  emit extentChanged();
539 }
540 
542 {
543  if ( mExtent == extent )
544  {
545  return;
546  }
547  mExtent = extent;
548 
549  //adjust height
550  QRectF currentRect = rect();
551 
552  double newHeight = currentRect.width() * extent.height() / extent.width();
553 
554  setSceneRect( QRectF( transform().dx(), transform().dy(), currentRect.width(), newHeight ) );
555  updateItem();
556 }
557 
558 void QgsComposerMap::setNewScale( double scaleDenominator )
559 {
560  double currentScaleDenominator = scale();
561 
562  if ( scaleDenominator == currentScaleDenominator )
563  {
564  return;
565  }
566 
567  double scaleRatio = scaleDenominator / currentScaleDenominator;
568  mExtent.scale( scaleRatio );
569  mCacheUpdated = false;
570  cache();
571  update();
572  emit itemChanged();
573  emit extentChanged();
574 }
575 
577 {
578  mPreviewMode = m;
579  emit itemChanged();
580 }
581 
582 void QgsComposerMap::setOffset( double xOffset, double yOffset )
583 {
584  mXOffset = xOffset;
585  mYOffset = yOffset;
586 }
587 
589 {
590  setRotation( r );
591  emit rotationChanged( r );
592  emit itemChanged();
593 }
594 
596 {
598  {
599  cache();
600  }
602 }
603 
605 {
606  if ( !mMapRenderer )
607  {
608  return false;
609  }
610 
611  QStringList layers = mMapRenderer->layerSet();
612 
613  QStringList::const_iterator layer_it = layers.constBegin();
614  QgsMapLayer* currentLayer = 0;
615 
616  for ( ; layer_it != layers.constEnd(); ++layer_it )
617  {
618  currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
619  if ( currentLayer )
620  {
621  QgsRasterLayer* currentRasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
622  if ( currentRasterLayer )
623  {
624  const QgsRasterDataProvider* rasterProvider = 0;
625  if (( rasterProvider = currentRasterLayer->dataProvider() ) )
626  {
627  if ( rasterProvider->name() == "wms" )
628  {
629  return true;
630  }
631  }
632  }
633  }
634  }
635  return false;
636 }
637 
639 {
640  // check if map contains advanced effects like blend modes, or flattened layers for transparency
641  if ( !mMapRenderer )
642  {
643  return false;
644  }
645 
646  QStringList layers = mMapRenderer->layerSet();
647 
648  //Also need to check PAL labeling for blend modes
649  QgsPalLabeling* lbl = dynamic_cast<QgsPalLabeling*>( mMapRenderer->labelingEngine() );
650 
651  QStringList::const_iterator layer_it = layers.constBegin();
652  QgsMapLayer* currentLayer = 0;
653 
654  for ( ; layer_it != layers.constEnd(); ++layer_it )
655  {
656  currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
657  if ( currentLayer )
658  {
659  if ( currentLayer->blendMode() != QPainter::CompositionMode_SourceOver )
660  {
661  return true;
662  }
663  // if vector layer, check labels and feature blend mode
664  QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
665  if ( currentVectorLayer )
666  {
667  if ( currentVectorLayer->layerTransparency() != 0 )
668  {
669  return true;
670  }
671  if ( currentVectorLayer->featureBlendMode() != QPainter::CompositionMode_SourceOver )
672  {
673  return true;
674  }
675  // check label blend modes
676  if ( lbl->willUseLayer( currentVectorLayer ) )
677  {
678  // Check all label blending properties
679  QgsPalLayerSettings& layerSettings = lbl->layer( currentVectorLayer->id() );
680  if (( layerSettings.blendMode != QPainter::CompositionMode_SourceOver ) ||
681  ( layerSettings.bufferSize != 0 && layerSettings.bufferBlendMode != QPainter::CompositionMode_SourceOver ) ||
682  ( layerSettings.shadowDraw && layerSettings.shadowBlendMode != QPainter::CompositionMode_SourceOver ) ||
683  ( layerSettings.shapeDraw && layerSettings.shapeBlendMode != QPainter::CompositionMode_SourceOver ) )
684  {
685  return true;
686  }
687  }
688  }
689  }
690  }
691 
692  return false;
693 }
694 
696 {
697  //connect signal from layer registry to update in case of new or deleted layers
699  if ( layerRegistry )
700  {
701  connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( updateCachedImage() ) );
702  connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( updateCachedImage() ) );
703  }
704 }
705 
706 bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
707 {
708  if ( elem.isNull() )
709  {
710  return false;
711  }
712 
713  QDomElement composerMapElem = doc.createElement( "ComposerMap" );
714  composerMapElem.setAttribute( "id", mId );
715 
716  //previewMode
717  if ( mPreviewMode == Cache )
718  {
719  composerMapElem.setAttribute( "previewMode", "Cache" );
720  }
721  else if ( mPreviewMode == Render )
722  {
723  composerMapElem.setAttribute( "previewMode", "Render" );
724  }
725  else //rectangle
726  {
727  composerMapElem.setAttribute( "previewMode", "Rectangle" );
728  }
729 
730  if ( mKeepLayerSet )
731  {
732  composerMapElem.setAttribute( "keepLayerSet", "true" );
733  }
734  else
735  {
736  composerMapElem.setAttribute( "keepLayerSet", "false" );
737  }
738 
739  if ( mDrawCanvasItems )
740  {
741  composerMapElem.setAttribute( "drawCanvasItems", "true" );
742  }
743  else
744  {
745  composerMapElem.setAttribute( "drawCanvasItems", "false" );
746  }
747 
748  //overview map frame
749  QDomElement overviewFrameElem = doc.createElement( "overviewFrame" );
750  overviewFrameElem.setAttribute( "overviewFrameMap", mOverviewFrameMapId );
751  overviewFrameElem.setAttribute( "overviewBlendMode", QgsMapRenderer::getBlendModeEnum( mOverviewBlendMode ) );
752  if ( mOverviewInverted )
753  {
754  overviewFrameElem.setAttribute( "overviewInverted", "true" );
755  }
756  else
757  {
758  overviewFrameElem.setAttribute( "overviewInverted", "false" );
759  }
760  QDomElement overviewFrameStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mOverviewFrameMapSymbol, doc );
761  overviewFrameElem.appendChild( overviewFrameStyleElem );
762  composerMapElem.appendChild( overviewFrameElem );
763 
764 
765  //extent
766  QDomElement extentElem = doc.createElement( "Extent" );
767  extentElem.setAttribute( "xmin", QString::number( mExtent.xMinimum() ) );
768  extentElem.setAttribute( "xmax", QString::number( mExtent.xMaximum() ) );
769  extentElem.setAttribute( "ymin", QString::number( mExtent.yMinimum() ) );
770  extentElem.setAttribute( "ymax", QString::number( mExtent.yMaximum() ) );
771  composerMapElem.appendChild( extentElem );
772 
773  //layer set
774  QDomElement layerSetElem = doc.createElement( "LayerSet" );
775  QStringList::const_iterator layerIt = mLayerSet.constBegin();
776  for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
777  {
778  QDomElement layerElem = doc.createElement( "Layer" );
779  QDomText layerIdText = doc.createTextNode( *layerIt );
780  layerElem.appendChild( layerIdText );
781  layerSetElem.appendChild( layerElem );
782  }
783  composerMapElem.appendChild( layerSetElem );
784 
785  //overview map frame
786  composerMapElem.setAttribute( "overviewFrameMap", mOverviewFrameMapId );
787 
788  //grid
789  QDomElement gridElem = doc.createElement( "Grid" );
790  gridElem.setAttribute( "show", mGridEnabled );
791  gridElem.setAttribute( "gridStyle", mGridStyle );
792  gridElem.setAttribute( "intervalX", QString::number( mGridIntervalX ) );
793  gridElem.setAttribute( "intervalY", QString::number( mGridIntervalY ) );
794  gridElem.setAttribute( "offsetX", QString::number( mGridOffsetX ) );
795  gridElem.setAttribute( "offsetY", QString::number( mGridOffsetY ) );
796  gridElem.setAttribute( "crossLength", QString::number( mCrossLength ) );
797  gridElem.setAttribute( "gridFrameStyle", mGridFrameStyle );
798  gridElem.setAttribute( "gridFrameWidth", QString::number( mGridFrameWidth ) );
799  gridElem.setAttribute( "gridBlendMode", QgsMapRenderer::getBlendModeEnum( mGridBlendMode ) );
800  QDomElement gridLineStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mGridLineSymbol, doc );
801  gridElem.appendChild( gridLineStyleElem );
802 
803  //grid annotation
804  QDomElement annotationElem = doc.createElement( "Annotation" );
805  annotationElem.setAttribute( "format", mGridAnnotationFormat );
806  annotationElem.setAttribute( "show", mShowGridAnnotation );
807  annotationElem.setAttribute( "leftPosition", mLeftGridAnnotationPosition );
808  annotationElem.setAttribute( "rightPosition", mRightGridAnnotationPosition );
809  annotationElem.setAttribute( "topPosition", mTopGridAnnotationPosition );
810  annotationElem.setAttribute( "bottomPosition", mBottomGridAnnotationPosition );
811  annotationElem.setAttribute( "leftDirection", mLeftGridAnnotationDirection );
812  annotationElem.setAttribute( "rightDirection", mRightGridAnnotationDirection );
813  annotationElem.setAttribute( "topDirection", mTopGridAnnotationDirection );
814  annotationElem.setAttribute( "bottomDirection", mBottomGridAnnotationDirection );
815  annotationElem.setAttribute( "frameDistance", QString::number( mAnnotationFrameDistance ) );
816  annotationElem.setAttribute( "font", mGridAnnotationFont.toString() );
817  annotationElem.setAttribute( "precision", mGridAnnotationPrecision );
818  //annotation font color
819  QDomElement annotationFontColorElem = doc.createElement( "fontColor" );
820  annotationFontColorElem.setAttribute( "red", mGridAnnotationFontColor.red() );
821  annotationFontColorElem.setAttribute( "green", mGridAnnotationFontColor.green() );
822  annotationFontColorElem.setAttribute( "blue", mGridAnnotationFontColor.blue() );
823  annotationElem.appendChild( annotationFontColorElem );
824 
825  gridElem.appendChild( annotationElem );
826  composerMapElem.appendChild( gridElem );
827 
828  elem.appendChild( composerMapElem );
829  return _writeXML( composerMapElem, doc );
830 }
831 
832 bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& doc )
833 {
834  if ( itemElem.isNull() )
835  {
836  return false;
837  }
838 
839  QString idRead = itemElem.attribute( "id", "not found" );
840  if ( idRead != "not found" )
841  {
842  mId = idRead.toInt();
843  }
845 
846  //previewMode
847  QString previewMode = itemElem.attribute( "previewMode" );
848  if ( previewMode == "Cache" )
849  {
851  }
852  else if ( previewMode == "Render" )
853  {
855  }
856  else
857  {
859  }
860 
861  QDomElement overviewFrameElem = itemElem.firstChildElement( "overviewFrame" );
862  if ( !overviewFrameElem.isNull() )
863  {
864  setOverviewFrameMap( overviewFrameElem.attribute( "overviewFrameMap", "-1" ).toInt() );
865  setOverviewBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) overviewFrameElem.attribute( "overviewBlendMode", "0" ).toUInt() ) );
866 
867  QString overviewInvertedFlag = overviewFrameElem.attribute( "overviewInverted" );
868  if ( overviewInvertedFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
869  {
870  setOverviewInverted( true );
871  }
872  else
873  {
874  setOverviewInverted( false );
875  }
876 
877  QDomElement overviewFrameSymbolElem = overviewFrameElem.firstChildElement( "symbol" );
878  if ( !overviewFrameSymbolElem.isNull() )
879  {
881  mOverviewFrameMapSymbol = dynamic_cast<QgsFillSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( overviewFrameSymbolElem ) );
882  }
883  }
884 
885  //extent
886  QDomNodeList extentNodeList = itemElem.elementsByTagName( "Extent" );
887  if ( extentNodeList.size() > 0 )
888  {
889  QDomElement extentElem = extentNodeList.at( 0 ).toElement();
890  double xmin, xmax, ymin, ymax;
891  xmin = extentElem.attribute( "xmin" ).toDouble();
892  xmax = extentElem.attribute( "xmax" ).toDouble();
893  ymin = extentElem.attribute( "ymin" ).toDouble();
894  ymax = extentElem.attribute( "ymax" ).toDouble();
895 
896  mExtent = QgsRectangle( xmin, ymin, xmax, ymax );
897  }
898 
899  //mKeepLayerSet flag
900  QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
901  if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
902  {
903  mKeepLayerSet = true;
904  }
905  else
906  {
907  mKeepLayerSet = false;
908  }
909 
910  QString drawCanvasItemsFlag = itemElem.attribute( "drawCanvasItems" );
911  if ( drawCanvasItemsFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
912  {
913  mDrawCanvasItems = true;
914  }
915  else
916  {
917  mDrawCanvasItems = false;
918  }
919 
920  //mLayerSet
921  QDomNodeList layerSetNodeList = itemElem.elementsByTagName( "LayerSet" );
922  QStringList layerSet;
923  if ( layerSetNodeList.size() > 0 )
924  {
925  QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
926  QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( "Layer" );
927  for ( int i = 0; i < layerIdNodeList.size(); ++i )
928  {
929  layerSet << layerIdNodeList.at( i ).toElement().text();
930  }
931  }
933 
934  mDrawing = false;
935  mNumCachedLayers = 0;
936  mCacheUpdated = false;
937 
938  //grid
939  QDomNodeList gridNodeList = itemElem.elementsByTagName( "Grid" );
940  if ( gridNodeList.size() > 0 )
941  {
942  QDomElement gridElem = gridNodeList.at( 0 ).toElement();
943  mGridEnabled = ( gridElem.attribute( "show", "0" ) != "0" );
944  mGridStyle = QgsComposerMap::GridStyle( gridElem.attribute( "gridStyle", "0" ).toInt() );
945  mGridIntervalX = gridElem.attribute( "intervalX", "0" ).toDouble();
946  mGridIntervalY = gridElem.attribute( "intervalY", "0" ).toDouble();
947  mGridOffsetX = gridElem.attribute( "offsetX", "0" ).toDouble();
948  mGridOffsetY = gridElem.attribute( "offsetY", "0" ).toDouble();
949  mCrossLength = gridElem.attribute( "crossLength", "3" ).toDouble();
950  mGridFrameStyle = ( QgsComposerMap::GridFrameStyle )gridElem.attribute( "gridFrameStyle", "0" ).toInt();
951  mGridFrameWidth = gridElem.attribute( "gridFrameWidth", "2.0" ).toDouble();
952  setGridBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) gridElem.attribute( "gridBlendMode", "0" ).toUInt() ) );
953 
954  QDomElement gridSymbolElem = gridElem.firstChildElement( "symbol" );
955  delete mGridLineSymbol;
956  if ( gridSymbolElem.isNull( ) )
957  {
958  //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
960  mGridLineSymbol->setWidth( gridElem.attribute( "penWidth", "0" ).toDouble() );
961  mGridLineSymbol->setColor( QColor( gridElem.attribute( "penColorRed", "0" ).toInt(),
962  gridElem.attribute( "penColorGreen", "0" ).toInt(),
963  gridElem.attribute( "penColorBlue", "0" ).toInt() ) );
964  }
965  else
966  {
967  mGridLineSymbol = dynamic_cast<QgsLineSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( gridSymbolElem ) );
968  }
969 
970  QDomNodeList annotationNodeList = gridElem.elementsByTagName( "Annotation" );
971  if ( annotationNodeList.size() > 0 )
972  {
973  QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
974  mShowGridAnnotation = ( annotationElem.attribute( "show", "0" ) != "0" );
975  mGridAnnotationFormat = QgsComposerMap::GridAnnotationFormat( annotationElem.attribute( "format", "0" ).toInt() );
976  mLeftGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "leftPosition", "0" ).toInt() );
977  mRightGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "rightPosition", "0" ).toInt() );
978  mTopGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "topPosition", "0" ).toInt() );
979  mBottomGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "bottomPosition", "0" ).toInt() );
980  mLeftGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "leftDirection", "0" ).toInt() );
981  mRightGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "rightDirection", "0" ).toInt() );
982  mTopGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "topDirection", "0" ).toInt() );
983  mBottomGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "bottomDirection", "0" ).toInt() );
984  mAnnotationFrameDistance = annotationElem.attribute( "frameDistance", "0" ).toDouble();
985  mGridAnnotationFont.fromString( annotationElem.attribute( "font", "" ) );
986 
987  //annotation font color
988  QDomNodeList annotationFontColorList = annotationElem.elementsByTagName( "fontColor" );
989  if ( annotationFontColorList.size() > 0 )
990  {
991  QDomElement fontColorElem = annotationFontColorList.at( 0 ).toElement();
992  int red = fontColorElem.attribute( "red", "0" ).toInt();
993  int green = fontColorElem.attribute( "green", "0" ).toInt();
994  int blue = fontColorElem.attribute( "blue", "0" ).toInt();
995  mGridAnnotationFontColor = QColor( red, green, blue );
996  }
997  else
998  {
999  mGridAnnotationFontColor = QColor( 0, 0, 0 );
1000  }
1001 
1002  mGridAnnotationPrecision = annotationElem.attribute( "precision", "3" ).toInt();
1003  }
1004  }
1005 
1006  //restore general composer item properties
1007  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
1008  if ( composerItemList.size() > 0 )
1009  {
1010  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
1011  _readXML( composerItemElem, doc );
1012  }
1013 
1015  emit itemChanged();
1016  return true;
1017 }
1018 
1020 {
1021  if ( mMapRenderer )
1022  {
1024  }
1025 }
1026 
1028 {
1029  if ( mLayerSet.size() < 1 && !mMapRenderer )
1030  {
1031  return;
1032  }
1033 
1034  //if layer set is fixed, do a lookup in the layer registry to also find the non-visible layers
1035  QStringList currentLayerSet;
1036  if ( mKeepLayerSet )
1037  {
1038  currentLayerSet = QgsMapLayerRegistry::instance()->mapLayers().uniqueKeys();
1039  }
1040  else //only consider layers visible in the map
1041  {
1042  currentLayerSet = mMapRenderer->layerSet();
1043  }
1044 
1045  for ( int i = mLayerSet.size() - 1; i >= 0; --i )
1046  {
1047  if ( !currentLayerSet.contains( mLayerSet.at( i ) ) )
1048  {
1049  mLayerSet.removeAt( i );
1050  }
1051  }
1052 }
1053 
1054 void QgsComposerMap::drawGrid( QPainter* p )
1055 {
1056  QList< QPair< double, QLineF > > verticalLines;
1057  yGridLines( verticalLines );
1058  QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
1059  QList< QPair< double, QLineF > > horizontalLines;
1060  xGridLines( horizontalLines );
1061  QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
1062 
1063  QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
1064  p->setClipRect( thisPaintRect );
1065 
1066  // set the blend mode for drawing grid lines
1067  p->save();
1068  p->setCompositionMode( mGridBlendMode );
1069 
1070  //simpler approach: draw vertical lines first, then horizontal ones
1072  {
1073  for ( ; vIt != verticalLines.constEnd(); ++vIt )
1074  {
1075  drawGridLine( vIt->second, p );
1076  }
1077 
1078  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
1079  {
1080  drawGridLine( hIt->second, p );
1081  }
1082  }
1083  else //cross
1084  {
1085  QPointF intersectionPoint, crossEnd1, crossEnd2;
1086  for ( ; vIt != verticalLines.constEnd(); ++vIt )
1087  {
1088  //start mark
1089  crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( vIt->second.p1(), vIt->second.p2(), mCrossLength );
1090  drawGridLine( QLineF( vIt->second.p1(), crossEnd1 ), p );
1091 
1092  //test for intersection with every horizontal line
1093  hIt = horizontalLines.constBegin();
1094  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
1095  {
1096  if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
1097  {
1098  crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength );
1099  crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength );
1100  drawGridLine( QLineF( crossEnd1, crossEnd2 ), p );
1101  }
1102  }
1103  //end mark
1104  QPointF crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( vIt->second.p2(), vIt->second.p1(), mCrossLength );
1105  drawGridLine( QLineF( vIt->second.p2(), crossEnd2 ), p );
1106  }
1107 
1108  hIt = horizontalLines.constBegin();
1109  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
1110  {
1111  //start mark
1112  crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( hIt->second.p1(), hIt->second.p2(), mCrossLength );
1113  drawGridLine( QLineF( hIt->second.p1(), crossEnd1 ), p );
1114 
1115  vIt = verticalLines.constBegin();
1116  for ( ; vIt != verticalLines.constEnd(); ++vIt )
1117  {
1118  if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
1119  {
1120  crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength );
1121  crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength );
1122  drawGridLine( QLineF( crossEnd1, crossEnd2 ), p );
1123  }
1124  }
1125  //end mark
1126  crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( hIt->second.p2(), hIt->second.p1(), mCrossLength );
1127  drawGridLine( QLineF( hIt->second.p2(), crossEnd1 ), p );
1128  }
1129  }
1130  // reset composition mode
1131  p->restore();
1132 
1133  p->setClipRect( thisPaintRect , Qt::NoClip );
1134 
1136  {
1137  drawGridFrame( p, horizontalLines, verticalLines );
1138  }
1139 
1140  if ( mShowGridAnnotation )
1141  {
1142  drawCoordinateAnnotations( p, horizontalLines, verticalLines );
1143  }
1144 
1145 }
1146 
1147 void QgsComposerMap::drawGridFrame( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines )
1148 {
1149  //Sort the coordinate positions for each side
1150  QMap< double, double > leftGridFrame;
1151  QMap< double, double > rightGridFrame;
1152  QMap< double, double > topGridFrame;
1153  QMap< double, double > bottomGridFrame;
1154 
1155  sortGridLinesOnBorders( hLines, vLines, leftGridFrame, rightGridFrame, topGridFrame, bottomGridFrame );
1156 
1157  drawGridFrameBorder( p, leftGridFrame, QgsComposerMap::Left );
1158  drawGridFrameBorder( p, rightGridFrame, QgsComposerMap::Right );
1159  drawGridFrameBorder( p, topGridFrame, QgsComposerMap::Top );
1160  drawGridFrameBorder( p, bottomGridFrame, QgsComposerMap::Bottom );
1161 }
1162 
1163 void QgsComposerMap::drawGridLine( const QLineF& line, QPainter* p )
1164 {
1165  if ( !mGridLineSymbol || !p )
1166  {
1167  return;
1168  }
1169 
1170  //setup render context
1171  QgsRenderContext context;
1172  context.setPainter( p );
1173  if ( mPreviewMode == Rectangle )
1174  {
1175  return;
1176  }
1177  else
1178  {
1179  context.setScaleFactor( 1.0 );
1180  context.setRasterScaleFactor( mComposition->printResolution() / 25.4 );
1181  }
1182 
1183  QPolygonF poly;
1184  poly << line.p1() << line.p2();
1185  mGridLineSymbol->startRender( context );
1186  mGridLineSymbol->renderPolyline( poly, 0, context );
1187  mGridLineSymbol->stopRender( context );
1188 }
1189 
1190 void QgsComposerMap::drawGridFrameBorder( QPainter* p, const QMap< double, double >& borderPos, Border border )
1191 {
1192  double currentCoord = - mGridFrameWidth;
1193  bool white = true;
1194  double x = 0;
1195  double y = 0;
1196  double width = 0;
1197  double height = 0;
1198 
1199  QMap< double, double > pos = borderPos;
1200  pos.insert( 0, 0 );
1201  if ( border == Left || border == Right )
1202  {
1203  pos.insert( rect().height(), rect().height() );
1204  pos.insert( rect().height() + mGridFrameWidth, rect().height() + mGridFrameWidth );
1205  }
1206  else //top or bottom
1207  {
1208  pos.insert( rect().width(), rect().width() );
1209  pos.insert( rect().width() + mGridFrameWidth, rect().width() + mGridFrameWidth );
1210  }
1211 
1212  QMap< double, double >::const_iterator posIt = pos.constBegin();
1213  for ( ; posIt != pos.constEnd(); ++posIt )
1214  {
1215  p->setBrush( QBrush( white ? Qt::white : Qt::black ) );
1216  if ( border == Left || border == Right )
1217  {
1218  height = posIt.key() - currentCoord;
1219  width = mGridFrameWidth;
1220  x = ( border == Left ) ? -mGridFrameWidth : rect().width();
1221  y = currentCoord;
1222  }
1223  else //top or bottom
1224  {
1225  height = mGridFrameWidth;
1226  width = posIt.key() - currentCoord;
1227  x = currentCoord;
1228  y = ( border == Top ) ? -mGridFrameWidth : rect().height();
1229  }
1230  p->drawRect( QRectF( x, y, width, height ) );
1231  currentCoord = posIt.key();
1232  white = !white;
1233  }
1234 }
1235 
1236 void QgsComposerMap::drawCoordinateAnnotations( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines )
1237 {
1238  if ( !p )
1239  {
1240  return;
1241  }
1242 
1243 
1244  QString currentAnnotationString;
1245  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
1246  for ( ; it != hLines.constEnd(); ++it )
1247  {
1248  currentAnnotationString = gridAnnotationString( it->first, Latitude );
1249  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
1250  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
1251  }
1252 
1253  it = vLines.constBegin();
1254  for ( ; it != vLines.constEnd(); ++it )
1255  {
1256  currentAnnotationString = gridAnnotationString( it->first, Longitude );
1257  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
1258  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
1259  }
1260 }
1261 
1262 void QgsComposerMap::drawCoordinateAnnotation( QPainter* p, const QPointF& pos, QString annotationString )
1263 {
1264  Border frameBorder = borderForLineCoord( pos );
1265  double textWidth = textWidthMillimeters( mGridAnnotationFont, annotationString );
1266  //relevant for annotations is the height of digits
1267  double textHeight = fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
1268  double xpos = pos.x();
1269  double ypos = pos.y();
1270  int rotation = 0;
1271 
1272  double gridFrameDistance = ( mGridFrameStyle == NoGridFrame ) ? 0 : mGridFrameWidth;
1273 
1274  if ( frameBorder == Left )
1275  {
1276 
1278  {
1280  {
1281  xpos += textHeight + mAnnotationFrameDistance;
1282  ypos += textWidth / 2.0;
1283  rotation = 270;
1284  }
1285  else
1286  {
1287  xpos += mAnnotationFrameDistance;
1288  ypos += textHeight / 2.0;
1289  }
1290  }
1291  else if ( mLeftGridAnnotationPosition == OutsideMapFrame ) //Outside map frame
1292  {
1294  {
1295  xpos -= ( mAnnotationFrameDistance + gridFrameDistance );
1296  ypos += textWidth / 2.0;
1297  rotation = 270;
1298  }
1299  else
1300  {
1301  xpos -= ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
1302  ypos += textHeight / 2.0;
1303  }
1304  }
1305  else
1306  {
1307  return;
1308  }
1309 
1310  }
1311  else if ( frameBorder == Right )
1312  {
1314  {
1316  {
1317  xpos -= mAnnotationFrameDistance;
1318  ypos += textWidth / 2.0;
1319  rotation = 270;
1320  }
1321  else
1322  {
1323  xpos -= textWidth + mAnnotationFrameDistance;
1324  ypos += textHeight / 2.0;
1325  }
1326  }
1327  else if ( mRightGridAnnotationPosition == OutsideMapFrame )//OutsideMapFrame
1328  {
1330  {
1331  xpos += ( textHeight + mAnnotationFrameDistance + gridFrameDistance );
1332  ypos += textWidth / 2.0;
1333  rotation = 270;
1334  }
1335  else //Horizontal
1336  {
1337  xpos += ( mAnnotationFrameDistance + gridFrameDistance );
1338  ypos += textHeight / 2.0;
1339  }
1340  }
1341  else
1342  {
1343  return;
1344  }
1345  }
1346  else if ( frameBorder == Bottom )
1347  {
1349  {
1351  {
1352  ypos -= mAnnotationFrameDistance;
1353  xpos -= textWidth / 2.0;
1354  }
1355  else //Vertical
1356  {
1357  xpos += textHeight / 2.0;
1358  ypos -= mAnnotationFrameDistance;
1359  rotation = 270;
1360  }
1361  }
1362  else if ( mBottomGridAnnotationPosition == OutsideMapFrame ) //OutsideMapFrame
1363  {
1365  {
1366  ypos += ( mAnnotationFrameDistance + textHeight + gridFrameDistance );
1367  xpos -= textWidth / 2.0;
1368  }
1369  else //Vertical
1370  {
1371  xpos += textHeight / 2.0;
1372  ypos += ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
1373  rotation = 270;
1374  }
1375  }
1376  else
1377  {
1378  return;
1379  }
1380  }
1381  else //Top
1382  {
1384  {
1386  {
1387  xpos -= textWidth / 2.0;
1388  ypos += textHeight + mAnnotationFrameDistance;
1389  }
1390  else //Vertical
1391  {
1392  xpos += textHeight / 2.0;
1393  ypos += textWidth + mAnnotationFrameDistance;
1394  rotation = 270;
1395  }
1396  }
1397  else if ( mTopGridAnnotationPosition == OutsideMapFrame ) //OutsideMapFrame
1398  {
1400  {
1401  xpos -= textWidth / 2.0;
1402  ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
1403  }
1404  else //Vertical
1405  {
1406  xpos += textHeight / 2.0;
1407  ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
1408  rotation = 270;
1409  }
1410  }
1411  else
1412  {
1413  return;
1414  }
1415  }
1416 
1417  drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
1418 }
1419 
1420 void QgsComposerMap::drawAnnotation( QPainter* p, const QPointF& pos, int rotation, const QString& annotationText )
1421 {
1422  p->save();
1423  p->translate( pos );
1424  p->rotate( rotation );
1425  p->setPen( QPen( QColor( mGridAnnotationFontColor ) ) );
1426  drawText( p, 0, 0, annotationText, mGridAnnotationFont );
1427  p->restore();
1428 }
1429 
1431 {
1432  if ( mGridAnnotationFormat == Decimal )
1433  {
1434  return QString::number( value, 'f', mGridAnnotationPrecision );
1435  }
1436 
1437  QgsPoint p;
1438  p.setX( coord == Longitude ? value : 0 );
1439  p.setY( coord == Longitude ? 0 : value );
1440 
1441  QString annotationString;
1443  {
1444  annotationString = p.toDegreesMinutes( mGridAnnotationPrecision );
1445  }
1446  else //DegreeMinuteSecond
1447  {
1448  annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision );
1449  }
1450 
1451  QStringList split = annotationString.split( "," );
1452  if ( coord == Longitude )
1453  {
1454  return split.at( 0 );
1455  }
1456  else
1457  {
1458  if ( split.size() < 2 )
1459  {
1460  return "";
1461  }
1462  return split.at( 1 );
1463  }
1464 }
1465 
1466 int QgsComposerMap::xGridLines( QList< QPair< double, QLineF > >& lines ) const
1467 {
1468  lines.clear();
1469  if ( mGridIntervalY <= 0.0 )
1470  {
1471  return 1;
1472  }
1473 
1474 
1475  QPolygonF mapPolygon = transformedMapPolygon();
1476  QRectF mapBoundingRect = mapPolygon.boundingRect();
1477 
1478  //consider to round up to the next step in case the left boundary is > 0
1479  double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
1480  double currentLevel = ( int )(( mapBoundingRect.top() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY;
1481 
1482  if ( qgsDoubleNear( mRotation, 0.0 ) )
1483  {
1484  //no rotation. Do it 'the easy way'
1485 
1486  double yCanvasCoord;
1487 
1488  while ( currentLevel <= mapBoundingRect.bottom() )
1489  {
1490  yCanvasCoord = rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
1491  lines.push_back( qMakePair( currentLevel, QLineF( 0, yCanvasCoord, rect().width(), yCanvasCoord ) ) );
1492  currentLevel += mGridIntervalY;
1493  }
1494  }
1495 
1496  //the four border lines
1497  QVector<QLineF> borderLines;
1498  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1499  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1500  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1501  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1502 
1503  QList<QPointF> intersectionList; //intersects between border lines and grid lines
1504 
1505  while ( currentLevel <= mapBoundingRect.bottom() )
1506  {
1507  intersectionList.clear();
1508  QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
1509 
1510  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1511  for ( ; it != borderLines.constEnd(); ++it )
1512  {
1513  QPointF intersectionPoint;
1514  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1515  {
1516  intersectionList.push_back( intersectionPoint );
1517  if ( intersectionList.size() >= 2 )
1518  {
1519  break; //we already have two intersections, skip further tests
1520  }
1521  }
1522  }
1523 
1524  if ( intersectionList.size() >= 2 )
1525  {
1526  lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1527  }
1528  currentLevel += mGridIntervalY;
1529  }
1530 
1531 
1532  return 0;
1533 }
1534 
1535 int QgsComposerMap::yGridLines( QList< QPair< double, QLineF > >& lines ) const
1536 {
1537  lines.clear();
1538  if ( mGridIntervalX <= 0.0 )
1539  {
1540  return 1;
1541  }
1542 
1543  QPolygonF mapPolygon = transformedMapPolygon();
1544  QRectF mapBoundingRect = mapPolygon.boundingRect();
1545 
1546  //consider to round up to the next step in case the left boundary is > 0
1547  double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
1548  double currentLevel = ( int )(( mapBoundingRect.left() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
1549 
1550  if ( qgsDoubleNear( mRotation, 0.0 ) )
1551  {
1552  //no rotation. Do it 'the easy way'
1553  double xCanvasCoord;
1554 
1555  while ( currentLevel <= mapBoundingRect.right() )
1556  {
1557  xCanvasCoord = rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
1558  lines.push_back( qMakePair( currentLevel, QLineF( xCanvasCoord, 0, xCanvasCoord, rect().height() ) ) );
1559  currentLevel += mGridIntervalX;
1560  }
1561  }
1562 
1563  //the four border lines
1564  QVector<QLineF> borderLines;
1565  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1566  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1567  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1568  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1569 
1570  QList<QPointF> intersectionList; //intersects between border lines and grid lines
1571 
1572  while ( currentLevel <= mapBoundingRect.right() )
1573  {
1574  intersectionList.clear();
1575  QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
1576 
1577  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1578  for ( ; it != borderLines.constEnd(); ++it )
1579  {
1580  QPointF intersectionPoint;
1581  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1582  {
1583  intersectionList.push_back( intersectionPoint );
1584  if ( intersectionList.size() >= 2 )
1585  {
1586  break; //we already have two intersections, skip further tests
1587  }
1588  }
1589  }
1590 
1591  if ( intersectionList.size() >= 2 )
1592  {
1593  lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1594  }
1595  currentLevel += mGridIntervalX;
1596  }
1597 
1598  return 0;
1599 }
1600 
1602 {
1603  if ( mGridLineSymbol )
1604  {
1605  mGridLineSymbol->setWidth( w );
1606  }
1607 }
1608 
1609 void QgsComposerMap::setGridPenColor( const QColor& c )
1610 {
1611  if ( mGridLineSymbol )
1612  {
1613  mGridLineSymbol->setColor( c );
1614  }
1615 }
1616 
1617 void QgsComposerMap::setGridPen( const QPen& p )
1618 {
1619  setGridPenWidth( p.widthF() );
1620  setGridPenColor( p.color() );
1621 }
1622 
1624 {
1625  QPen p;
1626  if ( mGridLineSymbol )
1627  {
1628  p.setWidthF( mGridLineSymbol->width() );
1629  p.setColor( mGridLineSymbol->color() );
1630  p.setCapStyle( Qt::FlatCap );
1631  }
1632  return p;
1633 }
1634 
1635 void QgsComposerMap::setGridBlendMode( QPainter::CompositionMode blendMode )
1636 {
1638  update();
1639 }
1640 
1642 {
1643  return mCurrentRectangle;
1644 }
1645 
1647 {
1648  QRectF rectangle = rect();
1649  double extension = maxExtension();
1650  rectangle.setLeft( rectangle.left() - extension );
1651  rectangle.setRight( rectangle.right() + extension );
1652  rectangle.setTop( rectangle.top() - extension );
1653  rectangle.setBottom( rectangle.bottom() + extension );
1654  if ( rectangle != mCurrentRectangle )
1655  {
1656  prepareGeometryChange();
1657  mCurrentRectangle = rectangle;
1658  }
1659 }
1660 
1662 {
1663  double dx = mXOffset;
1664  double dy = mYOffset;
1665  transformShift( dx, dy );
1666  return QgsRectangle( mExtent.xMinimum() - dx, mExtent.yMinimum() - dy, mExtent.xMaximum() - dx, mExtent.yMaximum() - dy );
1667 }
1668 
1670 {
1671  double dx = mXOffset;
1672  double dy = mYOffset;
1673  //qWarning("offset");
1674  //qWarning(QString::number(dx).toLocal8Bit().data());
1675  //qWarning(QString::number(dy).toLocal8Bit().data());
1676  transformShift( dx, dy );
1677  //qWarning("transformed:");
1678  //qWarning(QString::number(dx).toLocal8Bit().data());
1679  //qWarning(QString::number(dy).toLocal8Bit().data());
1680  QPolygonF poly;
1681  mapPolygon( poly );
1682  poly.translate( -dx, -dy );
1683  return poly;
1684 }
1685 
1687 {
1690  {
1691  return 0;
1692  }
1693 
1694  QList< QPair< double, QLineF > > xLines;
1695  QList< QPair< double, QLineF > > yLines;
1696 
1697  int xGridReturn = xGridLines( xLines );
1698  int yGridReturn = yGridLines( yLines );
1699 
1700  if ( xGridReturn != 0 && yGridReturn != 0 )
1701  {
1702  return 0;
1703  }
1704 
1705  double maxExtension = 0;
1706  double currentExtension = 0;
1707  QString currentAnnotationString;
1708 
1709  QList< QPair< double, QLineF > >::const_iterator it = xLines.constBegin();
1710  for ( ; it != xLines.constEnd(); ++it )
1711  {
1712  currentAnnotationString = gridAnnotationString( it->first, Latitude );
1713  currentExtension = qMax( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
1714  maxExtension = qMax( maxExtension, currentExtension );
1715  }
1716 
1717  it = yLines.constBegin();
1718  for ( ; it != yLines.constEnd(); ++it )
1719  {
1720  currentAnnotationString = gridAnnotationString( it->first, Longitude );
1721  currentExtension = qMax( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
1722  maxExtension = qMax( maxExtension, currentExtension );
1723  }
1724 
1725  //grid frame
1726  double gridFrameDist = ( mGridFrameStyle == NoGridFrame ) ? 0 : mGridFrameWidth;
1727  return maxExtension + mAnnotationFrameDistance + gridFrameDist;
1728 }
1729 
1730 void QgsComposerMap::mapPolygon( QPolygonF& poly ) const
1731 {
1732  poly.clear();
1733  if ( mRotation == 0 )
1734  {
1735  poly << QPointF( mExtent.xMinimum(), mExtent.yMaximum() );
1736  poly << QPointF( mExtent.xMaximum(), mExtent.yMaximum() );
1737  poly << QPointF( mExtent.xMaximum(), mExtent.yMinimum() );
1738  poly << QPointF( mExtent.xMinimum(), mExtent.yMinimum() );
1739  return;
1740  }
1741 
1742  //there is rotation
1743  QgsPoint rotationPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
1744  double dx, dy; //x-, y- shift from rotation point to corner point
1745 
1746  //top left point
1747  dx = rotationPoint.x() - mExtent.xMinimum();
1748  dy = rotationPoint.y() - mExtent.yMaximum();
1749  rotate( mRotation, dx, dy );
1750  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1751 
1752  //top right point
1753  dx = rotationPoint.x() - mExtent.xMaximum();
1754  dy = rotationPoint.y() - mExtent.yMaximum();
1755  rotate( mRotation, dx, dy );
1756  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1757 
1758  //bottom right point
1759  dx = rotationPoint.x() - mExtent.xMaximum();
1760  dy = rotationPoint.y() - mExtent.yMinimum();
1761  rotate( mRotation, dx, dy );
1762  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1763 
1764  //bottom left point
1765  dx = rotationPoint.x() - mExtent.xMinimum();
1766  dy = rotationPoint.y() - mExtent.yMinimum();
1767  rotate( mRotation, dx, dy );
1768  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1769 }
1770 
1772 {
1773  if ( mRotation == 0 )
1774  {
1775  extent = mExtent;
1776  return;
1777  }
1778 
1779  QPolygonF poly;
1780  mapPolygon( poly );
1781  QRectF bRect = poly.boundingRect();
1782  extent.setXMinimum( bRect.left() );
1783  extent.setXMaximum( bRect.right() );
1784  extent.setYMinimum( bRect.top() );
1785  extent.setYMaximum( bRect.bottom() );
1786  return;
1787 }
1788 
1790 {
1791  double extentWidth = mExtent.width();
1792  if ( extentWidth <= 0 )
1793  {
1794  return 1;
1795  }
1796  return rect().width() / extentWidth;
1797 }
1798 
1800 {
1801  if ( mOverviewFrameMapId != -1 )
1802  {
1803  const QgsComposerMap* map = mComposition->getComposerMapById( mapId );
1804  if ( map )
1805  {
1806  QObject::disconnect( map, SIGNAL( extentChanged() ), this, SLOT( repaint() ) );
1807  }
1808  }
1809  mOverviewFrameMapId = mapId;
1810  if ( mOverviewFrameMapId != -1 )
1811  {
1812  const QgsComposerMap* map = mComposition->getComposerMapById( mapId );
1813  if ( map )
1814  {
1815  QObject::connect( map, SIGNAL( extentChanged() ), this, SLOT( repaint() ) );
1816  }
1817  }
1818  update();
1819 }
1820 
1822 {
1823  delete mOverviewFrameMapSymbol;
1824  mOverviewFrameMapSymbol = symbol;
1825 }
1826 
1827 void QgsComposerMap::setOverviewBlendMode( QPainter::CompositionMode blendMode )
1828 {
1830  update();
1831 }
1832 
1834 {
1835  mOverviewInverted = inverted;
1836  update();
1837 }
1838 
1840 {
1841  delete mGridLineSymbol;
1842  mGridLineSymbol = symbol;
1843 }
1844 
1845 void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
1846 {
1847  double mmToMapUnits = 1.0 / mapUnitsToMM();
1848  double dxScaled = xShift * mmToMapUnits;
1849  double dyScaled = - yShift * mmToMapUnits;
1850 
1851  rotate( mRotation, dxScaled, dyScaled );
1852 
1853  xShift = dxScaled;
1854  yShift = dyScaled;
1855 }
1856 
1857 QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
1858 {
1859  QPolygonF mapPoly = transformedMapPolygon();
1860  if ( mapPoly.size() < 1 )
1861  {
1862  return QPointF( 0, 0 );
1863  }
1864 
1865  QgsRectangle tExtent = transformedExtent();
1866  QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
1867  double dx = mapCoords.x() - rotationPoint.x();
1868  double dy = mapCoords.y() - rotationPoint.y();
1869  rotate( -mRotation, dx, dy );
1870  QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
1871 
1872  QgsRectangle unrotatedExtent = transformedExtent();
1873  double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
1874  double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
1875  return QPointF( xItem, yItem );
1876 }
1877 
1879 {
1880  if ( p.x() <= pen().widthF() )
1881  {
1882  return Left;
1883  }
1884  else if ( p.x() >= ( rect().width() - pen().widthF() ) )
1885  {
1886  return Right;
1887  }
1888  else if ( p.y() <= pen().widthF() )
1889  {
1890  return Top;
1891  }
1892  else
1893  {
1894  return Bottom;
1895  }
1896 }
1897 
1898 void QgsComposerMap::drawCanvasItems( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
1899 {
1900  if ( !mMapCanvas || !mDrawCanvasItems )
1901  {
1902  return;
1903  }
1904 
1905  QList<QGraphicsItem*> itemList = mMapCanvas->items();
1906  if ( itemList.size() < 1 )
1907  {
1908  return;
1909  }
1910  QGraphicsItem* currentItem = 0;
1911 
1912 #if QT_VERSION >= 0x40600 //Qt 4.6 provides the items in visibility order
1913  for ( int i = itemList.size() - 1; i >= 0; --i )
1914  {
1915  currentItem = itemList.at( i );
1916  //don't draw mapcanvasmap (has z value -10)
1917  if ( !currentItem || currentItem->data( 0 ).toString() != "AnnotationItem" )
1918  {
1919  continue;
1920  }
1921  drawCanvasItem( currentItem, painter, itemStyle );
1922  }
1923 #else //Qt <4.6 provides the items in random order
1924  QMultiMap<int, QGraphicsItem*> topLevelItems;
1925  QMultiMap<QGraphicsItem*, QGraphicsItem*> childItems; //QMultiMap<parentItem, childItem>
1926 
1927  for ( int i = 0; i < itemList.size(); ++i )
1928  {
1929  currentItem = itemList.at( i );
1930  //don't draw mapcanvasmap (has z value -10)
1931  if ( !currentItem || currentItem->data( 0 ) != "AnnotationItem" )
1932  {
1933  continue;
1934  }
1935  if ( currentItem->parentItem() )
1936  {
1937  childItems.insert( currentItem->parentItem(), currentItem );
1938  }
1939  else
1940  {
1941  topLevelItems.insert( currentItem->zValue(), currentItem );
1942  }
1943  }
1944 
1945  QMultiMap<int, QGraphicsItem*>::iterator topLevelIt = topLevelItems.begin();
1946  for ( ; topLevelIt != topLevelItems.end(); ++topLevelIt )
1947  {
1948  drawCanvasItem( topLevelIt.value(), painter, itemStyle );
1949  //Draw children. They probably should be sorted according to z-order, but we don't do it because this code is only
1950  //there for backward compatibility. And currently, having several embedded children is not used in QGIS
1951  QMap<QGraphicsItem*, QGraphicsItem*>::iterator childIt = childItems.find( topLevelIt.value() );
1952  while ( childIt != childItems.end() && childIt.key() == topLevelIt.value() )
1953  {
1954  drawCanvasItem( childIt.value(), painter, itemStyle );
1955  ++childIt;
1956  }
1957  }
1958 #endif
1959 }
1960 
1961 void QgsComposerMap::drawCanvasItem( QGraphicsItem* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
1962 {
1963  if ( !item || !mMapCanvas || !mMapRenderer || !item->isVisible() )
1964  {
1965  return;
1966  }
1967 
1968  painter->save();
1969 
1970  QgsRectangle rendererExtent = mMapRenderer->extent();
1971  QgsRectangle composerMapExtent = mExtent;
1972 
1973  //determine scale factor according to graphics view dpi
1974  double scaleFactor = 1.0 / mMapCanvas->logicalDpiX() * 25.4;
1975 
1976  double itemX, itemY;
1977  QGraphicsItem* parent = item->parentItem();
1978  if ( !parent )
1979  {
1980  QPointF mapPos = composerMapPosForItem( item );
1981  itemX = mapPos.x();
1982  itemY = mapPos.y();
1983  }
1984  else //place item relative to the parent item
1985  {
1986  QPointF itemScenePos = item->scenePos();
1987  QPointF parentScenePos = parent->scenePos();
1988 
1989  QPointF mapPos = composerMapPosForItem( parent );
1990 
1991  itemX = mapPos.x() + ( itemScenePos.x() - parentScenePos.x() ) * scaleFactor;
1992  itemY = mapPos.y() + ( itemScenePos.y() - parentScenePos.y() ) * scaleFactor;
1993  }
1994  painter->translate( itemX, itemY );
1995 
1996 
1997  painter->scale( scaleFactor, scaleFactor );
1998 
1999  //a little trick to let the item know that the paint request comes from the composer
2000  item->setData( 1, "composer" );
2001  item->paint( painter, itemStyle, 0 );
2002  item->setData( 1, "" );
2003  painter->restore();
2004 }
2005 
2006 QPointF QgsComposerMap::composerMapPosForItem( const QGraphicsItem* item ) const
2007 {
2008  if ( !item || !mMapCanvas || !mMapRenderer )
2009  {
2010  return QPointF( 0, 0 );
2011  }
2012 
2013  if ( mExtent.height() <= 0 || mExtent.width() <= 0 || mMapCanvas->width() <= 0 || mMapCanvas->height() <= 0 )
2014  {
2015  return QPointF( 0, 0 );
2016  }
2017 
2018  QRectF graphicsSceneRect = mMapCanvas->sceneRect();
2019  QPointF itemScenePos = item->scenePos();
2020  QgsRectangle mapRendererExtent = mMapRenderer->extent();
2021 
2022  double mapX = itemScenePos.x() / graphicsSceneRect.width() * mapRendererExtent.width() + mapRendererExtent.xMinimum();
2023  double mapY = mapRendererExtent.yMaximum() - itemScenePos.y() / graphicsSceneRect.height() * mapRendererExtent.height();
2024  return mapToItemCoords( QPointF( mapX, mapY ) );
2025 }
2026 
2028 {
2029  switch ( border )
2030  {
2031  case QgsComposerMap::Left:
2033  break;
2034  case QgsComposerMap::Right:
2036  break;
2037  case QgsComposerMap::Top:
2039  break;
2042  break;
2043  default:
2044  return;
2045  }
2047  update();
2048 }
2049 
2051 {
2052  switch ( border )
2053  {
2054  case QgsComposerMap::Left:
2056  break;
2057  case QgsComposerMap::Right:
2059  break;
2060  case QgsComposerMap::Top:
2062  break;
2064  default:
2066  break;
2067  }
2068 }
2069 
2071 {
2072  switch ( border )
2073  {
2074  case QgsComposerMap::Left:
2076  break;
2077  case QgsComposerMap::Right:
2079  break;
2080  case QgsComposerMap::Top:
2082  break;
2085  break;
2086  default:
2087  return;
2088  break;
2089  }
2091  update();
2092 }
2093 
2095 {
2096  switch ( border )
2097  {
2098  case QgsComposerMap::Left:
2100  break;
2101  case QgsComposerMap::Right:
2103  break;
2104  case QgsComposerMap::Top:
2106  break;
2108  default:
2110  break;
2111  }
2112 }
2113 
2114 void QgsComposerMap::sortGridLinesOnBorders( const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines, QMap< double, double >& leftFrameEntries,
2115  QMap< double, double >& rightFrameEntries, QMap< double, double >& topFrameEntries, QMap< double, double >& bottomFrameEntries ) const
2116 {
2117  QList< QPair< double, QPointF > > borderPositions;
2118  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
2119  for ( ; it != hLines.constEnd(); ++it )
2120  {
2121  borderPositions << qMakePair( it->first, it->second.p1() );
2122  borderPositions << qMakePair( it->first, it->second.p2() );
2123  }
2124  it = vLines.constBegin();
2125  for ( ; it != vLines.constEnd(); ++it )
2126  {
2127  borderPositions << qMakePair( it->first, it->second.p1() );
2128  borderPositions << qMakePair( it->first, it->second.p2() );
2129  }
2130 
2131  QList< QPair< double, QPointF > >::const_iterator bIt = borderPositions.constBegin();
2132  for ( ; bIt != borderPositions.constEnd(); ++bIt )
2133  {
2134  Border frameBorder = borderForLineCoord( bIt->second );
2135  if ( frameBorder == QgsComposerMap::Left )
2136  {
2137  leftFrameEntries.insert( bIt->second.y(), bIt->first );
2138  }
2139  else if ( frameBorder == QgsComposerMap::Right )
2140  {
2141  rightFrameEntries.insert( bIt->second.y(), bIt->first );
2142  }
2143  else if ( frameBorder == QgsComposerMap::Top )
2144  {
2145  topFrameEntries.insert( bIt->second.x(), bIt->first );
2146  }
2147  else //Bottom
2148  {
2149  bottomFrameEntries.insert( bIt->second.x(), bIt->first );
2150  }
2151  }
2152 }
2153 
2155 {
2156  if ( mOverviewFrameMapId == -1 || !mComposition )
2157  {
2158  return;
2159  }
2160 
2161  const QgsComposerMap* overviewFrameMap = mComposition->getComposerMapById( mOverviewFrameMapId );
2162  if ( !overviewFrameMap )
2163  {
2164  return;
2165  }
2166 
2167  QgsRectangle otherExtent = overviewFrameMap->extent();
2168  QgsRectangle thisExtent = extent();
2169  QgsRectangle intersectRect = thisExtent.intersect( &otherExtent );
2170 
2171  QgsRenderContext context;
2172  context.setPainter( p );
2173  if ( mPreviewMode == Rectangle )
2174  {
2175  return;
2176  }
2177  else
2178  {
2179  context.setScaleFactor( 1.0 );
2180  context.setRasterScaleFactor( mComposition->printResolution() / 25.4 );
2181  }
2182 
2183  p->save();
2184  p->setCompositionMode( mOverviewBlendMode );
2186 
2187  //construct a polygon corresponding to the intersecting map extent
2188  QPolygonF intersectPolygon;
2189  double x = ( intersectRect.xMinimum() - thisExtent.xMinimum() ) / thisExtent.width() * rect().width();
2190  double y = ( thisExtent.yMaximum() - intersectRect.yMaximum() ) / thisExtent.height() * rect().height();
2191  double width = intersectRect.width() / thisExtent.width() * rect().width();
2192  double height = intersectRect.height() / thisExtent.height() * rect().height();
2193  intersectPolygon << QPointF( x, y ) << QPointF( x + width, y ) << QPointF( x + width, y + height ) << QPointF( x, y + height ) << QPointF( x, y );
2194 
2195  QList<QPolygonF> rings; //empty list
2196  if ( !mOverviewInverted )
2197  {
2198  //Render the intersecting map extent
2199  mOverviewFrameMapSymbol->renderPolygon( intersectPolygon, &rings, 0, context );;
2200  }
2201  else
2202  {
2203  //We are inverting the overview frame (ie, shading outside the intersecting extent)
2204  //Construct a polygon corresponding to the overview map extent
2205  QPolygonF outerPolygon;
2206  outerPolygon << QPointF( 0, 0 ) << QPointF( rect().width(), 0 ) << QPointF( rect().width(), rect().height() ) << QPointF( 0, rect().height() ) << QPointF( 0, 0 );
2207 
2208  //Intersecting extent is an inner ring for the shaded area
2209  rings.append( intersectPolygon );
2210  mOverviewFrameMapSymbol->renderPolygon( outerPolygon, &rings, 0, context );
2211  }
2212 
2213  mOverviewFrameMapSymbol->stopRender( context );
2214  p->restore();
2215 }
2216 
2218 {
2219  delete mOverviewFrameMapSymbol;
2220  QgsStringMap properties;
2221  properties.insert( "color", "255,0,0,255" );
2222  properties.insert( "style", "solid" );
2223  properties.insert( "style_border", "no" );
2226 }
2227 
2229 {
2230  delete mGridLineSymbol;
2231  QgsStringMap properties;
2232  properties.insert( "color", "0,0,0,255" );
2233  properties.insert( "width", "0.3" );
2234  properties.insert( "capstyle", "flat" );
2236 }
2237 
2239 {
2240  QString format = QgsProject::instance()->readEntry( "PositionPrecision", "/DegreeFormat", "D" );
2241 
2242  bool degreeUnits = true;
2243  if ( mMapRenderer )
2244  {
2245  degreeUnits = ( mMapRenderer->mapUnits() == QGis::Degrees );
2246  }
2247 
2248  if ( format == "DM" && degreeUnits )
2249  {
2251  }
2252  else if ( format == "DMS" && degreeUnits )
2253  {
2255  }
2256  else
2257  {
2259  }
2260 }
2261 
2263 {
2264  if ( !mComposition )
2265  {
2266  return;
2267  }
2268 
2269  const QgsComposerMap* existingMap = mComposition->getComposerMapById( mId );
2270  if ( !existingMap )
2271  {
2272  return; //keep mId as it is still available
2273  }
2274 
2275  int maxId = -1;
2276  QList<const QgsComposerMap*> mapList = mComposition->composerMapItems();
2277  QList<const QgsComposerMap*>::const_iterator mapIt = mapList.constBegin();
2278  for ( ; mapIt != mapList.constEnd(); ++mapIt )
2279  {
2280  if (( *mapIt )->id() > maxId )
2281  {
2282  maxId = ( *mapIt )->id();
2283  }
2284  }
2285  mId = maxId + 1;
2286 }