QGIS API Documentation  3.27.0-Master (0e23467727)
qgsbasicnumericformat.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsbasicnumericformat.cpp
3  ----------------------------
4  begin : January 2020
5  copyright : (C) 2020 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 "qgsbasicnumericformat.h"
18 #include "qgis.h"
19 #include <memory>
20 #include <iostream>
21 #include <locale>
22 #include <iomanip>
23 
24 struct formatter : std::numpunct<wchar_t>
25 {
26  formatter( QChar thousands, bool showThousands, QChar decimal )
27  : mThousands( thousands.unicode() )
28  , mDecimal( decimal.unicode() )
29  , mShowThousands( showThousands )
30  {}
31  wchar_t do_decimal_point() const override { return mDecimal; }
32  wchar_t do_thousands_sep() const override { return mThousands; }
33  std::string do_grouping() const override { return mShowThousands ? "\3" : "\0"; }
34 
35  wchar_t mThousands;
36  wchar_t mDecimal;
37  bool mShowThousands = true;
38 };
39 
41 {
42 }
43 
45 {
46  return QStringLiteral( "basic" );
47 }
48 
50 {
51  return QObject::tr( "Number" );
52 }
53 
55 {
56  return 1;
57 }
58 
59 QString QgsBasicNumericFormat::formatDouble( double value, const QgsNumericFormatContext &context ) const
60 {
61  const QChar decimal = mDecimalSeparator.isNull() ? context.decimalSeparator() : mDecimalSeparator;
62  std::basic_stringstream<wchar_t> os;
63  os.imbue( std::locale( os.getloc(), new formatter( mThousandsSeparator.isNull() ? context.thousandsSeparator() : mThousandsSeparator,
64  mShowThousandsSeparator,
65  decimal ) ) );
66 
67  if ( !mUseScientific )
68  {
69  switch ( mRoundingType )
70  {
71  case DecimalPlaces:
72  os << std::fixed << std::setprecision( mNumberDecimalPlaces );
73  if ( qgsDoubleNear( value, 0 ) )
74  os << 0.0;
75  else
76  os << value;
77 
78  break;
79 
80  case SignificantFigures:
81  {
82  if ( qgsDoubleNear( value, 0 ) )
83  {
84  os << std::fixed << std::setprecision( mNumberDecimalPlaces - 1 ) << 0.0;
85  }
86  else
87  {
88  // digits before decimal point
89  const int d = std::floor( std::log10( value < 0 ? -value : value ) ) + 1;
90  const double order = std::pow( 10.0, mNumberDecimalPlaces - d );
91  os << std::fixed << std::setprecision( std::max( mNumberDecimalPlaces - d, 0 ) ) << std::round( value * order ) / order;
92  }
93  break;
94  }
95  }
96  }
97  else
98  {
99  os << std::scientific << std::setprecision( mNumberDecimalPlaces );
100  if ( qgsDoubleNear( value, 0 ) )
101  os << 0.0;
102  else
103  os << value;
104  }
105 
106  QString res = QString::fromStdWString( os.str() );
107 
108  if ( mShowPlusSign && value > 0 )
109  res.prepend( context.positiveSign() );
110 
111  if ( !mShowTrailingZeros && res.contains( decimal ) )
112  {
113  int trimPoint = res.length() - 1;
114  int ePoint = 0;
115  if ( mUseScientific )
116  {
117  while ( res.at( trimPoint ).toUpper() != context.exponential().toUpper() )
118  trimPoint--;
119  ePoint = trimPoint;
120  trimPoint--;
121  }
122 
123  while ( res.at( trimPoint ) == context.zeroDigit() )
124  trimPoint--;
125 
126  if ( res.at( trimPoint ) == decimal )
127  trimPoint--;
128 
129  const QString original = res;
130  res.truncate( trimPoint + 1 );
131  if ( mUseScientific )
132  res += original.mid( ePoint );
133  }
134 
135  return res;
136 }
137 
139 {
140  return new QgsBasicNumericFormat( *this );
141 }
142 
143 QgsNumericFormat *QgsBasicNumericFormat::create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const
144 {
145  std::unique_ptr< QgsBasicNumericFormat > res = std::make_unique< QgsBasicNumericFormat >();
146  res->setConfiguration( configuration, context );
147  return res.release();
148 }
149 
151 {
152  QVariantMap res;
153  res.insert( QStringLiteral( "decimals" ), mNumberDecimalPlaces );
154  res.insert( QStringLiteral( "show_thousand_separator" ), mShowThousandsSeparator );
155  res.insert( QStringLiteral( "show_plus" ), mShowPlusSign );
156  res.insert( QStringLiteral( "show_trailing_zeros" ), mShowTrailingZeros );
157  res.insert( QStringLiteral( "rounding_type" ), static_cast< int >( mRoundingType ) );
158  res.insert( QStringLiteral( "thousand_separator" ), mThousandsSeparator );
159  res.insert( QStringLiteral( "decimal_separator" ), mDecimalSeparator );
160  return res;
161 }
162 
163 void QgsBasicNumericFormat::setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext & )
164 {
165  mNumberDecimalPlaces = configuration.value( QStringLiteral( "decimals" ), 6 ).toInt();
166  mShowThousandsSeparator = configuration.value( QStringLiteral( "show_thousand_separator" ), true ).toBool();
167  mShowPlusSign = configuration.value( QStringLiteral( "show_plus" ), false ).toBool();
168  mShowTrailingZeros = configuration.value( QStringLiteral( "show_trailing_zeros" ), false ).toBool();
169  mRoundingType = static_cast< RoundingType >( configuration.value( QStringLiteral( "rounding_type" ), static_cast< int >( DecimalPlaces ) ).toInt() );
170  mThousandsSeparator = configuration.value( QStringLiteral( "thousand_separator" ), QChar() ).toChar();
171  mDecimalSeparator = configuration.value( QStringLiteral( "decimal_separator" ), QChar() ).toChar();
172 }
173 
175 {
176  return mNumberDecimalPlaces;
177 }
178 
179 void QgsBasicNumericFormat::setNumberDecimalPlaces( int numberDecimalPlaces )
180 {
181  mNumberDecimalPlaces = numberDecimalPlaces;
182 }
183 
185 {
186  return mShowThousandsSeparator;
187 }
188 
189 void QgsBasicNumericFormat::setShowThousandsSeparator( bool showThousandsSeparator )
190 {
191  mShowThousandsSeparator = showThousandsSeparator;
192 }
193 
195 {
196  return mShowPlusSign;
197 }
198 
199 void QgsBasicNumericFormat::setShowPlusSign( bool showPlusSign )
200 {
201  mShowPlusSign = showPlusSign;
202 }
203 
205 {
206  return mShowTrailingZeros;
207 }
208 
209 void QgsBasicNumericFormat::setShowTrailingZeros( bool showTrailingZeros )
210 {
211  mShowTrailingZeros = showTrailingZeros;
212 }
213 
215 {
216  return mRoundingType;
217 }
218 
220 {
221  mRoundingType = type;
222 }
223 
225 {
226  return mThousandsSeparator;
227 }
228 
230 {
231  mThousandsSeparator = character;
232 }
233 
235 {
236  return mDecimalSeparator;
237 }
238 
240 {
241  mDecimalSeparator = character;
242 }
QgsNumericFormat * create(const QVariantMap &configuration, const QgsReadWriteContext &context) const override
Creates a new copy of the format, using the supplied configuration.
QChar thousandsSeparator() const
Returns any override for the thousands separator character.
RoundingType
Sets rounding type and behavior of the numberDecimalPlaces() setting.
@ DecimalPlaces
Maximum number of decimal places.
@ SignificantFigures
Maximum number of significant figures.
void setThousandsSeparator(QChar character)
Sets an override character for the thousands separator character.
QgsBasicNumericFormat()
Default constructor.
void setShowTrailingZeros(bool show)
Sets whether trailing zeros will be shown (up to the specified numberDecimalPlaces()).
void setShowThousandsSeparator(bool show)
Sets whether the thousands grouping separator will be shown.
bool showThousandsSeparator() const
Returns true if the thousands grouping separator will be shown.
QString id() const override
Returns a unique id for this numeric format.
virtual void setConfiguration(const QVariantMap &configuration, const QgsReadWriteContext &context)
Sets the format's configuration.
RoundingType roundingType() const
Returns the rounding type, which controls the behavior of the numberDecimalPlaces() setting.
QString visibleName() const override
Returns the translated, user-visible name for this format.
int sortKey() override
Returns a sorting key value, where formats with a lower sort key will be shown earlier in lists.
bool showTrailingZeros() const
Returns true if trailing zeros will be shown (up to the specified numberDecimalPlaces()).
void setRoundingType(RoundingType type)
Sets the rounding type, which controls the behavior of the numberDecimalPlaces() setting.
QgsNumericFormat * clone() const override
Clones the format, returning a new object.
bool showPlusSign() const
Returns true if a leading plus sign will be shown for positive values.
QChar decimalSeparator() const
Returns any override for the decimal separator character.
void setShowPlusSign(bool show)
Sets whether a leading plus sign will be shown for positive values.
QString formatDouble(double value, const QgsNumericFormatContext &context) const override
Returns a formatted string representation of a numeric double value.
virtual void setNumberDecimalPlaces(int places)
Sets the maximum number of decimal places to show.
int numberDecimalPlaces() const
Returns the maximum number of decimal places to show.
QVariantMap configuration(const QgsReadWriteContext &context) const override
Returns the current configuration of the formatter.
void setDecimalSeparator(QChar character)
Sets an override character for the decimal separator character.
A context for numeric formats.
QChar thousandsSeparator() const
Returns the thousands separator character.
QChar zeroDigit() const
Returns the zero digit character.
QChar exponential() const
Returns the exponential character.
QChar decimalSeparator() const
Returns the decimal separator character.
QChar positiveSign() const
Returns the positive sign character.
A numeric formatter allows for formatting a numeric value for display, using a variety of different f...
The class is used as a container of context for various read/write operations on other objects.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2260
wchar_t do_decimal_point() const override
std::string do_grouping() const override
wchar_t do_thousands_sep() const override
formatter(QChar thousands, bool showThousands, QChar decimal)