LCOV - code coverage report
Current view: top level - core/symbology - qgsinterpolatedlinerenderer.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 433 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           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 : }

Generated by: LCOV version 1.14