QGIS API Documentation  3.0.2-Girona (307d082)
qgslayoutitemscalebar.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutitemscalebar.cpp
3  -------------------------
4  begin : November 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
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 "qgslayoutitemscalebar.h"
18 #include "qgslayoutitemregistry.h"
19 #include "qgslayoutitemmap.h"
20 #include "qgslayout.h"
21 #include "qgslayoututils.h"
22 #include "qgsdistancearea.h"
23 #include "qgsscalebarrenderer.h"
25 #include "qgsmapsettings.h"
29 #include "qgsrectangle.h"
30 #include "qgsproject.h"
31 #include "qgssymbollayerutils.h"
32 #include "qgsfontutils.h"
33 #include "qgsunittypes.h"
34 #include "qgssettings.h"
35 
36 #include <QDomDocument>
37 #include <QDomElement>
38 #include <QFontMetricsF>
39 #include <QPainter>
40 
41 #include <cmath>
42 
44  : QgsLayoutItem( layout )
45 {
48 }
49 
51 {
53 }
54 
56 {
57  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemScaleBar.svg" ) );
58 }
59 
61 {
62  return new QgsLayoutItemScaleBar( layout );
63 }
64 
66 {
67  return QgsLayoutSize( mStyle->calculateBoxSize( mSettings, createScaleContext() ), QgsUnitTypes::LayoutMillimeters );
68 }
69 
71 {
72  if ( !mStyle )
73  return;
74 
75  mStyle->draw( context.renderContext(), mSettings, createScaleContext() );
76 }
77 
79 {
80  if ( !mStyle )
81  {
82  mSettings.setNumberOfSegments( nSegments );
83  return;
84  }
85  mSettings.setNumberOfSegments( nSegments );
87 }
88 
90 {
91  if ( !mStyle )
92  {
93  mSettings.setUnitsPerSegment( units );
94  return;
95  }
96  mSettings.setUnitsPerSegment( units );
97  refreshSegmentMillimeters();
99 }
100 
102 {
103  if ( !mStyle )
104  {
105  mSettings.setSegmentSizeMode( mode );
106  return;
107  }
108  mSettings.setSegmentSizeMode( mode );
109  refreshSegmentMillimeters();
111 }
112 
114 {
115  if ( !mStyle )
116  {
117  mSettings.setMinimumBarWidth( minWidth );
118  return;
119  }
120  mSettings.setMinimumBarWidth( minWidth );
121  refreshSegmentMillimeters();
123 }
124 
126 {
127  if ( !mStyle )
128  {
129  mSettings.setMaximumBarWidth( maxWidth );
130  return;
131  }
132  mSettings.setMaximumBarWidth( maxWidth );
133  refreshSegmentMillimeters();
135 }
136 
138 {
139  if ( !mStyle )
140  {
141  mSettings.setNumberOfSegmentsLeft( nSegmentsLeft );
142  return;
143  }
144  mSettings.setNumberOfSegmentsLeft( nSegmentsLeft );
146 }
147 
149 {
150  if ( !mStyle )
151  {
152  mSettings.setBoxContentSpace( space );
153  return;
154  }
155  mSettings.setBoxContentSpace( space );
156  refreshItemSize();
157 }
158 
160 {
161  disconnectCurrentMap();
162 
163  mMap = map;
164 
165  if ( !map )
166  {
167  return;
168  }
169 
170  connect( mMap, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemScaleBar::updateScale );
171  connect( mMap, &QObject::destroyed, this, &QgsLayoutItemScaleBar::disconnectCurrentMap );
172 
173  refreshSegmentMillimeters();
174  emit changed();
175 }
176 
177 void QgsLayoutItemScaleBar::disconnectCurrentMap()
178 {
179  if ( !mMap )
180  {
181  return;
182  }
183 
184  disconnect( mMap, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemScaleBar::updateScale );
185  disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemScaleBar::disconnectCurrentMap );
186  mMap = nullptr;
187 }
188 
190 {
192 
193  bool forceUpdate = false;
194  //updates data defined properties and redraws item to match
196  {
197  QBrush b = mSettings.brush();
199  mSettings.setBrush( b );
200  forceUpdate = true;
201  }
203  {
204  QBrush b = mSettings.brush2();
206  mSettings.setBrush2( b );
207  forceUpdate = true;
208  }
210  {
211  QPen p = mSettings.pen();
213  mSettings.setPen( p );
214  forceUpdate = true;
215  }
217  {
218  QPen p = mSettings.pen();
220  mSettings.setPen( p );
221  forceUpdate = true;
222  }
223  if ( forceUpdate )
224  {
225  refreshItemSize();
226  update();
227  }
228 
230 }
231 
232 // nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc
233 inline double nextNiceNumber( double a, double d = 1 )
234 {
235  double s = std::pow( 10.0, std::floor( std::log10( a ) ) ) / d;
236  return std::ceil( a / s ) * s;
237 }
238 
239 // prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc
240 inline double prevNiceNumber( double a, double d = 1 )
241 {
242  double s = std::pow( 10.0, std::floor( std::log10( a ) ) ) / d;
243  return std::floor( a / s ) * s;
244 }
245 
246 void QgsLayoutItemScaleBar::refreshSegmentMillimeters()
247 {
248  if ( mMap )
249  {
250  //get mm dimension of composer map
251  QRectF composerItemRect = mMap->rect();
252 
254  {
255  //calculate size depending on mNumUnitsPerSegment
256  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mSettings.unitsPerSegment();
257  }
258  else /*if(mSegmentSizeMode == SegmentSizeFitWidth)*/
259  {
260  if ( mSettings.maximumBarWidth() < mSettings.minimumBarWidth() )
261  {
262  mSegmentMillimeters = 0;
263  }
264  else
265  {
266  double nSegments = ( mSettings.numberOfSegmentsLeft() != 0 ) + mSettings.numberOfSegments();
267  // unitsPerSegments which fit minBarWidth resp. maxBarWidth
268  double minUnitsPerSeg = ( mSettings.minimumBarWidth() * mapWidth() ) / ( nSegments * composerItemRect.width() );
269  double maxUnitsPerSeg = ( mSettings.maximumBarWidth() * mapWidth() ) / ( nSegments * composerItemRect.width() );
270 
271  // Start with coarsest "nice" number closest to minUnitsPerSeg resp
272  // maxUnitsPerSeg, then proceed to finer numbers as long as neither
273  // lowerNiceUnitsPerSeg nor upperNiceUnitsPerSeg are are in
274  // [minUnitsPerSeg, maxUnitsPerSeg]
275  double lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg );
276  double upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg );
277 
278  double d = 1;
279  while ( lowerNiceUnitsPerSeg > maxUnitsPerSeg && upperNiceUnitsPerSeg < minUnitsPerSeg )
280  {
281  d *= 10;
282  lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg, d );
283  upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg, d );
284  }
285 
286  // Pick mNumUnitsPerSegment from {lowerNiceUnitsPerSeg, upperNiceUnitsPerSeg}, use the larger if possible
287  mSettings.setUnitsPerSegment( upperNiceUnitsPerSeg < minUnitsPerSeg ? lowerNiceUnitsPerSeg : upperNiceUnitsPerSeg );
288  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mSettings.unitsPerSegment();
289  }
290  }
291  }
292 }
293 
294 double QgsLayoutItemScaleBar::mapWidth() const
295 {
296  if ( !mMap )
297  {
298  return 0.0;
299  }
300 
301  QgsRectangle mapExtent = mMap->extent();
302  if ( mSettings.units() == QgsUnitTypes::DistanceUnknownUnit )
303  {
304  return mapExtent.width();
305  }
306  else
307  {
308  QgsDistanceArea da;
309  da.setSourceCrs( mMap->crs(), mLayout->project()->transformContext() );
310  da.setEllipsoid( mLayout->project()->ellipsoid() );
311 
313  double measure = da.measureLine( QgsPointXY( mapExtent.xMinimum(), mapExtent.yMinimum() ),
314  QgsPointXY( mapExtent.xMaximum(), mapExtent.yMinimum() ) );
315  measure /= QgsUnitTypes::fromUnitToUnitFactor( mSettings.units(), units );
316  return measure;
317  }
318 }
319 
320 QgsScaleBarRenderer::ScaleBarContext QgsLayoutItemScaleBar::createScaleContext() const
321 {
323  scaleContext.size = rect().size();
324  scaleContext.segmentWidth = mSegmentMillimeters;
325  scaleContext.scale = mMap ? mMap->scale() : 1.0;
326  return scaleContext;
327 }
328 
330 {
331  mSettings.setAlignment( a );
332  refreshItemSize();
333  emit changed();
334 }
335 
337 {
338  mSettings.setUnits( u );
339  refreshSegmentMillimeters();
340  refreshItemSize();
341  emit changed();
342 }
343 
345 {
346  if ( mSettings.lineJoinStyle() == style )
347  {
348  //no change
349  return;
350  }
351  mSettings.setLineJoinStyle( style );
352  update();
353  emit changed();
354 }
355 
357 {
358  if ( mSettings.lineCapStyle() == style )
359  {
360  //no change
361  return;
362  }
363  mSettings.setLineCapStyle( style );
364  update();
365  emit changed();
366 }
367 
369 {
370  //style
371  mStyle = qgis::make_unique< QgsSingleBoxScaleBarRenderer >();
372 
373  //default to no background
374  setBackgroundEnabled( false );
375 
376  //get default composer font from settings
377  QgsSettings settings;
378  QString defaultFontString = settings.value( QStringLiteral( "LayoutDesigner/defaultFont" ), QVariant(), QgsSettings::Gui ).toString();
379  QFont f;
380  if ( !defaultFontString.isEmpty() )
381  {
382  f.setFamily( defaultFontString );
383  }
384  f.setPointSizeF( 12.0 );
385  mSettings.setFont( f );
386 
388  refreshItemSize();
389 
390  emit changed();
391 }
392 
394 {
395  if ( !mMap )
397 
398  QgsCoordinateReferenceSystem crs = mMap->crs();
399  // start with crs units
402  {
403  // geographic CRS, use metric units
405  }
406 
407  // try to pick reasonable choice between metric / imperial units
408  double widthInSelectedUnits = mapWidth();
409  double initialUnitsPerSegment = widthInSelectedUnits / 10.0; //default scalebar width equals half the map width
410  switch ( unit )
411  {
413  {
414  if ( initialUnitsPerSegment > 1000.0 )
415  {
417  }
418  break;
419  }
421  {
422  if ( initialUnitsPerSegment > 5419.95 )
423  {
425  }
426  break;
427  }
428  default:
429  break;
430  }
431 
432  return unit;
433 }
434 
436 {
437  mSettings.setUnits( units );
438  if ( mMap )
439  {
440  double upperMagnitudeMultiplier = 1.0;
441  double widthInSelectedUnits = mapWidth();
442  double initialUnitsPerSegment = widthInSelectedUnits / 10.0; //default scalebar width equals half the map width
443  mSettings.setUnitsPerSegment( initialUnitsPerSegment );
444 
446  upperMagnitudeMultiplier = 1;
447 
448  double segmentWidth = initialUnitsPerSegment / upperMagnitudeMultiplier;
449  int segmentMagnitude = std::floor( std::log10( segmentWidth ) );
450  double unitsPerSegment = upperMagnitudeMultiplier * ( std::pow( 10.0, segmentMagnitude ) );
451  double multiplier = std::floor( ( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5;
452 
453  if ( multiplier > 0 )
454  {
455  unitsPerSegment = unitsPerSegment * multiplier;
456  }
457  mSettings.setUnitsPerSegment( unitsPerSegment );
458  mSettings.setMapUnitsPerScaleBarUnit( upperMagnitudeMultiplier );
459 
460  mSettings.setNumberOfSegments( 4 );
461  mSettings.setNumberOfSegmentsLeft( 2 );
462  }
463 
464  refreshSegmentMillimeters();
466  emit changed();
467 }
468 
470 {
471  if ( !mStyle )
472  return;
473 
474  double widthMM = mStyle->calculateBoxSize( mSettings, createScaleContext() ).width();
475  QgsLayoutSize currentSize = sizeWithUnits();
476  currentSize.setWidth( mLayout->renderContext().measurementConverter().convert( QgsLayoutMeasurement( widthMM, QgsUnitTypes::LayoutMillimeters ), currentSize.units() ).length() );
477  attemptResize( currentSize );
478  update();
479  emit changed();
480 }
481 
483 {
484  //Don't adjust box size for numeric scale bars:
485  if ( mStyle && mStyle->name() != QLatin1String( "Numeric" ) )
486  {
487  refreshItemSize();
488  }
489  QgsLayoutItem::update();
490 }
491 
492 void QgsLayoutItemScaleBar::updateScale()
493 {
494  refreshSegmentMillimeters();
496  update();
497 }
498 
499 void QgsLayoutItemScaleBar::setStyle( const QString &styleName )
500 {
501  //switch depending on style name
502  if ( styleName == QLatin1String( "Single Box" ) )
503  {
504  mStyle = qgis::make_unique< QgsSingleBoxScaleBarRenderer >();
505  }
506  else if ( styleName == QLatin1String( "Double Box" ) )
507  {
508  mStyle = qgis::make_unique< QgsDoubleBoxScaleBarRenderer >();
509  }
510  else if ( styleName == QLatin1String( "Line Ticks Middle" ) || styleName == QLatin1String( "Line Ticks Down" ) || styleName == QLatin1String( "Line Ticks Up" ) )
511  {
512  std::unique_ptr< QgsTicksScaleBarRenderer > tickStyle = qgis::make_unique< QgsTicksScaleBarRenderer >();
513  if ( styleName == QLatin1String( "Line Ticks Middle" ) )
514  {
515  tickStyle->setTickPosition( QgsTicksScaleBarRenderer::TicksMiddle );
516  }
517  else if ( styleName == QLatin1String( "Line Ticks Down" ) )
518  {
519  tickStyle->setTickPosition( QgsTicksScaleBarRenderer::TicksDown );
520  }
521  else if ( styleName == QLatin1String( "Line Ticks Up" ) )
522  {
523  tickStyle->setTickPosition( QgsTicksScaleBarRenderer::TicksUp );
524  }
525  mStyle = std::move( tickStyle );
526  }
527  else if ( styleName == QLatin1String( "Numeric" ) )
528  {
529  mStyle = qgis::make_unique< QgsNumericScaleBarRenderer >();
530  }
531  refreshItemSize();
532  emit changed();
533 }
534 
536 {
537  if ( mStyle )
538  {
539  return mStyle->name();
540  }
541  else
542  {
543  return QString();
544  }
545 }
546 
548 {
549  return mSettings.font();
550 }
551 
553 {
554  mSettings.setFont( font );
555  refreshItemSize();
556  emit changed();
557 }
558 
559 bool QgsLayoutItemScaleBar::writePropertiesToElement( QDomElement &composerScaleBarElem, QDomDocument &doc, const QgsReadWriteContext & ) const
560 {
561  composerScaleBarElem.setAttribute( QStringLiteral( "height" ), QString::number( mSettings.height() ) );
562  composerScaleBarElem.setAttribute( QStringLiteral( "labelBarSpace" ), QString::number( mSettings.labelBarSpace() ) );
563  composerScaleBarElem.setAttribute( QStringLiteral( "boxContentSpace" ), QString::number( mSettings.boxContentSpace() ) );
564  composerScaleBarElem.setAttribute( QStringLiteral( "numSegments" ), mSettings.numberOfSegments() );
565  composerScaleBarElem.setAttribute( QStringLiteral( "numSegmentsLeft" ), mSettings.numberOfSegmentsLeft() );
566  composerScaleBarElem.setAttribute( QStringLiteral( "numUnitsPerSegment" ), QString::number( mSettings.unitsPerSegment() ) );
567  composerScaleBarElem.setAttribute( QStringLiteral( "segmentSizeMode" ), mSettings.segmentSizeMode() );
568  composerScaleBarElem.setAttribute( QStringLiteral( "minBarWidth" ), mSettings.minimumBarWidth() );
569  composerScaleBarElem.setAttribute( QStringLiteral( "maxBarWidth" ), mSettings.maximumBarWidth() );
570  composerScaleBarElem.setAttribute( QStringLiteral( "segmentMillimeters" ), QString::number( mSegmentMillimeters ) );
571  composerScaleBarElem.setAttribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QString::number( mSettings.mapUnitsPerScaleBarUnit() ) );
572  composerScaleBarElem.appendChild( QgsFontUtils::toXmlElement( mSettings.font(), doc, QStringLiteral( "scaleBarFont" ) ) );
573  composerScaleBarElem.setAttribute( QStringLiteral( "outlineWidth" ), QString::number( mSettings.lineWidth() ) );
574  composerScaleBarElem.setAttribute( QStringLiteral( "unitLabel" ), mSettings.unitLabel() );
575  composerScaleBarElem.setAttribute( QStringLiteral( "unitType" ), QgsUnitTypes::encodeUnit( mSettings.units() ) );
576  composerScaleBarElem.setAttribute( QStringLiteral( "lineJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mSettings.lineJoinStyle() ) );
577  composerScaleBarElem.setAttribute( QStringLiteral( "lineCapStyle" ), QgsSymbolLayerUtils::encodePenCapStyle( mSettings.lineCapStyle() ) );
578 
579  //style
580  if ( mStyle )
581  {
582  composerScaleBarElem.setAttribute( QStringLiteral( "style" ), mStyle->name() );
583  }
584 
585  //map id
586  if ( mMap )
587  {
588  composerScaleBarElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
589  }
590 
591  //colors
592 
593  //fill color
594  QDomElement fillColorElem = doc.createElement( QStringLiteral( "fillColor" ) );
595  fillColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mSettings.fillColor().red() ) );
596  fillColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mSettings.fillColor().green() ) );
597  fillColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mSettings.fillColor().blue() ) );
598  fillColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mSettings.fillColor().alpha() ) );
599  composerScaleBarElem.appendChild( fillColorElem );
600 
601  //fill color 2
602  QDomElement fillColor2Elem = doc.createElement( QStringLiteral( "fillColor2" ) );
603  fillColor2Elem.setAttribute( QStringLiteral( "red" ), QString::number( mSettings.fillColor2().red() ) );
604  fillColor2Elem.setAttribute( QStringLiteral( "green" ), QString::number( mSettings.fillColor2().green() ) );
605  fillColor2Elem.setAttribute( QStringLiteral( "blue" ), QString::number( mSettings.fillColor2().blue() ) );
606  fillColor2Elem.setAttribute( QStringLiteral( "alpha" ), QString::number( mSettings.fillColor2().alpha() ) );
607  composerScaleBarElem.appendChild( fillColor2Elem );
608 
609  //pen color
610  QDomElement strokeColorElem = doc.createElement( QStringLiteral( "strokeColor" ) );
611  strokeColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mSettings.lineColor().red() ) );
612  strokeColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mSettings.lineColor().green() ) );
613  strokeColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mSettings.lineColor().blue() ) );
614  strokeColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mSettings.lineColor().alpha() ) );
615  composerScaleBarElem.appendChild( strokeColorElem );
616 
617  //font color
618  QDomElement fontColorElem = doc.createElement( QStringLiteral( "textColor" ) );
619  fontColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mSettings.fontColor().red() ) );
620  fontColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mSettings.fontColor().green() ) );
621  fontColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mSettings.fontColor().blue() ) );
622  fontColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mSettings.fontColor().alpha() ) );
623  composerScaleBarElem.appendChild( fontColorElem );
624 
625  //alignment
626  composerScaleBarElem.setAttribute( QStringLiteral( "alignment" ), QString::number( static_cast< int >( mSettings.alignment() ) ) );
627 
628  return true;
629 }
630 
631 bool QgsLayoutItemScaleBar::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext & )
632 {
633  mSettings.setHeight( itemElem.attribute( QStringLiteral( "height" ), QStringLiteral( "5.0" ) ).toDouble() );
634  mSettings.setLabelBarSpace( itemElem.attribute( QStringLiteral( "labelBarSpace" ), QStringLiteral( "3.0" ) ).toDouble() );
635  mSettings.setBoxContentSpace( itemElem.attribute( QStringLiteral( "boxContentSpace" ), QStringLiteral( "1.0" ) ).toDouble() );
636  mSettings.setNumberOfSegments( itemElem.attribute( QStringLiteral( "numSegments" ), QStringLiteral( "2" ) ).toInt() );
637  mSettings.setNumberOfSegmentsLeft( itemElem.attribute( QStringLiteral( "numSegmentsLeft" ), QStringLiteral( "0" ) ).toInt() );
638  mSettings.setUnitsPerSegment( itemElem.attribute( QStringLiteral( "numUnitsPerSegment" ), QStringLiteral( "1.0" ) ).toDouble() );
639  mSettings.setSegmentSizeMode( static_cast<QgsScaleBarSettings::SegmentSizeMode>( itemElem.attribute( QStringLiteral( "segmentSizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
640  mSettings.setMinimumBarWidth( itemElem.attribute( QStringLiteral( "minBarWidth" ), QStringLiteral( "50" ) ).toDouble() );
641  mSettings.setMaximumBarWidth( itemElem.attribute( QStringLiteral( "maxBarWidth" ), QStringLiteral( "150" ) ).toDouble() );
642  mSegmentMillimeters = itemElem.attribute( QStringLiteral( "segmentMillimeters" ), QStringLiteral( "0.0" ) ).toDouble();
643  mSettings.setMapUnitsPerScaleBarUnit( itemElem.attribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QStringLiteral( "1.0" ) ).toDouble() );
644  mSettings.setLineWidth( itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "0.3" ) ).toDouble() );
645  mSettings.setUnitLabel( itemElem.attribute( QStringLiteral( "unitLabel" ) ) );
646  mSettings.setLineJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( itemElem.attribute( QStringLiteral( "lineJoinStyle" ), QStringLiteral( "miter" ) ) ) );
647  mSettings.setLineCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( itemElem.attribute( QStringLiteral( "lineCapStyle" ), QStringLiteral( "square" ) ) ) );
648  QFont f;
649  if ( !QgsFontUtils::setFromXmlChildNode( f, itemElem, QStringLiteral( "scaleBarFont" ) ) )
650  {
651  f.fromString( itemElem.attribute( QStringLiteral( "font" ), QLatin1String( "" ) ) );
652  }
653  mSettings.setFont( f );
654 
655  //colors
656  //fill color
657  QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "fillColor" ) );
658  if ( !fillColorList.isEmpty() )
659  {
660  QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
661  bool redOk, greenOk, blueOk, alphaOk;
662  int fillRed, fillGreen, fillBlue, fillAlpha;
663 
664  fillRed = fillColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
665  fillGreen = fillColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
666  fillBlue = fillColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
667  fillAlpha = fillColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
668 
669  if ( redOk && greenOk && blueOk && alphaOk )
670  {
671  mSettings.setFillColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) );
672  }
673  }
674  else
675  {
676  mSettings.setFillColor( QColor( itemElem.attribute( QStringLiteral( "brushColor" ), QStringLiteral( "#000000" ) ) ) );
677  }
678 
679  //fill color 2
680  QDomNodeList fillColor2List = itemElem.elementsByTagName( QStringLiteral( "fillColor2" ) );
681  if ( !fillColor2List.isEmpty() )
682  {
683  QDomElement fillColor2Elem = fillColor2List.at( 0 ).toElement();
684  bool redOk, greenOk, blueOk, alphaOk;
685  int fillRed, fillGreen, fillBlue, fillAlpha;
686 
687  fillRed = fillColor2Elem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
688  fillGreen = fillColor2Elem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
689  fillBlue = fillColor2Elem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
690  fillAlpha = fillColor2Elem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
691 
692  if ( redOk && greenOk && blueOk && alphaOk )
693  {
694  mSettings.setFillColor2( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) );
695  }
696  }
697  else
698  {
699  mSettings.setFillColor2( QColor( itemElem.attribute( QStringLiteral( "brush2Color" ), QStringLiteral( "#ffffff" ) ) ) );
700  }
701 
702  //stroke color
703  QDomNodeList strokeColorList = itemElem.elementsByTagName( QStringLiteral( "strokeColor" ) );
704  if ( !strokeColorList.isEmpty() )
705  {
706  QDomElement strokeColorElem = strokeColorList.at( 0 ).toElement();
707  bool redOk, greenOk, blueOk, alphaOk;
708  int strokeRed, strokeGreen, strokeBlue, strokeAlpha;
709 
710  strokeRed = strokeColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
711  strokeGreen = strokeColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
712  strokeBlue = strokeColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
713  strokeAlpha = strokeColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
714 
715  if ( redOk && greenOk && blueOk && alphaOk )
716  {
717  mSettings.setLineColor( QColor( strokeRed, strokeGreen, strokeBlue, strokeAlpha ) );
718  QPen p = mSettings.pen();
719  p.setColor( mSettings.lineColor() );
720  mSettings.setPen( p );
721  }
722  }
723  else
724  {
725  mSettings.setLineColor( QColor( itemElem.attribute( QStringLiteral( "penColor" ), QStringLiteral( "#000000" ) ) ) );
726  QPen p = mSettings.pen();
727  p.setColor( mSettings.lineColor() );
728  mSettings.setPen( p );
729  }
730 
731  //font color
732  QDomNodeList textColorList = itemElem.elementsByTagName( QStringLiteral( "textColor" ) );
733  if ( !textColorList.isEmpty() )
734  {
735  QDomElement textColorElem = textColorList.at( 0 ).toElement();
736  bool redOk, greenOk, blueOk, alphaOk;
737  int textRed, textGreen, textBlue, textAlpha;
738 
739  textRed = textColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
740  textGreen = textColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
741  textBlue = textColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
742  textAlpha = textColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
743 
744  if ( redOk && greenOk && blueOk && alphaOk )
745  {
746  mSettings.setFontColor( QColor( textRed, textGreen, textBlue, textAlpha ) );
747  }
748  }
749  else
750  {
751  QColor c;
752  c.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
753  mSettings.setFontColor( c );
754  }
755 
756  //style
757  QString styleString = itemElem.attribute( QStringLiteral( "style" ), QLatin1String( "" ) );
758  setStyle( styleString.toLocal8Bit().data() );
759 
760  if ( itemElem.attribute( QStringLiteral( "unitType" ) ).isEmpty() )
761  {
763  switch ( itemElem.attribute( QStringLiteral( "units" ) ).toInt() )
764  {
765  case 0:
767  break;
768  case 1:
770  break;
771  case 2:
773  break;
774  case 3:
776  break;
777  }
778  mSettings.setUnits( u );
779  }
780  else
781  {
782  mSettings.setUnits( QgsUnitTypes::decodeDistanceUnit( itemElem.attribute( QStringLiteral( "unitType" ) ) ) );
783  }
784  mSettings.setAlignment( static_cast< QgsScaleBarSettings::Alignment >( itemElem.attribute( QStringLiteral( "alignment" ), QStringLiteral( "0" ) ).toInt() ) );
785 
786  //map
787  disconnectCurrentMap();
788  mMap = nullptr;
789  mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
790  return true;
791 }
792 
793 
795 {
796  if ( mLayout && !mMapUuid.isEmpty() )
797  {
798  disconnectCurrentMap();
799  mMap = qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) );
800  if ( mMap )
801  {
802  connect( mMap, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemScaleBar::updateScale );
803  connect( mMap, &QObject::destroyed, this, &QgsLayoutItemScaleBar::disconnectCurrentMap );
804  }
805  }
806 
807  updateScale();
808 }
QgsUnitTypes::DistanceUnit guessUnits() const
Attempts to guess the most reasonable unit choice for the scalebar, given the current linked map&#39;s sc...
void setFontColor(const QColor &color)
Sets the color used for drawing text in the scalebar.
QgsUnitTypes::DistanceUnit lengthUnits() const
Returns the units of distance for length calculations made by this object.
The class is used as a container of context for various read/write operations on other objects...
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...
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setMinimumBarWidth(double width)
Sets the minimum width (in millimeters) for scale bar segments.
A rectangle specified with double values.
Definition: qgsrectangle.h:39
double mapUnitsPerScaleBarUnit() const
Returns the number of map units per scale bar unit used by the scalebar.
void refreshDataDefinedProperty(const QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
QBrush brush() const
Returns the primary brush used for filling the scalebar.
QString style() const
Returns the scale bar style name.
void setLineColor(const QColor &color)
Sets the color used for lines in the scalebar.
void setLineJoinStyle(Qt::PenJoinStyle style)
Sets the join style used when drawing the lines in the scalebar.
Base class for graphical items within a QgsLayout.
QgsLayoutSize minimumSize() const override
Returns the minimum allowed size of the item, if applicable, or an empty size if item can be freely r...
QString unitLabel() const
Returns the label for units.
QFont font() const
Returns the font used for drawing text in the scalebar.
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...
This class is a composition of two QSettings instances:
Definition: qgssettings.h:57
QPen pen() const
Returns the pen used for drawing outlines in the scalebar.
double segmentWidth
Width of each individual segment (in millimeters)
QColor fontColor() const
Returns the color used for drawing text in the scalebar.
Alignment alignment() const
Returns the scalebar alignment.
void setPen(const QPen &pen)
Sets the pen used for drawing outlines in the scalebar.
QColor fillColor2() const
Returns the secondary color used for fills in the scalebar.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map item linked to the scalebar.
A class to represent a 2D point.
Definition: qgspointxy.h:43
void extentChanged()
Is emitted when the map&#39;s extent changes.
SegmentSizeMode segmentSizeMode() const
Returns the size mode for the scale bar segments.
void setBoxContentSpace(double space)
Sets the space (margin) between the scalebar box and content in millimeters.
QgsLayoutSize sizeWithUnits() const
Returns the item&#39;s current size, including units.
void setLineCapStyle(Qt::PenCapStyle style)
Sets the cap style used when drawing the lines in the scalebar.
double lineWidth() const
Returns the line width in millimeters for lines in the scalebar.
Alignment
Scalebar alignment.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void update()
Adjusts the scale bar box size and updates the item.
void setAlignment(QgsScaleBarSettings::Alignment alignment)
Sets the scalebar alignment.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
QColor lineColor() const
Returns the color used for lines in the scalebar.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
QFont font() const
Returns the font used for drawing text in the scalebar.
void setSegmentSizeMode(QgsScaleBarSettings::SegmentSizeMode mode)
Sets the size mode for scale bar segments.
void setStyle(const QString &name)
Sets the scale bar style by name.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
QColor fillColor() const
Returns the color used for fills in the scalebar.
int type() const override
void refreshItemSize()
Refreshes an item&#39;s size by rechecking it against any possible item fixed or minimum sizes...
double height() const
Returns the scalebar height (in millimeters).
void setMaximumBarWidth(double maxWidth)
Sets the maximum width (in millimeters) for scale bar segments.
void setFillColor(const QColor &color)
Sets the color used for fills in the scalebar.
QgsRectangle extent() const
Returns the current map extent.
Layout graphical items for displaying a map.
static Q_INVOKABLE QString toAbbreviatedString(QgsUnitTypes::DistanceUnit unit)
Returns a translated abbreviation representing a distance unit.
int numberOfSegments() const
Returns the number of segments included in the scalebar.
Scalebar secondary fill color.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void setNumberOfSegments(int segments)
Sets the number of segments included in the scalebar.
double minimumBarWidth() const
Returns the minimum width (in millimeters) for scale bar segments.
void setLineCapStyle(Qt::PenCapStyle style)
Sets the cap style used when drawing the lines in the scalebar.
void resizeToMinimumWidth()
Resizes the scale bar to its minimum width, without changing the height.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
void setMapUnitsPerScaleBarUnit(double units)
Sets the number of map units per scale bar unit used by the scalebar.
QSizeF size
Destination size for scalebar.
void setAlignment(Alignment alignment)
Sets the scalebar alignment.
QgsUnitTypes::DistanceUnit units() const
Returns the distance units used by the scalebar.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:142
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgslayoutitem.h:71
double boxContentSpace() const
Returns the spacing (margin) between the scalebar box and content in millimeters. ...
void setMinimumBarWidth(double minWidth)
Sets the minimum width (in millimeters) for scale bar segments.
double labelBarSpace() const
Returns the spacing (in millimeters) between labels and the scalebar.
QPointer< QgsLayout > mLayout
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
void setMaximumBarWidth(double width)
Sets the maximum width (in millimeters) for scale bar segments.
Degrees, for planar geographic CRS distance measurements.
Definition: qgsunittypes.h:51
void setBoxContentSpace(double space)
Sets the space (margin) between the scalebar box and content in millimeters.
void setUnitsPerSegment(double units)
Sets the number of scalebar units per segment.
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.
void setFont(const QFont &font)
Sets the font used for drawing text in the scalebar.
void setSegmentSizeMode(SegmentSizeMode mode)
Sets the size mode for scale bar segments.
QgsUnitTypes::LayoutUnit units() const
Returns the units for the size.
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
double unitsPerSegment() const
Returns the number of scalebar units per segment.
void setFillColor2(const QColor &color)
Sets the secondary color used for fills in the scalebar.
void setUnitLabel(const QString &label)
Sets the label for units.
SegmentSizeMode
Modes for setting size for scale bar segments.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
Scale bar segment size is fixed to a map unit.
QIcon icon() const override
Returns the item&#39;s icon.
double maximumBarWidth() const
Returns the maximum width (in millimeters) for scale bar segments.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
void setFont(const QFont &font)
Sets the font used for drawing text in the scalebar.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:130
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:43
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:43
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:115
void setHeight(double height)
Sets the scalebar height (in millimeters).
Unknown distance unit.
Definition: qgsunittypes.h:54
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
QgsLayoutItemScaleBar(QgsLayout *layout)
Constructor for QgsLayoutItemScaleBar, with the specified parent layout.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item&#39;s contents using the specified item render context.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
void setUnits(QgsUnitTypes::DistanceUnit units)
Sets the distance units used by the scalebar.
void setLineJoinStyle(Qt::PenJoinStyle style)
Sets the join style used when drawing the lines in the scalebar.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
double scale() const
Returns the map scale.
void setLineWidth(double width)
Sets the line width in millimeters for lines in the scalebar.
virtual QString uuid() const
Returns the item identification string.
This class represents a coordinate reference system (CRS).
void setLabelBarSpace(double space)
Sets the spacing (in millimeters) between labels and the scalebar.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void applyDefaultSettings()
Applies the default scalebar settings to the scale bar.
void setBrush2(const QBrush &brush)
Sets the secondary brush used for filling the scalebar.
virtual void refreshDataDefinedProperty(const QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:120
A layout item subclass for scale bars.
void setNumberOfSegmentsLeft(int segments)
Sets the number of segments included in the left part of the scalebar.
double unitsPerSegment() const
Returns the number of scalebar units per segment.
Qt::PenCapStyle lineCapStyle() const
Returns the cap style used for drawing lines in the scalebar.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
void setNumberOfSegmentsLeft(int segments)
Sets the number of segments included in the left part of the scalebar.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
QBrush brush2() const
Returns the secondary brush for the scalebar.
static QString encodePenCapStyle(Qt::PenCapStyle style)
Terrestrial miles.
Definition: qgsunittypes.h:50
double prevNiceNumber(double a, double d=1)
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:40
Qt::PenJoinStyle lineJoinStyle() const
Returns the join style used for drawing lines in the scalebar.
static Q_INVOKABLE double fromUnitToUnitFactor(QgsUnitTypes::DistanceUnit fromUnit, QgsUnitTypes::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
int numberOfSegmentsLeft() const
Returns the number of segments included in the left part of the scalebar.
void setUnits(QgsUnitTypes::DistanceUnit units)
Sets the distance units used by the scalebar.
void changed()
Emitted when the object&#39;s properties change.
DataDefinedProperty
Data defined properties for different item types.
void setUnitsPerSegment(double units)
Sets the number of scalebar units per segment.
double nextNiceNumber(double a, double d=1)
void setBrush(const QBrush &brush)
Sets the primary brush used for filling the scalebar.
void setUnitLabel(const QString &label)
Sets the label for units.
double measureLine(const QVector< QgsPointXY > &points) const
Measures the length of a line with multiple segments.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void applyDefaultSize(QgsUnitTypes::DistanceUnit units=QgsUnitTypes::DistanceMeters)
Applies the default size to the scale bar (scale bar 1/5 of map item width)
void setWidth(const double width)
Sets the width for the size.
Definition: qgslayoutsize.h:83
void setNumberOfSegments(int segments)
Sets the number of segments included in the scalebar.
static QgsLayoutItemScaleBar * create(QgsLayout *layout)
Returns a new scale bar item for the specified layout.
QgsUnitTypes::DistanceUnit units() const
Returns the distance units used by the scalebar.
All properties for item.
Contains parameters regarding scalebar calculations.