|
QGIS API Documentation
master-59fd5e0
|
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 }