Skip to content

Commit

Permalink
Merge pull request #44490 from qgis/backport-43981-to-release-3_20
Browse files Browse the repository at this point in the history
[Backport release-3_20] Server : Fix legend when no LAYERS parameter provided, Fix layer style defined by a maptheme
  • Loading branch information
elpaso committed Aug 1, 2021
2 parents 5b02138 + 390f82d commit 1395156
Show file tree
Hide file tree
Showing 6 changed files with 2,671 additions and 41 deletions.
36 changes: 35 additions & 1 deletion src/server/services/wms/qgswmsrenderer.cpp
Expand Up @@ -33,6 +33,7 @@
#include "qgslegendrenderer.h"
#include "qgsmaplayer.h"
#include "qgsmaplayerlegend.h"
#include "qgsmapthemecollection.h"
#include "qgsmaptopixel.h"
#include "qgsproject.h"
#include "qgsrasteridentifyresult.h"
Expand Down Expand Up @@ -695,15 +696,29 @@ namespace QgsWms
// If the map is set to follow preset we need to disable follow preset and manually
// configure the layers here or the map item internal logic will override and get
// the layers from the map theme.
QMap<QString, QString> layersStyle;
if ( map->followVisibilityPreset() )
{
const QString presetName = map->followVisibilityPresetName();
if ( layerSet.isEmpty() )
{
// Get the layers from the theme
const QgsExpressionContext ex { map->createExpressionContext() };
layerSet = map->layersToRender( &ex );
}
// Disable the theme
map->setFollowVisibilityPreset( false );

// Collect the style of each layer in the theme that has been disabled
const QList<QgsMapThemeCollection::MapThemeLayerRecord> mapThemeRecords = QgsProject::instance()->mapThemeCollection()->mapThemeState( presetName ).layerRecords();
for ( const auto &layerMapThemeRecord : std::as_const( mapThemeRecords ) )
{
if ( layerSet.contains( layerMapThemeRecord.layer() ) )
{
layersStyle.insert( layerMapThemeRecord.layer()->id(),
layerMapThemeRecord.layer()->styleManager()->style( layerMapThemeRecord.currentStyle ).xmlData() );
}
}
}

// Handle highlight layers
Expand All @@ -715,6 +730,14 @@ namespace QgsWms

map->setLayers( layerSet );
map->setKeepLayerSet( true );

// Set style override if a particular style should be used due to a map theme.
// It will actualize linked legend symbols too.
if ( !layersStyle.isEmpty() )
{
map->setLayerStyleOverrides( layersStyle );
map->setKeepLayerStyles( true );
}
}

//grid space x / y
Expand Down Expand Up @@ -803,7 +826,18 @@ namespace QgsWms
// get model and layer tree root of the legend
QgsLegendModel *model = legend->model();
QStringList layerSet;
const QList<QgsMapLayer *> layerList( map->layers() );
QList<QgsMapLayer *> mapLayers;
if ( map->layers().isEmpty() )
{
// in QGIS desktop, each layer has its legend, including invisible layers
// and using maptheme, legend items are automatically filtered
mapLayers = mProject->mapLayers( true ).values();
}
else
{
mapLayers = map->layers();
}
const QList<QgsMapLayer *> layerList = mapLayers;
for ( const auto &layer : layerList )
layerSet << layer->id();

Expand Down
26 changes: 26 additions & 0 deletions tests/src/python/test_qgsserver.py
Expand Up @@ -43,6 +43,7 @@
from qgis.core import QgsRenderChecker, QgsApplication, QgsFontUtils, QgsMultiRenderChecker
from qgis.testing import unittest, start_app
from qgis.PyQt.QtCore import QSize
from qgis.PyQt.QtGui import QColor
from utilities import unitTestDataPath

import osgeo.gdal # NOQA
Expand Down Expand Up @@ -284,6 +285,31 @@ def _assert_status_code(self, status_code, qs, requestMethod=QgsServerRequest.Ge
self.server.handleRequest(request, response, project)
assert response.statusCode() == status_code, "%s != %s" % (response.statusCode(), status_code)

def _assertRed(self, color: QColor):
self.assertEqual(color.red(), 255)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 0)

def _assertGreen(self, color: QColor):
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 255)
self.assertEqual(color.blue(), 0)

def _assertBlue(self, color: QColor):
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 255)

def _assertBlack(self, color: QColor):
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 255)

def _assertWhite(self, color: QColor):
self.assertEqual(color.red(), 255)
self.assertEqual(color.green(), 255)
self.assertEqual(color.blue(), 255)


class TestQgsServerTestBase(unittest.TestCase):

