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