Skip to content
Permalink
Browse files

Merge pull request #9362 from pblottiere/legend_json

Add json format for legend
  • Loading branch information
pblottiere committed Mar 21, 2019
2 parents efc7e87 + 90b46f6 commit e9067834eb82422b4f8e9ece94406fae3b9bb3d6
Showing with 463 additions and 2 deletions.
  1. +45 −0 python/core/auto_generated/layertree/qgslayertreemodellegendnode.sip.in
  2. +7 −0 python/core/auto_generated/qgslegendrenderer.sip.in
  3. +121 −0 src/core/layertree/qgslayertreemodellegendnode.cpp
  4. +35 −0 src/core/layertree/qgslayertreemodellegendnode.h
  5. +72 −0 src/core/qgslegendrenderer.cpp
  6. +15 −0 src/core/qgslegendrenderer.h
  7. +168 −2 tests/src/core/testqgslegendrenderer.cpp
  8. BIN tests/testdata/control_images/legend/expected_line_layer_icon/expected_line_layer_icon.png
  9. BIN .../testdata/control_images/legend/expected_point_layer_icon_blue/expected_point_layer_icon_blue.png
  10. BIN ...l_images/legend/expected_point_layer_icon_blue_opacity/expected_point_layer_icon_blue_opacity.png
  11. BIN ...estdata/control_images/legend/expected_point_layer_icon_green/expected_point_layer_icon_green.png
  12. BIN ...images/legend/expected_point_layer_icon_green_opacity/expected_point_layer_icon_green_opacity.png
  13. BIN tests/testdata/control_images/legend/expected_point_layer_icon_red/expected_point_layer_icon_red.png
  14. BIN ...ata/control_images/legend/expected_point_layer_icon_red_big/expected_point_layer_icon_red_big.png
  15. BIN ...rol_images/legend/expected_point_layer_icon_red_opacity/expected_point_layer_icon_red_opacity.png
  16. BIN tests/testdata/control_images/legend/expected_polygon_layer_icon/expected_polygon_layer_icon.png
  17. BIN tests/testdata/control_images/legend/expected_raster_layer_icon_1/expected_raster_layer_icon_1.png
  18. BIN tests/testdata/control_images/legend/expected_raster_layer_icon_2/expected_raster_layer_icon_2.png
@@ -94,6 +94,18 @@ Entry point called from QgsLegendRenderer to do the rendering.
Default implementation calls drawSymbol() and drawSymbolText() methods.

If ctx is ``None``, this is just first stage when preparing layout - without actual rendering.
%End

void exportToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json );
%Docstring
Entry point called from QgsLegendRenderer to do the rendering in a
JSON object.

:param settings: Legend layout configuration
:param context: Rendering context
:param json: The json object to update

.. versionadded:: 3.8
%End

virtual QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const;
@@ -105,6 +117,17 @@ Draws symbol on the left side of the item
:param itemHeight: Minimal height of the legend item - used for correct positioning when rendering

:return: Real size of the symbol (may be bigger than "normal" symbol size from settings)
%End

virtual void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const;
%Docstring
Adds a symbol in base64 string within a JSON object with the key "icon".

:param settings: Legend layout configuration
:param context: Rendering context
:param json: The json object to update

.. versionadded:: 3.8
%End

virtual QSizeF drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const;
@@ -116,6 +139,16 @@ Draws label on the right side of the item
:param symbolSize: Real size of the associated symbol - used for correct positioning when rendering

:return: Size of the label (may span multiple lines)
%End

void exportSymbolTextToJson( const QgsLegendSettings &settings, QJsonObject &json ) const;
%Docstring
Adds a label in a JSON object with the key "title".

:param settings: Legend layout configuration
:param json: The json object to update

.. versionadded:: 3.8
%End

signals:
@@ -172,6 +205,9 @@ Constructor for QgsSymbolLegendNode.
virtual QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const;


virtual void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const;


virtual void setEmbeddedInParent( bool embedded );


@@ -358,6 +394,9 @@ Constructor for QgsImageLegendNode.
virtual QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const;


virtual void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const;


};

class QgsRasterSymbolLegendNode : QgsLayerTreeModelLegendNode
@@ -389,6 +428,9 @@ Constructor for QgsRasterSymbolLegendNode.
virtual QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const;


virtual void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const;


};


@@ -419,6 +461,9 @@ Constructor for QgsWmsLegendNode.
virtual QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const;


virtual void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const;


virtual void invalidateMapBasedData();


@@ -76,6 +76,13 @@ The ``painter`` should be scaled beforehand so that units correspond to millimet
Draws the legend using a given render ``context``. The legend will occupy the area reported in legendSize().

.. versionadded:: 3.6
%End

void exportLegendToJson( const QgsRenderContext &context, QJsonObject &json );
%Docstring
Renders the legend in a ``json`` object.

.. versionadded:: 3.8
%End

static void setNodeLegendStyle( QgsLayerTreeNode *node, QgsLegendStyle::Style style );
@@ -71,6 +71,11 @@ QgsLayerTreeModelLegendNode::ItemMetrics QgsLayerTreeModelLegendNode::draw( cons
return im;
}

void QgsLayerTreeModelLegendNode::exportToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json )
{
exportSymbolToJson( settings, context, json );
exportSymbolTextToJson( settings, json );
}

QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
{
@@ -84,6 +89,19 @@ QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &setting
return settings.symbolSize();
}

void QgsLayerTreeModelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &, QJsonObject &json ) const
{
const QIcon icon = data( Qt::DecorationRole ).value<QIcon>();
if ( icon.isNull() )
return;

const QImage image( icon.pixmap( settings.symbolSize().width(), settings.symbolSize().height() ).toImage() );
QByteArray byteArray;
QBuffer buffer( &byteArray );
image.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
}

QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const
{
@@ -129,6 +147,12 @@ QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &set
return labelSize;
}

void QgsLayerTreeModelLegendNode::exportSymbolTextToJson( const QgsLegendSettings &, QJsonObject &json ) const
{
const QString text = data( Qt::DisplayRole ).toString();
json[ "title" ] = text;
}

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

QgsSymbolLegendNode::QgsSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsLegendSymbolItem &item, QObject *parent )
@@ -490,6 +514,47 @@ QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemC
std::max( height + 2 * heightOffset, static_cast< double >( settings.symbolSize().height() ) ) );
}

void QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const
{
const QgsSymbol *s = mItem.symbol();
if ( !s )
{
return;
}

QgsRenderContext ctx;
ctx.setScaleFactor( settings.dpi() / 25.4 );
ctx.setRendererScale( settings.mapScale() );
ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) );
ctx.setForceVectorOutput( true );

// ensure that a minimal expression context is available
QgsExpressionContext expContext = context.expressionContext();
expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
ctx.setExpressionContext( expContext );

const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx );
QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );

int opacity = 255;
if ( QgsVectorLayer *vectorLayer = dynamic_cast<QgsVectorLayer *>( layerNode()->layer() ) )
opacity = ( 255 * vectorLayer->opacity() );

if ( opacity != 255 )
{
QPainter painter;
painter.begin( &img );
painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
painter.end();
}

QByteArray byteArray;
QBuffer buffer( &byteArray );
img.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
}

void QgsSymbolLegendNode::setEmbeddedInParent( bool embedded )
{
@@ -598,6 +663,15 @@ QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemCo
return settings.wmsLegendSize();
}

void QgsImageLegendNode::exportSymbolToJson( const QgsLegendSettings &, const QgsRenderContext &, QJsonObject &json ) const
{
QByteArray byteArray;
QBuffer buffer( &byteArray );
mImage.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
}

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

QgsRasterSymbolLegendNode::QgsRasterSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent )
@@ -654,6 +728,44 @@ QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings,
return settings.symbolSize();
}

void QgsRasterSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &, QJsonObject &json ) const
{
QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
img.fill( Qt::transparent );

QPainter painter( &img );
painter.setRenderHint( QPainter::Antialiasing );

QColor itemColor = mColor;
if ( QgsRasterLayer *rasterLayer = dynamic_cast<QgsRasterLayer *>( layerNode()->layer() ) )
{
if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
}
painter.setBrush( itemColor );

if ( settings.drawRasterStroke() )
{
QPen pen;
pen.setColor( settings.rasterStrokeColor() );
pen.setWidthF( settings.rasterStrokeWidth() );
pen.setJoinStyle( Qt::MiterJoin );
painter.setPen( pen );
}
else
{
painter.setPen( Qt::NoPen );
}

painter.drawRect( QRectF( 0, 0, settings.symbolSize().width(), settings.symbolSize().height() ) );

QByteArray byteArray;
QBuffer buffer( &byteArray );
img.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
}

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

QgsWmsLegendNode::QgsWmsLegendNode( QgsLayerTreeLayer *nodeLayer, QObject *parent )
@@ -722,6 +834,15 @@ QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemCont
return settings.wmsLegendSize();
}

void QgsWmsLegendNode::exportSymbolToJson( const QgsLegendSettings &, const QgsRenderContext &, QJsonObject &json ) const
{
QByteArray byteArray;
QBuffer buffer( &byteArray );
mImage.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
}

/* private */
QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
{
@@ -110,6 +110,16 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
*/
virtual ItemMetrics draw( const QgsLegendSettings &settings, ItemContext *ctx );

/**
* Entry point called from QgsLegendRenderer to do the rendering in a
* JSON object.
* \param settings Legend layout configuration
* \param context Rendering context
* \param json The json object to update
* \since QGIS 3.8
*/
void exportToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json );

/**
* Draws symbol on the left side of the item
* \param settings Legend layout configuration
@@ -119,6 +129,15 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
*/
virtual QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const;

/**
* Adds a symbol in base64 string within a JSON object with the key "icon".
* \param settings Legend layout configuration
* \param context Rendering context
* \param json The json object to update
* \since QGIS 3.8
*/
virtual void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const;

/**
* Draws label on the right side of the item
* \param settings Legend layout configuration
@@ -128,6 +147,14 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
*/
virtual QSizeF drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const;

/**
* Adds a label in a JSON object with the key "title".
* \param settings Legend layout configuration
* \param json The json object to update
* \since QGIS 3.8
*/
void exportSymbolTextToJson( const QgsLegendSettings &settings, QJsonObject &json ) const;

signals:
//! Emitted on internal data change so the layer tree model can forward the signal to views
void dataChanged();
@@ -175,6 +202,8 @@ class CORE_EXPORT QgsSymbolLegendNode : public QgsLayerTreeModelLegendNode

QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const override;

void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const override;

void setEmbeddedInParent( bool embedded ) override;

void setUserLabel( const QString &userLabel ) override { mUserLabel = userLabel; updateLabel(); }
@@ -354,6 +383,8 @@ class CORE_EXPORT QgsImageLegendNode : public QgsLayerTreeModelLegendNode

QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const override;

void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const override;

private:
QImage mImage;
};
@@ -383,6 +414,8 @@ class CORE_EXPORT QgsRasterSymbolLegendNode : public QgsLayerTreeModelLegendNode

QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const override;

void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const override;

private:
QColor mColor;
QString mLabel;
@@ -413,6 +446,8 @@ class CORE_EXPORT QgsWmsLegendNode : public QgsLayerTreeModelLegendNode

QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const override;

void exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const override;

void invalidateMapBasedData() override;

private slots:

0 comments on commit e906783

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