Skip to content

Commit

Permalink
Add json support to WMS GetLegendGraphic
Browse files Browse the repository at this point in the history
  • Loading branch information
Éric Lemoine authored and nyalldawson committed Oct 26, 2019
1 parent b81d306 commit 2483304
Show file tree
Hide file tree
Showing 14 changed files with 129 additions and 45 deletions.
1 change: 1 addition & 0 deletions src/server/services/wms/qgswmsgetcapabilities.cpp
Expand Up @@ -497,6 +497,7 @@ namespace QgsWms
elem = doc.createElement( ( version == QLatin1String( "1.1.1" ) ? "GetLegendGraphic" : "sld:GetLegendGraphic" )/*wms:GetLegendGraphic*/ );
appendFormat( elem, QStringLiteral( "image/jpeg" ) );
appendFormat( elem, QStringLiteral( "image/png" ) );
appendFormat( elem, QStringLiteral( "application/json" ) );
elem.appendChild( dcpTypeElem.cloneNode().toElement() ); //this is the same as for 'GetCapabilities'
requestElem.appendChild( elem );

Expand Down
127 changes: 83 additions & 44 deletions src/server/services/wms/qgswmsgetlegendgraphics.cpp
Expand Up @@ -31,6 +31,8 @@
#include "qgswmsrenderer.h"

#include <QImage>
#include <QJsonObject>
#include <QJsonDocument>

