QGIS API Documentation  2.8.2-Wien
 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 "qgscomposermapgrid.h"
20 #include "qgscomposermapoverview.h"
21 #include "qgscomposition.h"
22 #include "qgscomposerutils.h"
23 #include "qgslogger.h"
24 #include "qgsmaprenderer.h"
26 #include "qgsmaplayerregistry.h"
28 #include "qgsmaptopixel.h"
29 #include "qgsproject.h"
30 #include "qgsrasterlayer.h"
31 #include "qgsrendercontext.h"
32 #include "qgsscalecalculator.h"
33 #include "qgsvectorlayer.h"
34 #include "qgspallabeling.h"
35 #include "qgsexpression.h"
36 
37 #include "qgslabel.h"
38 #include "qgslabelattributes.h"
39 #include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance
40 
41 #include <QGraphicsScene>
42 #include <QGraphicsView>
43 #include <QPainter>
44 #include <QSettings>
45 #include <cmath>
46 
47 QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
48  : QgsComposerItem( x, y, width, height, composition )
49  , mGridStack( 0 )
50  , mOverviewStack( 0 )
51  , mMapRotation( 0 )
52  , mEvaluatedMapRotation( 0 )
53  , mKeepLayerSet( false )
54  , mKeepLayerStyles( false )
55  , mUpdatesEnabled( true )
56  , mMapCanvas( 0 )
57  , mDrawCanvasItems( true )
58  , mAtlasDriven( false )
59  , mAtlasScalingMode( Auto )
60  , mAtlasMargin( 0.10 )
61 {
63 
64  mId = 0;
65  assignFreeId();
66 
67  mPreviewMode = QgsComposerMap::Rectangle;
68  mCurrentRectangle = rect();
69 
70  // Cache
71  mCacheUpdated = false;
72  mDrawing = false;
73 
74  //Offset
75  mXOffset = 0.0;
76  mYOffset = 0.0;
77 
78  //get the color for map canvas background and set map background color accordingly
79  int bgRedInt = QgsProject::instance()->readNumEntry( "Gui", "/CanvasColorRedPart", 255 );
80  int bgGreenInt = QgsProject::instance()->readNumEntry( "Gui", "/CanvasColorGreenPart", 255 );
81  int bgBlueInt = QgsProject::instance()->readNumEntry( "Gui", "/CanvasColorBluePart", 255 );
82  setBackgroundColor( QColor( bgRedInt, bgGreenInt, bgBlueInt ) );
83 
84  //calculate mExtent based on width/height ratio and map canvas extent
85  mExtent = mComposition->mapSettings().visibleExtent();
86 
87  init();
88 
89  setSceneRect( QRectF( x, y, width, height ) );
90 }
91 
93  : QgsComposerItem( 0, 0, 10, 10, composition )
94  , mGridStack( 0 )
95  , mOverviewStack( 0 )
96  , mMapRotation( 0 )
97  , mEvaluatedMapRotation( 0 )
98  , mKeepLayerSet( false )
99  , mKeepLayerStyles( false )
100  , mUpdatesEnabled( true )
101  , mMapCanvas( 0 )
102  , mDrawCanvasItems( true )
103  , mAtlasDriven( false )
104  , mAtlasScalingMode( Auto )
105  , mAtlasMargin( 0.10 )
106 {
107  //Offset
108  mXOffset = 0.0;
109  mYOffset = 0.0;
110 
112  mId = mComposition->composerMapItems().size();
113  mPreviewMode = QgsComposerMap::Rectangle;
114  mCurrentRectangle = rect();
115 
116  init();
117  updateToolTip();
118 }
119 
120 void QgsComposerMap::init()
121 {
122  mGridStack = new QgsComposerMapGridStack( this );
123  mOverviewStack = new QgsComposerMapOverviewStack( this );
124  connectUpdateSlot();
125 
126  // data defined strings
127  mDataDefinedNames.insert( QgsComposerObject::MapRotation, QString( "dataDefinedMapRotation" ) );
128  mDataDefinedNames.insert( QgsComposerObject::MapScale, QString( "dataDefinedMapScale" ) );
129  mDataDefinedNames.insert( QgsComposerObject::MapXMin, QString( "dataDefinedMapXMin" ) );
130  mDataDefinedNames.insert( QgsComposerObject::MapYMin, QString( "dataDefinedMapYMin" ) );
131  mDataDefinedNames.insert( QgsComposerObject::MapXMax, QString( "dataDefinedMapXMax" ) );
132  mDataDefinedNames.insert( QgsComposerObject::MapYMax, QString( "dataDefinedMapYMax" ) );
133  mDataDefinedNames.insert( QgsComposerObject::MapAtlasMargin, QString( "dataDefinedMapAtlasMargin" ) );
134 }
135 
136 void QgsComposerMap::updateToolTip()
137 {
138  setToolTip( tr( "Map %1" ).arg( mId ) );
139 }
140 
141 void QgsComposerMap::adjustExtentToItemShape( double itemWidth, double itemHeight, QgsRectangle& extent ) const
142 {
143  double itemWidthHeightRatio = itemWidth / itemHeight;
144  double newWidthHeightRatio = extent.width() / extent.height();
145 
146  if ( itemWidthHeightRatio <= newWidthHeightRatio )
147  {
148  //enlarge height of new extent, ensuring the map center stays the same
149  double newHeight = extent.width() / itemWidthHeightRatio;
150  double deltaHeight = newHeight - extent.height();
151  extent.setYMinimum( extent.yMinimum() - deltaHeight / 2 );
152  extent.setYMaximum( extent.yMaximum() + deltaHeight / 2 );
153  }
154  else
155  {
156  //enlarge width of new extent, ensuring the map center stays the same
157  double newWidth = itemWidthHeightRatio * extent.height();
158  double deltaWidth = newWidth - extent.width();
159  extent.setXMinimum( extent.xMinimum() - deltaWidth / 2 );
160  extent.setXMaximum( extent.xMaximum() + deltaWidth / 2 );
161  }
162 }
163 
165 {
166  delete mOverviewStack;
167  delete mGridStack;
168 }
169 
170 /* This function is called by paint() and cache() to render the map. It does not override any functions
171 from QGraphicsItem. */
172 void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSizeF& size, double dpi, double* forceWidthScale )
173 {
174  Q_UNUSED( forceWidthScale );
175 
176  if ( !painter )
177  {
178  return;
179  }
180  if ( size.width() == 0 || size.height() == 0 )
181  {
182  //don't attempt to draw if size is invalid
183  return;
184  }
185 
186  // render
187  QgsMapRendererCustomPainterJob job( mapSettings( extent, size, dpi ), painter );
188  // Render the map in this thread. This is done because of problems
189  // with printing to printer on Windows (printing to PDF is fine though).
190  // Raster images were not displayed - see #10599
191  job.renderSynchronously();
192 }
193 
194 QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, const QSizeF& size, int dpi ) const
195 {
196  const QgsMapSettings &ms = mComposition->mapSettings();
197 
198  QgsMapSettings jobMapSettings;
199  jobMapSettings.setExtent( extent );
200  jobMapSettings.setOutputSize( size.toSize() );
201  jobMapSettings.setOutputDpi( dpi );
202  jobMapSettings.setMapUnits( ms.mapUnits() );
203  jobMapSettings.setBackgroundColor( Qt::transparent );
204  jobMapSettings.setOutputImageFormat( ms.outputImageFormat() );
205  jobMapSettings.setRotation( mEvaluatedMapRotation );
206 
207  //set layers to render
208  QStringList theLayerSet = layersToRender();
209  if ( -1 != mCurrentExportLayer )
210  {
211  //exporting with separate layers (eg, to svg layers), so we only want to render a single map layer
212  const int layerIdx = mCurrentExportLayer - ( hasBackground() ? 1 : 0 );
213  theLayerSet =
214  ( layerIdx >= 0 && layerIdx < theLayerSet.length() )
215  ? QStringList( theLayerSet[ theLayerSet.length() - layerIdx - 1 ] )
216  : QStringList(); //exporting decorations such as map frame/grid/overview, so no map layers required
217  }
218  jobMapSettings.setLayers( theLayerSet );
219  jobMapSettings.setLayerStyleOverrides( mLayerStyleOverrides );
220  jobMapSettings.setDestinationCrs( ms.destinationCrs() );
221  jobMapSettings.setCrsTransformEnabled( ms.hasCrsTransformEnabled() );
222  jobMapSettings.setFlags( ms.flags() );
223  jobMapSettings.setFlag( QgsMapSettings::DrawSelection, false );
224 
227  {
228  //if outputing composer, disable optimisations like layer simplification
229  jobMapSettings.setFlag( QgsMapSettings::UseRenderingOptimization, false );
230  }
231 
232  //update $map variable. Use QgsComposerItem's id since that is user-definable
234 
235  // composer-specific overrides of flags
236  jobMapSettings.setFlag( QgsMapSettings::ForceVectorOutput ); // force vector output (no caching of marker images etc.)
237  jobMapSettings.setFlag( QgsMapSettings::DrawEditingInfo, false );
238  jobMapSettings.setFlag( QgsMapSettings::UseAdvancedEffects, mComposition->useAdvancedEffects() ); // respect the composition's useAdvancedEffects flag
239 
240  jobMapSettings.datumTransformStore() = ms.datumTransformStore();
241 
242  return jobMapSettings;
243 }
244 
246 {
247  if ( mPreviewMode == Rectangle )
248  {
249  return;
250  }
251 
252  if ( mDrawing )
253  {
254  return;
255  }
256 
257  mDrawing = true;
258 
259  double horizontalVScaleFactor = horizontalViewScaleFactor();
260  if ( horizontalVScaleFactor < 0 )
261  {
262  //make sure scale factor is positive
263  horizontalVScaleFactor = mLastValidViewScaleFactor > 0 ? mLastValidViewScaleFactor : 1;
264  }
265 
266  const QgsRectangle &ext = *currentMapExtent();
267  double widthMM = ext.width() * mapUnitsToMM();
268  double heightMM = ext.height() * mapUnitsToMM();
269 
270  int w = widthMM * horizontalVScaleFactor;
271  int h = heightMM * horizontalVScaleFactor;
272 
273  // limit size of image for better performance
274  if ( w > 5000 || h > 5000 )
275  {
276  if ( w > h )
277  {
278  w = 5000;
279  h = w * heightMM / widthMM;
280  }
281  else
282  {
283  h = 5000;
284  w = h * widthMM / heightMM;
285  }
286  }
287 
288  mCacheImage = QImage( w, h, QImage::Format_ARGB32 );
289 
290  // set DPI of the image
291  mCacheImage.setDotsPerMeterX( 1000 * w / widthMM );
292  mCacheImage.setDotsPerMeterY( 1000 * h / heightMM );
293 
294  if ( hasBackground() )
295  {
296  //Initially fill image with specified background color. This ensures that layers with blend modes will
297  //preview correctly
298  mCacheImage.fill( backgroundColor().rgba() );
299  }
300  else
301  {
302  //no background, but start with empty fill to avoid artifacts
303  mCacheImage.fill( QColor( 255, 255, 255, 0 ).rgba() );
304  }
305 
306  QPainter p( &mCacheImage );
307 
308  draw( &p, ext, QSizeF( w, h ), mCacheImage.logicalDpiX() );
309  p.end();
310  mCacheUpdated = true;
311 
312  mDrawing = false;
313 }
314 
315 void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
316 {
317  Q_UNUSED( pWidget );
318 
319  if ( !mComposition || !painter )
320  {
321  return;
322  }
323  if ( !shouldDrawItem() )
324  {
325  return;
326  }
327 
328  QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
329  painter->save();
330  painter->setClipRect( thisPaintRect );
331 
332  if ( mComposition->plotStyle() == QgsComposition::Preview && mPreviewMode == Rectangle )
333  {
334  // Fill with background color
335  drawBackground( painter );
336  QFont messageFont( "", 12 );
337  painter->setFont( messageFont );
338  painter->setPen( QColor( 0, 0, 0, 125 ) );
339  painter->drawText( thisPaintRect, tr( "Map will be printed here" ) );
340  }
342  {
343  //draw cached pixmap. This function does not call cache() any more because
344  //Qt 4.4.0 and 4.4.1 have problems with recursive paintings
345  //QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
346  //client functions
347 
348  //Background color is already included in cached image, so no need to draw
349 
350  double imagePixelWidth = mCacheImage.width(); //how many pixels of the image are for the map extent?
351  double scale = rect().width() / imagePixelWidth;
352 
353  painter->save();
354 
355  painter->translate( mXOffset, mYOffset );
356  painter->scale( scale, scale );
357  painter->drawImage( 0, 0, mCacheImage );
358 
359  //restore rotation
360  painter->restore();
361 
362  //draw canvas items
363  drawCanvasItems( painter, itemStyle );
364  }
365  else if ( mComposition->plotStyle() == QgsComposition::Print ||
367  {
368  if ( mDrawing )
369  {
370  return;
371  }
372 
373  mDrawing = true;
374  QPaintDevice* thePaintDevice = painter->device();
375  if ( !thePaintDevice )
376  {
377  return;
378  }
379 
380  // Fill with background color
381  if ( shouldDrawPart( Background ) )
382  {
383  drawBackground( painter );
384  }
385 
386  QgsRectangle cExtent = *currentMapExtent();
387 
388  QSizeF theSize( cExtent.width() * mapUnitsToMM(), cExtent.height() * mapUnitsToMM() );
389 
390  painter->save();
391  painter->translate( mXOffset, mYOffset );
392 
393  double dotsPerMM = thePaintDevice->logicalDpiX() / 25.4;
394  theSize *= dotsPerMM; // output size will be in dots (pixels)
395  painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
396  draw( painter, cExtent, theSize, thePaintDevice->logicalDpiX() );
397 
398  //restore rotation
399  painter->restore();
400 
401  //draw canvas items
402  drawCanvasItems( painter, itemStyle );
403 
404  mDrawing = false;
405  }
406 
407  painter->setClipRect( thisPaintRect, Qt::NoClip );
408  if ( shouldDrawPart( OverviewMapExtent ) &&
409  ( mComposition->plotStyle() != QgsComposition::Preview || mPreviewMode != Rectangle ) )
410  {
411  mOverviewStack->drawItems( painter );
412  }
413  if ( shouldDrawPart( Grid ) &&
414  ( mComposition->plotStyle() != QgsComposition::Preview || mPreviewMode != Rectangle ) )
415  {
416  mGridStack->drawItems( painter );
417  }
418  if ( shouldDrawPart( Frame ) )
419  {
420  drawFrame( painter );
421  }
422  if ( isSelected() && shouldDrawPart( SelectionBoxes ) )
423  {
424  drawSelectionBoxes( painter );
425  }
426 
427  painter->restore();
428 }
429 
431 {
432  return
433  ( hasBackground() ? 1 : 0 )
434  + layersToRender().length()
435  + 1 // for grids, if they exist
436  + 1 // for overviews, if they exist
437  + ( hasFrame() ? 1 : 0 )
438  + ( isSelected() ? 1 : 0 )
439  ;
440 }
441 
442 bool QgsComposerMap::shouldDrawPart( PartType part ) const
443 {
444  if ( -1 == mCurrentExportLayer )
445  {
446  //all parts of the composer map are visible
447  return true;
448  }
449 
450  int idx = numberExportLayers();
451  if ( isSelected() )
452  {
453  --idx;
454  if ( SelectionBoxes == part )
455  {
456  return mCurrentExportLayer == idx;
457  }
458  }
459 
460  if ( hasFrame() )
461  {
462  --idx;
463  if ( Frame == part )
464  {
465  return mCurrentExportLayer == idx;
466  }
467  }
468  --idx;
469  if ( OverviewMapExtent == part )
470  {
471  return mCurrentExportLayer == idx;
472  }
473  --idx;
474  if ( Grid == part )
475  {
476  return mCurrentExportLayer == idx;
477  }
478  if ( hasBackground() )
479  {
480  if ( Background == part )
481  {
482  return mCurrentExportLayer == 0;
483  }
484  }
485 
486  return true; // for Layer
487 }
488 
490 {
491  syncLayerSet(); //layer list may have changed
492  mCacheUpdated = false;
493  cache();
494  QGraphicsRectItem::update();
495 }
496 
498 {
499  if ( mPreviewMode == Render )
500  {
502  }
503 }
504 
506 {
507  mCacheUpdated = u;
508 }
509 
511 {
513  return mComposition->mapRenderer();
515 }
516 
517 QStringList QgsComposerMap::layersToRender() const
518 {
519  //use stored layer set or read current set from main canvas
520  QStringList renderLayerSet;
521  if ( mKeepLayerSet )
522  {
523  renderLayerSet = mLayerSet;
524  }
525  else
526  {
527  renderLayerSet = mComposition->mapSettings().layers();
528  }
529 
530  //remove atlas coverage layer if required
531  //TODO - move setting for hiding coverage layer to map item properties
533  {
535  {
536  //hiding coverage layer
537  int removeAt = renderLayerSet.indexOf( mComposition->atlasComposition().coverageLayer()->id() );
538  if ( removeAt != -1 )
539  {
540  renderLayerSet.removeAt( removeAt );
541  }
542  }
543  }
544 
545  return renderLayerSet;
546 }
547 
548 double QgsComposerMap::scale() const
549 {
550  QgsScaleCalculator calculator;
551  calculator.setMapUnits( mComposition->mapSettings().mapUnits() );
552  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
553  return calculator.calculate( *currentMapExtent(), rect().width() );
554 }
555 
556 void QgsComposerMap::resize( double dx, double dy )
557 {
558  //setRect
559  QRectF currentRect = rect();
560  QRectF newSceneRect = QRectF( pos().x(), pos().y(), currentRect.width() + dx, currentRect.height() + dy );
561  setSceneRect( newSceneRect );
562  updateItem();
563 }
564 
565 void QgsComposerMap::moveContent( double dx, double dy )
566 {
567  if ( !mDrawing )
568  {
569  transformShift( dx, dy );
570  currentMapExtent()->setXMinimum( currentMapExtent()->xMinimum() + dx );
571  currentMapExtent()->setXMaximum( currentMapExtent()->xMaximum() + dx );
572  currentMapExtent()->setYMinimum( currentMapExtent()->yMinimum() + dy );
573  currentMapExtent()->setYMaximum( currentMapExtent()->yMaximum() + dy );
574 
575  //in case data defined extents are set, these override the calculated values
576  refreshMapExtents();
577 
578  cache();
579  update();
580  emit itemChanged();
581  emit extentChanged();
582  }
583 }
584 
585 void QgsComposerMap::zoomContent( int delta, double x, double y )
586 {
587  QSettings settings;
588 
589  //read zoom mode
590  QgsComposerItem::ZoomMode zoomMode = ( QgsComposerItem::ZoomMode )settings.value( "/qgis/wheel_action", 2 ).toInt();
591  if ( zoomMode == QgsComposerItem::NoZoom )
592  {
593  //do nothing
594  return;
595  }
596 
597  double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
598  zoomFactor = delta > 0 ? zoomFactor : 1 / zoomFactor;
599 
600  zoomContent( zoomFactor, QPointF( x, y ), zoomMode );
601 }
602 
603 void QgsComposerMap::zoomContent( const double factor, const QPointF point, const ZoomMode mode )
604 {
605  if ( mDrawing )
606  {
607  return;
608  }
609 
610  if ( mode == QgsComposerItem::NoZoom )
611  {
612  //do nothing
613  return;
614  }
615 
616  //find out map coordinates of position
617  double mapX = currentMapExtent()->xMinimum() + ( point.x() / rect().width() ) * ( currentMapExtent()->xMaximum() - currentMapExtent()->xMinimum() );
618  double mapY = currentMapExtent()->yMinimum() + ( 1 - ( point.y() / rect().height() ) ) * ( currentMapExtent()->yMaximum() - currentMapExtent()->yMinimum() );
619 
620  //find out new center point
621  double centerX = ( currentMapExtent()->xMaximum() + currentMapExtent()->xMinimum() ) / 2;
622  double centerY = ( currentMapExtent()->yMaximum() + currentMapExtent()->yMinimum() ) / 2;
623 
624  if ( mode != QgsComposerItem::Zoom )
625  {
626  if ( mode == QgsComposerItem::ZoomRecenter )
627  {
628  centerX = mapX;
629  centerY = mapY;
630  }
631  else if ( mode == QgsComposerItem::ZoomToPoint )
632  {
633  centerX = mapX + ( centerX - mapX ) * ( 1.0 / factor );
634  centerY = mapY + ( centerY - mapY ) * ( 1.0 / factor );
635  }
636  }
637 
638  double newIntervalX, newIntervalY;
639 
640  if ( factor > 0 )
641  {
642  newIntervalX = ( currentMapExtent()->xMaximum() - currentMapExtent()->xMinimum() ) / factor;
643  newIntervalY = ( currentMapExtent()->yMaximum() - currentMapExtent()->yMinimum() ) / factor;
644  }
645  else //no need to zoom
646  {
647  return;
648  }
649 
650  currentMapExtent()->setXMaximum( centerX + newIntervalX / 2 );
651  currentMapExtent()->setXMinimum( centerX - newIntervalX / 2 );
652  currentMapExtent()->setYMaximum( centerY + newIntervalY / 2 );
653  currentMapExtent()->setYMinimum( centerY - newIntervalY / 2 );
654 
655  if ( mAtlasDriven && mAtlasScalingMode == Fixed && mComposition->atlasMode() != QgsComposition::AtlasOff )
656  {
657  //if map is atlas controlled and set to fixed scaling mode, then scale changes should be treated as permanant
658  //and also apply to the map's original extent (see #9602)
659  //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems
660  QgsScaleCalculator calculator;
661  calculator.setMapUnits( mComposition->mapSettings().mapUnits() );
662  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
663  double scaleRatio = scale() / calculator.calculate( mExtent, rect().width() );
664  mExtent.scale( scaleRatio );
665  }
666 
667  //recalculate data defined scale and extents, since that may override zoom
668  refreshMapExtents();
669 
670  cache();
671  update();
672  emit itemChanged();
673  emit extentChanged();
674 }
675 
676 void QgsComposerMap::setSceneRect( const QRectF& rectangle )
677 {
678  double w = rectangle.width();
679  double h = rectangle.height();
680  //prepareGeometryChange();
681 
682  QgsComposerItem::setSceneRect( rectangle );
683 
684  //QGraphicsRectItem::update();
685  double newHeight = mExtent.width() * h / w;
686  mExtent = QgsRectangle( mExtent.xMinimum(), mExtent.yMinimum(), mExtent.xMaximum(), mExtent.yMinimum() + newHeight );
687 
688  //recalculate data defined scale and extents
689  refreshMapExtents();
690  mCacheUpdated = false;
691 
693  update();
694  emit itemChanged();
695  emit extentChanged();
696 }
697 
699 {
700  if ( *currentMapExtent() == extent )
701  {
702  return;
703  }
705 
706  //recalculate data defined scale and extents, since that may override extent
707  refreshMapExtents();
708 
709  //adjust height
710  QRectF currentRect = rect();
711 
712  double newHeight = currentRect.width() * currentMapExtent()->height() / currentMapExtent()->width();
713 
714  setSceneRect( QRectF( pos().x(), pos().y(), currentRect.width(), newHeight ) );
715  updateItem();
716 }
717 
719 {
720  QgsRectangle newExtent = extent;
721  //Make sure the width/height ratio is the same as the current composer map extent.
722  //This is to keep the map item frame size fixed
723  double currentWidthHeightRatio = currentMapExtent()->width() / currentMapExtent()->height();
724  double newWidthHeightRatio = newExtent.width() / newExtent.height();
725 
726  if ( currentWidthHeightRatio < newWidthHeightRatio )
727  {
728  //enlarge height of new extent, ensuring the map center stays the same
729  double newHeight = newExtent.width() / currentWidthHeightRatio;
730  double deltaHeight = newHeight - newExtent.height();
731  newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
732  newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
733  }
734  else
735  {
736  //enlarge width of new extent, ensuring the map center stays the same
737  double newWidth = currentWidthHeightRatio * newExtent.height();
738  double deltaWidth = newWidth - newExtent.width();
739  newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
740  newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
741  }
742 
743  if ( *currentMapExtent() == newExtent )
744  {
745  return;
746  }
747  *currentMapExtent() = newExtent;
748 
749  //recalculate data defined scale and extents, since that may override extent
750  refreshMapExtents();
751 
752  mCacheUpdated = false;
753  updateItem();
754  emit itemChanged();
755  emit extentChanged();
756 }
757 
759 {
760  if ( mAtlasFeatureExtent != extent )
761  {
762  //don't adjust size of item, instead adjust size of bounds to fit
763  QgsRectangle newExtent = extent;
764 
765  //Make sure the width/height ratio is the same as the map item size
766  double currentWidthHeightRatio = rect().width() / rect().height();
767  double newWidthHeightRatio = newExtent.width() / newExtent.height();
768 
769  if ( currentWidthHeightRatio < newWidthHeightRatio )
770  {
771  //enlarge height of new extent, ensuring the map center stays the same
772  double newHeight = newExtent.width() / currentWidthHeightRatio;
773  double deltaHeight = newHeight - newExtent.height();
774  newExtent.setYMinimum( extent.yMinimum() - deltaHeight / 2 );
775  newExtent.setYMaximum( extent.yMaximum() + deltaHeight / 2 );
776  }
777  else if ( currentWidthHeightRatio >= newWidthHeightRatio )
778  {
779  //enlarge width of new extent, ensuring the map center stays the same
780  double newWidth = currentWidthHeightRatio * newExtent.height();
781  double deltaWidth = newWidth - newExtent.width();
782  newExtent.setXMinimum( extent.xMinimum() - deltaWidth / 2 );
783  newExtent.setXMaximum( extent.xMaximum() + deltaWidth / 2 );
784  }
785 
786  mAtlasFeatureExtent = newExtent;
787  }
788 
789  //recalculate data defined scale and extents, since that may override extents
790  refreshMapExtents();
791 
792  mCacheUpdated = false;
793  emit preparedForAtlas();
794  updateItem();
795  emit itemChanged();
796  emit extentChanged();
797 }
798 
800 {
801  //non-const version
802  if ( mAtlasDriven && mComposition->atlasMode() != QgsComposition::AtlasOff )
803  {
804  //if atlas is enabled, and we are either exporting the composition or previewing the atlas, then
805  //return the current temporary atlas feature extent
806  return &mAtlasFeatureExtent;
807  }
808  else
809  {
810  //otherwise return permenant user set extent
811  return &mExtent;
812  }
813 }
814 
816 {
817  //const version
818  if ( mAtlasDriven && mComposition->atlasMode() != QgsComposition::AtlasOff )
819  {
820  //if atlas is enabled, and we are either exporting the composition or previewing the atlas, then
821  //return the current temporary atlas feature extent
822  return &mAtlasFeatureExtent;
823  }
824  else
825  {
826  //otherwise return permenant user set extent
827  return &mExtent;
828  }
829 }
830 
831 void QgsComposerMap::setNewScale( double scaleDenominator, bool forceUpdate )
832 {
833  double currentScaleDenominator = scale();
834 
835  if ( scaleDenominator == currentScaleDenominator || scaleDenominator == 0 )
836  {
837  return;
838  }
839 
840  double scaleRatio = scaleDenominator / currentScaleDenominator;
841  currentMapExtent()->scale( scaleRatio );
842 
843  if ( mAtlasDriven && mAtlasScalingMode == Fixed && mComposition->atlasMode() != QgsComposition::AtlasOff )
844  {
845  //if map is atlas controlled and set to fixed scaling mode, then scale changes should be treated as permanant
846  //and also apply to the map's original extent (see #9602)
847  //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems
848  QgsScaleCalculator calculator;
849  calculator.setMapUnits( mComposition->mapSettings().mapUnits() );
850  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
851  scaleRatio = scaleDenominator / calculator.calculate( mExtent, rect().width() );
852  mExtent.scale( scaleRatio );
853  }
854 
855  mCacheUpdated = false;
856  if ( forceUpdate )
857  {
858  cache();
859  update();
860  emit itemChanged();
861  }
862  emit extentChanged();
863 }
864 
866 {
867  mPreviewMode = m;
868  emit itemChanged();
869 }
870 
871 void QgsComposerMap::setOffset( double xOffset, double yOffset )
872 {
873  mXOffset = xOffset;
874  mYOffset = yOffset;
875 }
876 
878 {
879  //kept for api compatibility with QGIS 2.0
880  setMapRotation( r );
881 }
882 
884 {
885  mMapRotation = r;
886  mEvaluatedMapRotation = mMapRotation;
887  emit mapRotationChanged( r );
888  emit itemChanged();
889  update();
890 }
891 
893 {
894  return valueType == QgsComposerObject::EvaluatedValue ? mEvaluatedMapRotation : mMapRotation;
895 }
896 
897 void QgsComposerMap::refreshMapExtents()
898 {
899  //data defined map extents set?
900  QVariant exprVal;
901 
902  QgsRectangle newExtent = *currentMapExtent();
903  bool useDdXMin = false;
904  bool useDdXMax = false;
905  bool useDdYMin = false;
906  bool useDdYMax = false;
907  double minXD = 0;
908  double minYD = 0;
909  double maxXD = 0;
910  double maxYD = 0;
911 
913  {
914  bool ok;
915  minXD = exprVal.toDouble( &ok );
916  QgsDebugMsg( QString( "exprVal Map XMin:%1" ).arg( minXD ) );
917  if ( ok && !exprVal.isNull() )
918  {
919  useDdXMin = true;
920  newExtent.setXMinimum( minXD );
921  }
922  }
924  {
925  bool ok;
926  minYD = exprVal.toDouble( &ok );
927  QgsDebugMsg( QString( "exprVal Map YMin:%1" ).arg( minYD ) );
928  if ( ok && !exprVal.isNull() )
929  {
930  useDdYMin = true;
931  newExtent.setYMinimum( minYD );
932  }
933  }
935  {
936  bool ok;
937  maxXD = exprVal.toDouble( &ok );
938  QgsDebugMsg( QString( "exprVal Map XMax:%1" ).arg( maxXD ) );
939  if ( ok && !exprVal.isNull() )
940  {
941  useDdXMax = true;
942  newExtent.setXMaximum( maxXD );
943  }
944  }
946  {
947  bool ok;
948  maxYD = exprVal.toDouble( &ok );
949  QgsDebugMsg( QString( "exprVal Map YMax:%1" ).arg( maxYD ) );
950  if ( ok && !exprVal.isNull() )
951  {
952  useDdYMax = true;
953  newExtent.setYMaximum( maxYD );
954  }
955  }
956 
957  if ( newExtent != *currentMapExtent() )
958  {
959  //calculate new extents to fit data defined extents
960 
961  //Make sure the width/height ratio is the same as in current map extent.
962  //This is to keep the map item frame and the page layout fixed
963  double currentWidthHeightRatio = currentMapExtent()->width() / currentMapExtent()->height();
964  double newWidthHeightRatio = newExtent.width() / newExtent.height();
965 
966  if ( currentWidthHeightRatio < newWidthHeightRatio )
967  {
968  //enlarge height of new extent, ensuring the map center stays the same
969  double newHeight = newExtent.width() / currentWidthHeightRatio;
970  double deltaHeight = newHeight - newExtent.height();
971  newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
972  newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
973  }
974  else
975  {
976  //enlarge width of new extent, ensuring the map center stays the same
977  double newWidth = currentWidthHeightRatio * newExtent.height();
978  double deltaWidth = newWidth - newExtent.width();
979  newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
980  newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
981  }
982 
983  *currentMapExtent() = newExtent;
984  }
985 
986  //now refresh scale, as this potentially overrides extents
987 
988  //data defined map scale set?
990  {
991  bool ok;
992  double scaleD = exprVal.toDouble( &ok );
993  QgsDebugMsg( QString( "exprVal Map Scale:%1" ).arg( scaleD ) );
994  if ( ok && !exprVal.isNull() )
995  {
996  setNewScale( scaleD, false );
997  newExtent = *currentMapExtent();
998  }
999  }
1000 
1001  if ( useDdXMax || useDdXMin || useDdYMax || useDdYMin )
1002  {
1003  //if only one of min/max was set for either x or y, then make sure our extent is locked on that value
1004  //as we can do this without altering the scale
1005  if ( useDdXMin && !useDdXMax )
1006  {
1007  double xMax = currentMapExtent()->xMaximum() - ( currentMapExtent()->xMinimum() - minXD );
1008  newExtent.setXMinimum( minXD );
1009  newExtent.setXMaximum( xMax );
1010  }
1011  else if ( !useDdXMin && useDdXMax )
1012  {
1013  double xMin = currentMapExtent()->xMinimum() - ( currentMapExtent()->xMaximum() - maxXD );
1014  newExtent.setXMinimum( xMin );
1015  newExtent.setXMaximum( maxXD );
1016  }
1017  if ( useDdYMin && !useDdYMax )
1018  {
1019  double yMax = currentMapExtent()->yMaximum() - ( currentMapExtent()->yMinimum() - minYD );
1020  newExtent.setYMinimum( minYD );
1021  newExtent.setYMaximum( yMax );
1022  }
1023  else if ( !useDdYMin && useDdYMax )
1024  {
1025  double yMin = currentMapExtent()->yMinimum() - ( currentMapExtent()->yMaximum() - maxYD );
1026  newExtent.setYMinimum( yMin );
1027  newExtent.setYMaximum( maxYD );
1028  }
1029 
1030  if ( newExtent != *currentMapExtent() )
1031  {
1032  *currentMapExtent() = newExtent;
1033  }
1034  }
1035 
1036  //lastly, map rotation overrides all
1037  double mapRotation = mMapRotation;
1038 
1039  //data defined map rotation set?
1041  {
1042  bool ok;
1043  double rotationD = exprVal.toDouble( &ok );
1044  QgsDebugMsg( QString( "exprVal Map Rotation:%1" ).arg( rotationD ) );
1045  if ( ok && !exprVal.isNull() )
1046  {
1047  mapRotation = rotationD;
1048  }
1049  }
1050 
1051  if ( mEvaluatedMapRotation != mapRotation )
1052  {
1053  mEvaluatedMapRotation = mapRotation;
1054  emit mapRotationChanged( mapRotation );
1055  }
1056 
1057 }
1058 
1060 {
1061  if ( !mUpdatesEnabled )
1062  {
1063  return;
1064  }
1065 
1066  if ( mPreviewMode != QgsComposerMap::Rectangle && !mCacheUpdated )
1067  {
1068  cache();
1069  }
1071 }
1072 
1074 {
1075  QStringList layers = mComposition->mapSettings().layers();
1076 
1077  QStringList::const_iterator layer_it = layers.constBegin();
1078  QgsMapLayer* currentLayer = 0;
1079 
1080  for ( ; layer_it != layers.constEnd(); ++layer_it )
1081  {
1082  currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
1083  if ( currentLayer )
1084  {
1085  QgsRasterLayer* currentRasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
1086  if ( currentRasterLayer )
1087  {
1088  const QgsRasterDataProvider* rasterProvider = 0;
1089  if (( rasterProvider = currentRasterLayer->dataProvider() ) )
1090  {
1091  if ( rasterProvider->name() == "wms" )
1092  {
1093  return true;
1094  }
1095  }
1096  }
1097  }
1098  }
1099  return false;
1100 }
1101 
1103 {
1104  //check easy things first
1105 
1106  //overviews
1107  if ( mOverviewStack->containsAdvancedEffects() )
1108  {
1109  return true;
1110  }
1111 
1112  //grids
1113  if ( mGridStack->containsAdvancedEffects() )
1114  {
1115  return true;
1116  }
1117 
1118  // check if map contains advanced effects like blend modes, or flattened layers for transparency
1119 
1120  QStringList layers = mComposition->mapSettings().layers();
1121 
1122  QStringList::const_iterator layer_it = layers.constBegin();
1123  QgsMapLayer* currentLayer = 0;
1124 
1125  for ( ; layer_it != layers.constEnd(); ++layer_it )
1126  {
1127  currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
1128  if ( currentLayer )
1129  {
1130  if ( currentLayer->blendMode() != QPainter::CompositionMode_SourceOver )
1131  {
1132  return true;
1133  }
1134  // if vector layer, check labels and feature blend mode
1135  QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
1136  if ( currentVectorLayer )
1137  {
1138  if ( currentVectorLayer->layerTransparency() != 0 )
1139  {
1140  return true;
1141  }
1142  if ( currentVectorLayer->featureBlendMode() != QPainter::CompositionMode_SourceOver )
1143  {
1144  return true;
1145  }
1146  // check label blend modes
1147  if ( QgsPalLabeling::staticWillUseLayer( currentVectorLayer ) )
1148  {
1149  // Check all label blending properties
1150  QgsPalLayerSettings layerSettings = QgsPalLayerSettings::fromLayer( currentVectorLayer );
1151  if (( layerSettings.blendMode != QPainter::CompositionMode_SourceOver ) ||
1152  ( layerSettings.bufferSize != 0 && layerSettings.bufferBlendMode != QPainter::CompositionMode_SourceOver ) ||
1153  ( layerSettings.shadowDraw && layerSettings.shadowBlendMode != QPainter::CompositionMode_SourceOver ) ||
1154  ( layerSettings.shapeDraw && layerSettings.shapeBlendMode != QPainter::CompositionMode_SourceOver ) )
1155  {
1156  return true;
1157  }
1158  }
1159  }
1160  }
1161  }
1162 
1163  return false;
1164 }
1165 
1166 void QgsComposerMap::connectUpdateSlot()
1167 {
1168  //connect signal from layer registry to update in case of new or deleted layers
1170  if ( layerRegistry )
1171  {
1172  connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( updateCachedImage() ) );
1173  connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( updateCachedImage() ) );
1174  }
1175 }
1176 
1177 bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
1178 {
1179  if ( elem.isNull() )
1180  {
1181  return false;
1182  }
1183 
1184  QDomElement composerMapElem = doc.createElement( "ComposerMap" );
1185  composerMapElem.setAttribute( "id", mId );
1186 
1187  //previewMode
1188  if ( mPreviewMode == Cache )
1189  {
1190  composerMapElem.setAttribute( "previewMode", "Cache" );
1191  }
1192  else if ( mPreviewMode == Render )
1193  {
1194  composerMapElem.setAttribute( "previewMode", "Render" );
1195  }
1196  else //rectangle
1197  {
1198  composerMapElem.setAttribute( "previewMode", "Rectangle" );
1199  }
1200 
1201  if ( mKeepLayerSet )
1202  {
1203  composerMapElem.setAttribute( "keepLayerSet", "true" );
1204  }
1205  else
1206  {
1207  composerMapElem.setAttribute( "keepLayerSet", "false" );
1208  }
1209 
1210  if ( mDrawCanvasItems )
1211  {
1212  composerMapElem.setAttribute( "drawCanvasItems", "true" );
1213  }
1214  else
1215  {
1216  composerMapElem.setAttribute( "drawCanvasItems", "false" );
1217  }
1218 
1219  //extent
1220  QDomElement extentElem = doc.createElement( "Extent" );
1221  extentElem.setAttribute( "xmin", qgsDoubleToString( mExtent.xMinimum() ) );
1222  extentElem.setAttribute( "xmax", qgsDoubleToString( mExtent.xMaximum() ) );
1223  extentElem.setAttribute( "ymin", qgsDoubleToString( mExtent.yMinimum() ) );
1224  extentElem.setAttribute( "ymax", qgsDoubleToString( mExtent.yMaximum() ) );
1225  composerMapElem.appendChild( extentElem );
1226 
1227  //map rotation
1228  composerMapElem.setAttribute( "mapRotation", QString::number( mMapRotation ) );
1229 
1230  //layer set
1231  QDomElement layerSetElem = doc.createElement( "LayerSet" );
1232  QStringList::const_iterator layerIt = mLayerSet.constBegin();
1233  for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
1234  {
1235  QDomElement layerElem = doc.createElement( "Layer" );
1236  QDomText layerIdText = doc.createTextNode( *layerIt );
1237  layerElem.appendChild( layerIdText );
1238  layerSetElem.appendChild( layerElem );
1239  }
1240  composerMapElem.appendChild( layerSetElem );
1241 
1242  // override styles
1243  if ( mKeepLayerStyles )
1244  {
1245  QDomElement stylesElem = doc.createElement( "LayerStyles" );
1246  QMap<QString, QString>::const_iterator styleIt = mLayerStyleOverrides.constBegin();
1247  for ( ; styleIt != mLayerStyleOverrides.constEnd(); ++styleIt )
1248  {
1249  QDomElement styleElem = doc.createElement( "LayerStyle" );
1250  styleElem.setAttribute( "layerid", styleIt.key() );
1251  QgsMapLayerStyle style( styleIt.value() );
1252  style.writeXml( styleElem );
1253  stylesElem.appendChild( styleElem );
1254  }
1255  composerMapElem.appendChild( stylesElem );
1256  }
1257 
1258  //write a dummy "Grid" element to prevent crashes on pre 2.5 versions (refs #10905)
1259  QDomElement gridElem = doc.createElement( "Grid" );
1260  composerMapElem.appendChild( gridElem );
1261 
1262  //grids
1263  mGridStack->writeXML( composerMapElem, doc );
1264 
1265  //overviews
1266  mOverviewStack->writeXML( composerMapElem, doc );
1267 
1268  //atlas
1269  QDomElement atlasElem = doc.createElement( "AtlasMap" );
1270  atlasElem.setAttribute( "atlasDriven", mAtlasDriven );
1271  atlasElem.setAttribute( "scalingMode", mAtlasScalingMode );
1272  atlasElem.setAttribute( "margin", qgsDoubleToString( mAtlasMargin ) );
1273  composerMapElem.appendChild( atlasElem );
1274 
1275  elem.appendChild( composerMapElem );
1276  return _writeXML( composerMapElem, doc );
1277 }
1278 
1279 bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& doc )
1280 {
1281  if ( itemElem.isNull() )
1282  {
1283  return false;
1284  }
1285 
1286  QString idRead = itemElem.attribute( "id", "not found" );
1287  if ( idRead != "not found" )
1288  {
1289  mId = idRead.toInt();
1290  updateToolTip();
1291  }
1292  mPreviewMode = Rectangle;
1293 
1294  //previewMode
1295  QString previewMode = itemElem.attribute( "previewMode" );
1296  if ( previewMode == "Cache" )
1297  {
1298  mPreviewMode = Cache;
1299  }
1300  else if ( previewMode == "Render" )
1301  {
1302  mPreviewMode = Render;
1303  }
1304  else
1305  {
1306  mPreviewMode = Rectangle;
1307  }
1308 
1309  //extent
1310  QDomNodeList extentNodeList = itemElem.elementsByTagName( "Extent" );
1311  if ( extentNodeList.size() > 0 )
1312  {
1313  QDomElement extentElem = extentNodeList.at( 0 ).toElement();
1314  double xmin, xmax, ymin, ymax;
1315  xmin = extentElem.attribute( "xmin" ).toDouble();
1316  xmax = extentElem.attribute( "xmax" ).toDouble();
1317  ymin = extentElem.attribute( "ymin" ).toDouble();
1318  ymax = extentElem.attribute( "ymax" ).toDouble();
1319  setNewExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
1320  }
1321 
1322  //map rotation
1323  if ( itemElem.attribute( "mapRotation", "0" ).toDouble() != 0 )
1324  {
1325  mMapRotation = itemElem.attribute( "mapRotation", "0" ).toDouble();
1326  }
1327 
1328  //mKeepLayerSet flag
1329  QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
1330  if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
1331  {
1332  mKeepLayerSet = true;
1333  }
1334  else
1335  {
1336  mKeepLayerSet = false;
1337  }
1338 
1339  QString drawCanvasItemsFlag = itemElem.attribute( "drawCanvasItems", "true" );
1340  if ( drawCanvasItemsFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
1341  {
1342  mDrawCanvasItems = true;
1343  }
1344  else
1345  {
1346  mDrawCanvasItems = false;
1347  }
1348 
1349  mLayerStyleOverrides.clear();
1350 
1351  //mLayerSet
1352  QDomNodeList layerSetNodeList = itemElem.elementsByTagName( "LayerSet" );
1353  QStringList layerSet;
1354  if ( layerSetNodeList.size() > 0 )
1355  {
1356  QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
1357  QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( "Layer" );
1358  for ( int i = 0; i < layerIdNodeList.size(); ++i )
1359  {
1360  const QDomElement& layerIdElement = layerIdNodeList.at( i ).toElement();
1361  layerSet << layerIdElement.text();
1362  }
1363  }
1364  mLayerSet = layerSet;
1365 
1366  // override styles
1367  QDomNodeList layerStylesNodeList = itemElem.elementsByTagName( "LayerStyles" );
1368  mKeepLayerStyles = layerStylesNodeList.size() > 0;
1369  if ( mKeepLayerStyles )
1370  {
1371  QDomElement layerStylesElem = layerStylesNodeList.at( 0 ).toElement();
1372  QDomNodeList layerStyleNodeList = layerStylesElem.elementsByTagName( "LayerStyle" );
1373  for ( int i = 0; i < layerStyleNodeList.size(); ++i )
1374  {
1375  const QDomElement& layerStyleElement = layerStyleNodeList.at( i ).toElement();
1376  QString layerId = layerStyleElement.attribute( "layerid" );
1377  QgsMapLayerStyle style;
1378  style.readXml( layerStyleElement );
1379  mLayerStyleOverrides.insert( layerId, style.xmlData() );
1380  }
1381  }
1382 
1383  mDrawing = false;
1384  mNumCachedLayers = 0;
1385  mCacheUpdated = false;
1386 
1387  //overviews
1388  mOverviewStack->readXML( itemElem, doc );
1389 
1390  //grids
1391  mGridStack->readXML( itemElem, doc );
1392 
1393  //load grid / grid annotation in old xml format
1394  //only do this if the grid stack didn't load any grids, otherwise this will
1395  //be the dummy element created by QGIS >= 2.5 (refs #10905)
1396  QDomNodeList gridNodeList = itemElem.elementsByTagName( "Grid" );
1397  if ( mGridStack->size() == 0 && gridNodeList.size() > 0 )
1398  {
1399  QDomElement gridElem = gridNodeList.at( 0 ).toElement();
1400  QgsComposerMapGrid* mapGrid = new QgsComposerMapGrid( tr( "Grid %1" ).arg( 1 ), this );
1401  mapGrid->setEnabled( gridElem.attribute( "show", "0" ) != "0" );
1402  mapGrid->setStyle( QgsComposerMapGrid::GridStyle( gridElem.attribute( "gridStyle", "0" ).toInt() ) );
1403  mapGrid->setIntervalX( gridElem.attribute( "intervalX", "0" ).toDouble() );
1404  mapGrid->setIntervalY( gridElem.attribute( "intervalY", "0" ).toDouble() );
1405  mapGrid->setOffsetX( gridElem.attribute( "offsetX", "0" ).toDouble() );
1406  mapGrid->setOffsetY( gridElem.attribute( "offsetY", "0" ).toDouble() );
1407  mapGrid->setCrossLength( gridElem.attribute( "crossLength", "3" ).toDouble() );
1408  mapGrid->setFrameStyle(( QgsComposerMapGrid::FrameStyle )gridElem.attribute( "gridFrameStyle", "0" ).toInt() );
1409  mapGrid->setFrameWidth( gridElem.attribute( "gridFrameWidth", "2.0" ).toDouble() );
1410  mapGrid->setFramePenSize( gridElem.attribute( "gridFramePenThickness", "0.5" ).toDouble() );
1411  mapGrid->setFramePenColor( QgsSymbolLayerV2Utils::decodeColor( gridElem.attribute( "framePenColor", "0,0,0" ) ) );
1412  mapGrid->setFrameFillColor1( QgsSymbolLayerV2Utils::decodeColor( gridElem.attribute( "frameFillColor1", "255,255,255,255" ) ) );
1413  mapGrid->setFrameFillColor2( QgsSymbolLayerV2Utils::decodeColor( gridElem.attribute( "frameFillColor2", "0,0,0,255" ) ) );
1414  mapGrid->setBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) itemElem.attribute( "gridBlendMode", "0" ).toUInt() ) );
1415  QDomElement gridSymbolElem = gridElem.firstChildElement( "symbol" );
1416  QgsLineSymbolV2* lineSymbol = 0;
1417  if ( gridSymbolElem.isNull() )
1418  {
1419  //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
1420  lineSymbol = QgsLineSymbolV2::createSimple( QgsStringMap() );
1421  lineSymbol->setWidth( gridElem.attribute( "penWidth", "0" ).toDouble() );
1422  lineSymbol->setColor( QColor( gridElem.attribute( "penColorRed", "0" ).toInt(),
1423  gridElem.attribute( "penColorGreen", "0" ).toInt(),
1424  gridElem.attribute( "penColorBlue", "0" ).toInt() ) );
1425  }
1426  else
1427  {
1428  lineSymbol = QgsSymbolLayerV2Utils::loadSymbol<QgsLineSymbolV2>( gridSymbolElem );
1429  }
1430  mapGrid->setLineSymbol( lineSymbol );
1431 
1432  //annotation
1433  QDomNodeList annotationNodeList = gridElem.elementsByTagName( "Annotation" );
1434  if ( annotationNodeList.size() > 0 )
1435  {
1436  QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
1437  mapGrid->setAnnotationEnabled( annotationElem.attribute( "show", "0" ) != "0" );
1438  mapGrid->setAnnotationFormat( QgsComposerMapGrid::AnnotationFormat( annotationElem.attribute( "format", "0" ).toInt() ) );
1439  mapGrid->setAnnotationPosition( QgsComposerMapGrid::AnnotationPosition( annotationElem.attribute( "leftPosition", "0" ).toInt() ), QgsComposerMapGrid::Left );
1440  mapGrid->setAnnotationPosition( QgsComposerMapGrid::AnnotationPosition( annotationElem.attribute( "rightPosition", "0" ).toInt() ), QgsComposerMapGrid::Right );
1441  mapGrid->setAnnotationPosition( QgsComposerMapGrid::AnnotationPosition( annotationElem.attribute( "topPosition", "0" ).toInt() ), QgsComposerMapGrid::Top );
1442  mapGrid->setAnnotationPosition( QgsComposerMapGrid::AnnotationPosition( annotationElem.attribute( "bottomPosition", "0" ).toInt() ), QgsComposerMapGrid::Bottom );
1443  mapGrid->setAnnotationDirection( QgsComposerMapGrid::AnnotationDirection( annotationElem.attribute( "leftDirection", "0" ).toInt() ), QgsComposerMapGrid::Left );
1444  mapGrid->setAnnotationDirection( QgsComposerMapGrid::AnnotationDirection( annotationElem.attribute( "rightDirection", "0" ).toInt() ), QgsComposerMapGrid::Right );
1445  mapGrid->setAnnotationDirection( QgsComposerMapGrid::AnnotationDirection( annotationElem.attribute( "topDirection", "0" ).toInt() ), QgsComposerMapGrid::Top );
1446  mapGrid->setAnnotationDirection( QgsComposerMapGrid::AnnotationDirection( annotationElem.attribute( "bottomDirection", "0" ).toInt() ), QgsComposerMapGrid::Bottom );
1447  mapGrid->setAnnotationFrameDistance( annotationElem.attribute( "frameDistance", "0" ).toDouble() );
1448  QFont annotationFont;
1449  annotationFont.fromString( annotationElem.attribute( "font", "" ) );
1450  mapGrid->setAnnotationFont( annotationFont );
1451  mapGrid->setAnnotationFontColor( QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "fontColor", "0,0,0,255" ) ) );
1452 
1453  mapGrid->setAnnotationPrecision( annotationElem.attribute( "precision", "3" ).toInt() );
1454  }
1455  mGridStack->addGrid( mapGrid );
1456  }
1457 
1458  //load overview in old xml format
1459  QDomElement overviewFrameElem = itemElem.firstChildElement( "overviewFrame" );
1460  if ( !overviewFrameElem.isNull() )
1461  {
1462  QgsComposerMapOverview* mapOverview = new QgsComposerMapOverview( tr( "Overview %1" ).arg( mOverviewStack->size() + 1 ), this );
1463 
1464  mapOverview->setFrameMap( overviewFrameElem.attribute( "overviewFrameMap", "-1" ).toInt() );
1465  mapOverview->setBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) overviewFrameElem.attribute( "overviewBlendMode", "0" ).toUInt() ) );
1466  mapOverview->setInverted( overviewFrameElem.attribute( "overviewInverted" ).compare( "true", Qt::CaseInsensitive ) == 0 );
1467  mapOverview->setCentered( overviewFrameElem.attribute( "overviewCentered" ).compare( "true", Qt::CaseInsensitive ) == 0 );
1468 
1469  QgsFillSymbolV2* fillSymbol = 0;
1470  QDomElement overviewFrameSymbolElem = overviewFrameElem.firstChildElement( "symbol" );
1471  if ( !overviewFrameSymbolElem.isNull() )
1472  {
1473  fillSymbol = QgsSymbolLayerV2Utils::loadSymbol<QgsFillSymbolV2>( overviewFrameSymbolElem );
1474  mapOverview->setFrameSymbol( fillSymbol );
1475  }
1476  mOverviewStack->addOverview( mapOverview );
1477  }
1478 
1479  //atlas
1480  QDomNodeList atlasNodeList = itemElem.elementsByTagName( "AtlasMap" );
1481  if ( atlasNodeList.size() > 0 )
1482  {
1483  QDomElement atlasElem = atlasNodeList.at( 0 ).toElement();
1484  mAtlasDriven = ( atlasElem.attribute( "atlasDriven", "0" ) != "0" );
1485  if ( atlasElem.hasAttribute( "fixedScale" ) ) // deprecated XML
1486  {
1487  mAtlasScalingMode = ( atlasElem.attribute( "fixedScale", "0" ) != "0" ) ? Fixed : Auto;
1488  }
1489  else if ( atlasElem.hasAttribute( "scalingMode" ) )
1490  {
1491  mAtlasScalingMode = static_cast<AtlasScalingMode>( atlasElem.attribute( "scalingMode" ).toInt() );
1492  }
1493  mAtlasMargin = atlasElem.attribute( "margin", "0.1" ).toDouble();
1494  }
1495 
1496  //restore general composer item properties
1497  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
1498  if ( composerItemList.size() > 0 )
1499  {
1500  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
1501 
1502  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
1503  {
1504  //in versions prior to 2.1 map rotation was stored in the rotation attribute
1505  mMapRotation = composerItemElem.attribute( "rotation", "0" ).toDouble();
1506  }
1507 
1508  _readXML( composerItemElem, doc );
1509  }
1510 
1512  emit itemChanged();
1513  return true;
1514 }
1515 
1517 {
1518  mLayerSet = mComposition->mapSettings().layers();
1519 
1520  if ( mKeepLayerStyles )
1521  {
1522  // also store styles associated with the layers
1524  }
1525 }
1526 
1528 {
1529  mLayerStyleOverrides.clear();
1530  foreach ( const QString& layerID, mLayerSet )
1531  {
1532  if ( QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID ) )
1533  {
1534  QgsMapLayerStyle style;
1535  style.readFromLayer( layer );
1536  mLayerStyleOverrides.insert( layerID, style.xmlData() );
1537  }
1538  }
1539 }
1540 
1541 void QgsComposerMap::syncLayerSet()
1542 {
1543  if ( mLayerSet.size() < 1 )
1544  {
1545  return;
1546  }
1547 
1548  //if layer set is fixed, do a lookup in the layer registry to also find the non-visible layers
1549  QStringList currentLayerSet;
1550  if ( mKeepLayerSet )
1551  {
1552  currentLayerSet = QgsMapLayerRegistry::instance()->mapLayers().uniqueKeys();
1553  }
1554  else //only consider layers visible in the map
1555  {
1556  currentLayerSet = mComposition->mapSettings().layers();
1557  }
1558 
1559  for ( int i = mLayerSet.size() - 1; i >= 0; --i )
1560  {
1561  if ( !currentLayerSet.contains( mLayerSet.at( i ) ) )
1562  {
1563  mLayerStyleOverrides.remove( mLayerSet.at( i ) );
1564  mLayerSet.removeAt( i );
1565  }
1566  }
1567 }
1568 
1570 {
1571  if ( mGridStack->size() < 1 )
1572  {
1573  QgsComposerMapGrid* grid = new QgsComposerMapGrid( tr( "Grid %1" ).arg( 1 ), this );
1574  mGridStack->addGrid( grid );
1575  }
1576  return mGridStack->grid( 0 );
1577 }
1578 
1579 const QgsComposerMapGrid* QgsComposerMap::constFirstMapGrid() const
1580 {
1581  return const_cast<QgsComposerMap*>( this )->grid();
1582 }
1583 
1585 {
1586  QgsComposerMapGrid* g = grid();
1587  g->setStyle( QgsComposerMapGrid::GridStyle( style ) );
1588 }
1589 
1591 {
1592  const QgsComposerMapGrid* g = constFirstMapGrid();
1593  return ( QgsComposerMap::GridStyle )g->style();
1594 }
1595 
1596 void QgsComposerMap::setGridIntervalX( double interval )
1597 {
1598  QgsComposerMapGrid* g = grid();
1599  g->setIntervalX( interval );
1600 }
1601 
1603 {
1604  const QgsComposerMapGrid* g = constFirstMapGrid();
1605  return g->intervalX();
1606 }
1607 
1608 void QgsComposerMap::setGridIntervalY( double interval )
1609 {
1610  QgsComposerMapGrid* g = grid();
1611  g->setIntervalY( interval );
1612 }
1613 
1615 {
1616  const QgsComposerMapGrid* g = constFirstMapGrid();
1617  return g->intervalY();
1618 }
1619 
1620 void QgsComposerMap::setGridOffsetX( double offset )
1621 {
1622  QgsComposerMapGrid* g = grid();
1623  g->setOffsetX( offset );
1624 }
1625 
1627 {
1628  const QgsComposerMapGrid* g = constFirstMapGrid();
1629  return g->offsetX();
1630 }
1631 
1632 void QgsComposerMap::setGridOffsetY( double offset )
1633 {
1634  QgsComposerMapGrid* g = grid();
1635  g->setOffsetY( offset );
1636 }
1637 
1639 {
1640  const QgsComposerMapGrid* g = constFirstMapGrid();
1641  return g->offsetY();
1642 }
1643 
1645 {
1646  QgsComposerMapGrid* g = grid();
1647  g->setGridLineWidth( w );
1648 }
1649 
1650 void QgsComposerMap::setGridPenColor( const QColor& c )
1651 {
1652  QgsComposerMapGrid* g = grid();
1653  g->setGridLineColor( c );
1654 }
1655 
1656 void QgsComposerMap::setGridPen( const QPen& p )
1657 {
1658  QgsComposerMapGrid* g = grid();
1659  g->setGridLineWidth( p.widthF() );
1660  g->setGridLineColor( p.color() );
1661 }
1662 
1664 {
1665  const QgsComposerMapGrid* g = constFirstMapGrid();
1666  QPen p;
1667  if ( g->lineSymbol() )
1668  {
1669  QgsLineSymbolV2* line = dynamic_cast<QgsLineSymbolV2*>( g->lineSymbol()->clone() );
1670  if ( !line )
1671  {
1672  return p;
1673  }
1674  p.setWidthF( line->width() );
1675  p.setColor( line->color() );
1676  p.setCapStyle( Qt::FlatCap );
1677  delete line;
1678  }
1679  return p;
1680 }
1681 
1683 {
1684  QgsComposerMapGrid* g = grid();
1685  g->setAnnotationFont( f );
1686 }
1687 
1689 {
1690  const QgsComposerMapGrid* g = constFirstMapGrid();
1691  return g->annotationFont();
1692 }
1693 
1695 {
1696  QgsComposerMapGrid* g = grid();
1697  g->setAnnotationFontColor( c );
1698 }
1699 
1701 {
1702  const QgsComposerMapGrid* g = constFirstMapGrid();
1703  return g->annotationFontColor();
1704 }
1705 
1707 {
1708  QgsComposerMapGrid* g = grid();
1709  g->setAnnotationPrecision( p );
1710 }
1711 
1713 {
1714  const QgsComposerMapGrid* g = constFirstMapGrid();
1715  return g->annotationPrecision();
1716 }
1717 
1719 {
1720  QgsComposerMapGrid* g = grid();
1721  g->setAnnotationEnabled( show );
1722 }
1723 
1725 {
1726  const QgsComposerMapGrid* g = constFirstMapGrid();
1727  return g->annotationEnabled();
1728 }
1729 
1731 {
1732  QgsComposerMapGrid* g = grid();
1733  if ( p != QgsComposerMap::Disabled )
1734  {
1736  }
1737  else
1738  {
1740  }
1741 }
1742 
1744 {
1745  const QgsComposerMapGrid* g = constFirstMapGrid();
1747 }
1748 
1750 {
1751  QgsComposerMapGrid* g = grid();
1753 }
1754 
1756 {
1757  const QgsComposerMapGrid* g = constFirstMapGrid();
1758  return g->annotationFrameDistance();
1759 }
1760 
1762 {
1763  QgsComposerMapGrid* g = grid();
1764  //map grid direction to QgsComposerMapGrid direction (values are different)
1766  switch ( d )
1767  {
1769  gridDirection = QgsComposerMapGrid::Horizontal;
1770  break;
1772  gridDirection = QgsComposerMapGrid::Vertical;
1773  break;
1775  gridDirection = QgsComposerMapGrid::BoundaryDirection;
1776  break;
1777  default:
1778  gridDirection = QgsComposerMapGrid::Horizontal;
1779  }
1780  g->setAnnotationDirection( gridDirection, ( QgsComposerMapGrid::BorderSide )border );
1781 
1782 }
1783 
1785 {
1786  const QgsComposerMapGrid* g = constFirstMapGrid();
1788 }
1789 
1791 {
1792  QgsComposerMapGrid* g = grid();
1794 }
1795 
1797 {
1798  const QgsComposerMapGrid* g = constFirstMapGrid();
1800 }
1801 
1803 {
1804  QgsComposerMapGrid* g = grid();
1806 }
1807 
1809 {
1810  const QgsComposerMapGrid* g = constFirstMapGrid();
1812 }
1813 
1815 {
1816  QgsComposerMapGrid* g = grid();
1817  g->setFrameWidth( w );
1818 }
1819 
1821 {
1822  const QgsComposerMapGrid* g = constFirstMapGrid();
1823  return g->frameWidth();
1824 }
1825 
1827 {
1828  QgsComposerMapGrid* g = grid();
1829  g->setFramePenSize( w );
1830 }
1831 
1833 {
1834  const QgsComposerMapGrid* g = constFirstMapGrid();
1835  return g->framePenSize();
1836 }
1837 
1839 {
1840  QgsComposerMapGrid* g = grid();
1841  g->setFramePenColor( c );
1842 }
1843 
1845 {
1846  const QgsComposerMapGrid* g = constFirstMapGrid();
1847  return g->framePenColor();
1848 }
1849 
1851 {
1852  QgsComposerMapGrid* g = grid();
1853  g->setFrameFillColor1( c );
1854 }
1855 
1857 {
1858  const QgsComposerMapGrid* g = constFirstMapGrid();
1859  return g->frameFillColor1();
1860 }
1861 
1863 {
1864  QgsComposerMapGrid* g = grid();
1865  g->setFrameFillColor2( c );
1866 }
1867 
1869 {
1870  const QgsComposerMapGrid* g = constFirstMapGrid();
1871  return g->frameFillColor2();
1872 }
1873 
1875 {
1876  QgsComposerMapGrid* g = grid();
1877  g->setCrossLength( l );
1878 }
1879 
1881 {
1882  const QgsComposerMapGrid* g = constFirstMapGrid();
1883  return g->crossLength();
1884 }
1885 
1887 {
1888  if ( mOverviewStack->size() < 1 )
1889  {
1890  QgsComposerMapOverview* overview = new QgsComposerMapOverview( tr( "Overview %1" ).arg( 1 ), this );
1891  mOverviewStack->addOverview( overview );
1892  }
1893  return mOverviewStack->overview( 0 );
1894 }
1895 
1896 const QgsComposerMapOverview *QgsComposerMap::constFirstMapOverview() const
1897 {
1898  return const_cast<QgsComposerMap*>( this )->overview();
1899 }
1900 
1901 void QgsComposerMap::setGridBlendMode( QPainter::CompositionMode blendMode )
1902 {
1903  QgsComposerMapGrid* g = grid();
1904  g->setBlendMode( blendMode );
1905 }
1906 
1907 QPainter::CompositionMode QgsComposerMap::gridBlendMode() const
1908 {
1909  const QgsComposerMapGrid* g = constFirstMapGrid();
1910  return g->blendMode();
1911 }
1912 
1914 {
1915  return mCurrentRectangle;
1916 }
1917 
1919 {
1920  QRectF rectangle = rect();
1921  double frameExtension = mFrame ? pen().widthF() / 2.0 : 0.0;
1922  double maxGridExtension = mGridStack ? mGridStack->maxGridExtension() : 0;
1923 
1924  double maxExtension = qMax( frameExtension, maxGridExtension );
1925 
1926  rectangle.setLeft( rectangle.left() - maxExtension );
1927  rectangle.setRight( rectangle.right() + maxExtension );
1928  rectangle.setTop( rectangle.top() - maxExtension );
1929  rectangle.setBottom( rectangle.bottom() + maxExtension );
1930  if ( rectangle != mCurrentRectangle )
1931  {
1932  prepareGeometryChange();
1933  mCurrentRectangle = rectangle;
1934  }
1935 }
1936 
1938 {
1939  QgsComposerItem::setFrameOutlineWidth( outlineWidth );
1941 }
1942 
1943 QgsRectangle QgsComposerMap::transformedExtent() const
1944 {
1945  double dx = mXOffset;
1946  double dy = mYOffset;
1947  transformShift( dx, dy );
1948  return QgsRectangle( currentMapExtent()->xMinimum() - dx, currentMapExtent()->yMinimum() - dy, currentMapExtent()->xMaximum() - dx, currentMapExtent()->yMaximum() - dy );
1949 }
1950 
1952 {
1953  double dx = mXOffset;
1954  double dy = mYOffset;
1955  //qWarning("offset");
1956  //qWarning(QString::number(dx).toLocal8Bit().data());
1957  //qWarning(QString::number(dy).toLocal8Bit().data());
1958  transformShift( dx, dy );
1959  //qWarning("transformed:");
1960  //qWarning(QString::number(dx).toLocal8Bit().data());
1961  //qWarning(QString::number(dy).toLocal8Bit().data());
1962  QPolygonF poly = visibleExtentPolygon();
1963  poly.translate( -dx, -dy );
1964  return poly;
1965 }
1966 
1967 void QgsComposerMap::mapPolygon( const QgsRectangle& extent, QPolygonF& poly ) const
1968 {
1969  poly.clear();
1970  if ( mEvaluatedMapRotation == 0 )
1971  {
1972  poly << QPointF( extent.xMinimum(), extent.yMaximum() );
1973  poly << QPointF( extent.xMaximum(), extent.yMaximum() );
1974  poly << QPointF( extent.xMaximum(), extent.yMinimum() );
1975  poly << QPointF( extent.xMinimum(), extent.yMinimum() );
1976  //ensure polygon is closed by readding first point
1977  poly << QPointF( poly.at( 0 ) );
1978  return;
1979  }
1980 
1981  //there is rotation
1982  QgsPoint rotationPoint(( extent.xMaximum() + extent.xMinimum() ) / 2.0, ( extent.yMaximum() + extent.yMinimum() ) / 2.0 );
1983  double dx, dy; //x-, y- shift from rotation point to corner point
1984 
1985  //top left point
1986  dx = rotationPoint.x() - extent.xMinimum();
1987  dy = rotationPoint.y() - extent.yMaximum();
1988  QgsComposerUtils::rotate( mEvaluatedMapRotation, dx, dy );
1989  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
1990 
1991  //top right point
1992  dx = rotationPoint.x() - extent.xMaximum();
1993  dy = rotationPoint.y() - extent.yMaximum();
1994  QgsComposerUtils::rotate( mEvaluatedMapRotation, dx, dy );
1995  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
1996 
1997  //bottom right point
1998  dx = rotationPoint.x() - extent.xMaximum();
1999  dy = rotationPoint.y() - extent.yMinimum();
2000  QgsComposerUtils::rotate( mEvaluatedMapRotation, dx, dy );
2001  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2002 
2003  //bottom left point
2004  dx = rotationPoint.x() - extent.xMinimum();
2005  dy = rotationPoint.y() - extent.yMinimum();
2006  QgsComposerUtils::rotate( mEvaluatedMapRotation, dx, dy );
2007  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2008 
2009  //ensure polygon is closed by readding first point
2010  poly << QPointF( poly.at( 0 ) );
2011 }
2012 
2014 {
2015  QPolygonF poly;
2016  mapPolygon( *currentMapExtent(), poly );
2017  return poly;
2018 }
2019 
2021 {
2022  if ( !QgsComposerItem::id().isEmpty() )
2023  {
2024  return QgsComposerItem::id();
2025  }
2026 
2027  return tr( "Map %1" ).arg( mId );
2028 }
2029 
2031 {
2032  QgsRectangle newExtent = *currentMapExtent();
2033  if ( mEvaluatedMapRotation == 0 )
2034  {
2035  extent = newExtent;
2036  }
2037  else
2038  {
2039  QPolygonF poly;
2040  mapPolygon( newExtent, poly );
2041  QRectF bRect = poly.boundingRect();
2042  extent.setXMinimum( bRect.left() );
2043  extent.setXMaximum( bRect.right() );
2044  extent.setYMinimum( bRect.top() );
2045  extent.setYMaximum( bRect.bottom() );
2046  }
2047 }
2048 
2050 {
2051  double extentWidth = currentMapExtent()->width();
2052  if ( extentWidth <= 0 )
2053  {
2054  return 1;
2055  }
2056  return rect().width() / extentWidth;
2057 }
2058 
2060 {
2062  o->setFrameMap( mapId );
2063 }
2064 
2066 {
2067  const QgsComposerMapOverview* o = constFirstMapOverview();
2068  return o->frameMapId();
2069 }
2070 
2072 {
2073  //updates data defined properties and redraws item to match
2074  if ( property == QgsComposerObject::MapRotation || property == QgsComposerObject::MapScale ||
2075  property == QgsComposerObject::MapXMin || property == QgsComposerObject::MapYMin ||
2076  property == QgsComposerObject::MapXMax || property == QgsComposerObject::MapYMax ||
2077  property == QgsComposerObject::MapAtlasMargin ||
2078  property == QgsComposerObject::AllProperties )
2079  {
2080  QgsRectangle beforeExtent = *currentMapExtent();
2081  refreshMapExtents();
2082  emit itemChanged();
2083  if ( *currentMapExtent() != beforeExtent )
2084  {
2085  emit extentChanged();
2086  }
2087  }
2088 
2089  //force redraw
2090  mCacheUpdated = false;
2091 
2093 }
2094 
2096 {
2098  o->setFrameSymbol( symbol );
2099 }
2100 
2102 {
2104  return o->frameSymbol();
2105 }
2106 
2107 QPainter::CompositionMode QgsComposerMap::overviewBlendMode() const
2108 {
2109  const QgsComposerMapOverview* o = constFirstMapOverview();
2110  return o->blendMode();
2111 }
2112 
2113 void QgsComposerMap::setOverviewBlendMode( QPainter::CompositionMode blendMode )
2114 {
2116  o->setBlendMode( blendMode );
2117 }
2118 
2120 {
2121  const QgsComposerMapOverview* o = constFirstMapOverview();
2122  return o->inverted();
2123 }
2124 
2126 {
2128  o->setInverted( inverted );
2129 }
2130 
2132 {
2133  const QgsComposerMapOverview* o = constFirstMapOverview();
2134  return o->centered();
2135 }
2136 
2138 {
2140  o->setCentered( centered );
2141  //overviewExtentChanged();
2142 }
2143 
2145 {
2146  QgsComposerMapGrid* g = grid();
2147  g->setLineSymbol( symbol );
2148 }
2149 
2151 {
2152  QgsComposerMapGrid* g = grid();
2153  return g->lineSymbol();
2154 }
2155 
2157 {
2158  QgsComposerMapGrid* g = grid();
2159  g->setEnabled( enabled );
2160 }
2161 
2163 {
2164  const QgsComposerMapGrid* g = constFirstMapGrid();
2165  return g->enabled();
2166 }
2167 
2168 void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
2169 {
2170  double mmToMapUnits = 1.0 / mapUnitsToMM();
2171  double dxScaled = xShift * mmToMapUnits;
2172  double dyScaled = - yShift * mmToMapUnits;
2173 
2174  QgsComposerUtils::rotate( mEvaluatedMapRotation, dxScaled, dyScaled );
2175 
2176  xShift = dxScaled;
2177  yShift = dyScaled;
2178 }
2179 
2180 QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
2181 {
2182  QPolygonF mapPoly = transformedMapPolygon();
2183  if ( mapPoly.size() < 1 )
2184  {
2185  return QPointF( 0, 0 );
2186  }
2187 
2188  QgsRectangle tExtent = transformedExtent();
2189  QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
2190  double dx = mapCoords.x() - rotationPoint.x();
2191  double dy = mapCoords.y() - rotationPoint.y();
2192  QgsComposerUtils::rotate( -mEvaluatedMapRotation, dx, dy );
2193  QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
2194 
2195  QgsRectangle unrotatedExtent = transformedExtent();
2196  double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
2197  double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
2198  return QPointF( xItem, yItem );
2199 }
2200 
2202 {
2203 
2204 }
2205 
2206 void QgsComposerMap::drawCanvasItems( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
2207 {
2208  if ( !mMapCanvas || !mDrawCanvasItems )
2209  {
2210  return;
2211  }
2212 
2213  QList<QGraphicsItem*> itemList = mMapCanvas->items();
2214  if ( itemList.size() < 1 )
2215  {
2216  return;
2217  }
2218  QGraphicsItem* currentItem = 0;
2219 
2220  for ( int i = itemList.size() - 1; i >= 0; --i )
2221  {
2222  currentItem = itemList.at( i );
2223  //don't draw mapcanvasmap (has z value -10)
2224  if ( !currentItem || currentItem->data( 0 ).toString() != "AnnotationItem" )
2225  {
2226  continue;
2227  }
2228  drawCanvasItem( currentItem, painter, itemStyle );
2229  }
2230 }
2231 
2232 void QgsComposerMap::drawCanvasItem( QGraphicsItem* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
2233 {
2234  if ( !item || !mMapCanvas || !item->isVisible() )
2235  {
2236  return;
2237  }
2238 
2239  painter->save();
2240  painter->setRenderHint( QPainter::Antialiasing );
2241 
2242  //determine scale factor according to graphics view dpi
2243  double scaleFactor = 1.0 / mMapCanvas->logicalDpiX() * 25.4;
2244 
2245  double itemX, itemY;
2246  QGraphicsItem* parent = item->parentItem();
2247  if ( !parent )
2248  {
2249  QPointF mapPos = composerMapPosForItem( item );
2250  itemX = mapPos.x();
2251  itemY = mapPos.y();
2252  }
2253  else //place item relative to the parent item
2254  {
2255  QPointF itemScenePos = item->scenePos();
2256  QPointF parentScenePos = parent->scenePos();
2257 
2258  QPointF mapPos = composerMapPosForItem( parent );
2259 
2260  itemX = mapPos.x() + ( itemScenePos.x() - parentScenePos.x() ) * scaleFactor;
2261  itemY = mapPos.y() + ( itemScenePos.y() - parentScenePos.y() ) * scaleFactor;
2262  }
2263  painter->translate( itemX, itemY );
2264 
2265  painter->scale( scaleFactor, scaleFactor );
2266 
2267  //a little trick to let the item know that the paint request comes from the composer
2268  item->setData( 1, "composer" );
2269  item->paint( painter, itemStyle, 0 );
2270  item->setData( 1, "" );
2271  painter->restore();
2272 }
2273 
2274 QPointF QgsComposerMap::composerMapPosForItem( const QGraphicsItem* item ) const
2275 {
2276  if ( !item || !mMapCanvas )
2277  {
2278  return QPointF( 0, 0 );
2279  }
2280 
2281  if ( currentMapExtent()->height() <= 0 || currentMapExtent()->width() <= 0 || mMapCanvas->width() <= 0 || mMapCanvas->height() <= 0 )
2282  {
2283  return QPointF( 0, 0 );
2284  }
2285 
2286  QRectF graphicsSceneRect = mMapCanvas->sceneRect();
2287  QPointF itemScenePos = item->scenePos();
2288  QgsRectangle mapRendererExtent = mComposition->mapSettings().visibleExtent();
2289 
2290  double mapX = itemScenePos.x() / graphicsSceneRect.width() * mapRendererExtent.width() + mapRendererExtent.xMinimum();
2291  double mapY = mapRendererExtent.yMaximum() - itemScenePos.y() / graphicsSceneRect.height() * mapRendererExtent.height();
2292  return mapToItemCoords( QPointF( mapX, mapY ) );
2293 }
2294 
2296 {
2297  if ( !mComposition )
2298  {
2299  return;
2300  }
2301 
2302  const QgsComposerMap* existingMap = mComposition->getComposerMapById( mId );
2303  if ( !existingMap )
2304  {
2305  return; //keep mId as it is still available
2306  }
2307 
2308  int maxId = -1;
2309  QList<const QgsComposerMap*> mapList = mComposition->composerMapItems();
2310  QList<const QgsComposerMap*>::const_iterator mapIt = mapList.constBegin();
2311  for ( ; mapIt != mapList.constEnd(); ++mapIt )
2312  {
2313  if (( *mapIt )->id() > maxId )
2314  {
2315  maxId = ( *mapIt )->id();
2316  }
2317  }
2318  mId = maxId + 1;
2319  updateToolTip();
2320 }
2321 
2322 bool QgsComposerMap::imageSizeConsideringRotation( double& width, double& height ) const
2323 {
2324  //kept for api compatibility with QGIS 2.0 - use mMapRotation
2326  return QgsComposerItem::imageSizeConsideringRotation( width, height, mEvaluatedMapRotation );
2328 }
2329 
2330 bool QgsComposerMap::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
2331 {
2332  //kept for api compatibility with QGIS 2.0 - use mMapRotation
2334  return QgsComposerItem::cornerPointOnRotatedAndScaledRect( x, y, width, height, mEvaluatedMapRotation );
2336 }
2337 
2338 void QgsComposerMap::sizeChangedByRotation( double& width, double& height )
2339 {
2340  //kept for api compatibility with QGIS 2.0 - use mMapRotation
2342  return QgsComposerItem::sizeChangedByRotation( width, height, mEvaluatedMapRotation );
2344 }
2345 
2347 {
2348  mAtlasDriven = enabled;
2349 
2350  if ( !enabled )
2351  {
2352  //if not enabling the atlas, we still need to refresh the map extents
2353  //so that data defined extents and scale are recalculated
2354  refreshMapExtents();
2355  }
2356 }
2357 
2359 {
2360  return mAtlasScalingMode == Fixed;
2361 }
2362 
2364 {
2365  // implicit : if set to false => auto scaling
2366  mAtlasScalingMode = fixed ? Fixed : Auto;
2367 }
2368 
2370 {
2371  if ( valueType == QgsComposerObject::EvaluatedValue )
2372  {
2373  //evaluate data defined atlas margin
2374 
2375  //start with user specified margin
2376  double margin = mAtlasMargin;
2377  QVariant exprVal;
2379  {
2380  bool ok;
2381  double ddMargin = exprVal.toDouble( &ok );
2382  QgsDebugMsg( QString( "exprVal Map Atlas Margin:%1" ).arg( ddMargin ) );
2383  if ( ok && !exprVal.isNull() )
2384  {
2385  //divide by 100 to convert to 0 -> 1.0 range
2386  margin = ddMargin / 100;
2387  }
2388  }
2389  return margin;
2390  }
2391  else
2392  {
2393  return mAtlasMargin;
2394  }
2395 }
2396