57 element.setAttribute( QStringLiteral(
"gridIntervalMinor" ),
qgsDoubleToString( mGridIntervalMinor ) );
58 element.setAttribute( QStringLiteral(
"gridIntervalMajor" ),
qgsDoubleToString( mGridIntervalMajor ) );
59 element.setAttribute( QStringLiteral(
"labelInterval" ),
qgsDoubleToString( mLabelInterval ) );
61 QDomElement numericFormatElement = document.createElement( QStringLiteral(
"numericFormat" ) );
62 mNumericFormat->writeXml( numericFormatElement, document, context );
63 element.appendChild( numericFormatElement );
65 QDomElement gridMajorElement = document.createElement( QStringLiteral(
"gridMajorSymbol" ) );
67 element.appendChild( gridMajorElement );
68 QDomElement gridMinorElement = document.createElement( QStringLiteral(
"gridMinorSymbol" ) );
70 element.appendChild( gridMinorElement );
72 QDomElement textFormatElement = document.createElement( QStringLiteral(
"textFormat" ) );
73 textFormatElement.appendChild( mLabelTextFormat.
writeXml( document, context ) );
74 element.appendChild( textFormatElement );
81 mGridIntervalMinor = element.attribute( QStringLiteral(
"gridIntervalMinor" ) ).toDouble();
82 mGridIntervalMajor = element.attribute( QStringLiteral(
"gridIntervalMajor" ) ).toDouble();
83 mLabelInterval = element.attribute( QStringLiteral(
"labelInterval" ) ).toDouble();
85 const QDomElement numericFormatElement = element.firstChildElement( QStringLiteral(
"numericFormat" ) );
88 const QDomElement gridMajorElement = element.firstChildElement( QStringLiteral(
"gridMajorSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
89 mGridMajorSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( gridMajorElement, context ) );
90 const QDomElement gridMinorElement = element.firstChildElement( QStringLiteral(
"gridMinorSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
91 mGridMinorSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( gridMinorElement, context ) );
93 const QDomElement textFormatElement = element.firstChildElement( QStringLiteral(
"textFormat" ) );
94 mLabelTextFormat.
readXml( textFormatElement, context );
101 return mNumericFormat.get();
106 mNumericFormat.reset( format );
111 return mGridMajorSymbol.get();
116 mGridMajorSymbol.reset( symbol );
121 return mGridMinorSymbol.get();
126 mGridMinorSymbol.reset( symbol );
131 return mLabelTextFormat;
136 mLabelTextFormat = format;
145 : mMargins( 2, 2, 2, 2 )
161 QDomElement xAxisElement = document.createElement( QStringLiteral(
"xAxis" ) );
162 mXAxis.
writeXml( xAxisElement, document, context );
163 element.appendChild( xAxisElement );
164 QDomElement yAxisElement = document.createElement( QStringLiteral(
"yAxis" ) );
165 mYAxis.
writeXml( yAxisElement, document, context );
166 element.appendChild( yAxisElement );
168 QDomElement backgroundElement = document.createElement( QStringLiteral(
"backgroundSymbol" ) );
170 element.appendChild( backgroundElement );
171 QDomElement borderElement = document.createElement( QStringLiteral(
"borderSymbol" ) );
173 element.appendChild( borderElement );
175 element.setAttribute( QStringLiteral(
"margins" ), mMargins.
toString() );
184 mMinX = element.attribute( QStringLiteral(
"minX" ) ).toDouble();
185 mMaxX = element.attribute( QStringLiteral(
"maxX" ) ).toDouble();
186 mMinY = element.attribute( QStringLiteral(
"minY" ) ).toDouble();
187 mMaxY = element.attribute( QStringLiteral(
"maxY" ) ).toDouble();
189 const QDomElement xAxisElement = element.firstChildElement( QStringLiteral(
"xAxis" ) );
190 mXAxis.
readXml( xAxisElement, context );
191 const QDomElement yAxisElement = element.firstChildElement( QStringLiteral(
"yAxis" ) );
192 mYAxis.
readXml( yAxisElement, context );
194 const QDomElement backgroundElement = element.firstChildElement( QStringLiteral(
"backgroundSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
195 mChartBackgroundSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( backgroundElement, context ) );
196 const QDomElement borderElement = element.firstChildElement( QStringLiteral(
"borderSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
197 mChartBorderSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( borderElement, context ) );
209 mChartBackgroundSymbol->startRender( context );
210 mChartBorderSymbol->startRender( context );
231 double maxYAxisLabelWidth = 0;
233 for (
double currentY = firstYLabel; currentY <= mMaxY && !
qgsDoubleNear( currentY, mMaxY, yTolerance ); currentY += mYAxis.
labelInterval() )
240 const double chartAreaLeft = plotArea.left();
241 const double chartAreaRight = plotArea.right();
242 const double chartAreaTop = plotArea.top();
243 const double chartAreaBottom = plotArea.bottom();
246 mChartBackgroundSymbol->renderPolygon( QPolygonF(
248 QPointF( chartAreaLeft, chartAreaTop ),
249 QPointF( chartAreaRight, chartAreaTop ),
250 QPointF( chartAreaRight, chartAreaBottom ),
251 QPointF( chartAreaLeft, chartAreaBottom ),
252 QPointF( chartAreaLeft, chartAreaTop )
253 } ),
nullptr,
nullptr, context );
255 const double xScale = ( chartAreaRight - chartAreaLeft ) / ( mMaxX - mMinX );
256 const double yScale = ( chartAreaBottom - chartAreaTop ) / ( mMaxY - mMinY );
258 constexpr int MAX_OBJECTS = 1000;
264 double nextMajorXGrid = firstMajorXGrid;
265 int objectNumber = 0;
266 for (
double currentX = firstMinorXGrid; objectNumber < MAX_OBJECTS && ( currentX <= mMaxX && !
qgsDoubleNear( currentX, mMaxX, xTolerance ) ); currentX += mXAxis.
gridIntervalMinor(), ++objectNumber )
281 QPointF( ( currentX - mMinX ) * xScale + chartAreaLeft, chartAreaBottom ),
282 QPointF( ( currentX - mMinX ) * xScale + chartAreaLeft, chartAreaTop )
283 } ),
nullptr, context );
288 double nextMajorYGrid = firstMajorYGrid;
290 for (
double currentY = firstMinorYGrid; objectNumber < MAX_OBJECTS && ( currentY <= mMaxY && !
qgsDoubleNear( currentY, mMaxY, yTolerance ) ); currentY += mYAxis.
gridIntervalMinor(), ++objectNumber )
305 QPointF( chartAreaLeft, chartAreaBottom - ( currentY - mMinY ) * yScale ),
306 QPointF( chartAreaRight, chartAreaBottom - ( currentY - mMinY ) * yScale )
307 } ),
nullptr, context );
315 for (
double currentX = firstXLabel; objectNumber < MAX_OBJECTS && ( currentX <= mMaxX ||
qgsDoubleNear( currentX, mMaxX, xTolerance ) ); currentX += mXAxis.
labelInterval(), ++objectNumber )
320 0, Qgis::TextHorizontalAlignment::Center, { text }, context, mXAxis.
textFormat() );
326 for (
double currentY = firstYLabel; objectNumber < MAX_OBJECTS && ( currentY <= mMaxY ||
qgsDoubleNear( currentY, mMaxY, yTolerance ) ); currentY += mYAxis.
labelInterval(), ++objectNumber )
333 chartAreaBottom - ( currentY - mMinY ) * yScale + height / 2 ),
334 0, Qgis::TextHorizontalAlignment::Right, { text }, context, mYAxis.
textFormat(),
false );
341 mChartBorderSymbol->renderPolygon( QPolygonF(
343 QPointF( chartAreaLeft, chartAreaTop ),
344 QPointF( chartAreaRight, chartAreaTop ),
345 QPointF( chartAreaRight, chartAreaBottom ),
346 QPointF( chartAreaLeft, chartAreaBottom ),
347 QPointF( chartAreaLeft, chartAreaTop )
348 } ),
nullptr,
nullptr, context );
350 mChartBackgroundSymbol->stopRender( context );
351 mChartBorderSymbol->stopRender( context );
388 constexpr int MAX_LABELS = 1000;
392 double maxXAxisLabelHeight = 0;
394 for (
double currentX = firstXLabel; labelNumber < MAX_LABELS && ( currentX <= mMaxX ||
qgsDoubleNear( currentX, mMaxX, xTolerance ) ); currentX += mXAxis.
labelInterval(), labelNumber++ )
401 double maxYAxisLabelWidth = 0;
404 for (
double currentY = firstMinorYGrid; labelNumber < MAX_LABELS && ( currentY <= mMaxY ||
qgsDoubleNear( currentY, mMaxY, yTolerance ) ); currentY += mYAxis.
gridIntervalMinor(), labelNumber ++ )
411 const double leftTextSize = maxYAxisLabelWidth + context.
convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
412 const double rightTextSize = 0;
413 const double bottomTextSize = maxXAxisLabelHeight + context.
convertToPainterUnits( 0.5, Qgis::RenderUnit::Millimeters );
414 const double topTextSize = 0;
416 const double leftMargin = context.
convertToPainterUnits( mMargins.
left(), Qgis::RenderUnit::Millimeters ) + leftTextSize;
418 const double topMargin = context.
convertToPainterUnits( mMargins.
top(), Qgis::RenderUnit::Millimeters ) + topTextSize;
421 return QRectF( leftMargin, topMargin, mSize.width() - rightMargin - leftMargin, mSize.height() - bottomMargin - topMargin );
427 constexpr double IDEAL_WIDTH = 0.4;
428 constexpr double TOLERANCE = 0.04;
429 constexpr int MAX_LABELS = 1000;
436 const double availableWidth = mSize.width() - leftMargin - rightMargin;
437 const double availableHeight = mSize.height() - topMargin - bottomMargin;
441 auto refineIntervalForAxis = [&](
double axisMinimum,
double axisMaximum,
442 const std::function< double(
double ) > &sizeForLabel,
443 double availableSize,
double idealSizePercent,
double sizeTolerancePercent,
444 double & labelInterval,
double & majorInterval,
double & minorInterval )
446 auto roundBase10 = [](
double value )->
double
448 return std::pow( 10, std::floor( std::log10( value ) ) );
452 double totalSize = 0;
453 int initialLabelCount = 0;
455 const double firstLabelPos = std::ceil( axisMinimum / labelInterval ) * labelInterval;
457 for (
double currentPos = firstLabelPos; initialLabelCount <= MAX_LABELS && currentPos <= axisMaximum; currentPos += labelInterval, ++initialLabelCount )
459 totalSize += sizeForLabel( currentPos );
464 if ( initialLabelCount >= MAX_LABELS || ( totalSize / availableSize < ( idealSizePercent - sizeTolerancePercent ) ) || ( totalSize / availableSize > ( idealSizePercent + sizeTolerancePercent ) ) )
467 int numberLabelsInitial = std::floor( availableSize / 30 );
469 double labelIntervalTest = ( axisMaximum - axisMinimum ) / numberLabelsInitial;
470 double baseValue = roundBase10( labelIntervalTest );
471 double candidate = baseValue;
472 int currentMultiplier = 1;
474 int numberLabels = 0;
477 const double firstLabelPosition = std::ceil( axisMinimum / candidate ) * candidate;
478 double totalSize = 0;
480 for (
double currentPos = firstLabelPosition; currentPos <= axisMaximum; currentPos += candidate )
482 totalSize += sizeForLabel( currentPos );
485 if ( numberLabels > MAX_LABELS )
489 if ( numberLabels <= MAX_LABELS && totalSize <= availableSize * idealSizePercent )
492 if ( currentMultiplier == 1 )
493 currentMultiplier = 2;
494 else if ( currentMultiplier == 2 )
495 currentMultiplier = 5;
496 else if ( currentMultiplier == 5 )
499 currentMultiplier = 1;
502 candidate = baseValue * currentMultiplier;
504 labelInterval = candidate;
505 if ( numberLabels < 10 )
507 minorInterval = labelInterval / 2;
508 majorInterval = minorInterval * 4;
512 minorInterval = labelInterval;
513 majorInterval = minorInterval * 5;
522 refineIntervalForAxis( mMinX, mMaxX, [ = ](
double position ) ->
double
524 const QString text = mXAxis.
numericFormat()->formatDouble( position, numericContext );
528 IDEAL_WIDTH, TOLERANCE, labelIntervalX, majorIntervalX, minorIntervalX );
538 refineIntervalForAxis( mMinY, mMaxY, [ = ](
double position ) ->
double
540 const QString text = mYAxis.
numericFormat()->formatDouble( position, numericContext );
543 IDEAL_WIDTH, TOLERANCE, labelIntervalY, majorIntervalY, minorIntervalY );
552 return mChartBackgroundSymbol.get();
557 mChartBackgroundSymbol.reset( symbol );
562 return mChartBorderSymbol.get();
567 mChartBorderSymbol.reset( symbol );
591 std::unique_ptr< QgsSimpleLineSymbolLayer > gridMajor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 150 ), 0.1 );
592 gridMajor->setPenCapStyle( Qt::FlatCap );
598 std::unique_ptr< QgsSimpleLineSymbolLayer > gridMinor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 50 ), 0.1 );
599 gridMinor->setPenCapStyle( Qt::FlatCap );
605 std::unique_ptr< QgsSimpleFillSymbolLayer > chartFill = std::make_unique< QgsSimpleFillSymbolLayer >( QColor( 255, 255, 255 ) );
611 std::unique_ptr< QgsSimpleLineSymbolLayer > chartBorder = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20 ), 0.1 );
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 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 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.
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.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
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.