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;
263 for (
double currentY = firstYLabel; ; currentY += mYAxis.
labelInterval() )
278 if ( currentY == firstYLabel )
283 if ( !hasMoreLabels )
288 if ( currentY == firstYLabel || !hasMoreLabels )
294 if ( !hasMoreLabels )
299 const double chartAreaLeft = plotArea.left();
300 const double chartAreaRight = plotArea.right();
301 const double chartAreaTop = plotArea.top();
302 const double chartAreaBottom = plotArea.bottom();
305 mChartBackgroundSymbol->renderPolygon( QPolygonF(
307 QPointF( chartAreaLeft, chartAreaTop ),
308 QPointF( chartAreaRight, chartAreaTop ),
309 QPointF( chartAreaRight, chartAreaBottom ),
310 QPointF( chartAreaLeft, chartAreaBottom ),
311 QPointF( chartAreaLeft, chartAreaTop )
312 } ),
nullptr,
nullptr, context );
314 const double xScale = ( chartAreaRight - chartAreaLeft ) / ( mMaxX - mMinX );
315 const double yScale = ( chartAreaBottom - chartAreaTop ) / ( mMaxY - mMinY );
317 constexpr int MAX_OBJECTS = 1000;
323 double nextMajorXGrid = firstMajorXGrid;
324 int objectNumber = 0;
325 for (
double currentX = firstMinorXGrid; objectNumber < MAX_OBJECTS && ( currentX <= mMaxX && !
qgsDoubleNear( currentX, mMaxX, xTolerance ) ); currentX += mXAxis.
gridIntervalMinor(), ++objectNumber )
340 QPointF( ( currentX - mMinX ) * xScale + chartAreaLeft, chartAreaBottom ),
341 QPointF( ( currentX - mMinX ) * xScale + chartAreaLeft, chartAreaTop )
342 } ),
nullptr, context );
347 double nextMajorYGrid = firstMajorYGrid;
349 for (
double currentY = firstMinorYGrid; objectNumber < MAX_OBJECTS && ( currentY <= mMaxY && !
qgsDoubleNear( currentY, mMaxY, yTolerance ) ); currentY += mYAxis.
gridIntervalMinor(), ++objectNumber )
364 QPointF( chartAreaLeft, chartAreaBottom - ( currentY - mMinY ) * yScale ),
365 QPointF( chartAreaRight, chartAreaBottom - ( currentY - mMinY ) * yScale )
366 } ),
nullptr, context );
376 for (
double currentX = firstXLabel; ; currentX += mXAxis.
labelInterval(), ++objectNumber )
391 if ( objectNumber == 0 )
396 if ( !hasMoreLabels )
401 if ( objectNumber == 0 || !hasMoreLabels )
408 if ( !hasMoreLabels )
418 for (
double currentY = firstYLabel; ; currentY += mYAxis.
labelInterval(), ++objectNumber )
433 if ( objectNumber == 0 )
438 if ( !hasMoreLabels )
443 if ( objectNumber == 0 || !hasMoreLabels )
451 chartAreaBottom - ( currentY - mMinY ) * yScale + height / 2 ),
453 if ( !hasMoreLabels )
462 mChartBorderSymbol->renderPolygon( QPolygonF(
464 QPointF( chartAreaLeft, chartAreaTop ),
465 QPointF( chartAreaRight, chartAreaTop ),
466 QPointF( chartAreaRight, chartAreaBottom ),
467 QPointF( chartAreaLeft, chartAreaBottom ),
468 QPointF( chartAreaLeft, chartAreaTop )
469 } ),
nullptr,
nullptr, context );
471 mChartBackgroundSymbol->stopRender( context );
472 mChartBorderSymbol->stopRender( context );
513 constexpr int MAX_LABELS = 1000;
517 double maxXAxisLabelHeight = 0;
521 for (
double currentX = firstXLabel; ; currentX += mXAxis.
labelInterval(), labelNumber++ )
537 if ( labelNumber == 0 )
542 if ( !hasMoreLabels )
547 if ( labelNumber == 0 || !hasMoreLabels )
552 if ( !hasMoreLabels )
557 double maxYAxisLabelWidth = 0;
560 for (
double currentY = firstMinorYGrid; ; currentY += mYAxis.
gridIntervalMinor(), labelNumber ++ )
566 if ( yAxisSuffixWidth > 0 )
574 thisLabelWidth += yAxisSuffixWidth;
578 if ( labelNumber == 0 )
579 thisLabelWidth += yAxisSuffixWidth;
583 if ( !hasMoreLabels )
584 thisLabelWidth += yAxisSuffixWidth;
588 if ( labelNumber == 0 || !hasMoreLabels )
589 thisLabelWidth += yAxisSuffixWidth;
593 maxYAxisLabelWidth = std::max( maxYAxisLabelWidth, thisLabelWidth );
594 if ( !hasMoreLabels )
599 const double rightTextSize = 0;
601 const double topTextSize = 0;
608 return QRectF( leftMargin, topMargin, mSize.width() - rightMargin - leftMargin, mSize.height() - bottomMargin - topMargin );
613 if ( !mSize.isValid() )
617 constexpr double IDEAL_WIDTH = 0.4;
618 constexpr double TOLERANCE = 0.04;
619 constexpr int MAX_LABELS = 1000;
626 const double availableWidth = mSize.width() - leftMargin - rightMargin;
627 const double availableHeight = mSize.height() - topMargin - bottomMargin;
631 auto refineIntervalForAxis = [&](
double axisMinimum,
double axisMaximum,
632 const std::function< double(
double ) > &sizeForLabel,
633 double availableSize,
double idealSizePercent,
double sizeTolerancePercent,
634 double & labelInterval,
double & majorInterval,
double & minorInterval )
636 auto roundBase10 = [](
double value )->
double
638 return std::pow( 10, std::floor( std::log10( value ) ) );
642 double totalSize = 0;
643 int initialLabelCount = 0;
645 const double firstLabelPos = std::ceil( axisMinimum / labelInterval ) * labelInterval;
647 for (
double currentPos = firstLabelPos; initialLabelCount <= MAX_LABELS && currentPos <= axisMaximum; currentPos += labelInterval, ++initialLabelCount )
649 totalSize += sizeForLabel( currentPos );
654 if ( initialLabelCount >= MAX_LABELS || ( totalSize / availableSize < ( idealSizePercent - sizeTolerancePercent ) ) || ( totalSize / availableSize > ( idealSizePercent + sizeTolerancePercent ) ) )
657 int numberLabelsInitial = std::floor( availableSize / 30 );
659 double labelIntervalTest = ( axisMaximum - axisMinimum ) / numberLabelsInitial;
660 double baseValue = roundBase10( labelIntervalTest );
661 double candidate = baseValue;
662 int currentMultiplier = 1;
664 int numberLabels = 0;
667 const double firstLabelPosition = std::ceil( axisMinimum / candidate ) * candidate;
668 double totalSize = 0;
670 for (
double currentPos = firstLabelPosition; currentPos <= axisMaximum; currentPos += candidate )
672 totalSize += sizeForLabel( currentPos );
675 if ( numberLabels > MAX_LABELS )
679 if ( numberLabels <= MAX_LABELS && totalSize <= availableSize * idealSizePercent )
682 if ( currentMultiplier == 1 )
683 currentMultiplier = 2;
684 else if ( currentMultiplier == 2 )
685 currentMultiplier = 5;
686 else if ( currentMultiplier == 5 )
689 currentMultiplier = 1;
692 candidate = baseValue * currentMultiplier;
694 labelInterval = candidate;
695 if ( numberLabels < 10 )
697 minorInterval = labelInterval / 2;
698 majorInterval = minorInterval * 4;
702 minorInterval = labelInterval;
703 majorInterval = minorInterval * 5;
714 refineIntervalForAxis( mMinX, mMaxX, [
this, &context, suffixWidth, &numericContext](
double position ) ->
double
716 const QString text = mXAxis.
numericFormat()->formatDouble( position, numericContext );
721 IDEAL_WIDTH, TOLERANCE, labelIntervalX, majorIntervalX, minorIntervalX );
732 refineIntervalForAxis( mMinY, mMaxY, [
this, &context, suffixY, &numericContext](
double position ) ->
double
734 const QString text = mYAxis.
numericFormat()->formatDouble( position, numericContext );
739 IDEAL_WIDTH, TOLERANCE, labelIntervalY, majorIntervalY, minorIntervalY );
748 return mChartBackgroundSymbol.get();
753 mChartBackgroundSymbol.reset( symbol );
758 return mChartBorderSymbol.get();
763 mChartBorderSymbol.reset( symbol );
787 std::unique_ptr< QgsSimpleLineSymbolLayer > gridMajor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 150 ), 0.1 );
788 gridMajor->setPenCapStyle( Qt::FlatCap );
794 std::unique_ptr< QgsSimpleLineSymbolLayer > gridMinor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 50 ), 0.1 );
795 gridMinor->setPenCapStyle( Qt::FlatCap );
801 std::unique_ptr< QgsSimpleFillSymbolLayer > chartFill = std::make_unique< QgsSimpleFillSymbolLayer >( QColor( 255, 255, 255 ) );
807 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.
@ Millimeters
Millimeters.
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.