Skip to content
Permalink
Browse files

[FEATURE] Rendering of data-defined size in legend in "collapsed" mode

Instead of having different marker sizes in legend as separate legend nodes,
the new "collapsed" mode packs all sizes into one legend node.

This commit only makes it available in the API, not exposed in GUI yet.
  • Loading branch information
wonder-sk committed Jun 15, 2017
1 parent a899963 commit 8c4d5bbb956cc4b4a6d1527ed2267d848ad810da
@@ -36,6 +36,7 @@
%Include qgscoordinatetransform.sip
%Include qgscredentials.sip
%Include qgscrscache.sip
%Include qgsdatadefinedsizelegend.sip
%Include qgsdataitem.sip
%Include qgsdataitemprovider.sip
%Include qgsdataitemproviderregistry.sip
@@ -151,6 +151,12 @@ Emitted on internal data change so the layer tree model can forward the signal t
Construct the node with pointer to its parent layer node
%End

QgsRenderContext *createTemporaryRenderContext() const /Factory/;
%Docstring
Returns a temporary context or null if legendMapViewData are not valid
:rtype: QgsRenderContext
%End

protected:
};

@@ -371,6 +377,30 @@ class QgsWmsLegendNode : QgsLayerTreeModelLegendNode

};


class QgsDataDefinedSizeLegendNode : QgsLayerTreeModelLegendNode
{
%Docstring
Produces legend node with a marker symbol
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgslayertreemodellegendnode.h"
%End
public:
QgsDataDefinedSizeLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsDataDefinedSizeLegend &settings, QObject *parent /TransferThis/ = 0 );
%Docstring
Construct the node using QgsDataDefinedSizeLegend as definition of the node's appearance
%End

virtual QVariant data( int role ) const;

virtual ItemMetrics draw( const QgsLegendSettings &settings, ItemContext *ctx );


};

/************************************************************************
* This file has been generated automatically from *
* *
@@ -760,13 +760,28 @@ Returns list with all diagram settings in the renderer
virtual QList< QgsLayerTreeModelLegendNode * > legendItems( QgsLayerTreeLayer *nodeLayer ) const /Factory/;


void setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings /Transfer/ );
%Docstring
Configures appearance of legend. Takes ownership of the passed settings objects.
.. versionadded:: 3.0
%End

QgsDataDefinedSizeLegend *dataDefinedSizeLegend() const;
%Docstring
Returns configuration of appearance of legend. Will return null if no configuration has been set.
.. versionadded:: 3.0
:rtype: QgsDataDefinedSizeLegend
%End

protected:
virtual bool diagramSettings( const QgsFeature &feature, const QgsRenderContext &c, QgsDiagramSettings &s ) const;


virtual QSizeF diagramSize( const QgsFeature &, const QgsRenderContext &c ) const;


QgsLinearlyInterpolatedDiagramRenderer( const QgsLinearlyInterpolatedDiagramRenderer &other );

};

/************************************************************************
@@ -78,6 +78,7 @@ class QgsCategorizedSymbolRenderer : QgsFeatureRenderer
public:

QgsCategorizedSymbolRenderer( const QString &attrName = QString(), const QgsCategoryList &categories = QgsCategoryList() );
~QgsCategorizedSymbolRenderer();

virtual QgsSymbol *symbolForFeature( QgsFeature &feature, QgsRenderContext &context );
virtual QgsSymbol *originalSymbolForFeature( QgsFeature &feature, QgsRenderContext &context );
@@ -220,10 +221,31 @@ create renderer from XML element
:rtype: QgsCategorizedSymbolRenderer
%End

void setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings /Transfer/ );
%Docstring
Configures appearance of legend when renderer is configured to use data-defined size for marker symbols.
This allows to configure for what values (symbol sizes) should be shown in the legend, whether to display
different symbol sizes collapsed in one legend node or separated across multiple legend nodes etc.

When renderer does not use data-defined size or does not use marker symbols, these settings will be ignored.
Takes ownership of the passed settings objects. Null pointer is a valid input that disables data-defined
size legend.
.. versionadded:: 3.0
%End

QgsDataDefinedSizeLegend *dataDefinedSizeLegend() const;
%Docstring
Returns configuration of appearance of legend when using data-defined size for marker symbols.
Will return null if the functionality is disabled.
.. versionadded:: 3.0
:rtype: QgsDataDefinedSizeLegend
%End

protected:




void rebuildHash();
%Docstring
hashtable for faster access to symbols
@@ -7,6 +7,7 @@
************************************************************************/



