QGIS API Documentation  2.99.0-Master (75367e4)
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 "qgscomposerutils.h"
21 #include "qgsdistancearea.h"
22 #include "qgsscalebarstyle.h"
24 #include "qgsmapsettings.h"
27 #include "qgsticksscalebarstyle.h"
28 #include "qgsrectangle.h"
29 #include "qgsproject.h"
30 #include "qgssymbollayerutils.h"
31 #include "qgsfontutils.h"
32 #include "qgsunittypes.h"
33 #include <QDomDocument>
34 #include <QDomElement>
35 #include <QFontMetricsF>
36 #include <QPainter>
37 #include <QSettings>
38 #include <cmath>
39 
41  : QgsComposerItem( composition )
42  , mComposerMap( nullptr )
43  , mNumUnitsPerSegment( 0 )
44  , mSegmentSizeMode( SegmentSizeFixed )
45  , mMinBarWidth( 50 )
46  , mMaxBarWidth( 150 )
47  , mFontColor( QColor( 0, 0, 0 ) )
48  , mStyle( nullptr )
49  , mSegmentMillimeters( 0.0 )
50  , mAlignment( Left )
51  , mUnits( MapUnits )
52  , mLineJoinStyle( Qt::MiterJoin )
53  , mLineCapStyle( Qt::SquareCap )
54 {
57 }
58 
60 {
61  delete mStyle;
62 }
63 
64 void QgsComposerScaleBar::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
65 {
66  Q_UNUSED( itemStyle );
67  Q_UNUSED( pWidget );
68  if ( !mStyle || !painter )
69  {
70  return;
71  }
72  if ( !shouldDrawItem() )
73  {
74  return;
75  }
76 
77  drawBackground( painter );
78 
79  //x-offset is half of first label width because labels are drawn centered
80  QString firstLabel = firstLabelString();
81  double firstLabelWidth = QgsComposerUtils::textWidthMM( mFont, firstLabel );
82 
83  mStyle->draw( painter, firstLabelWidth / 2 );
84 
85  //draw frame and selection boxes if necessary
86  drawFrame( painter );
87  if ( isSelected() )
88  {
89  drawSelectionBoxes( painter );
90  }
91 }
92 
94 {
95  if ( !mStyle )
96  {
97  mNumSegments = nSegments;
98  return;
99  }
100  double width = mStyle->calculateBoxSize().width();
101  mNumSegments = nSegments;
102  double widthAfter = mStyle->calculateBoxSize().width();
103  correctXPositionAlignment( width, widthAfter );
104  emit itemChanged();
105 }
106 
108 {
109  if ( !mStyle )
110  {
112  return;
113  }
114  double width = mStyle->calculateBoxSize().width();
117  double widthAfter = mStyle->calculateBoxSize().width();
118  correctXPositionAlignment( width, widthAfter );
119  emit itemChanged();
120 }
121 
123 {
124  if ( !mStyle )
125  {
126  mSegmentSizeMode = mode;
127  return;
128  }
129  double width = mStyle->calculateBoxSize().width();
130  mSegmentSizeMode = mode;
132  double widthAfter = mStyle->calculateBoxSize().width();
133  correctXPositionAlignment( width, widthAfter );
134  emit itemChanged();
135 }
136 
137 void QgsComposerScaleBar::setMinBarWidth( double minWidth )
138 {
139  if ( !mStyle )
140  {
141  mMinBarWidth = minWidth;
142  return;
143  }
144  double width = mStyle->calculateBoxSize().width();
145  mMinBarWidth = minWidth;
147  double widthAfter = mStyle->calculateBoxSize().width();
148  correctXPositionAlignment( width, widthAfter );
149  emit itemChanged();
150 }
151 
152 void QgsComposerScaleBar::setMaxBarWidth( double maxWidth )
153 {
154  if ( !mStyle )
155  {
156  mMaxBarWidth = maxWidth;
157  return;
158  }
159  double width = mStyle->calculateBoxSize().width();
160  mMaxBarWidth = maxWidth;
162  double widthAfter = mStyle->calculateBoxSize().width();
163  correctXPositionAlignment( width, widthAfter );
164  emit itemChanged();
165 }
166 
168 {
169  if ( !mStyle )
170  {
171  mNumSegmentsLeft = nSegmentsLeft;
172  return;
173  }
174  double width = mStyle->calculateBoxSize().width();
175  mNumSegmentsLeft = nSegmentsLeft;
176  double widthAfter = mStyle->calculateBoxSize().width();
177  correctXPositionAlignment( width, widthAfter );
178  emit itemChanged();
179 }
180 
182 {
183  if ( !mStyle )
184  {
185  mBoxContentSpace = space;
186  return;
187  }
188  double width = mStyle->calculateBoxSize().width();
189  mBoxContentSpace = space;
190  double widthAfter = mStyle->calculateBoxSize().width();
191  correctXPositionAlignment( width, widthAfter );
192  emit itemChanged();
193 }
194 
196 {
197  if ( mComposerMap )
198  {
199  disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
200  disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
201  }
202  mComposerMap = map;
203 
204  if ( !map )
205  {
206  return;
207  }
208 
209  connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
210  connect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
211 
213  emit itemChanged();
214 }
215 
217 {
218  if ( !mComposerMap )
219  {
220  return;
221  }
222 
223  disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
224  disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
225  mComposerMap = nullptr;
226 }
227 
229 {
231  const QgsExpressionContext* evalContext = context ? context : &scopedContext;
232 
233  bool forceUpdate = false;
234  //updates data defined properties and redraws item to match
236  {
238  forceUpdate = true;
239  }
241  {
243  forceUpdate = true;
244  }
246  {
248  forceUpdate = true;
249  }
251  {
253  forceUpdate = true;
254  }
255  if ( forceUpdate )
256  {
257  update();
258  }
259 
261 }
262 
263 // nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc
264 inline double nextNiceNumber( double a, double d = 1 )
265 {
266  double s = qPow( 10.0, floor( log10( a ) ) ) / d;
267  return ceil( a / s ) * s;
268 }
269 
270 // prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc
271 inline double prevNiceNumber( double a, double d = 1 )
272 {
273  double s = qPow( 10.0, floor( log10( a ) ) ) / d;
274  return floor( a / s ) * s;
275 }
276 
278 {
279  if ( mComposerMap )
280  {
281  //get mm dimension of composer map
282  QRectF composerItemRect = mComposerMap->rect();
283 
285  {
286  //calculate size depending on mNumUnitsPerSegment
287  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
288  }
289  else /*if(mSegmentSizeMode == SegmentSizeFitWidth)*/
290  {
291  if ( mMaxBarWidth < mMinBarWidth )
292  {
294  }
295  else
296  {
297  double nSegments = ( mNumSegmentsLeft != 0 ) + mNumSegments;
298  // unitsPerSegments which fit minBarWidth resp. maxBarWidth
299  double minUnitsPerSeg = ( mMinBarWidth * mapWidth() ) / ( nSegments * composerItemRect.width() );
300  double maxUnitsPerSeg = ( mMaxBarWidth * mapWidth() ) / ( nSegments * composerItemRect.width() );
301 
302  // Start with coarsest "nice" number closest to minUnitsPerSeg resp
303  // maxUnitsPerSeg, then proceed to finer numbers as long as neither
304  // lowerNiceUnitsPerSeg nor upperNiceUnitsPerSeg are are in
305  // [minUnitsPerSeg, maxUnitsPerSeg]
306  double lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg );
307  double upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg );
308 
309  double d = 1;
310  while ( lowerNiceUnitsPerSeg > maxUnitsPerSeg && upperNiceUnitsPerSeg < minUnitsPerSeg )
311  {
312  d *= 10;
313  lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg, d );
314  upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg, d );
315  }
316 
317  // Pick mNumUnitsPerSegment from {lowerNiceUnitsPerSeg, upperNiceUnitsPerSeg}, use the larger if possible
318  mNumUnitsPerSegment = upperNiceUnitsPerSeg < minUnitsPerSeg ? lowerNiceUnitsPerSeg : upperNiceUnitsPerSeg;
319  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
320  }
321  }
322  }
323 }
324 
326 {
327  if ( !mComposerMap )
328  {
329  return 0.0;
330  }
331 
332  QgsRectangle composerMapRect = *( mComposerMap->currentMapExtent() );
333  if ( mUnits == MapUnits )
334  {
335  return composerMapRect.width();
336  }
337  else
338  {
339  QgsDistanceArea da;
340  da.setEllipsoidalMode( true );
341  da.setSourceCrs( mComposerMap->crs().srsid() );
343 
345  double measure = da.measureLine( QgsPoint( composerMapRect.xMinimum(), composerMapRect.yMinimum() ),
346  QgsPoint( composerMapRect.xMaximum(), composerMapRect.yMinimum() ),
347  units );
348  switch ( mUnits )
349  {
352  break;
355  break;
358  break;
360  //avoid warning
361  break;
362  }
363  return measure;
364  }
365 }
366 
368 {
369  mAlignment = a;
370  update();
371  emit itemChanged();
372 }
373 
375 {
376  mUnits = u;
378  emit itemChanged();
379 }
380 
382 {
383  if ( mLineJoinStyle == style )
384  {
385  //no change
386  return;
387  }
389  mPen.setJoinStyle( mLineJoinStyle );
390  update();
391  emit itemChanged();
392 }
393 
395 {
396  if ( mLineCapStyle == style )
397  {
398  //no change
399  return;
400  }
402  mPen.setCapStyle( mLineCapStyle );
403  update();
404  emit itemChanged();
405 }
406 
408 {
409  mNumSegments = 2;
410  mNumSegmentsLeft = 0;
411 
413 
414  //style
415  delete mStyle;
416  mStyle = new QgsSingleBoxScaleBarStyle( this );
417 
418  mHeight = 3;
419 
420  //default to no background
421  setBackgroundEnabled( false );
422 
423  mPen = QPen( mLineColor );
424  mPen.setJoinStyle( mLineJoinStyle );
425  mPen.setCapStyle( mLineCapStyle );
426  mPen.setWidthF( mLineWidth );
427 
428  mBrush.setColor( mFillColor );
429  mBrush.setStyle( Qt::SolidPattern );
430 
431  mBrush2.setColor( mFillColor2 );
432  mBrush2.setStyle( Qt::SolidPattern );
433 
434  //get default composer font from settings
435  QSettings settings;
436  QString defaultFontString = settings.value( QStringLiteral( "/Composer/defaultFont" ) ).toString();
437  if ( !defaultFontString.isEmpty() )
438  {
439  mFont.setFamily( defaultFontString );
440  }
441  mFont.setPointSizeF( 12.0 );
442  mFontColor = QColor( 0, 0, 0 );
443 
444  mLabelBarSpace = 3.0;
445  mBoxContentSpace = 1.0;
446  emit itemChanged();
447 }
448 
450 {
451  if ( mComposerMap )
452  {
453  setUnits( u );
454  double upperMagnitudeMultiplier = 1.0;
455  double widthInSelectedUnits = mapWidth();
456  double initialUnitsPerSegment = widthInSelectedUnits / 10.0; //default scalebar width equals half the map width
457  setNumUnitsPerSegment( initialUnitsPerSegment );
458 
459  switch ( mUnits )
460  {
461  case MapUnits:
462  {
463  upperMagnitudeMultiplier = 1.0;
464  setUnitLabeling( tr( "units" ) );
465  break;
466  }
467  case Meters:
468  {
469  if ( initialUnitsPerSegment > 1000.0 )
470  {
471  upperMagnitudeMultiplier = 1000.0;
472  setUnitLabeling( tr( "km" ) );
473  }
474  else
475  {
476  upperMagnitudeMultiplier = 1.0;
477  setUnitLabeling( tr( "m" ) );
478  }
479  break;
480  }
481  case Feet:
482  {
483  if ( initialUnitsPerSegment > 5419.95 )
484  {
485  upperMagnitudeMultiplier = 5419.95;
486  setUnitLabeling( tr( "miles" ) );
487  }
488  else
489  {
490  upperMagnitudeMultiplier = 1.0;
491  setUnitLabeling( tr( "ft" ) );
492  }
493  break;
494  }
495  case NauticalMiles:
496  {
497  upperMagnitudeMultiplier = 1;
498  setUnitLabeling( tr( "Nm" ) );
499  break;
500  }
501  }
502 
503  double segmentWidth = initialUnitsPerSegment / upperMagnitudeMultiplier;
504  int segmentMagnitude = floor( log10( segmentWidth ) );
505  double unitsPerSegment = upperMagnitudeMultiplier * ( qPow( 10.0, segmentMagnitude ) );
506  double multiplier = floor(( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5;
507 
508  if ( multiplier > 0 )
509  {
510  unitsPerSegment = unitsPerSegment * multiplier;
511  }
512  setNumUnitsPerSegment( unitsPerSegment );
513  setNumMapUnitsPerScaleBarUnit( upperMagnitudeMultiplier );
514 
515  setNumSegments( 4 );
516  setNumSegmentsLeft( 2 );
517  }
518 
520  adjustBoxSize();
521  emit itemChanged();
522 }
523 
525 {
526  if ( !mStyle )
527  {
528  return;
529  }
530 
531  QRectF box = mStyle->calculateBoxSize();
532  if ( rect().height() > box.height() )
533  {
534  //keep user specified item height if higher than minimum scale bar height
535  box.setHeight( rect().height() );
536  }
537 
538  //update rect for data defined size and position
539  QRectF newRect = evalItemRect( box, true );
540 
541  //scale bars have a minimum size, respect that regardless of data defined settings
542  if ( newRect.width() < box.width() )
543  {
544  newRect.setWidth( box.width() );
545  }
546  if ( newRect.height() < box.height() )
547  {
548  newRect.setHeight( box.height() );
549  }
550 
552 }
553 
554 void QgsComposerScaleBar::setSceneRect( const QRectF& rectangle )
555 {
556  QRectF box = mStyle->calculateBoxSize();
557  if ( rectangle.height() > box.height() )
558  {
559  //keep user specified item height if higher than minimum scale bar height
560  box.setHeight( rectangle.height() );
561  }
562  box.moveTopLeft( rectangle.topLeft() );
563 
564  //update rect for data defined size and position
565  QRectF newRect = evalItemRect( rectangle );
566 
567  //scale bars have a minimum size, respect that regardless of data defined settings
568  if ( newRect.width() < box.width() )
569  {
570  newRect.setWidth( box.width() );
571  }
572  if ( newRect.height() < box.height() )
573  {
574  newRect.setHeight( box.height() );
575  }
576 
578 }
579 
581 {
582  //Don't adjust box size for numeric scale bars:
583  if ( mStyle && mStyle->name() != QLatin1String( "Numeric" ) )
584  {
585  adjustBoxSize();
586  }
587  QgsComposerItem::update();
588 }
589 
591 {
592  if ( !mStyle )
593  {
594  return;
595  }
596  double width = mStyle->calculateBoxSize().width();
598  double widthAfter = mStyle->calculateBoxSize().width();
599  correctXPositionAlignment( width, widthAfter );
600  update();
601  emit itemChanged();
602 }
603 
604 void QgsComposerScaleBar::segmentPositions( QList<QPair<double, double> >& posWidthList ) const
605 {
606  posWidthList.clear();
607  double mCurrentXCoord = mPen.widthF() + mBoxContentSpace;
608 
609  //left segments
610  double leftSegmentSize = mSegmentMillimeters / mNumSegmentsLeft;
611  for ( int i = 0; i < mNumSegmentsLeft; ++i )
612  {
613  posWidthList.push_back( qMakePair( mCurrentXCoord, leftSegmentSize ) );
614  mCurrentXCoord += leftSegmentSize;
615  }
616 
617  //right segments
618  for ( int i = 0; i < mNumSegments; ++i )
619  {
620  posWidthList.push_back( qMakePair( mCurrentXCoord, mSegmentMillimeters ) );
621  mCurrentXCoord += mSegmentMillimeters;
622  }
623 }
624 
625 void QgsComposerScaleBar::setStyle( const QString& styleName )
626 {
627  delete mStyle;
628  mStyle = nullptr;
629 
630  //switch depending on style name
631  if ( styleName == QLatin1String( "Single Box" ) )
632  {
633  mStyle = new QgsSingleBoxScaleBarStyle( this );
634  }
635  else if ( styleName == QLatin1String( "Double Box" ) )
636  {
637  mStyle = new QgsDoubleBoxScaleBarStyle( this );
638  }
639  else if ( styleName == QLatin1String( "Line Ticks Middle" ) || styleName == QLatin1String( "Line Ticks Down" ) || styleName == QLatin1String( "Line Ticks Up" ) )
640  {
641  QgsTicksScaleBarStyle* tickStyle = new QgsTicksScaleBarStyle( this );
642  if ( styleName == QLatin1String( "Line Ticks Middle" ) )
643  {
645  }
646  else if ( styleName == QLatin1String( "Line Ticks Down" ) )
647  {
649  }
650  else if ( styleName == QLatin1String( "Line Ticks Up" ) )
651  {
653  }
654  mStyle = tickStyle;
655  }
656  else if ( styleName == QLatin1String( "Numeric" ) )
657  {
658  mStyle = new QgsNumericScaleBarStyle( this );
659  }
660  emit itemChanged();
661 }
662 
664 {
665  if ( mStyle )
666  {
667  return mStyle->name();
668  }
669  else
670  {
671  return QLatin1String( "" );
672  }
673 }
674 
676 {
677  if ( mNumSegmentsLeft > 0 )
678  {
679  return QString::number( mNumUnitsPerSegment / mNumMapUnitsPerScaleBarUnit );
680  }
681  else
682  {
683  return QStringLiteral( "0" );
684  }
685 }
686 
688 {
689  return mFont;
690 }
691 
692 void QgsComposerScaleBar::setFont( const QFont& font )
693 {
694  mFont = font;
695  update();
696  emit itemChanged();
697 }
698 
699 bool QgsComposerScaleBar::writeXml( QDomElement& elem, QDomDocument & doc ) const
700 {
701  if ( elem.isNull() )
702  {
703  return false;
704  }
705 
706  QDomElement composerScaleBarElem = doc.createElement( QStringLiteral( "ComposerScaleBar" ) );
707  composerScaleBarElem.setAttribute( QStringLiteral( "height" ), QString::number( mHeight ) );
708  composerScaleBarElem.setAttribute( QStringLiteral( "labelBarSpace" ), QString::number( mLabelBarSpace ) );
709  composerScaleBarElem.setAttribute( QStringLiteral( "boxContentSpace" ), QString::number( mBoxContentSpace ) );
710  composerScaleBarElem.setAttribute( QStringLiteral( "numSegments" ), mNumSegments );
711  composerScaleBarElem.setAttribute( QStringLiteral( "numSegmentsLeft" ), mNumSegmentsLeft );
712  composerScaleBarElem.setAttribute( QStringLiteral( "numUnitsPerSegment" ), QString::number( mNumUnitsPerSegment ) );
713  composerScaleBarElem.setAttribute( QStringLiteral( "segmentSizeMode" ), mSegmentSizeMode );
714  composerScaleBarElem.setAttribute( QStringLiteral( "minBarWidth" ), mMinBarWidth );
715  composerScaleBarElem.setAttribute( QStringLiteral( "maxBarWidth" ), mMaxBarWidth );
716  composerScaleBarElem.setAttribute( QStringLiteral( "segmentMillimeters" ), QString::number( mSegmentMillimeters ) );
717  composerScaleBarElem.setAttribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QString::number( mNumMapUnitsPerScaleBarUnit ) );
718  composerScaleBarElem.appendChild( QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "scaleBarFont" ) ) );
719  composerScaleBarElem.setAttribute( QStringLiteral( "outlineWidth" ), QString::number( mLineWidth ) );
720  composerScaleBarElem.setAttribute( QStringLiteral( "unitLabel" ), mUnitLabeling );
721  composerScaleBarElem.setAttribute( QStringLiteral( "units" ), mUnits );
722  composerScaleBarElem.setAttribute( QStringLiteral( "lineJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mLineJoinStyle ) );
723  composerScaleBarElem.setAttribute( QStringLiteral( "lineCapStyle" ), QgsSymbolLayerUtils::encodePenCapStyle( mLineCapStyle ) );
724 
725  //style
726  if ( mStyle )
727  {
728  composerScaleBarElem.setAttribute( QStringLiteral( "style" ), mStyle->name() );
729  }
730 
731  //map id
732  if ( mComposerMap )
733  {
734  composerScaleBarElem.setAttribute( QStringLiteral( "mapId" ), mComposerMap->id() );
735  }
736 
737  //colors
738 
739  //fill color
740  QDomElement fillColorElem = doc.createElement( QStringLiteral( "fillColor" ) );
741  fillColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mFillColor.red() ) );
742  fillColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mFillColor.green() ) );
743  fillColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mFillColor.blue() ) );
744  fillColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFillColor.alpha() ) );
745  composerScaleBarElem.appendChild( fillColorElem );
746 
747  //fill color 2
748  QDomElement fillColor2Elem = doc.createElement( QStringLiteral( "fillColor2" ) );
749  fillColor2Elem.setAttribute( QStringLiteral( "red" ), QString::number( mFillColor2.red() ) );
750  fillColor2Elem.setAttribute( QStringLiteral( "green" ), QString::number( mFillColor2.green() ) );
751  fillColor2Elem.setAttribute( QStringLiteral( "blue" ), QString::number( mFillColor2.blue() ) );
752  fillColor2Elem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFillColor2.alpha() ) );
753  composerScaleBarElem.appendChild( fillColor2Elem );
754 
755  //pen color
756  QDomElement strokeColorElem = doc.createElement( QStringLiteral( "strokeColor" ) );
757  strokeColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mLineColor.red() ) );
758  strokeColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mLineColor.green() ) );
759  strokeColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mLineColor.blue() ) );
760  strokeColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mLineColor.alpha() ) );
761  composerScaleBarElem.appendChild( strokeColorElem );
762 
763  //font color
764  QDomElement fontColorElem = doc.createElement( QStringLiteral( "textColor" ) );
765  fontColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mFontColor.red() ) );
766  fontColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mFontColor.green() ) );
767  fontColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mFontColor.blue() ) );
768  fontColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFontColor.alpha() ) );
769  composerScaleBarElem.appendChild( fontColorElem );
770 
771  //alignment
772  composerScaleBarElem.setAttribute( QStringLiteral( "alignment" ), QString::number( static_cast< int >( mAlignment ) ) );
773 
774  elem.appendChild( composerScaleBarElem );
775  return _writeXml( composerScaleBarElem, doc );
776 }
777 
778 bool QgsComposerScaleBar::readXml( const QDomElement& itemElem, const QDomDocument& doc )
779 {
780  if ( itemElem.isNull() )
781  {
782  return false;
783  }
784 
785  mHeight = itemElem.attribute( QStringLiteral( "height" ), QStringLiteral( "5.0" ) ).toDouble();
786  mLabelBarSpace = itemElem.attribute( QStringLiteral( "labelBarSpace" ), QStringLiteral( "3.0" ) ).toDouble();
787  mBoxContentSpace = itemElem.attribute( QStringLiteral( "boxContentSpace" ), QStringLiteral( "1.0" ) ).toDouble();
788  mNumSegments = itemElem.attribute( QStringLiteral( "numSegments" ), QStringLiteral( "2" ) ).toInt();
789  mNumSegmentsLeft = itemElem.attribute( QStringLiteral( "numSegmentsLeft" ), QStringLiteral( "0" ) ).toInt();
790  mNumUnitsPerSegment = itemElem.attribute( QStringLiteral( "numUnitsPerSegment" ), QStringLiteral( "1.0" ) ).toDouble();
791  mSegmentSizeMode = static_cast<SegmentSizeMode>( itemElem.attribute( QStringLiteral( "segmentSizeMode" ), QStringLiteral( "0" ) ).toInt() );
792  mMinBarWidth = itemElem.attribute( QStringLiteral( "minBarWidth" ), QStringLiteral( "50" ) ).toInt();
793  mMaxBarWidth = itemElem.attribute( QStringLiteral( "maxBarWidth" ), QStringLiteral( "150" ) ).toInt();
794  mSegmentMillimeters = itemElem.attribute( QStringLiteral( "segmentMillimeters" ), QStringLiteral( "0.0" ) ).toDouble();
795  mNumMapUnitsPerScaleBarUnit = itemElem.attribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QStringLiteral( "1.0" ) ).toDouble();
796  mLineWidth = itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "0.3" ) ).toDouble();
797  mPen.setWidthF( mLineWidth );
798  mUnitLabeling = itemElem.attribute( QStringLiteral( "unitLabel" ) );
799  mLineJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( itemElem.attribute( QStringLiteral( "lineJoinStyle" ), QStringLiteral( "miter" ) ) );
800  mPen.setJoinStyle( mLineJoinStyle );
801  mLineCapStyle = QgsSymbolLayerUtils::decodePenCapStyle( itemElem.attribute( QStringLiteral( "lineCapStyle" ), QStringLiteral( "square" ) ) );
802  mPen.setCapStyle( mLineCapStyle );
803  if ( !QgsFontUtils::setFromXmlChildNode( mFont, itemElem, QStringLiteral( "scaleBarFont" ) ) )
804  {
805  mFont.fromString( itemElem.attribute( QStringLiteral( "font" ), QLatin1String( "" ) ) );
806  }
807 
808  //colors
809  //fill color
810  QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "fillColor" ) );
811  if ( !fillColorList.isEmpty() )
812  {
813  QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
814  bool redOk, greenOk, blueOk, alphaOk;
815  int fillRed, fillGreen, fillBlue, fillAlpha;
816 
817  fillRed = fillColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
818  fillGreen = fillColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
819  fillBlue = fillColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
820  fillAlpha = fillColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
821 
822  if ( redOk && greenOk && blueOk && alphaOk )
823  {
824  mFillColor = QColor( fillRed, fillGreen, fillBlue, fillAlpha );
825  mBrush.setColor( mFillColor );
826  }
827  }
828  else
829  {
830  mFillColor = QColor( itemElem.attribute( QStringLiteral( "brushColor" ), QStringLiteral( "#000000" ) ) );
831  mBrush.setColor( mFillColor );
832  }
833 
834  //fill color 2
835  QDomNodeList fillColor2List = itemElem.elementsByTagName( QStringLiteral( "fillColor2" ) );
836  if ( !fillColor2List.isEmpty() )
837  {
838  QDomElement fillColor2Elem = fillColor2List.at( 0 ).toElement();
839  bool redOk, greenOk, blueOk, alphaOk;
840  int fillRed, fillGreen, fillBlue, fillAlpha;
841 
842  fillRed = fillColor2Elem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
843  fillGreen = fillColor2Elem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
844  fillBlue = fillColor2Elem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
845  fillAlpha = fillColor2Elem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
846 
847  if ( redOk && greenOk && blueOk && alphaOk )
848  {
849  mFillColor2 = QColor( fillRed, fillGreen, fillBlue, fillAlpha );
850  mBrush2.setColor( mFillColor2 );
851  }
852  }
853  else
854  {
855  mFillColor2 = QColor( itemElem.attribute( QStringLiteral( "brush2Color" ), QStringLiteral( "#ffffff" ) ) );
856  mBrush2.setColor( mFillColor2 );
857  }
858 
859  //stroke color
860  QDomNodeList strokeColorList = itemElem.elementsByTagName( QStringLiteral( "strokeColor" ) );
861  if ( !strokeColorList.isEmpty() )
862  {
863  QDomElement strokeColorElem = strokeColorList.at( 0 ).toElement();
864  bool redOk, greenOk, blueOk, alphaOk;
865  int strokeRed, strokeGreen, strokeBlue, strokeAlpha;
866 
867  strokeRed = strokeColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
868  strokeGreen = strokeColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
869  strokeBlue = strokeColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
870  strokeAlpha = strokeColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
871 
872  if ( redOk && greenOk && blueOk && alphaOk )
873  {
874  mLineColor = QColor( strokeRed, strokeGreen, strokeBlue, strokeAlpha );
875  mPen.setColor( mLineColor );
876  }
877  }
878  else
879  {
880  mLineColor = QColor( itemElem.attribute( QStringLiteral( "penColor" ), QStringLiteral( "#000000" ) ) );
881  mPen.setColor( mLineColor );
882  }
883 
884  //font color
885  QDomNodeList textColorList = itemElem.elementsByTagName( QStringLiteral( "textColor" ) );
886  if ( !textColorList.isEmpty() )
887  {
888  QDomElement textColorElem = textColorList.at( 0 ).toElement();
889  bool redOk, greenOk, blueOk, alphaOk;
890  int textRed, textGreen, textBlue, textAlpha;
891 
892  textRed = textColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
893  textGreen = textColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
894  textBlue = textColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
895  textAlpha = textColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
896 
897  if ( redOk && greenOk && blueOk && alphaOk )
898  {
899  mFontColor = QColor( textRed, textGreen, textBlue, textAlpha );
900  }
901  }
902  else
903  {
904  mFontColor.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
905  }
906 
907  //style
908  delete mStyle;
909  mStyle = nullptr;
910  QString styleString = itemElem.attribute( QStringLiteral( "style" ), QLatin1String( "" ) );
911  setStyle( tr( styleString.toLocal8Bit().data() ) );
912 
913  mUnits = static_cast< ScaleBarUnits >( itemElem.attribute( QStringLiteral( "units" ) ).toInt() );
914  mAlignment = static_cast< Alignment >( itemElem.attribute( QStringLiteral( "alignment" ), QStringLiteral( "0" ) ).toInt() );
915 
916  //map
917  int mapId = itemElem.attribute( QStringLiteral( "mapId" ), QStringLiteral( "-1" ) ).toInt();
918  if ( mapId >= 0 )
919  {
922  if ( mComposerMap )
923  {
924  connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateSegmentSize() ) );
925  connect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
926  }
927  }
928 
930 
931  //restore general composer item properties
932  QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral( "ComposerItem" ) );
933  if ( !composerItemList.isEmpty() )
934  {
935  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
936  _readXml( composerItemElem, doc );
937  }
938 
939  return true;
940 }
941 
942 void QgsComposerScaleBar::correctXPositionAlignment( double width, double widthAfter )
943 {
944  //Don't adjust position for numeric scale bars:
945  if ( mStyle->name() == QLatin1String( "Numeric" ) )
946  {
947  return;
948  }
949 
950  if ( mAlignment == Middle )
951  {
952  move( -( widthAfter - width ) / 2.0, 0 );
953  }
954  else if ( mAlignment == Right )
955  {
956  move( -( widthAfter - width ), 0 );
957  }
958 }
959 
Double box with alternating colors.
ScaleBarUnits units() const
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores state in Dom element.
A rectangle specified with double values.
Definition: qgsrectangle.h:36
double mLabelBarSpace
Space between bar and Text labels.
const QgsComposerMap * getComposerMapById(const int id) const
Returns the composer map with specified id.
virtual QRectF calculateBoxSize() const
double mHeight
Height of bars/lines.
double mNumUnitsPerSegment
Size of a segment (in map units)
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
virtual void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=nullptr) override
void setUnits(ScaleBarUnits u)
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
void setLineCapStyle(Qt::PenCapStyle style)
Sets cap style used when drawing the lines in the scalebar.
double mNumMapUnitsPerScaleBarUnit
Number of map units per scale bar units (e.g. 1000 to have km for a map with m units) ...
void setFont(const QFont &font)
A scale bar style that draws text in the form of &#39;1:XXXXX&#39;.
void itemChanged()
Emitted when the item changes.
Qt::PenJoinStyle mLineJoinStyle
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 paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Reimplementation of QCanvasItem::paint.
virtual void refreshDataDefinedProperty(const DataDefinedProperty property=AllProperties, const QgsExpressionContext *context=nullptr)
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
QRectF evalItemRect(const QRectF &newRect, const bool resizeOnly=false, const QgsExpressionContext *context=nullptr)
Evaluates an item&#39;s bounding rect to consider data defined position and size of item and reference po...
void setNumSegmentsLeft(int nSegmentsLeft)
double mMinBarWidth
Minimum allowed bar width, when mSegmentSizeMode is FitWidth.
int id() const
Get identification number.
virtual void drawFrame(QPainter *p)
Draw black frame around item.
QgsScaleBarStyle * mStyle
Scalebar style.
QBrush mBrush2
Secondary fill.
virtual QString name() const =0
Get a name for this style.
bool setEllipsoid(const QString &ellipsoid)
Sets ellipsoid by its acronym.
Scalebar secondary fill color.
void update()
Adjusts box size and calls QgsComposerItem::update()
void setMinBarWidth(double minWidth)
Sets the minimum size (in millimeters) for scale bar segments.
SegmentSizeMode
Modes for setting size for scale bar segments.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
DataDefinedProperty
Data defined properties for different item types.
A scale bar that draws segments using short ticks.
void setNumMapUnitsPerScaleBarUnit(double d)
void adjustBoxSize()
Sets box size suitable to content.
QColor mFillColor2
Secondary fill color.
void setMaxBarWidth(double maxWidth)
Sets the maximum size (in millimeters) for scale bar segments.
double mSegmentMillimeters
Width of a segment (in mm)
bool _writeXml(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document. Usually called from writeXml methods of ...
void setLineJoinStyle(Qt::PenJoinStyle style)
Sets join style used when drawing the lines in the scalebar.
QString firstLabelString() const
Returns string of first label (important for drawing, labeling, size calculation. ...
virtual void drawSelectionBoxes(QPainter *p)
Draws additional graphics on selected items.
Qt::PenCapStyle mLineCapStyle
void setStyle(const QString &styleName)
Sets style by name.
virtual void draw(QPainter *p, double xOffset=0) const =0
Draws the style.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:212
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
double prevNiceNumber(double a, double d=1)
QgsPropertyCollection mDataDefinedProperties
QgsComposerScaleBar(QgsComposition *composition)
double measureLine(const QList< QgsPoint > &points) const
Measures the length of a line with multiple segments.
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
int mNumSegmentsLeft
Number of segments on left side.
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
QColor mLineColor
Line color.
bool _readXml(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document. Usually called from readXml methods of su...
void setBackgroundEnabled(const bool drawBackground)
Set whether this item has a Background drawn around it or not.
A class to represent a point.
Definition: qgspoint.h:143
Graphics scene for map printing.
Object representing map window.
void setComposerMap(const QgsComposerMap *map)
bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
void refreshSegmentMillimeters()
Calculates with of a segment in mm and stores it in mSegmentMillimeters.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:207
void invalidateCurrentMap()
Sets mCompositionMap to 0 if the map is deleted.
void setBoxContentSpace(double space)
virtual QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the item&#39;s current state.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:43
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:192
QColor mFillColor
Fill color.
General purpose distance and area calculator.
static double textWidthMM(const QFont &font, const QString &text)
Calculate font width in millimeters for a string, including workarounds for QT font rendering issues...
QgsComposition * mComposition
const QgsComposerMap * composerMap() const
int mNumSegments
Number of segments on right side.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
double mMaxBarWidth
Maximum allowed bar width, when mSegmentSizeMode is FitWidth.
void setSegmentSizeMode(SegmentSizeMode mode)
Sets the size mode for scale bar segments.
SegmentSizeMode mSegmentSizeMode
Either fixed (i.e. mNumUnitsPerSegment) or try to best fit scale bar width (mMinBarWidth, mMaxBarWidth)
double mapWidth() const
Returns diagonal of composer map in selected units (map units / meters / feet / nautical miles) ...
QgsProject * project() const
The project associated with the composition.
virtual void drawBackground(QPainter *p)
Draw background.
QString style() const
Returns style name.
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 mLineWidth
Line width.
double mBoxContentSpace
Space between content and item box.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:197
QString mUnitLabeling
Labeling of map units.
void setSceneRect(const QRectF &rectangle) override
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
double nextNiceNumber(double a, double d=1)
const QgsComposerMap * mComposerMap
Reference to composer map object.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
void setNumUnitsPerSegment(double units)
QString ellipsoid() const
Returns a proj string representing the project&#39;s ellipsoid setting, e.g., "WGS84".
Definition: qgsproject.cpp:437
static QString encodePenCapStyle(Qt::PenCapStyle style)
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...
long srsid() const
Returns the internal CRS ID, if available.
static Q_INVOKABLE double fromUnitToUnitFactor(QgsUnitTypes::DistanceUnit fromUnit, QgsUnitTypes::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
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...
const QgsRectangle * currentMapExtent() const
Returns a pointer to the current map extent, which is either the original user specified extent or th...
Scale bar segment size is fixed to a map unit.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void setEllipsoidalMode(bool flag)
Sets whether coordinates must be projected to ellipsoid before measuring.
void setTickPosition(TickPosition p)
All properties for item.