QGIS API Documentation  master-3f58142
src/core/composer/qgscomposerscalebar.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                            qgscomposerscalebar.cpp
00003                              -------------------
00004     begin                : March 2005
00005     copyright            : (C) 2005 by Radim Blazek
00006     email                : blazek@itc.it
00007  ***************************************************************************/
00008 /***************************************************************************
00009  *                                                                         *
00010  *   This program is free software; you can redistribute it and/or modify  *
00011  *   it under the terms of the GNU General Public License as published by  *
00012  *   the Free Software Foundation; either version 2 of the License, or     *
00013  *   (at your option) any later version.                                   *
00014  *                                                                         *
00015  ***************************************************************************/
00016 
00017 #include "qgscomposerscalebar.h"
00018 #include "qgscomposermap.h"
00019 #include "qgscomposition.h"
00020 #include "qgsdistancearea.h"
00021 #include "qgsscalebarstyle.h"
00022 #include "qgsdoubleboxscalebarstyle.h"
00023 #include "qgsmaprenderer.h"
00024 #include "qgsnumericscalebarstyle.h"
00025 #include "qgssingleboxscalebarstyle.h"
00026 #include "qgsticksscalebarstyle.h"
00027 #include "qgsrectangle.h"
00028 #include "qgsproject.h"
00029 #include <QDomDocument>
00030 #include <QDomElement>
00031 #include <QFontMetricsF>
00032 #include <QPainter>
00033 #include <QSettings>
00034 #include <cmath>
00035 
00036 QgsComposerScaleBar::QgsComposerScaleBar( QgsComposition* composition )
00037     : QgsComposerItem( composition )
00038     , mComposerMap( 0 )
00039     , mNumUnitsPerSegment( 0 )
00040     , mFontColor( QColor( 0, 0, 0 ) )
00041     , mStyle( 0 )
00042     , mSegmentMillimeters( 0.0 )
00043     , mAlignment( Left )
00044     , mUnits( MapUnits )
00045 {
00046   applyDefaultSettings();
00047   applyDefaultSize();
00048 }
00049 
00050 QgsComposerScaleBar::~QgsComposerScaleBar()
00051 {
00052   delete mStyle;
00053 }
00054 
00055 void QgsComposerScaleBar::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
00056 {
00057   Q_UNUSED( itemStyle );
00058   Q_UNUSED( pWidget );
00059   if ( !mStyle || !painter )
00060   {
00061     return;
00062   }
00063 
00064   drawBackground( painter );
00065 
00066   //x-offset is half of first label width because labels are drawn centered
00067   QString firstLabel = firstLabelString();
00068   double firstLabelWidth = textWidthMillimeters( mFont, firstLabel );
00069 
00070   mStyle->draw( painter, firstLabelWidth / 2 );
00071 
00072   //draw frame and selection boxes if necessary
00073   drawFrame( painter );
00074   if ( isSelected() )
00075   {
00076     drawSelectionBoxes( painter );
00077   }
00078 }
00079 
00080 void QgsComposerScaleBar::setNumSegments( int nSegments )
00081 {
00082   if ( !mStyle )
00083   {
00084     mNumSegments = nSegments;
00085     return;
00086   }
00087   double width = mStyle->calculateBoxSize().width();
00088   mNumSegments = nSegments;
00089   double widthAfter = mStyle->calculateBoxSize().width();
00090   correctXPositionAlignment( width, widthAfter );
00091   emit itemChanged();
00092 }
00093 
00094 void QgsComposerScaleBar::setNumUnitsPerSegment( double units )
00095 {
00096   if ( !mStyle )
00097   {
00098     mNumUnitsPerSegment = units;
00099     return;
00100   }
00101   double width = mStyle->calculateBoxSize().width();
00102   mNumUnitsPerSegment = units;
00103   refreshSegmentMillimeters();
00104   double widthAfter = mStyle->calculateBoxSize().width();
00105   correctXPositionAlignment( width, widthAfter );
00106   emit itemChanged();
00107 }
00108 
00109 void QgsComposerScaleBar::setNumSegmentsLeft( int nSegmentsLeft )
00110 {
00111   if ( !mStyle )
00112   {
00113     mNumSegmentsLeft = nSegmentsLeft;
00114     return;
00115   }
00116   double width = mStyle->calculateBoxSize().width();
00117   mNumSegmentsLeft = nSegmentsLeft;
00118   double widthAfter = mStyle->calculateBoxSize().width();
00119   correctXPositionAlignment( width, widthAfter );
00120   emit itemChanged();
00121 }
00122 
00123 void QgsComposerScaleBar::setBoxContentSpace( double space )
00124 {
00125   if ( !mStyle )
00126   {
00127     mBoxContentSpace = space;
00128     return;
00129   }
00130   double width = mStyle->calculateBoxSize().width();
00131   mBoxContentSpace = space;
00132   double widthAfter = mStyle->calculateBoxSize().width();
00133   correctXPositionAlignment( width, widthAfter );
00134   emit itemChanged();
00135 }
00136 
00137 void QgsComposerScaleBar::setComposerMap( const QgsComposerMap* map )
00138 {
00139   disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
00140   disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
00141   mComposerMap = map;
00142 
00143   if ( !map )
00144   {
00145     return;
00146   }
00147 
00148   connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
00149   connect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
00150 
00151   refreshSegmentMillimeters();
00152   emit itemChanged();
00153 }
00154 
00155 void QgsComposerScaleBar::invalidateCurrentMap()
00156 {
00157   disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
00158   disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
00159   mComposerMap = 0;
00160 }
00161 
00162 void QgsComposerScaleBar::refreshSegmentMillimeters()
00163 {
00164   if ( mComposerMap )
00165   {
00166     //get extent of composer map
00167     QgsRectangle composerMapRect = mComposerMap->extent();
00168 
00169     //get mm dimension of composer map
00170     QRectF composerItemRect = mComposerMap->rect();
00171 
00172     //calculate size depending on mNumUnitsPerSegment
00173     mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
00174   }
00175 }
00176 
00177 double QgsComposerScaleBar::mapWidth() const
00178 {
00179   if ( !mComposerMap )
00180   {
00181     return 0.0;
00182   }
00183 
00184   QgsRectangle composerMapRect = mComposerMap->extent();
00185   if ( mUnits == MapUnits )
00186   {
00187     return composerMapRect.width();
00188   }
00189   else
00190   {
00191     QgsDistanceArea da;
00192     da.setEllipsoidalMode( mComposerMap->mapRenderer()->hasCrsTransformEnabled() );
00193     da.setSourceCrs( mComposerMap->mapRenderer()->destinationCrs().srsid() );
00194     da.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", "WGS84" ) );
00195 
00196     double measure = da.measureLine( QgsPoint( composerMapRect.xMinimum(), composerMapRect.yMinimum() ), QgsPoint( composerMapRect.xMaximum(), composerMapRect.yMinimum() ) );
00197     if ( mUnits == Feet )
00198     {
00199       measure /= 0.3048;
00200     }
00201     return measure;
00202   }
00203 }
00204 
00205 void QgsComposerScaleBar::setUnits( ScaleBarUnits u )
00206 {
00207   mUnits = u;
00208   refreshSegmentMillimeters();
00209   emit itemChanged();
00210 }
00211 
00212 void QgsComposerScaleBar::applyDefaultSettings()
00213 {
00214   mNumSegments = 2;
00215   mNumSegmentsLeft = 0;
00216 
00217   mNumMapUnitsPerScaleBarUnit = 1.0;
00218 
00219   //style
00220   delete mStyle;
00221   mStyle = new QgsSingleBoxScaleBarStyle( this );
00222 
00223   mHeight = 3;
00224 
00225   mPen = QPen( QColor( 0, 0, 0 ) );
00226   mPen.setWidthF( 1.0 );
00227 
00228   mBrush.setColor( QColor( 0, 0, 0 ) );
00229   mBrush.setStyle( Qt::SolidPattern );
00230 
00231   mFont.setPointSizeF( 12.0 );
00232   mFontColor = QColor( 0, 0, 0 );
00233 
00234   mLabelBarSpace = 3.0;
00235   mBoxContentSpace = 1.0;
00236   emit itemChanged();
00237 }
00238 
00239 void QgsComposerScaleBar::applyDefaultSize()
00240 {
00241   if ( mComposerMap )
00242   {
00243     setUnits( Meters );
00244     double widthMeter = mapWidth();
00245     int nUnitsPerSegment =  widthMeter / 10.0; //default scalebar width equals half the map width
00246     setNumUnitsPerSegment( nUnitsPerSegment );
00247 
00248     if ( nUnitsPerSegment > 1000 )
00249     {
00250       setNumUnitsPerSegment(( int )( numUnitsPerSegment() / 1000.0 + 0.5 ) * 1000 );
00251       setUnitLabeling( tr( "km" ) );
00252       setNumMapUnitsPerScaleBarUnit( 1000 );
00253     }
00254     else
00255     {
00256       setUnitLabeling( tr( "m" ) );
00257     }
00258 
00259     setNumSegments( 4 );
00260     setNumSegmentsLeft( 2 );
00261   }
00262 
00263   refreshSegmentMillimeters();
00264   adjustBoxSize();
00265   emit itemChanged();
00266 }
00267 
00268 void QgsComposerScaleBar::adjustBoxSize()
00269 {
00270   if ( !mStyle )
00271   {
00272     return;
00273   }
00274 
00275   QRectF box = mStyle->calculateBoxSize();
00276   setSceneRect( box );
00277 }
00278 
00279 void QgsComposerScaleBar::update()
00280 {
00281   adjustBoxSize();
00282   QgsComposerItem::update();
00283 }
00284 
00285 void QgsComposerScaleBar::updateSegmentSize()
00286 {
00287   if ( !mStyle )
00288   {
00289     return;
00290   }
00291   double width = mStyle->calculateBoxSize().width();
00292   refreshSegmentMillimeters();
00293   double widthAfter = mStyle->calculateBoxSize().width();
00294   correctXPositionAlignment( width, widthAfter );
00295   update();
00296   emit itemChanged();
00297 }
00298 
00299 void QgsComposerScaleBar::segmentPositions( QList<QPair<double, double> >& posWidthList ) const
00300 {
00301   posWidthList.clear();
00302   double mCurrentXCoord = mPen.widthF() + mBoxContentSpace;
00303 
00304   //left segments
00305   for ( int i = 0; i < mNumSegmentsLeft; ++i )
00306   {
00307     posWidthList.push_back( qMakePair( mCurrentXCoord, mSegmentMillimeters / mNumSegmentsLeft ) );
00308     mCurrentXCoord += mSegmentMillimeters / mNumSegmentsLeft;
00309   }
00310 
00311   //right segments
00312   for ( int i = 0; i < mNumSegments; ++i )
00313   {
00314     posWidthList.push_back( qMakePair( mCurrentXCoord, mSegmentMillimeters ) );
00315     mCurrentXCoord += mSegmentMillimeters;
00316   }
00317 }
00318 
00319 void QgsComposerScaleBar::setStyle( const QString& styleName )
00320 {
00321   delete mStyle;
00322   mStyle = 0;
00323 
00324   //switch depending on style name
00325   if ( styleName == "Single Box" )
00326   {
00327     mStyle = new QgsSingleBoxScaleBarStyle( this );
00328   }
00329   else if ( styleName == "Double Box" )
00330   {
00331     mStyle = new QgsDoubleBoxScaleBarStyle( this );
00332   }
00333   else if ( styleName == "Line Ticks Middle"  || styleName == "Line Ticks Down" || styleName == "Line Ticks Up" )
00334   {
00335     QgsTicksScaleBarStyle* tickStyle = new QgsTicksScaleBarStyle( this );
00336     if ( styleName == "Line Ticks Middle" )
00337     {
00338       tickStyle->setTickPosition( QgsTicksScaleBarStyle::TicksMiddle );
00339     }
00340     else if ( styleName == "Line Ticks Down" )
00341     {
00342       tickStyle->setTickPosition( QgsTicksScaleBarStyle::TicksDown );
00343     }
00344     else if ( styleName == "Line Ticks Up" )
00345     {
00346       tickStyle->setTickPosition( QgsTicksScaleBarStyle::TicksUp );
00347     }
00348     mStyle = tickStyle;
00349   }
00350   else if ( styleName == "Numeric" )
00351   {
00352     mStyle = new QgsNumericScaleBarStyle( this );
00353   }
00354   emit itemChanged();
00355 }
00356 
00357 QString QgsComposerScaleBar::style() const
00358 {
00359   if ( mStyle )
00360   {
00361     return mStyle->name();
00362   }
00363   else
00364   {
00365     return "";
00366   }
00367 }
00368 
00369 QString QgsComposerScaleBar::firstLabelString() const
00370 {
00371   if ( mNumSegmentsLeft > 0 )
00372   {
00373     return QString::number( mNumUnitsPerSegment / mNumMapUnitsPerScaleBarUnit );
00374   }
00375   else
00376   {
00377     return "0";
00378   }
00379 }
00380 
00381 QFont QgsComposerScaleBar::font() const
00382 {
00383   return mFont;
00384 }
00385 
00386 void QgsComposerScaleBar::setFont( const QFont& font )
00387 {
00388   mFont = font;
00389   adjustBoxSize();
00390   update();
00391   emit itemChanged();
00392 }
00393 
00394 bool QgsComposerScaleBar::writeXML( QDomElement& elem, QDomDocument & doc ) const
00395 {
00396   if ( elem.isNull() )
00397   {
00398     return false;
00399   }
00400 
00401   QDomElement composerScaleBarElem = doc.createElement( "ComposerScaleBar" );
00402   composerScaleBarElem.setAttribute( "height", QString::number( mHeight ) );
00403   composerScaleBarElem.setAttribute( "labelBarSpace", QString::number( mLabelBarSpace ) );
00404   composerScaleBarElem.setAttribute( "boxContentSpace", QString::number( mBoxContentSpace ) );
00405   composerScaleBarElem.setAttribute( "numSegments", mNumSegments );
00406   composerScaleBarElem.setAttribute( "numSegmentsLeft", mNumSegmentsLeft );
00407   composerScaleBarElem.setAttribute( "numUnitsPerSegment", QString::number( mNumUnitsPerSegment ) );
00408   composerScaleBarElem.setAttribute( "segmentMillimeters", QString::number( mSegmentMillimeters ) );
00409   composerScaleBarElem.setAttribute( "numMapUnitsPerScaleBarUnit", QString::number( mNumMapUnitsPerScaleBarUnit ) );
00410   composerScaleBarElem.setAttribute( "font", mFont.toString() );
00411   composerScaleBarElem.setAttribute( "outlineWidth", QString::number( mPen.widthF() ) );
00412   composerScaleBarElem.setAttribute( "unitLabel", mUnitLabeling );
00413   composerScaleBarElem.setAttribute( "units", mUnits );
00414 
00415   //style
00416   if ( mStyle )
00417   {
00418     composerScaleBarElem.setAttribute( "style", mStyle->name() );
00419   }
00420 
00421   //map id
00422   if ( mComposerMap )
00423   {
00424     composerScaleBarElem.setAttribute( "mapId", mComposerMap->id() );
00425   }
00426 
00427   //colors
00428   composerScaleBarElem.setAttribute( "brushColor", mBrush.color().name() );
00429   composerScaleBarElem.setAttribute( "penColor", mPen.color().name() );
00430   composerScaleBarElem.setAttribute( "fontColor", mFontColor.name() );
00431 
00432   //alignment
00433   composerScaleBarElem.setAttribute( "alignment", QString::number(( int ) mAlignment ) );
00434 
00435   elem.appendChild( composerScaleBarElem );
00436   return _writeXML( composerScaleBarElem, doc );
00437 }
00438 
00439 bool QgsComposerScaleBar::readXML( const QDomElement& itemElem, const QDomDocument& doc )
00440 {
00441   if ( itemElem.isNull() )
00442   {
00443     return false;
00444   }
00445 
00446   mHeight = itemElem.attribute( "height", "5.0" ).toDouble();
00447   mLabelBarSpace = itemElem.attribute( "labelBarSpace", "3.0" ).toDouble();
00448   mBoxContentSpace = itemElem.attribute( "boxContentSpace", "1.0" ).toDouble();
00449   mNumSegments = itemElem.attribute( "numSegments", "2" ).toInt();
00450   mNumSegmentsLeft = itemElem.attribute( "numSegmentsLeft", "0" ).toInt();
00451   mNumUnitsPerSegment = itemElem.attribute( "numUnitsPerSegment", "1.0" ).toDouble();
00452   mSegmentMillimeters = itemElem.attribute( "segmentMillimeters", "0.0" ).toDouble();
00453   mNumMapUnitsPerScaleBarUnit = itemElem.attribute( "numMapUnitsPerScaleBarUnit", "1.0" ).toDouble();
00454   mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
00455   mUnitLabeling = itemElem.attribute( "unitLabel" );
00456   QString fontString = itemElem.attribute( "font", "" );
00457   if ( !fontString.isEmpty() )
00458   {
00459     mFont.fromString( fontString );
00460   }
00461 
00462   //colors
00463   //fill color
00464   mBrush.setColor( QColor( itemElem.attribute( "brushColor", "#000000" ) ) );
00465   mPen.setColor( QColor( itemElem.attribute( "penColor", "#000000" ) ) );
00466   mFontColor.setNamedColor( itemElem.attribute( "fontColor", "#000000" ) );
00467 
00468   //style
00469   delete mStyle;
00470   mStyle = 0;
00471   QString styleString = itemElem.attribute( "style", "" );
00472   setStyle( tr( styleString.toLocal8Bit().data() ) );
00473 
00474   mUnits = ( ScaleBarUnits )itemElem.attribute( "units" ).toInt();
00475   mAlignment = ( Alignment )( itemElem.attribute( "alignment", "0" ).toInt() );
00476 
00477   //map
00478   int mapId = itemElem.attribute( "mapId", "-1" ).toInt();
00479   if ( mapId >= 0 )
00480   {
00481     const QgsComposerMap* composerMap = mComposition->getComposerMapById( mapId );
00482     mComposerMap = composerMap;
00483     if ( mComposerMap )
00484     {
00485       connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
00486       connect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
00487     }
00488   }
00489 
00490   updateSegmentSize();
00491 
00492   //restore general composer item properties
00493   QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
00494   if ( composerItemList.size() > 0 )
00495   {
00496     QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
00497     _readXML( composerItemElem, doc );
00498   }
00499 
00500   return true;
00501 }
00502 
00503 void QgsComposerScaleBar::correctXPositionAlignment( double width, double widthAfter )
00504 {
00505   if ( mAlignment == Middle )
00506   {
00507     move( -( widthAfter - width ) / 2.0, 0 );
00508   }
00509   else if ( mAlignment == Right )
00510   {
00511     move( -( widthAfter - width ), 0 );
00512   }
00513 }
00514 
00515 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines