QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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  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  os << value;
74  break;
75 
76  case SignificantFigures:
77  {
78  if ( qgsDoubleNear( value, 0 ) )
79  {
80  os << std::fixed << std::setprecision( mNumberDecimalPlaces - 1 ) << value;
81  }
82  else
83  {
84  // digits before decimal point
85  const int d = std::floor( std::log10( value < 0 ? -value : value ) ) + 1;
86  double order = std::pow( 10.0, mNumberDecimalPlaces - d );
87  os << std::fixed << std::setprecision( std::max( mNumberDecimalPlaces - d, 0 ) ) << std::round( value * order ) / order;
88  }
89  break;
90  }
91  }
92  }
93  else
94  {
95  os << std::scientific << std::setprecision( mNumberDecimalPlaces );
96  os << value;
97  }
98 
99  QString res = QString::fromStdWString( os.str() );
100 
101  if ( mShowPlusSign && value > 0 )
102  res.prepend( context.positiveSign() );
103 
104  if ( !mShowTrailingZeros && res.contains( decimal ) )
105  {
106  int trimPoint = res.length() - 1;
107  int ePoint = 0;
108  if ( mUseScientific )
109  {
110  while ( res.at( trimPoint ).toUpper() != context.exponential().toUpper() )
111  trimPoint--;
112  ePoint = trimPoint;
113  trimPoint--;
114  }
115 
116  while ( res.at( trimPoint ) == context.zeroDigit() )
117  trimPoint--;
118 
119  if ( res.at( trimPoint ) == decimal )
120  trimPoint--;
121 
122  QString original = res;
123  res.truncate( trimPoint + 1 );
124  if ( mUseScientific )
125  res += original.mid( ePoint );
126  }
127 
128  return res;
129 }
130 
132 {
133  return new QgsBasicNumericFormat( *this );
134 }
135 
136 QgsNumericFormat *QgsBasicNumericFormat::create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const
137 {
138  std::unique_ptr< QgsBasicNumericFormat > res = qgis::make_unique< QgsBasicNumericFormat >();
139  res->setConfiguration( configuration, context );
140  return res.release();
141 }
142 
144 {
145  QVariantMap res;
146  res.insert( QStringLiteral( "decimals" ), mNumberDecimalPlaces );
147  res.insert( QStringLiteral( "show_thousand_separator" ), mShowThousandsSeparator );
148  res.insert( QStringLiteral( "show_plus" ), mShowPlusSign );
149  res.insert( QStringLiteral( "show_trailing_zeros" ), mShowTrailingZeros );
150  res.insert( QStringLiteral( "rounding_type" ), static_cast< int >( mRoundingType ) );
151  res.insert( QStringLiteral( "thousand_separator" ), mThousandsSeparator );
152  res.insert( QStringLiteral( "decimal_separator" ), mDecimalSeparator );
153  return res;
154 }
155 
156 void QgsBasicNumericFormat::setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext & )
157 {
158  mNumberDecimalPlaces = configuration.value( QStringLiteral( "decimals" ), 6 ).toInt();
159  mShowThousandsSeparator = configuration.value( QStringLiteral( "show_thousand_separator" ), true ).toBool();
160  mShowPlusSign = configuration.value( QStringLiteral( "show_plus" ), false ).toBool();
161  mShowTrailingZeros = configuration.value( QStringLiteral( "show_trailing_zeros" ), false ).toBool();
162  mRoundingType = static_cast< RoundingType >( configuration.value( QStringLiteral( "rounding_type" ), static_cast< int >( DecimalPlaces ) ).toInt() );
163  mThousandsSeparator = configuration.value( QStringLiteral( "thousand_separator" ), QChar() ).toChar();
164  mDecimalSeparator = configuration.value( QStringLiteral( "decimal_separator" ), QChar() ).toChar();
165 }
166 
168 {
169  return mNumberDecimalPlaces;
170 }
171 
172 void QgsBasicNumericFormat::setNumberDecimalPlaces( int numberDecimalPlaces )
173 {
174  mNumberDecimalPlaces = numberDecimalPlaces;
175 }
176 
178 {
179  return mShowThousandsSeparator;
180 }
181 
182 void QgsBasicNumericFormat::setShowThousandsSeparator( bool showThousandsSeparator )
183 {
184  mShowThousandsSeparator = showThousandsSeparator;
185 }
186 
188 {
189  return mShowPlusSign;
190 }
191 
192 void QgsBasicNumericFormat::setShowPlusSign( bool showPlusSign )
193 {
194  mShowPlusSign = showPlusSign;
195 }
196 
198 {
199  return mShowTrailingZeros;
200 }
201 
202 void QgsBasicNumericFormat::setShowTrailingZeros( bool showTrailingZeros )
203 {
204  mShowTrailingZeros = showTrailingZeros;
205 }
206 
208 {
209  return mRoundingType;
210 }
211 
213 {
214  mRoundingType = type;
215 }
216 
218 {
219  return mThousandsSeparator;
220 }
221 
223 {
224  mThousandsSeparator = character;
225 }
226 
228 {
229  return mDecimalSeparator;
230 }
231 
233 {
234  mDecimalSeparator = character;
235 }
QgsBasicNumericFormat::clone
QgsNumericFormat * clone() const override
Clones the format, returning a new object.
Definition: qgsbasicnumericformat.cpp:131
formatter
Definition: qgsbasicnumericformat.cpp:25
QgsBasicNumericFormat::mUseScientific
bool mUseScientific
Definition: qgsbasicnumericformat.h:168
formatter::do_thousands_sep
wchar_t do_thousands_sep() const override
Definition: qgsbasicnumericformat.cpp:32
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:35
QgsBasicNumericFormat::numberDecimalPlaces
int numberDecimalPlaces() const
Returns the maximum number of decimal places to show.
Definition: qgsbasicnumericformat.cpp:167
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:172
qgis.h
QgsNumericFormat
A numeric formatter allows for formatting a numeric value for display, using a variety of different f...
Definition: qgsnumericformat.h:218
formatter::mShowThousands
bool mShowThousands
Definition: qgsbasicnumericformat.cpp:37
QgsBasicNumericFormat::RoundingType
RoundingType
Sets rounding type and behavior of the numberDecimalPlaces() setting.
Definition: qgsbasicnumericformat.h:39
QgsBasicNumericFormat::showPlusSign
bool showPlusSign() const
Returns true if a leading plus sign will be shown for positive values.
Definition: qgsbasicnumericformat.cpp:187
QgsBasicNumericFormat::setThousandsSeparator
void setThousandsSeparator(QChar character)
Sets an override character for the thousands separator character.
Definition: qgsbasicnumericformat.cpp:222
QgsNumericFormatContext::exponential
QChar exponential() const
Returns the exponential character.
Definition: qgsnumericformat.h:171
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:136
QgsBasicNumericFormat::setDecimalSeparator
void setDecimalSeparator(QChar character)
Sets an override character for the decimal separator character.
Definition: qgsbasicnumericformat.cpp:232
QgsBasicNumericFormat::decimalSeparator
QChar decimalSeparator() const
Returns any override for the decimal separator character.
Definition: qgsbasicnumericformat.cpp:227
QgsBasicNumericFormat::roundingType
RoundingType roundingType() const
Returns the rounding type, which controls the behavior of the numberDecimalPlaces() setting.
Definition: qgsbasicnumericformat.cpp:207
QgsBasicNumericFormat::setShowThousandsSeparator
void setShowThousandsSeparator(bool show)
Sets whether the thousands grouping separator will be shown.
Definition: qgsbasicnumericformat.cpp:182
QgsBasicNumericFormat::setShowPlusSign
void setShowPlusSign(bool show)
Sets whether a leading plus sign will be shown for positive values.
Definition: qgsbasicnumericformat.cpp:192
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:315
QgsNumericFormatContext::positiveSign
QChar positiveSign() const
Returns the positive sign character.
Definition: qgsnumericformat.h:151
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:31
QgsBasicNumericFormat::setConfiguration
virtual void setConfiguration(const QVariantMap &configuration, const QgsReadWriteContext &context)
Sets the format's configuration.
Definition: qgsbasicnumericformat.cpp:156
formatter::mThousands
wchar_t mThousands
Definition: qgsbasicnumericformat.cpp:35
QgsBasicNumericFormat::SignificantFigures
@ SignificantFigures
Maximum number of significant figures.
Definition: qgsbasicnumericformat.h:41
QgsBasicNumericFormat::thousandsSeparator
QChar thousandsSeparator() const
Returns any override for the thousands separator character.
Definition: qgsbasicnumericformat.cpp:217
QgsBasicNumericFormat::setRoundingType
void setRoundingType(RoundingType type)
Sets the rounding type, which controls the behavior of the numberDecimalPlaces() setting.
Definition: qgsbasicnumericformat.cpp:212
QgsBasicNumericFormat::showTrailingZeros
bool showTrailingZeros() const
Returns true if trailing zeros will be shown (up to the specified numberDecimalPlaces()).
Definition: qgsbasicnumericformat.cpp:197
QgsBasicNumericFormat::configuration
QVariantMap configuration(const QgsReadWriteContext &context) const override
Returns the current configuration of the formatter.
Definition: qgsbasicnumericformat.cpp:143
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:177
formatter::formatter
formatter(QChar thousands, bool showThousands, QChar decimal)
Definition: qgsbasicnumericformat.cpp:26
QgsNumericFormatContext::zeroDigit
QChar zeroDigit() const
Returns the zero digit character.
Definition: qgsnumericformat.h:111
QgsBasicNumericFormat::setShowTrailingZeros
void setShowTrailingZeros(bool show)
Sets whether trailing zeros will be shown (up to the specified numberDecimalPlaces()).
Definition: qgsbasicnumericformat.cpp:202
formatter::do_grouping
std::string do_grouping() const override
Definition: qgsbasicnumericformat.cpp:33
formatter::mDecimal
wchar_t mDecimal
Definition: qgsbasicnumericformat.cpp:36
QgsBasicNumericFormat::DecimalPlaces
@ DecimalPlaces
Maximum number of decimal places.
Definition: qgsbasicnumericformat.h:40
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:71
QgsNumericFormatContext
A context for numeric formats.
Definition: qgsnumericformat.h:35
QgsNumericFormatContext::thousandsSeparator
QChar thousandsSeparator() const
Returns the thousands separator character.
Definition: qgsnumericformat.h:51