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 : [email protected]
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  }
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