QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 }
QgsBasicNumericFormat::clone
QgsNumericFormat * clone() const override
Clones the format, returning a new object.
Definition: qgsbasicnumericformat.cpp:138
formatter
Definition: qgsbasicnumericformat.cpp:24
QgsBasicNumericFormat::mUseScientific
bool mUseScientific
Definition: qgsbasicnumericformat.h:181
formatter::do_thousands_sep
wchar_t do_thousands_sep() const override
Definition: qgsbasicnumericformat.cpp:46
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
QgsBasicNumericFormat::numberDecimalPlaces
int numberDecimalPlaces() const
Returns the maximum number of decimal places to show.
Definition: qgsbasicnumericformat.cpp:174
QgsBasicNumericFormat::QgsBasicNumericFormat
QgsBasicNumericFormat()
Default constructor.
Definition: qgsbasicnumericformat.cpp:40
QgsBasicNumericFormat::id
QString id() const override
Returns a unique id for this numeric format.
Definition: qgsbasicnumericformat.cpp:44
QgsBasicNumericFormat::setNumberDecimalPlaces
virtual void setNumberDecimalPlaces(int places)
Sets the maximum number of decimal places to show.
Definition: qgsbasicnumericformat.cpp:179
qgis.h
QgsNumericFormat
A numeric formatter allows for formatting a numeric value for display, using a variety of different f...
Definition: qgsnumericformat.h:259
formatter::mShowThousands
bool mShowThousands
Definition: qgsbasicnumericformat.cpp:51
QgsBasicNumericFormat::RoundingType
RoundingType
Sets rounding type and behavior of the numberDecimalPlaces() setting.
Definition: qgsbasicnumericformat.h:51
QgsBasicNumericFormat::showPlusSign
bool showPlusSign() const
Returns true if a leading plus sign will be shown for positive values.
Definition: qgsbasicnumericformat.cpp:194
QgsBasicNumericFormat::setThousandsSeparator
void setThousandsSeparator(QChar character)
Sets an override character for the thousands separator character.
Definition: qgsbasicnumericformat.cpp:229
QgsNumericFormatContext::exponential
QChar exponential() const
Returns the exponential character.
Definition: qgsnumericformat.h:173
QgsBasicNumericFormat::create
QgsNumericFormat * create(const QVariantMap &configuration, const QgsReadWriteContext &context) const override
Creates a new copy of the format, using the supplied configuration.
Definition: qgsbasicnumericformat.cpp:143
QgsBasicNumericFormat::setDecimalSeparator
void setDecimalSeparator(QChar character)
Sets an override character for the decimal separator character.
Definition: qgsbasicnumericformat.cpp:239
QgsBasicNumericFormat::decimalSeparator
QChar decimalSeparator() const
Returns any override for the decimal separator character.
Definition: qgsbasicnumericformat.cpp:234
QgsBasicNumericFormat::roundingType
RoundingType roundingType() const
Returns the rounding type, which controls the behavior of the numberDecimalPlaces() setting.
Definition: qgsbasicnumericformat.cpp:214
QgsBasicNumericFormat::setShowThousandsSeparator
void setShowThousandsSeparator(bool show)
Sets whether the thousands grouping separator will be shown.
Definition: qgsbasicnumericformat.cpp:189
QgsBasicNumericFormat::setShowPlusSign
void setShowPlusSign(bool show)
Sets whether a leading plus sign will be shown for positive values.
Definition: qgsbasicnumericformat.cpp:199
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
QgsNumericFormatContext::positiveSign
QChar positiveSign() const
Returns the positive sign character.
Definition: qgsnumericformat.h:153
QgsBasicNumericFormat::sortKey
int sortKey() override
Returns a sorting key value, where formats with a lower sort key will be shown earlier in lists.
Definition: qgsbasicnumericformat.cpp:54
formatter::do_decimal_point
wchar_t do_decimal_point() const override
Definition: qgsbasicnumericformat.cpp:45
QgsBasicNumericFormat::setConfiguration
virtual void setConfiguration(const QVariantMap &configuration, const QgsReadWriteContext &context)
Sets the format's configuration.
Definition: qgsbasicnumericformat.cpp:163
formatter::mThousands
wchar_t mThousands
Definition: qgsbasicnumericformat.cpp:49
QgsBasicNumericFormat::SignificantFigures
@ SignificantFigures
Maximum number of significant figures.
Definition: qgsbasicnumericformat.h:67
QgsBasicNumericFormat::thousandsSeparator
QChar thousandsSeparator() const
Returns any override for the thousands separator character.
Definition: qgsbasicnumericformat.cpp:224
QgsBasicNumericFormat::setRoundingType
void setRoundingType(RoundingType type)
Sets the rounding type, which controls the behavior of the numberDecimalPlaces() setting.
Definition: qgsbasicnumericformat.cpp:219
QgsBasicNumericFormat::showTrailingZeros
bool showTrailingZeros() const
Returns true if trailing zeros will be shown (up to the specified numberDecimalPlaces()).
Definition: qgsbasicnumericformat.cpp:204
QgsBasicNumericFormat::configuration
QVariantMap configuration(const QgsReadWriteContext &context) const override
Returns the current configuration of the formatter.
Definition: qgsbasicnumericformat.cpp:150
qgsbasicnumericformat.h
QgsBasicNumericFormat::formatDouble
QString formatDouble(double value, const QgsNumericFormatContext &context) const override
Returns a formatted string representation of a numeric double value.
Definition: qgsbasicnumericformat.cpp:59
QgsBasicNumericFormat::showThousandsSeparator
bool showThousandsSeparator() const
Returns true if the thousands grouping separator will be shown.
Definition: qgsbasicnumericformat.cpp:184
formatter::formatter
formatter(QChar thousands, bool showThousands, QChar decimal)
Definition: qgsbasicnumericformat.cpp:40
QgsNumericFormatContext::zeroDigit
QChar zeroDigit() const
Returns the zero digit character.
Definition: qgsnumericformat.h:113
QgsBasicNumericFormat::setShowTrailingZeros
void setShowTrailingZeros(bool show)
Sets whether trailing zeros will be shown (up to the specified numberDecimalPlaces()).
Definition: qgsbasicnumericformat.cpp:209
formatter::do_grouping
std::string do_grouping() const override
Definition: qgsbasicnumericformat.cpp:47
formatter::mDecimal
wchar_t mDecimal
Definition: qgsbasicnumericformat.cpp:50
QgsBasicNumericFormat::DecimalPlaces
@ DecimalPlaces
Maximum number of decimal places.
Definition: qgsbasicnumericformat.h:66
QgsBasicNumericFormat::visibleName
QString visibleName() const override
Returns the translated, user-visible name for this format.
Definition: qgsbasicnumericformat.cpp:49
QgsNumericFormatContext::decimalSeparator
QChar decimalSeparator() const
Returns the decimal separator character.
Definition: qgsnumericformat.h:73
QgsNumericFormatContext
A context for numeric formats.
Definition: qgsnumericformat.h:34
QgsNumericFormatContext::thousandsSeparator
QChar thousandsSeparator() const
Returns the thousands separator character.
Definition: qgsnumericformat.h:53