1
/***************************************************************************
5
copyright : (C) 2005 by Radim Blazek
7
***************************************************************************/
9
/***************************************************************************
11
* This program is free software; you can redistribute it and/or modify *
12
* it under the terms of the GNU General Public License as published by *
13
* the Free Software Foundation; either version 2 of the License, or *
14
* (at your option) any later version. *
16
***************************************************************************/
18
#include "qgscomposermap.h"
20
#include "qgscoordinatetransform.h"
21
#include "qgslogger.h"
22
#include "qgsmaprenderer.h"
23
#include "qgsmaplayer.h"
24
#include "qgsmaplayerregistry.h"
25
#include "qgsmaptopixel.h"
26
#include "qgsproject.h"
27
#include "qgsmaprenderer.h"
28
#include "qgsrasterlayer.h"
29
#include "qgsrendercontext.h"
30
#include "qgsscalecalculator.h"
31
#include "qgsvectorlayer.h"
34
#include "qgslabelattributes.h"
36
#include <QGraphicsScene>
42
QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
43
: QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ), mGridEnabled( false ), mGridStyle( Solid ), \
44
mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), \
45
mGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mGridAnnotationDirection( Horizontal ), mCrossLength( 3 )
47
mComposition = composition;
48
mId = mComposition->composerMapItems().size();
49
mMapRenderer = mComposition->mapRenderer();
50
mPreviewMode = QgsComposerMap::Rectangle;
51
mCurrentRectangle = rect();
54
mCacheUpdated = false;
63
//calculate mExtent based on width/height ratio and map canvas extent
66
mExtent = mMapRenderer->extent();
68
setSceneRect( QRectF( x, y, width, height ) );
69
setToolTip( tr( "Map %1" ).arg( mId ) );
70
mGridPen.setCapStyle( Qt::FlatCap );
73
QgsComposerMap::QgsComposerMap( QgsComposition *composition )
74
: QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mGridEnabled( false ), mGridStyle( Solid ), \
75
mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), \
76
mGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mGridAnnotationDirection( Horizontal ), mCrossLength( 3 )
84
mComposition = composition;
85
mMapRenderer = mComposition->mapRenderer();
86
mId = mComposition->composerMapItems().size();
87
mPreviewMode = QgsComposerMap::Rectangle;
88
mCurrentRectangle = rect();
90
setToolTip( tr( "Map %1" ).arg( mId ) );
91
mGridPen.setCapStyle( Qt::FlatCap );
94
QgsComposerMap::~QgsComposerMap()
98
/* This function is called by paint() and cache() to render the map. It does not override any functions
99
from QGraphicsItem. */
100
void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSize& size, int dpi )
112
QgsMapRenderer theMapRenderer;
113
theMapRenderer.setExtent( extent );
114
theMapRenderer.setOutputSize( size, dpi );
116
//use stored layer set or read current set from main canvas
119
theMapRenderer.setLayerSet( mLayerSet );
123
theMapRenderer.setLayerSet( mMapRenderer->layerSet() );
125
theMapRenderer.setProjectionsEnabled( mMapRenderer->hasCrsTransformEnabled() );
126
theMapRenderer.setDestinationSrs( mMapRenderer->destinationSrs() );
128
//set antialiasing if enabled in options
130
if ( settings.value( "/qgis/enable_anti_aliasing", false ).toBool() )
132
painter->setRenderHint( QPainter::Antialiasing );
135
QgsRenderContext* theRendererContext = theMapRenderer.rendererContext();
136
if ( theRendererContext )
138
theRendererContext->setDrawEditingInformation( false );
139
theRendererContext->setRenderingStopped( false );
142
//force composer map scale for scale dependent visibility
143
double bk_scale = theMapRenderer.scale();
144
theMapRenderer.setScale( scale() );
146
//layer caching (as QImages) cannot be done for composer prints
148
bool bkLayerCaching = s.value( "/qgis/enable_render_caching", false ).toBool();
149
s.setValue( "/qgis/enable_render_caching", false );
151
theMapRenderer.render( painter );
152
s.setValue( "/qgis/enable_render_caching", bkLayerCaching );
154
theMapRenderer.setScale( bk_scale );
157
void QgsComposerMap::cache( void )
159
if ( mPreviewMode == Rectangle )
171
//in case of rotation, we need to request a larger rectangle and create a larger cache image
172
QgsRectangle requestExtent;
173
requestedExtent( requestExtent );
175
double horizontalVScaleFactor = horizontalViewScaleFactor();
176
if ( horizontalVScaleFactor < 0 )
178
horizontalVScaleFactor = mLastValidViewScaleFactor;
181
int w = requestExtent.width() * mapUnitsToMM() * horizontalVScaleFactor;
182
int h = requestExtent.height() * mapUnitsToMM() * horizontalVScaleFactor;
184
if ( w > 5000 ) //limit size of image for better performance
194
mCacheImage = QImage( w, h, QImage::Format_ARGB32 );
195
mCacheImage.fill( brush().color().rgb() ); //consider the item background brush
196
double mapUnitsPerPixel = mExtent.width() / w;
198
// WARNING: ymax in QgsMapToPixel is device height!!!
199
QgsMapToPixel transform( mapUnitsPerPixel, h, requestExtent.yMinimum(), requestExtent.xMinimum() );
201
QPainter p( &mCacheImage );
203
draw( &p, requestExtent, QSize( w, h ), mCacheImage.logicalDpiX() );
205
mCacheUpdated = true;
210
void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
212
if ( !mComposition || !painter )
217
QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
219
painter->setClipRect( thisPaintRect );
221
drawBackground( painter );
223
if ( mComposition->plotStyle() == QgsComposition::Preview && mPreviewMode == Rectangle )
225
QFont messageFont( "", 12 );
226
painter->setFont( messageFont );
227
painter->setPen( QColor( 0, 0, 0 ) );
228
painter->drawText( thisPaintRect, tr( "Map will be printed here" ) );
230
else if ( mComposition->plotStyle() == QgsComposition::Preview )
232
//draw cached pixmap. This function does not call cache() any more because
233
//Qt 4.4.0 and 4.4.1 have problems with recursive paintings
234
//QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
237
QgsRectangle requestRectangle;
238
requestedExtent( requestRectangle );
239
double horizontalVScaleFactor = horizontalViewScaleFactor();
240
if ( horizontalVScaleFactor < 0 )
242
horizontalVScaleFactor = mLastValidViewScaleFactor;
245
double imagePixelWidth = mExtent.width() / requestRectangle.width() * mCacheImage.width() ; //how many pixels of the image are for the map extent?
246
double scale = rect().width() / imagePixelWidth;
247
QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
249
//shift such that rotation point is at 0/0 point in the coordinate system
250
double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
251
double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
253
//shift such that top left point of the extent at point 0/0 in item coordinate system
254
double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
255
double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
258
//painter->scale( scale, scale );
259
painter->translate( mXOffset, mYOffset );
260
painter->translate( xTopLeftShift, yTopLeftShift );
261
painter->rotate( mRotation );
262
painter->translate( xShiftMM, -yShiftMM );
263
painter->scale( scale, scale );
264
painter->drawImage( 0, 0, mCacheImage );
267
else if ( mComposition->plotStyle() == QgsComposition::Print ||
268
mComposition->plotStyle() == QgsComposition::Postscript )
276
QPaintDevice* thePaintDevice = painter->device();
277
if ( !thePaintDevice )
282
QgsRectangle requestRectangle;
283
requestedExtent( requestRectangle );
285
QSize theSize( requestRectangle.width() * mapUnitsToMM(), requestRectangle.height() * mapUnitsToMM() );
286
QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
288
//shift such that rotation point is at 0/0 point in the coordinate system
289
double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
290
double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
292
//shift such that top left point of the extent at point 0/0 in item coordinate system
293
double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
294
double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
296
painter->translate( mXOffset, mYOffset );
297
painter->translate( xTopLeftShift, yTopLeftShift );
298
painter->rotate( mRotation );
299
painter->translate( xShiftMM, -yShiftMM );
300
draw( painter, requestRectangle, theSize, 25.4 ); //scene coordinates seem to be in mm
306
painter->setClipRect( thisPaintRect , Qt::NoClip );
312
drawFrame( painter );
315
drawSelectionBoxes( painter );
322
void QgsComposerMap::updateCachedImage( void )
324
syncLayerSet(); //layer list may have changed
325
mCacheUpdated = false;
327
QGraphicsRectItem::update();
330
void QgsComposerMap::renderModeUpdateCachedImage()
332
if ( mPreviewMode == Render )
338
void QgsComposerMap::setCacheUpdated( bool u )
343
double QgsComposerMap::scale() const
345
QgsScaleCalculator calculator;
346
calculator.setMapUnits( mMapRenderer->mapUnits() );
347
calculator.setDpi( 25.4 ); //QGraphicsView units are mm
348
return calculator.calculate( mExtent, rect().width() );
351
void QgsComposerMap::resize( double dx, double dy )
354
QRectF currentRect = rect();
355
QRectF newSceneRect = QRectF( transform().dx(), transform().dy(), currentRect.width() + dx, currentRect.height() + dy );
356
setSceneRect( newSceneRect );
359
void QgsComposerMap::moveContent( double dx, double dy )
363
transformShift( dx, dy );
364
mExtent.setXMinimum( mExtent.xMinimum() + dx );
365
mExtent.setXMaximum( mExtent.xMaximum() + dx );
366
mExtent.setYMinimum( mExtent.yMinimum() + dy );
367
mExtent.setYMaximum( mExtent.yMaximum() + dy );
368
emit extentChanged();
374
void QgsComposerMap::zoomContent( int delta, double x, double y )
384
//0: zoom, 1: zoom and recenter, 2: zoom to cursor, 3: nothing
385
int zoomMode = settings.value( "/qgis/wheel_action", 0 ).toInt();
386
if ( zoomMode == 3 ) //do nothing
391
double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
393
//find out new center point
394
double centerX = ( mExtent.xMaximum() + mExtent.xMinimum() ) / 2;
395
double centerY = ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2;
399
//find out map coordinates of mouse position
400
double mapMouseX = mExtent.xMinimum() + ( x / rect().width() ) * ( mExtent.xMaximum() - mExtent.xMinimum() );
401
double mapMouseY = mExtent.yMinimum() + ( 1 - ( y / rect().height() ) ) * ( mExtent.yMaximum() - mExtent.yMinimum() );
402
if ( zoomMode == 1 ) //zoom and recenter
407
else if ( zoomMode == 2 ) //zoom to cursor
409
centerX = mapMouseX + ( centerX - mapMouseX ) * ( 1.0 / zoomFactor );
410
centerY = mapMouseY + ( centerY - mapMouseY ) * ( 1.0 / zoomFactor );
414
double newIntervalX, newIntervalY;
418
newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) / zoomFactor;
419
newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) / zoomFactor;
421
else if ( delta < 0 )
423
newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) * zoomFactor;
424
newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) * zoomFactor;
426
else //no need to zoom
431
mExtent.setXMaximum( centerX + newIntervalX / 2 );
432
mExtent.setXMinimum( centerX - newIntervalX / 2 );
433
mExtent.setYMaximum( centerY + newIntervalY / 2 );
434
mExtent.setYMinimum( centerY - newIntervalY / 2 );
436
emit extentChanged();
441
void QgsComposerMap::setSceneRect( const QRectF& rectangle )
443
double w = rectangle.width();
444
double h = rectangle.height();
445
//prepareGeometryChange();
447
QgsComposerItem::setSceneRect( rectangle );
449
//QGraphicsRectItem::update();
450
double newHeight = mExtent.width() * h / w ;
451
mExtent = QgsRectangle( mExtent.xMinimum(), mExtent.yMinimum(), mExtent.xMaximum(), mExtent.yMinimum() + newHeight );
452
mCacheUpdated = false;
453
emit extentChanged();
454
if ( mPreviewMode != Rectangle )
458
updateBoundingRect();
462
void QgsComposerMap::setNewExtent( const QgsRectangle& extent )
464
if ( mExtent == extent )
471
QRectF currentRect = rect();
473
double newHeight = currentRect.width() * extent.height() / extent.width();
475
setSceneRect( QRectF( transform().dx(), transform().dy(), currentRect.width(), newHeight ) );
478
void QgsComposerMap::setNewScale( double scaleDenominator )
480
double currentScaleDenominator = scale();
482
if ( scaleDenominator == currentScaleDenominator )
487
double scaleRatio = scaleDenominator / currentScaleDenominator;
488
mExtent.scale( scaleRatio );
489
mCacheUpdated = false;
490
emit extentChanged();
495
void QgsComposerMap::setOffset( double xOffset, double yOffset )
501
void QgsComposerMap::setMapRotation( double r )
504
emit rotationChanged( r );
507
bool QgsComposerMap::containsWMSLayer() const
514
QStringList layers = mMapRenderer->layerSet();
516
QStringList::const_iterator layer_it = layers.constBegin();
517
QgsMapLayer* currentLayer = 0;
519
for ( ; layer_it != layers.constEnd(); ++layer_it )
521
currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
524
QgsRasterLayer* currentRasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
525
if ( currentRasterLayer )
527
const QgsRasterDataProvider* rasterProvider = 0;
528
if (( rasterProvider = currentRasterLayer->dataProvider() ) )
530
if ( rasterProvider->name() == "wms" )
541
void QgsComposerMap::connectUpdateSlot()
543
//connect signal from layer registry to update in case of new or deleted layers
544
QgsMapLayerRegistry* layerRegistry = QgsMapLayerRegistry::instance();
547
connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( updateCachedImage() ) );
548
connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( updateCachedImage() ) );
552
bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
559
QDomElement composerMapElem = doc.createElement( "ComposerMap" );
560
composerMapElem.setAttribute( "id", mId );
563
if ( mPreviewMode == Cache )
565
composerMapElem.setAttribute( "previewMode", "Cache" );
567
else if ( mPreviewMode == Render )
569
composerMapElem.setAttribute( "previewMode", "Render" );
573
composerMapElem.setAttribute( "previewMode", "Rectangle" );
578
composerMapElem.setAttribute( "keepLayerSet", "true" );
582
composerMapElem.setAttribute( "keepLayerSet", "false" );
586
QDomElement extentElem = doc.createElement( "Extent" );
587
extentElem.setAttribute( "xmin", QString::number( mExtent.xMinimum() ) );
588
extentElem.setAttribute( "xmax", QString::number( mExtent.xMaximum() ) );
589
extentElem.setAttribute( "ymin", QString::number( mExtent.yMinimum() ) );
590
extentElem.setAttribute( "ymax", QString::number( mExtent.yMaximum() ) );
591
composerMapElem.appendChild( extentElem );
594
QDomElement layerSetElem = doc.createElement( "LayerSet" );
595
QStringList::const_iterator layerIt = mLayerSet.constBegin();
596
for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
598
QDomElement layerElem = doc.createElement( "Layer" );
599
QDomText layerIdText = doc.createTextNode( *layerIt );
600
layerElem.appendChild( layerIdText );
601
layerSetElem.appendChild( layerElem );
603
composerMapElem.appendChild( layerSetElem );
606
QDomElement gridElem = doc.createElement( "Grid" );
607
gridElem.setAttribute( "show", mGridEnabled );
608
gridElem.setAttribute( "gridStyle", mGridStyle );
609
gridElem.setAttribute( "intervalX", mGridIntervalX );
610
gridElem.setAttribute( "intervalY", mGridIntervalY );
611
gridElem.setAttribute( "offsetX", mGridOffsetX );
612
gridElem.setAttribute( "offsetY", mGridOffsetY );
613
gridElem.setAttribute( "penWidth", mGridPen.widthF() );
614
gridElem.setAttribute( "penColorRed", mGridPen.color().red() );
615
gridElem.setAttribute( "penColorGreen", mGridPen.color().green() );
616
gridElem.setAttribute( "penColorBlue", mGridPen.color().blue() );
617
gridElem.setAttribute( "crossLength", mCrossLength );
620
QDomElement annotationElem = doc.createElement( "Annotation" );
621
annotationElem.setAttribute( "show", mShowGridAnnotation );
622
annotationElem.setAttribute( "position", mGridAnnotationPosition );
623
annotationElem.setAttribute( "frameDistance", mAnnotationFrameDistance );
624
annotationElem.setAttribute( "direction", mGridAnnotationDirection );
625
annotationElem.setAttribute( "font", mGridAnnotationFont.toString() );
626
annotationElem.setAttribute( "precision", mGridAnnotationPrecision );
628
gridElem.appendChild( annotationElem );
629
composerMapElem.appendChild( gridElem );
631
elem.appendChild( composerMapElem );
632
return _writeXML( composerMapElem, doc );
635
bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& doc )
637
if ( itemElem.isNull() )
642
QString idRead = itemElem.attribute( "id", "not found" );
643
if ( idRead != "not found" )
645
mId = idRead.toInt();
647
mPreviewMode = Rectangle;
650
QString previewMode = itemElem.attribute( "previewMode" );
651
if ( previewMode == "Cache" )
653
mPreviewMode = Cache;
655
else if ( previewMode == "Render" )
657
mPreviewMode = Render;
661
mPreviewMode = Rectangle;
665
QDomNodeList extentNodeList = itemElem.elementsByTagName( "Extent" );
666
if ( extentNodeList.size() > 0 )
668
QDomElement extentElem = extentNodeList.at( 0 ).toElement();
669
double xmin, xmax, ymin, ymax;
670
xmin = extentElem.attribute( "xmin" ).toDouble();
671
xmax = extentElem.attribute( "xmax" ).toDouble();
672
ymin = extentElem.attribute( "ymin" ).toDouble();
673
ymax = extentElem.attribute( "ymax" ).toDouble();
675
mExtent = QgsRectangle( xmin, ymin, xmax, ymax );
679
QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
680
if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
682
mKeepLayerSet = true;
686
mKeepLayerSet = false;
690
QDomNodeList layerSetNodeList = itemElem.elementsByTagName( "LayerSet" );
691
QStringList layerSet;
692
if ( layerSetNodeList.size() > 0 )
694
QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
695
QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( "Layer" );
696
for ( int i = 0; i < layerIdNodeList.size(); ++i )
698
layerSet << layerIdNodeList.at( i ).toElement().text();
701
mLayerSet = layerSet;
704
mNumCachedLayers = 0;
705
mCacheUpdated = false;
708
QDomNodeList gridNodeList = itemElem.elementsByTagName( "Grid" );
709
if ( gridNodeList.size() > 0 )
711
QDomElement gridElem = gridNodeList.at( 0 ).toElement();
712
mGridEnabled = ( gridElem.attribute( "show", "0" ) != "0" );
713
mGridStyle = QgsComposerMap::GridStyle( gridElem.attribute( "gridStyle", "0" ).toInt() );
714
mGridIntervalX = gridElem.attribute( "intervalX", "0" ).toDouble();
715
mGridIntervalY = gridElem.attribute( "intervalY", "0" ).toDouble();
716
mGridOffsetX = gridElem.attribute( "offsetX", "0" ).toDouble();
717
mGridOffsetY = gridElem.attribute( "offsetY", "0" ).toDouble();
718
mGridPen.setWidthF( gridElem.attribute( "penWidth", "0" ).toDouble() );
719
mGridPen.setColor( QColor( gridElem.attribute( "penColorRed", "0" ).toInt(), \
720
gridElem.attribute( "penColorGreen", "0" ).toInt(), \
721
gridElem.attribute( "penColorBlue", "0" ).toInt() ) );
722
mCrossLength = gridElem.attribute( "crossLength", "3" ).toDouble();
724
QDomNodeList annotationNodeList = gridElem.elementsByTagName( "Annotation" );
725
if ( annotationNodeList.size() > 0 )
727
QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
728
mShowGridAnnotation = ( annotationElem.attribute( "show", "0" ) != "0" );
729
mGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "position", "0" ).toInt() );
730
mAnnotationFrameDistance = annotationElem.attribute( "frameDistance", "0" ).toDouble();
731
mGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "direction", "0" ).toInt() );
732
mGridAnnotationFont.fromString( annotationElem.attribute( "font", "" ) );
733
mGridAnnotationPrecision = annotationElem.attribute( "precision", "3" ).toInt();
737
//restore general composer item properties
738
QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
739
if ( composerItemList.size() > 0 )
741
QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
742
_readXML( composerItemElem, doc );
745
updateBoundingRect();
749
void QgsComposerMap::storeCurrentLayerSet()
753
mLayerSet = mMapRenderer->layerSet();
757
void QgsComposerMap::syncLayerSet()
759
if ( mLayerSet.size() < 1 && !mMapRenderer )
764
QStringList currentLayerSet = mMapRenderer->layerSet();
765
for ( int i = mLayerSet.size() - 1; i >= 0; --i )
767
if ( !currentLayerSet.contains( mLayerSet.at( i ) ) )
769
mLayerSet.removeAt( i );
774
void QgsComposerMap::drawGrid( QPainter* p )
776
p->setPen( mGridPen );
778
QList< QPair< double, QLineF > > verticalLines;
779
yGridLines( verticalLines );
780
QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
781
QList< QPair< double, QLineF > > horizontalLines;
782
xGridLines( horizontalLines );
783
QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
785
QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
786
p->setClipRect( thisPaintRect );
788
//simpler approach: draw vertical lines first, then horizontal ones
789
if ( mGridStyle == QgsComposerMap::Solid )
791
for ( ; vIt != verticalLines.constEnd(); ++vIt )
793
p->drawLine( vIt->second );
796
for ( ; hIt != horizontalLines.constEnd(); ++hIt )
798
p->drawLine( hIt->second );
803
QPointF intersectionPoint, crossEnd1, crossEnd2;
804
for ( ; vIt != verticalLines.constEnd(); ++vIt )
807
crossEnd1 = pointOnLineWithDistance( vIt->second.p1(), vIt->second.p2(), mCrossLength );
808
p->drawLine( vIt->second.p1(), crossEnd1 );
810
//test for intersection with every horizontal line
811
hIt = horizontalLines.constBegin();
812
for ( ; hIt != horizontalLines.constEnd(); ++hIt )
814
if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
816
crossEnd1 = pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength );
817
crossEnd2 = pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength );
818
p->drawLine( crossEnd1, crossEnd2 );
822
QPointF crossEnd2 = pointOnLineWithDistance( vIt->second.p2(), vIt->second.p1(), mCrossLength );
823
p->drawLine( vIt->second.p2(), crossEnd2 );
826
hIt = horizontalLines.constBegin();
827
for ( ; hIt != horizontalLines.constEnd(); ++hIt )
830
crossEnd1 = pointOnLineWithDistance( hIt->second.p1(), hIt->second.p2(), mCrossLength );
831
p->drawLine( hIt->second.p1(), crossEnd1 );
833
vIt = verticalLines.constBegin();
834
for ( ; vIt != verticalLines.constEnd(); ++vIt )
836
if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
838
crossEnd1 = pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength );
839
crossEnd2 = pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength );
840
p->drawLine( crossEnd1, crossEnd2 );
844
crossEnd1 = pointOnLineWithDistance( hIt->second.p2(), hIt->second.p1(), mCrossLength );
845
p->drawLine( hIt->second.p2(), crossEnd1 );
851
p->setClipRect( thisPaintRect , Qt::NoClip );
853
if ( mShowGridAnnotation )
855
drawCoordinateAnnotations( p, horizontalLines, verticalLines );
859
void QgsComposerMap::drawCoordinateAnnotations( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines )
867
QString currentAnnotationString;
868
QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
869
for ( ; it != hLines.constEnd(); ++it )
871
currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
872
drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
873
drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
876
it = vLines.constBegin();
877
for ( ; it != vLines.constEnd(); ++it )
879
currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
880
drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
881
drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
885
void QgsComposerMap::drawCoordinateAnnotation( QPainter* p, const QPointF& pos, QString annotationString )
887
Border frameBorder = borderForLineCoord( pos );
888
double textWidth = textWidthMillimeters( mGridAnnotationFont, annotationString );
889
double textHeight = fontAscentMillimeters( mGridAnnotationFont );
890
double xpos = pos.x();
891
double ypos = pos.y();
894
if ( frameBorder == Left )
897
if ( mGridAnnotationPosition == InsideMapFrame )
899
if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
901
xpos -= textHeight + mAnnotationFrameDistance;
902
ypos += textWidth / 2.0;
907
xpos += mAnnotationFrameDistance;
908
ypos += textHeight / 2.0;
911
else //Outside map frame
913
if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
915
xpos -= mAnnotationFrameDistance;
916
ypos += textWidth / 2.0;
921
xpos -= textWidth + mAnnotationFrameDistance;
922
ypos += textHeight / 2.0;
927
else if ( frameBorder == Right )
929
if ( mGridAnnotationPosition == InsideMapFrame )
931
if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
933
xpos -= mAnnotationFrameDistance;
934
ypos += textWidth / 2.0;
939
xpos -= textWidth + mAnnotationFrameDistance;
940
ypos += textHeight / 2.0;
943
else //OutsideMapFrame
945
if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
947
xpos += textHeight + mAnnotationFrameDistance;
948
ypos += textWidth / 2.0;
953
xpos += mAnnotationFrameDistance;
954
ypos += textHeight / 2.0;
958
else if ( frameBorder == Bottom )
960
if ( mGridAnnotationPosition == InsideMapFrame )
962
if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
964
ypos -= mAnnotationFrameDistance;
965
xpos -= textWidth / 2.0;
969
xpos += textHeight / 2.0;
970
ypos -= mAnnotationFrameDistance;
974
else //OutsideMapFrame
976
if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
978
ypos += mAnnotationFrameDistance + textHeight;
979
xpos -= textWidth / 2.0;
983
xpos += textHeight / 2.0;
984
ypos += textWidth + mAnnotationFrameDistance;
991
if ( mGridAnnotationPosition == InsideMapFrame )
993
if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
995
xpos -= textWidth / 2.0;
996
ypos += textHeight + mAnnotationFrameDistance;
1000
xpos += textHeight / 2.0;
1001
ypos += textWidth + mAnnotationFrameDistance;
1005
else //OutsideMapFrame
1007
if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
1009
xpos -= textWidth / 2.0;
1010
ypos -= mAnnotationFrameDistance;
1014
xpos += textHeight / 2.0;
1015
ypos -= mAnnotationFrameDistance;
1021
drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
1024
void QgsComposerMap::drawAnnotation( QPainter* p, const QPointF& pos, int rotation, const QString& annotationText )
1027
p->translate( pos );
1028
p->rotate( rotation );
1029
p->setPen( QColor( 0, 0, 0 ) );
1030
drawText( p, 0, 0, annotationText, mGridAnnotationFont );
1034
int QgsComposerMap::xGridLines( QList< QPair< double, QLineF > >& lines ) const
1037
if ( mGridIntervalY <= 0.0 )
1042
QPolygonF mapPolygon = transformedMapPolygon();
1043
QRectF mapBoundingRect = mapPolygon.boundingRect();
1044
double currentLevel = ( int )(( mapBoundingRect.top() - mGridOffsetY ) / mGridIntervalY + 1.0 ) * mGridIntervalY + mGridOffsetY;
1046
if ( mRotation <= 0.0 )
1048
//no rotation. Do it 'the easy way'
1050
double yCanvasCoord;
1052
while ( currentLevel <= mapBoundingRect.bottom() )
1054
yCanvasCoord = rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
1055
lines.push_back( qMakePair( currentLevel, QLineF( 0, yCanvasCoord, rect().width(), yCanvasCoord ) ) );
1056
currentLevel += mGridIntervalY;
1060
//the four border lines
1061
QVector<QLineF> borderLines;
1062
borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1063
borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1064
borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1065
borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1067
QList<QPointF> intersectionList; //intersects between border lines and grid lines
1069
while ( currentLevel <= mapBoundingRect.bottom() )
1071
intersectionList.clear();
1072
QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
1074
QVector<QLineF>::const_iterator it = borderLines.constBegin();
1075
for ( ; it != borderLines.constEnd(); ++it )
1077
QPointF intersectionPoint;
1078
if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1080
intersectionList.push_back( intersectionPoint );
1081
if ( intersectionList.size() >= 2 )
1083
break; //we already have two intersections, skip further tests
1088
if ( intersectionList.size() >= 2 )
1090
lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1092
currentLevel += mGridIntervalY;
1099
int QgsComposerMap::yGridLines( QList< QPair< double, QLineF > >& lines ) const
1102
if ( mGridIntervalX <= 0.0 )
1107
QPolygonF mapPolygon = transformedMapPolygon();
1108
QRectF mapBoundingRect = mapPolygon.boundingRect();
1109
double currentLevel = ( int )(( mapBoundingRect.left() - mGridOffsetX ) / mGridIntervalX + 1.0 ) * mGridIntervalX + mGridOffsetX;
1111
if ( mRotation <= 0.0 )
1113
//no rotation. Do it 'the easy way'
1114
double xCanvasCoord;
1116
while ( currentLevel <= mapBoundingRect.right() )
1118
xCanvasCoord = rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
1119
lines.push_back( qMakePair( currentLevel, QLineF( xCanvasCoord, 0, xCanvasCoord, rect().height() ) ) );
1120
currentLevel += mGridIntervalX;
1124
//the four border lines
1125
QVector<QLineF> borderLines;
1126
borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
1127
borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
1128
borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
1129
borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
1131
QList<QPointF> intersectionList; //intersects between border lines and grid lines
1133
while ( currentLevel <= mapBoundingRect.right() )
1135
intersectionList.clear();
1136
QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
1138
QVector<QLineF>::const_iterator it = borderLines.constBegin();
1139
for ( ; it != borderLines.constEnd(); ++it )
1141
QPointF intersectionPoint;
1142
if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
1144
intersectionList.push_back( intersectionPoint );
1145
if ( intersectionList.size() >= 2 )
1147
break; //we already have two intersections, skip further tests
1152
if ( intersectionList.size() >= 2 )
1154
lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
1156
currentLevel += mGridIntervalX;
1162
void QgsComposerMap::setGridPenWidth( double w )
1164
mGridPen.setWidthF( w );
1167
void QgsComposerMap::setGridPenColor( const QColor& c )
1169
mGridPen.setColor( c );
1172
QRectF QgsComposerMap::boundingRect() const
1174
return mCurrentRectangle;
1177
void QgsComposerMap::updateBoundingRect()
1179
QRectF rectangle = rect();
1180
double extension = maxExtension();
1181
rectangle.setLeft( rectangle.left() - extension );
1182
rectangle.setRight( rectangle.right() + extension );
1183
rectangle.setTop( rectangle.top() - extension );
1184
rectangle.setBottom( rectangle.bottom() + extension );
1185
if ( rectangle != mCurrentRectangle )
1187
prepareGeometryChange();
1188
mCurrentRectangle = rectangle;
1192
QgsRectangle QgsComposerMap::transformedExtent() const
1194
double dx = mXOffset;
1195
double dy = mYOffset;
1196
transformShift( dx, dy );
1197
return QgsRectangle( mExtent.xMinimum() - dx, mExtent.yMinimum() - dy, mExtent.xMaximum() - dx, mExtent.yMaximum() - dy );
1200
QPolygonF QgsComposerMap::transformedMapPolygon() const
1202
double dx = mXOffset;
1203
double dy = mYOffset;
1204
//qWarning("offset");
1205
//qWarning(QString::number(dx).toLocal8Bit().data());
1206
//qWarning(QString::number(dy).toLocal8Bit().data());
1207
transformShift( dx, dy );
1208
//qWarning("transformed:");
1209
//qWarning(QString::number(dx).toLocal8Bit().data());
1210
//qWarning(QString::number(dy).toLocal8Bit().data());
1213
poly.translate( -dx, -dy );
1217
double QgsComposerMap::maxExtension() const
1219
if ( !mGridEnabled || !mShowGridAnnotation || mGridAnnotationPosition != OutsideMapFrame )
1224
QList< QPair< double, QLineF > > xLines;
1225
QList< QPair< double, QLineF > > yLines;
1227
if ( xGridLines( xLines ) != 0 )
1232
if ( yGridLines( yLines ) != 0 )
1237
double maxExtension = 0;
1238
double currentExtension = 0;
1239
QString currentAnnotationString;
1241
QList< QPair< double, QLineF > >::const_iterator it = xLines.constBegin();
1242
for ( ; it != xLines.constEnd(); ++it )
1244
currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
1245
currentExtension = std::max( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
1246
maxExtension = std::max( maxExtension, currentExtension );
1249
it = yLines.constBegin();
1250
for ( ; it != yLines.constEnd(); ++it )
1252
currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
1253
currentExtension = std::max( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
1254
maxExtension = std::max( maxExtension, currentExtension );
1257
return maxExtension + mAnnotationFrameDistance;
1260
void QgsComposerMap::mapPolygon( QPolygonF& poly ) const
1263
if ( mRotation == 0 )
1265
poly << QPointF( mExtent.xMinimum(), mExtent.yMaximum() );
1266
poly << QPointF( mExtent.xMaximum(), mExtent.yMaximum() );
1267
poly << QPointF( mExtent.xMaximum(), mExtent.yMinimum() );
1268
poly << QPointF( mExtent.xMinimum(), mExtent.yMinimum() );
1273
QgsPoint rotationPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
1274
double dx, dy; //x-, y- shift from rotation point to corner point
1277
dx = rotationPoint.x() - mExtent.xMinimum();
1278
dy = rotationPoint.y() - mExtent.yMaximum();
1279
rotate( mRotation, dx, dy );
1280
poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1283
dx = rotationPoint.x() - mExtent.xMaximum();
1284
dy = rotationPoint.y() - mExtent.yMaximum();
1285
rotate( mRotation, dx, dy );
1286
poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1288
//bottom right point
1289
dx = rotationPoint.x() - mExtent.xMaximum();
1290
dy = rotationPoint.y() - mExtent.yMinimum();
1291
rotate( mRotation, dx, dy );
1292
poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1295
dx = rotationPoint.x() - mExtent.xMinimum();
1296
dy = rotationPoint.y() - mExtent.yMinimum();
1297
rotate( mRotation, dx, dy );
1298
poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
1301
void QgsComposerMap::requestedExtent( QgsRectangle& extent ) const
1303
if ( mRotation == 0 )
1311
QRectF bRect = poly.boundingRect();
1312
extent.setXMinimum( bRect.left() );
1313
extent.setXMaximum( bRect.right() );
1314
extent.setYMinimum( bRect.top() );
1315
extent.setYMaximum( bRect.bottom() );
1319
double QgsComposerMap::mapUnitsToMM() const
1321
double extentWidth = mExtent.width();
1322
if ( extentWidth <= 0 )
1326
return rect().width() / extentWidth;
1329
void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
1331
double mmToMapUnits = 1.0 / mapUnitsToMM();
1332
double dxScaled = xShift * mmToMapUnits;
1333
double dyScaled = - yShift * mmToMapUnits;
1335
rotate( mRotation, dxScaled, dyScaled );
1341
QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
1343
QPolygonF mapPoly = transformedMapPolygon();
1344
if ( mapPoly.size() < 1 )
1346
return QPointF( 0, 0 );
1349
QgsRectangle tExtent = transformedExtent();
1350
QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
1351
double dx = mapCoords.x() - rotationPoint.x();
1352
double dy = mapCoords.y() - rotationPoint.y();
1353
rotate( -mRotation, dx, dy );
1354
QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
1356
QgsRectangle unrotatedExtent = transformedExtent();
1357
double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
1358
double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
1359
return QPointF( xItem, yItem );
1362
QgsComposerMap::Border QgsComposerMap::borderForLineCoord( const QPointF& p ) const
1364
if ( p.x() <= pen().widthF() )
1368
else if ( p.x() >= ( rect().width() - pen().widthF() ) )
1372
else if ( p.y() <= pen().widthF() )