Merge pull request #36370 from pblottiere/legend_json

Update the API to export a legend in JSON
This commit is contained in:
Paul Blottiere 2020-05-13 15:13:43 +02:00 committed by GitHub
commit dee58822af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 118 additions and 78 deletions

View File

@ -160,14 +160,13 @@ 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 );
QJsonObject exportToJson( const QgsLegendSettings &settings, const QgsRenderContext &context );
%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
@ -183,13 +182,12 @@ Draws symbol on the left side of the item
: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;
virtual QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) 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
@ -203,16 +201,6 @@ 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:
@ -269,7 +257,7 @@ 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 QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const;
virtual void setEmbeddedInParent( bool embedded );
@ -519,7 +507,7 @@ 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;
virtual QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const;
};
@ -553,7 +541,7 @@ 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;
virtual QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const;
};
@ -586,7 +574,7 @@ 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 QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const;
virtual void invalidateMapBasedData();

View File

@ -81,7 +81,7 @@ Draws the legend using a given render ``context``. The legend will occupy the ar
.. versionadded:: 3.6
%End
void exportLegendToJson( const QgsRenderContext &context, QJsonObject &json );
QJsonObject exportLegendToJson( const QgsRenderContext &context );
%Docstring
Renders the legend in a ``json`` object.

View File

@ -84,10 +84,12 @@ QgsLayerTreeModelLegendNode::ItemMetrics QgsLayerTreeModelLegendNode::draw( cons
return im;
}
void QgsLayerTreeModelLegendNode::exportToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json )
QJsonObject QgsLayerTreeModelLegendNode::exportToJson( const QgsLegendSettings &settings, const QgsRenderContext &context )
{
exportSymbolToJson( settings, context, json );
exportSymbolTextToJson( settings, json );
QJsonObject json = exportSymbolToJson( settings, context );
const QString text = data( Qt::DisplayRole ).toString();
json[ QStringLiteral( "title" ) ] = text;
return json;
}
QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
@ -130,18 +132,21 @@ QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &setting
return size;
}
void QgsLayerTreeModelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &, QJsonObject &json ) const
QJsonObject QgsLayerTreeModelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext & ) const
{
const QIcon icon = data( Qt::DecorationRole ).value<QIcon>();
if ( icon.isNull() )
return;
return QJsonObject();
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;
QJsonObject json;
json[ QStringLiteral( "icon" ) ] = base64;
return json;
}
QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const
@ -225,12 +230,6 @@ 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 )
@ -661,12 +660,12 @@ QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemC
std::max( height + 2 * heightOffset, static_cast< double >( desiredHeight ) ) );
}
void QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const
QJsonObject QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
{
const QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
if ( !s )
{
return;
return QJsonObject();
}
@ -704,7 +703,10 @@ void QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings,
QBuffer buffer( &byteArray );
img.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
QJsonObject json;
json[ QStringLiteral( "icon" ) ] = base64;
return json;
}
void QgsSymbolLegendNode::setEmbeddedInParent( bool embedded )
@ -862,13 +864,16 @@ QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemCo
return settings.wmsLegendSize();
}
void QgsImageLegendNode::exportSymbolToJson( const QgsLegendSettings &, const QgsRenderContext &, QJsonObject &json ) const
QJsonObject QgsImageLegendNode::exportSymbolToJson( const QgsLegendSettings &, const QgsRenderContext & ) const
{
QByteArray byteArray;
QBuffer buffer( &byteArray );
mImage.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
QJsonObject json;
json[ QStringLiteral( "icon" ) ] = base64;
return json;
}
// -------------------------------------------------------------------------
@ -954,7 +959,7 @@ QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings,
return size;
}
void QgsRasterSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &, QJsonObject &json ) const
QJsonObject QgsRasterSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext & ) const
{
QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
img.fill( Qt::transparent );
@ -989,7 +994,10 @@ void QgsRasterSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &set
QBuffer buffer( &byteArray );
img.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
QJsonObject json;
json[ QStringLiteral( "icon" ) ] = base64;
return json;
}
// -------------------------------------------------------------------------
@ -1078,13 +1086,16 @@ QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemCont
return settings.wmsLegendSize();
}
void QgsWmsLegendNode::exportSymbolToJson( const QgsLegendSettings &, const QgsRenderContext &, QJsonObject &json ) const
QJsonObject QgsWmsLegendNode::exportSymbolToJson( const QgsLegendSettings &, const QgsRenderContext & ) const
{
QByteArray byteArray;
QBuffer buffer( &byteArray );
mImage.save( &buffer, "PNG" );
const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
json[ "icon" ] = base64;
QJsonObject json;
json[ QStringLiteral( "icon" ) ] = base64;
return json;
}
/* private */

