QGIS API Documentation  2.0.1-Dufour
 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  disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
140  disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
141  mComposerMap = map;
142 
143  if ( !map )
144  {
145  return;
146  }
147 
148  connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
149  connect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
150 
152  emit itemChanged();
153 }
154 
156 {
157  disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
158  disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
159  mComposerMap = 0;
160 }
161 
163 {
164  if ( mComposerMap )
165  {
166  //get extent of composer map
167  QgsRectangle composerMapRect = mComposerMap->extent();
168 
169  //get mm dimension of composer map
170  QRectF composerItemRect = mComposerMap->rect();
171 
172  //calculate size depending on mNumUnitsPerSegment
173  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
174  }
175 }
176 
178 {
179  if ( !mComposerMap )
180  {
181  return 0.0;
182  }
183 
184  QgsRectangle composerMapRect = mComposerMap->extent();
185  if ( mUnits == MapUnits )
186  {
187  return composerMapRect.width();
188  }
189  else
190  {
191  QgsDistanceArea da;
194  da.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", "WGS84" ) );
195 
196  double measure = da.measureLine( QgsPoint( composerMapRect.xMinimum(), composerMapRect.yMinimum() ), QgsPoint( composerMapRect.xMaximum(), composerMapRect.yMinimum() ) );
197  if ( mUnits == Feet )
198  {
199  measure /= 0.3048;
200  }
201  return measure;
202  }
203 }
204 
206 {
207  mAlignment = a;
208  update();
209  emit itemChanged();
210 }
211 
213 {
214  mUnits = u;
216  emit itemChanged();
217 }
218 
220 {
221  mNumSegments = 2;
222  mNumSegmentsLeft = 0;
223 
225 
226  //style
227  delete mStyle;
228  mStyle = new QgsSingleBoxScaleBarStyle( this );
229 
230  mHeight = 3;
231 
232  mPen = QPen( QColor( 0, 0, 0 ) );
233  mPen.setWidthF( 1.0 );
234 
235  mBrush.setColor( QColor( 0, 0, 0 ) );
236  mBrush.setStyle( Qt::SolidPattern );
237 
238  mFont.setPointSizeF( 12.0 );
239  mFontColor = QColor( 0, 0, 0 );
240 
241  mLabelBarSpace = 3.0;
242  mBoxContentSpace = 1.0;
243  emit itemChanged();
244 }
245 
247 {
248  if ( mComposerMap )
249  {
250  setUnits( Meters );
251  double widthMeter = mapWidth();
252  int nUnitsPerSegment = widthMeter / 10.0; //default scalebar width equals half the map width
253  setNumUnitsPerSegment( nUnitsPerSegment );
254 
255  if ( nUnitsPerSegment > 1000 )
256  {
257  setNumUnitsPerSegment(( int )( numUnitsPerSegment() / 1000.0 + 0.5 ) * 1000 );
258  setUnitLabeling( tr( "km" ) );
260  }
261  else
262  {
263  setUnitLabeling( tr( "m" ) );
264  }
265 
266  setNumSegments( 4 );
267  setNumSegmentsLeft( 2 );
268  }
269 
271  adjustBoxSize();
272  emit itemChanged();
273 }
274 
276 {
277  if ( !mStyle )
278  {
279  return;
280  }
281 
282  QRectF box = mStyle->calculateBoxSize();
283  setSceneRect( box );
284 }
285 
287 {
288  //Don't adjust box size for numeric scale bars:
289  if ( mStyle->name() != "Numeric" )
290  {
291  adjustBoxSize();
292  }
293  QgsComposerItem::update();
294 }
295 
297 {
298  if ( !mStyle )
299  {
300  return;
301  }
302  double width = mStyle->calculateBoxSize().width();
304  double widthAfter = mStyle->calculateBoxSize().width();
305  correctXPositionAlignment( width, widthAfter );
306  update();
307  emit itemChanged();
308 }
309 
310 void QgsComposerScaleBar::segmentPositions( QList<QPair<double, double> >& posWidthList ) const
311 {
312  posWidthList.clear();
313  double mCurrentXCoord = mPen.widthF() + mBoxContentSpace;
314 
315  //left segments
316  for ( int i = 0; i < mNumSegmentsLeft; ++i )
317  {
318  posWidthList.push_back( qMakePair( mCurrentXCoord, mSegmentMillimeters / mNumSegmentsLeft ) );
319  mCurrentXCoord += mSegmentMillimeters / mNumSegmentsLeft;
320  }
321 
322  //right segments
323  for ( int i = 0; i < mNumSegments; ++i )
324  {
325  posWidthList.push_back( qMakePair( mCurrentXCoord, mSegmentMillimeters ) );
326  mCurrentXCoord += mSegmentMillimeters;
327  }
328 }
329 
330 void QgsComposerScaleBar::setStyle( const QString& styleName )
331 {
332  delete mStyle;
333  mStyle = 0;
334 
335  //switch depending on style name
336  if ( styleName == "Single Box" )
337  {
338  mStyle = new QgsSingleBoxScaleBarStyle( this );
339  }
340  else if ( styleName == "Double Box" )
341  {
342  mStyle = new QgsDoubleBoxScaleBarStyle( this );
343  }
344  else if ( styleName == "Line Ticks Middle" || styleName == "Line Ticks Down" || styleName == "Line Ticks Up" )
345  {
346  QgsTicksScaleBarStyle* tickStyle = new QgsTicksScaleBarStyle( this );
347  if ( styleName == "Line Ticks Middle" )
348  {
350  }
351  else if ( styleName == "Line Ticks Down" )
352  {
354  }
355  else if ( styleName == "Line Ticks Up" )
356  {
358  }
359  mStyle = tickStyle;
360  }
361  else if ( styleName == "Numeric" )
362  {
363  mStyle = new QgsNumericScaleBarStyle( this );
364  }
365  emit itemChanged();
366 }
367 
369 {
370  if ( mStyle )
371  {
372  return mStyle->name();
373  }
374  else
375  {
376  return "";
377  }
378 }
379 
381 {
382  if ( mNumSegmentsLeft > 0 )
383  {
384  return QString::number( mNumUnitsPerSegment / mNumMapUnitsPerScaleBarUnit );
385  }
386  else
387  {
388  return "0";
389  }
390 }
391 
393 {
394  return mFont;
395 }
396 
397 void QgsComposerScaleBar::setFont( const QFont& font )
398 {
399  mFont = font;
400  update();
401  emit itemChanged();
402 }
403 
404 bool QgsComposerScaleBar::writeXML( QDomElement& elem, QDomDocument & doc ) const
405 {
406  if ( elem.isNull() )
407  {
408  return false;
409  }
410 
411  QDomElement composerScaleBarElem = doc.createElement( "ComposerScaleBar" );
412  composerScaleBarElem.setAttribute( "height", QString::number( mHeight ) );
413  composerScaleBarElem.setAttribute( "labelBarSpace", QString::number( mLabelBarSpace ) );
414  composerScaleBarElem.setAttribute( "boxContentSpace", QString::number( mBoxContentSpace ) );
415  composerScaleBarElem.setAttribute( "numSegments", mNumSegments );
416  composerScaleBarElem.setAttribute( "numSegmentsLeft", mNumSegmentsLeft );
417  composerScaleBarElem.setAttribute( "numUnitsPerSegment", QString::number( mNumUnitsPerSegment ) );
418  composerScaleBarElem.setAttribute( "segmentMillimeters", QString::number( mSegmentMillimeters ) );
419  composerScaleBarElem.setAttribute( "numMapUnitsPerScaleBarUnit", QString::number( mNumMapUnitsPerScaleBarUnit ) );
420  composerScaleBarElem.setAttribute( "font", mFont.toString() );
421  composerScaleBarElem.setAttribute( "outlineWidth", QString::number( mPen.widthF() ) );
422  composerScaleBarElem.setAttribute( "unitLabel", mUnitLabeling );
423  composerScaleBarElem.setAttribute( "units", mUnits );
424 
425  //style
426  if ( mStyle )
427  {
428  composerScaleBarElem.setAttribute( "style", mStyle->name() );
429  }
430 
431  //map id
432  if ( mComposerMap )
433  {
434  composerScaleBarElem.setAttribute( "mapId", mComposerMap->id() );
435  }
436 
437  //colors
438  composerScaleBarElem.setAttribute( "brushColor", mBrush.color().name() );
439  composerScaleBarElem.setAttribute( "penColor", mPen.color().name() );
440  composerScaleBarElem.setAttribute( "fontColor", mFontColor.name() );
441 
442  //alignment
443  composerScaleBarElem.setAttribute( "alignment", QString::number(( int ) mAlignment ) );
444 
445  elem.appendChild( composerScaleBarElem );
446  return _writeXML( composerScaleBarElem, doc );
447 }
448 
449 bool QgsComposerScaleBar::readXML( const QDomElement& itemElem, const QDomDocument& doc )
450 {
451  if ( itemElem.isNull() )
452  {
453  return false;
454  }
455 
456  mHeight = itemElem.attribute( "height", "5.0" ).toDouble();
457  mLabelBarSpace = itemElem.attribute( "labelBarSpace", "3.0" ).toDouble();
458  mBoxContentSpace = itemElem.attribute( "boxContentSpace", "1.0" ).toDouble();
459  mNumSegments = itemElem.attribute( "numSegments", "2" ).toInt();
460  mNumSegmentsLeft = itemElem.attribute( "numSegmentsLeft", "0" ).toInt();
461  mNumUnitsPerSegment = itemElem.attribute( "numUnitsPerSegment", "1.0" ).toDouble();
462  mSegmentMillimeters = itemElem.attribute( "segmentMillimeters", "0.0" ).toDouble();
463  mNumMapUnitsPerScaleBarUnit = itemElem.attribute( "numMapUnitsPerScaleBarUnit", "1.0" ).toDouble();
464  mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
465  mUnitLabeling = itemElem.attribute( "unitLabel" );
466  QString fontString = itemElem.attribute( "font", "" );
467  if ( !fontString.isEmpty() )
468  {
469  mFont.fromString( fontString );
470  }
471 
472  //colors
473  //fill color
474  mBrush.setColor( QColor( itemElem.attribute( "brushColor", "#000000" ) ) );
475  mPen.setColor( QColor( itemElem.attribute( "penColor", "#000000" ) ) );
476  mFontColor.setNamedColor( itemElem.attribute( "fontColor", "#000000" ) );
477 
478  //style
479  delete mStyle;
480  mStyle = 0;
481  QString styleString = itemElem.attribute( "style", "" );
482  setStyle( tr( styleString.toLocal8Bit().data() ) );
483 
484  mUnits = ( ScaleBarUnits )itemElem.attribute( "units" ).toInt();
485  mAlignment = ( Alignment )( itemElem.attribute( "alignment", "0" ).toInt() );
486 
487  //map
488  int mapId = itemElem.attribute( "mapId", "-1" ).toInt();
489  if ( mapId >= 0 )
490  {
493  if ( mComposerMap )
494  {
495  connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
496  connect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
497  }
498  }
499 
501 
502  //restore general composer item properties
503  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
504  if ( composerItemList.size() > 0 )
505  {
506  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
507  _readXML( composerItemElem, doc );
508  }
509 
510  return true;
511 }
512 
513 void QgsComposerScaleBar::correctXPositionAlignment( double width, double widthAfter )
514 {
515  //Don't adjust position for numeric scale bars:
516  if ( mStyle->name() == "Numeric" )
517  {
518  return;
519  }
520 
521  if ( mAlignment == Middle )
522  {
523  move( -( widthAfter - width ) / 2.0, 0 );
524  }
525  else if ( mAlignment == Right )
526  {
527  move( -( widthAfter - width ), 0 );
528  }
529 }
530 
531 
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 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:190
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:175
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.
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.
QgsRectangle extent() const
double numUnitsPerSegment() const
virtual void drawBackground(QPainter *p)
Draw background.
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:358
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.
void applyDefaultSize()
Apply default size (scale bar 1/5 of map item width)
QString mUnitLabeling
Labeling of map units.
double mapWidth() const
Returns diagonal of composer map in selected units (map units / meters / feet)
const QgsComposerMap * mComposerMap
Reference to composer map object.
void setNumUnitsPerSegment(double units)
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:195
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:180
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)