Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsinterpolatedlinerenderer.cpp
3 : : --------------------------------------
4 : : Date : April 2020
5 : : Copyright : (C) 2020 by Vincent Cloarec
6 : : Email : vcloarec at gmail dot com
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 <QPainter>
17 : :
18 : : #include "qgsinterpolatedlinerenderer.h"
19 : : #include "qgssymbollayerutils.h"
20 : :
21 : 0 : void QgsInterpolatedLineRenderer::setInterpolatedWidth( const QgsInterpolatedLineWidth &strokeWidth )
22 : : {
23 : 0 : mStrokeWidth = strokeWidth;
24 : 0 : }
25 : :
26 : 0 : void QgsInterpolatedLineRenderer::setInterpolatedColor( const QgsInterpolatedLineColor &strokeColoring )
27 : : {
28 : 0 : mStrokeColoring = strokeColoring;
29 : 0 : }
30 : :
31 : 0 : void QgsInterpolatedLineRenderer::setWidthUnit( const QgsUnitTypes::RenderUnit &strokeWidthUnit )
32 : : {
33 : 0 : mStrokeWidthUnit = strokeWidthUnit;
34 : 0 : }
35 : :
36 : 0 : void QgsInterpolatedLineRenderer::render( double value1, double value2, QgsPointXY point1, QgsPointXY point2, QgsRenderContext &context ) const
37 : : {
38 : 0 : QPainter *painter = context.painter();
39 : 0 : QgsScopedQPainterState painterState( painter );
40 : 0 : context.setPainterFlagsUsingContext( painter );
41 : :
42 : 0 : const QgsMapToPixel &mapToPixel = context.mapToPixel();
43 : :
44 : 0 : if ( value1 > value2 )
45 : : {
46 : 0 : std::swap( value1, value2 );
47 : 0 : std::swap( point1, point2 );
48 : 0 : }
49 : :
50 : 0 : QPointF p1 = mapToPixel.transform( point1 ).toQPointF();
51 : 0 : QPointF p2 = mapToPixel.transform( point2 ).toQPointF();
52 : 0 : QPointF dir = p2 - p1;
53 : 0 : double length = sqrt( pow( dir.x(), 2 ) + pow( dir.y(), 2 ) );
54 : 0 : QPointF diru = dir / length;
55 : 0 : QPointF orthu = QPointF( -diru.y(), diru.x() );
56 : :
57 : 0 : QList<double> breakValues;
58 : 0 : QList<QColor> breakColors;
59 : 0 : QList<QLinearGradient> gradients;
60 : :
61 : 0 : mStrokeColoring.graduatedColors( value1, value2, breakValues, breakColors, gradients );
62 : :
63 : 0 : if ( gradients.isEmpty() && !breakValues.empty() && !breakColors.isEmpty() ) //exact colors to render
64 : : {
65 : : Q_ASSERT( breakColors.count() == breakValues.count() );
66 : 0 : for ( int i = 0; i < breakValues.count(); ++i )
67 : : {
68 : 0 : double value = breakValues.at( i );
69 : 0 : double width = context.convertToPainterUnits( mStrokeWidth.strokeWidth( value ), mStrokeWidthUnit );
70 : 0 : QPen pen( breakColors.at( i ) );
71 : 0 : pen.setWidthF( width );
72 : 0 : pen.setCapStyle( Qt::PenCapStyle::RoundCap );
73 : 0 : painter->setPen( pen );
74 : 0 : QPointF point = p1 + dir * ( value - value1 ) / ( value2 - value1 );
75 : 0 : painter->drawPoint( point );
76 : 0 : }
77 : 0 : }
78 : : else
79 : : {
80 : 0 : double width1 = mStrokeWidth.strokeWidth( value1 );
81 : 0 : double width2 = mStrokeWidth.strokeWidth( value2 );
82 : :
83 : 0 : if ( !std::isnan( width1 ) || !std::isnan( width2 ) ) // the two widths on extremity are not out of range and ignored
84 : : {
85 : : //Draw line cap
86 : 0 : QBrush brush( Qt::SolidPattern );
87 : 0 : QPen pen;
88 : : int startAngle;
89 : 0 : startAngle = ( acos( -orthu.x() ) / M_PI ) * 180;
90 : 0 : if ( orthu.y() < 0 )
91 : 0 : startAngle = 360 - startAngle;
92 : :
93 : 0 : bool outOfRange1 = std::isnan( width1 );
94 : 0 : bool outOfRange2 = std::isnan( width2 );
95 : :
96 : 0 : if ( !outOfRange1 )
97 : : {
98 : 0 : width1 = context.convertToPainterUnits( width1, mStrokeWidthUnit );
99 : 0 : QRectF capBox1( p1.x() - width1 / 2, p1.y() - width1 / 2, width1, width1 );
100 : 0 : brush.setColor( mStrokeColoring.color( value1 ) );
101 : 0 : painter->setBrush( brush );
102 : 0 : pen.setBrush( brush );
103 : 0 : painter->setPen( pen );
104 : 0 : painter->drawPie( capBox1, ( startAngle - 1 ) * 16, 182 * 16 );
105 : 0 : }
106 : :
107 : 0 : if ( !outOfRange2 )
108 : : {
109 : 0 : width2 = context.convertToPainterUnits( width2, mStrokeWidthUnit ) ;
110 : 0 : QRectF capBox2( p2.x() - width2 / 2, p2.y() - width2 / 2, width2, width2 );
111 : 0 : brush.setColor( mStrokeColoring.color( value2 ) );
112 : 0 : pen.setBrush( brush );
113 : 0 : painter->setBrush( brush );
114 : 0 : painter->setPen( pen );
115 : 0 : painter->drawPie( capBox2, ( startAngle + 179 ) * 16, 182 * 16 );
116 : 0 : }
117 : :
118 : 0 : if ( gradients.isEmpty() && breakValues.empty() && breakColors.count() == 1 ) //only one color to render
119 : : {
120 : 0 : double startAdjusting = 0;
121 : 0 : if ( outOfRange1 )
122 : 0 : adjustLine( value1, value1, value2, width1, startAdjusting );
123 : :
124 : :
125 : 0 : double endAdjusting = 0;
126 : 0 : if ( outOfRange2 )
127 : 0 : adjustLine( value2, value1, value2, width2, endAdjusting );
128 : :
129 : 0 : QPointF pointStartAdjusted = p1 + dir * startAdjusting;
130 : 0 : QPointF pointEndAdjusted = p2 - dir * endAdjusting;
131 : :
132 : 0 : QPolygonF varLine;
133 : 0 : double semiWidth1 = width1 / 2;
134 : 0 : double semiWidth2 = width2 / 2;
135 : :
136 : 0 : varLine.append( pointStartAdjusted + orthu * semiWidth1 );
137 : 0 : varLine.append( pointEndAdjusted + orthu * semiWidth2 );
138 : 0 : varLine.append( pointEndAdjusted - orthu * semiWidth2 );
139 : 0 : varLine.append( pointStartAdjusted - orthu * semiWidth1 );
140 : :
141 : 0 : QBrush brush( Qt::SolidPattern );
142 : 0 : brush.setColor( breakColors.first() );
143 : 0 : painter->setBrush( brush );
144 : 0 : painter->setPen( pen );
145 : :
146 : 0 : QPen pen;
147 : 0 : pen.setBrush( brush );
148 : 0 : pen.setWidthF( 0 );
149 : 0 : painter->setPen( pen );
150 : :
151 : 0 : painter->drawPolygon( varLine );
152 : 0 : }
153 : 0 : else if ( !gradients.isEmpty() && !breakValues.isEmpty() && !breakColors.isEmpty() )
154 : : {
155 : : Q_ASSERT( breakColors.count() == breakValues.count() );
156 : : Q_ASSERT( breakColors.count() == gradients.count() + 1 );
157 : :
158 : 0 : for ( int i = 0; i < gradients.count(); ++i )
159 : : {
160 : 0 : double firstValue = breakValues.at( i );
161 : 0 : double secondValue = breakValues.at( i + 1 );
162 : 0 : double w1 = mStrokeWidth.strokeWidth( firstValue );
163 : 0 : double w2 = mStrokeWidth.strokeWidth( secondValue );
164 : :
165 : 0 : if ( std::isnan( w1 ) && std::isnan( w2 ) )
166 : 0 : continue;
167 : :
168 : 0 : double firstAdjusting = 0;
169 : 0 : if ( std::isnan( w1 ) )
170 : 0 : adjustLine( firstValue, value1, value2, w1, firstAdjusting );
171 : :
172 : :
173 : 0 : double secondAdjusting = 0;
174 : 0 : if ( std::isnan( w2 ) )
175 : 0 : adjustLine( secondValue, value1, value2, w2, secondAdjusting );
176 : :
177 : 0 : w1 = context.convertToPainterUnits( w1, mStrokeWidthUnit );
178 : 0 : w2 = context.convertToPainterUnits( w2, mStrokeWidthUnit ) ;
179 : :
180 : 0 : QPointF pointStart = p1 + dir * ( firstValue - value1 ) / ( value2 - value1 );
181 : 0 : QPointF pointEnd = p1 + dir * ( secondValue - value1 ) / ( value2 - value1 );
182 : :
183 : 0 : QPointF pointStartAdjusted = pointStart + dir * firstAdjusting;
184 : 0 : QPointF pointEndAdjusted = pointEnd - dir * secondAdjusting;
185 : :
186 : 0 : QPolygonF varLine;
187 : 0 : double sw1 = w1 / 2;
188 : 0 : double sw2 = w2 / 2;
189 : :
190 : 0 : varLine.append( pointStartAdjusted + orthu * sw1 );
191 : 0 : varLine.append( pointEndAdjusted + orthu * sw2 );
192 : 0 : varLine.append( pointEndAdjusted - orthu * sw2 );
193 : 0 : varLine.append( pointStartAdjusted - orthu * sw1 );
194 : :
195 : 0 : QLinearGradient gradient = gradients.at( i );
196 : 0 : gradient.setStart( pointStart );
197 : 0 : gradient.setFinalStop( pointEnd );
198 : 0 : QBrush brush( gradient );
199 : 0 : painter->setBrush( brush );
200 : :
201 : 0 : QPen pen;
202 : 0 : pen.setBrush( brush );
203 : 0 : pen.setWidthF( 0 );
204 : 0 : painter->setPen( pen );
205 : :
206 : 0 : painter->drawPolygon( varLine );
207 : 0 : }
208 : 0 : }
209 : 0 : }
210 : : }
211 : 0 : }
212 : :
213 : 0 : void QgsInterpolatedLineRenderer::adjustLine( const double &value, const double &value1, const double &value2, double &width, double &adjusting ) const
214 : : {
215 : 0 : if ( value > mStrokeWidth.maximumValue() )
216 : : {
217 : 0 : adjusting = fabs( ( value - mStrokeWidth.maximumValue() ) / ( value2 - value1 ) );
218 : 0 : width = mStrokeWidth.maximumWidth();
219 : 0 : }
220 : : else
221 : : {
222 : 0 : adjusting = fabs( ( value - mStrokeWidth.minimumValue() ) / ( value2 - value1 ) );
223 : 0 : width = mStrokeWidth.minimumWidth();
224 : : }
225 : 0 : }
226 : :
227 : 0 : double QgsInterpolatedLineWidth::minimumValue() const
228 : : {
229 : 0 : return mMinimumValue;
230 : : }
231 : :
232 : 0 : void QgsInterpolatedLineWidth::setMinimumValue( double minimumValue )
233 : : {
234 : 0 : mMinimumValue = minimumValue;
235 : 0 : mNeedUpdateFormula = true;
236 : 0 : }
237 : :
238 : 0 : double QgsInterpolatedLineWidth::maximumValue() const
239 : : {
240 : 0 : return mMaximumValue;
241 : : }
242 : :
243 : 0 : void QgsInterpolatedLineWidth::setMaximumValue( double maximumValue )
244 : : {
245 : 0 : mMaximumValue = maximumValue;
246 : 0 : mNeedUpdateFormula = true;
247 : 0 : }
248 : :
249 : 0 : double QgsInterpolatedLineWidth::minimumWidth() const
250 : : {
251 : 0 : return mMinimumWidth;
252 : : }
253 : :
254 : 0 : void QgsInterpolatedLineWidth::setMinimumWidth( double minimumWidth )
255 : : {
256 : 0 : mMinimumWidth = minimumWidth;
257 : 0 : mNeedUpdateFormula = true;
258 : 0 : }
259 : :
260 : 0 : double QgsInterpolatedLineWidth::maximumWidth() const
261 : : {
262 : 0 : return mMaximumWidth;
263 : : }
264 : :
265 : 0 : void QgsInterpolatedLineWidth::setMaximumWidth( double maximumWidth )
266 : : {
267 : 0 : mMaximumWidth = maximumWidth;
268 : 0 : mNeedUpdateFormula = true;
269 : 0 : }
270 : :
271 : 0 : double QgsInterpolatedLineWidth::strokeWidth( double value ) const
272 : : {
273 : 0 : if ( mIsWidthVariable )
274 : : {
275 : 0 : if ( mNeedUpdateFormula )
276 : 0 : updateLinearFormula();
277 : :
278 : 0 : if ( mUseAbsoluteValue )
279 : 0 : value = std::fabs( value );
280 : :
281 : 0 : if ( value > mMaximumValue )
282 : : {
283 : 0 : if ( mIgnoreOutOfRange )
284 : 0 : return std::numeric_limits<double>::quiet_NaN();
285 : : else
286 : 0 : return mMaximumWidth;
287 : : }
288 : :
289 : 0 : if ( value < mMinimumValue )
290 : : {
291 : 0 : if ( mIgnoreOutOfRange )
292 : 0 : return std::numeric_limits<double>::quiet_NaN();
293 : : else
294 : 0 : return mMinimumWidth;
295 : : }
296 : :
297 : 0 : return ( value - mMinimumValue ) * mLinearCoef + mMinimumWidth;
298 : : }
299 : : else
300 : 0 : return fixedStrokeWidth();
301 : 0 : }
302 : :
303 : 0 : QDomElement QgsInterpolatedLineWidth::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
304 : : {
305 : 0 : Q_UNUSED( context );
306 : :
307 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "mesh-stroke-width" ) );
308 : :
309 : 0 : elem.setAttribute( QStringLiteral( "width-varying" ), mIsWidthVariable ? 1 : 0 );
310 : 0 : elem.setAttribute( QStringLiteral( "fixed-width" ), mFixedWidth );
311 : 0 : elem.setAttribute( QStringLiteral( "minimum-value" ), mMinimumValue );
312 : 0 : elem.setAttribute( QStringLiteral( "maximum-value" ), mMaximumValue );
313 : 0 : elem.setAttribute( QStringLiteral( "minimum-width" ), mMinimumWidth );
314 : 0 : elem.setAttribute( QStringLiteral( "maximum-width" ), mMaximumWidth );
315 : 0 : elem.setAttribute( QStringLiteral( "ignore-out-of-range" ), mIgnoreOutOfRange ? 1 : 0 );
316 : 0 : elem.setAttribute( QStringLiteral( "use-absolute-value" ), mUseAbsoluteValue ? 1 : 0 );
317 : :
318 : 0 : return elem;
319 : 0 : }
320 : :
321 : 0 : void QgsInterpolatedLineWidth::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
322 : : {
323 : 0 : Q_UNUSED( context );
324 : :
325 : 0 : mIsWidthVariable = elem.attribute( QStringLiteral( "width-varying" ) ).toInt();
326 : 0 : mFixedWidth = elem.attribute( QStringLiteral( "fixed-width" ) ).toDouble();
327 : 0 : mMinimumValue = elem.attribute( QStringLiteral( "minimum-value" ) ).toDouble();
328 : 0 : mMaximumValue = elem.attribute( QStringLiteral( "maximum-value" ) ).toDouble();
329 : 0 : mMinimumWidth = elem.attribute( QStringLiteral( "minimum-width" ) ).toDouble();
330 : 0 : mMaximumWidth = elem.attribute( QStringLiteral( "maximum-width" ) ).toDouble();
331 : 0 : mIgnoreOutOfRange = elem.attribute( QStringLiteral( "ignore-out-of-range" ) ).toInt();
332 : 0 : mUseAbsoluteValue = elem.attribute( QStringLiteral( "use-absolute-value" ) ).toInt();
333 : 0 : }
334 : :
335 : 0 : bool QgsInterpolatedLineWidth::useAbsoluteValue() const
336 : : {
337 : 0 : return mUseAbsoluteValue;
338 : : }
339 : :
340 : 0 : void QgsInterpolatedLineWidth::setUseAbsoluteValue( bool useAbsoluteValue )
341 : : {
342 : 0 : mUseAbsoluteValue = useAbsoluteValue;
343 : 0 : }
344 : :
345 : 0 : double QgsInterpolatedLineWidth::fixedStrokeWidth() const
346 : : {
347 : 0 : return mFixedWidth;
348 : : }
349 : :
350 : 0 : bool QgsInterpolatedLineWidth::ignoreOutOfRange() const
351 : : {
352 : 0 : return mIgnoreOutOfRange;
353 : : }
354 : :
355 : 0 : void QgsInterpolatedLineWidth::setIgnoreOutOfRange( bool ignoreOutOfRange )
356 : : {
357 : 0 : mIgnoreOutOfRange = ignoreOutOfRange;
358 : 0 : }
359 : :
360 : 0 : bool QgsInterpolatedLineWidth::isVariableWidth() const
361 : : {
362 : 0 : return mIsWidthVariable;
363 : : }
364 : :
365 : 0 : void QgsInterpolatedLineWidth::setIsVariableWidth( bool isWidthVarying )
366 : : {
367 : 0 : mIsWidthVariable = isWidthVarying;
368 : 0 : }
369 : :
370 : 0 : void QgsInterpolatedLineWidth::setFixedStrokeWidth( double fixedWidth )
371 : : {
372 : 0 : mFixedWidth = fixedWidth;
373 : 0 : }
374 : :
375 : 0 : void QgsInterpolatedLineWidth::updateLinearFormula() const
376 : : {
377 : 0 : if ( mMaximumWidth - mMinimumWidth != 0 )
378 : 0 : mLinearCoef = ( mMaximumWidth - mMinimumWidth ) / ( mMaximumValue - mMinimumValue ) ;
379 : : else
380 : 0 : mLinearCoef = 0;
381 : 0 : mNeedUpdateFormula = false;
382 : 0 : }
383 : :
384 : 0 : QgsInterpolatedLineColor::QgsInterpolatedLineColor( const QgsColorRampShader &colorRampShader )
385 : : {
386 : 0 : setColor( colorRampShader );
387 : 0 : }
388 : :
389 : 0 : QgsInterpolatedLineColor::QgsInterpolatedLineColor( const QColor &color )
390 : : {
391 : 0 : setColor( color );
392 : 0 : mColoringMethod = SingleColor;
393 : 0 : }
394 : :
395 : 0 : void QgsInterpolatedLineColor::setColor( const QgsColorRampShader &colorRampShader )
396 : : {
397 : 0 : mColorRampShader = colorRampShader;
398 : 0 : if ( ( mColorRampShader.sourceColorRamp() ) )
399 : 0 : mColoringMethod = ColorRamp;
400 : : else
401 : 0 : mColoringMethod = SingleColor;
402 : 0 : }
403 : :
404 : 0 : void QgsInterpolatedLineColor::setColor( const QColor &color )
405 : : {
406 : 0 : mColorRampShader = QgsColorRampShader();
407 : 0 : mSingleColor = color;
408 : 0 : }
409 : :
410 : 0 : QColor QgsInterpolatedLineColor::color( double magnitude ) const
411 : : {
412 : 0 : if ( auto *lSourceColorRamp = mColorRampShader.sourceColorRamp() )
413 : : {
414 : 0 : if ( mColorRampShader.isEmpty() )
415 : 0 : return lSourceColorRamp->color( 0 );
416 : :
417 : : int r, g, b, a;
418 : 0 : if ( mColorRampShader.shade( magnitude, &r, &g, &b, &a ) )
419 : 0 : return QColor( r, g, b, a );
420 : : else
421 : 0 : return QColor( 0, 0, 0, 0 );
422 : : }
423 : : else
424 : : {
425 : 0 : return mSingleColor;
426 : : }
427 : 0 : }
428 : :
429 : 0 : QgsInterpolatedLineColor::ColoringMethod QgsInterpolatedLineColor::coloringMethod() const
430 : : {
431 : 0 : return mColoringMethod;
432 : : }
433 : :
434 : 0 : QgsColorRampShader QgsInterpolatedLineColor::colorRampShader() const
435 : : {
436 : 0 : return mColorRampShader;
437 : : }
438 : :
439 : 0 : QDomElement QgsInterpolatedLineColor::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
440 : : {
441 : 0 : Q_UNUSED( context );
442 : :
443 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "mesh-stroke-color" ) );
444 : :
445 : 0 : elem.setAttribute( QStringLiteral( "single-color" ), QgsSymbolLayerUtils::encodeColor( mSingleColor ) );
446 : 0 : elem.setAttribute( QStringLiteral( "coloring-method" ), mColoringMethod );
447 : 0 : elem.appendChild( mColorRampShader.writeXml( doc ) );
448 : :
449 : 0 : return elem;
450 : 0 : }
451 : :
452 : 0 : void QgsInterpolatedLineColor::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
453 : : {
454 : 0 : Q_UNUSED( context );
455 : :
456 : 0 : QDomElement shaderElem = elem.firstChildElement( QStringLiteral( "colorrampshader" ) );
457 : 0 : mColorRampShader.readXml( shaderElem );
458 : :
459 : 0 : mSingleColor = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "single-color" ) ) );
460 : 0 : mColoringMethod = static_cast<QgsInterpolatedLineColor::ColoringMethod>(
461 : 0 : elem.attribute( QStringLiteral( "coloring-method" ) ).toInt() );
462 : 0 : }
463 : :
464 : 0 : void QgsInterpolatedLineColor::graduatedColors( double value1, double value2, QList<double> &breakValues, QList<QColor> &breakColors, QList<QLinearGradient> &gradients ) const
465 : : {
466 : 0 : breakValues.clear();
467 : 0 : breakColors.clear();
468 : 0 : gradients.clear();
469 : 0 : if ( mColoringMethod == SingleColor )
470 : : {
471 : 0 : breakValues.append( value1 );
472 : 0 : breakColors.append( mSingleColor );
473 : 0 : breakValues.append( value2 );
474 : 0 : breakColors.append( mSingleColor );
475 : 0 : gradients.append( makeSimpleLinearGradient( mSingleColor, mSingleColor ) );
476 : 0 : return;
477 : : }
478 : :
479 : 0 : switch ( mColorRampShader.colorRampType() )
480 : : {
481 : : case QgsColorRampShader::Interpolated:
482 : 0 : graduatedColorsInterpolated( value1, value2, breakValues, breakColors, gradients );
483 : 0 : break;
484 : : case QgsColorRampShader::Discrete:
485 : 0 : graduatedColorsDiscrete( value1, value2, breakValues, breakColors, gradients );
486 : 0 : break;
487 : : case QgsColorRampShader::Exact:
488 : 0 : graduatedColorsExact( value1, value2, breakValues, breakColors, gradients );
489 : 0 : break;
490 : : }
491 : :
492 : 0 : }
493 : :
494 : 0 : QLinearGradient QgsInterpolatedLineColor::makeSimpleLinearGradient( const QColor &color1, const QColor &color2 ) const
495 : : {
496 : 0 : QLinearGradient gradient;
497 : 0 : gradient.setColorAt( 0, color1 );
498 : 0 : gradient.setColorAt( 1, color2 );
499 : :
500 : 0 : return gradient;
501 : 0 : }
502 : :
503 : 0 : int QgsInterpolatedLineColor::itemColorIndexInf( double value ) const
504 : : {
505 : 0 : QList<QgsColorRampShader::ColorRampItem> itemList = mColorRampShader.colorRampItemList();
506 : :
507 : 0 : if ( itemList.isEmpty() || itemList.first().value > value )
508 : 0 : return -1;
509 : :
510 : 0 : if ( mColorRampShader.colorRampType() == QgsColorRampShader::Discrete )
511 : 0 : itemList.removeLast(); //remove the inf value
512 : :
513 : 0 : if ( value > itemList.last().value )
514 : 0 : return itemList.count() - 1;
515 : :
516 : 0 : int indSup = itemList.count() - 1;
517 : 0 : int indInf = 0;
518 : :
519 : 0 : while ( true )
520 : : {
521 : 0 : if ( abs( indSup - indInf ) <= 1 ) //always indSup>indInf, but abs to prevent infinity loop
522 : 0 : return indInf;
523 : :
524 : 0 : int newInd = ( indInf + indSup ) / 2;
525 : :
526 : 0 : if ( itemList.at( newInd ).value == std::numeric_limits<double>::quiet_NaN() )
527 : 0 : return -1;
528 : :
529 : 0 : if ( itemList.at( newInd ).value <= value )
530 : 0 : indInf = newInd;
531 : : else
532 : 0 : indSup = newInd;
533 : : }
534 : 0 : }
535 : :
536 : 0 : void QgsInterpolatedLineColor::graduatedColorsExact( double value1, double value2, QList<double> &breakValues, QList<QColor> &breakColors, QList<QLinearGradient> &gradients ) const
537 : : {
538 : : Q_ASSERT( mColorRampShader.colorRampType() == QgsColorRampShader::Exact );
539 : : Q_ASSERT( breakValues.isEmpty() );
540 : : Q_ASSERT( breakColors.isEmpty() );
541 : : Q_ASSERT( gradients.isEmpty() );
542 : :
543 : 0 : const QList<QgsColorRampShader::ColorRampItem> &itemList = mColorRampShader.colorRampItemList();
544 : 0 : if ( itemList.isEmpty() )
545 : 0 : return;
546 : :
547 : 0 : int index = itemColorIndexInf( value1 );
548 : 0 : if ( index < 0 || !qgsDoubleNear( value1, itemList.at( index ).value ) )
549 : 0 : index++;
550 : :
551 : 0 : if ( qgsDoubleNear( value1, value2 ) && qgsDoubleNear( value1, itemList.at( index ).value ) )
552 : : {
553 : : //the two value are the same and are equal to the value in the item list --> render only one color
554 : 0 : breakColors.append( itemList.at( index ).color );
555 : 0 : return;
556 : : }
557 : :
558 : 0 : while ( index < itemList.count() && itemList.at( index ).value <= value2 )
559 : : {
560 : 0 : breakValues.append( itemList.at( index ).value );
561 : 0 : breakColors.append( itemList.at( index ).color );
562 : 0 : index++;
563 : : }
564 : 0 : }
565 : :
566 : 0 : void QgsInterpolatedLineColor::graduatedColorsInterpolated( double value1, double value2, QList<double> &breakValues, QList<QColor> &breakColors, QList<QLinearGradient> &gradients ) const
567 : : {
568 : : Q_ASSERT( mColorRampShader.colorRampType() == QgsColorRampShader::Interpolated );
569 : : Q_ASSERT( breakValues.isEmpty() );
570 : : Q_ASSERT( breakColors.isEmpty() );
571 : : Q_ASSERT( gradients.isEmpty() );
572 : :
573 : :
574 : 0 : const QList<QgsColorRampShader::ColorRampItem> &itemList = mColorRampShader.colorRampItemList();
575 : 0 : if ( itemList.empty() )
576 : 0 : return;
577 : :
578 : 0 : if ( itemList.count() == 1 )
579 : : {
580 : 0 : breakColors.append( itemList.first().color );
581 : 0 : return;
582 : : }
583 : :
584 : 0 : if ( value2 <= itemList.first().value ) // completely out of range and less
585 : : {
586 : 0 : if ( !mColorRampShader.clip() )
587 : 0 : breakColors.append( itemList.first().color ); // render only the first color in the whole range if not clipped
588 : 0 : return;
589 : : }
590 : :
591 : 0 : if ( value1 > itemList.last().value ) // completely out of range and greater
592 : : {
593 : 0 : if ( !mColorRampShader.clip() )
594 : 0 : breakColors.append( itemList.last().color ); // render only the last color in the whole range if not clipped
595 : 0 : return;
596 : : }
597 : :
598 : 0 : if ( qgsDoubleNear( value1, value2 ) )
599 : : {
600 : : // the two values are the same
601 : : // --> render only one color
602 : : int r, g, b, a;
603 : 0 : QColor color;
604 : 0 : if ( mColorRampShader.shade( value1, &r, &g, &b, &a ) )
605 : 0 : color = QColor( r, g, b, a );
606 : 0 : breakColors.append( color );
607 : 0 : return;
608 : : }
609 : :
610 : : // index of the inf value of the interval where value1 is in the color ramp shader
611 : 0 : int index = itemColorIndexInf( value1 );
612 : 0 : if ( index < 0 ) // value1 out of range
613 : : {
614 : 0 : QColor color = itemList.first().color;
615 : 0 : breakColors.append( color );
616 : 0 : if ( mColorRampShader.clip() ) // The first value/color returned is the first of the item list
617 : 0 : breakValues.append( itemList.first().value );
618 : : else // The first value/color returned is the first color of the item list and value1
619 : 0 : breakValues.append( value1 );
620 : 0 : }
621 : : else
622 : : {
623 : : // shade the color
624 : : int r, g, b, a;
625 : 0 : QColor color;
626 : 0 : if ( mColorRampShader.shade( value1, &r, &g, &b, &a ) )
627 : 0 : color = QColor( r, g, b, a );
628 : 0 : breakValues.append( value1 );
629 : 0 : breakColors.append( color );
630 : : }
631 : :
632 : 0 : index++; // increment the index before go through the intervals
633 : :
634 : 0 : while ( index < itemList.count() && itemList.at( index ).value < value2 )
635 : : {
636 : 0 : QColor color1 = breakColors.last();
637 : 0 : QColor color2 = itemList.at( index ).color;
638 : 0 : breakValues.append( itemList.at( index ).value );
639 : 0 : breakColors.append( color2 );
640 : 0 : gradients.append( makeSimpleLinearGradient( color1, color2 ) );
641 : 0 : index++;
642 : : }
643 : :
644 : : // close the lists with value2 or last item if >value2
645 : 0 : QColor color1 = breakColors.last();
646 : 0 : QColor color2;
647 : 0 : if ( value2 < itemList.last().value )
648 : : {
649 : : int r, g, b, a;
650 : 0 : if ( mColorRampShader.shade( value2, &r, &g, &b, &a ) )
651 : 0 : color2 = QColor( r, g, b, a );
652 : 0 : breakValues.append( value2 );
653 : 0 : }
654 : : else
655 : : {
656 : 0 : color2 = itemList.last().color;
657 : 0 : if ( mColorRampShader.clip() )
658 : 0 : breakValues.append( itemList.last().value );
659 : : else
660 : 0 : breakValues.append( value2 );
661 : : }
662 : 0 : breakColors.append( color2 );
663 : 0 : gradients.append( makeSimpleLinearGradient( color1, color2 ) );
664 : 0 : }
665 : :
666 : :
667 : 0 : void QgsInterpolatedLineColor::graduatedColorsDiscrete( double value1, double value2, QList<double> &breakValues, QList<QColor> &breakColors, QList<QLinearGradient> &gradients ) const
668 : : {
669 : : Q_ASSERT( mColorRampShader.colorRampType() == QgsColorRampShader::Discrete );
670 : : Q_ASSERT( breakValues.isEmpty() );
671 : : Q_ASSERT( breakColors.isEmpty() );
672 : : Q_ASSERT( gradients.isEmpty() );
673 : :
674 : 0 : const QList<QgsColorRampShader::ColorRampItem> &itemList = mColorRampShader.colorRampItemList();
675 : 0 : if ( itemList.empty() )
676 : 0 : return;
677 : :
678 : 0 : if ( itemList.count() == 1 )
679 : : {
680 : 0 : breakColors.append( itemList.first().color );
681 : 0 : return;
682 : : }
683 : :
684 : 0 : double lastValue = itemList.at( itemList.count() - 2 ).value;
685 : :
686 : :
687 : 0 : if ( value2 <= itemList.first().value ) // completely out of range and less
688 : : {
689 : 0 : breakColors.append( itemList.first().color ); // render only the first color in the whole range
690 : 0 : return;
691 : : }
692 : :
693 : 0 : if ( value1 > lastValue ) // completely out of range and greater
694 : : {
695 : 0 : breakColors.append( itemList.last().color ); // render only the last color in the whole range
696 : 0 : return;
697 : : }
698 : :
699 : : // index of the inf value of the interval where value1 is in the color ramp shader
700 : 0 : int index = itemColorIndexInf( value1 );
701 : :
702 : 0 : if ( qgsDoubleNear( value1, value2 ) )
703 : : {
704 : : // the two values are the same and are equal to the value in the item list
705 : : // --> render only one color, the sup one
706 : 0 : breakColors.append( itemList.at( index + 1 ).color );
707 : 0 : return;
708 : : }
709 : :
710 : 0 : if ( index < 0 ) // value1 out of range
711 : : {
712 : 0 : breakValues.append( value1 );
713 : 0 : breakColors.append( itemList.first().color );
714 : 0 : }
715 : : else // append the first value with corresponding color
716 : : {
717 : 0 : QColor color = itemList.at( index ).color;
718 : 0 : breakValues.append( value1 );
719 : 0 : breakColors.append( color );
720 : : }
721 : :
722 : 0 : index++; // increment the index before go through the intervals
723 : :
724 : 0 : while ( index < ( itemList.count() - 1 ) && itemList.at( index ).value < value2 )
725 : : {
726 : 0 : QColor color = itemList.at( index ).color;
727 : 0 : breakValues.append( itemList.at( index ).value );
728 : 0 : breakColors.append( color );
729 : 0 : gradients.append( makeSimpleLinearGradient( color, color ) );
730 : 0 : index++;
731 : : }
732 : :
733 : : // add value2 to close
734 : 0 : QColor lastColor = itemList.at( index ).color;
735 : 0 : breakColors.append( lastColor );
736 : 0 : breakValues.append( value2 );
737 : 0 : gradients.append( makeSimpleLinearGradient( lastColor, lastColor ) );
738 : :
739 : 0 : }
|