class QgsRendererRange
{

@@ -405,9 +406,30 @@ create renderer from XML element
:rtype: QgsGraduatedSymbolRenderer
%End

void setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings /Transfer/ );
%Docstring
Configures appearance of legend when renderer is configured to use data-defined size for marker symbols.
This allows to configure for what values (symbol sizes) should be shown in the legend, whether to display
different symbol sizes collapsed in one legend node or separated across multiple legend nodes etc.

When renderer does not use data-defined size or does not use marker symbols, these settings will be ignored.
Takes ownership of the passed settings objects. Null pointer is a valid input that disables data-defined
size legend.
.. versionadded:: 3.0
%End

QgsDataDefinedSizeLegend *dataDefinedSizeLegend() const;
%Docstring
Returns configuration of appearance of legend when using data-defined size for marker symbols.
Will return null if the functionality is disabled.
.. versionadded:: 3.0
:rtype: QgsDataDefinedSizeLegend
%End

protected:



QgsSymbol *symbolForValue( double value );
%Docstring
attribute index (derived from attribute name in startRender)
@@ -94,6 +94,22 @@ Identation level that tells how deep the item is in a hierarchy of items. For fl
Set symbol of the item. Takes ownership of symbol.
%End

void setDataDefinedSizeLegendSettings( QgsDataDefinedSizeLegend *settings /Transfer/ );
%Docstring
Sets extra information about data-defined size. If set, this item should be converted to QgsDataDefinedSizeLegendNode
rather than QgsSymbolLegendNode instance as usual. Passing null removes any data-defined size legend settings.

Takes ownership of the settings object.
.. versionadded:: 3.0
%End

QgsDataDefinedSizeLegend *dataDefinedSizeLegendSettings() const;
%Docstring
Returns extra information for data-defined size legend rendering. Normally it returns null.
.. versionadded:: 3.0
:rtype: QgsDataDefinedSizeLegend
%End

};


@@ -7,6 +7,7 @@
************************************************************************/



class QgsSingleSymbolRenderer : QgsFeatureRenderer
{

@@ -16,6 +17,7 @@ class QgsSingleSymbolRenderer : QgsFeatureRenderer
public:

QgsSingleSymbolRenderer( QgsSymbol *symbol /Transfer/ );
~QgsSingleSymbolRenderer();

virtual QgsSymbol *symbolForFeature( QgsFeature &feature, QgsRenderContext &context );
virtual QgsSymbol *originalSymbolForFeature( QgsFeature &feature, QgsRenderContext &context );
@@ -58,6 +60,26 @@ create renderer from XML element
:rtype: QgsSingleSymbolRenderer
%End

void setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings /Transfer/ );
%Docstring
Configures appearance of legend when renderer is configured to use data-defined size for marker symbols.
This allows to configure for what values (symbol sizes) should be shown in the legend, whether to display
different symbol sizes collapsed in one legend node or separated across multiple legend nodes etc.

When renderer does not use data-defined size or does not use marker symbols, these settings will be ignored.
Takes ownership of the passed settings objects. Null pointer is a valid input that disables data-defined
size legend.
.. versionadded:: 3.0
%End

QgsDataDefinedSizeLegend *dataDefinedSizeLegend() const;
%Docstring
Returns configuration of appearance of legend when using data-defined size for marker symbols.
Will return null if the functionality is disabled.
.. versionadded:: 3.0
:rtype: QgsDataDefinedSizeLegend
%End

protected:

private:
@@ -141,6 +141,7 @@ SET(QGIS_CORE_SRCS
qgscredentials.cpp
qgscrscache.cpp
qgsdartmeasurement.cpp
qgsdatadefinedsizelegend.cpp
qgsdataitem.cpp
qgsdataitemprovider.cpp
qgsdataitemproviderregistry.cpp
@@ -738,6 +739,7 @@ SET(QGIS_CORE_HDRS
qgscrscache.h
qgscsexception.h
qgsdartmeasurement.h
qgsdatadefinedsizelegend.h
qgsdataitem.h
qgsdataitemprovider.h
qgsdataitemproviderregistry.h
@@ -18,6 +18,7 @@

#include "qgslayertreemodellegendnode.h"

#include "qgsdatadefinedsizelegend.h"
#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgslegendsettings.h"
@@ -215,22 +216,23 @@ void QgsSymbolLegendNode::uncheckAllItems()
checkAll( false );
}

inline
QgsRenderContext *QgsSymbolLegendNode::createTemporaryRenderContext() const
QgsRenderContext *QgsLayerTreeModelLegendNode::createTemporaryRenderContext() const
{
double scale = 0.0;
double mupp = 0.0;
int dpi = 0;
if ( model() )
model()->legendMapViewData( &mupp, &dpi, &scale );
bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );

if ( qgsDoubleNear( mupp, 0.0 ) || dpi == 0 || qgsDoubleNear( scale, 0.0 ) )
return nullptr;

// setup temporary render context
std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
QgsRenderContext *context = new QgsRenderContext;
context->setScaleFactor( dpi / 25.4 );
context->setRendererScale( scale );
context->setMapToPixel( QgsMapToPixel( mupp ) );
return validData ? context.release() : nullptr;
return context;
}

