QGIS API Documentation 3.41.0-Master (cea29feecf2)
Loading...
Searching...
No Matches
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
18#include "qgis.h"
19#include <memory>
20#include <iostream>
21#include <locale>
22#include <iomanip>
23
25{
26 struct formatter : std::numpunct<wchar_t>
27 {
28 formatter( QChar thousands, bool showThousands, QChar decimal )
29 : mThousands( thousands.unicode() )
30 , mDecimal( decimal.unicode() )
31 , mShowThousands( showThousands )
32 {}
33 wchar_t do_decimal_point() const override { return mDecimal; }
34 wchar_t do_thousands_sep() const override { return mThousands; }
35 std::string do_grouping() const override { return mShowThousands ? "\3" : "\0"; }
36
37 wchar_t mThousands;
38 wchar_t mDecimal;
39 bool mShowThousands = true;
40 };
41}
42
46
48{
49 return QStringLiteral( "basic" );
50}
51
53{
54 return QObject::tr( "Number" );
55}
56
58{
59 return 1;
60}
61
62QString QgsBasicNumericFormat::formatDouble( double value, const QgsNumericFormatContext &context ) const
63{
64 const QChar decimal = mDecimalSeparator.isNull() ? context.decimalSeparator() : mDecimalSeparator;
65 std::basic_stringstream<wchar_t> os;
66 os.imbue( std::locale( os.getloc(), new QgsBasicNumericFormat_ns::formatter( mThousandsSeparator.isNull() ? context.thousandsSeparator() : mThousandsSeparator,
67 mShowThousandsSeparator,
68 decimal ) ) );
69
70 if ( !mUseScientific )
71 {
72 switch ( mRoundingType )
73 {
74 case DecimalPlaces:
75 os << std::fixed << std::setprecision( mNumberDecimalPlaces );
76 if ( qgsDoubleNear( value, 0 ) )
77 os << 0.0;
78 else
79 os << value;
80
81 break;
82
84 {
85 if ( qgsDoubleNear( value, 0 ) )
86 {
87 os << std::fixed << std::setprecision( mNumberDecimalPlaces - 1 ) << 0.0;
88 }
89 else
90 {
91 // digits before decimal point
92 const int d = std::floor( std::log10( value < 0 ? -value : value ) ) + 1;
93 const double order = std::pow( 10.0, mNumberDecimalPlaces - d );
94 os << std::fixed << std::setprecision( std::max( mNumberDecimalPlaces - d, 0 ) ) << std::round( value * order ) / order;
95 }
96 break;
97 }
98 }
99 }
100 else
101 {
102 os << std::scientific << std::setprecision( mNumberDecimalPlaces );
103 if ( qgsDoubleNear( value, 0 ) )
104 os << 0.0;
105 else
106 os << value;
107 }
108
109 QString res = QString::fromStdWString( os.str() );
110
111 if ( mShowPlusSign && value > 0 )
112 res.prepend( context.positiveSign() );
113
114 if ( !mShowTrailingZeros && res.contains( decimal ) )
115 {
116 int trimPoint = res.length() - 1;
117 int ePoint = 0;
118 if ( mUseScientific )
119 {
120 while ( res.at( trimPoint ).toUpper() != context.exponential().toUpper() )
121 trimPoint--;
122 ePoint = trimPoint;
123 trimPoint--;
124 }
125
126 while ( res.at( trimPoint ) == context.zeroDigit() )
127 trimPoint--;
128
129 if ( res.at( trimPoint ) == decimal )
130 trimPoint--;
131
132 const QString original = res;
133 res.truncate( trimPoint + 1 );
134 if ( mUseScientific )
135 res += original.mid( ePoint );
136 }
137
138 return res;
139}
140
142{
143 return new QgsBasicNumericFormat( *this );
144}
145
146QgsNumericFormat *QgsBasicNumericFormat::create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const
147{
148 std::unique_ptr< QgsBasicNumericFormat > res = std::make_unique< QgsBasicNumericFormat >();
149 res->setConfiguration( configuration, context );
150 return res.release();
151}
152
154{
155 QVariantMap res;
156 res.insert( QStringLiteral( "decimals" ), mNumberDecimalPlaces );
157 res.insert( QStringLiteral( "show_thousand_separator" ), mShowThousandsSeparator );
158 res.insert( QStringLiteral( "show_plus" ), mShowPlusSign );
159 res.insert( QStringLiteral( "show_trailing_zeros" ), mShowTrailingZeros );
160 res.insert( QStringLiteral( "rounding_type" ), static_cast< int >( mRoundingType ) );
161 res.insert( QStringLiteral( "thousand_separator" ), mThousandsSeparator.isNull() ? QVariant() : QVariant::fromValue( mThousandsSeparator ) );
162 res.insert( QStringLiteral( "decimal_separator" ), mDecimalSeparator.isNull() ? QVariant() : QVariant::fromValue( mDecimalSeparator ) );
163 return res;
164}
165
166void QgsBasicNumericFormat::setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext & )
167{
168 mNumberDecimalPlaces = configuration.value( QStringLiteral( "decimals" ), 6 ).toInt();
169 mShowThousandsSeparator = configuration.value( QStringLiteral( "show_thousand_separator" ), true ).toBool();
170 mShowPlusSign = configuration.value( QStringLiteral( "show_plus" ), false ).toBool();
171 mShowTrailingZeros = configuration.value( QStringLiteral( "show_trailing_zeros" ), false ).toBool();
172 mRoundingType = static_cast< RoundingType >( configuration.value( QStringLiteral( "rounding_type" ), static_cast< int >( DecimalPlaces ) ).toInt() );
173 mThousandsSeparator = configuration.value( QStringLiteral( "thousand_separator" ), QChar() ).toChar();
174 mDecimalSeparator = configuration.value( QStringLiteral( "decimal_separator" ), QChar() ).toChar();
175}
176
178{
179 return mNumberDecimalPlaces;
180}
181
183{
184 mNumberDecimalPlaces = numberDecimalPlaces;
185}
186
188{
189 return mShowThousandsSeparator;
190}
191
192void QgsBasicNumericFormat::setShowThousandsSeparator( bool showThousandsSeparator )
193{
194 mShowThousandsSeparator = showThousandsSeparator;
195}
196
198{
199 return mShowPlusSign;
200}
201
203{
204 mShowPlusSign = showPlusSign;
205}
206
208{
209 return mShowTrailingZeros;
210}
211
213{
214 mShowTrailingZeros = showTrailingZeros;
215}
216
221
226
228{
229 return mThousandsSeparator;
230}
231
233{
234 mThousandsSeparator = character;
235}
236
238{
239 return mDecimalSeparator;
240}
241
243{
244 mDecimalSeparator = character;
245}
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:6024
std::string do_grouping() const override
formatter(QChar thousands, bool showThousands, QChar decimal)