QGIS API Documentation  3.2.0-Bonn (bc43194)
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  return mSettings.textFormat();
140 }
141 
143 {
144  mSettings.setTextFormat( format );
145  refreshItemSize();
146  emit changed();
147 }
148 
150 {
151  if ( !mStyle )
152  {
153  mSettings.setNumberOfSegmentsLeft( nSegmentsLeft );
154  return;
155  }
156  mSettings.setNumberOfSegmentsLeft( nSegmentsLeft );
158 }
159 
161 {
162  if ( !mStyle )
163  {
164  mSettings.setBoxContentSpace( space );
165  return;
166  }
167  mSettings.setBoxContentSpace( space );
168  refreshItemSize();
169 }
170 
172 {
173  disconnectCurrentMap();
174 
175  mMap = map;
176 
177  if ( !map )
178  {
179  return;
180  }
181 
182  connect( mMap, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemScaleBar::updateScale );
183  connect( mMap, &QObject::destroyed, this, &QgsLayoutItemScaleBar::disconnectCurrentMap );
184 
185  refreshSegmentMillimeters();
186  emit changed();
187 }
188 
189 void QgsLayoutItemScaleBar::disconnectCurrentMap()
190 {
191  if ( !mMap )
192  {
193  return;
194  }
195 
196  disconnect( mMap, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemScaleBar::updateScale );
197  disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemScaleBar::disconnectCurrentMap );
198  mMap = nullptr;
199 }
200 
202 {
204 
205  bool forceUpdate = false;
206  //updates data defined properties and redraws item to match
208  {
209  QBrush b = mSettings.brush();
211  mSettings.setBrush( b );
212  forceUpdate = true;
213  }
215  {
216  QBrush b = mSettings.brush2();
218  mSettings.setBrush2( b );
219  forceUpdate = true;
220  }
222  {
223  QPen p = mSettings.pen();
225  mSettings.setPen( p );
226  forceUpdate = true;
227  }
229  {
230  QPen p = mSettings.pen();
232  mSettings.setPen( p );
233  forceUpdate = true;
234  }
235  if ( forceUpdate )
236  {
237  refreshItemSize();
238  update();
239  }
240 
242 }
243 
244 // nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc
245 inline double nextNiceNumber( double a, double d = 1 )
246 {
247  double s = std::pow( 10.0, std::floor( std::log10( a ) ) ) / d;
248  return std::ceil( a / s ) * s;
249 }
250 
251 // prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc
252 inline double prevNiceNumber( double a, double d = 1 )
253 {
254  double s = std::pow( 10.0, std::floor( std::log10( a ) ) ) / d;
255  return std::floor( a / s ) * s;
256 }
257 
258 void QgsLayoutItemScaleBar::refreshSegmentMillimeters()
259 {
260  if ( mMap )
261  {
262  //get mm dimension of composer map
263  QRectF composerItemRect = mMap->rect();
264 
266  {
267  //calculate size depending on mNumUnitsPerSegment
268  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mSettings.unitsPerSegment();
269  }
270  else /*if(mSegmentSizeMode == SegmentSizeFitWidth)*/
271  {
272  if ( mSettings.maximumBarWidth() < mSettings.minimumBarWidth() )
273  {
274  mSegmentMillimeters = 0;
275  }
276  else
277  {
278  double nSegments = ( mSettings.numberOfSegmentsLeft() != 0 ) + mSettings.numberOfSegments();
279  // unitsPerSegments which fit minBarWidth resp. maxBarWidth
280  double minUnitsPerSeg = ( mSettings.minimumBarWidth() * mapWidth() ) / ( nSegments * composerItemRect.width() );
281  double maxUnitsPerSeg = ( mSettings.maximumBarWidth() * mapWidth() ) / ( nSegments * composerItemRect.width() );
282 
283  // Start with coarsest "nice" number closest to minUnitsPerSeg resp
284  // maxUnitsPerSeg, then proceed to finer numbers as long as neither
285  // lowerNiceUnitsPerSeg nor upperNiceUnitsPerSeg are in
286  // [minUnitsPerSeg, maxUnitsPerSeg]
287  double lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg );
288  double upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg );
289 
290  double d = 1;
291  while ( lowerNiceUnitsPerSeg > maxUnitsPerSeg && upperNiceUnitsPerSeg < minUnitsPerSeg )
292  {
293  d *= 10;
294  lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg, d );
295  upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg, d );
296  }
297 
298  // Pick mNumUnitsPerSegment from {lowerNiceUnitsPerSeg, upperNiceUnitsPerSeg}, use the larger if possible
299  mSettings.setUnitsPerSegment( upperNiceUnitsPerSeg < minUnitsPerSeg ? lowerNiceUnitsPerSeg : upperNiceUnitsPerSeg );
300  mSegmentMillimeters = composerItemRect.width() / mapWidth() * mSettings.unitsPerSegment();
301  }
302  }
303  }
304 }
305 
306 double QgsLayoutItemScaleBar::mapWidth() const
307 {
308  if ( !mMap )
309  {
310  return 0.0;
311  }
312 
313  QgsRectangle mapExtent = mMap->extent();
314  if ( mSettings.units() == QgsUnitTypes::DistanceUnknownUnit )
315  {
316  return mapExtent.width();
317  }
318  else
319  {
320  QgsDistanceArea da;
321  da.setSourceCrs( mMap->crs(), mLayout->project()->transformContext() );
322  da.setEllipsoid( mLayout->project()->ellipsoid() );
323 
325  double measure = da.measureLine( QgsPointXY( mapExtent.xMinimum(), mapExtent.yMinimum() ),
326  QgsPointXY( mapExtent.xMaximum(), mapExtent.yMinimum() ) );
327  measure /= QgsUnitTypes::fromUnitToUnitFactor( mSettings.units(), units );
328  return measure;
329  }
330 }
331 
332 QgsScaleBarRenderer::ScaleBarContext QgsLayoutItemScaleBar::createScaleContext() const
333 {
335  scaleContext.size = rect().size();
336  scaleContext.segmentWidth = mSegmentMillimeters;
337  scaleContext.scale = mMap ? mMap->scale() : 1.0;
338  return scaleContext;
339 }
340 
342 {
343  mSettings.setAlignment( a );
344  refreshItemSize();
345  emit changed();
346 }
347 
349 {
350  mSettings.setUnits( u );
351  refreshSegmentMillimeters();
352  refreshItemSize();
353  emit changed();
354 }
355 
357 {
358  if ( mSettings.lineJoinStyle() == style )
359  {
360  //no change
361  return;
362  }
363  mSettings.setLineJoinStyle( style );
364  update();
365  emit changed();
366 }
367 
369 {
370  if ( mSettings.lineCapStyle() == style )
371  {
372  //no change
373  return;
374  }
375  mSettings.setLineCapStyle( style );
376  update();
377  emit changed();
378 }
379 
381 {
382  //style
383  mStyle = qgis::make_unique< QgsSingleBoxScaleBarRenderer >();
384 
385  //default to no background
386  setBackgroundEnabled( false );
387 
388  //get default composer font from settings
389  QgsSettings settings;
390  QString defaultFontString = settings.value( QStringLiteral( "LayoutDesigner/defaultFont" ), QVariant(), QgsSettings::Gui ).toString();
391  QgsTextFormat format;
392  QFont f;
393  if ( !defaultFontString.isEmpty() )
394  {
395  f.setFamily( defaultFontString );
396  }
397  format.setFont( f );
398  format.setSize( 12.0 );
400 
401  mSettings.setTextFormat( format );
402 
404  refreshItemSize();
405 
406  emit changed();
407 }
408 
410 {
411  if ( !mMap )
413 
414  QgsCoordinateReferenceSystem crs = mMap->crs();
415  // start with crs units
418  {
419  // geographic CRS, use metric units
421  }
422 
423  // try to pick reasonable choice between metric / imperial units
424  double widthInSelectedUnits = mapWidth();
425  double initialUnitsPerSegment = widthInSelectedUnits / 10.0; //default scalebar width equals half the map width
426  switch ( unit )
427  {
429  {
430  if ( initialUnitsPerSegment > 1000.0 )
431  {
433  }
434  break;
435  }
437  {
438  if ( initialUnitsPerSegment > 5419.95 )
439  {
441  }
442  break;
443  }
444  default:
445  break;
446  }
447 
448  return unit;
449 }
450 
452 {
453  mSettings.setUnits( units );
454  if ( mMap )
455  {
456  double upperMagnitudeMultiplier = 1.0;
457  double widthInSelectedUnits = mapWidth();
458  double initialUnitsPerSegment = widthInSelectedUnits / 10.0; //default scalebar width equals half the map width
459  mSettings.setUnitsPerSegment( initialUnitsPerSegment );
460 
462  upperMagnitudeMultiplier = 1;
463 
464  double segmentWidth = initialUnitsPerSegment / upperMagnitudeMultiplier;
465  int segmentMagnitude = std::floor( std::log10( segmentWidth ) );
466  double unitsPerSegment = upperMagnitudeMultiplier * ( std::pow( 10.0, segmentMagnitude ) );
467  double multiplier = std::floor( ( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5;
468 
469  if ( multiplier > 0 )
470  {
471  unitsPerSegment = unitsPerSegment * multiplier;
472  }
473  mSettings.setUnitsPerSegment( unitsPerSegment );
474  mSettings.setMapUnitsPerScaleBarUnit( upperMagnitudeMultiplier );
475 
476  mSettings.setNumberOfSegments( 4 );
477  mSettings.setNumberOfSegmentsLeft( 2 );
478  }
479 
480  refreshSegmentMillimeters();
482  emit changed();
483 }
484 
486 {
487  if ( !mStyle )
488  return;
489 
490  double widthMM = mStyle->calculateBoxSize( mSettings, createScaleContext() ).width();
491  QgsLayoutSize currentSize = sizeWithUnits();
492  currentSize.setWidth( mLayout->renderContext().measurementConverter().convert( QgsLayoutMeasurement( widthMM, QgsUnitTypes::LayoutMillimeters ), currentSize.units() ).length() );
493  attemptResize( currentSize );
494  update();
495  emit changed();
496 }
497 
499 {
500  //Don't adjust box size for numeric scale bars:
501  if ( mStyle && mStyle->name() != QLatin1String( "Numeric" ) )
502  {
503  refreshItemSize();
504  }
505  QgsLayoutItem::update();
506 }
507 
508 void QgsLayoutItemScaleBar::updateScale()
509 {
510  refreshSegmentMillimeters();
512  update();
513 }
514 
515 void QgsLayoutItemScaleBar::setStyle( const QString &styleName )
516 {
517  //switch depending on style name
518  if ( styleName == QLatin1String( "Single Box" ) )
519  {
520  mStyle = qgis::make_unique< QgsSingleBoxScaleBarRenderer >();
521  }
522  else if ( styleName == QLatin1String( "Double Box" ) )
523  {
524  mStyle = qgis::make_unique< QgsDoubleBoxScaleBarRenderer >();
525  }
526  else if ( styleName == QLatin1String( "Line Ticks Middle" ) || styleName == QLatin1String( "Line Ticks Down" ) || styleName == QLatin1String( "Line Ticks Up" ) )
527  {
528  std::unique_ptr< QgsTicksScaleBarRenderer > tickStyle = qgis::make_unique< QgsTicksScaleBarRenderer >();
529  if ( styleName == QLatin1String( "Line Ticks Middle" ) )
530  {
531  tickStyle->setTickPosition( QgsTicksScaleBarRenderer::TicksMiddle );
532  }
533  else if ( styleName == QLatin1String( "Line Ticks Down" ) )
534  {
535  tickStyle->setTickPosition( QgsTicksScaleBarRenderer::TicksDown );
536  }
537  else if ( styleName == QLatin1String( "Line Ticks Up" ) )
538  {
539  tickStyle->setTickPosition( QgsTicksScaleBarRenderer::TicksUp );
540  }
541  mStyle = std::move( tickStyle );
542  }
543  else if ( styleName == QLatin1String( "Numeric" ) )
544  {
545  mStyle = qgis::make_unique< QgsNumericScaleBarRenderer >();
546  }
547  refreshItemSize();
548  emit changed();
549 }
550 
552 {
553  if ( mStyle )
554  {
555  return mStyle->name();
556  }
557  else
558  {
559  return QString();
560  }
561 }
562 
564 {
565  return mSettings.textFormat().font();
566 }
567 
569 {
571  mSettings.setFont( font );
573  refreshItemSize();
574  emit changed();
575 }
576 
578 {
579  QColor color = mSettings.textFormat().color();
580  color.setAlphaF( mSettings.textFormat().opacity() );
581  return color;
582 }
583 
584 void QgsLayoutItemScaleBar::setFontColor( const QColor &color )
585 {
586  mSettings.textFormat().setColor( color );
587  mSettings.textFormat().setOpacity( color.alphaF() );
588 }
589 
590 bool QgsLayoutItemScaleBar::writePropertiesToElement( QDomElement &composerScaleBarElem, QDomDocument &doc, const QgsReadWriteContext &rwContext ) const
591 {
592  composerScaleBarElem.setAttribute( QStringLiteral( "height" ), QString::number( mSettings.height() ) );
593  composerScaleBarElem.setAttribute( QStringLiteral( "labelBarSpace" ), QString::number( mSettings.labelBarSpace() ) );
594  composerScaleBarElem.setAttribute( QStringLiteral( "boxContentSpace" ), QString::number( mSettings.boxContentSpace() ) );
595  composerScaleBarElem.setAttribute( QStringLiteral( "numSegments" ), mSettings.numberOfSegments() );
596  composerScaleBarElem.setAttribute( QStringLiteral( "numSegmentsLeft" ), mSettings.numberOfSegmentsLeft() );
597  composerScaleBarElem.setAttribute( QStringLiteral( "numUnitsPerSegment" ), QString::number( mSettings.unitsPerSegment() ) );
598  composerScaleBarElem.setAttribute( QStringLiteral( "segmentSizeMode" ), mSettings.segmentSizeMode() );
599  composerScaleBarElem.setAttribute( QStringLiteral( "minBarWidth" ), mSettings.minimumBarWidth() );
600  composerScaleBarElem.setAttribute( QStringLiteral( "maxBarWidth" ), mSettings.maximumBarWidth() );
601  composerScaleBarElem.setAttribute( QStringLiteral( "segmentMillimeters" ), QString::number( mSegmentMillimeters ) );
602  composerScaleBarElem.setAttribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QString::number( mSettings.mapUnitsPerScaleBarUnit() ) );
603 
604  QDomElement textElem = mSettings.textFormat().writeXml( doc, rwContext );
605  composerScaleBarElem.appendChild( textElem );
606 
607  composerScaleBarElem.setAttribute( QStringLiteral( "outlineWidth" ), QString::number( mSettings.lineWidth() ) );
608  composerScaleBarElem.setAttribute( QStringLiteral( "unitLabel" ), mSettings.unitLabel() );
609  composerScaleBarElem.setAttribute( QStringLiteral( "unitType" ), QgsUnitTypes::encodeUnit( mSettings.units() ) );
610  composerScaleBarElem.setAttribute( QStringLiteral( "lineJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mSettings.lineJoinStyle() ) );
611  composerScaleBarElem.setAttribute( QStringLiteral( "lineCapStyle" ), QgsSymbolLayerUtils::encodePenCapStyle( mSettings.lineCapStyle() ) );
612 
613  //style
614  if ( mStyle )
615  {
616  composerScaleBarElem.setAttribute( QStringLiteral( "style" ), mStyle->name() );
617  }
618 
619  //map id
620  if ( mMap )
621  {
622  composerScaleBarElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
623  }
624 
625  //colors
626 
627  //fill color
628  QDomElement fillColorElem = doc.createElement( QStringLiteral( "fillColor" ) );
629  fillColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mSettings.fillColor().red() ) );
630  fillColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mSettings.fillColor().green() ) );
631  fillColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mSettings.fillColor().blue() ) );
632  fillColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mSettings.fillColor().alpha() ) );
633  composerScaleBarElem.appendChild( fillColorElem );
634 
635  //fill color 2
636  QDomElement fillColor2Elem = doc.createElement( QStringLiteral( "fillColor2" ) );
637  fillColor2Elem.setAttribute( QStringLiteral( "red" ), QString::number( mSettings.fillColor2().red() ) );
638  fillColor2Elem.setAttribute( QStringLiteral( "green" ), QString::number( mSettings.fillColor2().green() ) );
639  fillColor2Elem.setAttribute( QStringLiteral( "blue" ), QString::number( mSettings.fillColor2().blue() ) );
640  fillColor2Elem.setAttribute( QStringLiteral( "alpha" ), QString::number( mSettings.fillColor2().alpha() ) );
641  composerScaleBarElem.appendChild( fillColor2Elem );
642 
643  //pen color
644  QDomElement strokeColorElem = doc.createElement( QStringLiteral( "strokeColor" ) );
645  strokeColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mSettings.lineColor().red() ) );
646  strokeColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mSettings.lineColor().green() ) );
647  strokeColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mSettings.lineColor().blue() ) );
648  strokeColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mSettings.lineColor().alpha() ) );
649  composerScaleBarElem.appendChild( strokeColorElem );
650 
651  //alignment
652  composerScaleBarElem.setAttribute( QStringLiteral( "alignment" ), QString::number( static_cast< int >( mSettings.alignment() ) ) );
653 
654  return true;
655 }
656 
657 bool QgsLayoutItemScaleBar::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext &context )
658 {
659  mSettings.setHeight( itemElem.attribute( QStringLiteral( "height" ), QStringLiteral( "5.0" ) ).toDouble() );
660  mSettings.setLabelBarSpace( itemElem.attribute( QStringLiteral( "labelBarSpace" ), QStringLiteral( "3.0" ) ).toDouble() );
661  mSettings.setBoxContentSpace( itemElem.attribute( QStringLiteral( "boxContentSpace" ), QStringLiteral( "1.0" ) ).toDouble() );
662  mSettings.setNumberOfSegments( itemElem.attribute( QStringLiteral( "numSegments" ), QStringLiteral( "2" ) ).toInt() );
663  mSettings.setNumberOfSegmentsLeft( itemElem.attribute( QStringLiteral( "numSegmentsLeft" ), QStringLiteral( "0" ) ).toInt() );
664  mSettings.setUnitsPerSegment( itemElem.attribute( QStringLiteral( "numUnitsPerSegment" ), QStringLiteral( "1.0" ) ).toDouble() );
665  mSettings.setSegmentSizeMode( static_cast<QgsScaleBarSettings::SegmentSizeMode>( itemElem.attribute( QStringLiteral( "segmentSizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
666  mSettings.setMinimumBarWidth( itemElem.attribute( QStringLiteral( "minBarWidth" ), QStringLiteral( "50" ) ).toDouble() );
667  mSettings.setMaximumBarWidth( itemElem.attribute( QStringLiteral( "maxBarWidth" ), QStringLiteral( "150" ) ).toDouble() );
668  mSegmentMillimeters = itemElem.attribute( QStringLiteral( "segmentMillimeters" ), QStringLiteral( "0.0" ) ).toDouble();
669  mSettings.setMapUnitsPerScaleBarUnit( itemElem.attribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QStringLiteral( "1.0" ) ).toDouble() );
670  mSettings.setLineWidth( itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "0.3" ) ).toDouble() );
671  mSettings.setUnitLabel( itemElem.attribute( QStringLiteral( "unitLabel" ) ) );
672  mSettings.setLineJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( itemElem.attribute( QStringLiteral( "lineJoinStyle" ), QStringLiteral( "miter" ) ) ) );
673  mSettings.setLineCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( itemElem.attribute( QStringLiteral( "lineCapStyle" ), QStringLiteral( "square" ) ) ) );
674 
675  QDomNodeList textFormatNodeList = itemElem.elementsByTagName( QStringLiteral( "text-style" ) );
676  if ( !textFormatNodeList.isEmpty() )
677  {
678  QDomElement textFormatElem = textFormatNodeList.at( 0 ).toElement();
679  mSettings.textFormat().readXml( textFormatElem, context );
680  }
681  else
682  {
683  QFont f;
684  if ( !QgsFontUtils::setFromXmlChildNode( f, itemElem, QStringLiteral( "scaleBarFont" ) ) )
685  {
686  f.fromString( itemElem.attribute( QStringLiteral( "font" ), QLatin1String( "" ) ) );
687  }
688  mSettings.textFormat().setFont( f );
689  if ( f.pointSizeF() > 0 )
690  {
691  mSettings.textFormat().setSize( f.pointSizeF() );
693  }
694  else if ( f.pixelSize() > 0 )
695  {
696  mSettings.textFormat().setSize( f.pixelSize() );
698  }
699  }
700 
701  //colors
702  //fill color
703  QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "fillColor" ) );
704  if ( !fillColorList.isEmpty() )
705  {
706  QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
707  bool redOk, greenOk, blueOk, alphaOk;
708  int fillRed, fillGreen, fillBlue, fillAlpha;
709 
710  fillRed = fillColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
711  fillGreen = fillColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
712  fillBlue = fillColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
713  fillAlpha = fillColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
714 
715  if ( redOk && greenOk && blueOk && alphaOk )
716  {
717  mSettings.setFillColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) );
718  }
719  }
720  else
721  {
722  mSettings.setFillColor( QColor( itemElem.attribute( QStringLiteral( "brushColor" ), QStringLiteral( "#000000" ) ) ) );
723  }
724 
725  //fill color 2
726  QDomNodeList fillColor2List = itemElem.elementsByTagName( QStringLiteral( "fillColor2" ) );
727  if ( !fillColor2List.isEmpty() )
728  {
729  QDomElement fillColor2Elem = fillColor2List.at( 0 ).toElement();
730  bool redOk, greenOk, blueOk, alphaOk;
731  int fillRed, fillGreen, fillBlue, fillAlpha;
732 
733  fillRed = fillColor2Elem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
734  fillGreen = fillColor2Elem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
735  fillBlue = fillColor2Elem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
736  fillAlpha = fillColor2Elem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
737 
738  if ( redOk && greenOk && blueOk && alphaOk )
739  {
740  mSettings.setFillColor2( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) );
741  }
742  }
743  else
744  {
745  mSettings.setFillColor2( QColor( itemElem.attribute( QStringLiteral( "brush2Color" ), QStringLiteral( "#ffffff" ) ) ) );
746  }
747 
748  //stroke color
749  QDomNodeList strokeColorList = itemElem.elementsByTagName( QStringLiteral( "strokeColor" ) );
750  if ( !strokeColorList.isEmpty() )
751  {
752  QDomElement strokeColorElem = strokeColorList.at( 0 ).toElement();
753  bool redOk, greenOk, blueOk, alphaOk;
754  int strokeRed, strokeGreen, strokeBlue, strokeAlpha;
755 
756  strokeRed = strokeColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
757  strokeGreen = strokeColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
758  strokeBlue = strokeColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
759  strokeAlpha = strokeColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
760 
761  if ( redOk && greenOk && blueOk && alphaOk )
762  {
763  mSettings.setLineColor( QColor( strokeRed, strokeGreen, strokeBlue, strokeAlpha ) );
764  QPen p = mSettings.pen();
765  p.setColor( mSettings.lineColor() );
766  mSettings.setPen( p );
767  }
768  }
769  else
770  {
771  mSettings.setLineColor( QColor( itemElem.attribute( QStringLiteral( "penColor" ), QStringLiteral( "#000000" ) ) ) );
772  QPen p = mSettings.pen();
773  p.setColor( mSettings.lineColor() );
774  mSettings.setPen( p );
775  }
776 
777  //font color
778  QDomNodeList textColorList = itemElem.elementsByTagName( QStringLiteral( "textColor" ) );
779  if ( !textColorList.isEmpty() )
780  {
781  QDomElement textColorElem = textColorList.at( 0 ).toElement();
782  bool redOk, greenOk, blueOk, alphaOk;
783  int textRed, textGreen, textBlue, textAlpha;
784 
785  textRed = textColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
786  textGreen = textColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
787  textBlue = textColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
788  textAlpha = textColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
789 
790  if ( redOk && greenOk && blueOk && alphaOk )
791  {
792  mSettings.textFormat().setColor( QColor( textRed, textGreen, textBlue, textAlpha ) );
793  }
794  }
795  else if ( itemElem.hasAttribute( QStringLiteral( "fontColor" ) ) )
796  {
797  QColor c;
798  c.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
799  mSettings.textFormat().setColor( c );
800  }
801 
802  //style
803  QString styleString = itemElem.attribute( QStringLiteral( "style" ), QLatin1String( "" ) );
804  setStyle( styleString.toLocal8Bit().data() );
805 
806  if ( itemElem.attribute( QStringLiteral( "unitType" ) ).isEmpty() )
807  {
809  switch ( itemElem.attribute( QStringLiteral( "units" ) ).toInt() )
810  {
811  case 0:
813  break;
814  case 1:
816  break;
817  case 2:
819  break;
820  case 3:
822  break;
823  }
824  mSettings.setUnits( u );
825  }
826  else
827  {
828  mSettings.setUnits( QgsUnitTypes::decodeDistanceUnit( itemElem.attribute( QStringLiteral( "unitType" ) ) ) );
829  }
830  mSettings.setAlignment( static_cast< QgsScaleBarSettings::Alignment >( itemElem.attribute( QStringLiteral( "alignment" ), QStringLiteral( "0" ) ).toInt() ) );
831 
832  //map
833  disconnectCurrentMap();
834  mMap = nullptr;
835  mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
836  return true;
837 }
838 
839 
841 {
842  if ( mLayout && !mMapUuid.isEmpty() )
843  {
844  disconnectCurrentMap();
845  mMap = qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) );
846  if ( mMap )
847  {
848  connect( mMap, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemScaleBar::updateScale );
849  connect( mMap, &QObject::destroyed, this, &QgsLayoutItemScaleBar::disconnectCurrentMap );
850  }
851  }
852 
853  updateScale();
854 }
QgsUnitTypes::DistanceUnit guessUnits() const
Attempts to guess the most reasonable unit choice for the scalebar, given the current linked map&#39;s sc...
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:40
double mapUnitsPerScaleBarUnit() const
Returns the number of map units per scale bar unit used by the scalebar.
QBrush brush() const
Returns the primary brush used for filling the scalebar.
QString style() const
Returns the scale bar style name.
void setOpacity(double opacity)
Sets the text&#39;s opacity.
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.
double opacity() const
Returns the text&#39;s opacity.
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...
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QString unitLabel() const
Returns the label for units.
Q_DECL_DEPRECATED 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...
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QPen pen() const
Returns the pen used for drawing outlines in the scalebar.
double segmentWidth
The width, in millimeters, of each individual segment drawn.
Alignment alignment() const
Returns the scalebar alignment.
void setPen(const QPen &pen)
Sets the pen used for drawing outlines in the scalebar.
Q_DECL_DEPRECATED void setFontColor(const QColor &color)
Sets the color used for drawing text in the scalebar.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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.
void setFont(const QFont &font)
Sets the font used for rendering text.
double lineWidth() const
Returns the line width in millimeters for lines in the scalebar.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:538
QColor color() const
Returns the color that text will be rendered in.
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.
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.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
QColor fillColor() const
Returns the color used for fills in the scalebar.
int type() const override
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
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.
void setSize(double size)
Sets the size for rendered text.
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.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
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:201
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 setColor(const QColor &color)
Sets the color that text will be rendered in.
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 setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
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 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.
QgsTextFormat & textFormat()
Returns the text format used for drawing text in the scalebar.
Q_DECL_DEPRECATED 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:176
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:161
void setHeight(double height)
Sets the scalebar height (in millimeters).
void setTextFormat(const QgsTextFormat &format)
Sets the text format used for drawing text in the scalebar.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:539
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.
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.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
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.
Q_DECL_DEPRECATED QColor fontColor() const
Returns the color used for drawing text in the scalebar.
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.
QBrush brush2() const
Returns the secondary brush for the scalebar.
void setTextFormat(const QgsTextFormat &format)
Sets the text format used for drawing text in the scalebar.
static QString encodePenCapStyle(Qt::PenCapStyle style)
Terrestrial miles.
Definition: qgsunittypes.h:50
Container for all settings relating to text rendering.
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.
QgsTextFormat textFormat() const
Returns the text format used for drawing text in the scalebar.
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.
QFont font() const
Returns the font used for rendering text.
Q_DECL_DEPRECATED void setFont(const QFont &font)
Sets the font used for drawing text in 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.