Quantum GIS API Documentation  1.7.4
src/core/composer/qgscomposermap.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                          qgscomposermap.cpp
00003                              -------------------
00004     begin                : January 2005
00005     copyright            : (C) 2005 by Radim Blazek
00006     email                : blazek@itc.it
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgscomposermap.h"
00019 
00020 #include "qgscoordinatetransform.h"
00021 #include "qgslogger.h"
00022 #include "qgsmaprenderer.h"
00023 #include "qgsmaplayer.h"
00024 #include "qgsmaplayerregistry.h"
00025 #include "qgsmaptopixel.h"
00026 #include "qgsproject.h"
00027 #include "qgsrasterlayer.h"
00028 #include "qgsrendercontext.h"
00029 #include "qgsscalecalculator.h"
00030 #include "qgsvectorlayer.h"
00031 
00032 #include "qgslabel.h"
00033 #include "qgslabelattributes.h"
00034 
00035 #include <QGraphicsScene>
00036 #include <QGraphicsView>
00037 #include <QPainter>
00038 #include <QSettings>
00039 #include <iostream>
00040 #include <cmath>
00041 
00042 QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
00043     : QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ), mGridEnabled( false ), mGridStyle( Solid ), \
00044     mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), \
00045     mGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mGridAnnotationDirection( Horizontal ),
00046     mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true )
00047 {
00048   mComposition = composition;
00049 
00050   //mId = mComposition->composerMapItems().size();
00051   int maxId = -1;
00052   QList<const QgsComposerMap*> mapList = mComposition->composerMapItems();
00053   QList<const QgsComposerMap*>::const_iterator mapIt = mapList.constBegin();
00054   for ( ; mapIt != mapList.constEnd(); ++mapIt )
00055   {
00056     if (( *mapIt )->id() > maxId )
00057     {
00058       maxId = ( *mapIt )->id();
00059     }
00060   }
00061   mId = maxId + 1;
00062 
00063   mMapRenderer = mComposition->mapRenderer();
00064   mPreviewMode = QgsComposerMap::Rectangle;
00065   mCurrentRectangle = rect();
00066 
00067   // Cache
00068   mCacheUpdated = false;
00069   mDrawing = false;
00070 
00071   //Offset
00072   mXOffset = 0.0;
00073   mYOffset = 0.0;
00074 
00075   connectUpdateSlot();
00076 
00077   //calculate mExtent based on width/height ratio and map canvas extent
00078   if ( mMapRenderer )
00079   {
00080     mExtent = mMapRenderer->extent();
00081   }
00082   setSceneRect( QRectF( x, y, width, height ) );
00083   setToolTip( tr( "Map %1" ).arg( mId ) );
00084   mGridPen.setCapStyle( Qt::FlatCap );
00085 }
00086 
00087 QgsComposerMap::QgsComposerMap( QgsComposition *composition )
00088     : QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mGridEnabled( false ), mGridStyle( Solid ), \
00089     mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), \
00090     mGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mGridAnnotationDirection( Horizontal ), mCrossLength( 3 ),
00091     mMapCanvas( 0 ), mDrawCanvasItems( true )
00092 {
00093   //Offset
00094   mXOffset = 0.0;
00095   mYOffset = 0.0;
00096 
00097   connectUpdateSlot();
00098 
00099   mComposition = composition;
00100   mMapRenderer = mComposition->mapRenderer();
00101   mId = mComposition->composerMapItems().size();
00102   mPreviewMode = QgsComposerMap::Rectangle;
00103   mCurrentRectangle = rect();
00104 
00105   setToolTip( tr( "Map %1" ).arg( mId ) );
00106   mGridPen.setCapStyle( Qt::FlatCap );
00107 }
00108 
00109 QgsComposerMap::~QgsComposerMap()
00110 {
00111 }
00112 
00113 void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSize& size, int dpi )
00114 {
00115   draw( painter, extent, QSizeF( size.width(), size.height() ), dpi );
00116 }
00117 
00118 /* This function is called by paint() and cache() to render the map.  It does not override any functions
00119 from QGraphicsItem. */
00120 void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSizeF& size, double dpi )
00121 {
00122   if ( !painter )
00123   {
00124     return;
00125   }
00126 
00127   if ( !mMapRenderer )
00128   {
00129     return;
00130   }
00131 
00132   QgsMapRenderer theMapRenderer;
00133   theMapRenderer.setExtent( extent );
00134   theMapRenderer.setOutputSize( size, dpi );
00135   if ( mMapRenderer->labelingEngine() )
00136     theMapRenderer.setLabelingEngine( mMapRenderer->labelingEngine()->clone() );
00137 
00138   //use stored layer set or read current set from main canvas
00139   if ( mKeepLayerSet )
00140   {
00141     theMapRenderer.setLayerSet( mLayerSet );
00142   }
00143   else
00144   {
00145     theMapRenderer.setLayerSet( mMapRenderer->layerSet() );
00146   }
00147   theMapRenderer.setProjectionsEnabled( mMapRenderer->hasCrsTransformEnabled() );
00148   theMapRenderer.setDestinationCrs( mMapRenderer->destinationCrs() );
00149 
00150   //set antialiasing if enabled in options
00151   QSettings settings;
00152   // Changed to enable anti aliased rendering by default as of QGIS 1.7
00153   if ( settings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
00154   {
00155     painter->setRenderHint( QPainter::Antialiasing );
00156   }
00157 
00158   QgsRenderContext* theRendererContext = theMapRenderer.rendererContext();
00159   if ( theRendererContext )
00160   {
00161     theRendererContext->setDrawEditingInformation( false );
00162     theRendererContext->setRenderingStopped( false );
00163   }
00164 
00165   // force vector output (no caching of marker images etc.)
00166   theRendererContext->setForceVectorOutput( true );
00167 
00168   //force composer map scale for scale dependent visibility
00169   double bk_scale = theMapRenderer.scale();
00170   theMapRenderer.setScale( scale() );
00171 
00172   //layer caching (as QImages) cannot be done for composer prints
00173   QSettings s;
00174   bool bkLayerCaching = s.value( "/qgis/enable_render_caching", false ).toBool();
00175   s.setValue( "/qgis/enable_render_caching", false );
00176 
00177   theMapRenderer.render( painter );
00178   s.setValue( "/qgis/enable_render_caching", bkLayerCaching );
00179 
00180   theMapRenderer.setScale( bk_scale );
00181 }
00182 
00183 void QgsComposerMap::cache( void )
00184 {
00185   if ( mPreviewMode == Rectangle )
00186   {
00187     return;
00188   }
00189 
00190   if ( mDrawing )
00191   {
00192     return;
00193   }
00194 
00195   mDrawing = true;
00196 
00197   //in case of rotation, we need to request a larger rectangle and create a larger cache image
00198   QgsRectangle requestExtent;
00199   requestedExtent( requestExtent );
00200 
00201   double horizontalVScaleFactor = horizontalViewScaleFactor();
00202   if ( horizontalVScaleFactor < 0 )
00203   {
00204     horizontalVScaleFactor = mLastValidViewScaleFactor;
00205   }
00206 
00207   int w = requestExtent.width() * mapUnitsToMM() * horizontalVScaleFactor;
00208   int h = requestExtent.height() * mapUnitsToMM() * horizontalVScaleFactor;
00209 
00210   if ( w > 5000 ) //limit size of image for better performance
00211   {
00212     w = 5000;
00213   }
00214 
00215   if ( h > 5000 )
00216   {
00217     h = 5000;
00218   }
00219 
00220   mCacheImage = QImage( w, h,  QImage::Format_ARGB32 );
00221   mCacheImage.fill( brush().color().rgb() ); //consider the item background brush
00222   double mapUnitsPerPixel = mExtent.width() / w;
00223 
00224   // WARNING: ymax in QgsMapToPixel is device height!!!
00225   QgsMapToPixel transform( mapUnitsPerPixel, h, requestExtent.yMinimum(), requestExtent.xMinimum() );
00226 
00227   QPainter p( &mCacheImage );
00228 
00229   draw( &p, requestExtent, QSizeF( w, h ), mCacheImage.logicalDpiX() );
00230   p.end();
00231   mCacheUpdated = true;
00232 
00233   mDrawing = false;
00234 }
00235 
00236 void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
00237 {
00238   if ( !mComposition || !painter )
00239   {
00240     return;
00241   }
00242 
00243   QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
00244   painter->save();
00245   painter->setClipRect( thisPaintRect );
00246 
00247   drawBackground( painter );
00248 
00249   if ( mComposition->plotStyle() == QgsComposition::Preview && mPreviewMode == Rectangle )
00250   {
00251     QFont messageFont( "", 12 );
00252     painter->setFont( messageFont );
00253     painter->setPen( QColor( 0, 0, 0 ) );
00254     painter->drawText( thisPaintRect, tr( "Map will be printed here" ) );
00255   }
00256   else if ( mComposition->plotStyle() == QgsComposition::Preview )
00257   {
00258     //draw cached pixmap. This function does not call cache() any more because
00259     //Qt 4.4.0 and 4.4.1 have problems with recursive paintings
00260     //QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
00261     //client functions
00262 
00263     QgsRectangle requestRectangle;
00264     requestedExtent( requestRectangle );
00265     double horizontalVScaleFactor = horizontalViewScaleFactor();
00266     if ( horizontalVScaleFactor < 0 )
00267     {
00268       horizontalVScaleFactor = mLastValidViewScaleFactor;
00269     }
00270 
00271     double imagePixelWidth = mExtent.width() / requestRectangle.width() * mCacheImage.width() ; //how many pixels of the image are for the map extent?
00272     double scale = rect().width() / imagePixelWidth;
00273     QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
00274 
00275     //shift such that rotation point is at 0/0 point in the coordinate system
00276     double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
00277     double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
00278 
00279     //shift such that top left point of the extent at point 0/0 in item coordinate system
00280     double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
00281     double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
00282 
00283     painter->save();
00284 
00285     painter->translate( mXOffset, mYOffset );
00286     painter->translate( xTopLeftShift, yTopLeftShift );
00287     painter->rotate( mRotation );
00288     painter->translate( xShiftMM, -yShiftMM );
00289     painter->scale( scale, scale );
00290     painter->drawImage( 0, 0, mCacheImage );
00291 
00292     //restore rotation
00293     painter->restore();
00294 
00295     //draw canvas items
00296     drawCanvasItems( painter, itemStyle );
00297   }
00298   else if ( mComposition->plotStyle() == QgsComposition::Print ||
00299             mComposition->plotStyle() == QgsComposition::Postscript )
00300   {
00301     if ( mDrawing )
00302     {
00303       return;
00304     }
00305 
00306     mDrawing = true;
00307     QPaintDevice* thePaintDevice = painter->device();
00308     if ( !thePaintDevice )
00309     {
00310       return;
00311     }
00312 
00313     QgsRectangle requestRectangle;
00314     requestedExtent( requestRectangle );
00315 
00316     QSizeF theSize( requestRectangle.width() * mapUnitsToMM(), requestRectangle.height() * mapUnitsToMM() );
00317     QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
00318 
00319     //shift such that rotation point is at 0/0 point in the coordinate system
00320     double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
00321     double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
00322 
00323     //shift such that top left point of the extent at point 0/0 in item coordinate system
00324     double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
00325     double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
00326     painter->save();
00327     painter->translate( mXOffset, mYOffset );
00328     painter->translate( xTopLeftShift, yTopLeftShift );
00329     painter->rotate( mRotation );
00330     painter->translate( xShiftMM, -yShiftMM );
00331     draw( painter, requestRectangle, theSize, 25.4 ); //scene coordinates seem to be in mm
00332 
00333     //restore rotation
00334     painter->restore();
00335 
00336     //draw canvas items
00337     drawCanvasItems( painter, itemStyle );
00338 
00339     mDrawing = false;
00340   }
00341 
00342   painter->setClipRect( thisPaintRect , Qt::NoClip );
00343 
00344   if ( mGridEnabled )
00345   {
00346     drawGrid( painter );
00347   }
00348   drawFrame( painter );
00349   if ( isSelected() )
00350   {
00351     drawSelectionBoxes( painter );
00352   }
00353 
00354 
00355   painter->restore();
00356 }
00357 
00358 void QgsComposerMap::updateCachedImage( void )
00359 {
00360   //If we have a locked layer set then don't reload the map canvas.
00361   if ( mKeepLayerSet )
00362   {
00363     return;
00364   }
00365   syncLayerSet(); //layer list may have changed
00366   mCacheUpdated = false;
00367   cache();
00368   QGraphicsRectItem::update();
00369 }
00370 
00371 void QgsComposerMap::renderModeUpdateCachedImage()
00372 {
00373   if ( mPreviewMode == Render )
00374   {
00375     updateCachedImage();
00376   }
00377 }
00378 
00379 void QgsComposerMap::setCacheUpdated( bool u )
00380 {
00381   mCacheUpdated = u;
00382 }
00383 
00384 double QgsComposerMap::scale() const
00385 {
00386   QgsScaleCalculator calculator;
00387   calculator.setMapUnits( mMapRenderer->mapUnits() );
00388   calculator.setDpi( 25.4 );  //QGraphicsView units are mm
00389   return calculator.calculate( mExtent, rect().width() );
00390 }
00391 
00392 void QgsComposerMap::resize( double dx, double dy )
00393 {
00394   //setRect
00395   QRectF currentRect = rect();
00396   QRectF newSceneRect = QRectF( transform().dx(), transform().dy(), currentRect.width() + dx, currentRect.height() + dy );
00397   setSceneRect( newSceneRect );
00398 }
00399 
00400 void QgsComposerMap::moveContent( double dx, double dy )
00401 {
00402   if ( !mDrawing )
00403   {
00404     transformShift( dx, dy );
00405     mExtent.setXMinimum( mExtent.xMinimum() + dx );
00406     mExtent.setXMaximum( mExtent.xMaximum() + dx );
00407     mExtent.setYMinimum( mExtent.yMinimum() + dy );
00408     mExtent.setYMaximum( mExtent.yMaximum() + dy );
00409     cache();
00410     update();
00411     emit itemChanged();
00412     emit extentChanged();
00413   }
00414 }
00415 
00416 void QgsComposerMap::zoomContent( int delta, double x, double y )
00417 {
00418   if ( mDrawing )
00419   {
00420     return;
00421   }
00422 
00423   QSettings settings;
00424 
00425   //read zoom mode
00426   //0: zoom, 1: zoom and recenter, 2: zoom to cursor, 3: nothing
00427   int zoomMode = settings.value( "/qgis/wheel_action", 0 ).toInt();
00428   if ( zoomMode == 3 ) //do nothing
00429   {
00430     return;
00431   }
00432 
00433   double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
00434 
00435   //find out new center point
00436   double centerX = ( mExtent.xMaximum() + mExtent.xMinimum() ) / 2;
00437   double centerY = ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2;
00438 
00439   if ( zoomMode != 0 )
00440   {
00441     //find out map coordinates of mouse position
00442     double mapMouseX = mExtent.xMinimum() + ( x / rect().width() ) * ( mExtent.xMaximum() - mExtent.xMinimum() );
00443     double mapMouseY = mExtent.yMinimum() + ( 1 - ( y / rect().height() ) ) * ( mExtent.yMaximum() - mExtent.yMinimum() );
00444     if ( zoomMode == 1 ) //zoom and recenter
00445     {
00446       centerX = mapMouseX;
00447       centerY = mapMouseY;
00448     }
00449     else if ( zoomMode == 2 ) //zoom to cursor
00450     {
00451       centerX = mapMouseX + ( centerX - mapMouseX ) * ( 1.0 / zoomFactor );
00452       centerY = mapMouseY + ( centerY - mapMouseY ) * ( 1.0 / zoomFactor );
00453     }
00454   }
00455 
00456   double newIntervalX, newIntervalY;
00457 
00458   if ( delta > 0 )
00459   {
00460     newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) / zoomFactor;
00461     newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) / zoomFactor;
00462   }
00463   else if ( delta < 0 )
00464   {
00465     newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) * zoomFactor;
00466     newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) * zoomFactor;
00467   }
00468   else //no need to zoom
00469   {
00470     return;
00471   }
00472 
00473   mExtent.setXMaximum( centerX + newIntervalX / 2 );
00474   mExtent.setXMinimum( centerX - newIntervalX / 2 );
00475   mExtent.setYMaximum( centerY + newIntervalY / 2 );
00476   mExtent.setYMinimum( centerY - newIntervalY / 2 );
00477 
00478   cache();
00479   update();
00480   emit itemChanged();
00481   emit extentChanged();
00482 }
00483 
00484 void QgsComposerMap::setSceneRect( const QRectF& rectangle )
00485 {
00486   double w = rectangle.width();
00487   double h = rectangle.height();
00488   //prepareGeometryChange();
00489 
00490   QgsComposerItem::setSceneRect( rectangle );
00491 
00492   //QGraphicsRectItem::update();
00493   double newHeight = mExtent.width() * h / w ;
00494   mExtent = QgsRectangle( mExtent.xMinimum(), mExtent.yMinimum(), mExtent.xMaximum(), mExtent.yMinimum() + newHeight );
00495   mCacheUpdated = false;
00496 
00497   if ( mPreviewMode != Rectangle )
00498   {
00499     cache();
00500   }
00501   updateBoundingRect();
00502   update();
00503   emit itemChanged();
00504   emit extentChanged();
00505 }
00506 
00507 void QgsComposerMap::setNewExtent( const QgsRectangle& extent )
00508 {
00509   if ( mExtent == extent )
00510   {
00511     return;
00512   }
00513   mExtent = extent;
00514 
00515   //adjust height
00516   QRectF currentRect = rect();
00517 
00518   double newHeight = currentRect.width() * extent.height() / extent.width();
00519 
00520   setSceneRect( QRectF( transform().dx(), transform().dy(), currentRect.width(), newHeight ) );
00521 }
00522 
00523 void QgsComposerMap::setNewScale( double scaleDenominator )
00524 {
00525   double currentScaleDenominator = scale();
00526 
00527   if ( scaleDenominator == currentScaleDenominator )
00528   {
00529     return;
00530   }
00531 
00532   double scaleRatio = scaleDenominator / currentScaleDenominator;
00533   mExtent.scale( scaleRatio );
00534   mCacheUpdated = false;
00535   cache();
00536   update();
00537   emit itemChanged();
00538   emit extentChanged();
00539 }
00540 
00541 void QgsComposerMap::setOffset( double xOffset, double yOffset )
00542 {
00543   mXOffset = xOffset;
00544   mYOffset = yOffset;
00545 }
00546 
00547 void QgsComposerMap::setMapRotation( double r )
00548 {
00549   setRotation( r );
00550   emit rotationChanged( r );
00551 }
00552 
00553 bool QgsComposerMap::containsWMSLayer() const
00554 {
00555   if ( !mMapRenderer )
00556   {
00557     return false;
00558   }
00559 
00560   QStringList layers = mMapRenderer->layerSet();
00561 
00562   QStringList::const_iterator layer_it = layers.constBegin();
00563   QgsMapLayer* currentLayer = 0;
00564 
00565   for ( ; layer_it != layers.constEnd(); ++layer_it )
00566   {
00567     currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
00568     if ( currentLayer )
00569     {
00570       QgsRasterLayer* currentRasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
00571       if ( currentRasterLayer )
00572       {
00573         const QgsRasterDataProvider* rasterProvider = 0;
00574         if (( rasterProvider = currentRasterLayer->dataProvider() ) )
00575         {
00576           if ( rasterProvider->name() == "wms" )
00577           {
00578             return true;
00579           }
00580         }
00581       }
00582     }
00583   }
00584   return false;
00585 }
00586 
00587 void QgsComposerMap::connectUpdateSlot()
00588 {
00589   //connect signal from layer registry to update in case of new or deleted layers
00590   QgsMapLayerRegistry* layerRegistry = QgsMapLayerRegistry::instance();
00591   if ( layerRegistry )
00592   {
00593     connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( updateCachedImage() ) );
00594     connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( updateCachedImage() ) );
00595   }
00596 }
00597 
00598 bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
00599 {
00600   if ( elem.isNull() )
00601   {
00602     return false;
00603   }
00604 
00605   QDomElement composerMapElem = doc.createElement( "ComposerMap" );
00606   composerMapElem.setAttribute( "id", mId );
00607 
00608   //previewMode
00609   if ( mPreviewMode == Cache )
00610   {
00611     composerMapElem.setAttribute( "previewMode", "Cache" );
00612   }
00613   else if ( mPreviewMode == Render )
00614   {
00615     composerMapElem.setAttribute( "previewMode", "Render" );
00616   }
00617   else //rectangle
00618   {
00619     composerMapElem.setAttribute( "previewMode", "Rectangle" );
00620   }
00621 
00622   if ( mKeepLayerSet )
00623   {
00624     composerMapElem.setAttribute( "keepLayerSet", "true" );
00625   }
00626   else
00627   {
00628     composerMapElem.setAttribute( "keepLayerSet", "false" );
00629   }
00630 
00631   if ( mDrawCanvasItems )
00632   {
00633     composerMapElem.setAttribute( "drawCanvasItems", "true" );
00634   }
00635   else
00636   {
00637     composerMapElem.setAttribute( "drawCanvasItems", "false" );
00638   }
00639 
00640   //extent
00641   QDomElement extentElem = doc.createElement( "Extent" );
00642   extentElem.setAttribute( "xmin", mExtent.xMinimum() );
00643   extentElem.setAttribute( "xmax", mExtent.xMaximum() );
00644   extentElem.setAttribute( "ymin", mExtent.yMinimum() );
00645   extentElem.setAttribute( "ymax", mExtent.yMaximum() );
00646   composerMapElem.appendChild( extentElem );
00647 
00648   //layer set
00649   QDomElement layerSetElem = doc.createElement( "LayerSet" );
00650   QStringList::const_iterator layerIt = mLayerSet.constBegin();
00651   for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
00652   {
00653     QDomElement layerElem = doc.createElement( "Layer" );
00654     QDomText layerIdText = doc.createTextNode( *layerIt );
00655     layerElem.appendChild( layerIdText );
00656     layerSetElem.appendChild( layerElem );
00657   }
00658   composerMapElem.appendChild( layerSetElem );
00659 
00660   //grid
00661   QDomElement gridElem = doc.createElement( "Grid" );
00662   gridElem.setAttribute( "show", mGridEnabled );
00663   gridElem.setAttribute( "gridStyle", mGridStyle );
00664   gridElem.setAttribute( "intervalX", mGridIntervalX );
00665   gridElem.setAttribute( "intervalY", mGridIntervalY );
00666   gridElem.setAttribute( "offsetX", mGridOffsetX );
00667   gridElem.setAttribute( "offsetY", mGridOffsetY );
00668   gridElem.setAttribute( "penWidth", mGridPen.widthF() );
00669   gridElem.setAttribute( "penColorRed", mGridPen.color().red() );
00670   gridElem.setAttribute( "penColorGreen", mGridPen.color().green() );
00671   gridElem.setAttribute( "penColorBlue", mGridPen.color().blue() );
00672   gridElem.setAttribute( "crossLength", mCrossLength );
00673 
00674   //grid annotation
00675   QDomElement annotationElem = doc.createElement( "Annotation" );
00676   annotationElem.setAttribute( "show", mShowGridAnnotation );
00677   annotationElem.setAttribute( "position", mGridAnnotationPosition );
00678   annotationElem.setAttribute( "frameDistance", mAnnotationFrameDistance );
00679   annotationElem.setAttribute( "direction", mGridAnnotationDirection );
00680   annotationElem.setAttribute( "font", mGridAnnotationFont.toString() );
00681   annotationElem.setAttribute( "precision", mGridAnnotationPrecision );
00682 
00683   gridElem.appendChild( annotationElem );
00684   composerMapElem.appendChild( gridElem );
00685 
00686   elem.appendChild( composerMapElem );
00687   return _writeXML( composerMapElem, doc );
00688 }
00689 
00690 bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& doc )
00691 {
00692   if ( itemElem.isNull() )
00693   {
00694     return false;
00695   }
00696 
00697   QString idRead = itemElem.attribute( "id", "not found" );
00698   if ( idRead != "not found" )
00699   {
00700     mId = idRead.toInt();
00701   }
00702   mPreviewMode = Rectangle;
00703 
00704   //previewMode
00705   QString previewMode = itemElem.attribute( "previewMode" );
00706   if ( previewMode == "Cache" )
00707   {
00708     mPreviewMode = Cache;
00709   }
00710   else if ( previewMode == "Render" )
00711   {
00712     mPreviewMode = Render;
00713   }
00714   else
00715   {
00716     mPreviewMode = Rectangle;
00717   }
00718 
00719   //extent
00720   QDomNodeList extentNodeList = itemElem.elementsByTagName( "Extent" );
00721   if ( extentNodeList.size() > 0 )
00722   {
00723     QDomElement extentElem = extentNodeList.at( 0 ).toElement();
00724     double xmin, xmax, ymin, ymax;
00725     xmin = extentElem.attribute( "xmin" ).toDouble();
00726     xmax = extentElem.attribute( "xmax" ).toDouble();
00727     ymin = extentElem.attribute( "ymin" ).toDouble();
00728     ymax = extentElem.attribute( "ymax" ).toDouble();
00729 
00730     mExtent = QgsRectangle( xmin, ymin, xmax, ymax );
00731   }
00732 
00733   //mKeepLayerSet flag
00734   QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
00735   if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
00736   {
00737     mKeepLayerSet = true;
00738   }
00739   else
00740   {
00741     mKeepLayerSet = false;
00742   }
00743 
00744   QString drawCanvasItemsFlag = itemElem.attribute( "drawCanvasItems" );
00745   if ( drawCanvasItemsFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
00746   {
00747     mDrawCanvasItems = true;
00748   }
00749   else
00750   {
00751     mDrawCanvasItems = false;
00752   }
00753 
00754   //mLayerSet
00755   QDomNodeList layerSetNodeList = itemElem.elementsByTagName( "LayerSet" );
00756   QStringList layerSet;
00757   if ( layerSetNodeList.size() > 0 )
00758   {
00759     QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
00760     QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( "Layer" );
00761     for ( int i = 0; i < layerIdNodeList.size(); ++i )
00762     {
00763       layerSet << layerIdNodeList.at( i ).toElement().text();
00764     }
00765   }
00766   mLayerSet = layerSet;
00767 
00768   mDrawing = false;
00769   mNumCachedLayers = 0;
00770   mCacheUpdated = false;
00771 
00772   //grid
00773   QDomNodeList gridNodeList = itemElem.elementsByTagName( "Grid" );
00774   if ( gridNodeList.size() > 0 )
00775   {
00776     QDomElement gridElem = gridNodeList.at( 0 ).toElement();
00777     mGridEnabled = ( gridElem.attribute( "show", "0" ) != "0" );
00778     mGridStyle = QgsComposerMap::GridStyle( gridElem.attribute( "gridStyle", "0" ).toInt() );
00779     mGridIntervalX = gridElem.attribute( "intervalX", "0" ).toDouble();
00780     mGridIntervalY = gridElem.attribute( "intervalY", "0" ).toDouble();
00781     mGridOffsetX = gridElem.attribute( "offsetX", "0" ).toDouble();
00782     mGridOffsetY = gridElem.attribute( "offsetY", "0" ).toDouble();
00783     mGridPen.setWidthF( gridElem.attribute( "penWidth", "0" ).toDouble() );
00784     mGridPen.setColor( QColor( gridElem.attribute( "penColorRed", "0" ).toInt(), \
00785                                gridElem.attribute( "penColorGreen", "0" ).toInt(), \
00786                                gridElem.attribute( "penColorBlue", "0" ).toInt() ) );
00787     mCrossLength = gridElem.attribute( "crossLength", "3" ).toDouble();
00788 
00789     QDomNodeList annotationNodeList = gridElem.elementsByTagName( "Annotation" );
00790     if ( annotationNodeList.size() > 0 )
00791     {
00792       QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
00793       mShowGridAnnotation = ( annotationElem.attribute( "show", "0" ) != "0" );
00794       mGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "position", "0" ).toInt() );
00795       mAnnotationFrameDistance = annotationElem.attribute( "frameDistance", "0" ).toDouble();
00796       mGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "direction", "0" ).toInt() );
00797       mGridAnnotationFont.fromString( annotationElem.attribute( "font", "" ) );
00798       mGridAnnotationPrecision = annotationElem.attribute( "precision", "3" ).toInt();
00799     }
00800   }
00801 
00802   //restore general composer item properties
00803   QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
00804   if ( composerItemList.size() > 0 )
00805   {
00806     QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
00807     _readXML( composerItemElem, doc );
00808   }
00809 
00810   updateBoundingRect();
00811   emit itemChanged();
00812   return true;
00813 }
00814 
00815 void QgsComposerMap::storeCurrentLayerSet()
00816 {
00817   if ( mMapRenderer )
00818   {
00819     mLayerSet = mMapRenderer->layerSet();
00820   }
00821 }
00822 
00823 void QgsComposerMap::syncLayerSet()
00824 {
00825   if ( mLayerSet.size() < 1 && !mMapRenderer )
00826   {
00827     return;
00828   }
00829 
00830   //if layer set is fixed, do a lookup in the layer registry to also find the non-visible layers
00831   QStringList currentLayerSet;
00832   if ( mKeepLayerSet )
00833   {
00834     currentLayerSet = QgsMapLayerRegistry::instance()->mapLayers().uniqueKeys();
00835   }
00836   else //only consider layers visible in the map
00837   {
00838     currentLayerSet = mMapRenderer->layerSet();
00839   }
00840 
00841   for ( int i = mLayerSet.size() - 1; i >= 0; --i )
00842   {
00843     if ( !currentLayerSet.contains( mLayerSet.at( i ) ) )
00844     {
00845       mLayerSet.removeAt( i );
00846     }
00847   }
00848 }
00849 
00850 void QgsComposerMap::drawGrid( QPainter* p )
00851 {
00852   p->setPen( mGridPen );
00853 
00854   QList< QPair< double, QLineF > > verticalLines;
00855   yGridLines( verticalLines );
00856   QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
00857   QList< QPair< double, QLineF > > horizontalLines;
00858   xGridLines( horizontalLines );
00859   QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
00860 
00861   QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
00862   p->setClipRect( thisPaintRect );
00863 
00864   //simpler approach: draw vertical lines first, then horizontal ones
00865   if ( mGridStyle == QgsComposerMap::Solid )
00866   {
00867     for ( ; vIt != verticalLines.constEnd(); ++vIt )
00868     {
00869       p->drawLine( vIt->second );
00870     }
00871 
00872     for ( ; hIt != horizontalLines.constEnd(); ++hIt )
00873     {
00874       p->drawLine( hIt->second );
00875     }
00876   }
00877   else //cross
00878   {
00879     QPointF intersectionPoint, crossEnd1, crossEnd2;
00880     for ( ; vIt != verticalLines.constEnd(); ++vIt )
00881     {
00882       //start mark
00883       crossEnd1 = pointOnLineWithDistance( vIt->second.p1(), vIt->second.p2(), mCrossLength );
00884       p->drawLine( vIt->second.p1(), crossEnd1 );
00885 
00886       //test for intersection with every horizontal line
00887       hIt = horizontalLines.constBegin();
00888       for ( ; hIt != horizontalLines.constEnd(); ++hIt )
00889       {
00890         if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
00891         {
00892           crossEnd1 = pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength );
00893           crossEnd2 = pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength );
00894           p->drawLine( crossEnd1, crossEnd2 );
00895         }
00896       }
00897       //end mark
00898       QPointF crossEnd2 = pointOnLineWithDistance( vIt->second.p2(), vIt->second.p1(), mCrossLength );
00899       p->drawLine( vIt->second.p2(), crossEnd2 );
00900     }
00901 
00902     hIt = horizontalLines.constBegin();
00903     for ( ; hIt != horizontalLines.constEnd(); ++hIt )
00904     {
00905       //start mark
00906       crossEnd1 = pointOnLineWithDistance( hIt->second.p1(), hIt->second.p2(), mCrossLength );
00907       p->drawLine( hIt->second.p1(), crossEnd1 );
00908 
00909       vIt = verticalLines.constBegin();
00910       for ( ; vIt != verticalLines.constEnd(); ++vIt )
00911       {
00912         if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
00913         {
00914           crossEnd1 = pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength );
00915           crossEnd2 = pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength );
00916           p->drawLine( crossEnd1, crossEnd2 );
00917         }
00918       }
00919       //end mark
00920       crossEnd1 = pointOnLineWithDistance( hIt->second.p2(), hIt->second.p1(), mCrossLength );
00921       p->drawLine( hIt->second.p2(), crossEnd1 );
00922     }
00923 
00924 
00925   }
00926 
00927   p->setClipRect( thisPaintRect , Qt::NoClip );
00928 
00929   if ( mShowGridAnnotation )
00930   {
00931     drawCoordinateAnnotations( p, horizontalLines, verticalLines );
00932   }
00933 }
00934 
00935 void QgsComposerMap::drawCoordinateAnnotations( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines )
00936 {
00937   if ( !p )
00938   {
00939     return;
00940   }
00941 
00942 
00943   QString currentAnnotationString;
00944   QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
00945   for ( ; it != hLines.constEnd(); ++it )
00946   {
00947     currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
00948     drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
00949     drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
00950   }
00951 
00952   it = vLines.constBegin();
00953   for ( ; it != vLines.constEnd(); ++it )
00954   {
00955     currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
00956     drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
00957     drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
00958   }
00959 }
00960 
00961 void QgsComposerMap::drawCoordinateAnnotation( QPainter* p, const QPointF& pos, QString annotationString )
00962 {
00963   Border frameBorder = borderForLineCoord( pos );
00964   double textWidth = textWidthMillimeters( mGridAnnotationFont, annotationString );
00965   //relevant for annotations is the height of digits
00966   double textHeight = fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
00967   double xpos = pos.x();
00968   double ypos = pos.y();
00969   int rotation = 0;
00970 
00971   if ( frameBorder == Left )
00972   {
00973 
00974     if ( mGridAnnotationPosition == InsideMapFrame )
00975     {
00976       if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
00977       {
00978         xpos += textHeight + mAnnotationFrameDistance;
00979         ypos += textWidth / 2.0;
00980         rotation = 270;
00981       }
00982       else
00983       {
00984         xpos += mAnnotationFrameDistance;
00985         ypos += textHeight / 2.0;
00986       }
00987     }
00988     else //Outside map frame
00989     {
00990       if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
00991       {
00992         xpos -= mAnnotationFrameDistance;
00993         ypos += textWidth / 2.0;
00994         rotation = 270;
00995       }
00996       else
00997       {
00998         xpos -= textWidth + mAnnotationFrameDistance;
00999         ypos += textHeight / 2.0;
01000       }
01001     }
01002 
01003   }
01004   else if ( frameBorder == Right )
01005   {
01006     if ( mGridAnnotationPosition == InsideMapFrame )
01007     {
01008       if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
01009       {
01010         xpos -= mAnnotationFrameDistance;
01011         ypos += textWidth / 2.0;
01012         rotation = 270;
01013       }
01014       else //Horizontal
01015       {
01016         xpos -= textWidth + mAnnotationFrameDistance;
01017         ypos += textHeight / 2.0;
01018       }
01019     }
01020     else //OutsideMapFrame
01021     {
01022       if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
01023       {
01024         xpos += textHeight + mAnnotationFrameDistance;
01025         ypos += textWidth / 2.0;
01026         rotation = 270;
01027       }
01028       else //Horizontal
01029       {
01030         xpos += mAnnotationFrameDistance;
01031         ypos += textHeight / 2.0;
01032       }
01033     }
01034   }
01035   else if ( frameBorder == Bottom )
01036   {
01037     if ( mGridAnnotationPosition == InsideMapFrame )
01038     {
01039       if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
01040       {
01041         ypos -= mAnnotationFrameDistance;
01042         xpos -= textWidth / 2.0;
01043       }
01044       else //Vertical
01045       {
01046         xpos += textHeight / 2.0;
01047         ypos -= mAnnotationFrameDistance;
01048         rotation = 270;
01049       }
01050     }
01051     else //OutsideMapFrame
01052     {
01053       if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
01054       {
01055         ypos += mAnnotationFrameDistance + textHeight;
01056         xpos -= textWidth / 2.0;
01057       }
01058       else //Vertical
01059       {
01060         xpos += textHeight / 2.0;
01061         ypos += textWidth + mAnnotationFrameDistance;
01062         rotation = 270;
01063       }
01064     }
01065   }
01066   else //Top
01067   {
01068     if ( mGridAnnotationPosition == InsideMapFrame )
01069     {
01070       if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
01071       {
01072         xpos -= textWidth / 2.0;
01073         ypos += textHeight + mAnnotationFrameDistance;
01074       }
01075       else //Vertical
01076       {
01077         xpos += textHeight / 2.0;
01078         ypos += textWidth + mAnnotationFrameDistance;
01079         rotation = 270;
01080       }
01081     }
01082     else //OutsideMapFrame
01083     {
01084       if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
01085       {
01086         xpos -= textWidth / 2.0;
01087         ypos -= mAnnotationFrameDistance;
01088       }
01089       else //Vertical
01090       {
01091         xpos += textHeight / 2.0;
01092         ypos -= mAnnotationFrameDistance;
01093         rotation = 270;
01094       }
01095     }
01096   }
01097 
01098   drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
01099 }
01100 
01101 void QgsComposerMap::drawAnnotation( QPainter* p, const QPointF& pos, int rotation, const QString& annotationText )
01102 {
01103   p->save();
01104   p->translate( pos );
01105   p->rotate( rotation );
01106   p->setPen( QColor( 0, 0, 0 ) );
01107   drawText( p, 0, 0, annotationText, mGridAnnotationFont );
01108   p->restore();
01109 }
01110 
01111 int QgsComposerMap::xGridLines( QList< QPair< double, QLineF > >& lines ) const
01112 {
01113   lines.clear();
01114   if ( mGridIntervalY <= 0.0 )
01115   {
01116     return 1;
01117   }
01118 
01119 
01120   QPolygonF mapPolygon = transformedMapPolygon();
01121   QRectF mapBoundingRect = mapPolygon.boundingRect();
01122 
01123   //consider to round up to the next step in case the left boundary is > 0
01124   double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
01125   double currentLevel = ( int )(( mapBoundingRect.top() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY;
01126 
01127   if ( doubleNear( mRotation, 0.0 ) )
01128   {
01129     //no rotation. Do it 'the easy way'
01130 
01131     double yCanvasCoord;
01132 
01133     while ( currentLevel <= mapBoundingRect.bottom() )
01134     {
01135       yCanvasCoord = rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
01136       lines.push_back( qMakePair( currentLevel, QLineF( 0, yCanvasCoord, rect().width(), yCanvasCoord ) ) );
01137       currentLevel += mGridIntervalY;
01138     }
01139   }
01140 
01141   //the four border lines
01142   QVector<QLineF> borderLines;
01143   borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
01144   borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
01145   borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
01146   borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
01147 
01148   QList<QPointF> intersectionList; //intersects between border lines and grid lines
01149 
01150   while ( currentLevel <= mapBoundingRect.bottom() )
01151   {
01152     intersectionList.clear();
01153     QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
01154 
01155     QVector<QLineF>::const_iterator it = borderLines.constBegin();
01156     for ( ; it != borderLines.constEnd(); ++it )
01157     {
01158       QPointF intersectionPoint;
01159       if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
01160       {
01161         intersectionList.push_back( intersectionPoint );
01162         if ( intersectionList.size() >= 2 )
01163         {
01164           break; //we already have two intersections, skip further tests
01165         }
01166       }
01167     }
01168 
01169     if ( intersectionList.size() >= 2 )
01170     {
01171       lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
01172     }
01173     currentLevel += mGridIntervalY;
01174   }
01175 
01176 
01177   return 0;
01178 }
01179 
01180 int QgsComposerMap::yGridLines( QList< QPair< double, QLineF > >& lines ) const
01181 {
01182   lines.clear();
01183   if ( mGridIntervalX <= 0.0 )
01184   {
01185     return 1;
01186   }
01187 
01188   QPolygonF mapPolygon = transformedMapPolygon();
01189   QRectF mapBoundingRect = mapPolygon.boundingRect();
01190 
01191   //consider to round up to the next step in case the left boundary is > 0
01192   double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
01193   double currentLevel = ( int )(( mapBoundingRect.left() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
01194 
01195   if ( doubleNear( mRotation, 0.0 ) )
01196   {
01197     //no rotation. Do it 'the easy way'
01198     double xCanvasCoord;
01199 
01200     while ( currentLevel <= mapBoundingRect.right() )
01201     {
01202       xCanvasCoord = rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
01203       lines.push_back( qMakePair( currentLevel, QLineF( xCanvasCoord, 0, xCanvasCoord, rect().height() ) ) );
01204       currentLevel += mGridIntervalX;
01205     }
01206   }
01207 
01208   //the four border lines
01209   QVector<QLineF> borderLines;
01210   borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
01211   borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
01212   borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
01213   borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
01214 
01215   QList<QPointF> intersectionList; //intersects between border lines and grid lines
01216 
01217   while ( currentLevel <= mapBoundingRect.right() )
01218   {
01219     intersectionList.clear();
01220     QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
01221 
01222     QVector<QLineF>::const_iterator it = borderLines.constBegin();
01223     for ( ; it != borderLines.constEnd(); ++it )
01224     {
01225       QPointF intersectionPoint;
01226       if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
01227       {
01228         intersectionList.push_back( intersectionPoint );
01229         if ( intersectionList.size() >= 2 )
01230         {
01231           break; //we already have two intersections, skip further tests
01232         }
01233       }
01234     }
01235 
01236     if ( intersectionList.size() >= 2 )
01237     {
01238       lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
01239     }
01240     currentLevel += mGridIntervalX;
01241   }
01242 
01243   return 0;
01244 }
01245 
01246 void QgsComposerMap::setGridPenWidth( double w )
01247 {
01248   mGridPen.setWidthF( w );
01249 }
01250 
01251 void QgsComposerMap::setGridPenColor( const QColor& c )
01252 {
01253   mGridPen.setColor( c );
01254 }
01255 
01256 QRectF QgsComposerMap::boundingRect() const
01257 {
01258   return mCurrentRectangle;
01259 }
01260 
01261 void QgsComposerMap::updateBoundingRect()
01262 {
01263   QRectF rectangle = rect();
01264   double extension = maxExtension();
01265   rectangle.setLeft( rectangle.left() - extension );
01266   rectangle.setRight( rectangle.right() + extension );
01267   rectangle.setTop( rectangle.top() - extension );
01268   rectangle.setBottom( rectangle.bottom() + extension );
01269   if ( rectangle != mCurrentRectangle )
01270   {
01271     prepareGeometryChange();
01272     mCurrentRectangle = rectangle;
01273   }
01274 }
01275 
01276 QgsRectangle QgsComposerMap::transformedExtent() const
01277 {
01278   double dx = mXOffset;
01279   double dy = mYOffset;
01280   transformShift( dx, dy );
01281   return QgsRectangle( mExtent.xMinimum() - dx, mExtent.yMinimum() - dy, mExtent.xMaximum() - dx, mExtent.yMaximum() - dy );
01282 }
01283 
01284 QPolygonF QgsComposerMap::transformedMapPolygon() const
01285 {
01286   double dx = mXOffset;
01287   double dy = mYOffset;
01288   //qWarning("offset");
01289   //qWarning(QString::number(dx).toLocal8Bit().data());
01290   //qWarning(QString::number(dy).toLocal8Bit().data());
01291   transformShift( dx, dy );
01292   //qWarning("transformed:");
01293   //qWarning(QString::number(dx).toLocal8Bit().data());
01294   //qWarning(QString::number(dy).toLocal8Bit().data());
01295   QPolygonF poly;
01296   mapPolygon( poly );
01297   poly.translate( -dx, -dy );
01298   return poly;
01299 }
01300 
01301 double QgsComposerMap::maxExtension() const
01302 {
01303   if ( !mGridEnabled || !mShowGridAnnotation || mGridAnnotationPosition != OutsideMapFrame )
01304   {
01305     return 0;
01306   }
01307 
01308   QList< QPair< double, QLineF > > xLines;
01309   QList< QPair< double, QLineF > > yLines;
01310 
01311   if ( xGridLines( xLines ) != 0 )
01312   {
01313     return 0;
01314   }
01315 
01316   if ( yGridLines( yLines ) != 0 )
01317   {
01318     return 0;
01319   }
01320 
01321   double maxExtension = 0;
01322   double currentExtension = 0;
01323   QString currentAnnotationString;
01324 
01325   QList< QPair< double, QLineF > >::const_iterator it = xLines.constBegin();
01326   for ( ; it != xLines.constEnd(); ++it )
01327   {
01328     currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
01329     currentExtension = qMax( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
01330     maxExtension = qMax( maxExtension, currentExtension );
01331   }
01332 
01333   it = yLines.constBegin();
01334   for ( ; it != yLines.constEnd(); ++it )
01335   {
01336     currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
01337     currentExtension = qMax( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
01338     maxExtension = qMax( maxExtension, currentExtension );
01339   }
01340 
01341   return maxExtension + mAnnotationFrameDistance;
01342 }
01343 
01344 void QgsComposerMap::mapPolygon( QPolygonF& poly ) const
01345 {
01346   poly.clear();
01347   if ( mRotation == 0 )
01348   {
01349     poly << QPointF( mExtent.xMinimum(), mExtent.yMaximum() );
01350     poly << QPointF( mExtent.xMaximum(), mExtent.yMaximum() );
01351     poly << QPointF( mExtent.xMaximum(), mExtent.yMinimum() );
01352     poly << QPointF( mExtent.xMinimum(), mExtent.yMinimum() );
01353     return;
01354   }
01355 
01356   //there is rotation
01357   QgsPoint rotationPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
01358   double dx, dy; //x-, y- shift from rotation point to corner point
01359 
01360   //top left point
01361   dx = rotationPoint.x() - mExtent.xMinimum();
01362   dy = rotationPoint.y() - mExtent.yMaximum();
01363   rotate( mRotation, dx, dy );
01364   poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
01365 
01366   //top right point
01367   dx = rotationPoint.x() - mExtent.xMaximum();
01368   dy = rotationPoint.y() - mExtent.yMaximum();
01369   rotate( mRotation, dx, dy );
01370   poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
01371 
01372   //bottom right point
01373   dx = rotationPoint.x() - mExtent.xMaximum();
01374   dy = rotationPoint.y() - mExtent.yMinimum();
01375   rotate( mRotation, dx, dy );
01376   poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
01377 
01378   //bottom left point
01379   dx = rotationPoint.x() - mExtent.xMinimum();
01380   dy = rotationPoint.y() - mExtent.yMinimum();
01381   rotate( mRotation, dx, dy );
01382   poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
01383 }
01384 
01385 void QgsComposerMap::requestedExtent( QgsRectangle& extent ) const
01386 {
01387   if ( mRotation == 0 )
01388   {
01389     extent = mExtent;
01390     return;
01391   }
01392 
01393   QPolygonF poly;
01394   mapPolygon( poly );
01395   QRectF bRect = poly.boundingRect();
01396   extent.setXMinimum( bRect.left() );
01397   extent.setXMaximum( bRect.right() );
01398   extent.setYMinimum( bRect.top() );
01399   extent.setYMaximum( bRect.bottom() );
01400   return;
01401 }
01402 
01403 double QgsComposerMap::mapUnitsToMM() const
01404 {
01405   double extentWidth = mExtent.width();
01406   if ( extentWidth <= 0 )
01407   {
01408     return 1;
01409   }
01410   return rect().width() / extentWidth;
01411 }
01412 
01413 void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
01414 {
01415   double mmToMapUnits = 1.0 / mapUnitsToMM();
01416   double dxScaled = xShift * mmToMapUnits;
01417   double dyScaled = - yShift * mmToMapUnits;
01418 
01419   rotate( mRotation, dxScaled, dyScaled );
01420 
01421   xShift = dxScaled;
01422   yShift = dyScaled;
01423 }
01424 
01425 QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
01426 {
01427   QPolygonF mapPoly = transformedMapPolygon();
01428   if ( mapPoly.size() < 1 )
01429   {
01430     return QPointF( 0, 0 );
01431   }
01432 
01433   QgsRectangle tExtent = transformedExtent();
01434   QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
01435   double dx = mapCoords.x() - rotationPoint.x();
01436   double dy = mapCoords.y() - rotationPoint.y();
01437   rotate( -mRotation, dx, dy );
01438   QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
01439 
01440   QgsRectangle unrotatedExtent = transformedExtent();
01441   double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
01442   double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
01443   return QPointF( xItem, yItem );
01444 }
01445 
01446 QgsComposerMap::Border QgsComposerMap::borderForLineCoord( const QPointF& p ) const
01447 {
01448   if ( p.x() <= pen().widthF() )
01449   {
01450     return Left;
01451   }
01452   else if ( p.x() >= ( rect().width() - pen().widthF() ) )
01453   {
01454     return Right;
01455   }
01456   else if ( p.y() <= pen().widthF() )
01457   {
01458     return Top;
01459   }
01460   else
01461   {
01462     return Bottom;
01463   }
01464 }
01465 
01466 void QgsComposerMap::drawCanvasItems( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
01467 {
01468   if ( !mMapCanvas || !mDrawCanvasItems )
01469   {
01470     return;
01471   }
01472 
01473   QList<QGraphicsItem*> itemList = mMapCanvas->items();
01474   if ( itemList.size() < 1 )
01475   {
01476     return;
01477   }
01478   QGraphicsItem* currentItem = 0;
01479 
01480 #if QT_VERSION >= 0x40600 //Qt 4.6 provides the items in visibility order
01481   for ( int i = itemList.size() - 1; i >= 0; --i )
01482   {
01483     currentItem = itemList.at( i );
01484     //don't draw mapcanvasmap (has z value -10)
01485     if ( !currentItem || currentItem->zValue() == -10 )
01486     {
01487       continue;
01488     }
01489     drawCanvasItem( currentItem, painter, itemStyle );
01490   }
01491 #else //Qt <4.6 provides the items in random order
01492   QMultiMap<int, QGraphicsItem*> topLevelItems;
01493   QMultiMap<QGraphicsItem*, QGraphicsItem*> childItems; //QMultiMap<parentItem, childItem>
01494 
01495   for ( int i = 0; i < itemList.size(); ++i )
01496   {
01497     currentItem = itemList.at( i );
01498     //don't draw mapcanvasmap (has z value -10)
01499     if ( !currentItem || currentItem->zValue() == -10 )
01500     {
01501       continue;
01502     }
01503     if ( currentItem->parentItem() )
01504     {
01505       childItems.insert( currentItem->parentItem(), currentItem );
01506     }
01507     else
01508     {
01509       topLevelItems.insert( currentItem->zValue(), currentItem );
01510     }
01511   }
01512 
01513   QMultiMap<int, QGraphicsItem*>::iterator topLevelIt = topLevelItems.begin();
01514   for ( ; topLevelIt != topLevelItems.end(); ++topLevelIt )
01515   {
01516     drawCanvasItem( topLevelIt.value(), painter, itemStyle );
01517     //Draw children. They probably should be sorted according to z-order, but we don't do it because this code is only
01518     //there for backward compatibility. And currently, having several embedded children is not used in QGIS
01519     QMap<QGraphicsItem*, QGraphicsItem*>::iterator childIt = childItems.find( topLevelIt.value() );
01520     while ( childIt != childItems.end() && childIt.key() == topLevelIt.value() )
01521     {
01522       drawCanvasItem( childIt.value(), painter, itemStyle );
01523       ++childIt;
01524     }
01525   }
01526 #endif
01527 }
01528 
01529 void QgsComposerMap::drawCanvasItem( QGraphicsItem* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
01530 {
01531   if ( !item || !mMapCanvas || !mMapRenderer  || !item->isVisible() )
01532   {
01533     return;
01534   }
01535 
01536   painter->save();
01537 
01538   QgsRectangle rendererExtent = mMapRenderer->extent();
01539   QgsRectangle composerMapExtent = mExtent;
01540 
01541   //determine scale factor according to graphics view dpi
01542   double scaleFactor = 1.0 / mMapCanvas->logicalDpiX() * 25.4;
01543 
01544   double itemX, itemY;
01545   QGraphicsItem* parent = item->parentItem();
01546   if ( !parent )
01547   {
01548     QPointF mapPos = composerMapPosForItem( item );
01549     itemX = mapPos.x();
01550     itemY = mapPos.y();
01551   }
01552   else //place item relative to the parent item
01553   {
01554     QPointF itemScenePos = item->scenePos();
01555     QPointF parentScenePos = parent->scenePos();
01556 
01557     QPointF mapPos = composerMapPosForItem( parent );
01558 
01559     itemX = mapPos.x() + ( itemScenePos.x() - parentScenePos.x() ) * scaleFactor;
01560     itemY = mapPos.y() + ( itemScenePos.y() - parentScenePos.y() ) * scaleFactor;
01561   }
01562   painter->translate( itemX, itemY );
01563 
01564 
01565   painter->scale( scaleFactor, scaleFactor );
01566 
01567   //a little trick to let the item know that the paint request comes from the composer
01568   item->setData( 0, "composer" );
01569   item->paint( painter, itemStyle, 0 );
01570   item->setData( 0, "" );
01571   painter->restore();
01572 }
01573 
01574 QPointF QgsComposerMap::composerMapPosForItem( const QGraphicsItem* item ) const
01575 {
01576   if ( !item || !mMapCanvas || !mMapRenderer )
01577   {
01578     return QPointF( 0, 0 );
01579   }
01580 
01581   if ( mExtent.height() <= 0 || mExtent.width() <= 0 || mMapCanvas->width() <= 0 || mMapCanvas->height() <= 0 )
01582   {
01583     return QPointF( 0, 0 );
01584   }
01585 
01586   QRectF graphicsSceneRect = mMapCanvas->sceneRect();
01587   QPointF itemScenePos = item->scenePos();
01588   QgsRectangle mapRendererExtent = mMapRenderer->extent();
01589 
01590   double mapX = itemScenePos.x() / graphicsSceneRect.width() * mapRendererExtent.width() + mapRendererExtent.xMinimum();
01591   double mapY = mapRendererExtent.yMaximum() - itemScenePos.y() / graphicsSceneRect.height() * mapRendererExtent.height();
01592   return mapToItemCoords( QPointF( mapX, mapY ) );
01593 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines