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