void QgsSymbolLegendNode::checkAll( bool state )
@@ -718,3 +720,71 @@ void QgsWmsLegendNode::invalidateMapBasedData()
mValid = false;
emit dataChanged();
}

// -------------------------------------------------------------------------

QgsDataDefinedSizeLegendNode::QgsDataDefinedSizeLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsDataDefinedSizeLegend &settings, QObject *parent )
: QgsLayerTreeModelLegendNode( nodeLayer, parent )
, mSettings( new QgsDataDefinedSizeLegend( settings ) )
{
}

QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
{
if ( role == Qt::DecorationRole )
{
cacheImage();
return QPixmap::fromImage( mImage );
}
else if ( role == Qt::SizeHintRole )
{
cacheImage();
return mImage.size();
}
return QVariant();
}

QgsLayerTreeModelLegendNode::ItemMetrics QgsDataDefinedSizeLegendNode::draw( const QgsLegendSettings &settings, QgsLayerTreeModelLegendNode::ItemContext *ctx )
{
// setup temporary render context
QgsRenderContext context;
context.setScaleFactor( settings.dpi() / 25.4 );
context.setRendererScale( settings.mapScale() );
context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) );
context.setForceVectorOutput( true );

if ( ctx )
{
context.setPainter( ctx->painter );
ctx->painter->save();
ctx->painter->setRenderHint( QPainter::Antialiasing );
ctx->painter->translate( ctx->point );
ctx->painter->scale( 1 / context.scaleFactor(), 1 / context.scaleFactor() );
}

QgsDataDefinedSizeLegend ddsLegend( *mSettings );
ddsLegend.setFont( settings.style( QgsLegendStyle::SymbolLabel ).font() );
ddsLegend.setTextColor( settings.fontColor() );

QSize contentSize;
int labelXOffset;
ddsLegend.drawCollapsedLegend( context, &contentSize, &labelXOffset );

if ( ctx )
ctx->painter->restore();

ItemMetrics im;
im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context.scaleFactor(), contentSize.height() / context.scaleFactor() );
im.labelSize = QSizeF( labelXOffset / context.scaleFactor(), contentSize.height() / context.scaleFactor() );
return im;
}


void QgsDataDefinedSizeLegendNode::cacheImage() const
{
if ( mImage.isNull() )
{
std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
mImage = mSettings->collapsedLegendImage( *context.get() );
}
}
@@ -131,6 +131,9 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
//! Construct the node with pointer to its parent layer node
explicit QgsLayerTreeModelLegendNode( QgsLayerTreeLayer *nodeL, QObject *parent SIP_TRANSFERTHIS = nullptr );

//! Returns a temporary context or null if legendMapViewData are not valid
QgsRenderContext *createTemporaryRenderContext() const SIP_FACTORY;

protected:
QgsLayerTreeLayer *mLayerNode = nullptr;
bool mEmbeddedInParent;
@@ -237,9 +240,6 @@ class CORE_EXPORT QgsSymbolLegendNode : public QgsLayerTreeModelLegendNode
// ident the symbol icon to make it look like a tree structure
static const int INDENT_SIZE = 20;

// return a temporary context or null if legendMapViewData are not valid
QgsRenderContext *createTemporaryRenderContext() const;

/** Sets all items belonging to the same layer as this node to the same check state.
* \param state check state
*/
@@ -380,4 +380,27 @@ class CORE_EXPORT QgsWmsLegendNode : public QgsLayerTreeModelLegendNode
mutable std::unique_ptr<QgsImageFetcher> mFetcher;
};


/**
* \ingroup core
* Produces legend node with a marker symbol
* \since QGIS 3.0
*/
class CORE_EXPORT QgsDataDefinedSizeLegendNode : public QgsLayerTreeModelLegendNode
{
public:
//! Construct the node using QgsDataDefinedSizeLegend as definition of the node's appearance
QgsDataDefinedSizeLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsDataDefinedSizeLegend &settings, QObject *parent SIP_TRANSFERTHIS = nullptr );

virtual QVariant data( int role ) const override;

ItemMetrics draw( const QgsLegendSettings &settings, ItemContext *ctx ) override;

private:
void cacheImage() const;

std::unique_ptr<QgsDataDefinedSizeLegend> mSettings;
mutable QImage mImage;
};

#endif // QGSLAYERTREEMODELLEGENDNODE_H

0 comments on commit 8c4d5bb

Please sign in to comment.
You can’t perform that action at this time.