QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgshistogramdiagram.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgshistogramdiagram.cpp
3  ---------------------
4  begin : August 2012
5  copyright : (C) 2012 by Matthias Kuhn
6  email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgshistogramdiagram.h"
16 #include "qgsdiagramrenderer.h"
17 #include "qgsrendercontext.h"
18 #include "qgsexpression.h"
19 #include "qgssymbollayerutils.h"
20 #include "qgslinesymbol.h"
21 
22 #include <QPainter>
23 
25 {
26  mCategoryBrush.setStyle( Qt::SolidPattern );
27  mPen.setStyle( Qt::SolidLine );
28  mScaleFactor = 0;
29 }
30 
32 {
33  return new QgsHistogramDiagram( *this );
34 }
35 
37 {
38  QSizeF size;
39  if ( feature.attributes().isEmpty() )
40  {
41  return size; //zero size if no attributes
42  }
43 
44  if ( qgsDoubleNear( is.upperValue, is.lowerValue ) )
45  return size; // invalid value range => zero size
46 
47  double maxValue = 0;
48 
49  QgsExpressionContext expressionContext = c.expressionContext();
50  expressionContext.setFeature( feature );
51  if ( !feature.fields().isEmpty() )
52  expressionContext.setFields( feature.fields() );
53 
54  for ( const QString &cat : std::as_const( s.categoryAttributes ) )
55  {
56  QgsExpression *expression = getExpression( cat, expressionContext );
57  maxValue = std::max( expression->evaluate( &expressionContext ).toDouble(), maxValue );
58  }
59 
60  // Scale, if extension is smaller than the specified minimum
61  if ( maxValue < s.minimumSize )
62  {
63  maxValue = s.minimumSize;
64  }
65 
66  // eh - this method returns size in unknown units ...! We'll have to fake it and use a rough estimation of
67  // a conversion factor to painter units...
68  // TODO QGIS 4.0 -- these methods should all use painter units, dependent on the render context scaling...
69  double painterUnitConversionScale = c.convertToPainterUnits( 1, s.sizeType );
70 
71  const double spacing = c.convertToPainterUnits( s.spacing(), s.spacingUnit(), s.spacingMapUnitScale() ) / painterUnitConversionScale;
72 
73  switch ( s.diagramOrientation )
74  {
77  mScaleFactor = ( ( is.upperSize.width() - is.lowerSize.height() ) / ( is.upperValue - is.lowerValue ) );
78  size.scale( s.barWidth * s.categoryAttributes.size() + spacing * std::max( 0, static_cast<int>( s.categoryAttributes.size() ) - 1 ), maxValue * mScaleFactor, Qt::IgnoreAspectRatio );
79  break;
80 
83  mScaleFactor = ( ( is.upperSize.width() - is.lowerSize.width() ) / ( is.upperValue - is.lowerValue ) );
84  size.scale( maxValue * mScaleFactor, s.barWidth * s.categoryAttributes.size() + spacing * std::max( 0, static_cast<int>( s.categoryAttributes.size() ) - 1 ), Qt::IgnoreAspectRatio );
85  break;
86  }
87 
88  if ( s.showAxis() && s.axisLineSymbol() )
89  {
90  const double maxBleed = QgsSymbolLayerUtils::estimateMaxSymbolBleed( s.axisLineSymbol(), c ) / painterUnitConversionScale;
91  size.setWidth( size.width() + 2 * maxBleed );
92  size.setHeight( size.height() + 2 * maxBleed );
93  }
94 
95  return size;
96 }
97 
99 {
100  if ( qgsDoubleNear( is.upperValue, is.lowerValue ) )
101  return s.minimumSize; // invalid value range => zero size
102 
103  // Scale, if extension is smaller than the specified minimum
104  if ( value < s.minimumSize )
105  {
106  value = s.minimumSize;
107  }
108 
109  double scaleFactor = ( ( is.upperSize.width() - is.lowerSize.width() ) / ( is.upperValue - is.lowerValue ) );
110  return value * scaleFactor;
111 }
112 
114 {
115  return DIAGRAM_NAME_HISTOGRAM;
116 }
117 
119 {
120  Q_UNUSED( c )
121  QSizeF size;
122 
123  if ( attributes.isEmpty() )
124  {
125  return QSizeF(); //zero size if no attributes
126  }
127 
128  double maxValue = attributes.at( 0 ).toDouble();
129 
130  for ( int i = 0; i < attributes.count(); ++i )
131  {
132  maxValue = std::max( attributes.at( i ).toDouble(), maxValue );
133  }
134 
135  // eh - this method returns size in unknown units ...! We'll have to fake it and use a rough estimation of
136  // a conversion factor to painter units...
137  // TODO QGIS 4.0 -- these methods should all use painter units, dependent on the render context scaling...
138  double painterUnitConversionScale = c.convertToPainterUnits( 1, s.sizeType );
139 
140  const double spacing = c.convertToPainterUnits( s.spacing(), s.spacingUnit(), s.spacingMapUnitScale() ) / painterUnitConversionScale;
141 
142  switch ( s.diagramOrientation )
143  {
146  mScaleFactor = maxValue / s.size.height();
147  size.scale( s.barWidth * s.categoryColors.size() + spacing * std::max( 0, static_cast<int>( s.categoryAttributes.size() ) - 1 ), s.size.height(), Qt::IgnoreAspectRatio );
148  break;
149 
152  mScaleFactor = maxValue / s.size.width();
153  size.scale( s.size.width(), s.barWidth * s.categoryColors.size() + spacing * std::max( 0, static_cast<int>( s.categoryAttributes.size() ) - 1 ), Qt::IgnoreAspectRatio );
154  break;
155  }
156 
157  if ( s.showAxis() && s.axisLineSymbol() )
158  {
159  const double maxBleed = QgsSymbolLayerUtils::estimateMaxSymbolBleed( s.axisLineSymbol(), c ) / painterUnitConversionScale;
160  size.setWidth( size.width() + 2 * maxBleed );
161  size.setHeight( size.height() + 2 * maxBleed );
162  }
163 
164  return size;
165 }
166 
167 void QgsHistogramDiagram::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position )
168 {
169  QPainter *p = c.painter();
170  if ( !p )
171  {
172  return;
173  }
174 
175  QList<double> values;
176  double maxValue = 0;
177 
178  QgsExpressionContext expressionContext = c.expressionContext();
179  expressionContext.setFeature( feature );
180  if ( !feature.fields().isEmpty() )
181  expressionContext.setFields( feature.fields() );
182 
183  values.reserve( s.categoryAttributes.size() );
184  for ( const QString &cat : std::as_const( s.categoryAttributes ) )
185  {
186  QgsExpression *expression = getExpression( cat, expressionContext );
187  double currentVal = expression->evaluate( &expressionContext ).toDouble();
188  values.push_back( currentVal );
189  maxValue = std::max( currentVal, maxValue );
190  }
191 
192  double scaledMaxVal = sizePainterUnits( maxValue * mScaleFactor, s, c );
193 
194  double currentOffset = 0;
195  double scaledWidth = sizePainterUnits( s.barWidth, s, c );
196 
197  const double spacing = c.convertToPainterUnits( s.spacing(), s.spacingUnit(), s.spacingMapUnitScale() );
198 
199  double baseX = position.x();
200  double baseY = position.y();
201 
202  if ( s.showAxis() && s.axisLineSymbol() )
203  {
204  // if showing axis, the diagram position needs shifting from the default base x so that the axis
205  // line stroke sits within the desired label engine rect (otherwise we risk overlaps of the axis line stroke)
206  const double maxBleed = QgsSymbolLayerUtils::estimateMaxSymbolBleed( s.axisLineSymbol(), c );
207  baseX += maxBleed;
208  baseY -= maxBleed;
209  }
210 
211 
212  mPen.setColor( s.penColor );
213  setPenWidth( mPen, s, c );
214  p->setPen( mPen );
215 
216  QList<double>::const_iterator valIt = values.constBegin();
217  QList< QColor >::const_iterator colIt = s.categoryColors.constBegin();
218  for ( ; valIt != values.constEnd(); ++valIt, ++colIt )
219  {
220  double length = sizePainterUnits( *valIt * mScaleFactor, s, c );
221 
222  QColor brushColor( *colIt );
223  brushColor.setAlphaF( brushColor.alphaF() * s.opacity );
224  mCategoryBrush.setColor( brushColor );
225  p->setBrush( mCategoryBrush );
226 
227  switch ( s.diagramOrientation )
228  {
230  p->drawRect( QRectF( baseX + currentOffset, baseY, scaledWidth, length * -1 ) );
231  break;
232 
234  p->drawRect( QRectF( baseX + currentOffset, baseY - scaledMaxVal, scaledWidth, length ) );
235  break;
236 
238  p->drawRect( QRectF( baseX, baseY - scaledWidth * values.size() - spacing * std::max( 0, static_cast<int>( values.size() ) - 1 ) + currentOffset, length, scaledWidth ) );
239  break;
240 
242  p->drawRect( QRectF( baseX + scaledMaxVal, baseY - scaledWidth * values.size() - spacing * std::max( 0, static_cast<int>( values.size() ) - 1 ) + currentOffset, 0 - length, scaledWidth ) );
243  break;
244  }
245 
246  currentOffset += scaledWidth + spacing;
247  }
248 
249  if ( s.showAxis() && s.axisLineSymbol() )
250  {
251  s.axisLineSymbol()->startRender( c );
252  QPolygonF axisPoints;
253  switch ( s.diagramOrientation )
254  {
256  axisPoints << QPointF( baseX, baseY - scaledMaxVal ) << QPointF( baseX, baseY ) << QPointF( baseX + scaledWidth * values.size() + spacing * std::max( 0, static_cast<int>( values.size() ) - 1 ), baseY );
257  break;
258 
260  axisPoints << QPointF( baseX, baseY ) << QPointF( baseX, baseY - scaledMaxVal ) << QPointF( baseX + scaledWidth * values.size() + spacing * std::max( 0, static_cast<int>( values.size() ) - 1 ), baseY - scaledMaxVal );
261  break;
262 
264  axisPoints << QPointF( baseX + scaledMaxVal, baseY - scaledWidth * values.size() - spacing * std::max( 0, static_cast<int>( values.size() ) - 1 ) )
265  << QPointF( baseX, baseY - scaledWidth * values.size() - spacing * std::max( 0, static_cast<int>( values.size() ) - 1 ) )
266  << QPointF( baseX, baseY );
267  break;
268 
270  axisPoints << QPointF( baseX, baseY - scaledWidth * values.size() - spacing * std::max( 0, static_cast<int>( values.size() ) - 1 ) )
271  << QPointF( baseX + scaledMaxVal, baseY - scaledWidth * values.size() - spacing * std::max( 0, static_cast<int>( values.size() ) - 1 ) )
272  << QPointF( baseX + scaledMaxVal, baseY );
273  break;
274  }
275 
276  s.axisLineSymbol()->renderPolyline( axisPoints, nullptr, c );
277  s.axisLineSymbol()->stopRender( c );
278  }
279 }
QgsDiagram::getExpression
QgsExpression * getExpression(const QString &expression, const QgsExpressionContext &context)
Returns a prepared expression for the specified context.
Definition: qgsdiagram.cpp:38
DIAGRAM_NAME_HISTOGRAM
#define DIAGRAM_NAME_HISTOGRAM
Definition: qgshistogramdiagram.h:19
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsDiagramInterpolationSettings::upperSize
QSizeF upperSize
Definition: qgsdiagramrenderer.h:666
QgsDiagramInterpolationSettings::lowerValue
double lowerValue
Definition: qgsdiagramrenderer.h:667
QgsFields::isEmpty
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
QgsDiagramSettings::categoryColors
QList< QColor > categoryColors
Definition: qgsdiagramrenderer.h:421
QgsDiagramSettings::penColor
QColor penColor
Definition: qgsdiagramrenderer.h:451
qgsexpression.h
QgsDiagramSettings::Up
@ Up
Definition: qgsdiagramrenderer.h:394
qgssymbollayerutils.h
QgsDiagramSettings::diagramOrientation
DiagramOrientation diagramOrientation
Definition: qgsdiagramrenderer.h:454
QgsDiagramSettings::minimumSize
double minimumSize
Scale diagrams smaller than mMinimumSize to mMinimumSize.
Definition: qgsdiagramrenderer.h:487
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:59
QgsExpressionContext::setFields
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Definition: qgsexpressioncontext.cpp:587
QgsDiagramSettings::spacingMapUnitScale
const QgsMapUnitScale & spacingMapUnitScale() const
Returns the map unit scale for the content spacing.
Definition: qgsdiagramrenderer.h:552
QgsDiagramSettings::opacity
double opacity
Opacity, from 0 (transparent) to 1.0 (opaque)
Definition: qgsdiagramrenderer.h:458
QgsDiagramSettings::Down
@ Down
Definition: qgsdiagramrenderer.h:395
QgsHistogramDiagram::QgsHistogramDiagram
QgsHistogramDiagram()
Definition: qgshistogramdiagram.cpp:24
QgsDiagramInterpolationSettings::upperValue
double upperValue
Definition: qgsdiagramrenderer.h:668
QgsHistogramDiagram
A histogram style diagram.
Definition: qgshistogramdiagram.h:40
QgsDiagramSettings::spacing
double spacing() const
Returns the spacing between diagram contents.
Definition: qgsdiagramrenderer.h:500
QgsDiagramInterpolationSettings::lowerSize
QSizeF lowerSize
Definition: qgsdiagramrenderer.h:665
QgsDiagram::setPenWidth
void setPenWidth(QPen &pen, const QgsDiagramSettings &s, const QgsRenderContext &c)
Changes the pen width to match the current settings and rendering context.
Definition: qgsdiagram.cpp:49
QgsSymbol::stopRender
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:842
QgsDiagram::sizePainterUnits
QSizeF sizePainterUnits(QSizeF size, const QgsDiagramSettings &s, const QgsRenderContext &c)
Calculates a size to match the current settings and rendering context.
Definition: qgsdiagram.cpp:55
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
qgshistogramdiagram.h
QgsDiagramSettings::Left
@ Left
Definition: qgsdiagramrenderer.h:396
qgsrendercontext.h
QgsDiagramSettings::Right
@ Right
Definition: qgsdiagramrenderer.h:397
QgsSymbol::startRender
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:794
qgsdiagramrenderer.h
QgsFeature::attributes
QgsAttributes attributes
Definition: qgsfeature.h:69
QgsDiagramInterpolationSettings
Additional diagram settings for interpolated size rendering.
Definition: qgsdiagramrenderer.h:662
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:350
QgsDiagramSettings::size
QSizeF size
Definition: qgsdiagramrenderer.h:425
QgsHistogramDiagram::legendSize
double legendSize(double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is) const override
Returns the size of the legend item for the diagram corresponding to a specified value.
Definition: qgshistogramdiagram.cpp:98
QgsHistogramDiagram::diagramName
QString diagramName() const override
Gets a descriptive name for this diagram type.
Definition: qgshistogramdiagram.cpp:113
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsHistogramDiagram::diagramSize
QSizeF diagramSize(const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s) override
Returns the size in map units the diagram will use to render.
Definition: qgshistogramdiagram.cpp:118
QgsDiagramSettings::barWidth
double barWidth
Definition: qgsdiagramrenderer.h:455
QgsDiagramSettings::showAxis
bool showAxis() const
Returns true if the diagram axis should be shown.
Definition: qgsdiagramrenderer.cpp:830
QgsDiagramSettings::axisLineSymbol
QgsLineSymbol * axisLineSymbol() const
Returns the line symbol to use for rendering axis in diagrams.
Definition: qgsdiagramrenderer.cpp:819
QgsLineSymbol::renderPolyline
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol along the line joining points, using the given render context.
Definition: qgslinesymbol.cpp:232
QgsHistogramDiagram::clone
QgsHistogramDiagram * clone() const override
Returns an instance that is equivalent to this one.
Definition: qgshistogramdiagram.cpp:31
QgsDiagramSettings::sizeType
QgsUnitTypes::RenderUnit sizeType
Diagram size unit.
Definition: qgsdiagramrenderer.h:430
QgsAttributes
A vector of attributes. Mostly equal to QVector<QVariant>.
Definition: qgsattributes.h:57
QgsFeature::fields
QgsFields fields
Definition: qgsfeature.h:70
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsSymbolLayerUtils::estimateMaxSymbolBleed
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
Definition: qgssymbollayerutils.cpp:934
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsHistogramDiagram::renderDiagram
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position) override
Draws the diagram at the given position (in pixel coordinates)
Definition: qgshistogramdiagram.cpp:167
QgsDiagramSettings::spacingUnit
QgsUnitTypes::RenderUnit spacingUnit() const
Returns the units for the content spacing.
Definition: qgsdiagramrenderer.h:532
QgsDiagramSettings::categoryAttributes
QList< QString > categoryAttributes
Definition: qgsdiagramrenderer.h:422
QgsDiagramSettings
Stores the settings for rendering a single diagram.
Definition: qgsdiagramrenderer.h:381
qgslinesymbol.h
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:525