mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-12 00:02:25 -04:00
Avoids projects from a new qgis using a new diagram type crashing when opened in earlier qgis versions
850 lines
33 KiB
C++
850 lines
33 KiB
C++
/***************************************************************************
|
|
qgsdiagramrenderer.cpp
|
|
---------------------
|
|
begin : March 2011
|
|
copyright : (C) 2011 by Marco Hugentobler
|
|
email : marco dot hugentobler at sourcepole dot ch
|
|
***************************************************************************
|
|
* *
|
|
* 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 "qgsdiagramrenderer.h"
|
|
|
|
#include "qgsdatadefinedsizelegend.h"
|
|
#include "qgsvectorlayer.h"
|
|
#include "diagram/qgstextdiagram.h"
|
|
#include "diagram/qgspiediagram.h"
|
|
#include "diagram/qgshistogramdiagram.h"
|
|
#include "qgsrendercontext.h"
|
|
#include "qgslayertreemodellegendnode.h"
|
|
#include "qgsfontutils.h"
|
|
#include "qgssymbollayerutils.h"
|
|
|
|
#include <QDomElement>
|
|
#include <QPainter>
|
|
|
|
QgsPropertiesDefinition QgsDiagramLayerSettings::sPropertyDefinitions;
|
|
|
|
void QgsDiagramLayerSettings::initPropertyDefinitions()
|
|
{
|
|
if ( !sPropertyDefinitions.isEmpty() )
|
|
return;
|
|
|
|
const QString origin = QStringLiteral( "diagram" );
|
|
|
|
sPropertyDefinitions = QgsPropertiesDefinition
|
|
{
|
|
{ QgsDiagramLayerSettings::BackgroundColor, QgsPropertyDefinition( "backgroundColor", QObject::tr( "Background color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
|
|
{ QgsDiagramLayerSettings::StrokeColor, QgsPropertyDefinition( "strokeColor", QObject::tr( "Stroke color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
|
|
{ QgsDiagramLayerSettings::StrokeWidth, QgsPropertyDefinition( "strokeWidth", QObject::tr( "Stroke width" ), QgsPropertyDefinition::StrokeWidth, origin ) },
|
|
{ QgsDiagramLayerSettings::PositionX, QgsPropertyDefinition( "positionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double, origin ) },
|
|
{ QgsDiagramLayerSettings::PositionY, QgsPropertyDefinition( "positionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double, origin ) },
|
|
{ QgsDiagramLayerSettings::Distance, QgsPropertyDefinition( "distance", QObject::tr( "Placement distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
|
|
{ QgsDiagramLayerSettings::Priority, QgsPropertyDefinition( "priority", QObject::tr( "Placement priority" ), QgsPropertyDefinition::DoublePositive, origin ) },
|
|
{ QgsDiagramLayerSettings::ZIndex, QgsPropertyDefinition( "zIndex", QObject::tr( "Placement z-index" ), QgsPropertyDefinition::Double, origin ) },
|
|
{ QgsDiagramLayerSettings::IsObstacle, QgsPropertyDefinition( "isObstacle", QObject::tr( "Diagram is an obstacle" ), QgsPropertyDefinition::Boolean, origin ) },
|
|
{ QgsDiagramLayerSettings::Show, QgsPropertyDefinition( "show", QObject::tr( "Show diagram" ), QgsPropertyDefinition::Boolean, origin ) },
|
|
{ QgsDiagramLayerSettings::AlwaysShow, QgsPropertyDefinition( "alwaysShow", QObject::tr( "Always show diagram" ), QgsPropertyDefinition::Boolean, origin ) },
|
|
{ QgsDiagramLayerSettings::StartAngle, QgsPropertyDefinition( "startAngle", QObject::tr( "Pie chart start angle" ), QgsPropertyDefinition::Rotation, origin ) },
|
|
};
|
|
}
|
|
|
|
const QgsPropertiesDefinition &QgsDiagramLayerSettings::propertyDefinitions()
|
|
{
|
|
initPropertyDefinitions();
|
|
return sPropertyDefinitions;
|
|
}
|
|
|
|
QgsDiagramLayerSettings::QgsDiagramLayerSettings()
|
|
{
|
|
initPropertyDefinitions();
|
|
}
|
|
|
|
QgsDiagramLayerSettings::QgsDiagramLayerSettings( const QgsDiagramLayerSettings &rh )
|
|
: mCt( rh.mCt )
|
|
, mPlacement( rh.mPlacement )
|
|
, mPlacementFlags( rh.mPlacementFlags )
|
|
, mPriority( rh.mPriority )
|
|
, mZIndex( rh.mZIndex )
|
|
, mObstacle( rh.mObstacle )
|
|
, mDistance( rh.mDistance )
|
|
, mRenderer( rh.mRenderer ? rh.mRenderer->clone() : nullptr )
|
|
, mShowAll( rh.mShowAll )
|
|
, mDataDefinedProperties( rh.mDataDefinedProperties )
|
|
{
|
|
initPropertyDefinitions();
|
|
}
|
|
|
|
QgsDiagramLayerSettings &QgsDiagramLayerSettings::operator=( const QgsDiagramLayerSettings &rh )
|
|
{
|
|
mPlacement = rh.mPlacement;
|
|
mPlacementFlags = rh.mPlacementFlags;
|
|
mPriority = rh.mPriority;
|
|
mZIndex = rh.mZIndex;
|
|
mObstacle = rh.mObstacle;
|
|
mDistance = rh.mDistance;
|
|
mRenderer = rh.mRenderer ? rh.mRenderer->clone() : nullptr;
|
|
mCt = rh.mCt;
|
|
mShowAll = rh.mShowAll;
|
|
mDataDefinedProperties = rh.mDataDefinedProperties;
|
|
return *this;
|
|
}
|
|
|
|
QgsDiagramLayerSettings::~QgsDiagramLayerSettings()
|
|
{
|
|
delete mRenderer;
|
|
}
|
|
|
|
void QgsDiagramLayerSettings::setRenderer( QgsDiagramRenderer *diagramRenderer )
|
|
{
|
|
if ( diagramRenderer == mRenderer )
|
|
return;
|
|
|
|
delete mRenderer;
|
|
mRenderer = diagramRenderer;
|
|
}
|
|
|
|
void QgsDiagramLayerSettings::setCoordinateTransform( const QgsCoordinateTransform &transform )
|
|
{
|
|
mCt = transform;
|
|
}
|
|
|
|
void QgsDiagramLayerSettings::readXml( const QDomElement &elem )
|
|
{
|
|
QDomNodeList propertyElems = elem.elementsByTagName( QStringLiteral( "properties" ) );
|
|
if ( !propertyElems.isEmpty() )
|
|
{
|
|
( void )mDataDefinedProperties.readXml( propertyElems.at( 0 ).toElement(), sPropertyDefinitions );
|
|
}
|
|
else
|
|
{
|
|
mDataDefinedProperties.clear();
|
|
}
|
|
|
|
mPlacement = static_cast< Placement >( elem.attribute( QStringLiteral( "placement" ) ).toInt() );
|
|
mPlacementFlags = static_cast< LinePlacementFlag >( elem.attribute( QStringLiteral( "linePlacementFlags" ) ).toInt() );
|
|
mPriority = elem.attribute( QStringLiteral( "priority" ) ).toInt();
|
|
mZIndex = elem.attribute( QStringLiteral( "zIndex" ) ).toDouble();
|
|
mObstacle = elem.attribute( QStringLiteral( "obstacle" ) ).toInt();
|
|
mDistance = elem.attribute( QStringLiteral( "dist" ) ).toDouble();
|
|
mShowAll = ( elem.attribute( QStringLiteral( "showAll" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
|
|
}
|
|
|
|
void QgsDiagramLayerSettings::writeXml( QDomElement &layerElem, QDomDocument &doc ) const
|
|
{
|
|
QDomElement diagramLayerElem = doc.createElement( QStringLiteral( "DiagramLayerSettings" ) );
|
|
QDomElement propertiesElem = doc.createElement( QStringLiteral( "properties" ) );
|
|
( void )mDataDefinedProperties.writeXml( propertiesElem, sPropertyDefinitions );
|
|
diagramLayerElem.appendChild( propertiesElem );
|
|
diagramLayerElem.setAttribute( QStringLiteral( "placement" ), mPlacement );
|
|
diagramLayerElem.setAttribute( QStringLiteral( "linePlacementFlags" ), mPlacementFlags );
|
|
diagramLayerElem.setAttribute( QStringLiteral( "priority" ), mPriority );
|
|
diagramLayerElem.setAttribute( QStringLiteral( "zIndex" ), mZIndex );
|
|
diagramLayerElem.setAttribute( QStringLiteral( "obstacle" ), mObstacle );
|
|
diagramLayerElem.setAttribute( QStringLiteral( "dist" ), QString::number( mDistance ) );
|
|
diagramLayerElem.setAttribute( QStringLiteral( "showAll" ), mShowAll );
|
|
layerElem.appendChild( diagramLayerElem );
|
|
}
|
|
|
|
bool QgsDiagramLayerSettings::prepare( const QgsExpressionContext &context ) const
|
|
{
|
|
return mDataDefinedProperties.prepare( context );
|
|
}
|
|
|
|
QSet<QString> QgsDiagramLayerSettings::referencedFields( const QgsExpressionContext &context ) const
|
|
{
|
|
QSet< QString > referenced;
|
|
if ( mRenderer )
|
|
referenced = mRenderer->referencedFields( context );
|
|
|
|
//add the ones needed for data defined settings
|
|
referenced.unite( mDataDefinedProperties.referencedFields( context ) );
|
|
|
|
return referenced;
|
|
}
|
|
|
|
void QgsDiagramSettings::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
|
|
{
|
|
enabled = ( elem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
|
|
if ( !QgsFontUtils::setFromXmlChildNode( font, elem, QStringLiteral( "fontProperties" ) ) )
|
|
{
|
|
font.fromString( elem.attribute( QStringLiteral( "font" ) ) );
|
|
}
|
|
backgroundColor.setNamedColor( elem.attribute( QStringLiteral( "backgroundColor" ) ) );
|
|
backgroundColor.setAlpha( elem.attribute( QStringLiteral( "backgroundAlpha" ) ).toInt() );
|
|
size.setWidth( elem.attribute( QStringLiteral( "width" ) ).toDouble() );
|
|
size.setHeight( elem.attribute( QStringLiteral( "height" ) ).toDouble() );
|
|
if ( elem.hasAttribute( QStringLiteral( "transparency" ) ) )
|
|
{
|
|
opacity = 1 - elem.attribute( QStringLiteral( "transparency" ), QStringLiteral( "0" ) ).toInt() / 255.0;
|
|
}
|
|
else
|
|
{
|
|
opacity = elem.attribute( QStringLiteral( "opacity" ), QStringLiteral( "1.00" ) ).toDouble();
|
|
}
|
|
|
|
penColor.setNamedColor( elem.attribute( QStringLiteral( "penColor" ) ) );
|
|
int penAlpha = elem.attribute( QStringLiteral( "penAlpha" ), QStringLiteral( "255" ) ).toInt();
|
|
penColor.setAlpha( penAlpha );
|
|
penWidth = elem.attribute( QStringLiteral( "penWidth" ) ).toDouble();
|
|
|
|
mDirection = static_cast< Direction >( elem.attribute( QStringLiteral( "direction" ), QStringLiteral( "1" ) ).toInt() );
|
|
|
|
maximumScale = elem.attribute( QStringLiteral( "minScaleDenominator" ), QStringLiteral( "-1" ) ).toDouble();
|
|
minimumScale = elem.attribute( QStringLiteral( "maxScaleDenominator" ), QStringLiteral( "-1" ) ).toDouble();
|
|
if ( elem.hasAttribute( QStringLiteral( "scaleBasedVisibility" ) ) )
|
|
{
|
|
scaleBasedVisibility = ( elem.attribute( QStringLiteral( "scaleBasedVisibility" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
|
|
}
|
|
else
|
|
{
|
|
scaleBasedVisibility = maximumScale >= 0 && minimumScale >= 0;
|
|
}
|
|
|
|
//diagram size unit type and scale
|
|
if ( elem.attribute( QStringLiteral( "sizeType" ) ) == QLatin1String( "MapUnits" ) )
|
|
{
|
|
//compatibility with pre-2.16 project files
|
|
sizeType = QgsUnitTypes::RenderMapUnits;
|
|
}
|
|
else
|
|
{
|
|
sizeType = QgsUnitTypes::decodeRenderUnit( elem.attribute( QStringLiteral( "sizeType" ) ) );
|
|
}
|
|
sizeScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( QStringLiteral( "sizeScale" ) ) );
|
|
|
|
//line width unit type and scale
|
|
lineSizeUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( QStringLiteral( "lineSizeType" ) ) );
|
|
lineSizeScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( QStringLiteral( "lineSizeScale" ) ) );
|
|
|
|
mSpacing = elem.attribute( QStringLiteral( "spacing" ) ).toDouble();
|
|
mSpacingUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( QStringLiteral( "spacingUnit" ) ) );
|
|
mSpacingMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( QStringLiteral( "spacingUnitScale" ) ) );
|
|
|
|
//label placement method
|
|
if ( elem.attribute( QStringLiteral( "labelPlacementMethod" ) ) == QLatin1String( "Height" ) )
|
|
{
|
|
labelPlacementMethod = Height;
|
|
}
|
|
else
|
|
{
|
|
labelPlacementMethod = XHeight;
|
|
}
|
|
|
|
// orientation
|
|
if ( elem.attribute( QStringLiteral( "diagramOrientation" ) ) == QLatin1String( "Left" ) )
|
|
{
|
|
diagramOrientation = Left;
|
|
}
|
|
else if ( elem.attribute( QStringLiteral( "diagramOrientation" ) ) == QLatin1String( "Right" ) )
|
|
{
|
|
diagramOrientation = Right;
|
|
}
|
|
else if ( elem.attribute( QStringLiteral( "diagramOrientation" ) ) == QLatin1String( "Down" ) )
|
|
{
|
|
diagramOrientation = Down;
|
|
}
|
|
else
|
|
{
|
|
diagramOrientation = Up;
|
|
}
|
|
|
|
// scale dependency
|
|
if ( elem.attribute( QStringLiteral( "scaleDependency" ) ) == QLatin1String( "Diameter" ) )
|
|
{
|
|
scaleByArea = false;
|
|
}
|
|
else
|
|
{
|
|
scaleByArea = true;
|
|
}
|
|
|
|
barWidth = elem.attribute( QStringLiteral( "barWidth" ) ).toDouble();
|
|
|
|
if ( elem.hasAttribute( QStringLiteral( "angleOffset" ) ) )
|
|
rotationOffset = std::fmod( 360.0 - elem.attribute( QStringLiteral( "angleOffset" ) ).toInt() / 16.0, 360.0 );
|
|
else
|
|
rotationOffset = elem.attribute( QStringLiteral( "rotationOffset" ) ).toDouble();
|
|
|
|
minimumSize = elem.attribute( QStringLiteral( "minimumSize" ) ).toDouble();
|
|
|
|
//colors
|
|
categoryColors.clear();
|
|
QDomNodeList attributes = elem.elementsByTagName( QStringLiteral( "attribute" ) );
|
|
|
|
if ( attributes.length() > 0 )
|
|
{
|
|
for ( int i = 0; i < attributes.size(); i++ )
|
|
{
|
|
QDomElement attrElem = attributes.at( i ).toElement();
|
|
QColor newColor( attrElem.attribute( QStringLiteral( "color" ) ) );
|
|
newColor.setAlphaF( opacity );
|
|
categoryColors.append( newColor );
|
|
categoryAttributes.append( attrElem.attribute( QStringLiteral( "field" ) ) );
|
|
categoryLabels.append( attrElem.attribute( QStringLiteral( "label" ) ) );
|
|
if ( categoryLabels.back().isEmpty() )
|
|
{
|
|
categoryLabels.back() = categoryAttributes.back();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Restore old format attributes and colors
|
|
|
|
QStringList colorList = elem.attribute( QStringLiteral( "colors" ) ).split( '/' );
|
|
QStringList::const_iterator colorIt = colorList.constBegin();
|
|
for ( ; colorIt != colorList.constEnd(); ++colorIt )
|
|
{
|
|
QColor newColor( *colorIt );
|
|
newColor.setAlphaF( opacity );
|
|
categoryColors.append( QColor( newColor ) );
|
|
}
|
|
|
|
//attribute indices
|
|
categoryAttributes.clear();
|
|
QStringList catList = elem.attribute( QStringLiteral( "categories" ) ).split( '/' );
|
|
QStringList::const_iterator catIt = catList.constBegin();
|
|
for ( ; catIt != catList.constEnd(); ++catIt )
|
|
{
|
|
categoryAttributes.append( *catIt );
|
|
categoryLabels.append( *catIt );
|
|
}
|
|
}
|
|
}
|
|
|
|
void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
|
|
{
|
|
QDomElement categoryElem = doc.createElement( QStringLiteral( "DiagramCategory" ) );
|
|
categoryElem.setAttribute( QStringLiteral( "enabled" ), enabled );
|
|
categoryElem.appendChild( QgsFontUtils::toXmlElement( font, doc, QStringLiteral( "fontProperties" ) ) );
|
|
categoryElem.setAttribute( QStringLiteral( "backgroundColor" ), backgroundColor.name() );
|
|
categoryElem.setAttribute( QStringLiteral( "backgroundAlpha" ), backgroundColor.alpha() );
|
|
categoryElem.setAttribute( QStringLiteral( "width" ), QString::number( size.width() ) );
|
|
categoryElem.setAttribute( QStringLiteral( "height" ), QString::number( size.height() ) );
|
|
categoryElem.setAttribute( QStringLiteral( "penColor" ), penColor.name() );
|
|
categoryElem.setAttribute( QStringLiteral( "penAlpha" ), penColor.alpha() );
|
|
categoryElem.setAttribute( QStringLiteral( "penWidth" ), QString::number( penWidth ) );
|
|
categoryElem.setAttribute( QStringLiteral( "scaleBasedVisibility" ), scaleBasedVisibility );
|
|
categoryElem.setAttribute( QStringLiteral( "minScaleDenominator" ), QString::number( maximumScale ) );
|
|
categoryElem.setAttribute( QStringLiteral( "maxScaleDenominator" ), QString::number( minimumScale ) );
|
|
categoryElem.setAttribute( QStringLiteral( "opacity" ), QString::number( opacity ) );
|
|
categoryElem.setAttribute( QStringLiteral( "spacing" ), QString::number( mSpacing ) );
|
|
categoryElem.setAttribute( QStringLiteral( "spacingUnit" ), QgsUnitTypes::encodeUnit( mSpacingUnit ) );
|
|
categoryElem.setAttribute( QStringLiteral( "spacingUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSpacingMapUnitScale ) );
|
|
categoryElem.setAttribute( QStringLiteral( "direction" ), QString::number( mDirection ) );
|
|
|
|
//diagram size unit type and scale
|
|
categoryElem.setAttribute( QStringLiteral( "sizeType" ), QgsUnitTypes::encodeUnit( sizeType ) );
|
|
categoryElem.setAttribute( QStringLiteral( "sizeScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( sizeScale ) );
|
|
|
|
//line width unit type and scale
|
|
categoryElem.setAttribute( QStringLiteral( "lineSizeType" ), QgsUnitTypes::encodeUnit( lineSizeUnit ) );
|
|
categoryElem.setAttribute( QStringLiteral( "lineSizeScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( lineSizeScale ) );
|
|
|
|
// label placement method (text diagram)
|
|
if ( labelPlacementMethod == Height )
|
|
{
|
|
categoryElem.setAttribute( QStringLiteral( "labelPlacementMethod" ), QStringLiteral( "Height" ) );
|
|
}
|
|
else
|
|
{
|
|
categoryElem.setAttribute( QStringLiteral( "labelPlacementMethod" ), QStringLiteral( "XHeight" ) );
|
|
}
|
|
|
|
if ( scaleByArea )
|
|
{
|
|
categoryElem.setAttribute( QStringLiteral( "scaleDependency" ), QStringLiteral( "Area" ) );
|
|
}
|
|
else
|
|
{
|
|
categoryElem.setAttribute( QStringLiteral( "scaleDependency" ), QStringLiteral( "Diameter" ) );
|
|
}
|
|
|
|
// orientation (histogram)
|
|
switch ( diagramOrientation )
|
|
{
|
|
case Left:
|
|
categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Left" ) );
|
|
break;
|
|
|
|
case Right:
|
|
categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Right" ) );
|
|
break;
|
|
|
|
case Down:
|
|
categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Down" ) );
|
|
break;
|
|
|
|
case Up:
|
|
categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Up" ) );
|
|
break;
|
|
|
|
default:
|
|
categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Up" ) );
|
|
break;
|
|
}
|
|
|
|
categoryElem.setAttribute( QStringLiteral( "barWidth" ), QString::number( barWidth ) );
|
|
categoryElem.setAttribute( QStringLiteral( "minimumSize" ), QString::number( minimumSize ) );
|
|
categoryElem.setAttribute( QStringLiteral( "rotationOffset" ), QString::number( rotationOffset ) );
|
|
|
|
int nCats = std::min( categoryColors.size(), categoryAttributes.size() );
|
|
for ( int i = 0; i < nCats; ++i )
|
|
{
|
|
QDomElement attributeElem = doc.createElement( QStringLiteral( "attribute" ) );
|
|
|
|
attributeElem.setAttribute( QStringLiteral( "field" ), categoryAttributes.at( i ) );
|
|
attributeElem.setAttribute( QStringLiteral( "color" ), categoryColors.at( i ).name() );
|
|
attributeElem.setAttribute( QStringLiteral( "label" ), categoryLabels.at( i ) );
|
|
categoryElem.appendChild( attributeElem );
|
|
}
|
|
|
|
rendererElem.appendChild( categoryElem );
|
|
}
|
|
|
|
void QgsDiagramRenderer::setDiagram( QgsDiagram *d )
|
|
{
|
|
if ( mDiagram.get() == d )
|
|
return;
|
|
|
|
mDiagram.reset( d );
|
|
}
|
|
|
|
QgsDiagramRenderer::QgsDiagramRenderer( const QgsDiagramRenderer &other )
|
|
: mDiagram( other.mDiagram ? other.mDiagram->clone() : nullptr )
|
|
, mShowAttributeLegend( other.mShowAttributeLegend )
|
|
{
|
|
}
|
|
|
|
QgsDiagramRenderer &QgsDiagramRenderer::operator=( const QgsDiagramRenderer &other )
|
|
{
|
|
mDiagram.reset( other.mDiagram ? other.mDiagram->clone() : nullptr );
|
|
mShowAttributeLegend = other.mShowAttributeLegend;
|
|
return *this;
|
|
}
|
|
|
|
void QgsDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties ) const
|
|
{
|
|
if ( !mDiagram )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QgsDiagramSettings s;
|
|
if ( !diagramSettings( feature, c, s ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( properties.hasActiveProperties() )
|
|
{
|
|
c.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( s.backgroundColor ) );
|
|
s.backgroundColor = properties.valueAsColor( QgsDiagramLayerSettings::BackgroundColor, c.expressionContext(), s.backgroundColor );
|
|
c.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( s.penColor ) );
|
|
s.penColor = properties.valueAsColor( QgsDiagramLayerSettings::StrokeColor, c.expressionContext(), s.penColor );
|
|
c.expressionContext().setOriginalValueVariable( s.penWidth );
|
|
s.penWidth = properties.valueAsDouble( QgsDiagramLayerSettings::StrokeWidth, c.expressionContext(), s.penWidth );
|
|
c.expressionContext().setOriginalValueVariable( s.rotationOffset );
|
|
s.rotationOffset = properties.valueAsDouble( QgsDiagramLayerSettings::StartAngle, c.expressionContext(), s.rotationOffset );
|
|
}
|
|
|
|
mDiagram->renderDiagram( feature, c, s, pos );
|
|
}
|
|
|
|
QSizeF QgsDiagramRenderer::sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const
|
|
{
|
|
QgsDiagramSettings s;
|
|
if ( !diagramSettings( feature, c, s ) )
|
|
{
|
|
return QSizeF();
|
|
}
|
|
|
|
QSizeF size = diagramSize( feature, c );
|
|
if ( size.isValid() )
|
|
{
|
|
double width = c.convertToMapUnits( size.width(), s.sizeType, s.sizeScale );
|
|
size.rheight() *= width / size.width();
|
|
size.setWidth( width );
|
|
}
|
|
return size;
|
|
}
|
|
|
|
QSet<QString> QgsDiagramRenderer::referencedFields( const QgsExpressionContext &context ) const
|
|
{
|
|
QSet< QString > referenced;
|
|
|
|
if ( !mDiagram )
|
|
return referenced;
|
|
|
|
const auto constDiagramAttributes = diagramAttributes();
|
|
for ( const QString &att : constDiagramAttributes )
|
|
{
|
|
QgsExpression *expression = mDiagram->getExpression( att, context );
|
|
const auto constReferencedColumns = expression->referencedColumns();
|
|
for ( const QString &field : constReferencedColumns )
|
|
{
|
|
referenced << field;
|
|
}
|
|
}
|
|
return referenced;
|
|
}
|
|
|
|
void QgsDiagramRenderer::convertSizeToMapUnits( QSizeF &size, const QgsRenderContext &context ) const
|
|
{
|
|
if ( !size.isValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
double pixelToMap = context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel();
|
|
size.rwidth() *= pixelToMap;
|
|
size.rheight() *= pixelToMap;
|
|
}
|
|
|
|
int QgsDiagramRenderer::dpiPaintDevice( const QPainter *painter )
|
|
{
|
|
if ( painter )
|
|
{
|
|
QPaintDevice *device = painter->device();
|
|
if ( device )
|
|
{
|
|
return device->logicalDpiX();
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void QgsDiagramRenderer::_readXml( const QDomElement &elem, const QgsReadWriteContext &context )
|
|
{
|
|
Q_UNUSED( context )
|
|
mDiagram.reset();
|
|
QString diagramType = elem.attribute( QStringLiteral( "diagramType" ) );
|
|
if ( diagramType == QLatin1String( "Pie" ) )
|
|
{
|
|
mDiagram.reset( new QgsPieDiagram() );
|
|
}
|
|
else if ( diagramType == QLatin1String( "Text" ) )
|
|
{
|
|
mDiagram.reset( new QgsTextDiagram() );
|
|
}
|
|
else if ( diagramType == QLatin1String( "Histogram" ) )
|
|
{
|
|
mDiagram.reset( new QgsHistogramDiagram() );
|
|
}
|
|
else
|
|
{
|
|
// unknown diagram type -- default to histograms
|
|
mDiagram.reset( new QgsHistogramDiagram() );
|
|
}
|
|
mShowAttributeLegend = ( elem.attribute( QStringLiteral( "attributeLegend" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
|
|
}
|
|
|
|
void QgsDiagramRenderer::_writeXml( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
|
|
{
|
|
Q_UNUSED( doc )
|
|
Q_UNUSED( context )
|
|
|
|
if ( mDiagram )
|
|
{
|
|
rendererElem.setAttribute( QStringLiteral( "diagramType" ), mDiagram->diagramName() );
|
|
}
|
|
rendererElem.setAttribute( QStringLiteral( "attributeLegend" ), mShowAttributeLegend );
|
|
}
|
|
|
|
QgsSingleCategoryDiagramRenderer *QgsSingleCategoryDiagramRenderer::clone() const
|
|
{
|
|
return new QgsSingleCategoryDiagramRenderer( *this );
|
|
}
|
|
|
|
bool QgsSingleCategoryDiagramRenderer::diagramSettings( const QgsFeature &, const QgsRenderContext &c, QgsDiagramSettings &s ) const
|
|
{
|
|
Q_UNUSED( c )
|
|
s = mSettings;
|
|
return true;
|
|
}
|
|
|
|
QSizeF QgsSingleCategoryDiagramRenderer::diagramSize( const QgsFeature &feature, const QgsRenderContext &c ) const
|
|
{
|
|
return mDiagram->diagramSize( feature.attributes(), c, mSettings );
|
|
}
|
|
|
|
QList<QgsDiagramSettings> QgsSingleCategoryDiagramRenderer::diagramSettings() const
|
|
{
|
|
QList<QgsDiagramSettings> settingsList;
|
|
settingsList.push_back( mSettings );
|
|
return settingsList;
|
|
}
|
|
|
|
void QgsSingleCategoryDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
|
|
{
|
|
QDomElement categoryElem = elem.firstChildElement( QStringLiteral( "DiagramCategory" ) );
|
|
if ( categoryElem.isNull() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mSettings.readXml( categoryElem, context );
|
|
_readXml( elem, context );
|
|
}
|
|
|
|
void QgsSingleCategoryDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
|
|
{
|
|
QDomElement rendererElem = doc.createElement( QStringLiteral( "SingleCategoryDiagramRenderer" ) );
|
|
mSettings.writeXml( rendererElem, doc, context );
|
|
_writeXml( rendererElem, doc, context );
|
|
layerElem.appendChild( rendererElem );
|
|
}
|
|
|
|
|
|
QgsLinearlyInterpolatedDiagramRenderer::QgsLinearlyInterpolatedDiagramRenderer()
|
|
{
|
|
mInterpolationSettings.classificationAttributeIsExpression = false;
|
|
}
|
|
|
|
QgsLinearlyInterpolatedDiagramRenderer::QgsLinearlyInterpolatedDiagramRenderer( const QgsLinearlyInterpolatedDiagramRenderer &other )
|
|
: QgsDiagramRenderer( other )
|
|
, mSettings( other.mSettings )
|
|
, mInterpolationSettings( other.mInterpolationSettings )
|
|
, mDataDefinedSizeLegend( other.mDataDefinedSizeLegend ? new QgsDataDefinedSizeLegend( *other.mDataDefinedSizeLegend ) : nullptr )
|
|
{
|
|
}
|
|
|
|
QgsLinearlyInterpolatedDiagramRenderer::~QgsLinearlyInterpolatedDiagramRenderer()
|
|
{
|
|
delete mDataDefinedSizeLegend;
|
|
}
|
|
|
|
QgsLinearlyInterpolatedDiagramRenderer *QgsLinearlyInterpolatedDiagramRenderer::clone() const
|
|
{
|
|
return new QgsLinearlyInterpolatedDiagramRenderer( *this );
|
|
}
|
|
|
|
QList<QgsDiagramSettings> QgsLinearlyInterpolatedDiagramRenderer::diagramSettings() const
|
|
{
|
|
QList<QgsDiagramSettings> settingsList;
|
|
settingsList.push_back( mSettings );
|
|
return settingsList;
|
|
}
|
|
|
|
bool QgsLinearlyInterpolatedDiagramRenderer::diagramSettings( const QgsFeature &feature, const QgsRenderContext &c, QgsDiagramSettings &s ) const
|
|
{
|
|
s = mSettings;
|
|
s.size = diagramSize( feature, c );
|
|
return true;
|
|
}
|
|
|
|
QList<QString> QgsLinearlyInterpolatedDiagramRenderer::diagramAttributes() const
|
|
{
|
|
return mSettings.categoryAttributes;
|
|
}
|
|
|
|
QSet<QString> QgsLinearlyInterpolatedDiagramRenderer::referencedFields( const QgsExpressionContext &context ) const
|
|
{
|
|
QSet< QString > referenced = QgsDiagramRenderer::referencedFields( context );
|
|
if ( mInterpolationSettings.classificationAttributeIsExpression )
|
|
{
|
|
QgsExpression *expression = mDiagram->getExpression( mInterpolationSettings.classificationAttributeExpression, context );
|
|
const auto constReferencedColumns = expression->referencedColumns();
|
|
for ( const QString &field : constReferencedColumns )
|
|
{
|
|
referenced << field;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
referenced << mInterpolationSettings.classificationField;
|
|
}
|
|
return referenced;
|
|
}
|
|
|
|
QSizeF QgsLinearlyInterpolatedDiagramRenderer::diagramSize( const QgsFeature &feature, const QgsRenderContext &c ) const
|
|
{
|
|
return mDiagram->diagramSize( feature, c, mSettings, mInterpolationSettings );
|
|
}
|
|
|
|
void QgsLinearlyInterpolatedDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
|
|
{
|
|
mInterpolationSettings.lowerValue = elem.attribute( QStringLiteral( "lowerValue" ) ).toDouble();
|
|
mInterpolationSettings.upperValue = elem.attribute( QStringLiteral( "upperValue" ) ).toDouble();
|
|
mInterpolationSettings.lowerSize.setWidth( elem.attribute( QStringLiteral( "lowerWidth" ) ).toDouble() );
|
|
mInterpolationSettings.lowerSize.setHeight( elem.attribute( QStringLiteral( "lowerHeight" ) ).toDouble() );
|
|
mInterpolationSettings.upperSize.setWidth( elem.attribute( QStringLiteral( "upperWidth" ) ).toDouble() );
|
|
mInterpolationSettings.upperSize.setHeight( elem.attribute( QStringLiteral( "upperHeight" ) ).toDouble() );
|
|
mInterpolationSettings.classificationAttributeIsExpression = elem.hasAttribute( QStringLiteral( "classificationAttributeExpression" ) );
|
|
if ( mInterpolationSettings.classificationAttributeIsExpression )
|
|
{
|
|
mInterpolationSettings.classificationAttributeExpression = elem.attribute( QStringLiteral( "classificationAttributeExpression" ) );
|
|
}
|
|
else
|
|
{
|
|
mInterpolationSettings.classificationField = elem.attribute( QStringLiteral( "classificationField" ) );
|
|
}
|
|
QDomElement settingsElem = elem.firstChildElement( QStringLiteral( "DiagramCategory" ) );
|
|
if ( !settingsElem.isNull() )
|
|
{
|
|
mSettings.readXml( settingsElem );
|
|
}
|
|
|
|
delete mDataDefinedSizeLegend;
|
|
|
|
QDomElement ddsLegendSizeElem = elem.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
|
|
if ( !ddsLegendSizeElem.isNull() )
|
|
{
|
|
mDataDefinedSizeLegend = QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context );
|
|
}
|
|
else
|
|
{
|
|
// pre-3.0 projects
|
|
if ( elem.attribute( QStringLiteral( "sizeLegend" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) )
|
|
{
|
|
mDataDefinedSizeLegend = new QgsDataDefinedSizeLegend();
|
|
QDomElement sizeLegendSymbolElem = elem.firstChildElement( QStringLiteral( "symbol" ) );
|
|
if ( !sizeLegendSymbolElem.isNull() && sizeLegendSymbolElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "sizeSymbol" ) )
|
|
{
|
|
mDataDefinedSizeLegend->setSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( sizeLegendSymbolElem, context ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mDataDefinedSizeLegend = nullptr;
|
|
}
|
|
}
|
|
|
|
_readXml( elem, context );
|
|
}
|
|
|
|
void QgsLinearlyInterpolatedDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
|
|
{
|
|
QDomElement rendererElem = doc.createElement( QStringLiteral( "LinearlyInterpolatedDiagramRenderer" ) );
|
|
rendererElem.setAttribute( QStringLiteral( "lowerValue" ), QString::number( mInterpolationSettings.lowerValue ) );
|
|
rendererElem.setAttribute( QStringLiteral( "upperValue" ), QString::number( mInterpolationSettings.upperValue ) );
|
|
rendererElem.setAttribute( QStringLiteral( "lowerWidth" ), QString::number( mInterpolationSettings.lowerSize.width() ) );
|
|
rendererElem.setAttribute( QStringLiteral( "lowerHeight" ), QString::number( mInterpolationSettings.lowerSize.height() ) );
|
|
rendererElem.setAttribute( QStringLiteral( "upperWidth" ), QString::number( mInterpolationSettings.upperSize.width() ) );
|
|
rendererElem.setAttribute( QStringLiteral( "upperHeight" ), QString::number( mInterpolationSettings.upperSize.height() ) );
|
|
if ( mInterpolationSettings.classificationAttributeIsExpression )
|
|
{
|
|
rendererElem.setAttribute( QStringLiteral( "classificationAttributeExpression" ), mInterpolationSettings.classificationAttributeExpression );
|
|
}
|
|
else
|
|
{
|
|
rendererElem.setAttribute( QStringLiteral( "classificationField" ), mInterpolationSettings.classificationField );
|
|
}
|
|
mSettings.writeXml( rendererElem, doc );
|
|
|
|
if ( mDataDefinedSizeLegend )
|
|
{
|
|
QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
|
|
mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
|
|
rendererElem.appendChild( ddsLegendElem );
|
|
}
|
|
|
|
_writeXml( rendererElem, doc, context );
|
|
layerElem.appendChild( rendererElem );
|
|
}
|
|
|
|
QList< QgsLayerTreeModelLegendNode * > QgsDiagramSettings::legendItems( QgsLayerTreeLayer *nodeLayer ) const
|
|
{
|
|
QList< QgsLayerTreeModelLegendNode * > list;
|
|
list.reserve( categoryLabels.size() );
|
|
for ( int i = 0; i < categoryLabels.size(); ++i )
|
|
{
|
|
QPixmap pix( 16, 16 );
|
|
pix.fill( categoryColors[i] );
|
|
list << new QgsSimpleLegendNode( nodeLayer, categoryLabels[i], QIcon( pix ), nullptr, QStringLiteral( "diagram_%1" ).arg( QString::number( i ) ) );
|
|
}
|
|
return list;
|
|
}
|
|
|
|
QgsDiagramSettings::Direction QgsDiagramSettings::direction() const
|
|
{
|
|
return mDirection;
|
|
}
|
|
|
|
void QgsDiagramSettings::setDirection( Direction direction )
|
|
{
|
|
mDirection = direction;
|
|
}
|
|
|
|
QList< QgsLayerTreeModelLegendNode * > QgsDiagramRenderer::legendItems( QgsLayerTreeLayer * ) const
|
|
{
|
|
return QList< QgsLayerTreeModelLegendNode * >();
|
|
}
|
|
|
|
QList< QgsLayerTreeModelLegendNode * > QgsSingleCategoryDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
|
|
{
|
|
QList< QgsLayerTreeModelLegendNode * > nodes;
|
|
if ( mShowAttributeLegend )
|
|
nodes = mSettings.legendItems( nodeLayer );
|
|
|
|
return nodes;
|
|
}
|
|
|
|
QList< QgsLayerTreeModelLegendNode * > QgsLinearlyInterpolatedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
|
|
{
|
|
QList< QgsLayerTreeModelLegendNode * > nodes;
|
|
if ( mShowAttributeLegend )
|
|
nodes = mSettings.legendItems( nodeLayer );
|
|
|
|
if ( mDataDefinedSizeLegend && mDiagram )
|
|
{
|
|
// add size legend
|
|
QgsMarkerSymbol *legendSymbol = mDataDefinedSizeLegend->symbol() ? mDataDefinedSizeLegend->symbol()->clone() : QgsMarkerSymbol::createSimple( QgsStringMap() );
|
|
legendSymbol->setSizeUnit( mSettings.sizeType );
|
|
legendSymbol->setSizeMapUnitScale( mSettings.sizeScale );
|
|
|
|
QgsDataDefinedSizeLegend ddSizeLegend( *mDataDefinedSizeLegend );
|
|
ddSizeLegend.setSymbol( legendSymbol ); // transfers ownership
|
|
|
|
QList<QgsDataDefinedSizeLegend::SizeClass> sizeClasses;
|
|
if ( ddSizeLegend.classes().isEmpty() )
|
|
{
|
|
// automatic class creation if the classes are not defined manually
|
|
const auto prettyBreaks { QgsSymbolLayerUtils::prettyBreaks( mInterpolationSettings.lowerValue, mInterpolationSettings.upperValue, 4 ) };
|
|
for ( double v : prettyBreaks )
|
|
{
|
|
double size = mDiagram->legendSize( v, mSettings, mInterpolationSettings );
|
|
sizeClasses << QgsDataDefinedSizeLegend::SizeClass( size, QString::number( v ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// manual classes need to get size scaled because the QgsSizeScaleTransformer is not used in diagrams :-(
|
|
const auto constClasses = ddSizeLegend.classes();
|
|
for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
|
|
{
|
|
double size = mDiagram->legendSize( sc.size, mSettings, mInterpolationSettings );
|
|
sizeClasses << QgsDataDefinedSizeLegend::SizeClass( size, sc.label );
|
|
}
|
|
}
|
|
ddSizeLegend.setClasses( sizeClasses );
|
|
|
|
const auto constLegendSymbolList = ddSizeLegend.legendSymbolList();
|
|
for ( const QgsLegendSymbolItem &si : constLegendSymbolList )
|
|
{
|
|
if ( si.dataDefinedSizeLegendSettings() )
|
|
nodes << new QgsDataDefinedSizeLegendNode( nodeLayer, *si.dataDefinedSizeLegendSettings() );
|
|
else
|
|
nodes << new QgsSymbolLegendNode( nodeLayer, si );
|
|
}
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
|
|
void QgsLinearlyInterpolatedDiagramRenderer::setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings )
|
|
{
|
|
delete mDataDefinedSizeLegend;
|
|
mDataDefinedSizeLegend = settings;
|
|
}
|
|
|
|
QgsDataDefinedSizeLegend *QgsLinearlyInterpolatedDiagramRenderer::dataDefinedSizeLegend() const
|
|
{
|
|
return mDataDefinedSizeLegend;
|
|
}
|