Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsrendererrange.cpp
3 : : ---------------------
4 : : begin : September 2019
5 : : copyright : (C) 2019 by Denis Rouzaud
6 : : email : denis@opengis.ch
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : :
16 : : #include "qgsrendererrange.h"
17 : : #include "qgsclassificationmethod.h"
18 : :
19 : : #include <QLocale>
20 : :
21 : :
22 : 0 : QgsRendererRange::QgsRendererRange( const QgsClassificationRange &range, QgsSymbol *symbol, bool render )
23 : 0 : : mLowerValue( range.lowerBound() )
24 : 0 : , mUpperValue( range.upperBound() )
25 : 0 : , mSymbol( symbol )
26 : 0 : , mLabel( range.label() )
27 : 0 : , mRender( render )
28 : : {
29 : 0 : }
30 : :
31 : 0 : QgsRendererRange::QgsRendererRange( double lowerValue, double upperValue, QgsSymbol *symbol, const QString &label, bool render )
32 : 0 : : mLowerValue( lowerValue )
33 : 0 : , mUpperValue( upperValue )
34 : 0 : , mSymbol( symbol )
35 : 0 : , mLabel( label )
36 : 0 : , mRender( render )
37 : 0 : {}
38 : :
39 : 0 : QgsRendererRange::QgsRendererRange( const QgsRendererRange &range )
40 : 0 : : mLowerValue( range.mLowerValue )
41 : 0 : , mUpperValue( range.mUpperValue )
42 : 0 : , mSymbol( range.mSymbol ? range.mSymbol->clone() : nullptr )
43 : 0 : , mLabel( range.mLabel )
44 : 0 : , mRender( range.mRender )
45 : 0 : {}
46 : :
47 : : // cpy and swap idiom, note that the cpy is done with 'pass by value'
48 : 0 : QgsRendererRange &QgsRendererRange::operator=( QgsRendererRange range )
49 : : {
50 : 0 : swap( range );
51 : 0 : return *this;
52 : : }
53 : :
54 : 0 : bool QgsRendererRange::operator<( const QgsRendererRange &other ) const
55 : : {
56 : 0 : return
57 : 0 : lowerValue() < other.lowerValue() ||
58 : 0 : ( qgsDoubleNear( lowerValue(), other.lowerValue() ) && upperValue() < other.upperValue() );
59 : : }
60 : :
61 : :
62 : 0 : void QgsRendererRange::swap( QgsRendererRange &other )
63 : : {
64 : 0 : std::swap( mLowerValue, other.mLowerValue );
65 : 0 : std::swap( mUpperValue, other.mUpperValue );
66 : 0 : std::swap( mSymbol, other.mSymbol );
67 : 0 : std::swap( mLabel, other.mLabel );
68 : 0 : }
69 : :
70 : 0 : double QgsRendererRange::lowerValue() const
71 : : {
72 : 0 : return mLowerValue;
73 : : }
74 : :
75 : 0 : double QgsRendererRange::upperValue() const
76 : : {
77 : 0 : return mUpperValue;
78 : : }
79 : :
80 : 0 : QgsSymbol *QgsRendererRange::symbol() const
81 : : {
82 : 0 : return mSymbol.get();
83 : : }
84 : :
85 : 0 : QString QgsRendererRange::label() const
86 : : {
87 : 0 : return mLabel;
88 : : }
89 : :
90 : 0 : void QgsRendererRange::setSymbol( QgsSymbol *s )
91 : : {
92 : 0 : if ( mSymbol.get() != s ) mSymbol.reset( s );
93 : 0 : }
94 : :
95 : 0 : void QgsRendererRange::setLabel( const QString &label )
96 : : {
97 : 0 : mLabel = label;
98 : 0 : }
99 : :
100 : 0 : void QgsRendererRange::setUpperValue( double upperValue )
101 : : {
102 : 0 : mUpperValue = upperValue;
103 : 0 : }
104 : :
105 : 0 : void QgsRendererRange::setLowerValue( double lowerValue )
106 : : {
107 : 0 : mLowerValue = lowerValue;
108 : 0 : }
109 : :
110 : 0 : bool QgsRendererRange::renderState() const
111 : : {
112 : 0 : return mRender;
113 : : }
114 : :
115 : 0 : void QgsRendererRange::setRenderState( bool render )
116 : : {
117 : 0 : mRender = render;
118 : 0 : }
119 : :
120 : 0 : QString QgsRendererRange::dump() const
121 : : {
122 : 0 : return QStringLiteral( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel, mSymbol ? mSymbol->dump() : QStringLiteral( "(no symbol)" ) );
123 : 0 : }
124 : :
125 : 0 : void QgsRendererRange::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props, bool firstRange ) const
126 : : {
127 : 0 : if ( !mSymbol || props.value( QStringLiteral( "attribute" ), QString() ).toString().isEmpty() )
128 : 0 : return;
129 : :
130 : 0 : QString attrName = props[ QStringLiteral( "attribute" )].toString();
131 : :
132 : 0 : QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
133 : 0 : element.appendChild( ruleElem );
134 : :
135 : 0 : QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
136 : 0 : nameElem.appendChild( doc.createTextNode( mLabel ) );
137 : 0 : ruleElem.appendChild( nameElem );
138 : 0 :
139 : 0 : QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
140 : 0 : QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
141 : 0 : QString descrStr = QStringLiteral( "range: %1 - %2" ).arg( qgsDoubleToString( mLowerValue ), qgsDoubleToString( mUpperValue ) );
142 : 0 : titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
143 : 0 : descrElem.appendChild( titleElem );
144 : 0 : ruleElem.appendChild( descrElem );
145 : :
146 : : // create the ogc:Filter for the range
147 : 0 : QString filterFunc = QStringLiteral( "\"%1\" %2 %3 AND \"%1\" <= %4" )
148 : 0 : .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
149 : 0 : firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
150 : 0 : qgsDoubleToString( mLowerValue ),
151 : 0 : qgsDoubleToString( mUpperValue ) );
152 : 0 : QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
153 : :
154 : 0 : mSymbol->toSld( doc, ruleElem, props );
155 : 0 : }
156 : :
157 : : //////////
158 : :
159 : :
160 : : const int QgsRendererRangeLabelFormat::MAX_PRECISION = 15;
161 : : const int QgsRendererRangeLabelFormat::MIN_PRECISION = -6;
162 : :
163 : 0 : QgsRendererRangeLabelFormat::QgsRendererRangeLabelFormat()
164 : 0 : : mFormat( QStringLiteral( "%1 - %2" ) )
165 : 0 : , mReTrailingZeroes( "[.,]?0*$" )
166 : 0 : , mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
167 : : {
168 : 0 : }
169 : :
170 : 0 : QgsRendererRangeLabelFormat::QgsRendererRangeLabelFormat( const QString &format, int precision, bool trimTrailingZeroes )
171 : 0 : : mReTrailingZeroes( "[.,]?0*$" )
172 : 0 : , mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
173 : : {
174 : 0 : setFormat( format );
175 : 0 : setPrecision( precision );
176 : 0 : setTrimTrailingZeroes( trimTrailingZeroes );
177 : 0 : }
178 : :
179 : :
180 : 0 : bool QgsRendererRangeLabelFormat::operator==( const QgsRendererRangeLabelFormat &other ) const
181 : : {
182 : 0 : return
183 : 0 : format() == other.format() &&
184 : 0 : precision() == other.precision() &&
185 : 0 : trimTrailingZeroes() == other.trimTrailingZeroes();
186 : 0 : }
187 : :
188 : 0 : bool QgsRendererRangeLabelFormat::operator!=( const QgsRendererRangeLabelFormat &other ) const
189 : : {
190 : 0 : return !( *this == other );
191 : : }
192 : :
193 : 0 : void QgsRendererRangeLabelFormat::setPrecision( int precision )
194 : : {
195 : : // Limit the range of decimal places to a reasonable range
196 : 0 : precision = std::clamp( precision, MIN_PRECISION, MAX_PRECISION );
197 : 0 : mPrecision = precision;
198 : 0 : mNumberScale = 1.0;
199 : 0 : mNumberSuffix.clear();
200 : 0 : while ( precision < 0 )
201 : : {
202 : 0 : precision++;
203 : 0 : mNumberScale /= 10.0;
204 : 0 : mNumberSuffix.append( '0' );
205 : : }
206 : 0 : }
207 : :
208 : 0 : QString QgsRendererRangeLabelFormat::labelForRange( const QgsRendererRange &range ) const
209 : : {
210 : 0 : return labelForRange( range.lowerValue(), range.upperValue() );
211 : : }
212 : :
213 : 0 : QString QgsRendererRangeLabelFormat::formatNumber( double value ) const
214 : : {
215 : 0 : if ( mPrecision > 0 )
216 : : {
217 : 0 : QString valueStr = QLocale().toString( value, 'f', mPrecision );
218 : 0 : if ( mTrimTrailingZeroes )
219 : 0 : valueStr = valueStr.remove( mReTrailingZeroes );
220 : 0 : if ( mReNegativeZero.exactMatch( valueStr ) )
221 : 0 : valueStr = valueStr.mid( 1 );
222 : 0 : return valueStr;
223 : 0 : }
224 : : else
225 : : {
226 : 0 : QString valueStr = QLocale().toString( value * mNumberScale, 'f', 0 );
227 : 0 : if ( valueStr == QLatin1String( "-0" ) )
228 : 0 : valueStr = '0';
229 : 0 : if ( valueStr != QLatin1String( "0" ) )
230 : 0 : valueStr = valueStr + mNumberSuffix;
231 : 0 : return valueStr;
232 : 0 : }
233 : 0 : }
234 : :
235 : 0 : QString QgsRendererRangeLabelFormat::labelForRange( double lower, double upper ) const
236 : : {
237 : 0 : QString lowerStr = formatNumber( lower );
238 : 0 : QString upperStr = formatNumber( upper );
239 : :
240 : 0 : QString legend( mFormat );
241 : 0 : return legend.replace( QLatin1String( "%1" ), lowerStr ).replace( QLatin1String( "%2" ), upperStr );
242 : 0 : }
243 : :
244 : 0 : void QgsRendererRangeLabelFormat::setFromDomElement( QDomElement &element )
245 : : {
246 : 0 : mFormat = element.attribute( QStringLiteral( "format" ),
247 : 0 : element.attribute( QStringLiteral( "prefix" ), QStringLiteral( " " ) ) + "%1" +
248 : 0 : element.attribute( QStringLiteral( "separator" ), QStringLiteral( " - " ) ) + "%2" +
249 : 0 : element.attribute( QStringLiteral( "suffix" ), QStringLiteral( " " ) )
250 : : );
251 : 0 : setPrecision( element.attribute( QStringLiteral( "decimalplaces" ), QStringLiteral( "4" ) ).toInt() );
252 : 0 : mTrimTrailingZeroes = element.attribute( QStringLiteral( "trimtrailingzeroes" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
253 : 0 : }
254 : :
255 : 0 : void QgsRendererRangeLabelFormat::saveToDomElement( QDomElement &element )
256 : : {
257 : 0 : element.setAttribute( QStringLiteral( "format" ), mFormat );
258 : 0 : element.setAttribute( QStringLiteral( "decimalplaces" ), mPrecision );
259 : 0 : element.setAttribute( QStringLiteral( "trimtrailingzeroes" ), mTrimTrailingZeroes ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
260 : 0 : }
|