Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator 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 
20 #include "qgscoordinatetransform.h"
21 #include "qgslogger.h"
22 #include "qgsmaprenderer.h"
23 #include "qgsmaplayer.h"
24 #include "qgsmaplayerregistry.h"
25 #include "qgsmaptopixel.h"
26 #include "qgsproject.h"
27 #include "qgsrasterlayer.h"
28 #include "qgsrendercontext.h"
29 #include "qgsscalecalculator.h"
30 #include "qgsvectorlayer.h"
31 
32 #include "qgslabel.h"
33 #include "qgslabelattributes.h"
34 
35 #include <QGraphicsScene>
36 #include <QGraphicsView>
37 #include <QPainter>
38 #include <QSettings>
39 #include <iostream>
40 #include <cmath>
41 
42 QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
43  : QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ), mGridEnabled( false ), mGridStyle( Solid ), \
44  mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), \
45  mGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mGridAnnotationDirection( Horizontal ),
46  mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true )
47 {
49 
50  //mId = mComposition->composerMapItems().size();
51  int maxId = -1;
52  QList<const QgsComposerMap*> mapList = mComposition->composerMapItems();
53  QList<const QgsComposerMap*>::const_iterator mapIt = mapList.constBegin();
54  for ( ; mapIt != mapList.constEnd(); ++mapIt )
55  {
56  if (( *mapIt )->id() > maxId )
57  {
58  maxId = ( *mapIt )->id();
59  }
60  }
61  mId = maxId + 1;
62 
65  mCurrentRectangle = rect();
66 
67  // Cache
68  mCacheUpdated = false;
69  mDrawing = false;
70 
71  //Offset
72  mXOffset = 0.0;
73  mYOffset = 0.0;
74 
76 
77  //calculate mExtent based on width/height ratio and map canvas extent
78  if ( mMapRenderer )
79  {
81  }
82  setSceneRect( QRectF( x, y, width, height ) );
83  setToolTip( tr( "Map %1" ).arg( mId ) );
84  mGridPen.setCapStyle( Qt::FlatCap );
85 }
86 
88  : QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mGridEnabled( false ), mGridStyle( Solid ), \
89  mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), \
90  mGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mGridAnnotationDirection( Horizontal ), mCrossLength( 3 ),
91  mMapCanvas( 0 ), mDrawCanvasItems( true )
92 {
93  //Offset
94  mXOffset = 0.0;
95  mYOffset = 0.0;
96 
98 
101  mId = mComposition->composerMapItems().size();
103  mCurrentRectangle = rect();
104 
105  setToolTip( tr( "Map %1" ).arg( mId ) );
106  mGridPen.setCapStyle( Qt::FlatCap );
107 }
108 
110 {
111 }
112 
113 void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSize& size, int dpi )
114 {
115  draw( painter, extent, QSizeF( size.width(), size.height() ), dpi );
116 }
117 
118 /* This function is called by paint() and cache() to render the map. It does not override any functions
119 from QGraphicsItem. */
120 void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSizeF& size, double dpi )
121 {
122  if ( !painter )
123  {
124  return;
125  }
126 
127  if ( !mMapRenderer )
128  {
129  return;
130  }
131 
132  QgsMapRenderer theMapRenderer;
133  theMapRenderer.setExtent( extent );
134  theMapRenderer.setOutputSize( size, dpi );
135  if ( mMapRenderer->labelingEngine() )
136  theMapRenderer.setLabelingEngine( mMapRenderer->labelingEngine()->clone() );
137 
138  //use stored layer set or read current set from main canvas
139  if ( mKeepLayerSet )
140  {
141  theMapRenderer.setLayerSet( mLayerSet );
142  }
143  else
144  {
145  theMapRenderer.setLayerSet( mMapRenderer->layerSet() );
146  }
148  theMapRenderer.setDestinationCrs( mMapRenderer->destinationCrs() );
149 
150  //set antialiasing if enabled in options
151  QSettings settings;
152  // Changed to enable anti aliased rendering by default as of QGIS 1.7
153  if ( settings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
154  {
155  painter->setRenderHint( QPainter::Antialiasing );
156  }
157 
158  QgsRenderContext* theRendererContext = theMapRenderer.rendererContext();
159  if ( theRendererContext )
160  {
161  theRendererContext->setDrawEditingInformation( false );
162  theRendererContext->setRenderingStopped( false );
163  }
164 
165  // force vector output (no caching of marker images etc.)
166  theRendererContext->setForceVectorOutput( true );
167 
168  //force composer map scale for scale dependent visibility
169  double bk_scale = theMapRenderer.scale();
170  theMapRenderer.setScale( scale() );
171 
172  //layer caching (as QImages) cannot be done for composer prints
173  QSettings s;
174  bool bkLayerCaching = s.value( "/qgis/enable_render_caching", false ).toBool();
175  s.setValue( "/qgis/enable_render_caching", false );
176 
177  theMapRenderer.render( painter );
178  s.setValue( "/qgis/enable_render_caching", bkLayerCaching );
179 
180  theMapRenderer.setScale( bk_scale );
181 }
182 
184 {
185  if ( mPreviewMode == Rectangle )
186  {
187  return;
188  }
189 
190  if ( mDrawing )
191  {
192  return;
193  }
194 
195  mDrawing = true;
196 
197  //in case of rotation, we need to request a larger rectangle and create a larger cache image
198  QgsRectangle requestExtent;
199  requestedExtent( requestExtent );
200 
201  double horizontalVScaleFactor = horizontalViewScaleFactor();
202  if ( horizontalVScaleFactor < 0 )
203  {
204  horizontalVScaleFactor = mLastValidViewScaleFactor;
205  }
206 
207  int w = requestExtent.width() * mapUnitsToMM() * horizontalVScaleFactor;
208  int h = requestExtent.height() * mapUnitsToMM() * horizontalVScaleFactor;
209 
210  if ( w > 5000 ) //limit size of image for better performance
211  {
212  w = 5000;
213  }
214 
215  if ( h > 5000 )
216  {
217  h = 5000;
218  }
219 
220  mCacheImage = QImage( w, h, QImage::Format_ARGB32 );
221  mCacheImage.fill( brush().color().rgb() ); //consider the item background brush
222  double mapUnitsPerPixel = mExtent.width() / w;
223 
224  // WARNING: ymax in QgsMapToPixel is device height!!!
225  QgsMapToPixel transform( mapUnitsPerPixel, h, requestExtent.yMinimum(), requestExtent.xMinimum() );
226 
227  QPainter p( &mCacheImage );
228 
229  draw( &p, requestExtent, QSizeF( w, h ), mCacheImage.logicalDpiX() );
230  p.end();
231  mCacheUpdated = true;
232 
233  mDrawing = false;
234 }
235 
236 void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
237 {
238  if ( !mComposition || !painter )
239  {
240  return;
241  }
242 
243  QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
244  painter->save();
245  painter->setClipRect( thisPaintRect );
246 
247  drawBackground( painter );
248 
250  {
251  QFont messageFont( "", 12 );
252  painter->setFont( messageFont );
253  painter->setPen( QColor( 0, 0, 0 ) );
254  painter->drawText( thisPaintRect, tr( "Map will be printed here" ) );
255  }
257  {
258  //draw cached pixmap. This function does not call cache() any more because
259  //Qt 4.4.0 and 4.4.1 have problems with recursive paintings
260  //QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
261  //client functions
262 
263  QgsRectangle requestRectangle;
264  requestedExtent( requestRectangle );
265  double horizontalVScaleFactor = horizontalViewScaleFactor();
266  if ( horizontalVScaleFactor < 0 )
267  {
268  horizontalVScaleFactor = mLastValidViewScaleFactor;
269  }
270 
271  double imagePixelWidth = mExtent.width() / requestRectangle.width() * mCacheImage.width() ; //how many pixels of the image are for the map extent?
272  double scale = rect().width() / imagePixelWidth;
273  QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
274 
275  //shift such that rotation point is at 0/0 point in the coordinate system
276  double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
277  double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
278 
279  //shift such that top left point of the extent at point 0/0 in item coordinate system
280  double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
281  double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
282 
283  painter->save();
284 
285  painter->translate( mXOffset, mYOffset );
286  painter->translate( xTopLeftShift, yTopLeftShift );
287  painter->rotate( mRotation );
288  painter->translate( xShiftMM, -yShiftMM );
289  painter->scale( scale, scale );
290  painter->drawImage( 0, 0, mCacheImage );
291 
292  //restore rotation
293  painter->restore();
294 
295  //draw canvas items
296  drawCanvasItems( painter, itemStyle );
297  }
298  else if ( mComposition->plotStyle() == QgsComposition::Print ||
300  {
301  if ( mDrawing )
302  {
303  return;
304  }
305 
306  mDrawing = true;
307  QPaintDevice* thePaintDevice = painter->device();
308  if ( !thePaintDevice )
309  {
310  return;
311  }
312 
313  QgsRectangle requestRectangle;
314  requestedExtent( requestRectangle );
315 
316  QSizeF theSize( requestRectangle.width() * mapUnitsToMM(), requestRectangle.height() * mapUnitsToMM() );
317  QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
318 
319  //shift such that rotation point is at 0/0 point in the coordinate system
320  double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
321  double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
322 
323  //shift such that top left point of the extent at point 0/0 in item coordinate system
324  double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
325  double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
326  painter->save();
327  painter->translate( mXOffset, mYOffset );
328  painter->translate( xTopLeftShift, yTopLeftShift );
329  painter->rotate( mRotation );
330  painter->translate( xShiftMM, -yShiftMM );
331  draw( painter, requestRectangle, theSize, 25.4 ); //scene coordinates seem to be in mm
332 
333  //restore rotation
334  painter->restore();
335 
336  //draw canvas items
337  drawCanvasItems( painter, itemStyle );
338 
339  mDrawing = false;
340  }
341 
342  painter->setClipRect( thisPaintRect , Qt::NoClip );
343 
344  if ( mGridEnabled )
345  {
346  drawGrid( painter );
347  }
348  drawFrame( painter );
349  if ( isSelected() )
350  {
351  drawSelectionBoxes( painter );
352  }
353 
354 
355  painter->restore();
356 }
357 
359 {
360  //If we have a locked layer set then don't reload the map canvas.
361  if ( mKeepLayerSet )
362  {
363  return;
364  }
365  syncLayerSet(); //layer list may have changed
366  mCacheUpdated = false;
367  cache();
368  QGraphicsRectItem::update();
369 }
370 
372 {
373  if ( mPreviewMode == Render )
374  {
376  }
377 }
378 
380 {
381  mCacheUpdated = u;
382 }
383 
384 double QgsComposerMap::scale() const
385 {
386  QgsScaleCalculator calculator;
387  calculator.setMapUnits( mMapRenderer->mapUnits() );
388  calculator.setDpi( 25.4 ); //QGraphicsView units are mm
389  return calculator.calculate( mExtent, rect().width() );
390 }
391 
392 void QgsComposerMap::resize( double dx, double dy )
393 {
394  //setRect
395  QRectF currentRect = rect();
396  QRectF newSceneRect = QRectF( transform().dx(), transform().dy(), currentRect.width() + dx, currentRect.height() + dy );
397  setSceneRect( newSceneRect );
398 }
399 
400 void QgsComposerMap::moveContent( double dx, double dy )
401 {
402  if ( !mDrawing )
403  {
404  transformShift( dx, dy );
409  cache();
410  update();
411  emit itemChanged();
412  emit extentChanged();
413  }
414 }
415 
416 void QgsComposerMap::zoomContent( int delta, double x, double y )
417 {
418  if ( mDrawing )
419  {
420  return;
421  }
422 
423  QSettings settings;
424 
425  //read zoom mode
426  //0: zoom, 1: zoom and recenter, 2: zoom to cursor, 3: nothing
427  int zoomMode = settings.value( "/qgis/wheel_action", 2 ).toInt();
428  if ( zoomMode == 3 ) //do nothing
429  {
430  return;
431  }
432 
433  double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
434 
435  //find out new center point
436  double centerX = ( mExtent.xMaximum() + mExtent.xMinimum() ) / 2;
437  double centerY = ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2;
438 
439  if ( zoomMode != 0 )
440  {
441  //find out map coordinates of mouse position
442  double mapMouseX = mExtent.xMinimum() + ( x / rect().width() ) * ( mExtent.xMaximum() - mExtent.xMinimum() );
443  double mapMouseY = mExtent.yMinimum() + ( 1 - ( y / rect().height() ) ) * ( mExtent.yMaximum() - mExtent.yMinimum() );
444  if ( zoomMode == 1 ) //zoom and recenter
445  {
446  centerX = mapMouseX;
447  centerY = mapMouseY;
448  }
449  else if ( zoomMode == 2 ) //zoom to cursor
450  {
451  centerX = mapMouseX + ( centerX - mapMouseX ) * ( 1.0 / zoomFactor );
452  centerY = mapMouseY + ( centerY - mapMouseY ) * ( 1.0 / zoomFactor );
453  }
454  }
455 
456  double newIntervalX, newIntervalY;
457 
458  if ( delta > 0 )
459  {
460  newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) / zoomFactor;
461  newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) / zoomFactor;
462  }
463  else if ( delta < 0 )
464  {
465  newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) * zoomFactor;
466  newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) * zoomFactor;
467  }
468  else //no need to zoom
469  {
470  return;
471  }
472 
473  mExtent.setXMaximum( centerX + newIntervalX / 2 );
474  mExtent.setXMinimum( centerX - newIntervalX / 2 );
475  mExtent.setYMaximum( centerY + newIntervalY / 2 );
476  mExtent.setYMinimum( centerY - newIntervalY / 2 );
477 
478  cache();
479  update();
480  emit itemChanged();
481  emit extentChanged();
482 }
483 
484 void QgsComposerMap::setSceneRect( const QRectF& rectangle )
485 {
486  double w = rectangle.width();
487  double h = rectangle.height();
488  //prepareGeometryChange();
489 
490  QgsComposerItem::setSceneRect( rectangle );
491 
492  //QGraphicsRectItem::update();
493  double newHeight = mExtent.width() * h / w ;
495  mCacheUpdated = false;
496 
497  if ( mPreviewMode != Rectangle )
498  {
499  cache();
500  }
502  update();
503  emit itemChanged();
504  emit extentChanged();
505 }
506 
508 {
509  if ( mExtent == extent )
510  {
511  return;
512  }
513  mExtent = extent;
514 
515  //adjust height
516  QRectF currentRect = rect();
517 
518  double newHeight = currentRect.width() * extent.height() / extent.width();
519 
520  setSceneRect( QRectF( transform().dx(), transform().dy(), currentRect.width(), newHeight ) );
521 }
522 
523 void QgsComposerMap::setNewScale( double scaleDenominator )
524 {
525  double currentScaleDenominator = scale();
526 
527  if ( scaleDenominator == currentScaleDenominator )
528  {
529  return;
530  }
531 
532  double scaleRatio = scaleDenominator / currentScaleDenominator;
533  mExtent.scale( scaleRatio );
534  mCacheUpdated = false;
535  cache();
536  update();
537  emit itemChanged();
538  emit extentChanged();
539 }
540 
541 void QgsComposerMap::setOffset( double xOffset, double yOffset )
542 {
543  mXOffset = xOffset;
544  mYOffset = yOffset;
545 }
546 
548 {
549  setRotation( r );
550  emit rotationChanged( r );
551 }
552 
554 {
555  if ( !mMapRenderer )
556  {
557  return false;
558  }
559 
560  QStringList layers = mMapRenderer->layerSet();
561 
562  QStringList::const_iterator layer_it = layers.constBegin();
563  QgsMapLayer* currentLayer = 0;
564 
565  for ( ; layer_it != layers.constEnd(); ++layer_it )
566  {
567  currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
568  if ( currentLayer )
569  {
570  QgsRasterLayer* currentRasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
571  if ( currentRasterLayer )
572  {
573  const QgsRasterDataProvider* rasterProvider = 0;
574  if (( rasterProvider = currentRasterLayer->dataProvider() ) )
575  {
576  if ( rasterProvider->name() == "wms" )
577  {
578  return true;
579  }
580  }
581  }
582  }
583  }
584  return false;
585 }
586 
588 {
589  //connect signal from layer registry to update in case of new or deleted layers
591  if ( layerRegistry )
592  {
593  connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( updateCachedImage() ) );
594  connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( updateCachedImage() ) );
595  }
596 }
597 
598 bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
599 {
600  if ( elem.isNull() )
601  {
602  return false;
603  }
604 
605  QDomElement composerMapElem = doc.createElement( "ComposerMap" );
606  composerMapElem.setAttribute( "id", mId );
607 
608  //previewMode
609  if ( mPreviewMode == Cache )
610  {
611  composerMapElem.setAttribute( "previewMode", "Cache" );
612  }
613  else if ( mPreviewMode == Render )
614  {
615  composerMapElem.setAttribute( "previewMode", "Render" );
616  }
617  else //rectangle
618  {
619  composerMapElem.setAttribute( "previewMode", "Rectangle" );
620  }
621 
622  if ( mKeepLayerSet )
623  {
624  composerMapElem.setAttribute( "keepLayerSet", "true" );
625  }
626  else
627  {
628  composerMapElem.setAttribute( "keepLayerSet", "false" );
629  }
630 
631  if ( mDrawCanvasItems )
632  {
633  composerMapElem.setAttribute( "drawCanvasItems", "true" );
634  }
635  else
636  {
637  composerMapElem.setAttribute( "drawCanvasItems", "false" );
638  }
639 
640  //extent
641  QDomElement extentElem = doc.createElement( "Extent" );
642  extentElem.setAttribute( "xmin", mExtent.xMinimum() );
643  extentElem.setAttribute( "xmax", mExtent.xMaximum() );
644  extentElem.setAttribute( "ymin", mExtent.yMinimum() );
645  extentElem.setAttribute( "ymax", mExtent.yMaximum() );
646  composerMapElem.appendChild( extentElem );
647 
648  //layer set
649  QDomElement layerSetElem = doc.createElement( "LayerSet" );
650  QStringList::const_iterator layerIt = mLayerSet.constBegin();
651  for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
652  {
653  QDomElement layerElem = doc.createElement( "Layer" );
654  QDomText layerIdText = doc.createTextNode( *layerIt );
655  layerElem.appendChild( layerIdText );
656  layerSetElem.appendChild( layerElem );
657  }
658  composerMapElem.appendChild( layerSetElem );
659 
660  //grid
661  QDomElement gridElem = doc.createElement( "Grid" );
662  gridElem.setAttribute( "show", mGridEnabled );
663  gridElem.setAttribute( "gridStyle", mGridStyle );
664  gridElem.setAttribute( "intervalX", mGridIntervalX );
665  gridElem.setAttribute( "intervalY", mGridIntervalY );
666  gridElem.setAttribute( "offsetX", mGridOffsetX );
667  gridElem.setAttribute( "offsetY", mGridOffsetY );
668  gridElem.setAttribute( "penWidth", mGridPen.widthF() );
669  gridElem.setAttribute( "penColorRed", mGridPen.color().red() );
670  gridElem.setAttribute( "penColorGreen", mGridPen.color().green() );
671  gridElem.setAttribute( "penColorBlue", mGridPen.color().blue() );
672  gridElem.setAttribute( "crossLength", mCrossLength );
673 
674  //grid annotation
675  QDomElement annotationElem = doc.createElement( "Annotation" );
676  annotationElem.setAttribute( "show", mShowGridAnnotation );
677  annotationElem.setAttribute( "position", mGridAnnotationPosition );
678  annotationElem.setAttribute( "frameDistance", mAnnotationFrameDistance );
679  annotationElem.setAttribute( "direction", mGridAnnotationDirection );
680  annotationElem.setAttribute( "font", mGridAnnotationFont.toString() );
681  annotationElem.setAttribute( "precision", mGridAnnotationPrecision );
682 
683  gridElem.appendChild( annotationElem );
684  composerMapElem.appendChild( gridElem );
685 
686  elem.appendChild( composerMapElem );
687  return _writeXML( composerMapElem, doc );
688 }
689 
690 bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& doc )
691 {
692  if ( itemElem.isNull() )
693  {
694  return false;
695  }
696 
697  QString idRead = itemElem.attribute( "id", "not found" );
698  if ( idRead != "not found" )
699  {
700  mId = idRead.toInt();
701  }
703 
704  //previewMode
705  QString previewMode = itemElem.attribute( "previewMode" );
706  if ( previewMode == "Cache" )
707  {
709  }
710  else if ( previewMode == "Render" )
711  {
713  }
714  else
715  {
717  }
718 
719  //extent
720  QDomNodeList extentNodeList = itemElem.elementsByTagName( "Extent" );
721  if ( extentNodeList.size() > 0 )
722  {
723  QDomElement extentElem = extentNodeList.at( 0 ).toElement();
724  double xmin, xmax, ymin, ymax;
725  xmin = extentElem.attribute( "xmin" ).toDouble();
726  xmax = extentElem.attribute( "xmax" ).toDouble();
727  ymin = extentElem.attribute( "ymin" ).toDouble();
728  ymax = extentElem.attribute( "ymax" ).toDouble();
729 
730  mExtent = QgsRectangle( xmin, ymin, xmax, ymax );
731  }
732 
733  //mKeepLayerSet flag
734  QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
735  if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
736  {
737  mKeepLayerSet = true;
738  }
739  else
740  {
741  mKeepLayerSet = false;
742  }
743 
744  QString drawCanvasItemsFlag = itemElem.attribute( "drawCanvasItems" );
745  if ( drawCanvasItemsFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
746  {
747  mDrawCanvasItems = true;
748  }
749  else
750  {
751  mDrawCanvasItems = false;
752  }
753 
754  //mLayerSet
755  QDomNodeList layerSetNodeList = itemElem.elementsByTagName( "LayerSet" );
756  QStringList layerSet;
757  if ( layerSetNodeList.size() > 0 )
758  {
759  QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
760  QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( "Layer" );
761  for ( int i = 0; i < layerIdNodeList.size(); ++i )
762  {
763  layerSet << layerIdNodeList.at( i ).toElement().text();
764  }
765  }
767 
768  mDrawing = false;
769  mNumCachedLayers = 0;
770  mCacheUpdated = false;
771 
772  //grid
773  QDomNodeList gridNodeList = itemElem.elementsByTagName( "Grid" );
774  if ( gridNodeList.size() > 0 )
775  {
776  QDomElement gridElem = gridNodeList.at( 0 ).toElement();
777  mGridEnabled = ( gridElem.attribute( "show", "0" ) != "0" );
778  mGridStyle = QgsComposerMap::GridStyle( gridElem.attribute( "gridStyle", "0" ).toInt() );
779  mGridIntervalX = gridElem.attribute( "intervalX", "0" ).toDouble();
780  mGridIntervalY = gridElem.attribute( "intervalY", "0" ).toDouble();
781  mGridOffsetX = gridElem.attribute( "offsetX", "0" ).toDouble();
782  mGridOffsetY = gridElem.attribute( "offsetY", "0" ).toDouble();
783  mGridPen.setWidthF( gridElem.attribute( "penWidth", "0" ).toDouble() );
784  mGridPen.setColor( QColor( gridElem.attribute( "penColorRed", "0" ).toInt(), \
785  gridElem.attribute( "penColorGreen", "0" ).toInt(), \
786  gridElem.attribute( "penColorBlue", "0" ).toInt() ) );
787  mCrossLength = gridElem.attribute( "crossLength", "3" ).toDouble();
788 
789  QDomNodeList annotationNodeList = gridElem.elementsByTagName( "Annotation" );
790  if ( annotationNodeList.size() > 0 )
791  {
792  QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
793  mShowGridAnnotation = ( annotationElem.attribute( "show", "0" ) != "0" );
794  mGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "position", "0" ).toInt() );
795  mAnnotationFrameDistance = annotationElem.attribute( "frameDistance", "0" ).toDouble();
796  mGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "direction", "0" ).toInt() );
797  mGridAnnotationFont.fromString( annotationElem.attribute( "font", "" ) );
798  mGridAnnotationPrecision = annotationElem.attribute( "precision", "3" ).toInt();
799  }
800  }
801 
802  //restore general composer item properties
803  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
804  if ( composerItemList.size() > 0 )
805  {
806  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
807  _readXML( composerItemElem, doc );
808  }
809 
811  emit itemChanged();
812  return true;
813 }
814 
816 {
817  if ( mMapRenderer )
818  {
820  }
821 }
822 
824 {
825  if ( mLayerSet.size() < 1 && !mMapRenderer )
826  {
827  return;
828  }
829 
830  //if layer set is fixed, do a lookup in the layer registry to also find the non-visible layers
831  QStringList currentLayerSet;
832  if ( mKeepLayerSet )
833  {
834  currentLayerSet = QgsMapLayerRegistry::instance()->mapLayers().uniqueKeys();
835  }
836  else //only consider layers visible in the map
837  {
838  currentLayerSet = mMapRenderer->layerSet();
839  }
840 
841  for ( int i = mLayerSet.size() - 1; i >= 0; --i )
842  {
843  if ( !currentLayerSet.contains( mLayerSet.at( i ) ) )
844  {
845  mLayerSet.removeAt( i );
846  }
847  }
848 }
849 
850 void QgsComposerMap::drawGrid( QPainter* p )
851 {
852  p->setPen( mGridPen );
853 
854  QList< QPair< double, QLineF > > verticalLines;
855  yGridLines( verticalLines );
856  QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
857  QList< QPair< double, QLineF > > horizontalLines;
858  xGridLines( horizontalLines );
859  QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
860 
861  QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
862  p->setClipRect( thisPaintRect );
863 
864  //simpler approach: draw vertical lines first, then horizontal ones
866  {
867  for ( ; vIt != verticalLines.constEnd(); ++vIt )
868  {
869  p->drawLine( vIt->second );
870  }
871 
872  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
873  {
874  p->drawLine( hIt->second );
875  }
876  }
877  else //cross
878  {
879  QPointF intersectionPoint, crossEnd1, crossEnd2;
880  for ( ; vIt != verticalLines.constEnd(); ++vIt )
881  {
882  //start mark
883  crossEnd1 = pointOnLineWithDistance( vIt->second.p1(), vIt->second.p2(), mCrossLength );
884  p->drawLine( vIt->second.p1(), crossEnd1 );
885 
886  //test for intersection with every horizontal line
887  hIt = horizontalLines.constBegin();
888  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
889  {
890  if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
891  {
892  crossEnd1 = pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength );
893  crossEnd2 = pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength );
894  p->drawLine( crossEnd1, crossEnd2 );
895  }
896  }
897  //end mark
898  QPointF crossEnd2 = pointOnLineWithDistance( vIt->second.p2(), vIt->second.p1(), mCrossLength );
899  p->drawLine( vIt->second.p2(), crossEnd2 );
900  }
901 
902  hIt = horizontalLines.constBegin();
903  for ( ; hIt != horizontalLines.constEnd(); ++hIt )
904  {
905  //start mark
906  crossEnd1 = pointOnLineWithDistance( hIt->second.p1(), hIt->second.p2(), mCrossLength );
907  p->drawLine( hIt->second.p1(), crossEnd1 );
908 
909  vIt = verticalLines.constBegin();
910  for ( ; vIt != verticalLines.constEnd(); ++vIt )
911  {
912  if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
913  {
914  crossEnd1 = pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength );
915  crossEnd2 = pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength );
916  p->drawLine( crossEnd1, crossEnd2 );
917  }
918  }
919  //end mark
920  crossEnd1 = pointOnLineWithDistance( hIt->second.p2(), hIt->second.p1(), mCrossLength );
921  p->drawLine( hIt->second.p2(), crossEnd1 );
922  }
923 
924 
925  }
926 
927  p->setClipRect( thisPaintRect , Qt::NoClip );
928 
929  if ( mShowGridAnnotation )
930  {
931  drawCoordinateAnnotations( p, horizontalLines, verticalLines );
932  }
933 }
934 
935 void QgsComposerMap::drawCoordinateAnnotations( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines )
936 {
937  if ( !p )
938  {
939  return;
940  }
941 
942 
943  QString currentAnnotationString;
944  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
945  for ( ; it != hLines.constEnd(); ++it )
946  {
947  currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
948  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
949  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
950  }
951 
952  it = vLines.constBegin();
953  for ( ; it != vLines.constEnd(); ++it )
954  {
955  currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
956  drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
957  drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
958  }
959 }
960 
961 void QgsComposerMap::drawCoordinateAnnotation( QPainter* p, const QPointF& pos, QString annotationString )
962 {
963  Border frameBorder = borderForLineCoord( pos );
964  double textWidth = textWidthMillimeters( mGridAnnotationFont, annotationString );
965  //relevant for annotations is the height of digits
966  double textHeight = fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
967  double xpos = pos.x();
968  double ypos = pos.y();
969  int rotation = 0;
970 
971  if ( frameBorder == Left )
972  {
973 
975  {
977  {
978  xpos += textHeight + mAnnotationFrameDistance;
979  ypos += textWidth / 2.0;
980  rotation = 270;
981  }
982  else
983  {
984  xpos += mAnnotationFrameDistance;
985  ypos += textHeight / 2.0;
986  }
987  }
988  else //Outside map frame
989  {
991  {
992  xpos -= mAnnotationFrameDistance;
993  ypos += textWidth / 2.0;
994  rotation = 270;
995  }
996  else
997  {
998  xpos -= textWidth + mAnnotationFrameDistance;
999  ypos += textHeight / 2.0;
1000  }
1001  }
1002 
1003  }
1004  else if ( frameBorder == Right )
1005  {
1007  {
1009  {
1010  xpos -= mAnnotationFrameDistance;
1011  ypos += textWidth / 2.0;
1012  rotation = 270;
1013  }
1014  else //Horizontal
1015  {
1016  xpos -= textWidth + mAnnotationFrameDistance;
1017  ypos += textHeight / 2.0;
1018  }
1019  }
1020  else //OutsideMapFrame
1021  {
1023  {
1024  xpos += textHeight + mAnnotationFrameDistance;
1025  ypos += textWidth / 2.0;
1026  rotation = 270;
1027  }
1028  else //Horizontal
1029  {
1030  xpos += mAnnotationFrameDistance;
1031  ypos += textHeight / 2.0;
1032  }
1033  }
1034  }
1035  else if ( frameBorder == Bottom )
1036  {
1038  {
1040  {
1041  ypos -= mAnnotationFrameDistance;
1042  xpos -= textWidth / 2.0;
1043  }
1044  else //Vertical
1045  {
1046  xpos += textHeight / 2.0;
1047  ypos -= mAnnotationFrameDistance;
1048  rotation = 270;
1049  }
1050  }
1051  else //OutsideMapFrame
1052  {
1054  {
1055  ypos += mAnnotationFrameDistance + textHeight;
1056  xpos -= textWidth / 2.0;
1057  }
1058  else //Vertical
1059  {
1060  xpos += textHeight / 2.0;
1061  ypos += textWidth + mAnnotationFrameDistance;
1062  rotation = 270;
1063  }
1064  }
1065  }
1066  else //Top
1067  {
1069  {
1071  {
1072  xpos -= textWidth / 2.0;
1073  ypos += textHeight + mAnnotationFrameDistance;
1074  }
1075  else //Vertical
1076  {
1077  xpos += textHeight / 2.0;
1078  ypos += textWidth + mAnnotationFrameDistance;
1079  rotation = 270;
1080  }
1081  }
1082  else //OutsideMapFrame
1083  {
1085  {
1086  xpos -= textWidth / 2.0;
1087  ypos -= mAnnotationFrameDistance;
1088  }
1089  else //Vertical
1090  {
1091  xpos += textHeight / 2.0;
1092  ypos -= mAnnotationFrameDistance;
1093  rotation = 270;
1094  }
1095  }
1096  }
1097 
1098  drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
1099 }
1100 
1101 void QgsComposerMap::drawAnnotation( QPainter* p, const QPointF& pos, int rotation, const QString& annotationText )
1102 {
1103  p->save();
1104  p->translate( pos );
1105  p->rotate( rotation );
1106  p->setPen( QColor( 0, 0, 0 ) );
1107  drawText( p, 0, 0, annotationText, mGridAnnotationFont );
1108  p->restore();
1109 }
1110 
1111 int QgsComposerMap::xGridLines( QList< QPair< double, QLineF > >& lines ) const
1112 {
1113  lines.clear();
1114  if ( mGridIntervalY <= 0.0 )
1115  {
1116  return 1;
1117  }
1118 
1119 
1120  QPolygonF mapPolygon = transformedMapPolygon();
1121  QRectF mapBoundingRect = mapPolygon.boundingRect();
1122 
1123  //consider to round up to the next step in case the left boundary is > 0
1124  double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
1125  double currentLevel = ( int )(( mapBoundingRect.top() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY;
1126 
1127  if ( doubleNear( mRotation, 0.0 ) )
1128  {
1129  //no rotation. Do it 'the easy way'
1130 
1131  double yCanvasCoord;
1132 
1133  while ( currentLevel <= mapBoundingRect.bottom() )
1134  {
1135  yCanvasCoord = rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
1136  lines.push_back( qMakePair( currentLevel, QLineF( 0, yCanvasCoord, rect().width(), yCanvasCoord ) ) );
1137  currentLevel += mGridIntervalY;
1138  }
1139  }
1140 
1141  //the four border lines
1142  QVector<QLineF> borderLines;
1143  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1144  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1145  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1146  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1147 
1148  QList<QPointF> intersectionList; //intersects between border lines and grid lines
1149 
1150  while ( currentLevel <= mapBoundingRect.bottom() )
1151  {
1152  intersectionList.clear();
1153  QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
1154 
1155  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1156  for ( ; it != borderLines.constEnd(); ++it )
1157  {
1158  QPointF intersectionPoint;
1159  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1160  {
1161  intersectionList.push_back( intersectionPoint );
1162  if ( intersectionList.size() >= 2 )
1163  {
1164  break; //we already have two intersections, skip further tests
1165  }
1166  }
1167  }
1168 
1169  if ( intersectionList.size() >= 2 )
1170  {
1171  lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1172  }
1173  currentLevel += mGridIntervalY;
1174  }
1175 
1176 
1177  return 0;
1178 }
1179 
1180 int QgsComposerMap::yGridLines( QList< QPair< double, QLineF > >& lines ) const
1181 {
1182  lines.clear();
1183  if ( mGridIntervalX <= 0.0 )
1184  {
1185  return 1;
1186  }
1187 
1188  QPolygonF mapPolygon = transformedMapPolygon();
1189  QRectF mapBoundingRect = mapPolygon.boundingRect();
1190 
1191  //consider to round up to the next step in case the left boundary is > 0
1192  double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
1193  double currentLevel = ( int )(( mapBoundingRect.left() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
1194 
1195  if ( doubleNear( mRotation, 0.0 ) )
1196  {
1197  //no rotation. Do it 'the easy way'
1198  double xCanvasCoord;
1199 
1200  while ( currentLevel <= mapBoundingRect.right() )
1201  {
1202  xCanvasCoord = rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
1203  lines.push_back( qMakePair( currentLevel, QLineF( xCanvasCoord, 0, xCanvasCoord, rect().height() ) ) );
1204  currentLevel += mGridIntervalX;
1205  }
1206  }
1207 
1208  //the four border lines
1209  QVector<QLineF> borderLines;
1210  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1211  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1212  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1213  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1214 
1215  QList<QPointF> intersectionList; //intersects between border lines and grid lines
1216 
1217  while ( currentLevel <= mapBoundingRect.right() )
1218  {
1219  intersectionList.clear();
1220  QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
1221 
1222  QVector<QLineF>::const_iterator it = borderLines.constBegin();
1223  for ( ; it != borderLines.constEnd(); ++it )
1224  {
1225  QPointF intersectionPoint;
1226  if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1227  {
1228  intersectionList.push_back( intersectionPoint );
1229  if ( intersectionList.size() >= 2 )
1230  {
1231  break; //we already have two intersections, skip further tests
1232  }
1233  }
1234  }
1235 
1236  if ( intersectionList.size() >= 2 )
1237  {
1238  lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1239  }
1240  currentLevel += mGridIntervalX;
1241  }
1242 
1243  return 0;
1244 }
1245 
1247 {
1248  mGridPen.setWidthF( w );
1249 }
1250 
1251 void QgsComposerMap::setGridPenColor( const QColor& c )
1252 {
1253  mGridPen.setColor( c );
1254 }
1255 
1257 {
1258  return mCurrentRectangle;
1259 }
1260 
1262 {
1263  QRectF rectangle = rect();
1264  double extension = maxExtension();
1265  rectangle.setLeft( rectangle.left() - extension );
1266  rectangle.setRight( rectangle.right() + extension );
1267  rectangle.setTop( rectangle.top() - extension );
1268  rectangle.setBottom( rectangle.bottom() + extension );
1269  if ( rectangle != mCurrentRectangle )
1270  {
1271  prepareGeometryChange();
1272  mCurrentRectangle = rectangle;
1273  }
1274 }
1275 
1277 {
1278  double dx = mXOffset;
1279  double dy = mYOffset;
1280  transformShift( dx, dy );
1281  return QgsRectangle( mExtent.xMinimum() - dx, mExtent.yMinimum() - dy, mExtent.xMaximum() - dx, mExtent.yMaximum() - dy );
1282 }
1283 
1285 {
1286  double dx = mXOffset;
1287  double dy = mYOffset;
1288  //qWarning("offset");
1289  //qWarning(QString::number(dx).toLocal8Bit().data());
1290  //qWarning(QString::number(dy).toLocal8Bit().data());
1291  transformShift( dx, dy );
1292  //qWarning("transformed:");
1293  //qWarning(QString::number(dx).toLocal8Bit().data());
1294  //qWarning(QString::number(dy).toLocal8Bit().data());
1295  QPolygonF poly;
1296  mapPolygon( poly );
1297  poly.translate( -dx, -dy );
1298  return poly;
1299 }
1300 
1302 {
1304  {
1305  return 0;
1306  }
1307 
1308  QList< QPair< double, QLineF > > xLines;
1309  QList< QPair< double, QLineF > > yLines;
1310 
1311  if ( xGridLines( xLines ) != 0 )
1312  {
1313  return 0;
1314  }
1315 
1316  if ( yGridLines( yLines ) != 0 )
1317  {
1318  return 0;
1319  }
1320 
1321  double maxExtension = 0;
1322  double currentExtension = 0;
1323  QString currentAnnotationString;
1324 
1325  QList< QPair< double, QLineF > >::const_iterator it = xLines.constBegin();
1326  for ( ; it != xLines.constEnd(); ++it )
1327  {
1328  currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
1329  currentExtension = qMax( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
1330  maxExtension = qMax( maxExtension, currentExtension );
1331  }
1332 
1333  it = yLines.constBegin();
1334  for ( ; it != yLines.constEnd(); ++it )
1335  {
1336  currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
1337  currentExtension = qMax( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
1338  maxExtension = qMax( maxExtension, currentExtension );
1339  }
1340 
1341  return maxExtension + mAnnotationFrameDistance;
1342 }
1343 
1344 void QgsComposerMap::mapPolygon( QPolygonF& poly ) const
1345 {
1346  poly.clear();
1347  if ( mRotation == 0 )
1348  {
1349  poly << QPointF( mExtent.xMinimum(), mExtent.yMaximum() );
1350  poly << QPointF( mExtent.xMaximum(), mExtent.yMaximum() );
1351  poly << QPointF( mExtent.xMaximum(), mExtent.yMinimum() );
1352  poly << QPointF( mExtent.xMinimum(), mExtent.yMinimum() );
1353  return;
1354  }
1355 
1356  //there is rotation
1357  QgsPoint rotationPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
1358  double dx, dy; //x-, y- shift from rotation point to corner point
1359 
1360  //top left point
1361  dx = rotationPoint.x() - mExtent.xMinimum();
1362  dy = rotationPoint.y() - mExtent.yMaximum();
1363  rotate( mRotation, dx, dy );
1364  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1365 
1366  //top right point
1367  dx = rotationPoint.x() - mExtent.xMaximum();
1368  dy = rotationPoint.y() - mExtent.yMaximum();
1369  rotate( mRotation, dx, dy );
1370  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1371 
1372  //bottom right point
1373  dx = rotationPoint.x() - mExtent.xMaximum();
1374  dy = rotationPoint.y() - mExtent.yMinimum();
1375  rotate( mRotation, dx, dy );
1376  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1377 
1378  //bottom left point
1379  dx = rotationPoint.x() - mExtent.xMinimum();
1380  dy = rotationPoint.y() - mExtent.yMinimum();
1381  rotate( mRotation, dx, dy );
1382  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1383 }
1384 
1386 {
1387  if ( mRotation == 0 )
1388  {
1389  extent = mExtent;
1390  return;
1391  }
1392 
1393  QPolygonF poly;
1394  mapPolygon( poly );
1395  QRectF bRect = poly.boundingRect();
1396  extent.setXMinimum( bRect.left() );
1397  extent.setXMaximum( bRect.right() );
1398  extent.setYMinimum( bRect.top() );
1399  extent.setYMaximum( bRect.bottom() );
1400  return;
1401 }
1402 
1404 {
1405  double extentWidth = mExtent.width();
1406  if ( extentWidth <= 0 )
1407  {
1408  return 1;
1409  }
1410  return rect().width() / extentWidth;
1411 }
1412 
1413 void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
1414 {
1415  double mmToMapUnits = 1.0 / mapUnitsToMM();
1416  double dxScaled = xShift * mmToMapUnits;
1417  double dyScaled = - yShift * mmToMapUnits;
1418 
1419  rotate( mRotation, dxScaled, dyScaled );
1420 
1421  xShift = dxScaled;
1422  yShift = dyScaled;
1423 }
1424 
1425 QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
1426 {
1427  QPolygonF mapPoly = transformedMapPolygon();
1428  if ( mapPoly.size() < 1 )
1429  {
1430  return QPointF( 0, 0 );
1431  }
1432 
1433  QgsRectangle tExtent = transformedExtent();
1434  QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
1435  double dx = mapCoords.x() - rotationPoint.x();
1436  double dy = mapCoords.y() - rotationPoint.y();
1437  rotate( -mRotation, dx, dy );
1438  QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
1439 
1440  QgsRectangle unrotatedExtent = transformedExtent();
1441  double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
1442  double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
1443  return QPointF( xItem, yItem );
1444 }
1445 
1447 {
1448  if ( p.x() <= pen().widthF() )
1449  {
1450  return Left;
1451  }
1452  else if ( p.x() >= ( rect().width() - pen().widthF() ) )
1453  {
1454  return Right;
1455  }
1456  else if ( p.y() <= pen().widthF() )
1457  {
1458  return Top;
1459  }
1460  else
1461  {
1462  return Bottom;
1463  }
1464 }
1465 
1466 void QgsComposerMap::drawCanvasItems( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
1467 {
1468  if ( !mMapCanvas || !mDrawCanvasItems )
1469  {
1470  return;
1471  }
1472 
1473  QList<QGraphicsItem*> itemList = mMapCanvas->items();
1474  if ( itemList.size() < 1 )
1475  {
1476  return;
1477  }
1478  QGraphicsItem* currentItem = 0;
1479 
1480 #if QT_VERSION >= 0x40600 //Qt 4.6 provides the items in visibility order
1481  for ( int i = itemList.size() - 1; i >= 0; --i )
1482  {
1483  currentItem = itemList.at( i );
1484  //don't draw mapcanvasmap (has z value -10)
1485  if ( !currentItem || currentItem->zValue() == -10 )
1486  {
1487  continue;
1488  }
1489  drawCanvasItem( currentItem, painter, itemStyle );
1490  }
1491 #else //Qt <4.6 provides the items in random order
1492  QMultiMap<int, QGraphicsItem*> topLevelItems;
1493  QMultiMap<QGraphicsItem*, QGraphicsItem*> childItems; //QMultiMap<parentItem, childItem>
1494 
1495  for ( int i = 0; i < itemList.size(); ++i )
1496  {
1497  currentItem = itemList.at( i );
1498  //don't draw mapcanvasmap (has z value -10)
1499  if ( !currentItem || currentItem->zValue() == -10 )
1500  {
1501  continue;
1502  }
1503  if ( currentItem->parentItem() )
1504  {
1505  childItems.insert( currentItem->parentItem(), currentItem );
1506  }
1507  else
1508  {
1509  topLevelItems.insert( currentItem->zValue(), currentItem );
1510  }
1511  }
1512 
1513  QMultiMap<int, QGraphicsItem*>::iterator topLevelIt = topLevelItems.begin();
1514  for ( ; topLevelIt != topLevelItems.end(); ++topLevelIt )
1515  {
1516  drawCanvasItem( topLevelIt.value(), painter, itemStyle );
1517  //Draw children. They probably should be sorted according to z-order, but we don't do it because this code is only
1518  //there for backward compatibility. And currently, having several embedded children is not used in QGIS
1519  QMap<QGraphicsItem*, QGraphicsItem*>::iterator childIt = childItems.find( topLevelIt.value() );
1520  while ( childIt != childItems.end() && childIt.key() == topLevelIt.value() )
1521  {
1522  drawCanvasItem( childIt.value(), painter, itemStyle );
1523  ++childIt;
1524  }
1525  }
1526 #endif
1527 }
1528 
1529 void QgsComposerMap::drawCanvasItem( QGraphicsItem* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
1530 {
1531  if ( !item || !mMapCanvas || !mMapRenderer || !item->isVisible() )
1532  {
1533  return;
1534  }
1535 
1536  painter->save();
1537 
1538  QgsRectangle rendererExtent = mMapRenderer->extent();
1539  QgsRectangle composerMapExtent = mExtent;
1540 
1541  //determine scale factor according to graphics view dpi
1542  double scaleFactor = 1.0 / mMapCanvas->logicalDpiX() * 25.4;
1543 
1544  double itemX, itemY;
1545  QGraphicsItem* parent = item->parentItem();
1546  if ( !parent )
1547  {
1548  QPointF mapPos = composerMapPosForItem( item );
1549  itemX = mapPos.x();
1550  itemY = mapPos.y();
1551  }
1552  else //place item relative to the parent item
1553  {
1554  QPointF itemScenePos = item->scenePos();
1555  QPointF parentScenePos = parent->scenePos();
1556 
1557  QPointF mapPos = composerMapPosForItem( parent );
1558 
1559  itemX = mapPos.x() + ( itemScenePos.x() - parentScenePos.x() ) * scaleFactor;
1560  itemY = mapPos.y() + ( itemScenePos.y() - parentScenePos.y() ) * scaleFactor;
1561  }
1562  painter->translate( itemX, itemY );
1563 
1564 
1565  painter->scale( scaleFactor, scaleFactor );
1566 
1567  //a little trick to let the item know that the paint request comes from the composer
1568  item->setData( 0, "composer" );
1569  item->paint( painter, itemStyle, 0 );
1570  item->setData( 0, "" );
1571  painter->restore();
1572 }
1573 
1574 QPointF QgsComposerMap::composerMapPosForItem( const QGraphicsItem* item ) const
1575 {
1576  if ( !item || !mMapCanvas || !mMapRenderer )
1577  {
1578  return QPointF( 0, 0 );
1579  }
1580 
1581  if ( mExtent.height() <= 0 || mExtent.width() <= 0 || mMapCanvas->width() <= 0 || mMapCanvas->height() <= 0 )
1582  {
1583  return QPointF( 0, 0 );
1584  }
1585 
1586  QRectF graphicsSceneRect = mMapCanvas->sceneRect();
1587  QPointF itemScenePos = item->scenePos();
1588  QgsRectangle mapRendererExtent = mMapRenderer->extent();
1589 
1590  double mapX = itemScenePos.x() / graphicsSceneRect.width() * mapRendererExtent.width() + mapRendererExtent.xMinimum();
1591  double mapY = mapRendererExtent.yMaximum() - itemScenePos.y() / graphicsSceneRect.height() * mapRendererExtent.height();
1592  return mapToItemCoords( QPointF( mapX, mapY ) );
1593 }