QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposerscalebar.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerscalebar.cpp
3  -------------------
4  begin : March 2005
5  copyright : (C) 2005 by Radim Blazek
6  email : blazek@itc.it
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgscomposerscalebar.h"
18 #include "qgscomposermap.h"
19 #include "qgscomposition.h"
20 #include "qgsdistancearea.h"
21 #include "qgsscalebarstyle.h"
23 #include "qgsmaprenderer.h"
26 #include "qgsticksscalebarstyle.h"
27 #include "qgsrectangle.h"
28 #include "qgsproject.h"
29 #include <QDomDocument>
30 #include <QDomElement>
31 #include <QFontMetricsF>
32 #include <QPainter>
33 #include <QSettings>
34 #include <cmath>
35 
37  : QgsComposerItem( composition )
38  , mComposerMap( 0 )
39  , mNumUnitsPerSegment( 0 )
40  , mFontColor( QColor( 0, 0, 0 ) )
41  , mStyle( 0 )
42  , mSegmentMillimeters( 0.0 )
43  , mAlignment( Left )
44  , mUnits( MapUnits )
45 {
48 }
49 
51 {
52  delete mStyle;
53 }
54 
55 void QgsComposerScaleBar::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
56 {
57  Q_UNUSED( itemStyle );
58  Q_UNUSED( pWidget );
59  if ( !mStyle || !painter )
60  {
61  return;
62  }
63 
64  drawBackground( painter );
65 
66  //x-offset is half of first label width because labels are drawn centered
67  QString firstLabel = firstLabelString();
68  double firstLabelWidth = textWidthMillimeters( mFont, firstLabel );
69 
70  mStyle->draw( painter, firstLabelWidth / 2 );
71 
72  //draw frame and selection boxes if necessary
73  drawFrame( painter );
74  if ( isSelected() )
75  {
76  drawSelectionBoxes( painter );
77  }
78 }
79 
81 {
82  if ( !mStyle )
83  {
84  mNumSegments = nSegments;
85  return;
86  }
87  double width = mStyle->calculateBoxSize().width();
88  mNumSegments = nSegments;
89  double widthAfter = mStyle->calculateBoxSize().width();
90  correctXPositionAlignment( width, widthAfter );
91  emit itemChanged();
92 }
93 
95 {
96  if ( !mStyle )
97  {
99  return;
100  }
101  double width = mStyle->calculateBoxSize().width();
104  double widthAfter = mStyle->calculateBoxSize().width();
105  correctXPositionAlignment( width, widthAfter );
106  emit itemChanged();
107 }
108 
110 {
111  if ( !mStyle )
112  {
113  mNumSegmentsLeft = nSegmentsLeft;
114  return;
115  }
116  double width = mStyle->calculateBoxSize().width();
117  mNumSegmentsLeft = nSegmentsLeft;
118  double widthAfter = mStyle->calculateBoxSize().width();
119  correctXPositionAlignment( width, widthAfter );
120  emit itemChanged();
121 }
122 
124 {
125  if ( !mStyle )
126  {
127  mBoxContentSpace = space;
128  return;
129  }
130  double width = mStyle->calculateBoxSize().width();
131  mBoxContentSpace = space;
132  double widthAfter = mStyle->calculateBoxSize().width();
133  correctXPositionAlignment( width, widthAfter );
134  emit itemChanged();
135 }
136 
138 {
139  if ( mComposerMap )
140  {
141  disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
142  disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
143  }
144  mComposerMap = map;
145 
146  if ( !map )
147  {
148  return;
149  }
150 
151  connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
152  connect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
153 
155  emit itemChanged();
156 }
157 
159 {
160  if ( !mComposerMap )
161  {
162  return;
163  }
164 
165  disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
166  disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
167  mComposerMap = 0;
168 }
169 
171 {
172  if ( mComposerMap )
173  {
174  //get extent of composer map
175  QgsRectangle composerMapRect = *( mComposerMap->currentMapExtent() );
176 
177  //get mm dimension of composer map
178  QRectF composerItemRect = mComposerMap->rect();
179 
180  //calculate size depending on mNumUnitsPerSegment
181  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
182  }
183 }
184 
186 {
187  if ( !mComposerMap )
188  {
189  return 0.0;
190  }
191 
192  QgsRectangle composerMapRect = *( mComposerMap->currentMapExtent() );
193  if ( mUnits == MapUnits )
194  {
195  return composerMapRect.width();
196  }
197  else
198  {
199  QgsDistanceArea da;
202  da.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", "WGS84" ) );
203 
204  double measure = da.measureLine( QgsPoint( composerMapRect.xMinimum(), composerMapRect.yMinimum() ), QgsPoint( composerMapRect.xMaximum(), composerMapRect.yMinimum() ) );
206  {
208  }
210  {
212  }
213  return measure;
214  }
215 }
216 
218 {
219  mAlignment = a;
220  update();
221  emit itemChanged();
222 }
223 
225 {
226  mUnits = u;
228  emit itemChanged();
229 }
230 
232 {
233  mNumSegments = 2;
234  mNumSegmentsLeft = 0;
235 
237 
238  //style
239  delete mStyle;
240  mStyle = new QgsSingleBoxScaleBarStyle( this );
241 
242  mHeight = 3;
243 
244  mPen = QPen( QColor( 0, 0, 0 ) );
245  mPen.setJoinStyle( Qt::MiterJoin );
246  mPen.setWidthF( 1.0 );
247 
248  mBrush.setColor( QColor( 0, 0, 0 ) );
249  mBrush.setStyle( Qt::SolidPattern );
250 
251  //get default composer font from settings
252  QSettings settings;
253  QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
254  if ( !defaultFontString.isEmpty() )
255  {
256  mFont.setFamily( defaultFontString );
257  }
258  mFont.setPointSizeF( 12.0 );
259  mFontColor = QColor( 0, 0, 0 );
260 
261  mLabelBarSpace = 3.0;
262  mBoxContentSpace = 1.0;
263  emit itemChanged();
264 }
265 
267 {
268  if ( mComposerMap )
269  {
270  setUnits( u );
271  double upperMagnitudeMultiplier = 1.0;
272  double widthInSelectedUnits = mapWidth();
273  double initialUnitsPerSegment = widthInSelectedUnits / 10.0; //default scalebar width equals half the map width
274  setNumUnitsPerSegment( initialUnitsPerSegment );
275 
276  switch ( mUnits )
277  {
278  case MapUnits:
279  {
280  upperMagnitudeMultiplier = 1.0;
281  setUnitLabeling( tr( "units" ) );
282  break;
283  }
284  case Meters:
285  {
286  if ( initialUnitsPerSegment > 1000.0 )
287  {
288  upperMagnitudeMultiplier = 1000.0;
289  setUnitLabeling( tr( "km" ) );
290  }
291  else
292  {
293  upperMagnitudeMultiplier = 1.0;
294  setUnitLabeling( tr( "m" ) );
295  }
296  break;
297  }
298  case Feet:
299  {
300  if ( initialUnitsPerSegment > 5419.95 )
301  {
302  upperMagnitudeMultiplier = 5419.95;
303  setUnitLabeling( tr( "miles" ) );
304  }
305  else
306  {
307  upperMagnitudeMultiplier = 1.0;
308  setUnitLabeling( tr( "ft" ) );
309  }
310  break;
311  }
312  case NauticalMiles:
313  {
314  upperMagnitudeMultiplier = 1;
315  setUnitLabeling( tr( "Nm" ) );
316  break;
317  }
318  }
319 
320  double segmentWidth = initialUnitsPerSegment / upperMagnitudeMultiplier;
321  int segmentMagnitude = floor( log10( segmentWidth ) );
322  double unitsPerSegment = upperMagnitudeMultiplier * ( pow( 10.0, segmentMagnitude ) );
323  double multiplier = floor(( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5;
324 
325  if ( multiplier > 0 )
326  {
327  unitsPerSegment = unitsPerSegment * multiplier;
328  }
329  setNumUnitsPerSegment( unitsPerSegment );
330  setNumMapUnitsPerScaleBarUnit( upperMagnitudeMultiplier );
331 
332  setNumSegments( 4 );
333  setNumSegmentsLeft( 2 );
334  }
335 
337  adjustBoxSize();
338  emit itemChanged();
339 }
340 
342 {
343  if ( !mStyle )
344  {
345  return;
346  }
347 
348  QRectF box = mStyle->calculateBoxSize();
349  setSceneRect( box );
350 }
351 
353 {
354  //Don't adjust box size for numeric scale bars:
355  if ( mStyle->name() != "Numeric" )
356  {
357  adjustBoxSize();
358  }
359  QgsComposerItem::update();
360 }
361 
363 {
364  if ( !mStyle )
365  {
366  return;
367  }
368  double width = mStyle->calculateBoxSize().width();
370  double widthAfter = mStyle->calculateBoxSize().width();
371  correctXPositionAlignment( width, widthAfter );
372  update();
373  emit itemChanged();
374 }
375 
376 void QgsComposerScaleBar::segmentPositions( QList<QPair<double, double> >& posWidthList ) const
377 {
378  posWidthList.clear();
379  double mCurrentXCoord = mPen.widthF() + mBoxContentSpace;
380 
381  //left segments
382  for ( int i = 0; i < mNumSegmentsLeft; ++i )
383  {
384  posWidthList.push_back( qMakePair( mCurrentXCoord, mSegmentMillimeters / mNumSegmentsLeft ) );
385  mCurrentXCoord += mSegmentMillimeters / mNumSegmentsLeft;
386  }
387 
388  //right segments
389  for ( int i = 0; i < mNumSegments; ++i )
390  {
391  posWidthList.push_back( qMakePair( mCurrentXCoord, mSegmentMillimeters ) );
392  mCurrentXCoord += mSegmentMillimeters;
393  }
394 }
395 
396 void QgsComposerScaleBar::setStyle( const QString& styleName )
397 {
398  delete mStyle;
399  mStyle = 0;
400 
401  //switch depending on style name
402  if ( styleName == "Single Box" )
403  {
404  mStyle = new QgsSingleBoxScaleBarStyle( this );
405  }
406  else if ( styleName == "Double Box" )
407  {
408  mStyle = new QgsDoubleBoxScaleBarStyle( this );
409  }
410  else if ( styleName == "Line Ticks Middle" || styleName == "Line Ticks Down" || styleName == "Line Ticks Up" )
411  {
412  QgsTicksScaleBarStyle* tickStyle = new QgsTicksScaleBarStyle( this );
413  if ( styleName == "Line Ticks Middle" )
414  {
416  }
417  else if ( styleName == "Line Ticks Down" )
418  {
420  }
421  else if ( styleName == "Line Ticks Up" )
422  {
424  }
425  mStyle = tickStyle;
426  }
427  else if ( styleName == "Numeric" )
428  {
429  mStyle = new QgsNumericScaleBarStyle( this );
430  }
431  emit itemChanged();
432 }
433 
435 {
436  if ( mStyle )
437  {
438  return mStyle->name();
439  }
440  else
441  {
442  return "";
443  }
444 }
445 
447 {
448  if ( mNumSegmentsLeft > 0 )
449  {
450  return QString::number( mNumUnitsPerSegment / mNumMapUnitsPerScaleBarUnit );
451  }
452  else
453  {
454  return "0";
455  }
456 }
457 
459 {
460  return mFont;
461 }
462 
463 void QgsComposerScaleBar::setFont( const QFont& font )
464 {
465  mFont = font;
466  update();
467  emit itemChanged();
468 }
469 
470 bool QgsComposerScaleBar::writeXML( QDomElement& elem, QDomDocument & doc ) const
471 {
472  if ( elem.isNull() )
473  {
474  return false;
475  }
476 
477  QDomElement composerScaleBarElem = doc.createElement( "ComposerScaleBar" );
478  composerScaleBarElem.setAttribute( "height", QString::number( mHeight ) );
479  composerScaleBarElem.setAttribute( "labelBarSpace", QString::number( mLabelBarSpace ) );
480  composerScaleBarElem.setAttribute( "boxContentSpace", QString::number( mBoxContentSpace ) );
481  composerScaleBarElem.setAttribute( "numSegments", mNumSegments );
482  composerScaleBarElem.setAttribute( "numSegmentsLeft", mNumSegmentsLeft );
483  composerScaleBarElem.setAttribute( "numUnitsPerSegment", QString::number( mNumUnitsPerSegment ) );
484  composerScaleBarElem.setAttribute( "segmentMillimeters", QString::number( mSegmentMillimeters ) );
485  composerScaleBarElem.setAttribute( "numMapUnitsPerScaleBarUnit", QString::number( mNumMapUnitsPerScaleBarUnit ) );
486  composerScaleBarElem.setAttribute( "font", mFont.toString() );
487  composerScaleBarElem.setAttribute( "outlineWidth", QString::number( mPen.widthF() ) );
488  composerScaleBarElem.setAttribute( "unitLabel", mUnitLabeling );
489  composerScaleBarElem.setAttribute( "units", mUnits );
490 
491  //style
492  if ( mStyle )
493  {
494  composerScaleBarElem.setAttribute( "style", mStyle->name() );
495  }
496 
497  //map id
498  if ( mComposerMap )
499  {
500  composerScaleBarElem.setAttribute( "mapId", mComposerMap->id() );
501  }
502 
503  //colors
504  composerScaleBarElem.setAttribute( "brushColor", mBrush.color().name() );
505  composerScaleBarElem.setAttribute( "penColor", mPen.color().name() );
506  composerScaleBarElem.setAttribute( "fontColor", mFontColor.name() );
507 
508  //alignment
509  composerScaleBarElem.setAttribute( "alignment", QString::number(( int ) mAlignment ) );
510 
511  elem.appendChild( composerScaleBarElem );
512  return _writeXML( composerScaleBarElem, doc );
513 }
514 
515 bool QgsComposerScaleBar::readXML( const QDomElement& itemElem, const QDomDocument& doc )
516 {
517  if ( itemElem.isNull() )
518  {
519  return false;
520  }
521 
522  mHeight = itemElem.attribute( "height", "5.0" ).toDouble();
523  mLabelBarSpace = itemElem.attribute( "labelBarSpace", "3.0" ).toDouble();
524  mBoxContentSpace = itemElem.attribute( "boxContentSpace", "1.0" ).toDouble();
525  mNumSegments = itemElem.attribute( "numSegments", "2" ).toInt();
526  mNumSegmentsLeft = itemElem.attribute( "numSegmentsLeft", "0" ).toInt();
527  mNumUnitsPerSegment = itemElem.attribute( "numUnitsPerSegment", "1.0" ).toDouble();
528  mSegmentMillimeters = itemElem.attribute( "segmentMillimeters", "0.0" ).toDouble();
529  mNumMapUnitsPerScaleBarUnit = itemElem.attribute( "numMapUnitsPerScaleBarUnit", "1.0" ).toDouble();
530  mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
531  mUnitLabeling = itemElem.attribute( "unitLabel" );
532  QString fontString = itemElem.attribute( "font", "" );
533  if ( !fontString.isEmpty() )
534  {
535  mFont.fromString( fontString );
536  }
537 
538  //colors
539  //fill color
540  mBrush.setColor( QColor( itemElem.attribute( "brushColor", "#000000" ) ) );
541  mPen.setColor( QColor( itemElem.attribute( "penColor", "#000000" ) ) );
542  mFontColor.setNamedColor( itemElem.attribute( "fontColor", "#000000" ) );
543 
544  //style
545  delete mStyle;
546  mStyle = 0;
547  QString styleString = itemElem.attribute( "style", "" );
548  setStyle( tr( styleString.toLocal8Bit().data() ) );
549 
550  mUnits = ( ScaleBarUnits )itemElem.attribute( "units" ).toInt();
551  mAlignment = ( Alignment )( itemElem.attribute( "alignment", "0" ).toInt() );
552 
553  //map
554  int mapId = itemElem.attribute( "mapId", "-1" ).toInt();
555  if ( mapId >= 0 )
556  {
559  if ( mComposerMap )
560  {
561  connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
562  connect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
563  }
564  }
565 
567 
568  //restore general composer item properties
569  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
570  if ( composerItemList.size() > 0 )
571  {
572  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
573  _readXML( composerItemElem, doc );
574  }
575 
576  return true;
577 }
578 
579 void QgsComposerScaleBar::correctXPositionAlignment( double width, double widthAfter )
580 {
581  //Don't adjust position for numeric scale bars:
582  if ( mStyle->name() == "Numeric" )
583  {
584  return;
585  }
586 
587  if ( mAlignment == Middle )
588  {
589  move( -( widthAfter - width ) / 2.0, 0 );
590  }
591  else if ( mAlignment == Right )
592  {
593  move( -( widthAfter - width ), 0 );
594  }
595 }
596 
597 
Double box with alternating colors.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
ScaleBarUnits
Added in version 1.9.
double mLabelBarSpace
Space between bar and Text labels.
double mHeight
Height of bars/lines.
double mNumUnitsPerSegment
Size of a segment (in map units)
bool writeXML(QDomElement &elem, QDomDocument &doc) const
stores state in Dom element
void setUnits(ScaleBarUnits u)
double mNumMapUnitsPerScaleBarUnit
Number of map units per scale bar units (e.g.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc)
sets state from Dom document
void setFont(const QFont &font)
A scale bar style that draws text in the form of '1:XXXXX'.
void setSourceCrs(long srsid)
sets source spatial reference system (by QGIS CRS)
void setUnitLabeling(const QString &label)
void applyDefaultSize(ScaleBarUnits u=Meters)
Apply default size (scale bar 1/5 of map item width)
void setAlignment(Alignment a)
void applyDefaultSettings()
Apply default settings.
A item that forms part of a map composition.
void setNumSegments(int nSegments)
void segmentPositions(QList< QPair< double, double > > &posWidthList) const
Returns the x - positions of the segment borders (in item coordinates) and the width of the segment...
virtual QRectF calculateBoxSize() const
void setNumSegmentsLeft(int nSegmentsLeft)
ScaleBarUnits units() const
virtual void drawFrame(QPainter *p)
Draw black frame around item.
QgsScaleBarStyle * mStyle
Scalebar style.
virtual QString name() const =0
bool setEllipsoid(const QString &ellipsoid)
sets ellipsoid by its acronym
void update()
Adjusts box size and calls QgsComposerItem::update()
A scale bar that draws segments using short ticks.
void setNumMapUnitsPerScaleBarUnit(double d)
void adjustBoxSize()
Sets box size suitable to content.
void itemChanged()
Used e.g.
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
double mSegmentMillimeters
Width of a segment (in mm)
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:194
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:179
virtual void drawSelectionBoxes(QPainter *p)
Draw selection boxes around item.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
void setStyle(const QString &styleName)
Sets style by name.
virtual void draw(QPainter *p, double xOffset=0) const =0
Draws the style.
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
const QgsMapRenderer * mapRenderer() const
QgsComposerScaleBar(QgsComposition *composition)
Alignment
Added in version 1.8.
int mNumSegmentsLeft
Number of segments on left side.
QgsComposition * mComposition
A class to represent a point geometry.
Definition: qgspoint.h:63
const QgsComposerMap * composerMap() const
Graphics scene for map printing.
Object representing map window.
QString style() const
Returns style name.
QgsRectangle * currentMapExtent()
Returns a pointer to the current map extent, which is either the original user specified extent or th...
void setComposerMap(const QgsComposerMap *map)
void refreshSegmentMillimeters()
Calculates with of a segment in mm and stores it in mSegmentMillimeters.
void invalidateCurrentMap()
Sets mCompositionMap to 0 if the map is deleted.
void setBoxContentSpace(double space)
int id() const
Get identification number.
General purpose distance and area calculator.
int mNumSegments
Number of segments on right side.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget)
Reimplementation of QCanvasItem::paint.
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
virtual void drawBackground(QPainter *p)
Draw background.
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:362
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
double measureLine(const QList< QgsPoint > &points)
measures line
double mBoxContentSpace
Space between content and item box.
static double fromUnitToUnitFactor(QGis::UnitType fromUnit, QGis::UnitType toUnit)
Returns the conversion factor between the specified units.
Definition: qgis.cpp:115
QString mUnitLabeling
Labeling of map units.
double mapWidth() const
Returns diagonal of composer map in selected units (map units / meters / feet / nautical miles) ...
const QgsComposerMap * mComposerMap
Reference to composer map object.
void setNumUnitsPerSegment(double units)
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:199
const QgsComposerMap * getComposerMapById(int id) const
Returns the composer map with specified id.
void move(double dx, double dy)
Moves item in canvas coordinates.
Scalebar style that draws a single box with alternating color for the segments.
void correctXPositionAlignment(double width, double widthAfter)
Moves scalebar position to the left / right depending on alignment and change in item width...
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:184
void setEllipsoidalMode(bool flag)
sets whether coordinates must be projected to ellipsoid before measuring
void setTickPosition(TickPosition p)
QString firstLabelString() const
Returns string of first label (important for drawing, labeling, size calculation. ...
#define tr(sourceText)