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