View File

@ -220,10 +220,9 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
* 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 );
QJsonObject exportToJson( const QgsLegendSettings &settings, const QgsRenderContext &context );
/**
* Draws symbol on the left side of the item
@ -238,10 +237,9 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
* 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;
virtual QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const;
/**
* Draws label on the right side of the item
@ -252,14 +250,6 @@ 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();
@ -311,7 +301,7 @@ 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;
QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const override;
void setEmbeddedInParent( bool embedded ) override;
@ -554,7 +544,7 @@ 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;
QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const override;
private:
QImage mImage;
@ -585,7 +575,7 @@ 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;
QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const override;
private:
QColor mColor;
@ -617,7 +607,7 @@ 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;
QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const override;
void invalidateMapBasedData() override;

View File

@ -68,18 +68,22 @@ void QgsLegendRenderer::drawLegend( QPainter *painter )
paintAndDetermineSize( context );
}
void QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &context, QJsonObject &json )
QJsonObject QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &context )
{
QJsonObject json;
QgsLayerTreeGroup *rootGroup = mLegendModel->rootGroup();
if ( !rootGroup )
return;
return json;
json = exportLegendToJson( context, rootGroup );
json[QStringLiteral( "title" )] = mSettings.title();
exportLegendToJson( context, rootGroup, json );
return json;
}
void QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &context, QgsLayerTreeGroup *nodeGroup, QJsonObject &json )
QJsonObject QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &context, QgsLayerTreeGroup *nodeGroup )
{
QJsonObject json;
QJsonArray nodes;
const QList<QgsLayerTreeNode *> childNodes = nodeGroup->children();
for ( QgsLayerTreeNode *node : childNodes )
@ -90,17 +94,13 @@ void QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &context, Qgs
const QModelIndex idx = mLegendModel->node2index( nodeGroup );
const QString text = mLegendModel->data( idx, Qt::DisplayRole ).toString();
QJsonObject group;
QJsonObject group = exportLegendToJson( context, nodeGroup );
group[ QStringLiteral( "type" ) ] = QStringLiteral( "group" );
group[ QStringLiteral( "title" ) ] = text;
exportLegendToJson( context, nodeGroup, group );
nodes.append( group );
}
else if ( QgsLayerTree::isLayer( node ) )
{
QJsonObject group;
group[ QStringLiteral( "type" ) ] = QStringLiteral( "layer" );
QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
QString text;
@ -117,27 +117,32 @@ void QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &context, Qgs
if ( legendNodes.count() == 1 )
{
legendNodes.at( 0 )->exportToJson( mSettings, context, group );
QJsonObject group = legendNodes.at( 0 )->exportToJson( mSettings, context );
group[ QStringLiteral( "type" ) ] = QStringLiteral( "layer" );
nodes.append( group );
}
else if ( legendNodes.count() > 1 )
{
QJsonObject group;
group[ QStringLiteral( "type" ) ] = QStringLiteral( "layer" );
group[ QStringLiteral( "title" ) ] = text;
QJsonArray symbols;
for ( int j = 0; j < legendNodes.count(); j++ )
{
QgsLayerTreeModelLegendNode *legendNode = legendNodes.at( j );
QJsonObject symbol;
legendNode->exportToJson( mSettings, context, symbol );
QJsonObject symbol = legendNode->exportToJson( mSettings, context );
symbols.append( symbol );
}
group[ QStringLiteral( "title" ) ] = text;
group[ QStringLiteral( "symbols" ) ] = symbols;
nodes.append( group );
}
}
}
json[QStringLiteral( "nodes" )] = nodes;
return json;
}
QSizeF QgsLegendRenderer::paintAndDetermineSize( QgsRenderContext &context )

View File

@ -100,7 +100,7 @@ class CORE_EXPORT QgsLegendRenderer
*
* \since QGIS 3.8
*/
void exportLegendToJson( const QgsRenderContext &context, QJsonObject &json );
QJsonObject exportLegendToJson( const QgsRenderContext &context );
/**
* Sets the \a style of a \a node.
@ -234,7 +234,7 @@ class CORE_EXPORT QgsLegendRenderer
*
* \since QGIS 3.8
*/
void exportLegendToJson( const QgsRenderContext &context, QgsLayerTreeGroup *nodeGroup, QJsonObject &json );
QJsonObject exportLegendToJson( const QgsRenderContext &context, QgsLayerTreeGroup *nodeGroup );
/**
* Draws the legend using the specified render \a context, and returns the actual size of the legend.

View File

@ -194,11 +194,8 @@ namespace QgsWms
QgsLegendRenderer renderer( &model, settings );
// rendering
QJsonObject json;
QgsRenderContext renderContext;
renderer.exportLegendToJson( renderContext, json );
return json;
return renderer.exportLegendToJson( renderContext );
}
void QgsRenderer::runHitTest( const QgsMapSettings &mapSettings, HitTest &hitTest ) const

View File

@ -101,10 +101,8 @@ static QJsonObject _renderJsonLegend( QgsLayerTreeModel *legendModel, const QgsL
{
QgsLegendRenderer legendRenderer( legendModel, settings );
QJsonObject json;
QgsRenderContext context;
legendRenderer.exportLegendToJson( context, json );
return json;
return legendRenderer.exportLegendToJson( context );
}
static bool _verifyImage( const QString &testName, QString &report, int diff = 30 )

View File

@ -138,6 +138,7 @@ ADD_PYTHON_TEST(PyQgsLayoutUnitsComboBox test_qgslayoutunitscombobox.py)
ADD_PYTHON_TEST(PyQgsLegendPatchShape test_qgslegendpatchshape.py)
ADD_PYTHON_TEST(PyQgsLegendPatchShapeButton test_qgslegendpatchshapebutton.py)
ADD_PYTHON_TEST(PyQgsLegendPatchShapeWidget test_qgslegendpatchshapewidget.py)
ADD_PYTHON_TEST(PyQgsLegendRenderer test_qgslegendrenderer.py)
ADD_PYTHON_TEST(PyQgsLineSegment test_qgslinesegment.py)
ADD_PYTHON_TEST(PyQgsLineSymbolLayers test_qgslinesymbollayers.py)
ADD_PYTHON_TEST(PyQgsLocalDefaultSettings test_qgslocaldefaultsettings.py)

View File

@ -0,0 +1,50 @@
# coding=utf-8
""""Test QgsLegendRenderer JSON export
.. note:: 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.
Run with ctest -V -R PyQgsLegendRenderer
"""
__author__ = 'elpaso@itopen.it'
__date__ = '2020-04-29'
__copyright__ = 'Copyright 2020, ItOpen'
import os
from qgis.core import (
QgsProject,
QgsLegendModel,
QgsLegendSettings,
QgsLegendRenderer,
QgsRenderContext,
)
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
QGISAPP = start_app()
TEST_DATA_DIR = unitTestDataPath()
class TestPyQgsLegendRenderer(unittest.TestCase):
def test_json_export(self):
project = QgsProject()
self.assertTrue(project.read(os.path.join(unitTestDataPath('qgis_server'), 'test_project.qgs')))
model = QgsLegendModel(project.layerTreeRoot())
ctx = QgsRenderContext()
settings = QgsLegendSettings()
renderer = QgsLegendRenderer(model, settings)
nodes = renderer.exportLegendToJson(ctx)['nodes'].toVariant()
self.assertEqual(len(nodes), 7)
self.assertEqual(nodes[0]['type'], 'layer')
self.assertEqual(nodes[0]['title'], 'testlayer')
if __name__ == '__main__':
unittest.main()