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