49 mNumericFormat = std::make_unique< QgsBasicNumericFormat >();
51 std::unique_ptr< QgsSimpleLineSymbolLayer > gridMinor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 50 ), 0.1 );
52 gridMinor->setPenCapStyle( Qt::FlatCap );
53 mGridMinorSymbol = std::make_unique< QgsLineSymbol>(
QgsSymbolLayerList( { gridMinor.release() } ) );
55 std::unique_ptr< QgsSimpleLineSymbolLayer > gridMajor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 150 ), 0.1 );
56 gridMajor->setPenCapStyle( Qt::FlatCap );
57 mGridMajorSymbol = std::make_unique< QgsLineSymbol>(
QgsSymbolLayerList( { gridMajor.release() } ) );
64 element.setAttribute( QStringLiteral(
"gridIntervalMinor" ),
qgsDoubleToString( mGridIntervalMinor ) );
65 element.setAttribute( QStringLiteral(
"gridIntervalMajor" ),
qgsDoubleToString( mGridIntervalMajor ) );
66 element.setAttribute( QStringLiteral(
"labelInterval" ),
qgsDoubleToString( mLabelInterval ) );
68 QDomElement numericFormatElement = document.createElement( QStringLiteral(
"numericFormat" ) );
69 mNumericFormat->writeXml( numericFormatElement, document, context );
70 element.appendChild( numericFormatElement );
72 QDomElement gridMajorElement = document.createElement( QStringLiteral(
"gridMajorSymbol" ) );
74 element.appendChild( gridMajorElement );
75 QDomElement gridMinorElement = document.createElement( QStringLiteral(
"gridMinorSymbol" ) );
77 element.appendChild( gridMinorElement );
79 QDomElement textFormatElement = document.createElement( QStringLiteral(
"textFormat" ) );
80 textFormatElement.appendChild( mLabelTextFormat.
writeXml( document, context ) );
81 element.appendChild( textFormatElement );
88 mGridIntervalMinor = element.attribute( QStringLiteral(
"gridIntervalMinor" ) ).toDouble();
89 mGridIntervalMajor = element.attribute( QStringLiteral(
"gridIntervalMajor" ) ).toDouble();
90 mLabelInterval = element.attribute( QStringLiteral(
"labelInterval" ) ).toDouble();
92 const QDomElement numericFormatElement = element.firstChildElement( QStringLiteral(
"numericFormat" ) );
95 const QDomElement gridMajorElement = element.firstChildElement( QStringLiteral(
"gridMajorSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
96 mGridMajorSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( gridMajorElement, context ) );
97 const QDomElement gridMinorElement = element.firstChildElement( QStringLiteral(
"gridMinorSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
98 mGridMinorSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( gridMinorElement, context ) );
100 const QDomElement textFormatElement = element.firstChildElement( QStringLiteral(
"textFormat" ) );
101 mLabelTextFormat.
readXml( textFormatElement, context );
108 return mNumericFormat.get();
113 mNumericFormat.reset( format );
118 return mGridMajorSymbol.get();
123 mGridMajorSymbol.reset( symbol );
128 return mGridMinorSymbol.get();
133 mGridMinorSymbol.reset( symbol );
138 return mLabelTextFormat;
143 mLabelTextFormat = format;
152 : mMargins( 2, 2, 2, 2 )
155 std::unique_ptr< QgsSimpleFillSymbolLayer > chartFill = std::make_unique< QgsSimpleFillSymbolLayer >( QColor( 255, 255, 255 ) );
156 mChartBackgroundSymbol = std::make_unique< QgsFillSymbol>(
QgsSymbolLayerList( { chartFill.release() } ) );
158 std::unique_ptr< QgsSimpleLineSymbolLayer > chartBorder = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20 ), 0.1 );
159 mChartBorderSymbol = std::make_unique< QgsFillSymbol>(
QgsSymbolLayerList( { chartBorder.release() } ) );
171 QDomElement xAxisElement = document.createElement( QStringLiteral(
"xAxis" ) );
172 mXAxis.
writeXml( xAxisElement, document, context );
173 element.appendChild( xAxisElement );
174 QDomElement yAxisElement = document.createElement( QStringLiteral(
"yAxis" ) );
175 mYAxis.
writeXml( yAxisElement, document, context );
176 element.appendChild( yAxisElement );
178 QDomElement backgroundElement = document.createElement( QStringLiteral(
"backgroundSymbol" ) );
180 element.appendChild( backgroundElement );
181 QDomElement borderElement = document.createElement( QStringLiteral(
"borderSymbol" ) );
183 element.appendChild( borderElement );
185 element.setAttribute( QStringLiteral(
"margins" ), mMargins.
toString() );
194 mMinX = element.attribute( QStringLiteral(
"minX" ) ).toDouble();
195 mMaxX = element.attribute( QStringLiteral(
"maxX" ) ).toDouble();
196 mMinY = element.attribute( QStringLiteral(
"minY" ) ).toDouble();
197 mMaxY = element.attribute( QStringLiteral(
"maxY" ) ).toDouble();
199 const QDomElement xAxisElement = element.firstChildElement( QStringLiteral(
"xAxis" ) );
200 mXAxis.
readXml( xAxisElement, context );
201 const QDomElement yAxisElement = element.firstChildElement( QStringLiteral(
"yAxis" ) );
202 mYAxis.
readXml( yAxisElement, context );
204 const QDomElement backgroundElement = element.firstChildElement( QStringLiteral(
"backgroundSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
205 mChartBackgroundSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( backgroundElement, context ) );
206 const QDomElement borderElement = element.firstChildElement( QStringLiteral(
"borderSymbol" ) ).firstChildElement( QStringLiteral(
"symbol" ) );
207 mChartBorderSymbol.reset( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( borderElement, context ) );
219 mChartBackgroundSymbol->startRender( context );
220 mChartBorderSymbol->startRender( context );
241 double maxYAxisLabelWidth = 0;
243 for (
double currentY = firstYLabel; currentY <= mMaxY && !
qgsDoubleNear( currentY, mMaxY, yTolerance ); currentY += mYAxis.
labelInterval() )
250 const double chartAreaLeft = plotArea.left();
251 const double chartAreaRight = plotArea.right();
252 const double chartAreaTop = plotArea.top();
253 const double chartAreaBottom = plotArea.bottom();
256 mChartBackgroundSymbol->renderPolygon( QPolygonF(
258 QPointF( chartAreaLeft, chartAreaTop ),
259 QPointF( chartAreaRight, chartAreaTop ),
260 QPointF( chartAreaRight, chartAreaBottom ),
261 QPointF( chartAreaLeft, chartAreaBottom ),
262 QPointF( chartAreaLeft, chartAreaTop )
263 } ),
nullptr,
nullptr, context );
265 const double xScale = ( chartAreaRight - chartAreaLeft ) / ( mMaxX - mMinX );
266 const double yScale = ( chartAreaBottom - chartAreaTop ) / ( mMaxY - mMinY );
268 constexpr int MAX_OBJECTS = 1000;
274 double nextMajorXGrid = firstMajorXGrid;
275 int objectNumber = 0;
276 for (
double currentX = firstMinorXGrid; objectNumber < MAX_OBJECTS && ( currentX <= mMaxX && !
qgsDoubleNear( currentX, mMaxX, xTolerance ) ); currentX += mXAxis.
gridIntervalMinor(), ++objectNumber )
291 QPointF( ( currentX - mMinX ) * xScale + chartAreaLeft, chartAreaBottom ),
292 QPointF( ( currentX - mMinX ) * xScale + chartAreaLeft, chartAreaTop )
293 } ),
nullptr, context );
298 double nextMajorYGrid = firstMajorYGrid;
300 for (
double currentY = firstMinorYGrid; objectNumber < MAX_OBJECTS && ( currentY <= mMaxY && !
qgsDoubleNear( currentY, mMaxY, yTolerance ) ); currentY += mYAxis.
gridIntervalMinor(), ++objectNumber )
315 QPointF( chartAreaLeft, chartAreaBottom - ( currentY - mMinY ) * yScale ),
316 QPointF( chartAreaRight, chartAreaBottom - ( currentY - mMinY ) * yScale )
317 } ),
nullptr, context );
325 for (
double currentX = firstXLabel; objectNumber < MAX_OBJECTS && ( currentX <= mMaxX ||
qgsDoubleNear( currentX, mMaxX, xTolerance ) ); currentX += mXAxis.
labelInterval(), ++objectNumber )
330 0, Qgis::TextHorizontalAlignment::Center, { text }, context, mXAxis.
textFormat() );
336 for (
double currentY = firstYLabel; objectNumber < MAX_OBJECTS && ( currentY <= mMaxY ||
qgsDoubleNear( currentY, mMaxY, yTolerance ) ); currentY += mYAxis.
labelInterval(), ++objectNumber )
343 chartAreaBottom - ( currentY - mMinY ) * yScale + height / 2 ),
344 0, Qgis::TextHorizontalAlignment::Right, { text }, context, mYAxis.
textFormat(),
false );
351 mChartBorderSymbol->renderPolygon( QPolygonF(
353 QPointF( chartAreaLeft, chartAreaTop ),
354 QPointF( chartAreaRight, chartAreaTop ),
355 QPointF( chartAreaRight, chartAreaBottom ),
356 QPointF( chartAreaLeft, chartAreaBottom ),
357 QPointF( chartAreaLeft, chartAreaTop )
358 } ),
nullptr,
nullptr, context );
360 mChartBackgroundSymbol->stopRender( context );
361 mChartBorderSymbol->stopRender( context );
398 constexpr int MAX_LABELS = 1000;
402 double maxXAxisLabelHeight = 0;
404 for (
double currentX = firstXLabel; labelNumber < MAX_LABELS && ( currentX <= mMaxX ||
qgsDoubleNear( currentX, mMaxX, xTolerance ) ); currentX += mXAxis.
labelInterval(), labelNumber++ )
411 double maxYAxisLabelWidth = 0;
414 for (
double currentY = firstMinorYGrid; labelNumber < MAX_LABELS && ( currentY <= mMaxY ||
qgsDoubleNear( currentY, mMaxY, yTolerance ) ); currentY += mYAxis.
gridIntervalMinor(), labelNumber ++ )
422 const double rightTextSize = 0;
424 const double topTextSize = 0;
431 return QRectF( leftMargin, topMargin, mSize.width() - rightMargin - leftMargin, mSize.height() - bottomMargin - topMargin );
437 constexpr double IDEAL_WIDTH = 0.4;
438 constexpr double TOLERANCE = 0.04;
439 constexpr int MAX_LABELS = 1000;
446 const double availableWidth = mSize.width() - leftMargin - rightMargin;
447 const double availableHeight = mSize.height() - topMargin - bottomMargin;
451 auto refineIntervalForAxis = [&](
double axisMinimum,
double axisMaximum,
452 const std::function< double(
double ) > &sizeForLabel,
453 double availableSize,
double idealSizePercent,
double sizeTolerancePercent,
454 double & labelInterval,
double & majorInterval,
double & minorInterval )
456 auto roundBase10 = [](
double value )->
double
458 return std::pow( 10, std::floor( std::log10( value ) ) );
462 double totalSize = 0;
463 int initialLabelCount = 0;
465 const double firstLabelPos = std::ceil( axisMinimum / labelInterval ) * labelInterval;
467 for (
double currentPos = firstLabelPos; initialLabelCount <= MAX_LABELS && currentPos <= axisMaximum; currentPos += labelInterval, ++initialLabelCount )
469 totalSize += sizeForLabel( currentPos );
474 if ( initialLabelCount >= MAX_LABELS || ( totalSize / availableSize < ( idealSizePercent - sizeTolerancePercent ) ) || ( totalSize / availableSize > ( idealSizePercent + sizeTolerancePercent ) ) )
477 int numberLabelsInitial = std::floor( availableSize / 30 );
479 double labelIntervalTest = ( axisMaximum - axisMinimum ) / numberLabelsInitial;
480 double baseValue = roundBase10( labelIntervalTest );
481 double candidate = baseValue;
482 int currentMultiplier = 1;
484 int numberLabels = 0;
487 const double firstLabelPosition = std::ceil( axisMinimum / candidate ) * candidate;
488 double totalSize = 0;
490 for (
double currentPos = firstLabelPosition; currentPos <= axisMaximum; currentPos += candidate )
492 totalSize += sizeForLabel( currentPos );
495 if ( numberLabels > MAX_LABELS )
499 if ( numberLabels <= MAX_LABELS && totalSize <= availableSize * idealSizePercent )
502 if ( currentMultiplier == 1 )
503 currentMultiplier = 2;
504 else if ( currentMultiplier == 2 )
505 currentMultiplier = 5;
506 else if ( currentMultiplier == 5 )
509 currentMultiplier = 1;
512 candidate = baseValue * currentMultiplier;
514 labelInterval = candidate;
515 if ( numberLabels < 10 )
517 minorInterval = labelInterval / 2;
518 majorInterval = minorInterval * 4;
522 minorInterval = labelInterval;
523 majorInterval = minorInterval * 5;
532 refineIntervalForAxis( mMinX, mMaxX, [ = ](
double position ) ->
double
534 const QString text = mXAxis.
numericFormat()->formatDouble( position, numericContext );
538 IDEAL_WIDTH, TOLERANCE, labelIntervalX, majorIntervalX, minorIntervalX );
548 refineIntervalForAxis( mMinY, mMaxY, [ = ](
double position ) ->
double
550 const QString text = mYAxis.
numericFormat()->formatDouble( position, numericContext );
553 IDEAL_WIDTH, TOLERANCE, labelIntervalY, majorIntervalY, minorIntervalY );
562 return mChartBackgroundSymbol.get();
567 mChartBackgroundSymbol.reset( symbol );
572 return mChartBorderSymbol.get();
577 mChartBorderSymbol.reset( symbol );
void calculateOptimisedIntervals(QgsRenderContext &context)
Automatically sets the grid and label intervals to optimal values for display in the given render con...
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, 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.
bool writeXml(QDomElement &element, QDomDocument &document, QgsReadWriteContext &context) const override
Writes the plot's properties into an XML element.
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.
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.
bool readXml(const QDomElement &element, QgsReadWriteContext &context)
Reads the axis' properties from an XML element.
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.
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.
bool writeXml(QDomElement &element, QDomDocument &document, QgsReadWriteContext &context) const
Writes the axis' properties into an XML element.
virtual bool readXml(const QDomElement &element, QgsReadWriteContext &context)
Reads the plot's properties from an XML element.
virtual bool writeXml(QDomElement &element, QDomDocument &document, 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, QgsUnitTypes::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 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.
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())
Draws text within a rectangle using the specified settings.
@ RenderMillimeters
Millimeters.
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.