QGIS API Documentation  master-59fd5e0
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 #include "qgscomposition.h"
00020 #include "qgscoordinatetransform.h"
00021 #include "qgslogger.h"
00022 #include "qgsmaprenderer.h"
00023 #include "qgsmaplayerregistry.h"
00024 #include "qgsmaptopixel.h"
00025 #include "qgsproject.h"
00026 #include "qgsrasterlayer.h"
00027 #include "qgsrendercontext.h"
00028 #include "qgsscalecalculator.h"
00029 #include "qgsvectorlayer.h"
00030 #include "qgspallabeling.h"
00031 
00032 #include "qgslabel.h"
00033 #include "qgslabelattributes.h"
00034 #include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance
00035 
00036 #include <QGraphicsScene>
00037 #include <QGraphicsView>
00038 #include <QPainter>
00039 #include <QSettings>
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 ),
00044     mOverviewFrameMapId( -1 ), mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
00045     mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationFontColor( QColor( 0, 0, 0 ) ),
00046     mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), mGridBlendMode( QPainter::CompositionMode_SourceOver ),
00047     mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ),
00048     mTopGridAnnotationPosition( OutsideMapFrame ), mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ),
00049     mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ), mTopGridAnnotationDirection( Horizontal ),
00050     mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ),  mGridFrameWidth( 2.0 ),
00051     mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true )
00052 {
00053   mComposition = composition;
00054   mOverviewFrameMapSymbol = 0;
00055   mGridLineSymbol = 0;
00056   createDefaultOverviewFrameSymbol();
00057   createDefaultGridLineSymbol();
00058 
00059   mId = 0;
00060   assignFreeId();
00061 
00062   mMapRenderer = mComposition->mapRenderer();
00063   mPreviewMode = QgsComposerMap::Rectangle;
00064   mCurrentRectangle = rect();
00065 
00066   // Cache
00067   mCacheUpdated = false;
00068   mDrawing = false;
00069 
00070   //Offset
00071   mXOffset = 0.0;
00072   mYOffset = 0.0;
00073 
00074   connectUpdateSlot();
00075 
00076   //calculate mExtent based on width/height ratio and map canvas extent
00077   if ( mMapRenderer )
00078   {
00079     mExtent = mMapRenderer->extent();
00080   }
00081   setSceneRect( QRectF( x, y, width, height ) );
00082   setToolTip( tr( "Map %1" ).arg( mId ) );
00083 
00084   initGridAnnotationFormatFromProject();
00085 }
00086 
00087 QgsComposerMap::QgsComposerMap( QgsComposition *composition )
00088     : QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 ),
00089     mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
00090     mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationFontColor( QColor( 0, 0, 0 ) ),
00091     mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), mGridBlendMode( QPainter::CompositionMode_SourceOver ),
00092     mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ),
00093     mTopGridAnnotationPosition( OutsideMapFrame ), mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ),
00094     mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ), mTopGridAnnotationDirection( Horizontal ),
00095     mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 ), mCrossLength( 3 ),
00096     mMapCanvas( 0 ), mDrawCanvasItems( true )
00097 {
00098   mOverviewFrameMapSymbol = 0;
00099   mGridLineSymbol = 0;
00100   createDefaultOverviewFrameSymbol();
00101 
00102   //Offset
00103   mXOffset = 0.0;
00104   mYOffset = 0.0;
00105 
00106   connectUpdateSlot();
00107 
00108   mComposition = composition;
00109   mMapRenderer = mComposition->mapRenderer();
00110   mId = mComposition->composerMapItems().size();
00111   mPreviewMode = QgsComposerMap::Rectangle;
00112   mCurrentRectangle = rect();
00113 
00114   setToolTip( tr( "Map %1" ).arg( mId ) );
00115 
00116   initGridAnnotationFormatFromProject();
00117 }
00118 
00119 QgsComposerMap::~QgsComposerMap()
00120 {
00121   delete mOverviewFrameMapSymbol;
00122   delete mGridLineSymbol;
00123 }
00124 
00125 /* This function is called by paint() and cache() to render the map.  It does not override any functions
00126 from QGraphicsItem. */
00127 void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSizeF& size, double dpi, double* forceWidthScale )
00128 {
00129   if ( !painter )
00130   {
00131     return;
00132   }
00133 
00134   if ( !mMapRenderer )
00135   {
00136     return;
00137   }
00138 
00139   QgsMapRenderer theMapRenderer;
00140   theMapRenderer.setExtent( extent );
00141   theMapRenderer.setOutputSize( size, dpi );
00142   if ( mMapRenderer->labelingEngine() )
00143     theMapRenderer.setLabelingEngine( mMapRenderer->labelingEngine()->clone() );
00144 
00145   //use stored layer set or read current set from main canvas
00146   if ( mKeepLayerSet )
00147   {
00148     theMapRenderer.setLayerSet( mLayerSet );
00149   }
00150   else
00151   {
00152     theMapRenderer.setLayerSet( mMapRenderer->layerSet() );
00153   }
00154   theMapRenderer.setDestinationCrs( mMapRenderer->destinationCrs() );
00155   theMapRenderer.setProjectionsEnabled( mMapRenderer->hasCrsTransformEnabled() );
00156 
00157   //set antialiasing if enabled in options
00158   QSettings settings;
00159   // Changed to enable anti aliased rendering by default as of QGIS 1.7
00160   if ( settings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
00161   {
00162     painter->setRenderHint( QPainter::Antialiasing );
00163   }
00164 
00165   QgsRenderContext* theRendererContext = theMapRenderer.rendererContext();
00166   if ( theRendererContext )
00167   {
00168     theRendererContext->setDrawEditingInformation( false );
00169     theRendererContext->setRenderingStopped( false );
00170   }
00171 
00172   // force vector output (no caching of marker images etc.)
00173   theRendererContext->setForceVectorOutput( true );
00174 
00175   // make the renderer respect the composition's useAdvancedEffects flag
00176   theRendererContext->setUseAdvancedEffects( mComposition->useAdvancedEffects() );
00177 
00178   //force composer map scale for scale dependent visibility
00179   double bk_scale = theMapRenderer.scale();
00180   theMapRenderer.setScale( scale() );
00181 
00182   //layer caching (as QImages) cannot be done for composer prints
00183   QSettings s;
00184   bool bkLayerCaching = s.value( "/qgis/enable_render_caching", false ).toBool();
00185   s.setValue( "/qgis/enable_render_caching", false );
00186 
00187   if ( forceWidthScale ) //force wysiwyg line widths / marker sizes
00188   {
00189     theMapRenderer.render( painter, forceWidthScale );
00190   }
00191   else
00192   {
00193     theMapRenderer.render( painter );
00194   }
00195   s.setValue( "/qgis/enable_render_caching", bkLayerCaching );
00196 
00197   theMapRenderer.setScale( bk_scale );
00198 }
00199 
00200 void QgsComposerMap::cache( void )
00201 {
00202   if ( mPreviewMode == Rectangle )
00203   {
00204     return;
00205   }
00206 
00207   if ( mDrawing )
00208   {
00209     return;
00210   }
00211 
00212   mDrawing = true;
00213 
00214   //in case of rotation, we need to request a larger rectangle and create a larger cache image
00215   QgsRectangle requestExtent;
00216   requestedExtent( requestExtent );
00217 
00218   double horizontalVScaleFactor = horizontalViewScaleFactor();
00219   if ( horizontalVScaleFactor < 0 )
00220   {
00221     horizontalVScaleFactor = mLastValidViewScaleFactor;
00222   }
00223 
00224   int w = requestExtent.width() * mapUnitsToMM() * horizontalVScaleFactor;
00225   int h = requestExtent.height() * mapUnitsToMM() * horizontalVScaleFactor;
00226 
00227   if ( w > 5000 ) //limit size of image for better performance
00228   {
00229     w = 5000;
00230   }
00231 
00232   if ( h > 5000 )
00233   {
00234     h = 5000;
00235   }
00236 
00237   double forcedWidthScaleFactor = w / requestExtent.width() / mapUnitsToMM();
00238 
00239   mCacheImage = QImage( w, h,  QImage::Format_ARGB32 );
00240 
00241   if ( hasBackground() )
00242   {
00243     //Initially fill image with specified background color. This ensures that layers with blend modes will
00244     //preview correctly
00245     mCacheImage.fill( backgroundColor().rgba() );
00246   }
00247   else
00248   {
00249     //no background, but start with empty fill to avoid artifacts
00250     mCacheImage.fill( QColor( 255, 255, 255, 0 ).rgba() );
00251   }
00252 
00253   double mapUnitsPerPixel = mExtent.width() / w;
00254 
00255   // WARNING: ymax in QgsMapToPixel is device height!!!
00256   QgsMapToPixel transform( mapUnitsPerPixel, h, requestExtent.yMinimum(), requestExtent.xMinimum() );
00257 
00258   QPainter p( &mCacheImage );
00259 
00260   draw( &p, requestExtent, QSizeF( w, h ), mCacheImage.logicalDpiX(), &forcedWidthScaleFactor );
00261   p.end();
00262   mCacheUpdated = true;
00263 
00264   mDrawing = false;
00265 }
00266 
00267 void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
00268 {
00269   Q_UNUSED( pWidget );
00270 
00271   if ( !mComposition || !painter )
00272   {
00273     return;
00274   }
00275 
00276   QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
00277   painter->save();
00278   painter->setClipRect( thisPaintRect );
00279 
00280   if ( mComposition->plotStyle() == QgsComposition::Preview && mPreviewMode == Rectangle )
00281   {
00282     // Fill with background color
00283     drawBackground( painter );
00284     QFont messageFont( "", 12 );
00285     painter->setFont( messageFont );
00286     painter->setPen( QColor( 0, 0, 0 ) );
00287     painter->drawText( thisPaintRect, tr( "Map will be printed here" ) );
00288   }
00289   else if ( mComposition->plotStyle() == QgsComposition::Preview )
00290   {
00291     //draw cached pixmap. This function does not call cache() any more because
00292     //Qt 4.4.0 and 4.4.1 have problems with recursive paintings
00293     //QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
00294     //client functions
00295 
00296     //Background color is already included in cached image, so no need to draw
00297 
00298     QgsRectangle requestRectangle;
00299     requestedExtent( requestRectangle );
00300     double horizontalVScaleFactor = horizontalViewScaleFactor();
00301     if ( horizontalVScaleFactor < 0 )
00302     {
00303       horizontalVScaleFactor = mLastValidViewScaleFactor;
00304     }
00305 
00306     double imagePixelWidth = mExtent.width() / requestRectangle.width() * mCacheImage.width() ; //how many pixels of the image are for the map extent?
00307     double scale = rect().width() / imagePixelWidth;
00308     QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
00309 
00310     //shift such that rotation point is at 0/0 point in the coordinate system
00311     double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
00312     double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
00313 
00314     //shift such that top left point of the extent at point 0/0 in item coordinate system
00315     double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
00316     double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
00317 
00318     painter->save();
00319 
00320     painter->translate( mXOffset, mYOffset );
00321     painter->translate( xTopLeftShift, yTopLeftShift );
00322     painter->rotate( mRotation );
00323     painter->translate( xShiftMM, -yShiftMM );
00324     painter->scale( scale, scale );
00325     painter->drawImage( 0, 0, mCacheImage );
00326 
00327     //restore rotation
00328     painter->restore();
00329 
00330     //draw canvas items
00331     drawCanvasItems( painter, itemStyle );
00332   }
00333   else if ( mComposition->plotStyle() == QgsComposition::Print ||
00334             mComposition->plotStyle() == QgsComposition::Postscript )
00335   {
00336     if ( mDrawing )
00337     {
00338       return;
00339     }
00340 
00341     mDrawing = true;
00342     QPaintDevice* thePaintDevice = painter->device();
00343     if ( !thePaintDevice )
00344     {
00345       return;
00346     }
00347 
00348     // Fill with background color
00349     drawBackground( painter );
00350 
00351     QgsRectangle requestRectangle;
00352     requestedExtent( requestRectangle );
00353 
00354     QSizeF theSize( requestRectangle.width() * mapUnitsToMM(), requestRectangle.height() * mapUnitsToMM() );
00355     QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
00356 
00357     //shift such that rotation point is at 0/0 point in the coordinate system
00358     double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
00359     double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
00360 
00361     //shift such that top left point of the extent at point 0/0 in item coordinate system
00362     double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
00363     double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
00364     painter->save();
00365     painter->translate( mXOffset, mYOffset );
00366     painter->translate( xTopLeftShift, yTopLeftShift );
00367     painter->rotate( mRotation );
00368     painter->translate( xShiftMM, -yShiftMM );
00369     draw( painter, requestRectangle, theSize, 25.4 ); //scene coordinates seem to be in mm
00370 
00371     //restore rotation
00372     painter->restore();
00373 
00374     //draw canvas items
00375     drawCanvasItems( painter, itemStyle );
00376 
00377     mDrawing = false;
00378   }
00379 
00380   painter->setClipRect( thisPaintRect , Qt::NoClip );
00381 
00382   if ( mGridEnabled )
00383   {
00384     drawGrid( painter );
00385   }
00386   drawFrame( painter );
00387   if ( isSelected() )
00388   {
00389     drawSelectionBoxes( painter );
00390   }
00391 
00392   if ( mOverviewFrameMapId != -1 )
00393   {
00394     drawOverviewMapExtent( painter );
00395   }
00396 
00397   painter->restore();
00398 }
00399 
00400 void QgsComposerMap::updateCachedImage( void )
00401 {
00402   syncLayerSet(); //layer list may have changed
00403   mCacheUpdated = false;
00404   cache();
00405   QGraphicsRectItem::update();
00406 }
00407 
00408 void QgsComposerMap::renderModeUpdateCachedImage()
00409 {
00410   if ( mPreviewMode == Render )
00411   {
00412     updateCachedImage();
00413   }
00414 }
00415 
00416 void QgsComposerMap::setCacheUpdated( bool u )
00417 {
00418   mCacheUpdated = u;
00419 }
00420 
00421 double QgsComposerMap::scale() const
00422 {
00423   QgsScaleCalculator calculator;
00424   calculator.setMapUnits( mMapRenderer->mapUnits() );
00425   calculator.setDpi( 25.4 );  //QGraphicsView units are mm
00426   return calculator.calculate( mExtent, rect().width() );
00427 }
00428 
00429 void QgsComposerMap::resize( double dx, double dy )
00430 {
00431   //setRect
00432   QRectF currentRect = rect();
00433   QRectF newSceneRect = QRectF( transform().dx(), transform().dy(), currentRect.width() + dx, currentRect.height() + dy );
00434   setSceneRect( newSceneRect );
00435   updateItem();
00436 }
00437 
00438 void QgsComposerMap::moveContent( double dx, double dy )
00439 {
00440   if ( !mDrawing )
00441   {
00442     transformShift( dx, dy );
00443     mExtent.setXMinimum( mExtent.xMinimum() + dx );
00444     mExtent.setXMaximum( mExtent.xMaximum() + dx );
00445     mExtent.setYMinimum( mExtent.yMinimum() + dy );
00446     mExtent.setYMaximum( mExtent.yMaximum() + dy );
00447     cache();
00448     update();
00449     emit itemChanged();
00450     emit extentChanged();
00451   }
00452 }
00453 
00454 void QgsComposerMap::zoomContent( int delta, double x, double y )
00455 {
00456   if ( mDrawing )
00457   {
00458     return;
00459   }
00460 
00461   QSettings settings;
00462 
00463   //read zoom mode
00464   //0: zoom, 1: zoom and recenter, 2: zoom to cursor, 3: nothing
00465   int zoomMode = settings.value( "/qgis/wheel_action", 2 ).toInt();
00466   if ( zoomMode == 3 ) //do nothing
00467   {
00468     return;
00469   }
00470 
00471   double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
00472 
00473   //find out new center point
00474   double centerX = ( mExtent.xMaximum() + mExtent.xMinimum() ) / 2;
00475   double centerY = ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2;
00476 
00477   if ( zoomMode != 0 )
00478   {
00479     //find out map coordinates of mouse position
00480     double mapMouseX = mExtent.xMinimum() + ( x / rect().width() ) * ( mExtent.xMaximum() - mExtent.xMinimum() );
00481     double mapMouseY = mExtent.yMinimum() + ( 1 - ( y / rect().height() ) ) * ( mExtent.yMaximum() - mExtent.yMinimum() );
00482     if ( zoomMode == 1 ) //zoom and recenter
00483     {
00484       centerX = mapMouseX;
00485       centerY = mapMouseY;
00486     }
00487     else if ( zoomMode == 2 ) //zoom to cursor
00488     {
00489       centerX = mapMouseX + ( centerX - mapMouseX ) * ( 1.0 / zoomFactor );
00490       centerY = mapMouseY + ( centerY - mapMouseY ) * ( 1.0 / zoomFactor );
00491     }
00492   }
00493 
00494   double newIntervalX, newIntervalY;
00495 
00496   if ( delta > 0 )
00497   {
00498     newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) / zoomFactor;
00499     newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) / zoomFactor;
00500   }
00501   else if ( delta < 0 )
00502   {
00503     newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) * zoomFactor;
00504     newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) * zoomFactor;
00505   }
00506   else //no need to zoom
00507   {
00508     return;
00509   }
00510 
00511   mExtent.setXMaximum( centerX + newIntervalX / 2 );
00512   mExtent.setXMinimum( centerX - newIntervalX / 2 );
00513   mExtent.setYMaximum( centerY + newIntervalY / 2 );
00514   mExtent.setYMinimum( centerY - newIntervalY / 2 );
00515 
00516   cache();
00517   update();
00518   emit itemChanged();
00519   emit extentChanged();
00520 }
00521 
00522 void QgsComposerMap::setSceneRect( const QRectF& rectangle )
00523 {
00524   double w = rectangle.width();
00525   double h = rectangle.height();
00526   //prepareGeometryChange();
00527 
00528   QgsComposerItem::setSceneRect( rectangle );
00529 
00530   //QGraphicsRectItem::update();
00531   double newHeight = mExtent.width() * h / w ;
00532   mExtent = QgsRectangle( mExtent.xMinimum(), mExtent.yMinimum(), mExtent.xMaximum(), mExtent.yMinimum() + newHeight );
00533   mCacheUpdated = false;
00534 
00535   updateBoundingRect();
00536   update();
00537   emit itemChanged();
00538   emit extentChanged();
00539 }
00540 
00541 void QgsComposerMap::setNewExtent( const QgsRectangle& extent )
00542 {
00543   if ( mExtent == extent )
00544   {
00545     return;
00546   }
00547   mExtent = extent;
00548 
00549   //adjust height
00550   QRectF currentRect = rect();
00551 
00552   double newHeight = currentRect.width() * extent.height() / extent.width();
00553 
00554   setSceneRect( QRectF( transform().dx(), transform().dy(), currentRect.width(), newHeight ) );
00555   updateItem();
00556 }
00557 
00558 void QgsComposerMap::setNewScale( double scaleDenominator )
00559 {
00560   double currentScaleDenominator = scale();
00561 
00562   if ( scaleDenominator == currentScaleDenominator )
00563   {
00564     return;
00565   }
00566 
00567   double scaleRatio = scaleDenominator / currentScaleDenominator;
00568   mExtent.scale( scaleRatio );
00569   mCacheUpdated = false;
00570   cache();
00571   update();
00572   emit itemChanged();
00573   emit extentChanged();
00574 }
00575 
00576 void QgsComposerMap::setPreviewMode( PreviewMode m )
00577 {
00578   mPreviewMode = m;
00579   emit itemChanged();
00580 }
00581 
00582 void QgsComposerMap::setOffset( double xOffset, double yOffset )
00583 {
00584   mXOffset = xOffset;
00585   mYOffset = yOffset;
00586 }
00587 
00588 void QgsComposerMap::setMapRotation( double r )
00589 {
00590   setRotation( r );
00591   emit rotationChanged( r );
00592   emit itemChanged();
00593 }
00594 
00595 void QgsComposerMap::updateItem()
00596 {
00597   if ( mPreviewMode != QgsComposerMap::Rectangle &&  !mCacheUpdated )
00598   {
00599     cache();
00600   }
00601   QgsComposerItem::updateItem();
00602 }
00603 
00604 bool QgsComposerMap::containsWMSLayer() const
00605 {
00606   if ( !mMapRenderer )
00607   {
00608     return false;
00609   }
00610 
00611   QStringList layers = mMapRenderer->layerSet();
00612 
00613   QStringList::const_iterator layer_it = layers.constBegin();
00614   QgsMapLayer* currentLayer = 0;
00615 
00616   for ( ; layer_it != layers.constEnd(); ++layer_it )
00617   {
00618     currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
00619     if ( currentLayer )
00620     {
00621       QgsRasterLayer* currentRasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
00622       if ( currentRasterLayer )
00623       {
00624         const QgsRasterDataProvider* rasterProvider = 0;
00625         if (( rasterProvider = currentRasterLayer->dataProvider() ) )
00626         {
00627           if ( rasterProvider->name() == "wms" )
00628           {
00629             return true;
00630           }
00631         }
00632       }
00633     }
00634   }
00635   return false;
00636 }
00637 
00638 bool QgsComposerMap::containsAdvancedEffects() const
00639 {
00640   // check if map contains advanced effects like blend modes, or flattened layers for transparency
00641   if ( !mMapRenderer )
00642   {
00643     return false;
00644   }
00645 
00646   QStringList layers = mMapRenderer->layerSet();
00647 
00648   //Also need to check PAL labeling for blend modes
00649   QgsPalLabeling* lbl = dynamic_cast<QgsPalLabeling*>( mMapRenderer->labelingEngine() );
00650 
00651   QStringList::const_iterator layer_it = layers.constBegin();
00652   QgsMapLayer* currentLayer = 0;
00653 
00654   for ( ; layer_it != layers.constEnd(); ++layer_it )
00655   {
00656     currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
00657     if ( currentLayer )
00658     {
00659       if ( currentLayer->blendMode() != QPainter::CompositionMode_SourceOver )
00660       {
00661         return true;
00662       }
00663       // if vector layer, check labels and feature blend mode
00664       QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
00665       if ( currentVectorLayer )
00666       {
00667         if ( currentVectorLayer->layerTransparency() != 0 )
00668         {
00669           return true;
00670         }
00671         if ( currentVectorLayer->featureBlendMode() != QPainter::CompositionMode_SourceOver )
00672         {
00673           return true;
00674         }
00675         // check label blend modes
00676         if ( lbl->willUseLayer( currentVectorLayer ) )
00677         {
00678           // Check all label blending properties
00679           QgsPalLayerSettings& layerSettings = lbl->layer( currentVectorLayer->id() );
00680           if (( layerSettings.blendMode != QPainter::CompositionMode_SourceOver ) ||
00681               ( layerSettings.bufferSize != 0 && layerSettings.bufferBlendMode != QPainter::CompositionMode_SourceOver ) ||
00682               ( layerSettings.shadowDraw && layerSettings.shadowBlendMode != QPainter::CompositionMode_SourceOver ) ||
00683               ( layerSettings.shapeDraw && layerSettings.shapeBlendMode != QPainter::CompositionMode_SourceOver ) )
00684           {
00685             return true;
00686           }
00687         }
00688       }
00689     }
00690   }
00691 
00692   return false;
00693 }
00694 
00695 void QgsComposerMap::connectUpdateSlot()
00696 {
00697   //connect signal from layer registry to update in case of new or deleted layers
00698   QgsMapLayerRegistry* layerRegistry = QgsMapLayerRegistry::instance();
00699   if ( layerRegistry )
00700   {
00701     connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( updateCachedImage() ) );
00702     connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( updateCachedImage() ) );
00703   }
00704 }
00705 
00706 bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
00707 {
00708   if ( elem.isNull() )
00709   {
00710     return false;
00711   }
00712 
00713   QDomElement composerMapElem = doc.createElement( "ComposerMap" );
00714   composerMapElem.setAttribute( "id", mId );
00715 
00716   //previewMode
00717   if ( mPreviewMode == Cache )
00718   {
00719     composerMapElem.setAttribute( "previewMode", "Cache" );
00720   }
00721   else if ( mPreviewMode == Render )
00722   {
00723     composerMapElem.setAttribute( "previewMode", "Render" );
00724   }
00725   else //rectangle
00726   {
00727     composerMapElem.setAttribute( "previewMode", "Rectangle" );
00728   }
00729 
00730   if ( mKeepLayerSet )
00731   {
00732     composerMapElem.setAttribute( "keepLayerSet", "true" );
00733   }
00734   else
00735   {
00736     composerMapElem.setAttribute( "keepLayerSet", "false" );
00737   }
00738 
00739   if ( mDrawCanvasItems )
00740   {
00741     composerMapElem.setAttribute( "drawCanvasItems", "true" );
00742   }
00743   else
00744   {
00745     composerMapElem.setAttribute( "drawCanvasItems", "false" );
00746   }
00747 
00748   //overview map frame
00749   QDomElement overviewFrameElem = doc.createElement( "overviewFrame" );
00750   overviewFrameElem.setAttribute( "overviewFrameMap", mOverviewFrameMapId );
00751   overviewFrameElem.setAttribute( "overviewBlendMode", QgsMapRenderer::getBlendModeEnum( mOverviewBlendMode ) );
00752   if ( mOverviewInverted )
00753   {
00754     overviewFrameElem.setAttribute( "overviewInverted", "true" );
00755   }
00756   else
00757   {
00758     overviewFrameElem.setAttribute( "overviewInverted", "false" );
00759   }
00760   QDomElement overviewFrameStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mOverviewFrameMapSymbol, doc );
00761   overviewFrameElem.appendChild( overviewFrameStyleElem );
00762   composerMapElem.appendChild( overviewFrameElem );
00763 
00764 
00765   //extent
00766   QDomElement extentElem = doc.createElement( "Extent" );
00767   extentElem.setAttribute( "xmin", QString::number( mExtent.xMinimum() ) );
00768   extentElem.setAttribute( "xmax", QString::number( mExtent.xMaximum() ) );
00769   extentElem.setAttribute( "ymin", QString::number( mExtent.yMinimum() ) );
00770   extentElem.setAttribute( "ymax", QString::number( mExtent.yMaximum() ) );
00771   composerMapElem.appendChild( extentElem );
00772 
00773   //layer set
00774   QDomElement layerSetElem = doc.createElement( "LayerSet" );
00775   QStringList::const_iterator layerIt = mLayerSet.constBegin();
00776   for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
00777   {
00778     QDomElement layerElem = doc.createElement( "Layer" );
00779     QDomText layerIdText = doc.createTextNode( *layerIt );
00780     layerElem.appendChild( layerIdText );
00781     layerSetElem.appendChild( layerElem );
00782   }
00783   composerMapElem.appendChild( layerSetElem );
00784 
00785   //overview map frame
00786   composerMapElem.setAttribute( "overviewFrameMap", mOverviewFrameMapId );
00787 
00788   //grid
00789   QDomElement gridElem = doc.createElement( "Grid" );
00790   gridElem.setAttribute( "show", mGridEnabled );
00791   gridElem.setAttribute( "gridStyle", mGridStyle );
00792   gridElem.setAttribute( "intervalX", QString::number( mGridIntervalX ) );
00793   gridElem.setAttribute( "intervalY", QString::number( mGridIntervalY ) );
00794   gridElem.setAttribute( "offsetX", QString::number( mGridOffsetX ) );
00795   gridElem.setAttribute( "offsetY", QString::number( mGridOffsetY ) );
00796   gridElem.setAttribute( "crossLength",  QString::number( mCrossLength ) );
00797   gridElem.setAttribute( "gridFrameStyle", mGridFrameStyle );
00798   gridElem.setAttribute( "gridFrameWidth", QString::number( mGridFrameWidth ) );
00799   gridElem.setAttribute( "gridBlendMode", QgsMapRenderer::getBlendModeEnum( mGridBlendMode ) );
00800   QDomElement gridLineStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mGridLineSymbol, doc );
00801   gridElem.appendChild( gridLineStyleElem );
00802 
00803   //grid annotation
00804   QDomElement annotationElem = doc.createElement( "Annotation" );
00805   annotationElem.setAttribute( "format", mGridAnnotationFormat );
00806   annotationElem.setAttribute( "show", mShowGridAnnotation );
00807   annotationElem.setAttribute( "leftPosition", mLeftGridAnnotationPosition );
00808   annotationElem.setAttribute( "rightPosition", mRightGridAnnotationPosition );
00809   annotationElem.setAttribute( "topPosition", mTopGridAnnotationPosition );
00810   annotationElem.setAttribute( "bottomPosition", mBottomGridAnnotationPosition );
00811   annotationElem.setAttribute( "leftDirection", mLeftGridAnnotationDirection );
00812   annotationElem.setAttribute( "rightDirection", mRightGridAnnotationDirection );
00813   annotationElem.setAttribute( "topDirection", mTopGridAnnotationDirection );
00814   annotationElem.setAttribute( "bottomDirection", mBottomGridAnnotationDirection );
00815   annotationElem.setAttribute( "frameDistance",  QString::number( mAnnotationFrameDistance ) );
00816   annotationElem.setAttribute( "font", mGridAnnotationFont.toString() );
00817   annotationElem.setAttribute( "precision", mGridAnnotationPrecision );
00818   //annotation font color
00819   QDomElement annotationFontColorElem = doc.createElement( "fontColor" );
00820   annotationFontColorElem.setAttribute( "red", mGridAnnotationFontColor.red() );
00821   annotationFontColorElem.setAttribute( "green", mGridAnnotationFontColor.green() );
00822   annotationFontColorElem.setAttribute( "blue", mGridAnnotationFontColor.blue() );
00823   annotationElem.appendChild( annotationFontColorElem );
00824 
00825   gridElem.appendChild( annotationElem );
00826   composerMapElem.appendChild( gridElem );
00827 
00828   elem.appendChild( composerMapElem );
00829   return _writeXML( composerMapElem, doc );
00830 }
00831 
00832 bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& doc )
00833 {
00834   if ( itemElem.isNull() )
00835   {
00836     return false;
00837   }
00838 
00839   QString idRead = itemElem.attribute( "id", "not found" );
00840   if ( idRead != "not found" )
00841   {
00842     mId = idRead.toInt();
00843   }
00844   mPreviewMode = Rectangle;
00845 
00846   //previewMode
00847   QString previewMode = itemElem.attribute( "previewMode" );
00848   if ( previewMode == "Cache" )
00849   {
00850     mPreviewMode = Cache;
00851   }
00852   else if ( previewMode == "Render" )
00853   {
00854     mPreviewMode = Render;
00855   }
00856   else
00857   {
00858     mPreviewMode = Rectangle;
00859   }
00860 
00861   QDomElement overviewFrameElem = itemElem.firstChildElement( "overviewFrame" );
00862   if ( !overviewFrameElem.isNull() )
00863   {
00864     setOverviewFrameMap( overviewFrameElem.attribute( "overviewFrameMap", "-1" ).toInt() );
00865     setOverviewBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) overviewFrameElem.attribute( "overviewBlendMode", "0" ).toUInt() ) );
00866 
00867     QString overviewInvertedFlag = overviewFrameElem.attribute( "overviewInverted" );
00868     if ( overviewInvertedFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
00869     {
00870       setOverviewInverted( true );
00871     }
00872     else
00873     {
00874       setOverviewInverted( false );
00875     }
00876 
00877     QDomElement overviewFrameSymbolElem = overviewFrameElem.firstChildElement( "symbol" );
00878     if ( !overviewFrameSymbolElem.isNull() )
00879     {
00880       delete mOverviewFrameMapSymbol;
00881       mOverviewFrameMapSymbol = dynamic_cast<QgsFillSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( overviewFrameSymbolElem ) );
00882     }
00883   }
00884 
00885   //extent
00886   QDomNodeList extentNodeList = itemElem.elementsByTagName( "Extent" );
00887   if ( extentNodeList.size() > 0 )
00888   {
00889     QDomElement extentElem = extentNodeList.at( 0 ).toElement();
00890     double xmin, xmax, ymin, ymax;
00891     xmin = extentElem.attribute( "xmin" ).toDouble();
00892     xmax = extentElem.attribute( "xmax" ).toDouble();
00893     ymin = extentElem.attribute( "ymin" ).toDouble();
00894     ymax = extentElem.attribute( "ymax" ).toDouble();
00895 
00896     mExtent = QgsRectangle( xmin, ymin, xmax, ymax );
00897   }
00898 
00899   //mKeepLayerSet flag
00900   QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
00901   if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
00902   {
00903     mKeepLayerSet = true;
00904   }
00905   else
00906   {
00907     mKeepLayerSet = false;
00908   }
00909 
00910   QString drawCanvasItemsFlag = itemElem.attribute( "drawCanvasItems" );
00911   if ( drawCanvasItemsFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
00912   {
00913     mDrawCanvasItems = true;
00914   }
00915   else
00916   {
00917     mDrawCanvasItems = false;
00918   }
00919 
00920   //mLayerSet
00921   QDomNodeList layerSetNodeList = itemElem.elementsByTagName( "LayerSet" );
00922   QStringList layerSet;
00923   if ( layerSetNodeList.size() > 0 )
00924   {
00925     QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
00926     QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( "Layer" );
00927     for ( int i = 0; i < layerIdNodeList.size(); ++i )
00928     {
00929       layerSet << layerIdNodeList.at( i ).toElement().text();
00930     }
00931   }
00932   mLayerSet = layerSet;
00933 
00934   mDrawing = false;
00935   mNumCachedLayers = 0;
00936   mCacheUpdated = false;
00937 
00938   //grid
00939   QDomNodeList gridNodeList = itemElem.elementsByTagName( "Grid" );
00940   if ( gridNodeList.size() > 0 )
00941   {
00942     QDomElement gridElem = gridNodeList.at( 0 ).toElement();
00943     mGridEnabled = ( gridElem.attribute( "show", "0" ) != "0" );
00944     mGridStyle = QgsComposerMap::GridStyle( gridElem.attribute( "gridStyle", "0" ).toInt() );
00945     mGridIntervalX = gridElem.attribute( "intervalX", "0" ).toDouble();
00946     mGridIntervalY = gridElem.attribute( "intervalY", "0" ).toDouble();
00947     mGridOffsetX = gridElem.attribute( "offsetX", "0" ).toDouble();
00948     mGridOffsetY = gridElem.attribute( "offsetY", "0" ).toDouble();
00949     mCrossLength = gridElem.attribute( "crossLength", "3" ).toDouble();
00950     mGridFrameStyle = ( QgsComposerMap::GridFrameStyle )gridElem.attribute( "gridFrameStyle", "0" ).toInt();
00951     mGridFrameWidth = gridElem.attribute( "gridFrameWidth", "2.0" ).toDouble();
00952     setGridBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) gridElem.attribute( "gridBlendMode", "0" ).toUInt() ) );
00953 
00954     QDomElement gridSymbolElem = gridElem.firstChildElement( "symbol" );
00955     delete mGridLineSymbol;
00956     if ( gridSymbolElem.isNull( ) )
00957     {
00958       //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
00959       mGridLineSymbol = QgsLineSymbolV2::createSimple( QgsStringMap() );
00960       mGridLineSymbol->setWidth( gridElem.attribute( "penWidth", "0" ).toDouble() );
00961       mGridLineSymbol->setColor( QColor( gridElem.attribute( "penColorRed", "0" ).toInt(),
00962                                          gridElem.attribute( "penColorGreen", "0" ).toInt(),
00963                                          gridElem.attribute( "penColorBlue", "0" ).toInt() ) );
00964     }
00965     else
00966     {
00967       mGridLineSymbol = dynamic_cast<QgsLineSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( gridSymbolElem ) );
00968     }
00969 
00970     QDomNodeList annotationNodeList = gridElem.elementsByTagName( "Annotation" );
00971     if ( annotationNodeList.size() > 0 )
00972     {
00973       QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
00974       mShowGridAnnotation = ( annotationElem.attribute( "show", "0" ) != "0" );
00975       mGridAnnotationFormat = QgsComposerMap::GridAnnotationFormat( annotationElem.attribute( "format", "0" ).toInt() );
00976       mLeftGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "leftPosition", "0" ).toInt() );
00977       mRightGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "rightPosition", "0" ).toInt() );
00978       mTopGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "topPosition", "0" ).toInt() );
00979       mBottomGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "bottomPosition", "0" ).toInt() );
00980       mLeftGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "leftDirection", "0" ).toInt() );
00981       mRightGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "rightDirection", "0" ).toInt() );
00982       mTopGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "topDirection", "0" ).toInt() );
00983       mBottomGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "bottomDirection", "0" ).toInt() );
00984       mAnnotationFrameDistance = annotationElem.attribute( "frameDistance", "0" ).toDouble();
00985       mGridAnnotationFont.fromString( annotationElem.attribute( "font", "" ) );
00986 
00987       //annotation font color
00988       QDomNodeList annotationFontColorList = annotationElem.elementsByTagName( "fontColor" );
00989       if ( annotationFontColorList.size() > 0 )
00990       {
00991         QDomElement fontColorElem = annotationFontColorList.at( 0 ).toElement();
00992         int red = fontColorElem.attribute( "red", "0" ).toInt();
00993         int green = fontColorElem.attribute( "green", "0" ).toInt();
00994         int blue = fontColorElem.attribute( "blue", "0" ).toInt();
00995         mGridAnnotationFontColor = QColor( red, green, blue );
00996       }
00997       else
00998       {
00999         mGridAnnotationFontColor = QColor( 0, 0, 0 );
01000       }
01001 
01002       mGridAnnotationPrecision = annotationElem.attribute( "precision", "3" ).toInt();
01003     }
01004   }
01005 
01006   //restore general composer item properties
01007   QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
01008   if ( composerItemList.size() > 0 )
01009   {
01010     QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
01011     _readXML( composerItemElem, doc );
01012   }
01013 
01014   updateBoundingRect();
01015   emit itemChanged();
01016   return true;
01017 }
01018 
01019 void QgsComposerMap::storeCurrentLayerSet()
01020 {
01021   if ( mMapRenderer )
01022   {
01023     mLayerSet = mMapRenderer->layerSet();
01024   }
01025 }
01026 
01027 void QgsComposerMap::syncLayerSet()
01028 {
01029   if ( mLayerSet.size() < 1 && !mMapRenderer )
01030   {
01031     return;
01032   }
01033 
01034   //if layer set is fixed, do a lookup in the layer registry to also find the non-visible layers
01035   QStringList currentLayerSet;
01036   if ( mKeepLayerSet )
01037   {
01038     currentLayerSet = QgsMapLayerRegistry::instance()->mapLayers().uniqueKeys();
01039   }
01040   else //only consider layers visible in the map
01041   {
01042     currentLayerSet = mMapRenderer->layerSet();
01043   }
01044 
01045   for ( int i = mLayerSet.size() - 1; i >= 0; --i )
01046   {
01047     if ( !currentLayerSet.contains( mLayerSet.at( i ) ) )
01048     {
01049       mLayerSet.removeAt( i );
01050     }
01051   }
01052 }
01053 
01054 void QgsComposerMap::drawGrid( QPainter* p )
01055 {
01056   QList< QPair< double, QLineF > > verticalLines;
01057   yGridLines( verticalLines );
01058   QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
01059   QList< QPair< double, QLineF > > horizontalLines;
01060   xGridLines( horizontalLines );
01061   QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
01062 
01063   QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
01064   p->setClipRect( thisPaintRect );
01065 
01066   // set the blend mode for drawing grid lines
01067   p->save();
01068   p->setCompositionMode( mGridBlendMode );
01069 
01070   //simpler approach: draw vertical lines first, then horizontal ones
01071   if ( mGridStyle == QgsComposerMap::Solid )
01072   {
01073     for ( ; vIt != verticalLines.constEnd(); ++vIt )
01074     {
01075       drawGridLine( vIt->second, p );
01076     }
01077 
01078     for ( ; hIt != horizontalLines.constEnd(); ++hIt )
01079     {
01080       drawGridLine( hIt->second, p );
01081     }
01082   }
01083   else //cross
01084   {
01085     QPointF intersectionPoint, crossEnd1, crossEnd2;
01086     for ( ; vIt != verticalLines.constEnd(); ++vIt )
01087     {
01088       //start mark
01089       crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( vIt->second.p1(), vIt->second.p2(), mCrossLength );
01090       drawGridLine( QLineF( vIt->second.p1(), crossEnd1 ), p );
01091 
01092       //test for intersection with every horizontal line
01093       hIt = horizontalLines.constBegin();
01094       for ( ; hIt != horizontalLines.constEnd(); ++hIt )
01095       {
01096         if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
01097         {
01098           crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength );
01099           crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength );
01100           drawGridLine( QLineF( crossEnd1, crossEnd2 ), p );
01101         }
01102       }
01103       //end mark
01104       QPointF crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( vIt->second.p2(), vIt->second.p1(), mCrossLength );
01105       drawGridLine( QLineF( vIt->second.p2(), crossEnd2 ), p );
01106     }
01107 
01108     hIt = horizontalLines.constBegin();
01109     for ( ; hIt != horizontalLines.constEnd(); ++hIt )
01110     {
01111       //start mark
01112       crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( hIt->second.p1(), hIt->second.p2(), mCrossLength );
01113       drawGridLine( QLineF( hIt->second.p1(), crossEnd1 ), p );
01114 
01115       vIt = verticalLines.constBegin();
01116       for ( ; vIt != verticalLines.constEnd(); ++vIt )
01117       {
01118         if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
01119         {
01120           crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength );
01121           crossEnd2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength );
01122           drawGridLine( QLineF( crossEnd1, crossEnd2 ), p );
01123         }
01124       }
01125       //end mark
01126       crossEnd1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( hIt->second.p2(), hIt->second.p1(), mCrossLength );
01127       drawGridLine( QLineF( hIt->second.p2(), crossEnd1 ), p );
01128     }
01129   }
01130   // reset composition mode
01131   p->restore();
01132 
01133   p->setClipRect( thisPaintRect , Qt::NoClip );
01134 
01135   if ( mGridFrameStyle != QgsComposerMap::NoGridFrame )
01136   {
01137     drawGridFrame( p, horizontalLines, verticalLines );
01138   }
01139 
01140   if ( mShowGridAnnotation )
01141   {
01142     drawCoordinateAnnotations( p, horizontalLines, verticalLines );
01143   }
01144 
01145 }
01146 
01147 void QgsComposerMap::drawGridFrame( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines )
01148 {
01149   //Sort the coordinate positions for each side
01150   QMap< double, double > leftGridFrame;
01151   QMap< double, double > rightGridFrame;
01152   QMap< double, double > topGridFrame;
01153   QMap< double, double > bottomGridFrame;
01154 
01155   sortGridLinesOnBorders( hLines, vLines, leftGridFrame, rightGridFrame, topGridFrame, bottomGridFrame );
01156 
01157   drawGridFrameBorder( p, leftGridFrame, QgsComposerMap::Left );
01158   drawGridFrameBorder( p, rightGridFrame, QgsComposerMap::Right );
01159   drawGridFrameBorder( p, topGridFrame, QgsComposerMap::Top );
01160   drawGridFrameBorder( p, bottomGridFrame, QgsComposerMap::Bottom );
01161 }
01162 
01163 void QgsComposerMap::drawGridLine( const QLineF& line, QPainter* p )
01164 {
01165   if ( !mGridLineSymbol || !p )
01166   {
01167     return;
01168   }
01169 
01170   //setup render context
01171   QgsRenderContext context;
01172   context.setPainter( p );
01173   if ( mPreviewMode == Rectangle )
01174   {
01175     return;
01176   }
01177   else
01178   {
01179     context.setScaleFactor( 1.0 );
01180     context.setRasterScaleFactor( mComposition->printResolution() / 25.4 );
01181   }
01182 
01183   QPolygonF poly;
01184   poly << line.p1() << line.p2();
01185   mGridLineSymbol->startRender( context );
01186   mGridLineSymbol->renderPolyline( poly, 0, context );
01187   mGridLineSymbol->stopRender( context );
01188 }
01189 
01190 void QgsComposerMap::drawGridFrameBorder( QPainter* p, const QMap< double, double >& borderPos, Border border )
01191 {
01192   double currentCoord = - mGridFrameWidth;
01193   bool white = true;
01194   double x = 0;
01195   double y = 0;
01196   double width = 0;
01197   double height = 0;
01198 
01199   QMap< double, double > pos = borderPos;
01200   pos.insert( 0, 0 );
01201   if ( border == Left || border == Right )
01202   {
01203     pos.insert( rect().height(), rect().height() );
01204     pos.insert( rect().height() + mGridFrameWidth, rect().height() + mGridFrameWidth );
01205   }
01206   else //top or bottom
01207   {
01208     pos.insert( rect().width(), rect().width() );
01209     pos.insert( rect().width() + mGridFrameWidth, rect().width() + mGridFrameWidth );
01210   }
01211 
01212   QMap< double, double >::const_iterator posIt = pos.constBegin();
01213   for ( ; posIt != pos.constEnd(); ++posIt )
01214   {
01215     p->setBrush( QBrush( white ? Qt::white : Qt::black ) );
01216     if ( border == Left || border == Right )
01217     {
01218       height = posIt.key() - currentCoord;
01219       width = mGridFrameWidth;
01220       x = ( border == Left ) ? -mGridFrameWidth : rect().width();
01221       y = currentCoord;
01222     }
01223     else //top or bottom
01224     {
01225       height = mGridFrameWidth;
01226       width = posIt.key() - currentCoord;
01227       x = currentCoord;
01228       y = ( border == Top ) ? -mGridFrameWidth : rect().height();
01229     }
01230     p->drawRect( QRectF( x, y, width, height ) );
01231     currentCoord = posIt.key();
01232     white = !white;
01233   }
01234 }
01235 
01236 void QgsComposerMap::drawCoordinateAnnotations( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines )
01237 {
01238   if ( !p )
01239   {
01240     return;
01241   }
01242 
01243 
01244   QString currentAnnotationString;
01245   QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
01246   for ( ; it != hLines.constEnd(); ++it )
01247   {
01248     currentAnnotationString = gridAnnotationString( it->first, Latitude );
01249     drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
01250     drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
01251   }
01252 
01253   it = vLines.constBegin();
01254   for ( ; it != vLines.constEnd(); ++it )
01255   {
01256     currentAnnotationString =  gridAnnotationString( it->first, Longitude );
01257     drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
01258     drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
01259   }
01260 }
01261 
01262 void QgsComposerMap::drawCoordinateAnnotation( QPainter* p, const QPointF& pos, QString annotationString )
01263 {
01264   Border frameBorder = borderForLineCoord( pos );
01265   double textWidth = textWidthMillimeters( mGridAnnotationFont, annotationString );
01266   //relevant for annotations is the height of digits
01267   double textHeight = fontHeightCharacterMM( mGridAnnotationFont, QChar( '0' ) );
01268   double xpos = pos.x();
01269   double ypos = pos.y();
01270   int rotation = 0;
01271 
01272   double gridFrameDistance = ( mGridFrameStyle == NoGridFrame ) ? 0 : mGridFrameWidth;
01273 
01274   if ( frameBorder == Left )
01275   {
01276 
01277     if ( mLeftGridAnnotationPosition == InsideMapFrame )
01278     {
01279       if ( mLeftGridAnnotationDirection == Vertical || mLeftGridAnnotationDirection == BoundaryDirection )
01280       {
01281         xpos += textHeight + mAnnotationFrameDistance;
01282         ypos += textWidth / 2.0;
01283         rotation = 270;
01284       }
01285       else
01286       {
01287         xpos += mAnnotationFrameDistance;
01288         ypos += textHeight / 2.0;
01289       }
01290     }
01291     else if ( mLeftGridAnnotationPosition == OutsideMapFrame ) //Outside map frame
01292     {
01293       if ( mLeftGridAnnotationDirection == Vertical || mLeftGridAnnotationDirection == BoundaryDirection )
01294       {
01295         xpos -= ( mAnnotationFrameDistance + gridFrameDistance );
01296         ypos += textWidth / 2.0;
01297         rotation = 270;
01298       }
01299       else
01300       {
01301         xpos -= ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
01302         ypos += textHeight / 2.0;
01303       }
01304     }
01305     else
01306     {
01307       return;
01308     }
01309 
01310   }
01311   else if ( frameBorder == Right )
01312   {
01313     if ( mRightGridAnnotationPosition == InsideMapFrame )
01314     {
01315       if ( mRightGridAnnotationDirection == Vertical || mRightGridAnnotationDirection == BoundaryDirection )
01316       {
01317         xpos -= mAnnotationFrameDistance;
01318         ypos += textWidth / 2.0;
01319         rotation = 270;
01320       }
01321       else
01322       {
01323         xpos -= textWidth + mAnnotationFrameDistance;
01324         ypos += textHeight / 2.0;
01325       }
01326     }
01327     else if ( mRightGridAnnotationPosition == OutsideMapFrame )//OutsideMapFrame
01328     {
01329       if ( mRightGridAnnotationDirection == Vertical || mRightGridAnnotationDirection == BoundaryDirection )
01330       {
01331         xpos += ( textHeight + mAnnotationFrameDistance + gridFrameDistance );
01332         ypos += textWidth / 2.0;
01333         rotation = 270;
01334       }
01335       else //Horizontal
01336       {
01337         xpos += ( mAnnotationFrameDistance + gridFrameDistance );
01338         ypos += textHeight / 2.0;
01339       }
01340     }
01341     else
01342     {
01343       return;
01344     }
01345   }
01346   else if ( frameBorder == Bottom )
01347   {
01348     if ( mBottomGridAnnotationPosition == InsideMapFrame )
01349     {
01350       if ( mBottomGridAnnotationDirection == Horizontal || mBottomGridAnnotationDirection == BoundaryDirection )
01351       {
01352         ypos -= mAnnotationFrameDistance;
01353         xpos -= textWidth / 2.0;
01354       }
01355       else //Vertical
01356       {
01357         xpos += textHeight / 2.0;
01358         ypos -= mAnnotationFrameDistance;
01359         rotation = 270;
01360       }
01361     }
01362     else if ( mBottomGridAnnotationPosition == OutsideMapFrame ) //OutsideMapFrame
01363     {
01364       if ( mBottomGridAnnotationDirection == Horizontal || mBottomGridAnnotationDirection == BoundaryDirection )
01365       {
01366         ypos += ( mAnnotationFrameDistance + textHeight + gridFrameDistance );
01367         xpos -= textWidth / 2.0;
01368       }
01369       else //Vertical
01370       {
01371         xpos += textHeight / 2.0;
01372         ypos += ( textWidth + mAnnotationFrameDistance + gridFrameDistance );
01373         rotation = 270;
01374       }
01375     }
01376     else
01377     {
01378       return;
01379     }
01380   }
01381   else //Top
01382   {
01383     if ( mTopGridAnnotationPosition == InsideMapFrame )
01384     {
01385       if ( mTopGridAnnotationDirection == Horizontal || mTopGridAnnotationDirection == BoundaryDirection )
01386       {
01387         xpos -= textWidth / 2.0;
01388         ypos += textHeight + mAnnotationFrameDistance;
01389       }
01390       else //Vertical
01391       {
01392         xpos += textHeight / 2.0;
01393         ypos += textWidth + mAnnotationFrameDistance;
01394         rotation = 270;
01395       }
01396     }
01397     else if ( mTopGridAnnotationPosition == OutsideMapFrame ) //OutsideMapFrame
01398     {
01399       if ( mTopGridAnnotationDirection == Horizontal || mTopGridAnnotationDirection == BoundaryDirection )
01400       {
01401         xpos -= textWidth / 2.0;
01402         ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
01403       }
01404       else //Vertical
01405       {
01406         xpos += textHeight / 2.0;
01407         ypos -= ( mAnnotationFrameDistance + gridFrameDistance );
01408         rotation = 270;
01409       }
01410     }
01411     else
01412     {
01413       return;
01414     }
01415   }
01416 
01417   drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
01418 }
01419 
01420 void QgsComposerMap::drawAnnotation( QPainter* p, const QPointF& pos, int rotation, const QString& annotationText )
01421 {
01422   p->save();
01423   p->translate( pos );
01424   p->rotate( rotation );
01425   p->setPen( QPen( QColor( mGridAnnotationFontColor ) ) );
01426   drawText( p, 0, 0, annotationText, mGridAnnotationFont );
01427   p->restore();
01428 }
01429 
01430 QString QgsComposerMap::gridAnnotationString( double value, AnnotationCoordinate coord ) const
01431 {
01432   if ( mGridAnnotationFormat == Decimal )
01433   {
01434     return QString::number( value, 'f', mGridAnnotationPrecision );
01435   }
01436 
01437   QgsPoint p;
01438   p.setX( coord == Longitude ? value : 0 );
01439   p.setY( coord == Longitude ? 0 : value );
01440 
01441   QString annotationString;
01442   if ( mGridAnnotationFormat == DegreeMinute )
01443   {
01444     annotationString = p.toDegreesMinutes( mGridAnnotationPrecision );
01445   }
01446   else //DegreeMinuteSecond
01447   {
01448     annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision );
01449   }
01450 
01451   QStringList split = annotationString.split( "," );
01452   if ( coord == Longitude )
01453   {
01454     return split.at( 0 );
01455   }
01456   else
01457   {
01458     if ( split.size() < 2 )
01459     {
01460       return "";
01461     }
01462     return split.at( 1 );
01463   }
01464 }
01465 
01466 int QgsComposerMap::xGridLines( QList< QPair< double, QLineF > >& lines ) const
01467 {
01468   lines.clear();
01469   if ( mGridIntervalY <= 0.0 )
01470   {
01471     return 1;
01472   }
01473 
01474 
01475   QPolygonF mapPolygon = transformedMapPolygon();
01476   QRectF mapBoundingRect = mapPolygon.boundingRect();
01477 
01478   //consider to round up to the next step in case the left boundary is > 0
01479   double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
01480   double currentLevel = ( int )(( mapBoundingRect.top() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY;
01481 
01482   if ( qgsDoubleNear( mRotation, 0.0 ) )
01483   {
01484     //no rotation. Do it 'the easy way'
01485 
01486     double yCanvasCoord;
01487 
01488     while ( currentLevel <= mapBoundingRect.bottom() )
01489     {
01490       yCanvasCoord = rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
01491       lines.push_back( qMakePair( currentLevel, QLineF( 0, yCanvasCoord, rect().width(), yCanvasCoord ) ) );
01492       currentLevel += mGridIntervalY;
01493     }
01494   }
01495 
01496   //the four border lines
01497   QVector<QLineF> borderLines;
01498   borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
01499   borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
01500   borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
01501   borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
01502 
01503   QList<QPointF> intersectionList; //intersects between border lines and grid lines
01504 
01505   while ( currentLevel <= mapBoundingRect.bottom() )
01506   {
01507     intersectionList.clear();
01508     QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
01509 
01510     QVector<QLineF>::const_iterator it = borderLines.constBegin();
01511     for ( ; it != borderLines.constEnd(); ++it )
01512     {
01513       QPointF intersectionPoint;
01514       if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
01515       {
01516         intersectionList.push_back( intersectionPoint );
01517         if ( intersectionList.size() >= 2 )
01518         {
01519           break; //we already have two intersections, skip further tests
01520         }
01521       }
01522     }
01523 
01524     if ( intersectionList.size() >= 2 )
01525     {
01526       lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
01527     }
01528     currentLevel += mGridIntervalY;
01529   }
01530 
01531 
01532   return 0;
01533 }
01534 
01535 int QgsComposerMap::yGridLines( QList< QPair< double, QLineF > >& lines ) const
01536 {
01537   lines.clear();
01538   if ( mGridIntervalX <= 0.0 )
01539   {
01540     return 1;
01541   }
01542 
01543   QPolygonF mapPolygon = transformedMapPolygon();
01544   QRectF mapBoundingRect = mapPolygon.boundingRect();
01545 
01546   //consider to round up to the next step in case the left boundary is > 0
01547   double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
01548   double currentLevel = ( int )(( mapBoundingRect.left() - mGridOffsetX ) / mGridIntervalX + roundCorrection ) * mGridIntervalX + mGridOffsetX;
01549 
01550   if ( qgsDoubleNear( mRotation, 0.0 ) )
01551   {
01552     //no rotation. Do it 'the easy way'
01553     double xCanvasCoord;
01554 
01555     while ( currentLevel <= mapBoundingRect.right() )
01556     {
01557       xCanvasCoord = rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
01558       lines.push_back( qMakePair( currentLevel, QLineF( xCanvasCoord, 0, xCanvasCoord, rect().height() ) ) );
01559       currentLevel += mGridIntervalX;
01560     }
01561   }
01562 
01563   //the four border lines
01564   QVector<QLineF> borderLines;
01565   borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
01566   borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
01567   borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
01568   borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
01569 
01570   QList<QPointF> intersectionList; //intersects between border lines and grid lines
01571 
01572   while ( currentLevel <= mapBoundingRect.right() )
01573   {
01574     intersectionList.clear();
01575     QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
01576 
01577     QVector<QLineF>::const_iterator it = borderLines.constBegin();
01578     for ( ; it != borderLines.constEnd(); ++it )
01579     {
01580       QPointF intersectionPoint;
01581       if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
01582       {
01583         intersectionList.push_back( intersectionPoint );
01584         if ( intersectionList.size() >= 2 )
01585         {
01586           break; //we already have two intersections, skip further tests
01587         }
01588       }
01589     }
01590 
01591     if ( intersectionList.size() >= 2 )
01592     {
01593       lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
01594     }
01595     currentLevel += mGridIntervalX;
01596   }
01597 
01598   return 0;
01599 }
01600 
01601 void QgsComposerMap::setGridPenWidth( double w )
01602 {
01603   if ( mGridLineSymbol )
01604   {
01605     mGridLineSymbol->setWidth( w );
01606   }
01607 }
01608 
01609 void QgsComposerMap::setGridPenColor( const QColor& c )
01610 {
01611   if ( mGridLineSymbol )
01612   {
01613     mGridLineSymbol->setColor( c );
01614   }
01615 }
01616 
01617 void QgsComposerMap::setGridPen( const QPen& p )
01618 {
01619   setGridPenWidth( p.widthF() );
01620   setGridPenColor( p.color() );
01621 }
01622 
01623 QPen QgsComposerMap::gridPen() const
01624 {
01625   QPen p;
01626   if ( mGridLineSymbol )
01627   {
01628     p.setWidthF( mGridLineSymbol->width() );
01629     p.setColor( mGridLineSymbol->color() );
01630     p.setCapStyle( Qt::FlatCap );
01631   }
01632   return p;
01633 }
01634 
01635 void QgsComposerMap::setGridBlendMode( QPainter::CompositionMode blendMode )
01636 {
01637   mGridBlendMode = blendMode;
01638   update();
01639 }
01640 
01641 QRectF QgsComposerMap::boundingRect() const
01642 {
01643   return mCurrentRectangle;
01644 }
01645 
01646 void QgsComposerMap::updateBoundingRect()
01647 {
01648   QRectF rectangle = rect();
01649   double extension = maxExtension();
01650   rectangle.setLeft( rectangle.left() - extension );
01651   rectangle.setRight( rectangle.right() + extension );
01652   rectangle.setTop( rectangle.top() - extension );
01653   rectangle.setBottom( rectangle.bottom() + extension );
01654   if ( rectangle != mCurrentRectangle )
01655   {
01656     prepareGeometryChange();
01657     mCurrentRectangle = rectangle;
01658   }
01659 }
01660 
01661 QgsRectangle QgsComposerMap::transformedExtent() const
01662 {
01663   double dx = mXOffset;
01664   double dy = mYOffset;
01665   transformShift( dx, dy );
01666   return QgsRectangle( mExtent.xMinimum() - dx, mExtent.yMinimum() - dy, mExtent.xMaximum() - dx, mExtent.yMaximum() - dy );
01667 }
01668 
01669 QPolygonF QgsComposerMap::transformedMapPolygon() const
01670 {
01671   double dx = mXOffset;
01672   double dy = mYOffset;
01673   //qWarning("offset");
01674   //qWarning(QString::number(dx).toLocal8Bit().data());
01675   //qWarning(QString::number(dy).toLocal8Bit().data());
01676   transformShift( dx, dy );
01677   //qWarning("transformed:");
01678   //qWarning(QString::number(dx).toLocal8Bit().data());
01679   //qWarning(QString::number(dy).toLocal8Bit().data());
01680   QPolygonF poly;
01681   mapPolygon( poly );
01682   poly.translate( -dx, -dy );
01683   return poly;
01684 }
01685 
01686 double QgsComposerMap::maxExtension() const
01687 {
01688   if ( !mGridEnabled || !mShowGridAnnotation || ( mLeftGridAnnotationPosition != OutsideMapFrame && mRightGridAnnotationPosition != OutsideMapFrame
01689        && mTopGridAnnotationPosition != OutsideMapFrame && mBottomGridAnnotationPosition != OutsideMapFrame ) )
01690   {
01691     return 0;
01692   }
01693 
01694   QList< QPair< double, QLineF > > xLines;
01695   QList< QPair< double, QLineF > > yLines;
01696 
01697   int xGridReturn = xGridLines( xLines );
01698   int yGridReturn = yGridLines( yLines );
01699 
01700   if ( xGridReturn != 0 && yGridReturn != 0 )
01701   {
01702     return 0;
01703   }
01704 
01705   double maxExtension = 0;
01706   double currentExtension = 0;
01707   QString currentAnnotationString;
01708 
01709   QList< QPair< double, QLineF > >::const_iterator it = xLines.constBegin();
01710   for ( ; it != xLines.constEnd(); ++it )
01711   {
01712     currentAnnotationString = gridAnnotationString( it->first, Latitude );
01713     currentExtension = qMax( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
01714     maxExtension = qMax( maxExtension, currentExtension );
01715   }
01716 
01717   it = yLines.constBegin();
01718   for ( ; it != yLines.constEnd(); ++it )
01719   {
01720     currentAnnotationString = gridAnnotationString( it->first, Longitude );
01721     currentExtension = qMax( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
01722     maxExtension = qMax( maxExtension, currentExtension );
01723   }
01724 
01725   //grid frame
01726   double gridFrameDist = ( mGridFrameStyle == NoGridFrame ) ? 0 : mGridFrameWidth;
01727   return maxExtension + mAnnotationFrameDistance + gridFrameDist;
01728 }
01729 
01730 void QgsComposerMap::mapPolygon( QPolygonF& poly ) const
01731 {
01732   poly.clear();
01733   if ( mRotation == 0 )
01734   {
01735     poly << QPointF( mExtent.xMinimum(), mExtent.yMaximum() );
01736     poly << QPointF( mExtent.xMaximum(), mExtent.yMaximum() );
01737     poly << QPointF( mExtent.xMaximum(), mExtent.yMinimum() );
01738     poly << QPointF( mExtent.xMinimum(), mExtent.yMinimum() );
01739     return;
01740   }
01741 
01742   //there is rotation
01743   QgsPoint rotationPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
01744   double dx, dy; //x-, y- shift from rotation point to corner point
01745 
01746   //top left point
01747   dx = rotationPoint.x() - mExtent.xMinimum();
01748   dy = rotationPoint.y() - mExtent.yMaximum();
01749   rotate( mRotation, dx, dy );
01750   poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
01751 
01752   //top right point
01753   dx = rotationPoint.x() - mExtent.xMaximum();
01754   dy = rotationPoint.y() - mExtent.yMaximum();
01755   rotate( mRotation, dx, dy );
01756   poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
01757 
01758   //bottom right point
01759   dx = rotationPoint.x() - mExtent.xMaximum();
01760   dy = rotationPoint.y() - mExtent.yMinimum();
01761   rotate( mRotation, dx, dy );
01762   poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
01763 
01764   //bottom left point
01765   dx = rotationPoint.x() - mExtent.xMinimum();
01766   dy = rotationPoint.y() - mExtent.yMinimum();
01767   rotate( mRotation, dx, dy );
01768   poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
01769 }
01770 
01771 void QgsComposerMap::requestedExtent( QgsRectangle& extent ) const
01772 {
01773   if ( mRotation == 0 )
01774   {
01775     extent = mExtent;
01776     return;
01777   }
01778 
01779   QPolygonF poly;
01780   mapPolygon( poly );
01781   QRectF bRect = poly.boundingRect();
01782   extent.setXMinimum( bRect.left() );
01783   extent.setXMaximum( bRect.right() );
01784   extent.setYMinimum( bRect.top() );
01785   extent.setYMaximum( bRect.bottom() );
01786   return;
01787 }
01788 
01789 double QgsComposerMap::mapUnitsToMM() const
01790 {
01791   double extentWidth = mExtent.width();
01792   if ( extentWidth <= 0 )
01793   {
01794     return 1;
01795   }
01796   return rect().width() / extentWidth;
01797 }
01798 
01799 void QgsComposerMap::setOverviewFrameMap( int mapId )
01800 {
01801   if ( mOverviewFrameMapId != -1 )
01802   {
01803     const QgsComposerMap* map = mComposition->getComposerMapById( mapId );
01804     if ( map )
01805     {
01806       QObject::disconnect( map, SIGNAL( extentChanged() ), this, SLOT( repaint() ) );
01807     }
01808   }
01809   mOverviewFrameMapId = mapId;
01810   if ( mOverviewFrameMapId != -1 )
01811   {
01812     const QgsComposerMap* map = mComposition->getComposerMapById( mapId );
01813     if ( map )
01814     {
01815       QObject::connect( map, SIGNAL( extentChanged() ), this, SLOT( repaint() ) );
01816     }
01817   }
01818   update();
01819 }
01820 
01821 void QgsComposerMap::setOverviewFrameMapSymbol( QgsFillSymbolV2* symbol )
01822 {
01823   delete mOverviewFrameMapSymbol;
01824   mOverviewFrameMapSymbol = symbol;
01825 }
01826 
01827 void QgsComposerMap::setOverviewBlendMode( QPainter::CompositionMode blendMode )
01828 {
01829   mOverviewBlendMode = blendMode;
01830   update();
01831 }
01832 
01833 void QgsComposerMap::setOverviewInverted( bool inverted )
01834 {
01835   mOverviewInverted = inverted;
01836   update();
01837 }
01838 
01839 void QgsComposerMap::setGridLineSymbol( QgsLineSymbolV2* symbol )
01840 {
01841   delete mGridLineSymbol;
01842   mGridLineSymbol = symbol;
01843 }
01844 
01845 void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
01846 {
01847   double mmToMapUnits = 1.0 / mapUnitsToMM();
01848   double dxScaled = xShift * mmToMapUnits;
01849   double dyScaled = - yShift * mmToMapUnits;
01850 
01851   rotate( mRotation, dxScaled, dyScaled );
01852 
01853   xShift = dxScaled;
01854   yShift = dyScaled;
01855 }
01856 
01857 QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
01858 {
01859   QPolygonF mapPoly = transformedMapPolygon();
01860   if ( mapPoly.size() < 1 )
01861   {
01862     return QPointF( 0, 0 );
01863   }
01864 
01865   QgsRectangle tExtent = transformedExtent();
01866   QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
01867   double dx = mapCoords.x() - rotationPoint.x();
01868   double dy = mapCoords.y() - rotationPoint.y();
01869   rotate( -mRotation, dx, dy );
01870   QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
01871 
01872   QgsRectangle unrotatedExtent = transformedExtent();
01873   double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
01874   double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
01875   return QPointF( xItem, yItem );
01876 }
01877 
01878 QgsComposerMap::Border QgsComposerMap::borderForLineCoord( const QPointF& p ) const
01879 {
01880   if ( p.x() <= pen().widthF() )
01881   {
01882     return Left;
01883   }
01884   else if ( p.x() >= ( rect().width() - pen().widthF() ) )
01885   {
01886     return Right;
01887   }
01888   else if ( p.y() <= pen().widthF() )
01889   {
01890     return Top;
01891   }
01892   else
01893   {
01894     return Bottom;
01895   }
01896 }
01897 
01898 void QgsComposerMap::drawCanvasItems( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
01899 {
01900   if ( !mMapCanvas || !mDrawCanvasItems )
01901   {
01902     return;
01903   }
01904 
01905   QList<QGraphicsItem*> itemList = mMapCanvas->items();
01906   if ( itemList.size() < 1 )
01907   {
01908     return;
01909   }
01910   QGraphicsItem* currentItem = 0;
01911 
01912 #if QT_VERSION >= 0x40600 //Qt 4.6 provides the items in visibility order
01913   for ( int i = itemList.size() - 1; i >= 0; --i )
01914   {
01915     currentItem = itemList.at( i );
01916     //don't draw mapcanvasmap (has z value -10)
01917     if ( !currentItem || currentItem->data( 0 ).toString() != "AnnotationItem" )
01918     {
01919       continue;
01920     }
01921     drawCanvasItem( currentItem, painter, itemStyle );
01922   }
01923 #else //Qt <4.6 provides the items in random order
01924   QMultiMap<int, QGraphicsItem*> topLevelItems;
01925   QMultiMap<QGraphicsItem*, QGraphicsItem*> childItems; //QMultiMap<parentItem, childItem>
01926 
01927   for ( int i = 0; i < itemList.size(); ++i )
01928   {
01929     currentItem = itemList.at( i );
01930     //don't draw mapcanvasmap (has z value -10)
01931     if ( !currentItem || currentItem->data( 0 ) != "AnnotationItem" )
01932     {
01933       continue;
01934     }
01935     if ( currentItem->parentItem() )
01936     {
01937       childItems.insert( currentItem->parentItem(), currentItem );
01938     }
01939     else
01940     {
01941       topLevelItems.insert( currentItem->zValue(), currentItem );
01942     }
01943   }
01944 
01945   QMultiMap<int, QGraphicsItem*>::iterator topLevelIt = topLevelItems.begin();
01946   for ( ; topLevelIt != topLevelItems.end(); ++topLevelIt )
01947   {
01948     drawCanvasItem( topLevelIt.value(), painter, itemStyle );
01949     //Draw children. They probably should be sorted according to z-order, but we don't do it because this code is only
01950     //there for backward compatibility. And currently, having several embedded children is not used in QGIS
01951     QMap<QGraphicsItem*, QGraphicsItem*>::iterator childIt = childItems.find( topLevelIt.value() );
01952     while ( childIt != childItems.end() && childIt.key() == topLevelIt.value() )
01953     {
01954       drawCanvasItem( childIt.value(), painter, itemStyle );
01955       ++childIt;
01956     }
01957   }
01958 #endif
01959 }
01960 
01961 void QgsComposerMap::drawCanvasItem( QGraphicsItem* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
01962 {
01963   if ( !item || !mMapCanvas || !mMapRenderer  || !item->isVisible() )
01964   {
01965     return;
01966   }
01967 
01968   painter->save();
01969 
01970   QgsRectangle rendererExtent = mMapRenderer->extent();
01971   QgsRectangle composerMapExtent = mExtent;
01972 
01973   //determine scale factor according to graphics view dpi
01974   double scaleFactor = 1.0 / mMapCanvas->logicalDpiX() * 25.4;
01975 
01976   double itemX, itemY;
01977   QGraphicsItem* parent = item->parentItem();
01978   if ( !parent )
01979   {
01980     QPointF mapPos = composerMapPosForItem( item );
01981     itemX = mapPos.x();
01982     itemY = mapPos.y();
01983   }
01984   else //place item relative to the parent item
01985   {
01986     QPointF itemScenePos = item->scenePos();
01987     QPointF parentScenePos = parent->scenePos();
01988 
01989     QPointF mapPos = composerMapPosForItem( parent );
01990 
01991     itemX = mapPos.x() + ( itemScenePos.x() - parentScenePos.x() ) * scaleFactor;
01992     itemY = mapPos.y() + ( itemScenePos.y() - parentScenePos.y() ) * scaleFactor;
01993   }
01994   painter->translate( itemX, itemY );
01995 
01996 
01997   painter->scale( scaleFactor, scaleFactor );
01998 
01999   //a little trick to let the item know that the paint request comes from the composer
02000   item->setData( 1, "composer" );
02001   item->paint( painter, itemStyle, 0 );
02002   item->setData( 1, "" );
02003   painter->restore();
02004 }
02005 
02006 QPointF QgsComposerMap::composerMapPosForItem( const QGraphicsItem* item ) const
02007 {
02008   if ( !item || !mMapCanvas || !mMapRenderer )
02009   {
02010     return QPointF( 0, 0 );
02011   }
02012 
02013   if ( mExtent.height() <= 0 || mExtent.width() <= 0 || mMapCanvas->width() <= 0 || mMapCanvas->height() <= 0 )
02014   {
02015     return QPointF( 0, 0 );
02016   }
02017 
02018   QRectF graphicsSceneRect = mMapCanvas->sceneRect();
02019   QPointF itemScenePos = item->scenePos();
02020   QgsRectangle mapRendererExtent = mMapRenderer->extent();
02021 
02022   double mapX = itemScenePos.x() / graphicsSceneRect.width() * mapRendererExtent.width() + mapRendererExtent.xMinimum();
02023   double mapY = mapRendererExtent.yMaximum() - itemScenePos.y() / graphicsSceneRect.height() * mapRendererExtent.height();
02024   return mapToItemCoords( QPointF( mapX, mapY ) );
02025 }
02026 
02027 void QgsComposerMap::setGridAnnotationPosition( GridAnnotationPosition p, QgsComposerMap::Border border )
02028 {
02029   switch ( border )
02030   {
02031     case QgsComposerMap::Left:
02032       mLeftGridAnnotationPosition = p;
02033       break;
02034     case QgsComposerMap::Right:
02035       mRightGridAnnotationPosition = p;
02036       break;
02037     case QgsComposerMap::Top:
02038       mTopGridAnnotationPosition = p;
02039       break;
02040     case QgsComposerMap::Bottom:
02041       mBottomGridAnnotationPosition = p;
02042       break;
02043     default:
02044       return;
02045   }
02046   updateBoundingRect();
02047   update();
02048 }
02049 
02050 QgsComposerMap::GridAnnotationPosition QgsComposerMap::gridAnnotationPosition( QgsComposerMap::Border border ) const
02051 {
02052   switch ( border )
02053   {
02054     case QgsComposerMap::Left:
02055       return mLeftGridAnnotationPosition;
02056       break;
02057     case QgsComposerMap::Right:
02058       return mRightGridAnnotationPosition;
02059       break;
02060     case QgsComposerMap::Top:
02061       return mTopGridAnnotationPosition;
02062       break;
02063     case QgsComposerMap::Bottom:
02064     default:
02065       return mBottomGridAnnotationPosition;
02066       break;
02067   }
02068 }
02069 
02070 void QgsComposerMap::setGridAnnotationDirection( GridAnnotationDirection d, QgsComposerMap::Border border )
02071 {
02072   switch ( border )
02073   {
02074     case QgsComposerMap::Left:
02075       mLeftGridAnnotationDirection = d;
02076       break;
02077     case QgsComposerMap::Right:
02078       mRightGridAnnotationDirection = d;
02079       break;
02080     case QgsComposerMap::Top:
02081       mTopGridAnnotationDirection = d;
02082       break;
02083     case QgsComposerMap::Bottom:
02084       mBottomGridAnnotationDirection = d;
02085       break;
02086     default:
02087       return;
02088       break;
02089   }
02090   updateBoundingRect();
02091   update();
02092 }
02093 
02094 QgsComposerMap::GridAnnotationDirection QgsComposerMap::gridAnnotationDirection( QgsComposerMap::Border border ) const
02095 {
02096   switch ( border )
02097   {
02098     case QgsComposerMap::Left:
02099       return mLeftGridAnnotationDirection;
02100       break;
02101     case QgsComposerMap::Right:
02102       return mRightGridAnnotationDirection;
02103       break;
02104     case QgsComposerMap::Top:
02105       return mTopGridAnnotationDirection;
02106       break;
02107     case QgsComposerMap::Bottom:
02108     default:
02109       return mBottomGridAnnotationDirection;
02110       break;
02111   }
02112 }
02113 
02114 void QgsComposerMap::sortGridLinesOnBorders( const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines,  QMap< double, double >& leftFrameEntries,
02115     QMap< double, double >& rightFrameEntries, QMap< double, double >& topFrameEntries, QMap< double, double >& bottomFrameEntries ) const
02116 {
02117   QList< QPair< double, QPointF > > borderPositions;
02118   QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
02119   for ( ; it != hLines.constEnd(); ++it )
02120   {
02121     borderPositions << qMakePair( it->first, it->second.p1() );
02122     borderPositions << qMakePair( it->first, it->second.p2() );
02123   }
02124   it = vLines.constBegin();
02125   for ( ; it != vLines.constEnd(); ++it )
02126   {
02127     borderPositions << qMakePair( it->first, it->second.p1() );
02128     borderPositions << qMakePair( it->first, it->second.p2() );
02129   }
02130 
02131   QList< QPair< double, QPointF > >::const_iterator bIt = borderPositions.constBegin();
02132   for ( ; bIt != borderPositions.constEnd(); ++bIt )
02133   {
02134     Border frameBorder = borderForLineCoord( bIt->second );
02135     if ( frameBorder == QgsComposerMap::Left )
02136     {
02137       leftFrameEntries.insert( bIt->second.y(), bIt->first );
02138     }
02139     else if ( frameBorder == QgsComposerMap::Right )
02140     {
02141       rightFrameEntries.insert( bIt->second.y(), bIt->first );
02142     }
02143     else if ( frameBorder == QgsComposerMap::Top )
02144     {
02145       topFrameEntries.insert( bIt->second.x(), bIt->first );
02146     }
02147     else //Bottom
02148     {
02149       bottomFrameEntries.insert( bIt->second.x(), bIt->first );
02150     }
02151   }
02152 }
02153 
02154 void QgsComposerMap::drawOverviewMapExtent( QPainter* p )
02155 {
02156   if ( mOverviewFrameMapId == -1 || !mComposition )
02157   {
02158     return;
02159   }
02160 
02161   const QgsComposerMap* overviewFrameMap = mComposition->getComposerMapById( mOverviewFrameMapId );
02162   if ( !overviewFrameMap )
02163   {
02164     return;
02165   }
02166 
02167   QgsRectangle otherExtent = overviewFrameMap->extent();
02168   QgsRectangle thisExtent = extent();
02169   QgsRectangle intersectRect = thisExtent.intersect( &otherExtent );
02170 
02171   QgsRenderContext context;
02172   context.setPainter( p );
02173   if ( mPreviewMode == Rectangle )
02174   {
02175     return;
02176   }
02177   else
02178   {
02179     context.setScaleFactor( 1.0 );
02180     context.setRasterScaleFactor( mComposition->printResolution() / 25.4 );
02181   }
02182 
02183   p->save();
02184   p->setCompositionMode( mOverviewBlendMode );
02185   mOverviewFrameMapSymbol->startRender( context );
02186 
02187   //construct a polygon corresponding to the intersecting map extent
02188   QPolygonF intersectPolygon;
02189   double x = ( intersectRect.xMinimum() - thisExtent.xMinimum() ) / thisExtent.width() * rect().width();
02190   double y = ( thisExtent.yMaximum() - intersectRect.yMaximum() ) / thisExtent.height() * rect().height();
02191   double width = intersectRect.width() / thisExtent.width() * rect().width();
02192   double height = intersectRect.height() / thisExtent.height() * rect().height();
02193   intersectPolygon << QPointF( x, y ) << QPointF( x + width, y ) << QPointF( x + width, y + height ) << QPointF( x, y + height ) << QPointF( x, y );
02194 
02195   QList<QPolygonF> rings; //empty list
02196   if ( !mOverviewInverted )
02197   {
02198     //Render the intersecting map extent
02199     mOverviewFrameMapSymbol->renderPolygon( intersectPolygon, &rings, 0, context );;
02200   }
02201   else
02202   {
02203     //We are inverting the overview frame (ie, shading outside the intersecting extent)
02204     //Construct a polygon corresponding to the overview map extent
02205     QPolygonF outerPolygon;
02206     outerPolygon << QPointF( 0, 0 ) << QPointF( rect().width(), 0 ) << QPointF( rect().width(), rect().height() ) << QPointF( 0, rect().height() ) << QPointF( 0, 0 );
02207 
02208     //Intersecting extent is an inner ring for the shaded area
02209     rings.append( intersectPolygon );
02210     mOverviewFrameMapSymbol->renderPolygon( outerPolygon, &rings, 0, context );
02211   }
02212 
02213   mOverviewFrameMapSymbol->stopRender( context );
02214   p->restore();
02215 }
02216 
02217 void QgsComposerMap::createDefaultOverviewFrameSymbol()
02218 {
02219   delete mOverviewFrameMapSymbol;
02220   QgsStringMap properties;
02221   properties.insert( "color", "255,0,0,255" );
02222   properties.insert( "style", "solid" );
02223   properties.insert( "style_border", "no" );
02224   mOverviewFrameMapSymbol = QgsFillSymbolV2::createSimple( properties );
02225   mOverviewFrameMapSymbol->setAlpha( 0.3 );
02226 }
02227 
02228 void QgsComposerMap::createDefaultGridLineSymbol()
02229 {
02230   delete mGridLineSymbol;
02231   QgsStringMap properties;
02232   properties.insert( "color", "0,0,0,255" );
02233   properties.insert( "width", "0.3" );
02234   properties.insert( "capstyle", "flat" );
02235   mGridLineSymbol = QgsLineSymbolV2::createSimple( properties );
02236 }
02237 
02238 void QgsComposerMap::initGridAnnotationFormatFromProject()
02239 {
02240   QString format = QgsProject::instance()->readEntry( "PositionPrecision", "/DegreeFormat", "D" );
02241 
02242   bool degreeUnits = true;
02243   if ( mMapRenderer )
02244   {
02245     degreeUnits = ( mMapRenderer->mapUnits() == QGis::Degrees );
02246   }
02247 
02248   if ( format == "DM" && degreeUnits )
02249   {
02250     mGridAnnotationFormat = DegreeMinute;
02251   }
02252   else if ( format == "DMS" && degreeUnits )
02253   {
02254     mGridAnnotationFormat = DegreeMinuteSecond;
02255   }
02256   else
02257   {
02258     mGridAnnotationFormat = Decimal;
02259   }
02260 }
02261 
02262 void QgsComposerMap::assignFreeId()
02263 {
02264   if ( !mComposition )
02265   {
02266     return;
02267   }
02268 
02269   const QgsComposerMap* existingMap = mComposition->getComposerMapById( mId );
02270   if ( !existingMap )
02271   {
02272     return; //keep mId as it is still available
02273   }
02274 
02275   int maxId = -1;
02276   QList<const QgsComposerMap*> mapList = mComposition->composerMapItems();
02277   QList<const QgsComposerMap*>::const_iterator mapIt = mapList.constBegin();
02278   for ( ; mapIt != mapList.constEnd(); ++mapIt )
02279   {
02280     if (( *mapIt )->id() > maxId )
02281     {
02282       maxId = ( *mapIt )->id();
02283     }
02284   }
02285   mId = maxId + 1;
02286 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines