[FEATURE][diagrams] New diagram type "stacked bars"
Stacks bars of varying colors for each attribute on top of each other vertically or horizontally. Sponsored by SLYR
@ -797,6 +797,7 @@
|
||||
<file>themes/default/mActionShowUnplacedLabel.svg</file>
|
||||
<file>themes/default/mActionHandleStoreFilterExpressionChecked.svg</file>
|
||||
<file>themes/default/mActionHandleStoreFilterExpressionUnchecked.svg</file>
|
||||
<file>themes/default/stacked-bar.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/images/tips">
|
||||
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
|
||||
|
1
images/themes/default/stacked-bar.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path fill="#fce94f" stroke="#c4a000" stroke-linecap="round" d="M4.5 15.5v-7h7v7z"/><path fill="#8ae234" stroke="#4e9a06" stroke-linecap="round" d="M4.5 7.5v-2h7v2z"/><path fill="#ef2929" stroke="#a40000" stroke-linecap="round" d="M4.5 4.5v-4h7v4z"/></svg>
|
After Width: | Height: | Size: 320 B |
@ -20,6 +20,19 @@ Base class for all diagram types*
|
||||
%End
|
||||
public:
|
||||
|
||||
%ConvertToSubClassCode
|
||||
if ( sipCpp->diagramName() == QStringLiteral( "Pie" ) )
|
||||
sipType = sipType_QgsPieDiagram;
|
||||
else if ( sipCpp->diagramName() == QStringLiteral( "Histogram" ) )
|
||||
sipType = sipType_QgsHistogramDiagram;
|
||||
else if ( sipCpp->diagramName() == QStringLiteral( "Text" ) )
|
||||
sipType = sipType_QgsTextDiagram;
|
||||
else if ( sipCpp->diagramName() == QStringLiteral( "Stacked" ) )
|
||||
sipType = sipType_QgsStackedBarDiagram;
|
||||
else
|
||||
sipType = NULL;
|
||||
%End
|
||||
|
||||
virtual ~QgsDiagram();
|
||||
|
||||
virtual QgsDiagram *clone() const = 0 /Factory/;
|
||||
|
@ -0,0 +1,53 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/diagram/qgsstackedbardiagram.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsStackedBarDiagram: QgsDiagram
|
||||
{
|
||||
%Docstring
|
||||
|
||||
A stacked bar chart diagram.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsstackedbardiagram.h"
|
||||
%End
|
||||
public:
|
||||
QgsStackedBarDiagram();
|
||||
|
||||
virtual QgsStackedBarDiagram *clone() const /Factory/;
|
||||
|
||||
|
||||
virtual void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position );
|
||||
|
||||
|
||||
virtual QSizeF diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s );
|
||||
|
||||
virtual QSizeF diagramSize( const QgsFeature &feature, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is );
|
||||
|
||||
virtual double legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const;
|
||||
|
||||
virtual QString diagramName() const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/diagram/qgsstackedbardiagram.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -252,6 +252,7 @@
|
||||
%Include auto_generated/diagram/qgsdiagram.sip
|
||||
%Include auto_generated/diagram/qgshistogramdiagram.sip
|
||||
%Include auto_generated/diagram/qgspiediagram.sip
|
||||
%Include auto_generated/diagram/qgsstackedbardiagram.sip
|
||||
%Include auto_generated/diagram/qgstextdiagram.sip
|
||||
%Include auto_generated/dxf/qgsdxfexport.sip
|
||||
%Include auto_generated/effects/qgsblureffect.sip
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "diagram/qgshistogramdiagram.h"
|
||||
#include "diagram/qgspiediagram.h"
|
||||
#include "diagram/qgstextdiagram.h"
|
||||
#include "diagram/qgsstackedbardiagram.h"
|
||||
|
||||
#include "qgsproject.h"
|
||||
#include "qgsapplication.h"
|
||||
@ -92,13 +93,15 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare
|
||||
|
||||
mDiagramTypeComboBox->blockSignals( true );
|
||||
QPixmap pix = QgsApplication::getThemePixmap( QStringLiteral( "diagramNone" ) );
|
||||
mDiagramTypeComboBox->addItem( pix, tr( "No diagrams" ), "None" );
|
||||
mDiagramTypeComboBox->addItem( pix, tr( "No Diagrams" ), "None" );
|
||||
pix = QgsApplication::getThemePixmap( QStringLiteral( "pie-chart" ) );
|
||||
mDiagramTypeComboBox->addItem( pix, tr( "Pie chart" ), DIAGRAM_NAME_PIE );
|
||||
mDiagramTypeComboBox->addItem( pix, tr( "Pie Chart" ), DIAGRAM_NAME_PIE );
|
||||
pix = QgsApplication::getThemePixmap( QStringLiteral( "text" ) );
|
||||
mDiagramTypeComboBox->addItem( pix, tr( "Text diagram" ), DIAGRAM_NAME_TEXT );
|
||||
mDiagramTypeComboBox->addItem( pix, tr( "Text Diagram" ), DIAGRAM_NAME_TEXT );
|
||||
pix = QgsApplication::getThemePixmap( QStringLiteral( "histogram" ) );
|
||||
mDiagramTypeComboBox->addItem( pix, tr( "Histogram" ), DIAGRAM_NAME_HISTOGRAM );
|
||||
pix = QgsApplication::getThemePixmap( QStringLiteral( "stacked-bar" ) );
|
||||
mDiagramTypeComboBox->addItem( pix, tr( "Stacked Bars" ), DIAGRAM_NAME_STACKED );
|
||||
mDiagramTypeComboBox->blockSignals( false );
|
||||
|
||||
mAxisLineStyleButton->setSymbolType( QgsSymbol::Line );
|
||||
@ -549,7 +552,7 @@ void QgsDiagramProperties::mDiagramTypeComboBox_currentIndexChanged( int index )
|
||||
mDiagramFontButton->hide();
|
||||
}
|
||||
|
||||
if ( DIAGRAM_NAME_HISTOGRAM == mDiagramType )
|
||||
if ( DIAGRAM_NAME_HISTOGRAM == mDiagramType || DIAGRAM_NAME_STACKED == mDiagramType )
|
||||
{
|
||||
mBarWidthLabel->show();
|
||||
mBarWidthSpinBox->show();
|
||||
@ -558,9 +561,10 @@ void QgsDiagramProperties::mDiagramTypeComboBox_currentIndexChanged( int index )
|
||||
mBarSpacingUnitComboBox->show();
|
||||
mBarOptionsFrame->show();
|
||||
mShowAxisGroupBox->show();
|
||||
if ( DIAGRAM_NAME_HISTOGRAM == mDiagramType )
|
||||
mAttributeBasedScalingRadio->setChecked( true );
|
||||
mFixedSizeRadio->setEnabled( false );
|
||||
mDiagramSizeSpinBox->setEnabled( false );
|
||||
mFixedSizeRadio->setEnabled( DIAGRAM_NAME_STACKED == mDiagramType );
|
||||
mDiagramSizeSpinBox->setEnabled( DIAGRAM_NAME_STACKED == mDiagramType );
|
||||
mLinearlyScalingLabel->setText( tr( "Bar length: Scale linearly, so that the following value matches the specified bar length:" ) );
|
||||
mSizeLabel->setText( tr( "Bar length" ) );
|
||||
mFrameIncreaseSize->setVisible( false );
|
||||
@ -748,7 +752,7 @@ void QgsDiagramProperties::apply()
|
||||
int index = mDiagramTypeComboBox->currentIndex();
|
||||
bool diagramsEnabled = ( index != 0 );
|
||||
|
||||
QgsDiagram *diagram = nullptr;
|
||||
std::unique_ptr< QgsDiagram > diagram;
|
||||
|
||||
if ( diagramsEnabled && 0 == mDiagramAttributesTreeWidget->topLevelItemCount() )
|
||||
{
|
||||
@ -760,15 +764,19 @@ void QgsDiagramProperties::apply()
|
||||
|
||||
if ( mDiagramType == DIAGRAM_NAME_TEXT )
|
||||
{
|
||||
diagram = new QgsTextDiagram();
|
||||
diagram = qgis::make_unique< QgsTextDiagram >();
|
||||
}
|
||||
else if ( mDiagramType == DIAGRAM_NAME_PIE )
|
||||
{
|
||||
diagram = new QgsPieDiagram();
|
||||
diagram = qgis::make_unique< QgsPieDiagram >();
|
||||
}
|
||||
else if ( mDiagramType == DIAGRAM_NAME_STACKED )
|
||||
{
|
||||
diagram = qgis::make_unique< QgsStackedBarDiagram >();
|
||||
}
|
||||
else // if ( diagramType == DIAGRAM_NAME_HISTOGRAM )
|
||||
{
|
||||
diagram = new QgsHistogramDiagram();
|
||||
diagram = qgis::make_unique< QgsHistogramDiagram >();
|
||||
}
|
||||
|
||||
QgsDiagramSettings ds;
|
||||
@ -799,7 +807,7 @@ void QgsDiagramProperties::apply()
|
||||
ds.lineSizeUnit = mDiagramLineUnitComboBox->unit();
|
||||
ds.lineSizeScale = mDiagramLineUnitComboBox->getMapUnitScale();
|
||||
ds.labelPlacementMethod = static_cast<QgsDiagramSettings::LabelPlacementMethod>( mLabelPlacementComboBox->currentData().toInt() );
|
||||
ds.scaleByArea = mScaleDependencyComboBox->currentData().toBool();
|
||||
ds.scaleByArea = ( mDiagramType == DIAGRAM_NAME_STACKED ) ? false : mScaleDependencyComboBox->currentData().toBool();
|
||||
|
||||
if ( mIncreaseSmallDiagramsCheck->isChecked() )
|
||||
{
|
||||
@ -865,7 +873,7 @@ void QgsDiagramProperties::apply()
|
||||
|
||||
renderer = dr;
|
||||
}
|
||||
renderer->setDiagram( diagram );
|
||||
renderer->setDiagram( diagram.release() );
|
||||
renderer->setAttributeLegend( mCheckBoxAttributeLegend->isChecked() );
|
||||
mLayer->setDiagramRenderer( renderer );
|
||||
|
||||
|
@ -76,6 +76,7 @@ SET(QGIS_CORE_SRCS
|
||||
diagram/qgsdiagram.cpp
|
||||
diagram/qgshistogramdiagram.cpp
|
||||
diagram/qgspiediagram.cpp
|
||||
diagram/qgsstackedbardiagram.cpp
|
||||
diagram/qgstextdiagram.cpp
|
||||
|
||||
effects/qgsblureffect.cpp
|
||||
@ -944,6 +945,7 @@ SET(QGIS_CORE_HDRS
|
||||
diagram/qgsdiagram.h
|
||||
diagram/qgshistogramdiagram.h
|
||||
diagram/qgspiediagram.h
|
||||
diagram/qgsstackedbardiagram.h
|
||||
diagram/qgstextdiagram.h
|
||||
|
||||
dxf/qgsdxfexport.h
|
||||
|
@ -39,6 +39,21 @@ class CORE_EXPORT QgsDiagram
|
||||
{
|
||||
public:
|
||||
|
||||
#ifdef SIP_RUN
|
||||
SIP_CONVERT_TO_SUBCLASS_CODE
|
||||
if ( sipCpp->diagramName() == QStringLiteral( "Pie" ) )
|
||||
sipType = sipType_QgsPieDiagram;
|
||||
else if ( sipCpp->diagramName() == QStringLiteral( "Histogram" ) )
|
||||
sipType = sipType_QgsHistogramDiagram;
|
||||
else if ( sipCpp->diagramName() == QStringLiteral( "Text" ) )
|
||||
sipType = sipType_QgsTextDiagram;
|
||||
else if ( sipCpp->diagramName() == QStringLiteral( "Stacked" ) )
|
||||
sipType = sipType_QgsStackedBarDiagram;
|
||||
else
|
||||
sipType = NULL;
|
||||
SIP_END
|
||||
#endif
|
||||
|
||||
virtual ~QgsDiagram() { clearCache(); }
|
||||
|
||||
/**
|
||||
|
238
src/core/diagram/qgsstackedbardiagram.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
/***************************************************************************
|
||||
qgsstackedbardiagram.cpp
|
||||
---------------------
|
||||
begin : November 2019
|
||||
copyright : (C) 2019 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsstackedbardiagram.h"
|
||||
#include "qgsdiagramrenderer.h"
|
||||
#include "qgsrendercontext.h"
|
||||
#include "qgsexpression.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
QgsStackedBarDiagram::QgsStackedBarDiagram()
|
||||
{
|
||||
mCategoryBrush.setStyle( Qt::SolidPattern );
|
||||
mPen.setStyle( Qt::SolidLine );
|
||||
}
|
||||
|
||||
QgsStackedBarDiagram *QgsStackedBarDiagram::clone() const
|
||||
{
|
||||
return new QgsStackedBarDiagram( *this );
|
||||
}
|
||||
|
||||
QSizeF QgsStackedBarDiagram::diagramSize( const QgsFeature &feature, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is )
|
||||
{
|
||||
if ( qgsDoubleNear( is.upperValue, is.lowerValue ) )
|
||||
return QSizeF(); // invalid value range => zero size
|
||||
|
||||
QVariant attrVal;
|
||||
if ( is.classificationAttributeIsExpression )
|
||||
{
|
||||
QgsExpressionContext expressionContext = c.expressionContext();
|
||||
if ( !feature.fields().isEmpty() )
|
||||
expressionContext.setFields( feature.fields() );
|
||||
expressionContext.setFeature( feature );
|
||||
|
||||
QgsExpression *expression = getExpression( is.classificationAttributeExpression, expressionContext );
|
||||
attrVal = expression->evaluate( &expressionContext );
|
||||
}
|
||||
else
|
||||
{
|
||||
attrVal = feature.attribute( is.classificationField );
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
double value = attrVal.toDouble( &ok );
|
||||
if ( !ok )
|
||||
{
|
||||
return QSizeF(); //zero size if attribute is missing
|
||||
}
|
||||
|
||||
QSizeF size = sizeForValue( value, s, is );
|
||||
|
||||
// eh - this method returns size in unknown units ...! We'll have to fake it and use a rough estimation of
|
||||
// a conversion factor to painter units...
|
||||
// TODO QGIS 4.0 -- these methods should all use painter units, dependent on the render context scaling...
|
||||
double painterUnitConversionScale = c.convertToPainterUnits( 1, s.sizeType );
|
||||
|
||||
const double spacing = c.convertToPainterUnits( s.spacing(), s.spacingUnit(), s.spacingMapUnitScale() ) / painterUnitConversionScale;
|
||||
mApplySpacingAdjust = true;
|
||||
|
||||
switch ( s.diagramOrientation )
|
||||
{
|
||||
case QgsDiagramSettings::Up:
|
||||
case QgsDiagramSettings::Down:
|
||||
{
|
||||
const double totalBarLength = size.height() + spacing * std::max( 0, s.categoryAttributes.size() - 1 );
|
||||
size = QSizeF( s.barWidth, totalBarLength );
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsDiagramSettings::Right:
|
||||
case QgsDiagramSettings::Left:
|
||||
{
|
||||
const double totalBarLength = size.width() + spacing * std::max( 0, s.categoryAttributes.size() - 1 );
|
||||
size = QSizeF( totalBarLength, s.barWidth );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
double QgsStackedBarDiagram::legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const
|
||||
{
|
||||
if ( qgsDoubleNear( is.upperValue, is.lowerValue ) )
|
||||
return s.minimumSize; // invalid value range => zero size
|
||||
|
||||
// Scale, if extension is smaller than the specified minimum
|
||||
if ( value < s.minimumSize )
|
||||
{
|
||||
value = s.minimumSize;
|
||||
}
|
||||
|
||||
double scaleFactor = ( ( is.upperSize.width() - is.lowerSize.width() ) / ( is.upperValue - is.lowerValue ) );
|
||||
return value * scaleFactor;
|
||||
}
|
||||
|
||||
QString QgsStackedBarDiagram::diagramName() const
|
||||
{
|
||||
return DIAGRAM_NAME_STACKED;
|
||||
}
|
||||
|
||||
QSizeF QgsStackedBarDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s )
|
||||
{
|
||||
Q_UNUSED( c )
|
||||
QSizeF size;
|
||||
|
||||
if ( attributes.isEmpty() )
|
||||
{
|
||||
return QSizeF(); //zero size if no attributes
|
||||
}
|
||||
|
||||
double totalSum = 0;
|
||||
for ( int i = 0; i < attributes.count(); ++i )
|
||||
{
|
||||
totalSum += attributes.at( i ).toDouble();
|
||||
}
|
||||
|
||||
// eh - this method returns size in unknown units ...! We'll have to fake it and use a rough estimation of
|
||||
// a conversion factor to painter units...
|
||||
// TODO QGIS 4.0 -- these methods should all use painter units, dependent on the render context scaling...
|
||||
double painterUnitConversionScale = c.convertToPainterUnits( 1, s.sizeType );
|
||||
|
||||
const double spacing = c.convertToPainterUnits( s.spacing(), s.spacingUnit(), s.spacingMapUnitScale() ) / painterUnitConversionScale;
|
||||
|
||||
switch ( s.diagramOrientation )
|
||||
{
|
||||
case QgsDiagramSettings::Up:
|
||||
case QgsDiagramSettings::Down:
|
||||
size.scale( s.barWidth, s.size.height() + spacing * std::max( 0, s.categoryAttributes.size() - 1 ), Qt::IgnoreAspectRatio );
|
||||
break;
|
||||
|
||||
case QgsDiagramSettings::Right:
|
||||
case QgsDiagramSettings::Left:
|
||||
size.scale( s.size.width() + spacing * std::max( 0, s.categoryAttributes.size() - 1 ), s.barWidth, Qt::IgnoreAspectRatio );
|
||||
break;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void QgsStackedBarDiagram::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position )
|
||||
{
|
||||
QPainter *p = c.painter();
|
||||
if ( !p )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QList<double> values;
|
||||
|
||||
QgsExpressionContext expressionContext = c.expressionContext();
|
||||
expressionContext.setFeature( feature );
|
||||
if ( !feature.fields().isEmpty() )
|
||||
expressionContext.setFields( feature.fields() );
|
||||
|
||||
values.reserve( s.categoryAttributes.size() );
|
||||
double total = 0;
|
||||
for ( const QString &cat : qgis::as_const( s.categoryAttributes ) )
|
||||
{
|
||||
QgsExpression *expression = getExpression( cat, expressionContext );
|
||||
double currentVal = expression->evaluate( &expressionContext ).toDouble();
|
||||
values.push_back( currentVal );
|
||||
total += currentVal;
|
||||
}
|
||||
|
||||
const double spacing = c.convertToPainterUnits( s.spacing(), s.spacingUnit(), s.spacingMapUnitScale() );
|
||||
const double totalSpacing = std::max( 0, s.categoryAttributes.size() - 1 ) * spacing;
|
||||
|
||||
double scaledMaxVal = 0;
|
||||
switch ( s.diagramOrientation )
|
||||
{
|
||||
case QgsDiagramSettings::Up:
|
||||
case QgsDiagramSettings::Down:
|
||||
scaledMaxVal = sizePainterUnits( s.size.height(), s, c );
|
||||
break;
|
||||
|
||||
case QgsDiagramSettings::Right:
|
||||
case QgsDiagramSettings::Left:
|
||||
scaledMaxVal = sizePainterUnits( s.size.width(), s, c );
|
||||
break;
|
||||
}
|
||||
if ( mApplySpacingAdjust )
|
||||
scaledMaxVal -= totalSpacing;
|
||||
|
||||
double currentOffset = 0;
|
||||
double scaledWidth = sizePainterUnits( s.barWidth, s, c );
|
||||
|
||||
|
||||
double baseX = position.x();
|
||||
double baseY = position.y();
|
||||
|
||||
mPen.setColor( s.penColor );
|
||||
setPenWidth( mPen, s, c );
|
||||
p->setPen( mPen );
|
||||
|
||||
QList<double>::const_iterator valIt = values.constBegin();
|
||||
QList< QColor >::const_iterator colIt = s.categoryColors.constBegin();
|
||||
for ( ; valIt != values.constEnd(); ++valIt, ++colIt )
|
||||
{
|
||||
double length = *valIt / total * scaledMaxVal;
|
||||
|
||||
mCategoryBrush.setColor( *colIt );
|
||||
p->setBrush( mCategoryBrush );
|
||||
|
||||
switch ( s.diagramOrientation )
|
||||
{
|
||||
case QgsDiagramSettings::Up:
|
||||
p->drawRect( QRectF( baseX, baseY - currentOffset, scaledWidth, length * -1 ) );
|
||||
break;
|
||||
|
||||
case QgsDiagramSettings::Down:
|
||||
p->drawRect( QRectF( baseX, baseY + currentOffset - scaledMaxVal - spacing * std::max( 0, values.size() - 1 ), scaledWidth, length ) );
|
||||
break;
|
||||
|
||||
case QgsDiagramSettings::Right:
|
||||
p->drawRect( QRectF( baseX + currentOffset, baseY - scaledWidth, length, scaledWidth ) );
|
||||
break;
|
||||
|
||||
case QgsDiagramSettings::Left:
|
||||
p->drawRect( QRectF( baseX + scaledMaxVal - currentOffset + spacing * std::max( 0, values.size() - 1 ), baseY - scaledWidth, 0 - length, scaledWidth ) );
|
||||
break;
|
||||
}
|
||||
|
||||
currentOffset += length + spacing;
|
||||
}
|
||||
}
|
64
src/core/diagram/qgsstackedbardiagram.h
Normal file
@ -0,0 +1,64 @@
|
||||
/***************************************************************************
|
||||
qgsstackedbardiagram.h
|
||||
---------------------
|
||||
begin : November 2019
|
||||
copyright : (C) 2019 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSSTACKEDBARDIAGRAM_H
|
||||
#define QGSSTACKEDBARDIAGRAM_H
|
||||
|
||||
#define DIAGRAM_NAME_STACKED "Stacked"
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgis.h"
|
||||
#include "qgsdiagram.h"
|
||||
#include <QPen>
|
||||
#include <QBrush>
|
||||
|
||||
class QgsFeature;
|
||||
class QPainter;
|
||||
class QPointF;
|
||||
class QgsDiagramSettings;
|
||||
class QgsDiagramInterpolationSettings;
|
||||
|
||||
class QgsRenderContext;
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsStackedBarDiagram
|
||||
*
|
||||
* A stacked bar chart diagram.
|
||||
*
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
class CORE_EXPORT QgsStackedBarDiagram: public QgsDiagram
|
||||
{
|
||||
public:
|
||||
QgsStackedBarDiagram();
|
||||
|
||||
QgsStackedBarDiagram *clone() const override SIP_FACTORY;
|
||||
|
||||
void renderDiagram( const QgsFeature &feature, QgsRenderContext &c, const QgsDiagramSettings &s, QPointF position ) override;
|
||||
|
||||
QSizeF diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) override;
|
||||
QSizeF diagramSize( const QgsFeature &feature, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) override;
|
||||
double legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const override;
|
||||
QString diagramName() const override;
|
||||
|
||||
private:
|
||||
QBrush mCategoryBrush;
|
||||
QPen mPen;
|
||||
bool mApplySpacingAdjust = false;
|
||||
};
|
||||
|
||||
|
||||
#endif // QGSSTACKEDBARDIAGRAM_H
|
@ -19,6 +19,7 @@
|
||||
#include "diagram/qgstextdiagram.h"
|
||||
#include "diagram/qgspiediagram.h"
|
||||
#include "diagram/qgshistogramdiagram.h"
|
||||
#include "diagram/qgsstackedbardiagram.h"
|
||||
#include "qgsrendercontext.h"
|
||||
#include "qgslayertreemodellegendnode.h"
|
||||
#include "qgsfontutils.h"
|
||||
@ -300,7 +301,7 @@ void QgsDiagramSettings::readXml( const QDomElement &elem, const QgsReadWriteCon
|
||||
categoryColors.append( newColor );
|
||||
categoryAttributes.append( attrElem.attribute( QStringLiteral( "field" ) ) );
|
||||
categoryLabels.append( attrElem.attribute( QStringLiteral( "label" ) ) );
|
||||
if ( categoryLabels.back().isEmpty() )
|
||||
if ( categoryLabels.constLast().isEmpty() )
|
||||
{
|
||||
categoryLabels.back() = categoryAttributes.back();
|
||||
}
|
||||
@ -556,6 +557,10 @@ void QgsDiagramRenderer::_readXml( const QDomElement &elem, const QgsReadWriteCo
|
||||
{
|
||||
mDiagram.reset( new QgsHistogramDiagram() );
|
||||
}
|
||||
else if ( diagramType == QLatin1String( "Stacked" ) )
|
||||
{
|
||||
mDiagram.reset( new QgsStackedBarDiagram() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown diagram type -- default to histograms
|
||||
|
@ -621,7 +621,7 @@ class CORE_EXPORT QgsDiagramSettings
|
||||
private:
|
||||
|
||||
double mSpacing = 0;
|
||||
QgsUnitTypes::RenderUnit mSpacingUnit;
|
||||
QgsUnitTypes::RenderUnit mSpacingUnit = QgsUnitTypes::RenderMillimeters;
|
||||
QgsMapUnitScale mSpacingMapUnitScale;
|
||||
Direction mDirection = Counterclockwise;
|
||||
|
||||
|
@ -24,21 +24,22 @@
|
||||
|
||||
//qgis includes...
|
||||
// #include <qgisapp.h>
|
||||
#include <diagram/qgspiediagram.h>
|
||||
#include <diagram/qgstextdiagram.h>
|
||||
#include <qgsdiagramrenderer.h>
|
||||
#include <qgsmaplayer.h>
|
||||
#include <qgsvectordataprovider.h>
|
||||
#include <qgsvectorlayer.h>
|
||||
#include <qgsapplication.h>
|
||||
#include <qgsproviderregistry.h>
|
||||
#include <qgsrenderer.h>
|
||||
#include "diagram/qgspiediagram.h"
|
||||
#include "diagram/qgstextdiagram.h"
|
||||
#include "diagram/qgsstackedbardiagram.h"
|
||||
#include "diagram/qgshistogramdiagram.h"
|
||||
#include "qgsdiagramrenderer.h"
|
||||
#include "qgsmaplayer.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsproviderregistry.h"
|
||||
#include "qgsrenderer.h"
|
||||
#include "qgssinglesymbolrenderer.h"
|
||||
//qgis test includes
|
||||
#include "qgsmultirenderchecker.h"
|
||||
#include "qgspallabeling.h"
|
||||
#include "qgsproject.h"
|
||||
#include "diagram/qgshistogramdiagram.h"
|
||||
|
||||
/**
|
||||
* \ingroup UnitTests
|
||||
@ -349,6 +350,103 @@ class TestQgsDiagram : public QObject
|
||||
|
||||
}
|
||||
|
||||
void testStackedFixSize()
|
||||
{
|
||||
QgsDiagramSettings ds;
|
||||
QColor col1 = Qt::red;
|
||||
QColor col2 = Qt::yellow;
|
||||
col1.setAlphaF( 0.5 );
|
||||
col2.setAlphaF( 0.5 );
|
||||
ds.categoryColors = QList<QColor>() << col1 << col2;
|
||||
ds.categoryAttributes = QList<QString>() << QStringLiteral( "\"Pilots\"" ) << QStringLiteral( "\"Cabin Crew\"" );
|
||||
ds.minimumScale = -1;
|
||||
ds.maximumScale = -1;
|
||||
ds.minimumSize = 0;
|
||||
ds.penColor = Qt::green;
|
||||
ds.penWidth = .5;
|
||||
ds.scaleByArea = true;
|
||||
ds.sizeType = QgsUnitTypes::RenderMillimeters;
|
||||
ds.size = QSizeF( 15, 15 );
|
||||
ds.rotationOffset = 0;
|
||||
ds.diagramOrientation = QgsDiagramSettings::Up;
|
||||
ds.setSpacing( 3 );
|
||||
|
||||
QgsSingleCategoryDiagramRenderer *dr = new QgsSingleCategoryDiagramRenderer();
|
||||
dr->setDiagram( new QgsStackedBarDiagram() );
|
||||
dr->setDiagramSettings( ds );
|
||||
mPointsLayer->setDiagramRenderer( dr );
|
||||
|
||||
QgsDiagramLayerSettings dls = QgsDiagramLayerSettings();
|
||||
dls.setPlacement( QgsDiagramLayerSettings::OverPoint );
|
||||
dls.setShowAllDiagrams( true );
|
||||
mPointsLayer->setDiagramLayerSettings( dls );
|
||||
QVERIFY( imageCheck( "stacked_up" ) );
|
||||
|
||||
ds.diagramOrientation = QgsDiagramSettings::Right;
|
||||
dr->setDiagramSettings( ds );
|
||||
QVERIFY( imageCheck( "stacked_right" ) );
|
||||
|
||||
ds.diagramOrientation = QgsDiagramSettings::Left;
|
||||
dr->setDiagramSettings( ds );
|
||||
QVERIFY( imageCheck( "stacked_left" ) );
|
||||
|
||||
ds.diagramOrientation = QgsDiagramSettings::Down;
|
||||
dr->setDiagramSettings( ds );
|
||||
QVERIFY( imageCheck( "stacked_down" ) );
|
||||
|
||||
}
|
||||
|
||||
void testStackedVaryingFixSize()
|
||||
{
|
||||
QgsDiagramSettings ds;
|
||||
QColor col1 = Qt::red;
|
||||
QColor col2 = Qt::yellow;
|
||||
col1.setAlphaF( 0.5 );
|
||||
col2.setAlphaF( 0.5 );
|
||||
ds.categoryColors = QList<QColor>() << col1 << col2;
|
||||
ds.categoryAttributes = QList<QString>() << QStringLiteral( "\"Pilots\"" ) << QStringLiteral( "\"Cabin Crew\"" );
|
||||
ds.minimumScale = -1;
|
||||
ds.maximumScale = -1;
|
||||
ds.minimumSize = 0;
|
||||
ds.penColor = Qt::green;
|
||||
ds.penWidth = .5;
|
||||
ds.scaleByArea = true;
|
||||
ds.sizeType = QgsUnitTypes::RenderMillimeters;
|
||||
ds.size = QSizeF( 5, 5 );
|
||||
ds.rotationOffset = 0;
|
||||
ds.diagramOrientation = QgsDiagramSettings::Up;
|
||||
ds.setSpacing( 3 );
|
||||
|
||||
QgsLinearlyInterpolatedDiagramRenderer *dr = new QgsLinearlyInterpolatedDiagramRenderer();
|
||||
dr->setLowerValue( 0.0 );
|
||||
dr->setLowerSize( QSizeF( 0.0, 0.0 ) );
|
||||
dr->setUpperValue( 10 );
|
||||
dr->setUpperSize( QSizeF( 40, 40 ) );
|
||||
dr->setClassificationField( QStringLiteral( "Staff" ) );
|
||||
dr->setDiagram( new QgsStackedBarDiagram() );
|
||||
dr->setDiagramSettings( ds );
|
||||
mPointsLayer->setDiagramRenderer( dr );
|
||||
|
||||
QgsDiagramLayerSettings dls = QgsDiagramLayerSettings();
|
||||
dls.setPlacement( QgsDiagramLayerSettings::OverPoint );
|
||||
dls.setShowAllDiagrams( true );
|
||||
mPointsLayer->setDiagramLayerSettings( dls );
|
||||
QVERIFY( imageCheck( "stacked_varying_up" ) );
|
||||
|
||||
ds.diagramOrientation = QgsDiagramSettings::Right;
|
||||
dr->setDiagramSettings( ds );
|
||||
QVERIFY( imageCheck( "stacked_varying_right" ) );
|
||||
|
||||
ds.diagramOrientation = QgsDiagramSettings::Left;
|
||||
dr->setDiagramSettings( ds );
|
||||
QVERIFY( imageCheck( "stacked_varying_left" ) );
|
||||
|
||||
ds.diagramOrientation = QgsDiagramSettings::Down;
|
||||
dr->setDiagramSettings( ds );
|
||||
QVERIFY( imageCheck( "stacked_varying_down" ) );
|
||||
|
||||
}
|
||||
|
||||
void testPieDiagramExpression()
|
||||
{
|
||||
QgsDiagramSettings ds;
|
||||
|
BIN
tests/testdata/control_images/diagrams/expected_stacked_down/expected_stacked_down.png
vendored
Normal file
After Width: | Height: | Size: 998 KiB |
BIN
tests/testdata/control_images/diagrams/expected_stacked_left/expected_stacked_left.png
vendored
Normal file
After Width: | Height: | Size: 998 KiB |
BIN
tests/testdata/control_images/diagrams/expected_stacked_right/expected_stacked_right.png
vendored
Normal file
After Width: | Height: | Size: 998 KiB |
BIN
tests/testdata/control_images/diagrams/expected_stacked_up/expected_stacked_up.png
vendored
Normal file
After Width: | Height: | Size: 998 KiB |
After Width: | Height: | Size: 998 KiB |
After Width: | Height: | Size: 998 KiB |
After Width: | Height: | Size: 998 KiB |
BIN
tests/testdata/control_images/diagrams/expected_stacked_varying_up/expected_stacked_varying_up.png
vendored
Normal file
After Width: | Height: | Size: 998 KiB |