57 element.setAttribute( QStringLiteral(
"gridIntervalMinor" ),
qgsDoubleToString( mGridIntervalMinor ) );
58 element.setAttribute( QStringLiteral(
"gridIntervalMajor" ),
qgsDoubleToString( mGridIntervalMajor ) );
59 element.setAttribute( QStringLiteral(
"labelInterval" ),
qgsDoubleToString( mLabelInterval ) );
60 element.setAttribute( QStringLiteral(
"suffix" ), mLabelSuffix );
61 element.setAttribute( QStringLiteral(
"suffixPlacement" ),
qgsEnumValueToKey( mSuffixPlacement ) );
63 QDomElement numericFormatElement = document.createElement( QStringLiteral(
"numericFormat" ) );
64 mNumericFormat->writeXml( numericFormatElement, document, context );
65 element.appendChild( numericFormatElement );
67 QDomElement gridMajorElement = document.createElement( QStringLiteral(
"gridMajorSymbol" ) );
69 element.appendChild( gridMajorElement );
70 QDomElement gridMinorElement = document.createElement( QStringLiteral(
"gridMinorSymbol" ) );
72 element.appendChild( gridMinorElement );
74 QDomElement textFormatElement = document.createElement( QStringLiteral(
"textFormat" ) );
75 textFormatElement.appendChild( mLabelTextFormat.
writeXml( document, context ) );
76 element.appendChild( textFormatElement );
83 mGridIntervalMinor = element.attribute( QStringLiteral(
"gridIntervalMinor" ) ).toDouble();
84 mGridIntervalMajor = element.attribute( QStringLiteral(
"gridIntervalMajor" ) ).toDouble();
85 mLabelInterval = element.attribute( QStringLiteral(
"labelInterval" ) ).toDouble();
87 mLabelSuffix = element.attribute( QStringLiteral(
"suffix" ) );
90 const QDomElement numericFormatElement = element.firstChildElement( QStringLiteral(
"numericFormat" ) );
93 const QDomElement gridMajorElement = element.firstChildElement( QStringLiteral(
"gridMajorSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
94 mGridMajorSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( gridMajorElement, context ) );
95 const QDomElement gridMinorElement = element.firstChildElement( QStringLiteral(
"gridMinorSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
96 mGridMinorSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( gridMinorElement, context ) );
98 const QDomElement textFormatElement = element.firstChildElement( QStringLiteral(
"textFormat" ) );
99 mLabelTextFormat.
readXml( textFormatElement, context );
106 return mNumericFormat.get();
111 mNumericFormat.reset( format );
121 mLabelSuffix = suffix;
126 return mSuffixPlacement;
131 mSuffixPlacement = placement;
136 return mGridMajorSymbol.get();
141 mGridMajorSymbol.reset( symbol );
146 return mGridMinorSymbol.get();
151 mGridMinorSymbol.reset( symbol );
156 return mLabelTextFormat;
161 mLabelTextFormat = format;
170 : mMargins( 2, 2, 2, 2 )
186 QDomElement xAxisElement = document.createElement( QStringLiteral(
"xAxis" ) );
187 mXAxis.
writeXml( xAxisElement, document, context );
188 element.appendChild( xAxisElement );
189 QDomElement yAxisElement = document.createElement( QStringLiteral(
"yAxis" ) );
190 mYAxis.
writeXml( yAxisElement, document, context );
191 element.appendChild( yAxisElement );
193 QDomElement backgroundElement = document.createElement( QStringLiteral(
"backgroundSymbol" ) );
195 element.appendChild( backgroundElement );
196 QDomElement borderElement = document.createElement( QStringLiteral(
"borderSymbol" ) );
198 element.appendChild( borderElement );
200 element.setAttribute( QStringLiteral(
"margins" ), mMargins.
toString() );
209 mMinX = element.attribute( QStringLiteral(
"minX" ) ).toDouble();
210 mMaxX = element.attribute( QStringLiteral(
"maxX" ) ).toDouble();
211 mMinY = element.attribute( QStringLiteral(
"minY" ) ).toDouble();
212 mMaxY = element.attribute( QStringLiteral(
"maxY" ) ).toDouble();
214 const QDomElement xAxisElement = element.firstChildElement( QStringLiteral(
"xAxis" ) );
215 mXAxis.
readXml( xAxisElement, context );
216 const QDomElement yAxisElement = element.firstChildElement( QStringLiteral(
"yAxis" ) );
217 mYAxis.
readXml( yAxisElement, context );
219 const QDomElement backgroundElement = element.firstChildElement( QStringLiteral(
"backgroundSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
220 mChartBackgroundSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( backgroundElement, context ) );
221 const QDomElement borderElement = element.firstChildElement( QStringLiteral(
"borderSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
222 mChartBorderSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( borderElement, context ) );
234 mChartBackgroundSymbol->startRender( context );
235 mChartBorderSymbol->startRender( context );
259 double maxYAxisLabelWidth = 0;
261 for (
double currentY = firstYLabel; ; currentY += mYAxis.
labelInterval() )
276 if ( currentY == firstYLabel )
281 if ( !hasMoreLabels )
286 if ( currentY == firstYLabel || !hasMoreLabels )
292 if ( !hasMoreLabels )
296 const double chartAreaLeft = plotArea.left();
297 const double chartAreaRight = plotArea.right();
298 const double chartAreaTop = plotArea.top();
299 const double chartAreaBottom = plotArea.bottom();
302 mChartBackgroundSymbol->renderPolygon( QPolygonF(
304 QPointF( chartAreaLeft, chartAreaTop ),
305 QPointF( chartAreaRight, chartAreaTop ),
306 QPointF( chartAreaRight, chartAreaBottom ),
307 QPointF( chartAreaLeft, chartAreaBottom ),
308 QPointF( chartAreaLeft, chartAreaTop )
309 } ),
nullptr,
nullptr, context );
311 const double xScale = ( chartAreaRight - chartAreaLeft ) / ( mMaxX - mMinX );
312 const double yScale = ( chartAreaBottom - chartAreaTop ) / ( mMaxY - mMinY );
314 constexpr int MAX_OBJECTS = 1000;
320 double nextMajorXGrid = firstMajorXGrid;
321 int objectNumber = 0;
322 for (
double currentX = firstMinorXGrid; objectNumber < MAX_OBJECTS && ( currentX <= mMaxX && !
qgsDoubleNear( currentX, mMaxX, xTolerance ) ); currentX += mXAxis.
gridIntervalMinor(), ++objectNumber )
337 QPointF( ( currentX - mMinX ) * xScale + chartAreaLeft, chartAreaBottom ),
338 QPointF( ( currentX - mMinX ) * xScale + chartAreaLeft, chartAreaTop )
339 } ),
nullptr, context );
344 double nextMajorYGrid = firstMajorYGrid;
346 for (
double currentY = firstMinorYGrid; objectNumber < MAX_OBJECTS && ( currentY <= mMaxY && !
qgsDoubleNear( currentY, mMaxY, yTolerance ) ); currentY += mYAxis.
gridIntervalMinor(), ++objectNumber )
361 QPointF( chartAreaLeft, chartAreaBottom - ( currentY - mMinY ) * yScale ),
362 QPointF( chartAreaRight, chartAreaBottom - ( currentY - mMinY ) * yScale )
363 } ),
nullptr, context );
371 for (
double currentX = firstXLabel; ; currentX += mXAxis.
labelInterval(), ++objectNumber )
386 if ( objectNumber == 0 )
391 if ( !hasMoreLabels )
396 if ( objectNumber == 0 || !hasMoreLabels )
402 0, Qgis::TextHorizontalAlignment::Center, { text }, context, mXAxis.
textFormat() );
403 if ( !hasMoreLabels )
410 for (
double currentY = firstYLabel; ; currentY += mYAxis.
labelInterval(), ++objectNumber )
425 if ( objectNumber == 0 )
430 if ( !hasMoreLabels )
435 if ( objectNumber == 0 || !hasMoreLabels )
443 chartAreaBottom - ( currentY - mMinY ) * yScale + height / 2 ),
444 0, Qgis::TextHorizontalAlignment::Right, { text }, context, mYAxis.
textFormat(),
false );
445 if ( !hasMoreLabels )
453 mChartBorderSymbol->renderPolygon( QPolygonF(
455 QPointF( chartAreaLeft, chartAreaTop ),
456 QPointF( chartAreaRight, chartAreaTop ),
457 QPointF( chartAreaRight, chartAreaBottom ),
458 QPointF( chartAreaLeft, chartAreaBottom ),
459 QPointF( chartAreaLeft, chartAreaTop )
460 } ),
nullptr,
nullptr, context );
462 mChartBackgroundSymbol->stopRender( context );
463 mChartBorderSymbol->stopRender( context );
504 constexpr int MAX_LABELS = 1000;
508 double maxXAxisLabelHeight = 0;
510 for (
double currentX = firstXLabel; ; currentX += mXAxis.
labelInterval(), labelNumber++ )
526 if ( labelNumber == 0 )
531 if ( !hasMoreLabels )
536 if ( labelNumber == 0 || !hasMoreLabels )
541 if ( !hasMoreLabels )
545 double maxYAxisLabelWidth = 0;
548 for (
double currentY = firstMinorYGrid; ; currentY += mYAxis.
gridIntervalMinor(), labelNumber ++ )
554 if ( yAxisSuffixWidth > 0 )
562 thisLabelWidth += yAxisSuffixWidth;
566 if ( labelNumber == 0 )
567 thisLabelWidth += yAxisSuffixWidth;
571 if ( !hasMoreLabels )
572 thisLabelWidth += yAxisSuffixWidth;
576 if ( labelNumber == 0 || !hasMoreLabels )
577 thisLabelWidth += yAxisSuffixWidth;
581 maxYAxisLabelWidth = std::max( maxYAxisLabelWidth, thisLabelWidth );
582 if ( !hasMoreLabels )
586 const double leftTextSize = maxYAxisLabelWidth + context.
convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
587 const double rightTextSize = 0;
588 const double bottomTextSize = maxXAxisLabelHeight + context.
convertToPainterUnits( 0.5, Qgis::RenderUnit::Millimeters );
589 const double topTextSize = 0;
591 const double leftMargin = context.
convertToPainterUnits( mMargins.
left(), Qgis::RenderUnit::Millimeters ) + leftTextSize;
593 const double topMargin = context.
convertToPainterUnits( mMargins.
top(), Qgis::RenderUnit::Millimeters ) + topTextSize;
596 return QRectF( leftMargin, topMargin, mSize.width() - rightMargin - leftMargin, mSize.height() - bottomMargin - topMargin );
601 if ( !mSize.isValid() )
605 constexpr double IDEAL_WIDTH = 0.4;
606 constexpr double TOLERANCE = 0.04;
607 constexpr int MAX_LABELS = 1000;
614 const double availableWidth = mSize.width() - leftMargin - rightMargin;
615 const double availableHeight = mSize.height() - topMargin - bottomMargin;
619 auto refineIntervalForAxis = [&](
double axisMinimum,
double axisMaximum,
620 const std::function< double(
double ) > &sizeForLabel,
621 double availableSize,
double idealSizePercent,
double sizeTolerancePercent,
622 double & labelInterval,
double & majorInterval,
double & minorInterval )
624 auto roundBase10 = [](
double value )->
double
626 return std::pow( 10, std::floor( std::log10( value ) ) );
630 double totalSize = 0;
631 int initialLabelCount = 0;
633 const double firstLabelPos = std::ceil( axisMinimum / labelInterval ) * labelInterval;
635 for (
double currentPos = firstLabelPos; initialLabelCount <= MAX_LABELS && currentPos <= axisMaximum; currentPos += labelInterval, ++initialLabelCount )
637 totalSize += sizeForLabel( currentPos );
642 if ( initialLabelCount >= MAX_LABELS || ( totalSize / availableSize < ( idealSizePercent - sizeTolerancePercent ) ) || ( totalSize / availableSize > ( idealSizePercent + sizeTolerancePercent ) ) )
645 int numberLabelsInitial = std::floor( availableSize / 30 );
647 double labelIntervalTest = ( axisMaximum - axisMinimum ) / numberLabelsInitial;
648 double baseValue = roundBase10( labelIntervalTest );
649 double candidate = baseValue;
650 int currentMultiplier = 1;
652 int numberLabels = 0;
655 const double firstLabelPosition = std::ceil( axisMinimum / candidate ) * candidate;
656 double totalSize = 0;
658 for (
double currentPos = firstLabelPosition; currentPos <= axisMaximum; currentPos += candidate )
660 totalSize += sizeForLabel( currentPos );
663 if ( numberLabels > MAX_LABELS )
667 if ( numberLabels <= MAX_LABELS && totalSize <= availableSize * idealSizePercent )
670 if ( currentMultiplier == 1 )
671 currentMultiplier = 2;
672 else if ( currentMultiplier == 2 )
673 currentMultiplier = 5;
674 else if ( currentMultiplier == 5 )
677 currentMultiplier = 1;
680 candidate = baseValue * currentMultiplier;
682 labelInterval = candidate;
683 if ( numberLabels < 10 )
685 minorInterval = labelInterval / 2;
686 majorInterval = minorInterval * 4;
690 minorInterval = labelInterval;
691 majorInterval = minorInterval * 5;
702 refineIntervalForAxis( mMinX, mMaxX, [ = ](
double position ) ->
double
704 const QString text = mXAxis.
numericFormat()->formatDouble( position, numericContext );
709 IDEAL_WIDTH, TOLERANCE, labelIntervalX, majorIntervalX, minorIntervalX );
720 refineIntervalForAxis( mMinY, mMaxY, [ = ](
double position ) ->
double
722 const QString text = mYAxis.
numericFormat()->formatDouble( position, numericContext );
727 IDEAL_WIDTH, TOLERANCE, labelIntervalY, majorIntervalY, minorIntervalY );
736 return mChartBackgroundSymbol.get();
741 mChartBackgroundSymbol.reset( symbol );
746 return mChartBorderSymbol.get();
751 mChartBorderSymbol.reset( symbol );
775 std::unique_ptr< QgsSimpleLineSymbolLayer > gridMajor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 150 ), 0.1 );
776 gridMajor->setPenCapStyle( Qt::FlatCap );
782 std::unique_ptr< QgsSimpleLineSymbolLayer > gridMinor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 50 ), 0.1 );
783 gridMinor->setPenCapStyle( Qt::FlatCap );
789 std::unique_ptr< QgsSimpleFillSymbolLayer > chartFill = std::make_unique< QgsSimpleFillSymbolLayer >( QColor( 255, 255, 255 ) );
795 std::unique_ptr< QgsSimpleLineSymbolLayer > chartBorder = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20 ), 0.1 );
PlotAxisSuffixPlacement
Placement options for suffixes in the labels for axis of plots.
@ FirstAndLastLabels
Place suffix after the first and last label values only.
@ EveryLabel
Place suffix after every value label.
@ FirstLabel
Place suffix after the first label value only.
@ LastLabel
Place suffix after the last label value only.
@ NoLabels
Do not place suffixes.
void calculateOptimisedIntervals(QgsRenderContext &context)
Automatically sets the grid and label intervals to optimal values for display in the given render con...
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Writes the plot's properties into an XML element.
void setSize(QSizeF size)
Sets the overall size of the plot (including titles and over components which sit outside the plot ar...
const QgsMargins & margins() const
Returns the margins of the plot area (in millimeters)
void render(QgsRenderContext &context)
Renders the plot.
void setChartBorderSymbol(QgsFillSymbol *symbol)
Sets the symbol used to render the border of the chart.
Qgs2DPlot()
Constructor for Qgs2DPlot.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the plot's properties from an XML element.
QgsFillSymbol * chartBorderSymbol()
Returns the symbol used to render the border of the chart.
QSizeF size() const
Returns the overall size of the plot (in millimeters) (including titles and other components which si...
void setMargins(const QgsMargins &margins)
Sets the margins of the plot area (in millimeters)
QRectF interiorPlotArea(QgsRenderContext &context) const
Returns the area of the plot which corresponds to the actual plot content (excluding all titles and o...
void setChartBackgroundSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to render the background of the chart.
virtual void renderContent(QgsRenderContext &context, const QRectF &plotArea)
Renders the plot content.
QgsFillSymbol * chartBackgroundSymbol()
Returns the fill symbol used to render the background of the chart.
static QgsNumericFormatRegistry * numericFormatRegistry()
Gets the registry of available numeric formats.
RAII class to pop scope from an expression context on destruction.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
A line symbol type, for rendering LineString and MultiLineString geometries.
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.
The QgsMargins class defines the four margins of a rectangle.
double top() const
Returns the top margin.
static QgsMargins fromString(const QString &string)
Returns a QgsMargins object decoded from a string, or a null QgsMargins if the string could not be in...
double right() const
Returns the right margin.
double bottom() const
Returns the bottom margin.
QString toString() const
Returns the margins encoded to a string.
double left() const
Returns the left margin.
A context for numeric formats.
QgsLineSymbol * gridMinorSymbol()
Returns the line symbol used to render the minor lines in the axis grid.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the axis' properties from an XML element.
void setGridMajorSymbol(QgsLineSymbol *symbol)
Sets the symbol used to render the major lines in the axis grid.
double gridIntervalMinor() const
Returns the interval of minor grid lines for the axis.
double gridIntervalMajor() const
Returns the interval of major grid lines for the axis.
void setNumericFormat(QgsNumericFormat *format)
Sets the numeric format used for the axis labels.
QgsTextFormat textFormat() const
Returns the text format used for the axis labels.
void setGridIntervalMajor(double interval)
Sets the interval of major grid lines for the axis.
void setLabelSuffixPlacement(Qgis::PlotAxisSuffixPlacement placement)
Sets the placement for the axis label suffixes.
void setGridIntervalMinor(double interval)
Sets the interval of minor grid lines for the axis.
void setLabelInterval(double interval)
Sets the interval of labels for the axis.
double labelInterval() const
Returns the interval of labels for the axis.
QgsLineSymbol * gridMajorSymbol()
Returns the line symbol used to render the major lines in the axis grid.
void setLabelSuffix(const QString &suffix)
Sets the axis label suffix.
void setTextFormat(const QgsTextFormat &format)
Sets the text format used for the axis labels.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes the axis' properties into an XML element.
void setGridMinorSymbol(QgsLineSymbol *symbol)
Sets the symbol used to render the minor lines in the axis grid.
QgsNumericFormat * numericFormat() const
Returns the numeric format used for the axis labels.
Qgis::PlotAxisSuffixPlacement labelSuffixPlacement() const
Returns the placement for the axis label suffixes.
QString labelSuffix() const
Returns the axis label suffix, or an empty string if no label suffix is to be used.
static QgsFillSymbol * chartBorderSymbol()
Returns the default fill symbol to use for the chart area border.
static QgsNumericFormat * axisLabelNumericFormat()
Returns the default numeric format to use for plot axis labels.
static QgsLineSymbol * axisGridMinorSymbol()
Returns the default line symbol to use for axis minor grid lines.
static QgsFillSymbol * chartBackgroundSymbol()
Returns the default fill symbol to use for the chart area background fill.
static QgsLineSymbol * axisGridMajorSymbol()
Returns the default line symbol to use for axis major grid lines.
virtual bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the plot's properties from an XML element.
virtual bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes the plot's properties into an XML element.
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.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QgsExpressionContext & expressionContext()
Gets the expression context.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Container for all settings relating to text rendering.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
static void drawText(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, Qgis::TextVerticalAlignment vAlignment=Qgis::TextVerticalAlignment::Top, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle)
Draws text within a rectangle using the specified settings.
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Point, QFontMetricsF *fontMetrics=nullptr, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), double maxLineWidth=0)
Returns the height of a text based on a given format.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QList< QgsSymbolLayer * > QgsSymbolLayerList
Single variable definition for use within a QgsExpressionContextScope.