Skip to content

Commit

Permalink
[FEATURE] Rendering of data-defined size in legend in "collapsed" mode
Browse files Browse the repository at this point in the history
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 8c4d5bb
Show file tree
Hide file tree
Showing 23 changed files with 949 additions and 78 deletions.
1 change: 1 addition & 0 deletions python/core/core.sip
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions python/core/layertree/qgslayertreemodellegendnode.sip
Expand Up @@ -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:
};

Expand Down Expand Up @@ -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 *
* *
Expand Down
15 changes: 15 additions & 0 deletions python/core/qgsdiagramrenderer.sip
Expand Up @@ -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 );

};

/************************************************************************
Expand Down
22 changes: 22 additions & 0 deletions python/core/symbology-ng/qgscategorizedsymbolrenderer.sip
Expand Up @@ -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 );
Expand Down Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions python/core/symbology-ng/qgsgraduatedsymbolrenderer.sip
Expand Up @@ -7,6 +7,7 @@
************************************************************************/



class QgsRendererRange
{

Expand Down Expand Up @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions python/core/symbology-ng/qgslegendsymbolitem.sip
Expand Up @@ -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

};


Expand Down
22 changes: 22 additions & 0 deletions python/core/symbology-ng/qgssinglesymbolrenderer.sip
Expand Up @@ -7,6 +7,7 @@
************************************************************************/



class QgsSingleSymbolRenderer : QgsFeatureRenderer
{

Expand All @@ -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 );
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -141,6 +141,7 @@ SET(QGIS_CORE_SRCS
qgscredentials.cpp
qgscrscache.cpp
qgsdartmeasurement.cpp
qgsdatadefinedsizelegend.cpp
qgsdataitem.cpp
qgsdataitemprovider.cpp
qgsdataitemproviderregistry.cpp
Expand Down Expand Up @@ -738,6 +739,7 @@ SET(QGIS_CORE_HDRS
qgscrscache.h
qgscsexception.h
qgsdartmeasurement.h
qgsdatadefinedsizelegend.h
qgsdataitem.h
qgsdataitemprovider.h
qgsdataitemproviderregistry.h
Expand Down
80 changes: 75 additions & 5 deletions src/core/layertree/qgslayertreemodellegendnode.cpp
Expand Up @@ -18,6 +18,7 @@

#include "qgslayertreemodellegendnode.h"

#include "qgsdatadefinedsizelegend.h"
#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgslegendsettings.h"
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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() );
}
}
29 changes: 26 additions & 3 deletions src/core/layertree/qgslayertreemodellegendnode.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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.