namespace QgsWms
{
Expand All @@ -42,6 +44,7 @@ namespace QgsWms
QgsWmsParameters parameters( QUrlQuery( request.url() ) );

// check parameters validity
// FIXME fail with png + mode
checkParameters( parameters );

// init render context
Expand All @@ -50,42 +53,60 @@ namespace QgsWms
context.setFlag( QgsWmsRenderContext::UseSrcWidthHeight );
context.setParameters( parameters );

const QString format = request.parameters().value( QStringLiteral( "FORMAT" ), QStringLiteral( "PNG" ) );
ImageOutputFormat outputFormat = parseImageFormat( format );
// get the requested output format
QgsWmsParameters::Format format = parameters.format();

QString saveFormat;
QString contentType;
switch ( outputFormat )
// parameters.format() returns NONE if the requested format is image/png with a
// mode (e.g. image/png;mode=16bit), so in that case we use parseImageFormat to
// give the requested format another chance

QString imageSaveFormat;
QString imageContentType;
if ( format == QgsWmsParameters::Format::PNG )
{
imageContentType = "image/png";
imageSaveFormat = "PNG";
}
else if ( format == QgsWmsParameters::Format::JPG )
{
imageContentType = "image/jpeg";
imageSaveFormat = "JPEG";
}
else if ( format == QgsWmsParameters::Format::NONE )
{
switch ( parseImageFormat( parameters.formatAsString() ) )
{
case PNG:
case PNG8:
case PNG16:
case PNG1:
format = QgsWmsParameters::Format::PNG;
imageContentType = "image/png";
imageSaveFormat = "PNG";
break;
default:
break;
}
}

if ( format == QgsWmsParameters::Format::NONE )
{
case PNG:
case PNG8:
case PNG16:
case PNG1:
contentType = "image/png";
saveFormat = "PNG";
break;
case JPEG:
contentType = "image/jpeg";
saveFormat = "JPEG";
break;
default:
throw QgsServiceException( "InvalidFormat",
QStringLiteral( "Output format '%1' is not supported in the GetLegendGraphic request" ).arg( format ) );
break;
throw QgsServiceException( "InvalidFormat",
QStringLiteral( "Output format '%1' is not supported in the GetLegendGraphic request" ).arg( parameters.formatAsString() ) );
}

// Get cached image
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsAccessControl *accessControl = serverIface->accessControls();
QgsServerCacheManager *cacheManager = serverIface->cacheManager();
if ( cacheManager )
if ( cacheManager && !imageSaveFormat.isEmpty() )
{
QImage image;
QByteArray content = cacheManager->getCachedImage( project, request, accessControl );
if ( !content.isEmpty() && image.loadFromData( content ) )
{
response.setHeader( QStringLiteral( "Content-Type" ), contentType );
image.save( response.io(), qPrintable( saveFormat ) );
response.setHeader( QStringLiteral( "Content-Type" ), imageContentType );
image.save( response.io(), qPrintable( imageSaveFormat ) );
return;
}
}
Expand All @@ -97,34 +118,52 @@ namespace QgsWms
std::unique_ptr<QgsLayerTreeModel> model( legendModel( context, *tree.get() ) );

// rendering
std::unique_ptr<QImage> result;
if ( !parameters.rule().isEmpty() )
if ( format == QgsWmsParameters::Format::JSON )
{
QgsLayerTreeModelLegendNode *node = legendNode( parameters.rule(), *model.get() );
result.reset( renderer.getLegendGraphics( *node ) );
QJsonObject result;
if ( !parameters.rule().isEmpty() )
{
throw QgsBadRequestException( QgsServiceException::QGIS_InvalidParameterValue,
QStringLiteral( "RULE cannot be used with JSON format" ) );
}
else
{
result = renderer.getLegendGraphicsAsJson( *model.get() );
}
tree->clear();
response.setHeader( QStringLiteral( "Content-Type" ), parameters.formatAsString() );
QJsonDocument doc( result );
response.write( doc.toJson( QJsonDocument::Compact ) );
}
else
{
result.reset( renderer.getLegendGraphics( *model.get() ) );
}

tree->clear();

if ( result )
{
writeImage( response, *result, format, context.imageQuality() );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( cacheManager )
std::unique_ptr<QImage> result;
if ( !parameters.rule().isEmpty() )
{
QgsLayerTreeModelLegendNode *node = legendNode( parameters.rule(), *model.get() );
result.reset( renderer.getLegendGraphics( *node ) );
}
else
{
QByteArray content = response.data();
if ( !content.isEmpty() )
cacheManager->setCachedImage( &content, project, request, accessControl );
result.reset( renderer.getLegendGraphics( *model.get() ) );
}
tree->clear();
if ( result )
{
writeImage( response, *result, parameters.formatAsString(), context.imageQuality() );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
if ( cacheManager )
{
QByteArray content = response.data();
if ( !content.isEmpty() )
cacheManager->setCachedImage( &content, project, request, accessControl );
}
#endif
}
else
{
throw QgsException( QStringLiteral( "Failed to compute GetLegendGraphics image" ) );
}
else
{
throw QgsException( QStringLiteral( "Failed to compute GetLegendGraphics image" ) );
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/server/services/wms/qgswmsparameters.cpp
Expand Up @@ -814,7 +814,11 @@ namespace QgsWms
{
f = Format::PDF;
}

else if ( fStr.compare( QLatin1String( "application/json" ), Qt::CaseInsensitive ) == 0 ||
fStr.compare( QLatin1String( "json" ), Qt::CaseInsensitive ) == 0 )
{
f = Format::JSON;
}
return f;
}

Expand Down
22 changes: 22 additions & 0 deletions src/server/services/wms/qgswmsrenderer.cpp
Expand Up @@ -174,6 +174,28 @@ namespace QgsWms
return image.release();
}

QJsonObject QgsRenderer::getLegendGraphicsAsJson( QgsLayerTreeModel &model )
{
// get layers
std::unique_ptr<QgsLayerRestorer> restorer;
restorer.reset( new QgsLayerRestorer( mContext.layers() ) );

// configure layers
QList<QgsMapLayer *> layers = mContext.layersToRender();
configureLayers( layers );

// init renderer
QgsLegendSettings settings = legendSettings();
QgsLegendRenderer renderer( &model, settings );

// rendering
QJsonObject json;
QgsRenderContext renderContext;
renderer.exportLegendToJson( renderContext, json );

return json;
}

void QgsRenderer::runHitTest( const QgsMapSettings &mapSettings, HitTest &hitTest ) const
{
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
Expand Down
9 changes: 9 additions & 0 deletions src/server/services/wms/qgswmsrenderer.h
Expand Up @@ -94,6 +94,15 @@ namespace QgsWms
*/
QImage *getLegendGraphics( QgsLayerTreeModelLegendNode &nodeModel );

/**
* Returns the map legend as a JSON object. The caller takes the ownership
* of the JSON object.
* \param model The layer tree model to use for building the legend
* \returns the legend as a JSON object
* \since QGIS 3.9
*/
QJsonObject getLegendGraphicsAsJson( QgsLayerTreeModel &model );

typedef QSet<QString> SymbolSet;
typedef QHash<QgsVectorLayer *, SymbolSet> HitTest;

Expand Down
1 change: 1 addition & 0 deletions tests/testdata/qgis_server/getcapabilities.txt
Expand Up @@ -67,6 +67,7 @@ Content-Type: text/xml; charset=utf-8
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down
1 change: 1 addition & 0 deletions tests/testdata/qgis_server/getcapabilities_inspire.txt
Expand Up @@ -68,6 +68,7 @@ Content-Type: text/xml; charset=utf-8
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down
1 change: 1 addition & 0 deletions tests/testdata/qgis_server/getprojectsettings.txt
Expand Up @@ -67,6 +67,7 @@ Content-Type: text/xml; charset=utf-8
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down
1 change: 1 addition & 0 deletions tests/testdata/qgis_server/wms_getcapabilities_1_1_1.txt
Expand Up @@ -68,6 +68,7 @@ Content-Type: text/xml; charset=utf-8
<GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down
1 change: 1 addition & 0 deletions tests/testdata/qgis_server/wms_getcapabilities_1_3_0.txt
Expand Up @@ -67,6 +67,7 @@ Content-Type: text/xml; charset=utf-8
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down
Expand Up @@ -58,6 +58,7 @@ Content-Type: text/xml; charset=utf-8
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down
Expand Up @@ -58,6 +58,7 @@ Content-Type: text/xml; charset=utf-8
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down
Expand Up @@ -60,6 +60,7 @@ Content-Type: text/xml; charset=utf-8
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down
Expand Up @@ -68,6 +68,7 @@ Content-Type: text/xml; charset=utf-8
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
Expand Down

0 comments on commit 2483304

Please sign in to comment.