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 : [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  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  }
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