QGIS API Documentation  3.27.0-Master (aef1b1ec20)
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 "qgscolorramplegendnode.h"
20 #include "qgssymbollayerutils.h"
21 #include "qgsstyle.h"
22 
23 
25 {
26  mStrokeWidth = strokeWidth;
27 }
28 
30 {
31  return mStrokeWidth;
32 }
33 
35 {
36  mStrokeColoring = strokeColoring;
37 }
38 
40 {
41  return mStrokeColoring;
42 }
43 
45 {
46  mStrokeWidthUnit = strokeWidthUnit;
47 }
48 
50 {
51  return mStrokeWidthUnit;
52 }
53 
54 void QgsInterpolatedLineRenderer::renderInDeviceCoordinates( double valueColor1, double valueColor2, double valueWidth1, double valueWidth2, QPointF p1, QPointF p2, QgsRenderContext &context ) const
55 {
56  QPainter *painter = context.painter();
57  QgsScopedQPainterState painterState( painter );
58  context.setPainterFlagsUsingContext( painter );
59 
60  QPointF dir = p2 - p1;
61  double length = sqrt( pow( dir.x(), 2 ) + pow( dir.y(), 2 ) );
62  QPointF diru = dir / length;
63  QPointF orthu = QPointF( -diru.y(), diru.x() );
64 
65  QList<double> breakValues;
66  QList<QColor> breakColors;
67  QList<QLinearGradient> gradients;
68 
69  mStrokeColoring.graduatedColors( valueColor1, valueColor2, breakValues, breakColors, gradients );
70  QColor selectedColor = context.selectionColor();
71 
72  if ( gradients.isEmpty() && !breakValues.empty() && !breakColors.isEmpty() ) //exact colors to render
73  {
74  Q_ASSERT( breakColors.count() == breakValues.count() );
75  for ( int i = 0; i < breakValues.count(); ++i )
76  {
77  const bool widthIsInverted { valueWidth1 > valueWidth2 };
78  const double value = breakValues.at( i );
79  const double width = context.convertToPainterUnits( mStrokeWidth.strokeWidth( widthIsInverted ? mStrokeWidth.maximumValue() - value : value ), mStrokeWidthUnit );
80  QPen pen( mSelected ? selectedColor : breakColors.at( i ) );
81  pen.setWidthF( width );
82  pen.setCapStyle( Qt::PenCapStyle::RoundCap );
83  painter->setPen( pen );
84  const QPointF point = p1 + dir * ( value - valueColor1 ) / ( valueColor2 - valueColor1 );
85  painter->drawPoint( point );
86  }
87  }
88  else
89  {
90  double width1 = mStrokeWidth.strokeWidth( valueWidth1 );
91  double width2 = mStrokeWidth.strokeWidth( valueWidth2 );
92 
93  if ( !std::isnan( width1 ) && !std::isnan( width2 ) ) // the two widths on extremity are not out of range and ignored
94  {
95  //Draw line cap
96  QBrush brush( Qt::SolidPattern );
97  QPen pen;
98  int startAngle;
99  startAngle = ( acos( -orthu.x() ) / M_PI ) * 180;
100  if ( orthu.y() < 0 )
101  startAngle = 360 - startAngle;
102 
103  bool outOfRange1 = std::isnan( width1 );
104  bool outOfRange2 = std::isnan( width2 );
105 
106  if ( !outOfRange1 )
107  {
108  width1 = context.convertToPainterUnits( width1, mStrokeWidthUnit );
109  QRectF capBox1( p1.x() - width1 / 2, p1.y() - width1 / 2, width1, width1 );
110  brush.setColor( mSelected ? selectedColor : mStrokeColoring.color( valueColor1 ) );
111  painter->setBrush( brush );
112  pen.setBrush( brush );
113  painter->setPen( pen );
114  painter->drawPie( capBox1, ( startAngle - 1 ) * 16, 182 * 16 );
115  }
116 
117  if ( !outOfRange2 )
118  {
119  width2 = context.convertToPainterUnits( width2, mStrokeWidthUnit ) ;
120  QRectF capBox2( p2.x() - width2 / 2, p2.y() - width2 / 2, width2, width2 );
121  brush.setColor( mSelected ? selectedColor : mStrokeColoring.color( valueColor2 ) );
122  pen.setBrush( brush );
123  painter->setBrush( brush );
124  painter->setPen( pen );
125  painter->drawPie( capBox2, ( startAngle + 179 ) * 16, 182 * 16 );
126  }
127 
128  if ( ( gradients.isEmpty() && breakValues.empty() && breakColors.count() == 1 ) || mSelected ) //only one color to render
129  {
130  double startAdjusting = 0;
131  if ( outOfRange1 )
132  adjustLine( valueColor1, valueColor1, valueColor2, width1, startAdjusting );
133 
134 
135  double endAdjusting = 0;
136  if ( outOfRange2 )
137  adjustLine( valueColor2, valueColor1, valueColor2, width2, endAdjusting );
138 
139  QPointF pointStartAdjusted = p1 + dir * startAdjusting;
140  QPointF pointEndAdjusted = p2 - dir * endAdjusting;
141 
142  QPolygonF varLine;
143  double semiWidth1 = width1 / 2;
144  double semiWidth2 = width2 / 2;
145 
146  varLine.append( pointStartAdjusted + orthu * semiWidth1 );
147  varLine.append( pointEndAdjusted + orthu * semiWidth2 );
148  varLine.append( pointEndAdjusted - orthu * semiWidth2 );
149  varLine.append( pointStartAdjusted - orthu * semiWidth1 );
150 
151  QBrush brush( Qt::SolidPattern );
152  brush.setColor( mSelected ? selectedColor : breakColors.first() );
153  painter->setBrush( brush );
154  painter->setPen( pen );
155 
156  QPen pen;
157  pen.setBrush( brush );
158  pen.setWidthF( 0 );
159  painter->setPen( pen );
160 
161  painter->drawPolygon( varLine );
162 
163  }
164  else if ( !gradients.isEmpty() && !breakValues.isEmpty() && !breakColors.isEmpty() )
165  {
166  Q_ASSERT( breakColors.count() == breakValues.count() );
167  Q_ASSERT( breakColors.count() == gradients.count() + 1 );
168  double widthColorVariationValueRatio = ( valueWidth2 - valueWidth1 ) / ( valueColor2 - valueColor1 );
169 
170  for ( int i = 0; i < gradients.count(); ++i )
171  {
172  double firstValue = breakValues.at( i );
173  double secondValue = breakValues.at( i + 1 );
174  double w1 = mStrokeWidth.strokeWidth( widthColorVariationValueRatio * ( firstValue - valueColor1 ) + valueWidth1 );
175  double w2 = mStrokeWidth.strokeWidth( widthColorVariationValueRatio * ( secondValue - valueColor1 ) + valueWidth1 );
176 
177  if ( std::isnan( w1 ) && std::isnan( w2 ) )
178  continue;
179 
180  double firstAdjusting = 0;
181  if ( std::isnan( w1 ) )
182  adjustLine( firstValue, valueColor1, valueColor2, w1, firstAdjusting );
183 
184 
185  double secondAdjusting = 0;
186  if ( std::isnan( w2 ) )
187  adjustLine( secondValue, valueColor1, valueColor2, w2, secondAdjusting );
188 
189  w1 = context.convertToPainterUnits( w1, mStrokeWidthUnit );
190  w2 = context.convertToPainterUnits( w2, mStrokeWidthUnit ) ;
191 
192  QPointF pointStart = p1 + dir * ( firstValue - valueColor1 ) / ( valueColor2 - valueColor1 );
193  QPointF pointEnd = p1 + dir * ( secondValue - valueColor1 ) / ( valueColor2 - valueColor1 );
194 
195  QPointF pointStartAdjusted = pointStart + dir * firstAdjusting;
196  QPointF pointEndAdjusted = pointEnd - dir * secondAdjusting;
197 
198  QPolygonF varLine;
199  double sw1 = w1 / 2;
200  double sw2 = w2 / 2;
201 
202  varLine.append( pointStartAdjusted + orthu * sw1 );
203  varLine.append( pointEndAdjusted + orthu * sw2 );
204  varLine.append( pointEndAdjusted - orthu * sw2 );
205  varLine.append( pointStartAdjusted - orthu * sw1 );
206 
207  QLinearGradient gradient = gradients.at( i );
208  gradient.setStart( pointStart );
209  gradient.setFinalStop( pointEnd );
210  QBrush brush( gradient );
211  painter->setBrush( brush );
212 
213  QPen pen;
214  pen.setBrush( brush );
215  pen.setWidthF( 0 );
216  painter->setPen( pen );
217 
218  painter->drawPolygon( varLine );
219  }
220  }
221  }
222  }
223 }
224 
225 
226 void QgsInterpolatedLineRenderer::render( double value1, double value2, const QgsPointXY &pt1, const QgsPointXY &pt2, QgsRenderContext &context ) const
227 {
228  const QgsMapToPixel &mapToPixel = context.mapToPixel();
229 
230  QgsPointXY point1 = pt1;
231  QgsPointXY point2 = pt2;
232 
233  if ( value1 > value2 )
234  {
235  std::swap( value1, value2 );
236  std::swap( point1, point2 );
237  }
238 
239  QPointF p1 = mapToPixel.transform( point1 ).toQPointF();
240  QPointF p2 = mapToPixel.transform( point2 ).toQPointF();
241 
242  renderInDeviceCoordinates( value1, value2, value1, value2, p1, p2, context );
243 }
244 
245 void QgsInterpolatedLineRenderer::render( double valueColor1, double valueColor2, double valueWidth1, double valueWidth2, const QgsPointXY &pt1, const QgsPointXY &pt2, QgsRenderContext &context ) const
246 {
247  const QgsMapToPixel &mapToPixel = context.mapToPixel();
248 
249  QgsPointXY point1 = pt1;
250  QgsPointXY point2 = pt2;
251 
252  if ( valueColor1 > valueColor2 )
253  {
254  std::swap( valueColor1, valueColor2 );
255  std::swap( valueWidth1, valueWidth2 );
256  std::swap( point1, point2 );
257  }
258 
259  QPointF p1 = mapToPixel.transform( point1 ).toQPointF();
260  QPointF p2 = mapToPixel.transform( point2 ).toQPointF();
261 
262  renderInDeviceCoordinates( valueColor1, valueColor2, valueWidth1, valueWidth2, p1, p2, context );
263 }
264 
266 {
267  mSelected = selected;
268 }
269 
270 void QgsInterpolatedLineRenderer::adjustLine( const double value, const double value1, const double value2, double &width, double &adjusting ) const
271 {
272  if ( value > mStrokeWidth.maximumValue() )
273  {
274  adjusting = fabs( ( value - mStrokeWidth.maximumValue() ) / ( value2 - value1 ) );
275  width = mStrokeWidth.maximumWidth();
276  }
277  else
278  {
279  adjusting = fabs( ( value - mStrokeWidth.minimumValue() ) / ( value2 - value1 ) );
280  width = mStrokeWidth.minimumWidth();
281  }
282 }
283 
285 {
286  return mMinimumValue;
287 }
288 
289 void QgsInterpolatedLineWidth::setMinimumValue( double minimumValue )
290 {
291  mMinimumValue = minimumValue;
292  mNeedUpdateFormula = true;
293 }
294 
296 {
297  return mMaximumValue;
298 }
299 
300 void QgsInterpolatedLineWidth::setMaximumValue( double maximumValue )
301 {
302  mMaximumValue = maximumValue;
303  mNeedUpdateFormula = true;
304 }
305 
307 {
308  return mMinimumWidth;
309 }
310 
311 void QgsInterpolatedLineWidth::setMinimumWidth( double minimumWidth )
312 {
313  mMinimumWidth = minimumWidth;
314  mNeedUpdateFormula = true;
315 }
316 
318 {
319  return mMaximumWidth;
320 }
321 
322 void QgsInterpolatedLineWidth::setMaximumWidth( double maximumWidth )
323 {
324  mMaximumWidth = maximumWidth;
325  mNeedUpdateFormula = true;
326 }
327 
328 double QgsInterpolatedLineWidth::strokeWidth( double value ) const
329 {
330  if ( mIsWidthVariable )
331  {
332  if ( mNeedUpdateFormula )
333  updateLinearFormula();
334 
335  if ( mUseAbsoluteValue )
336  value = std::fabs( value );
337 
338  if ( value > mMaximumValue )
339  {
340  if ( mIgnoreOutOfRange )
341  return std::numeric_limits<double>::quiet_NaN();
342  else
343  return mMaximumWidth;
344  }
345 
346  if ( value < mMinimumValue )
347  {
348  if ( mIgnoreOutOfRange )
349  return std::numeric_limits<double>::quiet_NaN();
350  else
351  return mMinimumWidth;
352  }
353 
354  return ( value - mMinimumValue ) * mLinearCoef + mMinimumWidth;
355  }
356  else
357  return fixedStrokeWidth();
358 }
359 
360 QDomElement QgsInterpolatedLineWidth::writeXml( QDomDocument &doc, const QgsReadWriteContext & ) const
361 {
362  QDomElement elem = doc.createElement( QStringLiteral( "mesh-stroke-width" ) );
363 
364  elem.setAttribute( QStringLiteral( "width-varying" ), mIsWidthVariable ? 1 : 0 );
365  elem.setAttribute( QStringLiteral( "fixed-width" ), mFixedWidth );
366  elem.setAttribute( QStringLiteral( "minimum-value" ), mMinimumValue );
367  elem.setAttribute( QStringLiteral( "maximum-value" ), mMaximumValue );
368  elem.setAttribute( QStringLiteral( "minimum-width" ), mMinimumWidth );
369  elem.setAttribute( QStringLiteral( "maximum-width" ), mMaximumWidth );
370  elem.setAttribute( QStringLiteral( "ignore-out-of-range" ), mIgnoreOutOfRange ? 1 : 0 );
371  elem.setAttribute( QStringLiteral( "use-absolute-value" ), mUseAbsoluteValue ? 1 : 0 );
372 
373  return elem;
374 }
375 
376 void QgsInterpolatedLineWidth::readXml( const QDomElement &elem, const QgsReadWriteContext & )
377 {
378  mIsWidthVariable = elem.attribute( QStringLiteral( "width-varying" ) ).toInt();
379  mFixedWidth = elem.attribute( QStringLiteral( "fixed-width" ) ).toDouble();
380  mMinimumValue = elem.attribute( QStringLiteral( "minimum-value" ) ).toDouble();
381  mMaximumValue = elem.attribute( QStringLiteral( "maximum-value" ) ).toDouble();
382  mMinimumWidth = elem.attribute( QStringLiteral( "minimum-width" ) ).toDouble();
383  mMaximumWidth = elem.attribute( QStringLiteral( "maximum-width" ) ).toDouble();
384  mIgnoreOutOfRange = elem.attribute( QStringLiteral( "ignore-out-of-range" ) ).toInt();
385  mUseAbsoluteValue = elem.attribute( QStringLiteral( "use-absolute-value" ) ).toInt();
386 }
387 
389 {
390  return mUseAbsoluteValue;
391 }
392 
394 {
395  mUseAbsoluteValue = useAbsoluteValue;
396 }
397 
399 {
400  return mFixedWidth;
401 }
402 
404 {
405  return mIgnoreOutOfRange;
406 }
407 
409 {
410  mIgnoreOutOfRange = ignoreOutOfRange;
411 }
412 
414 {
415  return mIsWidthVariable;
416 }
417 
419 {
420  mIsWidthVariable = isWidthVarying;
421 }
422 
424 {
425  mFixedWidth = fixedWidth;
426 }
427 
428 void QgsInterpolatedLineWidth::updateLinearFormula() const
429 {
430  if ( !qgsDoubleNear( mMaximumWidth, mMinimumWidth ) )
431  mLinearCoef = ( mMaximumWidth - mMinimumWidth ) / ( mMaximumValue - mMinimumValue ) ;
432  else
433  mLinearCoef = 0;
434  mNeedUpdateFormula = false;
435 }
436 
438 {
439  mColorRampShader.setMinimumValue( std::numeric_limits<double>::quiet_NaN() );
440  mColorRampShader.setMaximumValue( std::numeric_limits<double>::quiet_NaN() );
441 }
442 
444 {
446 }
447 
449 {
450  setColor( color );
451  mColoringMethod = SingleColor;
452  mColorRampShader.setMinimumValue( std::numeric_limits<double>::quiet_NaN() );
453  mColorRampShader.setMaximumValue( std::numeric_limits<double>::quiet_NaN() );
454 }
455 
457 {
458  mColorRampShader = colorRampShader;
459  if ( ( mColorRampShader.sourceColorRamp() ) )
460  mColoringMethod = ColorRamp;
461  else
462  mColoringMethod = SingleColor;
463 }
464 
465 void QgsInterpolatedLineColor::setColor( const QColor &color )
466 {
467  mSingleColor = color;
468 }
469 
470 QColor QgsInterpolatedLineColor::color( double magnitude ) const
471 {
472  QgsColorRamp *lSourceColorRamp = mColorRampShader.sourceColorRamp();
473  if ( mColoringMethod == ColorRamp && lSourceColorRamp )
474  {
475  if ( mColorRampShader.isEmpty() )
476  return lSourceColorRamp->color( 0 );
477 
478  int r, g, b, a;
479  if ( mColorRampShader.shade( magnitude, &r, &g, &b, &a ) )
480  return QColor( r, g, b, a );
481  else
482  return QColor( 0, 0, 0, 0 );
483  }
484  else
485  {
486  return mSingleColor;
487  }
488 }
489 
491 {
492  return mColoringMethod;
493 }
494 
496 {
497  return mColorRampShader;
498 }
499 
501 {
502  return mSingleColor;
503 }
504 
505 QDomElement QgsInterpolatedLineColor::writeXml( QDomDocument &doc, const QgsReadWriteContext & ) const
506 {
507  QDomElement elem = doc.createElement( QStringLiteral( "mesh-stroke-color" ) );
508 
509  elem.setAttribute( QStringLiteral( "single-color" ), QgsSymbolLayerUtils::encodeColor( mSingleColor ) );
510  elem.setAttribute( QStringLiteral( "coloring-method" ), mColoringMethod );
511  elem.appendChild( mColorRampShader.writeXml( doc ) );
512 
513  return elem;
514 }
515 
516 void QgsInterpolatedLineColor::readXml( const QDomElement &elem, const QgsReadWriteContext & )
517 {
518  QDomElement shaderElem = elem.firstChildElement( QStringLiteral( "colorrampshader" ) );
519  mColorRampShader.readXml( shaderElem );
520 
521  mSingleColor = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "single-color" ) ) );
522  mColoringMethod = static_cast<QgsInterpolatedLineColor::ColoringMethod>(
523  elem.attribute( QStringLiteral( "coloring-method" ) ).toInt() );
524 }
525 
526 void QgsInterpolatedLineColor::graduatedColors( double value1, double value2, QList<double> &breakValues, QList<QColor> &breakColors, QList<QLinearGradient> &gradients ) const
527 {
528  breakValues.clear();
529  breakColors.clear();
530  gradients.clear();
531  if ( mColoringMethod == SingleColor )
532  {
533  breakColors.append( mSingleColor );
534  return;
535  }
536 
537  switch ( mColorRampShader.colorRampType() )
538  {
540  graduatedColorsInterpolated( value1, value2, breakValues, breakColors, gradients );
541  break;
543  graduatedColorsDiscrete( value1, value2, breakValues, breakColors, gradients );
544  break;
546  graduatedColorsExact( value1, value2, breakValues, breakColors, gradients );
547  break;
548  }
549 
550 }
551 
553 {
554  mColoringMethod = coloringMethod;
555 }
556 
557 QLinearGradient QgsInterpolatedLineColor::makeSimpleLinearGradient( const QColor &color1, const QColor &color2 ) const
558 {
559  QLinearGradient gradient;
560  gradient.setColorAt( 0, color1 );
561  gradient.setColorAt( 1, color2 );
562 
563  return gradient;
564 }
565 
566 int QgsInterpolatedLineColor::itemColorIndexInf( double value ) const
567 {
568  QList<QgsColorRampShader::ColorRampItem> itemList = mColorRampShader.colorRampItemList();
569 
570  if ( itemList.isEmpty() || itemList.first().value > value )
571  return -1;
572 
573  if ( mColorRampShader.colorRampType() == QgsColorRampShader::Discrete )
574  itemList.removeLast(); //remove the inf value
575 
576  if ( value > itemList.last().value )
577  return itemList.count() - 1;
578 
579  int indSup = itemList.count() - 1;
580  int indInf = 0;
581 
582  while ( true )
583  {
584  if ( abs( indSup - indInf ) <= 1 ) //always indSup>indInf, but abs to prevent infinity loop
585  return indInf;
586 
587  int newInd = ( indInf + indSup ) / 2;
588 
589  if ( std::isnan( itemList.at( newInd ).value ) )
590  return -1;
591 
592  if ( itemList.at( newInd ).value <= value )
593  indInf = newInd;
594  else
595  indSup = newInd;
596  }
597 }
598 
599 void QgsInterpolatedLineColor::graduatedColorsExact( double value1, double value2, QList<double> &breakValues, QList<QColor> &breakColors, const QList<QLinearGradient> &gradients ) const
600 {
601  Q_ASSERT( mColorRampShader.colorRampType() == QgsColorRampShader::Exact );
602  Q_ASSERT( breakValues.isEmpty() );
603  Q_ASSERT( breakColors.isEmpty() );
604  Q_ASSERT( gradients.isEmpty() );
605 
606  const QList<QgsColorRampShader::ColorRampItem> &itemList = mColorRampShader.colorRampItemList();
607  if ( itemList.isEmpty() )
608  return;
609 
610  int index = itemColorIndexInf( value1 );
611  if ( index < 0 || !qgsDoubleNear( value1, itemList.at( index ).value ) )
612  index++;
613 
614  if ( qgsDoubleNear( value1, value2 ) && qgsDoubleNear( value1, itemList.at( index ).value ) )
615  {
616  //the two value are the same and are equal to the value in the item list --> render only one color
617  breakColors.append( itemList.at( index ).color );
618  return;
619  }
620 
621  while ( index < itemList.count() && itemList.at( index ).value <= value2 )
622  {
623  breakValues.append( itemList.at( index ).value );
624  breakColors.append( itemList.at( index ).color );
625  index++;
626  }
627 }
628 
629 void QgsInterpolatedLineColor::graduatedColorsInterpolated( double value1, double value2, QList<double> &breakValues, QList<QColor> &breakColors, QList<QLinearGradient> &gradients ) const
630 {
631  Q_ASSERT( mColorRampShader.colorRampType() == QgsColorRampShader::Interpolated );
632  Q_ASSERT( breakValues.isEmpty() );
633  Q_ASSERT( breakColors.isEmpty() );
634  Q_ASSERT( gradients.isEmpty() );
635 
636 
637  const QList<QgsColorRampShader::ColorRampItem> &itemList = mColorRampShader.colorRampItemList();
638  if ( itemList.empty() )
639  return;
640 
641  if ( itemList.count() == 1 )
642  {
643  breakColors.append( itemList.first().color );
644  return;
645  }
646 
647  if ( value2 <= itemList.first().value ) // completely out of range and less
648  {
649  if ( !mColorRampShader.clip() )
650  breakColors.append( itemList.first().color ); // render only the first color in the whole range if not clipped
651  return;
652  }
653 
654  if ( value1 > itemList.last().value ) // completely out of range and greater
655  {
656  if ( !mColorRampShader.clip() )
657  breakColors.append( itemList.last().color ); // render only the last color in the whole range if not clipped
658  return;
659  }
660 
661  if ( qgsDoubleNear( value1, value2 ) )
662  {
663  // the two values are the same
664  // --> render only one color
665  int r, g, b, a;
666  QColor color;
667  if ( mColorRampShader.shade( value1, &r, &g, &b, &a ) )
668  color = QColor( r, g, b, a );
669  breakColors.append( color );
670  return;
671  }
672 
673  // index of the inf value of the interval where value1 is in the color ramp shader
674  int index = itemColorIndexInf( value1 );
675  if ( index < 0 ) // value1 out of range
676  {
677  QColor color = itemList.first().color;
678  breakColors.append( color );
679  if ( mColorRampShader.clip() ) // The first value/color returned is the first of the item list
680  breakValues.append( itemList.first().value );
681  else // The first value/color returned is the first color of the item list and value1
682  breakValues.append( value1 );
683  }
684  else
685  {
686  // shade the color
687  int r, g, b, a;
688  QColor color;
689  if ( mColorRampShader.shade( value1, &r, &g, &b, &a ) )
690  color = QColor( r, g, b, a );
691  breakValues.append( value1 );
692  breakColors.append( color );
693  }
694 
695  index++; // increment the index before go through the intervals
696 
697  while ( index < itemList.count() && itemList.at( index ).value < value2 )
698  {
699  QColor color1 = breakColors.last();
700  QColor color2 = itemList.at( index ).color;
701  breakValues.append( itemList.at( index ).value );
702  breakColors.append( color2 );
703  gradients.append( makeSimpleLinearGradient( color1, color2 ) );
704  index++;
705  }
706 
707  // close the lists with value2 or last item if >value2
708  QColor color1 = breakColors.last();
709  QColor color2;
710  if ( value2 < itemList.last().value )
711  {
712  int r, g, b, a;
713  if ( mColorRampShader.shade( value2, &r, &g, &b, &a ) )
714  color2 = QColor( r, g, b, a );
715  breakValues.append( value2 );
716  }
717  else
718  {
719  color2 = itemList.last().color;
720  if ( mColorRampShader.clip() )
721  breakValues.append( itemList.last().value );
722  else
723  breakValues.append( value2 );
724  }
725  breakColors.append( color2 );
726  gradients.append( makeSimpleLinearGradient( color1, color2 ) );
727 }
728 
729 
730 void QgsInterpolatedLineColor::graduatedColorsDiscrete( double value1, double value2, QList<double> &breakValues, QList<QColor> &breakColors, QList<QLinearGradient> &gradients ) const
731 {
732  Q_ASSERT( mColorRampShader.colorRampType() == QgsColorRampShader::Discrete );
733  Q_ASSERT( breakValues.isEmpty() );
734  Q_ASSERT( breakColors.isEmpty() );
735  Q_ASSERT( gradients.isEmpty() );
736 
737  const QList<QgsColorRampShader::ColorRampItem> &itemList = mColorRampShader.colorRampItemList();
738  if ( itemList.empty() )
739  return;
740 
741  if ( itemList.count() == 1 )
742  {
743  breakColors.append( itemList.first().color );
744  return;
745  }
746 
747  double lastValue = itemList.at( itemList.count() - 2 ).value;
748 
749 
750  if ( value2 <= itemList.first().value ) // completely out of range and less
751  {
752  breakColors.append( itemList.first().color ); // render only the first color in the whole range
753  return;
754  }
755 
756  if ( value1 > lastValue ) // completely out of range and greater
757  {
758  breakColors.append( itemList.last().color ); // render only the last color in the whole range
759  return;
760  }
761 
762  // index of the inf value of the interval where value1 is in the color ramp shader
763  int index = itemColorIndexInf( value1 );
764 
765  if ( qgsDoubleNear( value1, value2 ) )
766  {
767  // the two values are the same and are equal to the value in the item list
768  // --> render only one color, the sup one
769  breakColors.append( itemList.at( index + 1 ).color );
770  return;
771  }
772 
773  if ( index < 0 ) // value1 out of range
774  {
775  breakValues.append( value1 );
776  breakColors.append( itemList.first().color );
777  }
778  else // append the first value with corresponding color
779  {
780  QColor color = itemList.at( index ).color;
781  breakValues.append( value1 );
782  breakColors.append( color );
783  }
784 
785  index++; // increment the index before go through the intervals
786 
787  while ( index < ( itemList.count() - 1 ) && itemList.at( index ).value < value2 )
788  {
789  QColor color = itemList.at( index ).color;
790  breakValues.append( itemList.at( index ).value );
791  breakColors.append( color );
792  gradients.append( makeSimpleLinearGradient( color, color ) );
793  index++;
794  }
795 
796  // add value2 to close
797  QColor lastColor = itemList.at( index ).color;
798  breakColors.append( lastColor );
799  breakValues.append( value2 );
800  gradients.append( makeSimpleLinearGradient( lastColor, lastColor ) );
801 
802 }
803 
804 QString QgsInterpolatedLineSymbolLayer::layerType() const {return QStringLiteral( "InterpolatedLine" );}
805 
807 {
808 }
809 
811 {
812 }
813 
815 {
818  copyPaintEffect( l );
819  return l;
820 }
821 
823 {
824  std::unique_ptr<QgsInterpolatedLineSymbolLayer> symbolLayer;
825  symbolLayer.reset( new QgsInterpolatedLineSymbolLayer() );
826 
827  if ( properties.contains( QStringLiteral( "start_width_expression" ) ) )
828  symbolLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyLineStartWidthValue, QgsProperty::fromExpression( properties.value( QStringLiteral( "start_width_expression" ) ).toString() ) );
829  if ( properties.contains( QStringLiteral( "end_width_expression" ) ) )
830  symbolLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyLineEndWidthValue, QgsProperty::fromExpression( properties.value( QStringLiteral( "end_width_expression" ) ).toString() ) );
831 
832  if ( properties.contains( QStringLiteral( "start_color_expression" ) ) )
833  symbolLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyLineStartColorValue, QgsProperty::fromExpression( properties.value( QStringLiteral( "start_color_expression" ) ).toString() ) );
834  if ( properties.contains( QStringLiteral( "end_color_expression" ) ) )
835  symbolLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyLineEndColorValue, QgsProperty::fromExpression( properties.value( QStringLiteral( "end_color_expression" ) ).toString() ) );
836 
837  if ( properties.contains( QStringLiteral( "line_width" ) ) )
838  symbolLayer->mLineRender.mStrokeWidth.setFixedStrokeWidth( properties.value( QStringLiteral( "line_width" ) ).toDouble() ) ;
839  if ( properties.contains( QStringLiteral( "line_width_unit" ) ) )
840  symbolLayer->mLineRender.setWidthUnit( QgsUnitTypes::decodeRenderUnit( properties.value( QStringLiteral( "line_width_unit" ) ).toString() ) );
841  if ( properties.contains( QStringLiteral( "width_varying_minimum_value" ) ) )
842  symbolLayer->mLineRender.mStrokeWidth.setMinimumValue( properties.value( QStringLiteral( "width_varying_minimum_value" ) ).toDouble() );
843  if ( properties.contains( QStringLiteral( "width_varying_maximum_value" ) ) )
844  symbolLayer->mLineRender.mStrokeWidth.setMaximumValue( properties.value( QStringLiteral( "width_varying_maximum_value" ) ).toDouble() );
845  if ( properties.contains( QStringLiteral( "width_varying_use_absolute_value" ) ) )
846  symbolLayer->mLineRender.mStrokeWidth.setUseAbsoluteValue( properties.value( QStringLiteral( "width_varying_use_absolute_value" ) ).toInt() );
847  if ( properties.contains( QStringLiteral( "width_varying_minimum_width" ) ) )
848  symbolLayer->mLineRender.mStrokeWidth.setMinimumWidth( properties.value( QStringLiteral( "width_varying_minimum_width" ) ).toDouble() );
849  if ( properties.contains( QStringLiteral( "width_varying_maximum_width" ) ) )
850  symbolLayer->mLineRender.mStrokeWidth.setMaximumWidth( properties.value( QStringLiteral( "width_varying_maximum_width" ) ).toDouble() );
851  if ( properties.contains( QStringLiteral( "width_varying_ignore_out_of_range" ) ) )
852  symbolLayer->mLineRender.mStrokeWidth.setIgnoreOutOfRange( properties.value( QStringLiteral( "width_varying_ignore_out_of_range" ) ).toInt() );
853  if ( properties.contains( QStringLiteral( "width_varying_is_variable_width" ) ) )
854  symbolLayer->mLineRender.mStrokeWidth.setIsVariableWidth( properties.value( QStringLiteral( "width_varying_is_variable_width" ) ).toInt() );
855 
856  if ( properties.contains( QStringLiteral( "single_color" ) ) )
857  symbolLayer->mLineRender.mStrokeColoring.setColor( QgsSymbolLayerUtils::decodeColor( properties.value( QStringLiteral( "single_color" ) ).toString() ) );
858  if ( properties.contains( QStringLiteral( "color_ramp_shader" ) ) )
859  symbolLayer->mLineRender.mStrokeColoring.setColor( createColorRampShaderFromProperties( properties.value( QStringLiteral( "color_ramp_shader" ) ) ) );
860  if ( properties.contains( QStringLiteral( "coloring_method" ) ) )
861  symbolLayer->mLineRender.mStrokeColoring.setColoringMethod(
862  static_cast<QgsInterpolatedLineColor::ColoringMethod>( properties.value( QStringLiteral( "coloring_method" ) ).toInt() ) );
863 
864  return symbolLayer.release();
865 }
866 
867 Qgis::SymbolLayerFlags QgsInterpolatedLineSymbolLayer::flags() const
868 {
870 }
871 
873 {
874  QVariantMap props;
875 
876  // Line width varying
877  props.insert( QStringLiteral( "line_width" ), QString::number( mLineRender.mStrokeWidth.fixedStrokeWidth() ) );
878  props.insert( QStringLiteral( "line_width_unit" ), QgsUnitTypes::encodeUnit( mLineRender.widthUnit() ) );
879  props.insert( QStringLiteral( "width_varying_minimum_value" ), mLineRender.mStrokeWidth.minimumValue() );
880  props.insert( QStringLiteral( "width_varying_maximum_value" ), mLineRender.mStrokeWidth.maximumValue() );
881  props.insert( QStringLiteral( "width_varying_use_absolute_value" ), mLineRender.mStrokeWidth.useAbsoluteValue() ? 1 : 0 );
882  props.insert( QStringLiteral( "width_varying_minimum_width" ), mLineRender.mStrokeWidth.minimumWidth() );
883  props.insert( QStringLiteral( "width_varying_maximum_width" ), mLineRender.mStrokeWidth.maximumWidth() );
884  props.insert( QStringLiteral( "width_varying_ignore_out_of_range" ), mLineRender.mStrokeWidth.ignoreOutOfRange() ? 1 : 0 );
885  props.insert( QStringLiteral( "width_varying_is_variable_width" ), mLineRender.mStrokeWidth.isVariableWidth() ? 1 : 0 );
886 
887  // Color varying
888  props.insert( QStringLiteral( "coloring_method" ), mLineRender.mStrokeColoring.coloringMethod() );
889  props.insert( QStringLiteral( "single_color" ), QgsSymbolLayerUtils::encodeColor( mLineRender.mStrokeColoring.singleColor() ) );
890  props.insert( QStringLiteral( "color_ramp_shader" ), colorRampShaderProperties() );
891 
892  return props;
893 }
894 
896 {
897  QgsGeometry geometry = context.patchShape() ? context.patchShape()->geometry()
899 
900  startRender( context );
901  double min = std::min( mLineRender.interpolatedLineWidth().minimumValue(), mLineRender.interpolatedColor().colorRampShader().minimumValue() );
902  double max = std::max( mLineRender.interpolatedLineWidth().maximumValue(), mLineRender.interpolatedColor().colorRampShader().maximumValue() );
903 
904  double totalLength = geometry.length();
905  if ( qgsDoubleNear( totalLength, 0 ) )
906  return;
907 
908  double variation = ( max - min ) / totalLength;
909 
910  QPolygonF points = geometry.asQPolygonF();
911  double lengthFromStart = 0;
912  for ( int i = 1; i < points.count(); ++i )
913  {
914  QPointF p1 = points.at( i - 1 );
915  QPointF p2 = points.at( i );
916 
917  double v1 = min + variation * lengthFromStart;
918  QPointF vectDist = p2 - p1;
919  lengthFromStart += sqrt( pow( vectDist.x(), 2 ) + pow( vectDist.y(), 2 ) );
920  double v2 = min + variation * lengthFromStart;
921  mLineRender.renderInDeviceCoordinates( v1, v2, v1, v2, p1, p2, context.renderContext() );
922  }
923 
924  renderPolyline( points, context );
925 
926 }
927 
929 {
930  switch ( mLineRender.interpolatedColor().coloringMethod() )
931  {
933  return mLineRender.interpolatedColor().singleColor();
935  return QColor();
936  }
938 }
939 
940 
941 void QgsInterpolatedLineSymbolLayer::setExpressionsStringForWidth( const QString &start, const QString &end )
942 {
943  if ( start.isEmpty() )
945  else
947 
948  if ( end.isEmpty() )
950  else
952 }
953 
955 {
957 }
958 
960 {
962 }
963 
965 {
966  mLineRender.mStrokeWidthUnit = strokeWidthUnit;
967 }
968 
970 
972 {
973  mLineRender.mStrokeWidth = interpolatedLineWidth;
974 }
975 
977 
978 void QgsInterpolatedLineSymbolLayer::setExpressionsStringForColor( const QString &start, const QString &end )
979 {
980  if ( start.isEmpty() )
982  else
984 
985  if ( end.isEmpty() )
987  else
989 }
990 
992 {
994 }
995 
997 {
999 }
1000 
1002 {
1003  mLineRender.setInterpolatedColor( interpolatedLineColor );
1004 }
1005 
1007 {
1008  return mLineRender.interpolatedColor();
1009 }
1010 
1011 QVariant QgsInterpolatedLineSymbolLayer::colorRampShaderProperties() const
1012 {
1013  const QgsColorRampShader &colorRampShader = mLineRender.mStrokeColoring.colorRampShader();
1014 
1015  QVariantMap props;
1016  if ( colorRampShader.sourceColorRamp() )
1017  props.insert( QStringLiteral( "color_ramp_source" ), QgsSymbolLayerUtils::colorRampToVariant( QString(), colorRampShader.sourceColorRamp() ) );
1018  props.insert( QStringLiteral( "color_ramp_shader_type" ), colorRampShader.colorRampType() );
1019  props.insert( QStringLiteral( "color_ramp_shader_classification_mode" ), colorRampShader.classificationMode() );
1020  QVariantList colorRampItemListVariant;
1021 
1022  const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader.colorRampItemList();
1023  for ( const QgsColorRampShader::ColorRampItem &item : colorRampItemList )
1024  {
1025  QVariantMap itemVar;
1026  itemVar[QStringLiteral( "label" )] = item.label;
1027  itemVar[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( item.color );
1028  itemVar[QStringLiteral( "value" )] = item.value;
1029  colorRampItemListVariant.append( itemVar );
1030  }
1031  props.insert( QStringLiteral( "color_ramp_shader_items_list" ), colorRampItemListVariant );
1032 
1033  props.insert( QStringLiteral( "color_ramp_shader_minimum_value" ), colorRampShader.minimumValue() );
1034  props.insert( QStringLiteral( "color_ramp_shader_maximum_value" ), colorRampShader.maximumValue() );
1035  props.insert( QStringLiteral( "color_ramp_shader_value_out_of_range" ), colorRampShader.clip() ? 1 : 0 );
1036  props.insert( QStringLiteral( "color_ramp_shader_label_precision" ), colorRampShader.labelPrecision() );
1037 
1038  return props;
1039 }
1040 
1041 QgsColorRampShader QgsInterpolatedLineSymbolLayer::createColorRampShaderFromProperties( const QVariant &properties )
1042 {
1043  QgsColorRampShader colorRampShader;
1044 
1045  if ( properties.type() != QVariant::Map )
1046  return colorRampShader;
1047 
1048  QVariantMap shaderVariantMap = properties.toMap();
1049 
1050  if ( shaderVariantMap.contains( QStringLiteral( "color_ramp_source" ) ) )
1051  colorRampShader.setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( shaderVariantMap.value( QStringLiteral( "color_ramp_source" ) ) ) );
1052 
1053  if ( shaderVariantMap.contains( QStringLiteral( "color_ramp_shader_type" ) ) )
1054  colorRampShader.setColorRampType( static_cast<QgsColorRampShader::Type>( shaderVariantMap.value( QStringLiteral( "color_ramp_shader_type" ) ).toInt() ) );
1055  if ( shaderVariantMap.contains( QStringLiteral( "color_ramp_shader_classification_mode" ) ) )
1056  colorRampShader.setClassificationMode( static_cast<QgsColorRampShader::ClassificationMode>(
1057  shaderVariantMap.value( QStringLiteral( "color_ramp_shader_classification_mode" ) ).toInt() ) );
1058 
1059  if ( shaderVariantMap.contains( QStringLiteral( "color_ramp_shader_items_list" ) ) )
1060  {
1061  QVariant colorRampItemsVar = shaderVariantMap.value( QStringLiteral( "color_ramp_shader_items_list" ) );
1062  if ( colorRampItemsVar.type() == QVariant::List )
1063  {
1064  QVariantList itemVariantList = colorRampItemsVar.toList();
1065  QList<QgsColorRampShader::ColorRampItem> colorRampItemList;
1066  for ( const QVariant &itemVar : std::as_const( itemVariantList ) )
1067  {
1069  if ( itemVar.type() != QVariant::Map )
1070  continue;
1071  QVariantMap itemVarMap = itemVar.toMap();
1072  if ( !itemVarMap.contains( QStringLiteral( "label" ) ) || !itemVarMap.contains( QStringLiteral( "color" ) ) || !itemVarMap.contains( QStringLiteral( "value" ) ) )
1073  continue;
1074 
1075  item.label = itemVarMap.value( QStringLiteral( "label" ) ).toString();
1076  item.color = QgsSymbolLayerUtils::decodeColor( itemVarMap.value( QStringLiteral( "color" ) ).toString() );
1077  item.value = itemVarMap.value( QStringLiteral( "value" ) ).toDouble();
1078 
1079  colorRampItemList.append( item );
1080  }
1081  colorRampShader.setColorRampItemList( colorRampItemList );
1082  }
1083  }
1084 
1085  if ( shaderVariantMap.contains( QStringLiteral( "color_ramp_shader_minimum_value" ) ) )
1086  colorRampShader.setMinimumValue( shaderVariantMap.value( QStringLiteral( "color_ramp_shader_minimum_value" ) ).toDouble() );
1087  else
1088  colorRampShader.setMinimumValue( std::numeric_limits<double>::quiet_NaN() );
1089 
1090  if ( shaderVariantMap.contains( QStringLiteral( "color_ramp_shader_maximum_value" ) ) )
1091  colorRampShader.setMaximumValue( shaderVariantMap.value( QStringLiteral( "color_ramp_shader_maximum_value" ) ).toDouble() );
1092  else
1093  colorRampShader.setMaximumValue( std::numeric_limits<double>::quiet_NaN() );
1094 
1095  if ( shaderVariantMap.contains( QStringLiteral( "color_ramp_shader_value_out_of_range" ) ) )
1096  colorRampShader.setClip( shaderVariantMap.value( QStringLiteral( "color_ramp_shader_value_out_of_range" ) ).toInt() == 1 );
1097  if ( shaderVariantMap.contains( QStringLiteral( "color_ramp_shader_label_precision" ) ) )
1098  colorRampShader.setLabelPrecision( shaderVariantMap.value( QStringLiteral( "color_ramp_shader_label_precision" ) ).toInt() );
1099 
1100  return colorRampShader;
1101 }
1102 
1104 
1105 
1107 {
1108  mRenderingFeature = true;
1109  mLineParts.clear();
1110 }
1111 
1113 {
1114  mRenderingFeature = false;
1115 
1116  if ( mLineParts.empty() )
1117  return;
1118 
1119  render( mLineParts, context );
1120  mLineParts.clear();
1121 }
1122 
1123 void QgsInterpolatedLineSymbolLayer::render( const QVector< QPolygonF > &parts, QgsRenderContext &context )
1124 {
1125  const double totalLength = std::accumulate( parts.begin(), parts.end(), 0.0, []( double total, const QPolygonF & part )
1126  {
1127  return total + QgsSymbolLayerUtils::polylineLength( part );
1128  } );
1129 
1130  if ( qgsDoubleNear( totalLength, 0 ) )
1131  return;
1132 
1133  double startValWidth = 0;
1134  double variationPerMapUnitWidth = 0;
1135  double startValColor = 0;
1136  double variationPerMapUnitColor = 0;
1137 
1138  bool ok = true;
1139 
1140  if ( mLineRender.interpolatedLineWidth().isVariableWidth() )
1141  {
1143  {
1145  if ( !ok )
1146  return;
1147  }
1148 
1149  double endValWidth = 0;
1151  {
1153  if ( !ok )
1154  return;
1155  }
1156 
1157  variationPerMapUnitWidth = ( endValWidth - startValWidth ) / totalLength;
1158  }
1159 
1161  {
1163  {
1165  if ( !ok )
1166  return;
1167  }
1168 
1169  double endValColor = 0;
1171  {
1173  if ( !ok )
1174  return;
1175  }
1176 
1177  variationPerMapUnitColor = ( endValColor - startValColor ) / totalLength;
1178  }
1179 
1180  for ( const QPolygonF &poly : parts )
1181  {
1182  double lengthFromStart = 0;
1183  for ( int i = 1; i < poly.count(); ++i )
1184  {
1185  const QPointF p1 = poly.at( i - 1 );
1186  const QPointF p2 = poly.at( i );
1187 
1188  const double v1c = startValColor + variationPerMapUnitColor * lengthFromStart;
1189  const double v1w = startValWidth + variationPerMapUnitWidth * lengthFromStart;
1190  lengthFromStart += std::sqrt( ( p1.x() - p2.x() ) * ( p1.x() - p2.x() ) + ( p1.y() - p2.y() ) * ( p1.y() - p2.y() ) );
1191  const double v2c = startValColor + variationPerMapUnitColor * lengthFromStart;
1192  const double v2w = startValWidth + variationPerMapUnitWidth * lengthFromStart;
1193  mLineRender.renderInDeviceCoordinates( v1c, v2c, v1w, v2w, p1, p2, context );
1194  }
1195  }
1196 }
1197 
1199 {
1200  mLineRender.setSelected( context.selected() );
1201 
1202  if ( points.empty() )
1203  return;
1204 
1205  if ( mRenderingFeature )
1206  {
1207  // in the middle of rendering a possibly multi-part feature, so we collect all the parts and defer the actual rendering
1208  // until after we've received the final part
1209  mLineParts.append( points );
1210  }
1211  else
1212  {
1213  // not rendering a feature, so we can just render the polyline immediately
1214  render( { points }, context.renderContext() );
1215  }
1216 }
1217 
1219 {
1220  return symbol && symbol->type() == Qgis::SymbolType::Line;
1221 }
1222 
1224 {
1225  return true;
1226 }
@ DisableFeatureClipping
If present, indicates that features should never be clipped to the map extent during rendering.
@ Line
Line symbol.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
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.
ClassificationMode classificationMode() const
Returns the classification mode.
bool isEmpty() const
Whether the color ramp contains any items.
Type colorRampType() const
Returns the color ramp type.
void setSourceColorRamp(QgsColorRamp *colorramp)
Set the source color ramp.
ClassificationMode
Classification modes used to create the color ramp shader.
void setClip(bool clip)
Sets whether the shader should not render values out of range.
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.
Type
Supported methods for color interpolation.
@ 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 setClassificationMode(ClassificationMode classificationMode)
Sets classification mode.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &list)
Sets a custom colormap.
void setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads configuration from the given DOM element.
Abstract base class for color ramps.
Definition: qgscolorramp.h:30
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
double length() const
Returns the planar, 2-dimensional length of geometry.
QPolygonF asQPolygonF() const SIP_HOLDGIL
Returns contents of the geometry as a QPolygonF.
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.
QgsInterpolatedLineColor()
Default constructor.
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.
QColor singleColor() const
Returns the single color that is used if SingleColor coloring mode is set.
void setColoringMethod(ColoringMethod coloringMethod)
Sets the coloring method used.
ColoringMethod
Defines how the color is defined.
@ ColorRamp
Render with a color ramp.
@ SingleColor
Render with a single color.
void setInterpolatedColor(const QgsInterpolatedLineColor &strokeColoring)
Sets the stroke color used to render.
void renderInDeviceCoordinates(double valueColor1, double valueColor2, double valueWidth1, double valueWidth2, QPointF point1, QPointF point2, QgsRenderContext &context) const
Renders a line in the context between point1 and point2 in device (painter) coordinates with color th...
QgsUnitTypes::RenderUnit widthUnit() const
Returns the unit of the stroke width.
void setInterpolatedWidth(const QgsInterpolatedLineWidth &strokeWidth)
Sets the stroke width used to render.
void setSelected(bool selected)
Sets if the rendering must be done as the element is selected.
void render(double value1, double value2, const QgsPointXY &point1, const QgsPointXY &point2, QgsRenderContext &context) const
Renders a line in the context between point1 and point2 with color and width that vary depending on v...
QgsInterpolatedLineWidth interpolatedLineWidth() const
Returns the stroke width used to render.
QgsInterpolatedLineColor interpolatedColor() const
Returns the stroke color used to render.
void setWidthUnit(QgsUnitTypes::RenderUnit strokeWidthUnit)
Sets the unit of the stroke width.
A symbol layer that represents vector layer line feature as interpolated line The interpolation is do...
Q_DECL_DEPRECATED QString endValueExpressionForWidth() const
Returns the expression related to the end extremity value for width.
QColor color() const override
Returns the "representative" color of the symbol layer.
Q_DECL_DEPRECATED QString endValueExpressionForColor() const
Returns the expression related to the end extremity value for width for color.
Q_DECL_DEPRECATED void setExpressionsStringForColor(const QString &start, const QString &end)
Sets the expressions (as string) that define the extremety values af the line feature for color.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
QgsInterpolatedLineSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool canCauseArtifactsBetweenAdjacentTiles() const override
Returns true if the symbol layer rendering can cause visible artifacts across a single feature when t...
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the line joining points, using the given render context.
QgsInterpolatedLineColor interpolatedColor() const
Returns the interpolated color used to render the colors of lines, see QgsInterpolatedLineColor.
bool isCompatibleWithSymbol(QgsSymbol *symbol) const override
Returns if the layer can be used below the specified symbol.
Q_DECL_DEPRECATED QString startValueExpressionForColor() const
Returns the epression related to the start extremity value for width for color.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called before the layer will be rendered for a particular feature.
QString layerType() const override
Returns a string that represents this layer type.
void setWidthUnit(QgsUnitTypes::RenderUnit strokeWidthUnit)
Sets the width unit.
Q_DECL_DEPRECATED void setExpressionsStringForWidth(const QString &start, const QString &end)
Sets the expressions (as string) that define the extremety values af the line feature for width.
void setInterpolatedWidth(const QgsInterpolatedLineWidth &interpolatedLineWidth)
Sets the interpolated width used to render the width of lines, see QgsInterpolatedLineWidth.
void setInterpolatedColor(const QgsInterpolatedLineColor &interpolatedLineColor)
Sets the interpolated color used to render the colors of lines, see QgsInterpolatedLineColor.
Q_DECL_DEPRECATED QString startValueExpressionForWidth() const
Returns the epression related to the start extremity value for width.
void drawPreviewIcon(QgsSymbolRenderContext &context, QSize size) override
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsUnitTypes::RenderUnit widthUnit() const
Returns the width unit.
QgsInterpolatedLineWidth interpolatedWidth() const
Returns the interpolated width used to render the width of lines, see QgsInterpolatedLineWidth.
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called after the layer has been rendered for a particular feature.
static QgsSymbolLayer * create(const QVariantMap &properties)
Creates the symbol layer.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
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.
QgsGeometry geometry() const
Returns the geometry for the patch shape.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:90
A class to represent a 2D point.
Definition: qgspointxy.h:59
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:169
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
A store for object properties.
Definition: qgsproperty.h:231
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
double maximumValue() const
Returns the minimum value for the raster shader.
void setLabelPrecision(int labelPrecision)
Sets label precision to labelPrecision.
int labelPrecision() const
Returns label precision.
virtual void setMaximumValue(double value)
Sets the maximum value for the raster shader.
virtual void setMinimumValue(double value)
Sets the minimum value for the raster shader.
double minimumValue() const
Returns the maximum value for the raster shader.
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.
QgsExpressionContext & expressionContext()
Gets the expression context.
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(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QColor selectionColor() const
Returns the color to use when rendering selected features.
Scoped object for saving and restoring a QPainter object's state.
QgsLegendPatchShape defaultPatch(Qgis::SymbolType type, QSizeF size) const
Returns the default legend patch shape for the given symbol type.
Definition: qgsstyle.cpp:1157
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
static QVariant colorRampToVariant(const QString &name, QgsColorRamp *ramp)
Saves a color ramp to a QVariantMap, wrapped in a QVariant.
static QColor decodeColor(const QString &str)
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static QString encodeColor(const QColor &color)
@ PropertyLineEndWidthValue
End line width for interpolated line renderer (since QGIS 3.22)
@ PropertyLineEndColorValue
End line color for interpolated line renderer (since QGIS 3.22)
@ PropertyLineStartColorValue
Start line color for interpolated line renderer (since QGIS 3.22)
@ PropertyLineStartWidthValue
Start line width for interpolated line renderer (since QGIS 3.22)
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QgsPropertyCollection mDataDefinedProperties
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
const QgsLegendPatchShape * patchShape() const
Returns the symbol patch shape, to use if rendering symbol preview icons.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
#define BUILTIN_UNREACHABLE
Definition: qgis.h:2902
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2260