QGIS API Documentation  2.9.0-Master
 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 : blazek@itc.it
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  mCacheUpdated = false;
492  cache();
493  QGraphicsRectItem::update();
494 }
495 
497 {
498  if ( mPreviewMode == Render )
499  {
501  }
502 }
503 
505 {
506  syncLayerSet();
508 }
509 
511 {
512  mCacheUpdated = u;
513 }
514 
516 {
518  return mComposition->mapRenderer();
520 }
521 
522 QStringList QgsComposerMap::layersToRender() const
523 {
524  //use stored layer set or read current set from main canvas
525  QStringList renderLayerSet;
526  if ( mKeepLayerSet )
527  {
528  renderLayerSet = mLayerSet;
529  }
530  else
531  {
532  renderLayerSet = mComposition->mapSettings().layers();
533  }
534 
535  //remove atlas coverage layer if required
536  //TODO - move setting for hiding coverage layer to map item properties
538  {
540  {
541  //hiding coverage layer
542  int removeAt = renderLayerSet.indexOf( mComposition->atlasComposition().coverageLayer()->id() );
543  if ( removeAt != -1 )
544  {
545  renderLayerSet.removeAt( removeAt );
546  }
547  }
548  }
549 
550  return renderLayerSet;
551 }
552 
553 double QgsComposerMap::scale() const
554 {
555  QgsScaleCalculator calculator;
556  calculator.setMapUnits( mComposition->mapSettings().mapUnits() );
557  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
558  return calculator.calculate( *currentMapExtent(), rect().width() );
559 }
560 
561 void QgsComposerMap::resize( double dx, double dy )
562 {
563  //setRect
564  QRectF currentRect = rect();
565  QRectF newSceneRect = QRectF( pos().x(), pos().y(), currentRect.width() + dx, currentRect.height() + dy );
566  setSceneRect( newSceneRect );
567  updateItem();
568 }
569 
570 void QgsComposerMap::moveContent( double dx, double dy )
571 {
572  if ( !mDrawing )
573  {
574  transformShift( dx, dy );
575  currentMapExtent()->setXMinimum( currentMapExtent()->xMinimum() + dx );
576  currentMapExtent()->setXMaximum( currentMapExtent()->xMaximum() + dx );
577  currentMapExtent()->setYMinimum( currentMapExtent()->yMinimum() + dy );
578  currentMapExtent()->setYMaximum( currentMapExtent()->yMaximum() + dy );
579 
580  //in case data defined extents are set, these override the calculated values
581  refreshMapExtents();
582 
583  cache();
584  update();
585  emit itemChanged();
586  emit extentChanged();
587  }
588 }
589 
590 void QgsComposerMap::zoomContent( int delta, double x, double y )
591 {
592  QSettings settings;
593 
594  //read zoom mode
595  QgsComposerItem::ZoomMode zoomMode = ( QgsComposerItem::ZoomMode )settings.value( "/qgis/wheel_action", 2 ).toInt();
596  if ( zoomMode == QgsComposerItem::NoZoom )
597  {
598  //do nothing
599  return;
600  }
601 
602  double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
603  zoomFactor = delta > 0 ? zoomFactor : 1 / zoomFactor;
604 
605  zoomContent( zoomFactor, QPointF( x, y ), zoomMode );
606 }
607 
608 void QgsComposerMap::zoomContent( const double factor, const QPointF point, const ZoomMode mode )
609 {
610  if ( mDrawing )
611  {
612  return;
613  }
614 
615  if ( mode == QgsComposerItem::NoZoom )
616  {
617  //do nothing
618  return;
619  }
620 
621  //find out map coordinates of position
622  double mapX = currentMapExtent()->xMinimum() + ( point.x() / rect().width() ) * ( currentMapExtent()->xMaximum() - currentMapExtent()->xMinimum() );
623  double mapY = currentMapExtent()->yMinimum() + ( 1 - ( point.y() / rect().height() ) ) * ( currentMapExtent()->yMaximum() - currentMapExtent()->yMinimum() );
624 
625  //find out new center point
626  double centerX = ( currentMapExtent()->xMaximum() + currentMapExtent()->xMinimum() ) / 2;
627  double centerY = ( currentMapExtent()->yMaximum() + currentMapExtent()->yMinimum() ) / 2;
628 
629  if ( mode != QgsComposerItem::Zoom )
630  {
631  if ( mode == QgsComposerItem::ZoomRecenter )
632  {
633  centerX = mapX;
634  centerY = mapY;
635  }
636  else if ( mode == QgsComposerItem::ZoomToPoint )
637  {
638  centerX = mapX + ( centerX - mapX ) * ( 1.0 / factor );
639  centerY = mapY + ( centerY - mapY ) * ( 1.0 / factor );
640  }
641  }
642 
643  double newIntervalX, newIntervalY;
644 
645  if ( factor > 0 )
646  {
647  newIntervalX = ( currentMapExtent()->xMaximum() - currentMapExtent()->xMinimum() ) / factor;
648  newIntervalY = ( currentMapExtent()->yMaximum() - currentMapExtent()->yMinimum() ) / factor;
649  }
650  else //no need to zoom
651  {
652  return;
653  }
654 
655  currentMapExtent()->setXMaximum( centerX + newIntervalX / 2 );
656  currentMapExtent()->setXMinimum( centerX - newIntervalX / 2 );
657  currentMapExtent()->setYMaximum( centerY + newIntervalY / 2 );
658  currentMapExtent()->setYMinimum( centerY - newIntervalY / 2 );
659 
660  if ( mAtlasDriven && mAtlasScalingMode == Fixed && mComposition->atlasMode() != QgsComposition::AtlasOff )
661  {
662  //if map is atlas controlled and set to fixed scaling mode, then scale changes should be treated as permanant
663  //and also apply to the map's original extent (see #9602)
664  //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems
665  QgsScaleCalculator calculator;
666  calculator.setMapUnits( mComposition->mapSettings().mapUnits() );
667  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
668  double scaleRatio = scale() / calculator.calculate( mExtent, rect().width() );
669  mExtent.scale( scaleRatio );
670  }
671 
672  //recalculate data defined scale and extents, since that may override zoom
673  refreshMapExtents();
674 
675  cache();
676  update();
677  emit itemChanged();
678  emit extentChanged();
679 }
680 
681 void QgsComposerMap::setSceneRect( const QRectF& rectangle )
682 {
683  double w = rectangle.width();
684  double h = rectangle.height();
685  //prepareGeometryChange();
686 
687  QgsComposerItem::setSceneRect( rectangle );
688 
689  //QGraphicsRectItem::update();
690  double newHeight = mExtent.width() * h / w;
691  mExtent = QgsRectangle( mExtent.xMinimum(), mExtent.yMinimum(), mExtent.xMaximum(), mExtent.yMinimum() + newHeight );
692 
693  //recalculate data defined scale and extents
694  refreshMapExtents();
695  mCacheUpdated = false;
696 
698  update();
699  emit itemChanged();
700  emit extentChanged();
701 }
702 
704 {
705  if ( *currentMapExtent() == extent )
706  {
707  return;
708  }
710 
711  //recalculate data defined scale and extents, since that may override extent
712  refreshMapExtents();
713 
714  //adjust height
715  QRectF currentRect = rect();
716 
717  double newHeight = currentRect.width() * currentMapExtent()->height() / currentMapExtent()->width();
718 
719  setSceneRect( QRectF( pos().x(), pos().y(), currentRect.width(), newHeight ) );
720  updateItem();
721 }
722 
724 {
725  QgsRectangle newExtent = extent;
726  //Make sure the width/height ratio is the same as the current composer map extent.
727  //This is to keep the map item frame size fixed
728  double currentWidthHeightRatio = currentMapExtent()->width() / currentMapExtent()->height();
729  double newWidthHeightRatio = newExtent.width() / newExtent.height();
730 
731  if ( currentWidthHeightRatio < newWidthHeightRatio )
732  {
733  //enlarge height of new extent, ensuring the map center stays the same
734  double newHeight = newExtent.width() / currentWidthHeightRatio;
735  double deltaHeight = newHeight - newExtent.height();
736  newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
737  newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
738  }
739  else
740  {
741  //enlarge width of new extent, ensuring the map center stays the same
742  double newWidth = currentWidthHeightRatio * newExtent.height();
743  double deltaWidth = newWidth - newExtent.width();
744  newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
745  newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
746  }
747 
748  if ( *currentMapExtent() == newExtent )
749  {
750  return;
751  }
752  *currentMapExtent() = newExtent;
753 
754  //recalculate data defined scale and extents, since that may override extent
755  refreshMapExtents();
756 
757  mCacheUpdated = false;
758  updateItem();
759  emit itemChanged();
760  emit extentChanged();
761 }
762 
764 {
765  if ( mAtlasFeatureExtent != extent )
766  {
767  //don't adjust size of item, instead adjust size of bounds to fit
768  QgsRectangle newExtent = extent;
769 
770  //Make sure the width/height ratio is the same as the map item size
771  double currentWidthHeightRatio = rect().width() / rect().height();
772  double newWidthHeightRatio = newExtent.width() / newExtent.height();
773 
774  if ( currentWidthHeightRatio < newWidthHeightRatio )
775  {
776  //enlarge height of new extent, ensuring the map center stays the same
777  double newHeight = newExtent.width() / currentWidthHeightRatio;
778  double deltaHeight = newHeight - newExtent.height();
779  newExtent.setYMinimum( extent.yMinimum() - deltaHeight / 2 );
780  newExtent.setYMaximum( extent.yMaximum() + deltaHeight / 2 );
781  }
782  else if ( currentWidthHeightRatio >= newWidthHeightRatio )
783  {
784  //enlarge width of new extent, ensuring the map center stays the same
785  double newWidth = currentWidthHeightRatio * newExtent.height();
786  double deltaWidth = newWidth - newExtent.width();
787  newExtent.setXMinimum( extent.xMinimum() - deltaWidth / 2 );
788  newExtent.setXMaximum( extent.xMaximum() + deltaWidth / 2 );
789  }
790 
791  mAtlasFeatureExtent = newExtent;
792  }
793 
794  //recalculate data defined scale and extents, since that may override extents
795  refreshMapExtents();
796 
797  mCacheUpdated = false;
798  emit preparedForAtlas();
799  updateItem();
800  emit itemChanged();
801  emit extentChanged();
802 }
803 
805 {
806  //non-const version
807  if ( mAtlasDriven && mComposition->atlasMode() != QgsComposition::AtlasOff )
808  {
809  //if atlas is enabled, and we are either exporting the composition or previewing the atlas, then
810  //return the current temporary atlas feature extent
811  return &mAtlasFeatureExtent;
812  }
813  else
814  {
815  //otherwise return permenant user set extent
816  return &mExtent;
817  }
818 }
819 
821 {
822  //const version
823  if ( mAtlasDriven && mComposition->atlasMode() != QgsComposition::AtlasOff )
824  {
825  //if atlas is enabled, and we are either exporting the composition or previewing the atlas, then
826  //return the current temporary atlas feature extent
827  return &mAtlasFeatureExtent;
828  }
829  else
830  {
831  //otherwise return permenant user set extent
832  return &mExtent;
833  }
834 }
835 
836 void QgsComposerMap::setNewScale( double scaleDenominator, bool forceUpdate )
837 {
838  double currentScaleDenominator = scale();
839 
840  if ( scaleDenominator == currentScaleDenominator || scaleDenominator == 0 )
841  {
842  return;
843  }
844 
845  double scaleRatio = scaleDenominator / currentScaleDenominator;
846  currentMapExtent()->scale( scaleRatio );
847 
848  if ( mAtlasDriven && mAtlasScalingMode == Fixed && mComposition->atlasMode() != QgsComposition::AtlasOff )
849  {
850  //if map is atlas controlled and set to fixed scaling mode, then scale changes should be treated as permanant
851  //and also apply to the map's original extent (see #9602)
852  //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems
853  QgsScaleCalculator calculator;
854  calculator.setMapUnits( mComposition->mapSettings().mapUnits() );
855  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
856  scaleRatio = scaleDenominator / calculator.calculate( mExtent, rect().width() );
857  mExtent.scale( scaleRatio );
858  }
859 
860  mCacheUpdated = false;
861  if ( forceUpdate )
862  {
863  cache();
864  update();
865  emit itemChanged();
866  }
867  emit extentChanged();
868 }
869 
871 {
872  mPreviewMode = m;
873  emit itemChanged();
874 }
875 
876 void QgsComposerMap::setOffset( double xOffset, double yOffset )
877 {
878  mXOffset = xOffset;
879  mYOffset = yOffset;
880 }
881 
883 {
884  //kept for api compatibility with QGIS 2.0
885  setMapRotation( r );
886 }
887 
889 {
890  mMapRotation = r;
891  mEvaluatedMapRotation = mMapRotation;
892  emit mapRotationChanged( r );
893  emit itemChanged();
894  update();
895 }
896 
898 {
899  return valueType == QgsComposerObject::EvaluatedValue ? mEvaluatedMapRotation : mMapRotation;
900 }
901 
902 void QgsComposerMap::refreshMapExtents()
903 {
904  //data defined map extents set?
905  QVariant exprVal;
906 
907  QgsRectangle newExtent = *currentMapExtent();
908  bool useDdXMin = false;
909  bool useDdXMax = false;
910  bool useDdYMin = false;
911  bool useDdYMax = false;
912  double minXD = 0;
913  double minYD = 0;
914  double maxXD = 0;
915  double maxYD = 0;
916 
918  {
919  bool ok;
920  minXD = exprVal.toDouble( &ok );
921  QgsDebugMsg( QString( "exprVal Map XMin:%1" ).arg( minXD ) );
922  if ( ok && !exprVal.isNull() )
923  {
924  useDdXMin = true;
925  newExtent.setXMinimum( minXD );
926  }
927  }
929  {
930  bool ok;
931  minYD = exprVal.toDouble( &ok );
932  QgsDebugMsg( QString( "exprVal Map YMin:%1" ).arg( minYD ) );
933  if ( ok && !exprVal.isNull() )
934  {
935  useDdYMin = true;
936  newExtent.setYMinimum( minYD );
937  }
938  }
940  {
941  bool ok;
942  maxXD = exprVal.toDouble( &ok );
943  QgsDebugMsg( QString( "exprVal Map XMax:%1" ).arg( maxXD ) );
944  if ( ok && !exprVal.isNull() )
945  {
946  useDdXMax = true;
947  newExtent.setXMaximum( maxXD );
948  }
949  }
951  {
952  bool ok;
953  maxYD = exprVal.toDouble( &ok );
954  QgsDebugMsg( QString( "exprVal Map YMax:%1" ).arg( maxYD ) );
955  if ( ok && !exprVal.isNull() )
956  {
957  useDdYMax = true;
958  newExtent.setYMaximum( maxYD );
959  }
960  }
961 
962  if ( newExtent != *currentMapExtent() )
963  {
964  //calculate new extents to fit data defined extents
965 
966  //Make sure the width/height ratio is the same as in current map extent.
967  //This is to keep the map item frame and the page layout fixed
968  double currentWidthHeightRatio = currentMapExtent()->width() / currentMapExtent()->height();
969  double newWidthHeightRatio = newExtent.width() / newExtent.height();
970 
971  if ( currentWidthHeightRatio < newWidthHeightRatio )
972  {
973  //enlarge height of new extent, ensuring the map center stays the same
974  double newHeight = newExtent.width() / currentWidthHeightRatio;
975  double deltaHeight = newHeight - newExtent.height();
976  newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
977  newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
978  }
979  else
980  {
981  //enlarge width of new extent, ensuring the map center stays the same
982  double newWidth = currentWidthHeightRatio * newExtent.height();
983  double deltaWidth = newWidth - newExtent.width();
984  newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
985  newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
986  }
987 
988  *currentMapExtent() = newExtent;
989  }
990 
991  //now refresh scale, as this potentially overrides extents
992 
993  //data defined map scale set?
995  {
996  bool ok;
997  double scaleD = exprVal.toDouble( &ok );
998  QgsDebugMsg( QString( "exprVal Map Scale:%1" ).arg( scaleD ) );
999  if ( ok && !exprVal.isNull() )
1000  {
1001  setNewScale( scaleD, false );
1002  newExtent = *currentMapExtent();
1003  }
1004  }
1005 
1006  if ( useDdXMax || useDdXMin || useDdYMax || useDdYMin )
1007  {
1008  //if only one of min/max was set for either x or y, then make sure our extent is locked on that value
1009  //as we can do this without altering the scale
1010  if ( useDdXMin && !useDdXMax )
1011  {
1012  double xMax = currentMapExtent()->xMaximum() - ( currentMapExtent()->xMinimum() - minXD );
1013  newExtent.setXMinimum( minXD );
1014  newExtent.setXMaximum( xMax );
1015  }
1016  else if ( !useDdXMin && useDdXMax )
1017  {
1018  double xMin = currentMapExtent()->xMinimum() - ( currentMapExtent()->xMaximum() - maxXD );
1019  newExtent.setXMinimum( xMin );
1020  newExtent.setXMaximum( maxXD );
1021  }
1022  if ( useDdYMin && !useDdYMax )
1023  {
1024  double yMax = currentMapExtent()->yMaximum() - ( currentMapExtent()->yMinimum() - minYD );
1025  newExtent.setYMinimum( minYD );
1026  newExtent.setYMaximum( yMax );
1027  }
1028  else if ( !useDdYMin && useDdYMax )
1029  {
1030  double yMin = currentMapExtent()->yMinimum() - ( currentMapExtent()->yMaximum() - maxYD );
1031  newExtent.setYMinimum( yMin );
1032  newExtent.setYMaximum( maxYD );
1033  }
1034 
1035  if ( newExtent != *currentMapExtent() )
1036  {
1037  *currentMapExtent() = newExtent;
1038  }
1039  }
1040 
1041  //lastly, map rotation overrides all
1042  double mapRotation = mMapRotation;
1043 
1044  //data defined map rotation set?
1046  {
1047  bool ok;
1048  double rotationD = exprVal.toDouble( &ok );
1049  QgsDebugMsg( QString( "exprVal Map Rotation:%1" ).arg( rotationD ) );
1050  if ( ok && !exprVal.isNull() )
1051  {
1052  mapRotation = rotationD;
1053  }
1054  }
1055 
1056  if ( mEvaluatedMapRotation != mapRotation )
1057  {
1058  mEvaluatedMapRotation = mapRotation;
1059  emit mapRotationChanged( mapRotation );
1060  }
1061 
1062 }
1063 
1065 {
1066  if ( !mUpdatesEnabled )
1067  {
1068  return;
1069  }
1070 
1071  if ( mPreviewMode != QgsComposerMap::Rectangle && !mCacheUpdated )
1072  {
1073  cache();
1074  }
1076 }
1077 
1079 {
1080  QStringList layers = mComposition->mapSettings().layers();
1081 
1082  QStringList::const_iterator layer_it = layers.constBegin();
1083  QgsMapLayer* currentLayer = 0;
1084 
1085  for ( ; layer_it != layers.constEnd(); ++layer_it )
1086  {
1087  currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
1088  if ( currentLayer )
1089  {
1090  QgsRasterLayer* currentRasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
1091  if ( currentRasterLayer )
1092  {
1093  const QgsRasterDataProvider* rasterProvider = 0;
1094  if (( rasterProvider = currentRasterLayer->dataProvider() ) )
1095  {
1096  if ( rasterProvider->name() == "wms" )
1097  {
1098  return true;
1099  }
1100  }
1101  }
1102  }
1103  }
1104  return false;
1105 }
1106 
1108 {
1109  //check easy things first
1110 
1111  //overviews
1112  if ( mOverviewStack->containsAdvancedEffects() )
1113  {
1114  return true;
1115  }
1116 
1117  //grids
1118  if ( mGridStack->containsAdvancedEffects() )
1119  {
1120  return true;
1121  }
1122 
1123  // check if map contains advanced effects like blend modes, or flattened layers for transparency
1124 
1125  QStringList layers = mComposition->mapSettings().layers();
1126 
1127  QStringList::const_iterator layer_it = layers.constBegin();
1128  QgsMapLayer* currentLayer = 0;
1129 
1130  for ( ; layer_it != layers.constEnd(); ++layer_it )
1131  {
1132  currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
1133  if ( currentLayer )
1134  {
1135  if ( currentLayer->blendMode() != QPainter::CompositionMode_SourceOver )
1136  {
1137  return true;
1138  }
1139  // if vector layer, check labels and feature blend mode
1140  QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
1141  if ( currentVectorLayer )
1142  {
1143  if ( currentVectorLayer->layerTransparency() != 0 )
1144  {
1145  return true;
1146  }
1147  if ( currentVectorLayer->featureBlendMode() != QPainter::CompositionMode_SourceOver )
1148  {
1149  return true;
1150  }
1151  // check label blend modes
1152  if ( QgsPalLabeling::staticWillUseLayer( currentVectorLayer ) )
1153  {
1154  // Check all label blending properties
1155  QgsPalLayerSettings layerSettings = QgsPalLayerSettings::fromLayer( currentVectorLayer );
1156  if (( layerSettings.blendMode != QPainter::CompositionMode_SourceOver ) ||
1157  ( layerSettings.bufferSize != 0 && layerSettings.bufferBlendMode != QPainter::CompositionMode_SourceOver ) ||
1158  ( layerSettings.shadowDraw && layerSettings.shadowBlendMode != QPainter::CompositionMode_SourceOver ) ||
1159  ( layerSettings.shapeDraw && layerSettings.shapeBlendMode != QPainter::CompositionMode_SourceOver ) )
1160  {
1161  return true;
1162  }
1163  }
1164  }
1165  }
1166  }
1167 
1168  return false;
1169 }
1170 
1171 void QgsComposerMap::connectUpdateSlot()
1172 {
1173  //connect signal from layer registry to update in case of new or deleted layers
1175  if ( layerRegistry )
1176  {
1177  connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( layersChanged() ) );
1178  connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( layersChanged() ) );
1179  }
1180 }
1181 
1182 bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
1183 {
1184  if ( elem.isNull() )
1185  {
1186  return false;
1187  }
1188 
1189  QDomElement composerMapElem = doc.createElement( "ComposerMap" );
1190  composerMapElem.setAttribute( "id", mId );
1191 
1192  //previewMode
1193  if ( mPreviewMode == Cache )
1194  {
1195  composerMapElem.setAttribute( "previewMode", "Cache" );
1196  }
1197  else if ( mPreviewMode == Render )
1198  {
1199  composerMapElem.setAttribute( "previewMode", "Render" );
1200  }
1201  else //rectangle
1202  {
1203  composerMapElem.setAttribute( "previewMode", "Rectangle" );
1204  }
1205 
1206  if ( mKeepLayerSet )
1207  {
1208  composerMapElem.setAttribute( "keepLayerSet", "true" );
1209  }
1210  else
1211  {
1212  composerMapElem.setAttribute( "keepLayerSet", "false" );
1213  }
1214 
1215  if ( mDrawCanvasItems )
1216  {
1217  composerMapElem.setAttribute( "drawCanvasItems", "true" );
1218  }
1219  else
1220  {
1221  composerMapElem.setAttribute( "drawCanvasItems", "false" );
1222  }
1223 
1224  //extent
1225  QDomElement extentElem = doc.createElement( "Extent" );
1226  extentElem.setAttribute( "xmin", qgsDoubleToString( mExtent.xMinimum() ) );
1227  extentElem.setAttribute( "xmax", qgsDoubleToString( mExtent.xMaximum() ) );
1228  extentElem.setAttribute( "ymin", qgsDoubleToString( mExtent.yMinimum() ) );
1229  extentElem.setAttribute( "ymax", qgsDoubleToString( mExtent.yMaximum() ) );
1230  composerMapElem.appendChild( extentElem );
1231 
1232  //map rotation
1233  composerMapElem.setAttribute( "mapRotation", QString::number( mMapRotation ) );
1234 
1235  //layer set
1236  QDomElement layerSetElem = doc.createElement( "LayerSet" );
1237  QStringList::const_iterator layerIt = mLayerSet.constBegin();
1238  for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
1239  {
1240  QDomElement layerElem = doc.createElement( "Layer" );
1241  QDomText layerIdText = doc.createTextNode( *layerIt );
1242  layerElem.appendChild( layerIdText );
1243  layerSetElem.appendChild( layerElem );
1244  }
1245  composerMapElem.appendChild( layerSetElem );
1246 
1247  // override styles
1248  if ( mKeepLayerStyles )
1249  {
1250  QDomElement stylesElem = doc.createElement( "LayerStyles" );
1251  QMap<QString, QString>::const_iterator styleIt = mLayerStyleOverrides.constBegin();
1252  for ( ; styleIt != mLayerStyleOverrides.constEnd(); ++styleIt )
1253  {
1254  QDomElement styleElem = doc.createElement( "LayerStyle" );
1255  styleElem.setAttribute( "layerid", styleIt.key() );
1256  QgsMapLayerStyle style( styleIt.value() );
1257  style.writeXml( styleElem );
1258  stylesElem.appendChild( styleElem );
1259  }
1260  composerMapElem.appendChild( stylesElem );
1261  }
1262 
1263  //write a dummy "Grid" element to prevent crashes on pre 2.5 versions (refs #10905)
1264  QDomElement gridElem = doc.createElement( "Grid" );
1265  composerMapElem.appendChild( gridElem );
1266 
1267  //grids
1268  mGridStack->writeXML( composerMapElem, doc );
1269 
1270  //overviews
1271  mOverviewStack->writeXML( composerMapElem, doc );
1272 
1273  //atlas
1274  QDomElement atlasElem = doc.createElement( "AtlasMap" );
1275  atlasElem.setAttribute( "atlasDriven", mAtlasDriven );
1276  atlasElem.setAttribute( "scalingMode", mAtlasScalingMode );
1277  atlasElem.setAttribute( "margin", qgsDoubleToString( mAtlasMargin ) );
1278  composerMapElem.appendChild( atlasElem );
1279 
1280  elem.appendChild( composerMapElem );
1281  return _writeXML( composerMapElem, doc );
1282 }
1283 
1284 bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& doc )
1285 {
1286  if ( itemElem.isNull() )
1287  {
1288  return false;
1289  }
1290 
1291  QString idRead = itemElem.attribute( "id", "not found" );
1292  if ( idRead != "not found" )
1293  {
1294  mId = idRead.toInt();
1295  updateToolTip();
1296  }
1297  mPreviewMode = Rectangle;
1298 
1299  //previewMode
1300  QString previewMode = itemElem.attribute( "previewMode" );
1301  if ( previewMode == "Cache" )
1302  {
1303  mPreviewMode = Cache;
1304  }
1305  else if ( previewMode == "Render" )
1306  {
1307  mPreviewMode = Render;
1308  }
1309  else
1310  {
1311  mPreviewMode = Rectangle;
1312  }
1313 
1314  //extent
1315  QDomNodeList extentNodeList = itemElem.elementsByTagName( "Extent" );
1316  if ( extentNodeList.size() > 0 )
1317  {
1318  QDomElement extentElem = extentNodeList.at( 0 ).toElement();
1319  double xmin, xmax, ymin, ymax;
1320  xmin = extentElem.attribute( "xmin" ).toDouble();
1321  xmax = extentElem.attribute( "xmax" ).toDouble();
1322  ymin = extentElem.attribute( "ymin" ).toDouble();
1323  ymax = extentElem.attribute( "ymax" ).toDouble();
1324  setNewExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
1325  }
1326 
1327  //map rotation
1328  if ( itemElem.attribute( "mapRotation", "0" ).toDouble() != 0 )
1329  {
1330  mMapRotation = itemElem.attribute( "mapRotation", "0" ).toDouble();
1331  }
1332 
1333  //mKeepLayerSet flag
1334  QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
1335  if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
1336  {
1337  mKeepLayerSet = true;
1338  }
1339  else
1340  {
1341  mKeepLayerSet = false;
1342  }
1343 
1344  QString drawCanvasItemsFlag = itemElem.attribute( "drawCanvasItems", "true" );
1345  if ( drawCanvasItemsFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
1346  {
1347  mDrawCanvasItems = true;
1348  }
1349  else
1350  {
1351  mDrawCanvasItems = false;
1352  }
1353 
1354  mLayerStyleOverrides.clear();
1355 
1356  //mLayerSet
1357  QDomNodeList layerSetNodeList = itemElem.elementsByTagName( "LayerSet" );
1358  QStringList layerSet;
1359  if ( layerSetNodeList.size() > 0 )
1360  {
1361  QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
1362  QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( "Layer" );
1363  for ( int i = 0; i < layerIdNodeList.size(); ++i )
1364  {
1365  const QDomElement& layerIdElement = layerIdNodeList.at( i ).toElement();
1366  layerSet << layerIdElement.text();
1367  }
1368  }
1369  mLayerSet = layerSet;
1370 
1371  // override styles
1372  QDomNodeList layerStylesNodeList = itemElem.elementsByTagName( "LayerStyles" );
1373  mKeepLayerStyles = layerStylesNodeList.size() > 0;
1374  if ( mKeepLayerStyles )
1375  {
1376  QDomElement layerStylesElem = layerStylesNodeList.at( 0 ).toElement();
1377  QDomNodeList layerStyleNodeList = layerStylesElem.elementsByTagName( "LayerStyle" );
1378  for ( int i = 0; i < layerStyleNodeList.size(); ++i )
1379  {
1380  const QDomElement& layerStyleElement = layerStyleNodeList.at( i ).toElement();
1381  QString layerId = layerStyleElement.attribute( "layerid" );
1382  QgsMapLayerStyle style;
1383  style.readXml( layerStyleElement );
1384  mLayerStyleOverrides.insert( layerId, style.xmlData() );
1385  }
1386  }
1387 
1388  mDrawing = false;
1389  mNumCachedLayers = 0;
1390  mCacheUpdated = false;
1391 
1392  //overviews
1393  mOverviewStack->readXML( itemElem, doc );
1394 
1395  //grids
1396  mGridStack->readXML( itemElem, doc );
1397 
1398  //load grid / grid annotation in old xml format
1399  //only do this if the grid stack didn't load any grids, otherwise this will
1400  //be the dummy element created by QGIS >= 2.5 (refs #10905)
1401  QDomNodeList gridNodeList = itemElem.elementsByTagName( "Grid" );
1402  if ( mGridStack->size() == 0 && gridNodeList.size() > 0 )
1403  {
1404  QDomElement gridElem = gridNodeList.at( 0 ).toElement();
1405  QgsComposerMapGrid* mapGrid = new QgsComposerMapGrid( tr( "Grid %1" ).arg( 1 ), this );
1406  mapGrid->setEnabled( gridElem.attribute( "show", "0" ) != "0" );
1407  mapGrid->setStyle( QgsComposerMapGrid::GridStyle( gridElem.attribute( "gridStyle", "0" ).toInt() ) );
1408  mapGrid->setIntervalX( gridElem.attribute( "intervalX", "0" ).toDouble() );
1409  mapGrid->setIntervalY( gridElem.attribute( "intervalY", "0" ).toDouble() );
1410  mapGrid->setOffsetX( gridElem.attribute( "offsetX", "0" ).toDouble() );
1411  mapGrid->setOffsetY( gridElem.attribute( "offsetY", "0" ).toDouble() );
1412  mapGrid->setCrossLength( gridElem.attribute( "crossLength", "3" ).toDouble() );
1413  mapGrid->setFrameStyle(( QgsComposerMapGrid::FrameStyle )gridElem.attribute( "gridFrameStyle", "0" ).toInt() );
1414  mapGrid->setFrameWidth( gridElem.attribute( "gridFrameWidth", "2.0" ).toDouble() );
1415  mapGrid->setFramePenSize( gridElem.attribute( "gridFramePenThickness", "0.5" ).toDouble() );
1416  mapGrid->setFramePenColor( QgsSymbolLayerV2Utils::decodeColor( gridElem.attribute( "framePenColor", "0,0,0" ) ) );
1417  mapGrid->setFrameFillColor1( QgsSymbolLayerV2Utils::decodeColor( gridElem.attribute( "frameFillColor1", "255,255,255,255" ) ) );
1418  mapGrid->setFrameFillColor2( QgsSymbolLayerV2Utils::decodeColor( gridElem.attribute( "frameFillColor2", "0,0,0,255" ) ) );
1419  mapGrid->setBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) itemElem.attribute( "gridBlendMode", "0" ).toUInt() ) );
1420  QDomElement gridSymbolElem = gridElem.firstChildElement( "symbol" );
1421  QgsLineSymbolV2* lineSymbol = 0;
1422  if ( gridSymbolElem.isNull() )
1423  {
1424  //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
1425  lineSymbol = QgsLineSymbolV2::createSimple( QgsStringMap() );
1426  lineSymbol->setWidth( gridElem.attribute( "penWidth", "0" ).toDouble() );
1427  lineSymbol->setColor( QColor( gridElem.attribute( "penColorRed", "0" ).toInt(),
1428  gridElem.attribute( "penColorGreen", "0" ).toInt(),
1429  gridElem.attribute( "penColorBlue", "0" ).toInt() ) );
1430  }
1431  else
1432  {
1433  lineSymbol = QgsSymbolLayerV2Utils::loadSymbol<QgsLineSymbolV2>( gridSymbolElem );
1434  }
1435  mapGrid->setLineSymbol( lineSymbol );
1436 
1437  //annotation
1438  QDomNodeList annotationNodeList = gridElem.elementsByTagName( "Annotation" );
1439  if ( annotationNodeList.size() > 0 )
1440  {
1441  QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
1442  mapGrid->setAnnotationEnabled( annotationElem.attribute( "show", "0" ) != "0" );
1443  mapGrid->setAnnotationFormat( QgsComposerMapGrid::AnnotationFormat( annotationElem.attribute( "format", "0" ).toInt() ) );
1444  mapGrid->setAnnotationPosition( QgsComposerMapGrid::AnnotationPosition( annotationElem.attribute( "leftPosition", "0" ).toInt() ), QgsComposerMapGrid::Left );
1445  mapGrid->setAnnotationPosition( QgsComposerMapGrid::AnnotationPosition( annotationElem.attribute( "rightPosition", "0" ).toInt() ), QgsComposerMapGrid::Right );
1446  mapGrid->setAnnotationPosition( QgsComposerMapGrid::AnnotationPosition( annotationElem.attribute( "topPosition", "0" ).toInt() ), QgsComposerMapGrid::Top );
1447  mapGrid->setAnnotationPosition( QgsComposerMapGrid::AnnotationPosition( annotationElem.attribute( "bottomPosition", "0" ).toInt() ), QgsComposerMapGrid::Bottom );
1448  mapGrid->setAnnotationDirection( QgsComposerMapGrid::AnnotationDirection( annotationElem.attribute( "leftDirection", "0" ).toInt() ), QgsComposerMapGrid::Left );
1449  mapGrid->setAnnotationDirection( QgsComposerMapGrid::AnnotationDirection( annotationElem.attribute( "rightDirection", "0" ).toInt() ), QgsComposerMapGrid::Right );
1450  mapGrid->setAnnotationDirection( QgsComposerMapGrid::AnnotationDirection( annotationElem.attribute( "topDirection", "0" ).toInt() ), QgsComposerMapGrid::Top );
1451  mapGrid->setAnnotationDirection( QgsComposerMapGrid::AnnotationDirection( annotationElem.attribute( "bottomDirection", "0" ).toInt() ), QgsComposerMapGrid::Bottom );
1452  mapGrid->setAnnotationFrameDistance( annotationElem.attribute( "frameDistance", "0" ).toDouble() );
1453  QFont annotationFont;
1454  annotationFont.fromString( annotationElem.attribute( "font", "" ) );
1455  mapGrid->setAnnotationFont( annotationFont );
1456  mapGrid->setAnnotationFontColor( QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "fontColor", "0,0,0,255" ) ) );
1457 
1458  mapGrid->setAnnotationPrecision( annotationElem.attribute( "precision", "3" ).toInt() );
1459  }
1460  mGridStack->addGrid( mapGrid );
1461  }
1462 
1463  //load overview in old xml format
1464  QDomElement overviewFrameElem = itemElem.firstChildElement( "overviewFrame" );
1465  if ( !overviewFrameElem.isNull() )
1466  {
1467  QgsComposerMapOverview* mapOverview = new QgsComposerMapOverview( tr( "Overview %1" ).arg( mOverviewStack->size() + 1 ), this );
1468 
1469  mapOverview->setFrameMap( overviewFrameElem.attribute( "overviewFrameMap", "-1" ).toInt() );
1470  mapOverview->setBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) overviewFrameElem.attribute( "overviewBlendMode", "0" ).toUInt() ) );
1471  mapOverview->setInverted( overviewFrameElem.attribute( "overviewInverted" ).compare( "true", Qt::CaseInsensitive ) == 0 );
1472  mapOverview->setCentered( overviewFrameElem.attribute( "overviewCentered" ).compare( "true", Qt::CaseInsensitive ) == 0 );
1473 
1474  QgsFillSymbolV2* fillSymbol = 0;
1475  QDomElement overviewFrameSymbolElem = overviewFrameElem.firstChildElement( "symbol" );
1476  if ( !overviewFrameSymbolElem.isNull() )
1477  {
1478  fillSymbol = QgsSymbolLayerV2Utils::loadSymbol<QgsFillSymbolV2>( overviewFrameSymbolElem );
1479  mapOverview->setFrameSymbol( fillSymbol );
1480  }
1481  mOverviewStack->addOverview( mapOverview );
1482  }
1483 
1484  //atlas
1485  QDomNodeList atlasNodeList = itemElem.elementsByTagName( "AtlasMap" );
1486  if ( atlasNodeList.size() > 0 )
1487  {
1488  QDomElement atlasElem = atlasNodeList.at( 0 ).toElement();
1489  mAtlasDriven = ( atlasElem.attribute( "atlasDriven", "0" ) != "0" );
1490  if ( atlasElem.hasAttribute( "fixedScale" ) ) // deprecated XML
1491  {
1492  mAtlasScalingMode = ( atlasElem.attribute( "fixedScale", "0" ) != "0" ) ? Fixed : Auto;
1493  }
1494  else if ( atlasElem.hasAttribute( "scalingMode" ) )
1495  {
1496  mAtlasScalingMode = static_cast<AtlasScalingMode>( atlasElem.attribute( "scalingMode" ).toInt() );
1497  }
1498  mAtlasMargin = atlasElem.attribute( "margin", "0.1" ).toDouble();
1499  }
1500 
1501  //restore general composer item properties
1502  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
1503  if ( composerItemList.size() > 0 )
1504  {
1505  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
1506 
1507  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
1508  {
1509  //in versions prior to 2.1 map rotation was stored in the rotation attribute
1510  mMapRotation = composerItemElem.attribute( "rotation", "0" ).toDouble();
1511  }
1512 
1513  _readXML( composerItemElem, doc );
1514  }
1515 
1517  emit itemChanged();
1518  return true;
1519 }
1520 
1522 {
1523  mLayerSet = mComposition->mapSettings().layers();
1524 
1525  if ( mKeepLayerStyles )
1526  {
1527  // also store styles associated with the layers
1529  }
1530 }
1531 
1533 {
1534  mLayerStyleOverrides.clear();
1535  foreach ( const QString& layerID, mLayerSet )
1536  {
1537  if ( QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID ) )
1538  {
1539  QgsMapLayerStyle style;
1540  style.readFromLayer( layer );
1541  mLayerStyleOverrides.insert( layerID, style.xmlData() );
1542  }
1543  }
1544 }
1545 
1546 void QgsComposerMap::syncLayerSet()
1547 {
1548  if ( mLayerSet.size() < 1 )
1549  {
1550  return;
1551  }
1552 
1553  //if layer set is fixed, do a lookup in the layer registry to also find the non-visible layers
1554  QStringList currentLayerSet;
1555  if ( mKeepLayerSet )
1556  {
1557  currentLayerSet = QgsMapLayerRegistry::instance()->mapLayers().uniqueKeys();
1558  }
1559  else //only consider layers visible in the map
1560  {
1561  currentLayerSet = mComposition->mapSettings().layers();
1562  }
1563 
1564  for ( int i = mLayerSet.size() - 1; i >= 0; --i )
1565  {
1566  if ( !currentLayerSet.contains( mLayerSet.at( i ) ) )
1567  {
1568  mLayerStyleOverrides.remove( mLayerSet.at( i ) );
1569  mLayerSet.removeAt( i );
1570  }
1571  }
1572 }
1573 
1575 {
1576  if ( mGridStack->size() < 1 )
1577  {
1578  QgsComposerMapGrid* grid = new QgsComposerMapGrid( tr( "Grid %1" ).arg( 1 ), this );
1579  mGridStack->addGrid( grid );
1580  }
1581  return mGridStack->grid( 0 );
1582 }
1583 
1584 const QgsComposerMapGrid* QgsComposerMap::constFirstMapGrid() const
1585 {
1586  return const_cast<QgsComposerMap*>( this )->grid();
1587 }
1588 
1590 {
1591  QgsComposerMapGrid* g = grid();
1592  g->setStyle( QgsComposerMapGrid::GridStyle( style ) );
1593 }
1594 
1596 {
1597  const QgsComposerMapGrid* g = constFirstMapGrid();
1598  return ( QgsComposerMap::GridStyle )g->style();
1599 }
1600 
1601 void QgsComposerMap::setGridIntervalX( double interval )
1602 {
1603  QgsComposerMapGrid* g = grid();
1604  g->setIntervalX( interval );
1605 }
1606 
1608 {
1609  const QgsComposerMapGrid* g = constFirstMapGrid();
1610  return g->intervalX();
1611 }
1612 
1613 void QgsComposerMap::setGridIntervalY( double interval )
1614 {
1615  QgsComposerMapGrid* g = grid();
1616  g->setIntervalY( interval );
1617 }
1618 
1620 {
1621  const QgsComposerMapGrid* g = constFirstMapGrid();
1622  return g->intervalY();
1623 }
1624 
1625 void QgsComposerMap::setGridOffsetX( double offset )
1626 {
1627  QgsComposerMapGrid* g = grid();
1628  g->setOffsetX( offset );
1629 }
1630 
1632 {
1633  const QgsComposerMapGrid* g = constFirstMapGrid();
1634  return g->offsetX();
1635 }
1636 
1637 void QgsComposerMap::setGridOffsetY( double offset )
1638 {
1639  QgsComposerMapGrid* g = grid();
1640  g->setOffsetY( offset );
1641 }
1642 
1644 {
1645  const QgsComposerMapGrid* g = constFirstMapGrid();
1646  return g->offsetY();
1647 }
1648 
1650 {
1651  QgsComposerMapGrid* g = grid();
1652  g->setGridLineWidth( w );
1653 }
1654 
1655 void QgsComposerMap::setGridPenColor( const QColor& c )
1656 {
1657  QgsComposerMapGrid* g = grid();
1658  g->setGridLineColor( c );
1659 }
1660 
1661 void QgsComposerMap::setGridPen( const QPen& p )
1662 {
1663  QgsComposerMapGrid* g = grid();
1664  g->setGridLineWidth( p.widthF() );
1665  g->setGridLineColor( p.color() );
1666 }
1667 
1669 {
1670  const QgsComposerMapGrid* g = constFirstMapGrid();
1671  QPen p;
1672  if ( g->lineSymbol() )
1673  {
1674  QgsLineSymbolV2* line = dynamic_cast<QgsLineSymbolV2*>( g->lineSymbol()->clone() );
1675  if ( !line )
1676  {
1677  return p;
1678  }
1679  p.setWidthF( line->width() );
1680  p.setColor( line->color() );
1681  p.setCapStyle( Qt::FlatCap );
1682  delete line;
1683  }
1684  return p;
1685 }
1686 
1688 {
1689  QgsComposerMapGrid* g = grid();
1690  g->setAnnotationFont( f );
1691 }
1692 
1694 {
1695  const QgsComposerMapGrid* g = constFirstMapGrid();
1696  return g->annotationFont();
1697 }
1698 
1700 {
1701  QgsComposerMapGrid* g = grid();
1702  g->setAnnotationFontColor( c );
1703 }
1704 
1706 {
1707  const QgsComposerMapGrid* g = constFirstMapGrid();
1708  return g->annotationFontColor();
1709 }
1710 
1712 {
1713  QgsComposerMapGrid* g = grid();
1714  g->setAnnotationPrecision( p );
1715 }
1716 
1718 {
1719  const QgsComposerMapGrid* g = constFirstMapGrid();
1720  return g->annotationPrecision();
1721 }
1722 
1724 {
1725  QgsComposerMapGrid* g = grid();
1726  g->setAnnotationEnabled( show );
1727 }
1728 
1730 {
1731  const QgsComposerMapGrid* g = constFirstMapGrid();
1732  return g->annotationEnabled();
1733 }
1734 
1736 {
1737  QgsComposerMapGrid* g = grid();
1738  if ( p != QgsComposerMap::Disabled )
1739  {
1741  }
1742  else
1743  {
1745  }
1746 }
1747 
1749 {
1750  const QgsComposerMapGrid* g = constFirstMapGrid();
1752 }
1753 
1755 {
1756  QgsComposerMapGrid* g = grid();
1758 }
1759 
1761 {
1762  const QgsComposerMapGrid* g = constFirstMapGrid();
1763  return g->annotationFrameDistance();
1764 }
1765 
1767 {
1768  QgsComposerMapGrid* g = grid();
1769  //map grid direction to QgsComposerMapGrid direction (values are different)
1771  switch ( d )
1772  {
1774  gridDirection = QgsComposerMapGrid::Horizontal;
1775  break;
1777  gridDirection = QgsComposerMapGrid::Vertical;
1778  break;
1780  gridDirection = QgsComposerMapGrid::BoundaryDirection;
1781  break;
1782  default:
1783  gridDirection = QgsComposerMapGrid::Horizontal;
1784  }
1785  g->setAnnotationDirection( gridDirection, ( QgsComposerMapGrid::BorderSide )border );
1786 
1787 }
1788 
1790 {
1791  const QgsComposerMapGrid* g = constFirstMapGrid();
1793 }
1794 
1796 {
1797  QgsComposerMapGrid* g = grid();
1799 }
1800 
1802 {
1803  const QgsComposerMapGrid* g = constFirstMapGrid();
1805 }
1806 
1808 {
1809  QgsComposerMapGrid* g = grid();
1811 }
1812 
1814 {
1815  const QgsComposerMapGrid* g = constFirstMapGrid();
1817 }
1818 
1820 {
1821  QgsComposerMapGrid* g = grid();
1822  g->setFrameWidth( w );
1823 }
1824 
1826 {
1827  const QgsComposerMapGrid* g = constFirstMapGrid();
1828  return g->frameWidth();
1829 }
1830 
1832 {
1833  QgsComposerMapGrid* g = grid();
1834  g->setFramePenSize( w );
1835 }
1836 
1838 {
1839  const QgsComposerMapGrid* g = constFirstMapGrid();
1840  return g->framePenSize();
1841 }
1842 
1844 {
1845  QgsComposerMapGrid* g = grid();
1846  g->setFramePenColor( c );
1847 }
1848 
1850 {
1851  const QgsComposerMapGrid* g = constFirstMapGrid();
1852  return g->framePenColor();
1853 }
1854 
1856 {
1857  QgsComposerMapGrid* g = grid();
1858  g->setFrameFillColor1( c );
1859 }
1860 
1862 {
1863  const QgsComposerMapGrid* g = constFirstMapGrid();
1864  return g->frameFillColor1();
1865 }
1866 
1868 {
1869  QgsComposerMapGrid* g = grid();
1870  g->setFrameFillColor2( c );
1871 }
1872 
1874 {
1875  const QgsComposerMapGrid* g = constFirstMapGrid();
1876  return g->frameFillColor2();
1877 }
1878 
1880 {
1881  QgsComposerMapGrid* g = grid();
1882  g->setCrossLength( l );
1883 }
1884 
1886 {
1887  const QgsComposerMapGrid* g = constFirstMapGrid();
1888  return g->crossLength();
1889 }
1890 
1892 {
1893  if ( mOverviewStack->size() < 1 )
1894  {
1895  QgsComposerMapOverview* overview = new QgsComposerMapOverview( tr( "Overview %1" ).arg( 1 ), this );
1896  mOverviewStack->addOverview( overview );
1897  }
1898  return mOverviewStack->overview( 0 );
1899 }
1900 
1901 const QgsComposerMapOverview *QgsComposerMap::constFirstMapOverview() const
1902 {
1903  return const_cast<QgsComposerMap*>( this )->overview();
1904 }
1905 
1906 void QgsComposerMap::setGridBlendMode( QPainter::CompositionMode blendMode )
1907 {
1908  QgsComposerMapGrid* g = grid();
1909  g->setBlendMode( blendMode );
1910 }
1911 
1912 QPainter::CompositionMode QgsComposerMap::gridBlendMode() const
1913 {
1914  const QgsComposerMapGrid* g = constFirstMapGrid();
1915  return g->blendMode();
1916 }
1917 
1919 {
1920  return mCurrentRectangle;
1921 }
1922 
1924 {
1925  QRectF rectangle = rect();
1926  double frameExtension = mFrame ? pen().widthF() / 2.0 : 0.0;
1927  double maxGridExtension = mGridStack ? mGridStack->maxGridExtension() : 0;
1928 
1929  double maxExtension = qMax( frameExtension, maxGridExtension );
1930 
1931  rectangle.setLeft( rectangle.left() - maxExtension );
1932  rectangle.setRight( rectangle.right() + maxExtension );
1933  rectangle.setTop( rectangle.top() - maxExtension );
1934  rectangle.setBottom( rectangle.bottom() + maxExtension );
1935  if ( rectangle != mCurrentRectangle )
1936  {
1937  prepareGeometryChange();
1938  mCurrentRectangle = rectangle;
1939  }
1940 }
1941 
1943 {
1944  QgsComposerItem::setFrameOutlineWidth( outlineWidth );
1946 }
1947 
1948 QgsRectangle QgsComposerMap::transformedExtent() const
1949 {
1950  double dx = mXOffset;
1951  double dy = mYOffset;
1952  transformShift( dx, dy );
1953  return QgsRectangle( currentMapExtent()->xMinimum() - dx, currentMapExtent()->yMinimum() - dy, currentMapExtent()->xMaximum() - dx, currentMapExtent()->yMaximum() - dy );
1954 }
1955 
1957 {
1958  double dx = mXOffset;
1959  double dy = mYOffset;
1960  //qWarning("offset");
1961  //qWarning(QString::number(dx).toLocal8Bit().data());
1962  //qWarning(QString::number(dy).toLocal8Bit().data());
1963  transformShift( dx, dy );
1964  //qWarning("transformed:");
1965  //qWarning(QString::number(dx).toLocal8Bit().data());
1966  //qWarning(QString::number(dy).toLocal8Bit().data());
1967  QPolygonF poly = visibleExtentPolygon();
1968  poly.translate( -dx, -dy );
1969  return poly;
1970 }
1971 
1972 void QgsComposerMap::mapPolygon( const QgsRectangle& extent, QPolygonF& poly ) const
1973 {
1974  poly.clear();
1975  if ( mEvaluatedMapRotation == 0 )
1976  {
1977  poly << QPointF( extent.xMinimum(), extent.yMaximum() );
1978  poly << QPointF( extent.xMaximum(), extent.yMaximum() );
1979  poly << QPointF( extent.xMaximum(), extent.yMinimum() );
1980  poly << QPointF( extent.xMinimum(), extent.yMinimum() );
1981  //ensure polygon is closed by readding first point
1982  poly << QPointF( poly.at( 0 ) );
1983  return;
1984  }
1985 
1986  //there is rotation
1987  QgsPoint rotationPoint(( extent.xMaximum() + extent.xMinimum() ) / 2.0, ( extent.yMaximum() + extent.yMinimum() ) / 2.0 );
1988  double dx, dy; //x-, y- shift from rotation point to corner point
1989 
1990  //top left point
1991  dx = rotationPoint.x() - extent.xMinimum();
1992  dy = rotationPoint.y() - extent.yMaximum();
1993  QgsComposerUtils::rotate( mEvaluatedMapRotation, dx, dy );
1994  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
1995 
1996  //top right point
1997  dx = rotationPoint.x() - extent.xMaximum();
1998  dy = rotationPoint.y() - extent.yMaximum();
1999  QgsComposerUtils::rotate( mEvaluatedMapRotation, dx, dy );
2000  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2001 
2002  //bottom right point
2003  dx = rotationPoint.x() - extent.xMaximum();
2004  dy = rotationPoint.y() - extent.yMinimum();
2005  QgsComposerUtils::rotate( mEvaluatedMapRotation, dx, dy );
2006  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2007 
2008  //bottom left point
2009  dx = rotationPoint.x() - extent.xMinimum();
2010  dy = rotationPoint.y() - extent.yMinimum();
2011  QgsComposerUtils::rotate( mEvaluatedMapRotation, dx, dy );
2012  poly << QPointF( rotationPoint.x() - dx, rotationPoint.y() - dy );
2013 
2014  //ensure polygon is closed by readding first point
2015  poly << QPointF( poly.at( 0 ) );
2016 }
2017 
2019 {
2020  QPolygonF poly;
2021  mapPolygon( *currentMapExtent(), poly );
2022  return poly;
2023 }
2024 
2026 {
2027  if ( !QgsComposerItem::id().isEmpty() )
2028  {
2029  return QgsComposerItem::id();
2030  }
2031 
2032  return tr( "Map %1" ).arg( mId );
2033 }
2034 
2036 {
2037  QgsRectangle newExtent = *currentMapExtent();
2038  if ( mEvaluatedMapRotation == 0 )
2039  {
2040  extent = newExtent;
2041  }
2042  else
2043  {
2044  QPolygonF poly;
2045  mapPolygon( newExtent, poly );
2046  QRectF bRect = poly.boundingRect();
2047  extent.setXMinimum( bRect.left() );
2048  extent.setXMaximum( bRect.right() );
2049  extent.setYMinimum( bRect.top() );
2050  extent.setYMaximum( bRect.bottom() );
2051  }
2052 }
2053 
2055 {
2056  double extentWidth = currentMapExtent()->width();
2057  if ( extentWidth <= 0 )
2058  {
2059  return 1;
2060  }
2061  return rect().width() / extentWidth;
2062 }
2063 
2065 {
2067  o->setFrameMap( mapId );
2068 }
2069 
2071 {
2072  const QgsComposerMapOverview* o = constFirstMapOverview();
2073  return o->frameMapId();
2074 }
2075 
2077 {
2078  //updates data defined properties and redraws item to match
2079  if ( property == QgsComposerObject::MapRotation || property == QgsComposerObject::MapScale ||
2080  property == QgsComposerObject::MapXMin || property == QgsComposerObject::MapYMin ||
2081  property == QgsComposerObject::MapXMax || property == QgsComposerObject::MapYMax ||
2082  property == QgsComposerObject::MapAtlasMargin ||
2083  property == QgsComposerObject::AllProperties )
2084  {
2085  QgsRectangle beforeExtent = *currentMapExtent();
2086  refreshMapExtents();
2087  emit itemChanged();
2088  if ( *currentMapExtent() != beforeExtent )
2089  {
2090  emit extentChanged();
2091  }
2092  }
2093 
2094  //force redraw
2095  mCacheUpdated = false;
2096 
2098 }
2099 
2101 {
2103  o->setFrameSymbol( symbol );
2104 }
2105 
2107 {
2109  return o->frameSymbol();
2110 }
2111 
2112 QPainter::CompositionMode QgsComposerMap::overviewBlendMode() const
2113 {
2114  const QgsComposerMapOverview* o = constFirstMapOverview();
2115  return o->blendMode();
2116 }
2117 
2118 void QgsComposerMap::setOverviewBlendMode( QPainter::CompositionMode blendMode )
2119 {
2121  o->setBlendMode( blendMode );
2122 }
2123 
2125 {
2126  const QgsComposerMapOverview* o = constFirstMapOverview();
2127  return o->inverted();
2128 }
2129 
2131 {
2133  o->setInverted( inverted );
2134 }
2135 
2137 {
2138  const QgsComposerMapOverview* o = constFirstMapOverview();
2139  return o->centered();
2140 }
2141 
2143 {
2145  o->setCentered( centered );
2146  //overviewExtentChanged();
2147 }
2148 
2150 {
2151  QgsComposerMapGrid* g = grid();
2152  g->setLineSymbol( symbol );
2153 }
2154 
2156 {
2157  QgsComposerMapGrid* g = grid();
2158  return g->lineSymbol();
2159 }
2160 
2162 {
2163  QgsComposerMapGrid* g = grid();
2164  g->setEnabled( enabled );
2165 }
2166 
2168 {
2169  const QgsComposerMapGrid* g = constFirstMapGrid();
2170  return g->enabled();
2171 }
2172 
2173 void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
2174 {
2175  double mmToMapUnits = 1.0 / mapUnitsToMM();
2176  double dxScaled = xShift * mmToMapUnits;
2177  double dyScaled = - yShift * mmToMapUnits;
2178 
2179  QgsComposerUtils::rotate( mEvaluatedMapRotation, dxScaled, dyScaled );
2180 
2181  xShift = dxScaled;
2182  yShift = dyScaled;
2183 }
2184 
2185 QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
2186 {
2187  QPolygonF mapPoly = transformedMapPolygon();
2188  if ( mapPoly.size() < 1 )
2189  {
2190  return QPointF( 0, 0 );
2191  }
2192 
2193  QgsRectangle tExtent = transformedExtent();
2194  QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
2195  double dx = mapCoords.x() - rotationPoint.x();
2196  double dy = mapCoords.y() - rotationPoint.y();
2197  QgsComposerUtils::rotate( -mEvaluatedMapRotation, dx, dy );
2198  QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
2199 
2200  QgsRectangle unrotatedExtent = transformedExtent();
2201  double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
2202  double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
2203  return QPointF( xItem, yItem );
2204 }
2205 
2207 {
2208 
2209 }
2210 
2211 void QgsComposerMap::drawCanvasItems( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
2212 {
2213  if ( !mMapCanvas || !mDrawCanvasItems )
2214  {
2215  return;
2216  }
2217 
2218  QList<QGraphicsItem*> itemList = mMapCanvas->items();
2219  if ( itemList.size() < 1 )
2220  {
2221  return;
2222  }
2223  QGraphicsItem* currentItem = 0;
2224 
2225  for ( int i = itemList.size() - 1; i >= 0; --i )
2226  {
2227  currentItem = itemList.at( i );
2228  //don't draw mapcanvasmap (has z value -10)
2229  if ( !currentItem || currentItem->data( 0 ).toString() != "AnnotationItem" )
2230  {
2231  continue;
2232  }
2233  drawCanvasItem( currentItem, painter, itemStyle );
2234  }
2235 }
2236 
2237 void QgsComposerMap::drawCanvasItem( QGraphicsItem* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
2238 {
2239  if ( !item || !mMapCanvas || !item->isVisible() )
2240  {
2241  return;
2242  }
2243 
2244  painter->save();
2245  painter->setRenderHint( QPainter::Antialiasing );
2246 
2247  //determine scale factor according to graphics view dpi
2248  double scaleFactor = 1.0 / mMapCanvas->logicalDpiX() * 25.4;
2249 
2250  double itemX, itemY;
2251  QGraphicsItem* parent = item->parentItem();
2252  if ( !parent )
2253  {
2254  QPointF mapPos = composerMapPosForItem( item );
2255  itemX = mapPos.x();
2256  itemY = mapPos.y();
2257  }
2258  else //place item relative to the parent item
2259  {
2260  QPointF itemScenePos = item->scenePos();
2261  QPointF parentScenePos = parent->scenePos();
2262 
2263  QPointF mapPos = composerMapPosForItem( parent );
2264 
2265  itemX = mapPos.x() + ( itemScenePos.x() - parentScenePos.x() ) * scaleFactor;
2266  itemY = mapPos.y() + ( itemScenePos.y() - parentScenePos.y() ) * scaleFactor;
2267  }
2268  painter->translate( itemX, itemY );
2269 
2270  painter->scale( scaleFactor, scaleFactor );
2271 
2272  //a little trick to let the item know that the paint request comes from the composer
2273  item->setData( 1, "composer" );
2274  item->paint( painter, itemStyle, 0 );
2275  item->setData( 1, "" );
2276  painter->restore();
2277 }
2278 
2279 QPointF QgsComposerMap::composerMapPosForItem( const QGraphicsItem* item ) const
2280 {
2281  if ( !item || !mMapCanvas )
2282  {
2283  return QPointF( 0, 0 );
2284  }
2285 
2286  if ( currentMapExtent()->height() <= 0 || currentMapExtent()->width() <= 0 || mMapCanvas->width() <= 0 || mMapCanvas->height() <= 0 )
2287  {
2288  return QPointF( 0, 0 );
2289  }
2290 
2291  QRectF graphicsSceneRect = mMapCanvas->sceneRect();
2292  QPointF itemScenePos = item->scenePos();
2293  QgsRectangle mapRendererExtent = mComposition->mapSettings().visibleExtent();
2294 
2295  double mapX = itemScenePos.x() / graphicsSceneRect.width() * mapRendererExtent.width() + mapRendererExtent.xMinimum();
2296  double mapY = mapRendererExtent.yMaximum() - itemScenePos.y() / graphicsSceneRect.height() * mapRendererExtent.height();
2297  return mapToItemCoords( QPointF( mapX, mapY ) );
2298 }
2299 
2301 {
2302  if ( !mComposition )
2303  {
2304  return;
2305  }
2306 
2307  const QgsComposerMap* existingMap = mComposition->getComposerMapById( mId );
2308  if ( !existingMap )
2309  {
2310  return; //keep mId as it is still available
2311  }
2312 
2313  int maxId = -1;
2314  QList<const QgsComposerMap*> mapList = mComposition->composerMapItems();
2315  QList<const QgsComposerMap*>::const_iterator mapIt = mapList.constBegin();
2316  for ( ; mapIt != mapList.constEnd(); ++mapIt )
2317  {
2318  if (( *mapIt )->id() > maxId )
2319  {
2320  maxId = ( *mapIt )->id();
2321  }
2322  }
2323  mId = maxId + 1;
2324  updateToolTip();
2325 }
2326 
2327 bool QgsComposerMap::imageSizeConsideringRotation( double& width, double& height ) const
2328 {
2329  //kept for api compatibility with QGIS 2.0 - use mMapRotation
2331  return QgsComposerItem::imageSizeConsideringRotation( width, height, mEvaluatedMapRotation );
2333 }
2334 
2335 bool QgsComposerMap::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
2336 {
2337  //kept for api compatibility with QGIS 2.0 - use mMapRotation
2339  return QgsComposerItem::cornerPointOnRotatedAndScaledRect( x, y, width, height, mEvaluatedMapRotation );
2341 }
2342 
2343 void QgsComposerMap::sizeChangedByRotation( double& width, double& height )
2344 {
2345  //kept for api compatibility with QGIS 2.0 - use mMapRotation
2347  return QgsComposerItem::sizeChangedByRotation( width, height, mEvaluatedMapRotation );
2349 }
2350 
2352 {
2353  mAtlasDriven = enabled;
2354 
2355  if ( !enabled )
2356  {
2357  //if not enabling the atlas, we still need to refresh the map extents
2358  //so that data defined extents and scale are recalculated
2359  refreshMapExtents();
2360  }
2361 }
2362 
2364 {
2365  return mAtlasScalingMode == Fixed;
2366 }
2367 
2369 {
2370  // implicit : if set to false => auto scaling
2371  mAtlasScalingMode = fixed ? Fixed : Auto;
2372 }
2373 
2375 {
2376  if ( valueType == QgsComposerObject::EvaluatedValue )
2377  {
2378  //evaluate data defined atlas margin
2379 
2380  //start with user specified margin
2381  double margin = mAtlasMargin;
2382  QVariant exprVal;
2384  {
2385  bool ok;
2386  double ddMargin = exprVal.toDouble( &ok );
2387  QgsDebugMsg( QString( "exprVal Map Atlas Margin:%1" ).arg( ddMargin ) );
2388  if ( ok && !exprVal.isNull() )
2389  {
2390  //divide by 100 to convert to 0 -> 1.0 range
2391  margin = ddMargin / 100;
2392  }
2393  }
2394  return margin;
2395  }
2396  else
2397  {
2398  return mAtlasMargin;
2399  }
2400 }
2401