QGIS API Documentation  2.99.0-Master (40f86b2)
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 "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 "qgssettings.h"
34 
35 #include <QDomDocument>
36 #include <QDomElement>
37 #include <QFontMetricsF>
38 #include <QPainter>
39 
40 #include <cmath>
41 
43  : QgsComposerItem( composition )
44  , mComposerMap( nullptr )
45  , mNumUnitsPerSegment( 0 )
46  , mSegmentSizeMode( SegmentSizeFixed )
47  , mMinBarWidth( 50 )
48  , mMaxBarWidth( 150 )
49  , mFontColor( QColor( 0, 0, 0 ) )
50  , mStyle( nullptr )
51  , mSegmentMillimeters( 0.0 )
52  , mAlignment( Left )
53  , mUnits( MapUnits )
54  , mLineJoinStyle( Qt::MiterJoin )
55  , mLineCapStyle( Qt::SquareCap )
56 {
59 }
60 
62 {
63  delete mStyle;
64 }
65 
66 void QgsComposerScaleBar::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
67 {
68  Q_UNUSED( itemStyle );
69  Q_UNUSED( pWidget );
70  if ( !mStyle || !painter )
71  {
72  return;
73  }
74  if ( !shouldDrawItem() )
75  {
76  return;
77  }
78 
79  drawBackground( painter );
80 
81  //x-offset is half of first label width because labels are drawn centered
82  QString firstLabel = firstLabelString();
83  double firstLabelWidth = QgsComposerUtils::textWidthMM( mFont, firstLabel );
84 
85  mStyle->draw( painter, firstLabelWidth / 2 );
86 
87  //draw frame and selection boxes if necessary
88  drawFrame( painter );
89  if ( isSelected() )
90  {
91  drawSelectionBoxes( painter );
92  }
93 }
94 
96 {
97  if ( !mStyle )
98  {
99  mNumSegments = nSegments;
100  return;
101  }
102  double width = mStyle->calculateBoxSize().width();
103  mNumSegments = nSegments;
104  double widthAfter = mStyle->calculateBoxSize().width();
105  correctXPositionAlignment( width, widthAfter );
106  emit itemChanged();
107 }
108 
110 {
111  if ( !mStyle )
112  {
114  return;
115  }
116  double width = mStyle->calculateBoxSize().width();
119  double widthAfter = mStyle->calculateBoxSize().width();
120  correctXPositionAlignment( width, widthAfter );
121  emit itemChanged();
122 }
123 
125 {
126  if ( !mStyle )
127  {
128  mSegmentSizeMode = mode;
129  return;
130  }
131  double width = mStyle->calculateBoxSize().width();
132  mSegmentSizeMode = mode;
134  double widthAfter = mStyle->calculateBoxSize().width();
135  correctXPositionAlignment( width, widthAfter );
136  emit itemChanged();
137 }
138 
139 void QgsComposerScaleBar::setMinBarWidth( double minWidth )
140 {
141  if ( !mStyle )
142  {
143  mMinBarWidth = minWidth;
144  return;
145  }
146  double width = mStyle->calculateBoxSize().width();
147  mMinBarWidth = minWidth;
149  double widthAfter = mStyle->calculateBoxSize().width();
150  correctXPositionAlignment( width, widthAfter );
151  emit itemChanged();
152 }
153 
154 void QgsComposerScaleBar::setMaxBarWidth( double maxWidth )
155 {
156  if ( !mStyle )
157  {
158  mMaxBarWidth = maxWidth;
159  return;
160  }
161  double width = mStyle->calculateBoxSize().width();
162  mMaxBarWidth = maxWidth;
164  double widthAfter = mStyle->calculateBoxSize().width();
165  correctXPositionAlignment( width, widthAfter );
166  emit itemChanged();
167 }
168 
170 {
171  if ( !mStyle )
172  {
173  mNumSegmentsLeft = nSegmentsLeft;
174  return;
175  }
176  double width = mStyle->calculateBoxSize().width();
177  mNumSegmentsLeft = nSegmentsLeft;
178  double widthAfter = mStyle->calculateBoxSize().width();
179  correctXPositionAlignment( width, widthAfter );
180  emit itemChanged();
181 }
182 
184 {
185  if ( !mStyle )
186  {
187  mBoxContentSpace = space;
188  return;
189  }
190  double width = mStyle->calculateBoxSize().width();
191  mBoxContentSpace = space;
192  double widthAfter = mStyle->calculateBoxSize().width();
193  correctXPositionAlignment( width, widthAfter );
194  emit itemChanged();
195 }
196 
198 {
199  if ( mComposerMap )
200  {
202  disconnect( mComposerMap, &QObject::destroyed, this, &QgsComposerScaleBar::invalidateCurrentMap );
203  }
204  mComposerMap = map;
205 
206  if ( !map )
207  {
208  return;
209  }
210 
212  connect( mComposerMap, &QObject::destroyed, this, &QgsComposerScaleBar::invalidateCurrentMap );
213 
215  emit itemChanged();
216 }
217 
219 {
220  if ( !mComposerMap )
221  {
222  return;
223  }
224 
226  disconnect( mComposerMap, &QObject::destroyed, this, &QgsComposerScaleBar::invalidateCurrentMap );
227  mComposerMap = nullptr;
228 }
229 
231 {
233  const QgsExpressionContext *evalContext = context ? context : &scopedContext;
234 
235  bool forceUpdate = false;
236  //updates data defined properties and redraws item to match
238  {
240  forceUpdate = true;
241  }
243  {
245  forceUpdate = true;
246  }
248  {
250  forceUpdate = true;
251  }
253  {
255  forceUpdate = true;
256  }
257  if ( forceUpdate )
258  {
259  update();
260  }
261 
263 }
264 
265 // nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc
266 inline double nextNiceNumber( double a, double d = 1 )
267 {
268  double s = qPow( 10.0, floor( log10( a ) ) ) / d;
269  return ceil( a / s ) * s;
270 }
271 
272 // prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc
273 inline double prevNiceNumber( double a, double d = 1 )
274 {
275  double s = qPow( 10.0, floor( log10( a ) ) ) / d;
276  return floor( a / s ) * s;
277 }
278 
280 {
281  if ( mComposerMap )
282  {
283  //get mm dimension of composer map
284  QRectF composerItemRect = mComposerMap->rect();
285 
287  {
288  //calculate size depending on mNumUnitsPerSegment
289  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
290  }
291  else /*if(mSegmentSizeMode == SegmentSizeFitWidth)*/
292  {
293  if ( mMaxBarWidth < mMinBarWidth )
294  {
296  }
297  else
298  {
299  double nSegments = ( mNumSegmentsLeft != 0 ) + mNumSegments;
300  // unitsPerSegments which fit minBarWidth resp. maxBarWidth
301  double minUnitsPerSeg = ( mMinBarWidth * mapWidth() ) / ( nSegments * composerItemRect.width() );
302  double maxUnitsPerSeg = ( mMaxBarWidth * mapWidth() ) / ( nSegments * composerItemRect.width() );
303 
304  // Start with coarsest "nice" number closest to minUnitsPerSeg resp
305  // maxUnitsPerSeg, then proceed to finer numbers as long as neither
306  // lowerNiceUnitsPerSeg nor upperNiceUnitsPerSeg are are in
307  // [minUnitsPerSeg, maxUnitsPerSeg]
308  double lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg );
309  double upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg );
310 
311  double d = 1;
312  while ( lowerNiceUnitsPerSeg > maxUnitsPerSeg && upperNiceUnitsPerSeg < minUnitsPerSeg )
313  {
314  d *= 10;
315  lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg, d );
316  upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg, d );
317  }
318 
319  // Pick mNumUnitsPerSegment from {lowerNiceUnitsPerSeg, upperNiceUnitsPerSeg}, use the larger if possible
320  mNumUnitsPerSegment = upperNiceUnitsPerSeg < minUnitsPerSeg ? lowerNiceUnitsPerSeg : upperNiceUnitsPerSeg;
321  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
322  }
323  }
324  }
325 }
326 
328 {
329  if ( !mComposerMap )
330  {
331  return 0.0;
332  }
333 
334  QgsRectangle composerMapRect = *( mComposerMap->currentMapExtent() );
335  if ( mUnits == MapUnits )
336  {
337  return composerMapRect.width();
338  }
339  else
340  {
341  QgsDistanceArea da;
342  da.setEllipsoidalMode( true );
343  da.setSourceCrs( mComposerMap->crs() );
345 
347  double measure = da.measureLine( QgsPoint( composerMapRect.xMinimum(), composerMapRect.yMinimum() ),
348  QgsPoint( composerMapRect.xMaximum(), composerMapRect.yMinimum() ),
349  units );
350  switch ( mUnits )
351  {
354  break;
357  break;
360  break;
362  //avoid warning
363  break;
364  }
365  return measure;
366  }
367 }
368 
370 {
371  mAlignment = a;
372  update();
373  emit itemChanged();
374 }
375 
377 {
378  mUnits = u;
380  emit itemChanged();
381 }
382 
384 {
385  if ( mLineJoinStyle == style )
386  {
387  //no change
388  return;
389  }
391  mPen.setJoinStyle( mLineJoinStyle );
392  update();
393  emit itemChanged();
394 }
395 
397 {
398  if ( mLineCapStyle == style )
399  {
400  //no change
401  return;
402  }
404  mPen.setCapStyle( mLineCapStyle );
405  update();
406  emit itemChanged();
407 }
408 
410 {
411  mNumSegments = 2;
412  mNumSegmentsLeft = 0;
413 
415 
416  //style
417  delete mStyle;
418  mStyle = new QgsSingleBoxScaleBarStyle( this );
419 
420  mHeight = 3;
421 
422  //default to no background
423  setBackgroundEnabled( false );
424 
425  mPen = QPen( mLineColor );
426  mPen.setJoinStyle( mLineJoinStyle );
427  mPen.setCapStyle( mLineCapStyle );
428  mPen.setWidthF( mLineWidth );
429 
430  mBrush.setColor( mFillColor );
431  mBrush.setStyle( Qt::SolidPattern );
432 
433  mBrush2.setColor( mFillColor2 );
434  mBrush2.setStyle( Qt::SolidPattern );
435 
436  //get default composer font from settings
437  QgsSettings settings;
438  QString defaultFontString = settings.value( QStringLiteral( "Composer/defaultFont" ) ).toString();
439  if ( !defaultFontString.isEmpty() )
440  {
441  mFont.setFamily( defaultFontString );
442  }
443  mFont.setPointSizeF( 12.0 );
444  mFontColor = QColor( 0, 0, 0 );
445 
446  mLabelBarSpace = 3.0;
447  mBoxContentSpace = 1.0;
448  emit itemChanged();
449 }
450 
452 {
453  if ( mComposerMap )
454  {
455  setUnits( u );
456  double upperMagnitudeMultiplier = 1.0;
457  double widthInSelectedUnits = mapWidth();
458  double initialUnitsPerSegment = widthInSelectedUnits / 10.0; //default scalebar width equals half the map width
459  setNumUnitsPerSegment( initialUnitsPerSegment );
460 
461  switch ( mUnits )
462  {
463  case MapUnits:
464  {
465  upperMagnitudeMultiplier = 1.0;
466  setUnitLabeling( tr( "units" ) );
467  break;
468  }
469  case Meters:
470  {
471  if ( initialUnitsPerSegment > 1000.0 )
472  {
473  upperMagnitudeMultiplier = 1000.0;
474  setUnitLabeling( tr( "km" ) );
475  }
476  else
477  {
478  upperMagnitudeMultiplier = 1.0;
479  setUnitLabeling( tr( "m" ) );
480  }
481  break;
482  }
483  case Feet:
484  {
485  if ( initialUnitsPerSegment > 5419.95 )
486  {
487  upperMagnitudeMultiplier = 5419.95;
488  setUnitLabeling( tr( "miles" ) );
489  }
490  else
491  {
492  upperMagnitudeMultiplier = 1.0;
493  setUnitLabeling( tr( "ft" ) );
494  }
495  break;
496  }
497  case NauticalMiles:
498  {
499  upperMagnitudeMultiplier = 1;
500  setUnitLabeling( tr( "Nm" ) );
501  break;
502  }
503  }
504 
505  double segmentWidth = initialUnitsPerSegment / upperMagnitudeMultiplier;
506  int segmentMagnitude = floor( log10( segmentWidth ) );
507  double unitsPerSegment = upperMagnitudeMultiplier * ( qPow( 10.0, segmentMagnitude ) );
508  double multiplier = floor( ( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5;
509 
510  if ( multiplier > 0 )
511  {
512  unitsPerSegment = unitsPerSegment * multiplier;
513  }
514  setNumUnitsPerSegment( unitsPerSegment );
515  setNumMapUnitsPerScaleBarUnit( upperMagnitudeMultiplier );
516 
517  setNumSegments( 4 );
518  setNumSegmentsLeft( 2 );
519  }
520 
522  adjustBoxSize();
523  emit itemChanged();
524 }
525 
527 {
528  if ( !mStyle )
529  {
530  return;
531  }
532 
533  QRectF box = mStyle->calculateBoxSize();
534  if ( rect().height() > box.height() )
535  {
536  //keep user specified item height if higher than minimum scale bar height
537  box.setHeight( rect().height() );
538  }
539 
540  //update rect for data defined size and position
541  QRectF newRect = evalItemRect( box, true );
542 
543  //scale bars have a minimum size, respect that regardless of data defined settings
544  if ( newRect.width() < box.width() )
545  {
546  newRect.setWidth( box.width() );
547  }
548  if ( newRect.height() < box.height() )
549  {
550  newRect.setHeight( box.height() );
551  }
552 
554 }
555 
556 void QgsComposerScaleBar::setSceneRect( const QRectF &rectangle )
557 {
558  QRectF box = mStyle->calculateBoxSize();
559  if ( rectangle.height() > box.height() )
560  {
561  //keep user specified item height if higher than minimum scale bar height
562  box.setHeight( rectangle.height() );
563  }
564  box.moveTopLeft( rectangle.topLeft() );
565 
566  //update rect for data defined size and position
567  QRectF newRect = evalItemRect( rectangle );
568 
569  //scale bars have a minimum size, respect that regardless of data defined settings
570  if ( newRect.width() < box.width() )
571  {
572  newRect.setWidth( box.width() );
573  }
574  if ( newRect.height() < box.height() )
575  {
576  newRect.setHeight( box.height() );
577  }
578 
580 }
581 
583 {
584  //Don't adjust box size for numeric scale bars:
585  if ( mStyle && mStyle->name() != QLatin1String( "Numeric" ) )
586  {
587  adjustBoxSize();
588  }
589  QgsComposerItem::update();
590 }
591 
593 {
594  if ( !mStyle )
595  {
596  return;
597  }
598  double width = mStyle->calculateBoxSize().width();
600  double widthAfter = mStyle->calculateBoxSize().width();
601  correctXPositionAlignment( width, widthAfter );
602  update();
603  emit itemChanged();
604 }
605 
606 void QgsComposerScaleBar::segmentPositions( QList<QPair<double, double> > &posWidthList ) const
607 {
608  posWidthList.clear();
609  double mCurrentXCoord = mPen.widthF() + mBoxContentSpace;
610 
611  //left segments
612  double leftSegmentSize = mSegmentMillimeters / mNumSegmentsLeft;
613  for ( int i = 0; i < mNumSegmentsLeft; ++i )
614  {
615  posWidthList.push_back( qMakePair( mCurrentXCoord, leftSegmentSize ) );
616  mCurrentXCoord += leftSegmentSize;
617  }
618 
619  //right segments
620  for ( int i = 0; i < mNumSegments; ++i )
621  {
622  posWidthList.push_back( qMakePair( mCurrentXCoord, mSegmentMillimeters ) );
623  mCurrentXCoord += mSegmentMillimeters;
624  }
625 }
626 
627 void QgsComposerScaleBar::setStyle( const QString &styleName )
628 {
629  delete mStyle;
630  mStyle = nullptr;
631 
632  //switch depending on style name
633  if ( styleName == QLatin1String( "Single Box" ) )
634  {
635  mStyle = new QgsSingleBoxScaleBarStyle( this );
636  }
637  else if ( styleName == QLatin1String( "Double Box" ) )
638  {
639  mStyle = new QgsDoubleBoxScaleBarStyle( this );
640  }
641  else if ( styleName == QLatin1String( "Line Ticks Middle" ) || styleName == QLatin1String( "Line Ticks Down" ) || styleName == QLatin1String( "Line Ticks Up" ) )
642  {
643  QgsTicksScaleBarStyle *tickStyle = new QgsTicksScaleBarStyle( this );
644  if ( styleName == QLatin1String( "Line Ticks Middle" ) )
645  {
647  }
648  else if ( styleName == QLatin1String( "Line Ticks Down" ) )
649  {
651  }
652  else if ( styleName == QLatin1String( "Line Ticks Up" ) )
653  {
655  }
656  mStyle = tickStyle;
657  }
658  else if ( styleName == QLatin1String( "Numeric" ) )
659  {
660  mStyle = new QgsNumericScaleBarStyle( this );
661  }
662  emit itemChanged();
663 }
664 
666 {
667  if ( mStyle )
668  {
669  return mStyle->name();
670  }
671  else
672  {
673  return QLatin1String( "" );
674  }
675 }
676 
678 {
679  if ( mNumSegmentsLeft > 0 )
680  {
681  return QString::number( mNumUnitsPerSegment / mNumMapUnitsPerScaleBarUnit );
682  }
683  else
684  {
685  return QStringLiteral( "0" );
686  }
687 }
688 
690 {
691  return mFont;
692 }
693 
694 void QgsComposerScaleBar::setFont( const QFont &font )
695 {
696  mFont = font;
697  update();
698  emit itemChanged();
699 }
700 
701 bool QgsComposerScaleBar::writeXml( QDomElement &elem, QDomDocument &doc ) const
702 {
703  if ( elem.isNull() )
704  {
705  return false;
706  }
707 
708  QDomElement composerScaleBarElem = doc.createElement( QStringLiteral( "ComposerScaleBar" ) );
709  composerScaleBarElem.setAttribute( QStringLiteral( "height" ), QString::number( mHeight ) );
710  composerScaleBarElem.setAttribute( QStringLiteral( "labelBarSpace" ), QString::number( mLabelBarSpace ) );
711  composerScaleBarElem.setAttribute( QStringLiteral( "boxContentSpace" ), QString::number( mBoxContentSpace ) );
712  composerScaleBarElem.setAttribute( QStringLiteral( "numSegments" ), mNumSegments );
713  composerScaleBarElem.setAttribute( QStringLiteral( "numSegmentsLeft" ), mNumSegmentsLeft );
714  composerScaleBarElem.setAttribute( QStringLiteral( "numUnitsPerSegment" ), QString::number( mNumUnitsPerSegment ) );
715  composerScaleBarElem.setAttribute( QStringLiteral( "segmentSizeMode" ), mSegmentSizeMode );
716  composerScaleBarElem.setAttribute( QStringLiteral( "minBarWidth" ), mMinBarWidth );
717  composerScaleBarElem.setAttribute( QStringLiteral( "maxBarWidth" ), mMaxBarWidth );
718  composerScaleBarElem.setAttribute( QStringLiteral( "segmentMillimeters" ), QString::number( mSegmentMillimeters ) );
719  composerScaleBarElem.setAttribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QString::number( mNumMapUnitsPerScaleBarUnit ) );
720  composerScaleBarElem.appendChild( QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "scaleBarFont" ) ) );
721  composerScaleBarElem.setAttribute( QStringLiteral( "outlineWidth" ), QString::number( mLineWidth ) );
722  composerScaleBarElem.setAttribute( QStringLiteral( "unitLabel" ), mUnitLabeling );
723  composerScaleBarElem.setAttribute( QStringLiteral( "units" ), mUnits );
724  composerScaleBarElem.setAttribute( QStringLiteral( "lineJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mLineJoinStyle ) );
725  composerScaleBarElem.setAttribute( QStringLiteral( "lineCapStyle" ), QgsSymbolLayerUtils::encodePenCapStyle( mLineCapStyle ) );
726 
727  //style
728  if ( mStyle )
729  {
730  composerScaleBarElem.setAttribute( QStringLiteral( "style" ), mStyle->name() );
731  }
732 
733  //map id
734  if ( mComposerMap )
735  {
736  composerScaleBarElem.setAttribute( QStringLiteral( "mapId" ), mComposerMap->id() );
737  }
738 
739  //colors
740 
741  //fill color
742  QDomElement fillColorElem = doc.createElement( QStringLiteral( "fillColor" ) );
743  fillColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mFillColor.red() ) );
744  fillColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mFillColor.green() ) );
745  fillColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mFillColor.blue() ) );
746  fillColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFillColor.alpha() ) );
747  composerScaleBarElem.appendChild( fillColorElem );
748 
749  //fill color 2
750  QDomElement fillColor2Elem = doc.createElement( QStringLiteral( "fillColor2" ) );
751  fillColor2Elem.setAttribute( QStringLiteral( "red" ), QString::number( mFillColor2.red() ) );
752  fillColor2Elem.setAttribute( QStringLiteral( "green" ), QString::number( mFillColor2.green() ) );
753  fillColor2Elem.setAttribute( QStringLiteral( "blue" ), QString::number( mFillColor2.blue() ) );
754  fillColor2Elem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFillColor2.alpha() ) );
755  composerScaleBarElem.appendChild( fillColor2Elem );
756 
757  //pen color
758  QDomElement strokeColorElem = doc.createElement( QStringLiteral( "strokeColor" ) );
759  strokeColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mLineColor.red() ) );
760  strokeColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mLineColor.green() ) );
761  strokeColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mLineColor.blue() ) );
762  strokeColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mLineColor.alpha() ) );
763  composerScaleBarElem.appendChild( strokeColorElem );
764 
765  //font color
766  QDomElement fontColorElem = doc.createElement( QStringLiteral( "textColor" ) );
767  fontColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mFontColor.red() ) );
768  fontColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mFontColor.green() ) );
769  fontColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mFontColor.blue() ) );
770  fontColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFontColor.alpha() ) );
771  composerScaleBarElem.appendChild( fontColorElem );
772 
773  //alignment
774  composerScaleBarElem.setAttribute( QStringLiteral( "alignment" ), QString::number( static_cast< int >( mAlignment ) ) );
775 
776  elem.appendChild( composerScaleBarElem );
777  return _writeXml( composerScaleBarElem, doc );
778 }
779 
780 bool QgsComposerScaleBar::readXml( const QDomElement &itemElem, const QDomDocument &doc )
781 {
782  if ( itemElem.isNull() )
783  {
784  return false;
785  }
786 
787  mHeight = itemElem.attribute( QStringLiteral( "height" ), QStringLiteral( "5.0" ) ).toDouble();
788  mLabelBarSpace = itemElem.attribute( QStringLiteral( "labelBarSpace" ), QStringLiteral( "3.0" ) ).toDouble();
789  mBoxContentSpace = itemElem.attribute( QStringLiteral( "boxContentSpace" ), QStringLiteral( "1.0" ) ).toDouble();
790  mNumSegments = itemElem.attribute( QStringLiteral( "numSegments" ), QStringLiteral( "2" ) ).toInt();
791  mNumSegmentsLeft = itemElem.attribute( QStringLiteral( "numSegmentsLeft" ), QStringLiteral( "0" ) ).toInt();
792  mNumUnitsPerSegment = itemElem.attribute( QStringLiteral( "numUnitsPerSegment" ), QStringLiteral( "1.0" ) ).toDouble();
793  mSegmentSizeMode = static_cast<SegmentSizeMode>( itemElem.attribute( QStringLiteral( "segmentSizeMode" ), QStringLiteral( "0" ) ).toInt() );
794  mMinBarWidth = itemElem.attribute( QStringLiteral( "minBarWidth" ), QStringLiteral( "50" ) ).toInt();
795  mMaxBarWidth = itemElem.attribute( QStringLiteral( "maxBarWidth" ), QStringLiteral( "150" ) ).toInt();
796  mSegmentMillimeters = itemElem.attribute( QStringLiteral( "segmentMillimeters" ), QStringLiteral( "0.0" ) ).toDouble();
797  mNumMapUnitsPerScaleBarUnit = itemElem.attribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QStringLiteral( "1.0" ) ).toDouble();
798  mLineWidth = itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "0.3" ) ).toDouble();
799  mPen.setWidthF( mLineWidth );
800  mUnitLabeling = itemElem.attribute( QStringLiteral( "unitLabel" ) );
801  mLineJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( itemElem.attribute( QStringLiteral( "lineJoinStyle" ), QStringLiteral( "miter" ) ) );
802  mPen.setJoinStyle( mLineJoinStyle );
803  mLineCapStyle = QgsSymbolLayerUtils::decodePenCapStyle( itemElem.attribute( QStringLiteral( "lineCapStyle" ), QStringLiteral( "square" ) ) );
804  mPen.setCapStyle( mLineCapStyle );
805  if ( !QgsFontUtils::setFromXmlChildNode( mFont, itemElem, QStringLiteral( "scaleBarFont" ) ) )
806  {
807  mFont.fromString( itemElem.attribute( QStringLiteral( "font" ), QLatin1String( "" ) ) );
808  }
809 
810  //colors
811  //fill color
812  QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "fillColor" ) );
813  if ( !fillColorList.isEmpty() )
814  {
815  QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
816  bool redOk, greenOk, blueOk, alphaOk;
817  int fillRed, fillGreen, fillBlue, fillAlpha;
818 
819  fillRed = fillColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
820  fillGreen = fillColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
821  fillBlue = fillColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
822  fillAlpha = fillColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
823 
824  if ( redOk && greenOk && blueOk && alphaOk )
825  {
826  mFillColor = QColor( fillRed, fillGreen, fillBlue, fillAlpha );
827  mBrush.setColor( mFillColor );
828  }
829  }
830  else
831  {
832  mFillColor = QColor( itemElem.attribute( QStringLiteral( "brushColor" ), QStringLiteral( "#000000" ) ) );
833  mBrush.setColor( mFillColor );
834  }
835 
836  //fill color 2
837  QDomNodeList fillColor2List = itemElem.elementsByTagName( QStringLiteral( "fillColor2" ) );
838  if ( !fillColor2List.isEmpty() )
839  {
840  QDomElement fillColor2Elem = fillColor2List.at( 0 ).toElement();
841  bool redOk, greenOk, blueOk, alphaOk;
842  int fillRed, fillGreen, fillBlue, fillAlpha;
843 
844  fillRed = fillColor2Elem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
845  fillGreen = fillColor2Elem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
846  fillBlue = fillColor2Elem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
847  fillAlpha = fillColor2Elem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
848 
849  if ( redOk && greenOk && blueOk && alphaOk )
850  {
851  mFillColor2 = QColor( fillRed, fillGreen, fillBlue, fillAlpha );
852  mBrush2.setColor( mFillColor2 );
853  }
854  }
855  else
856  {
857  mFillColor2 = QColor( itemElem.attribute( QStringLiteral( "brush2Color" ), QStringLiteral( "#ffffff" ) ) );
858  mBrush2.setColor( mFillColor2 );
859  }
860 
861  //stroke color
862  QDomNodeList strokeColorList = itemElem.elementsByTagName( QStringLiteral( "strokeColor" ) );
863  if ( !strokeColorList.isEmpty() )
864  {
865  QDomElement strokeColorElem = strokeColorList.at( 0 ).toElement();
866  bool redOk, greenOk, blueOk, alphaOk;
867  int strokeRed, strokeGreen, strokeBlue, strokeAlpha;
868 
869  strokeRed = strokeColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
870  strokeGreen = strokeColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
871  strokeBlue = strokeColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
872  strokeAlpha = strokeColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
873 
874  if ( redOk && greenOk && blueOk && alphaOk )
875  {
876  mLineColor = QColor( strokeRed, strokeGreen, strokeBlue, strokeAlpha );
877  mPen.setColor( mLineColor );
878  }
879  }
880  else
881  {
882  mLineColor = QColor( itemElem.attribute( QStringLiteral( "penColor" ), QStringLiteral( "#000000" ) ) );
883  mPen.setColor( mLineColor );
884  }
885 
886  //font color
887  QDomNodeList textColorList = itemElem.elementsByTagName( QStringLiteral( "textColor" ) );
888  if ( !textColorList.isEmpty() )
889  {
890  QDomElement textColorElem = textColorList.at( 0 ).toElement();
891  bool redOk, greenOk, blueOk, alphaOk;
892  int textRed, textGreen, textBlue, textAlpha;
893 
894  textRed = textColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
895  textGreen = textColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
896  textBlue = textColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
897  textAlpha = textColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
898 
899  if ( redOk && greenOk && blueOk && alphaOk )
900  {
901  mFontColor = QColor( textRed, textGreen, textBlue, textAlpha );
902  }
903  }
904  else
905  {
906  mFontColor.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
907  }
908 
909  //style
910  delete mStyle;
911  mStyle = nullptr;
912  QString styleString = itemElem.attribute( QStringLiteral( "style" ), QLatin1String( "" ) );
913  setStyle( tr( styleString.toLocal8Bit().data() ) );
914 
915  mUnits = static_cast< ScaleBarUnits >( itemElem.attribute( QStringLiteral( "units" ) ).toInt() );
916  mAlignment = static_cast< Alignment >( itemElem.attribute( QStringLiteral( "alignment" ), QStringLiteral( "0" ) ).toInt() );
917 
918  //map
919  int mapId = itemElem.attribute( QStringLiteral( "mapId" ), QStringLiteral( "-1" ) ).toInt();
920  if ( mapId >= 0 )
921  {
924  if ( mComposerMap )
925  {
927  connect( mComposerMap, &QObject::destroyed, this, &QgsComposerScaleBar::invalidateCurrentMap );
928  }
929  }
930 
932 
933  //restore general composer item properties
934  QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral( "ComposerItem" ) );
935  if ( !composerItemList.isEmpty() )
936  {
937  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
938  _readXml( composerItemElem, doc );
939  }
940 
941  return true;
942 }
943 
944 void QgsComposerScaleBar::correctXPositionAlignment( double width, double widthAfter )
945 {
946  //Don't adjust position for numeric scale bars:
947  if ( mStyle->name() == QLatin1String( "Numeric" ) )
948  {
949  return;
950  }
951 
952  if ( mAlignment == Middle )
953  {
954  move( -( widthAfter - width ) / 2.0, 0 );
955  }
956  else if ( mAlignment == Right )
957  {
958  move( -( widthAfter - width ), 0 );
959  }
960 }
961 
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)
This class is a composition of two QSettings instances:
Definition: qgssettings.h:51
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:215
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:37
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:210
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:42
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:195
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.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=Section::NoSection) const
Returns the value for setting key.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:200
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 extentChanged()
void setNumUnitsPerSegment(double units)
QString ellipsoid() const
Returns a proj string representing the project&#39;s ellipsoid setting, e.g., "WGS84".
Definition: qgsproject.cpp:438
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...
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.