Expand Down
244 changes: 244 additions & 0 deletions tests/src/python/test_qgsserver_wms_getprint_legend.py
@@ -0,0 +1,244 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsServer WMS GetPrint legend.
From build dir, run: ctest -R PyQgsServerWMSGetPrintLegend -V
.. 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.
"""
__author__ = 'Sebastien Peillet'
__date__ = '30/06/2021'
__copyright__ = 'Copyright 2021, The QGIS Project'

import os
import shutil

# Needed on Qt 5 so that the serialization of XML is consistent among all executions
os.environ['QT_HASH_SEED'] = '1'

from qgis.testing import unittest
from qgis.server import QgsBufferServerRequest, QgsBufferServerResponse
from qgis.core import QgsProject
from qgis.PyQt.QtCore import QTemporaryDir
from qgis.PyQt.QtGui import QImage


from test_qgsserver import QgsServerTestBase
from utilities import unitTestDataPath


class PyQgsServerWMSGetPrintLegend(QgsServerTestBase):
"""Tests for issue GH #42036 QGIS Server GetPrint:
QGIS server print behaves inconsistently regarding legend content"""

def test_wms_getprint_legend(self):
"""Test project has 2 layer: red and green and five templates:
red: follow map theme red
green: follow map theme green
blank: no map theme
full: follow map theme full with both layer
falsegreen : follow map theme falsegreen (visible layer : green but with blue style)
"""

tmp_dir = QTemporaryDir()
shutil.copyfile(os.path.join(unitTestDataPath('qgis_server'), 'test_project_legend.qgs'), os.path.join(tmp_dir.path(), 'test_project_legend.qgs'))
shutil.copyfile(os.path.join(unitTestDataPath('qgis_server'), 'test_project_legend.gpkg'), os.path.join(tmp_dir.path(), 'test_project_legend.gpkg'))

project = QgsProject()
self.assertTrue(project.read(os.path.join(tmp_dir.path(), 'test_project_legend.qgs')))

params = {
"SERVICE": "WMS",
"VERSION": "1.3",
"REQUEST": "GetPrint",
"TEMPLATE": "blank",
"FORMAT": "png",
"LAYERS": "",
"map0:EXTENT": "778000,5600000,836000,5650000",
"map0:SCALE": "281285",
"map0:LAYERS": "red",
"CRS": "EPSG:3857",
"DPI": '72'
}

######################################################
# Template legend tests
# Legend symbol are displayed at coordinates :
# First item : 600 x , 40 y
# Second item : 600 x , 60 y

# blank template, no theme, no LAYERS, specified map0:LAYERS is red
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the red layer is displayed, there is no second item
self._assertRed(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# blank template, no LAYERS, specified map0:LAYERS is green
params["map0:LAYERS"] = "green"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the green layer is displayed, there is no second item
self._assertGreen(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# blank template
params["map0:LAYERS"] = ""
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the red layer is displayed, there is no second item
self._assertRed(image.pixelColor(600, 40))
self._assertGreen(image.pixelColor(600, 60))

# red template, red theme, specified map0:LAYERS is red
params["TEMPLATE"] = "red"
params["map0:LAYERS"] = "red"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the red layer is displayed, there is no second item
self._assertRed(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# red template, red theme, specified map0:LAYERS is green
params["map0:LAYERS"] = "green"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the green layer is displayed, there is no second item
self._assertGreen(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# red template, red theme, no map0:LAYERS
params["map0:LAYERS"] = ""
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the red layer is displayed, there is no second item
self._assertRed(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# green template, green theme, specified map0:LAYERS is red
params["TEMPLATE"] = "green"
params["map0:LAYERS"] = "red"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the red layer is displayed, there is no second item
self._assertRed(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# green template, green theme, specified map0:LAYERS is green
params["map0:LAYERS"] = "green"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the green layer is displayed, there is no second item
self._assertGreen(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# green template, green theme, no map0:LAYERS
params["map0:LAYERS"] = ""
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the green layer is displayed, there is no second item
self._assertGreen(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# full template, full theme, specified map0:LAYERS is red
params["TEMPLATE"] = "full"
params["map0:LAYERS"] = "red"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the red layer is displayed, there is no second item
self._assertRed(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# full template, full theme, specified map0:LAYERS is green
params["map0:LAYERS"] = "green"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the green layer is displayed, there is no second item
self._assertGreen(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# full template, full theme, no map0:LAYERS
params["map0:LAYERS"] = ""
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Both red and green layers are displayed
self._assertRed(image.pixelColor(600, 40))
self._assertGreen(image.pixelColor(600, 60))

# falsegreen template, falsegreen theme (green layer is blue), specified map0:LAYERS is red
params["TEMPLATE"] = "falsegreen"
params["map0:LAYERS"] = "red"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the red layer is displayed, there is no second item
self._assertRed(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# full template, full theme, specified map0:LAYERS is green
params["map0:LAYERS"] = "green"
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the green layer (in blue) is displayed, there is no second item
self._assertBlue(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))

# full template, full theme, no map0:LAYERS
params["map0:LAYERS"] = ""
response = QgsBufferServerResponse()
request = QgsBufferServerRequest('?' + '&'.join(["%s=%s" % i for i in params.items()]))
self.server.handleRequest(request, response, project)

image = QImage.fromData(response.body(), "PNG")
# Only the green layer (in blue) is displayed, there is no second item
self._assertBlue(image.pixelColor(600, 40))
self._assertWhite(image.pixelColor(600, 60))


if __name__ == '__main__':
unittest.main()

0 comments on commit 1395156

Please sign in to comment.