Skip to content
Permalink
Browse files
Merge pull request #43391 from elpaso/bugfix-gh34178-server-getprint-…
…maptheme

Server WMS getPrint: fix HIGHLIGHT with follow map themes
  • Loading branch information
elpaso committed Jun 9, 2021
2 parents 15c40d6 + 5c59764 commit 3a4d437a511f77af63a0f31866e98901a47d7d2b
@@ -508,12 +508,17 @@ be used instead.

void setLayers( const QList<QgsMapLayer *> &layers );
%Docstring
Sets the stored ``layers`` set. If empty, the current project layers will
be used instead.
Sets the stored ``layers`` set. If empty, the current project layers will be used.
If the map item is set to follow a map theme (via :py:func:`~QgsLayoutItemMap.followVisibilityPreset` and :py:func:`~QgsLayoutItemMap.followVisibilityPresetName` ),
then this method will have no effect and the layers rendered in the map will always follow the map theme.

.. seealso:: :py:func:`layers`

.. seealso:: :py:func:`keepLayerSet`

.. seealso:: :py:func:`followVisibilityPreset`

.. seealso:: :py:func:`followVisibilityPresetName`
%End

bool keepLayerStyles() const;
@@ -361,6 +361,7 @@ void QgsLayoutItemMap::setFollowVisibilityPreset( bool follow )
return;

mFollowVisibilityPreset = follow;

if ( !mFollowVisibilityPresetName.isEmpty() )
emit themeChanged( mFollowVisibilityPreset ? mFollowVisibilityPresetName : QString() );
}
@@ -513,10 +513,13 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem, public QgsTemporalRan
QList<QgsMapLayer *> layers() const;

/**
* Sets the stored \a layers set. If empty, the current project layers will
* be used instead.
* Sets the stored \a layers set. If empty, the current project layers will be used.
* If the map item is set to follow a map theme (via followVisibilityPreset() and followVisibilityPresetName() ),
* then this method will have no effect and the layers rendered in the map will always follow the map theme.
* \see layers()
* \see keepLayerSet()
* \see followVisibilityPreset()
* \see followVisibilityPresetName()
*/
void setLayers( const QList<QgsMapLayer *> &layers );

@@ -646,13 +646,13 @@ namespace QgsWms

if ( !map->keepLayerSet() )
{
QList<QgsMapLayer *> layerSet;
if ( cMapParams.mLayers.isEmpty() )
{
map->setLayers( mapSettings.layers() );
layerSet = mapSettings.layers();
}
else
{
QList<QgsMapLayer *> layerSet;
for ( auto layer : cMapParams.mLayers )
{
if ( mContext.isValidGroup( layer.mNickname ) )
@@ -689,11 +689,31 @@ namespace QgsWms
layerSet << mlayer;
}
}

layerSet << highlightLayers( cMapParams.mHighlightLayers );
std::reverse( layerSet.begin(), layerSet.end() );
map->setLayers( layerSet );
}

// 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.
if ( map->followVisibilityPreset() )
{
if ( layerSet.isEmpty() )
{
// Get the layers from the theme
const QgsExpressionContext ex { map->createExpressionContext() };
layerSet = map->layersToRender( &ex );
}
map->setFollowVisibilityPreset( false );
}

// Handle highlight layers
const QList< QgsMapLayer *> highlights = highlightLayers( cMapParams.mHighlightLayers );
for ( const auto &hl : std::as_const( highlights ) )
{
layerSet.prepend( hl );
}

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

@@ -2743,13 +2763,21 @@ namespace QgsWms
{
// create sld document from symbology
QDomDocument sldDoc;
if ( !sldDoc.setContent( param.mSld, true ) )
{
QString errorMsg;
int errorLine;
int errorColumn;
if ( !sldDoc.setContent( param.mSld, true, &errorMsg, &errorLine, &errorColumn ) )
{
QgsMessageLog::logMessage( QStringLiteral( "Error parsing SLD for layer %1 at line %2, column %3:\n%4" )
.arg( param.mName )
.arg( errorLine )
.arg( errorColumn )
.arg( errorMsg ),
QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
continue;
}

// create renderer from sld document
QString errorMsg;
std::unique_ptr<QgsFeatureRenderer> renderer;
QDomElement el = sldDoc.documentElement();
renderer.reset( QgsFeatureRenderer::loadSld( el, param.mGeom.type(), errorMsg ) );
@@ -445,6 +445,7 @@ if (WITH_SERVER)
ADD_PYTHON_TEST(PyQgsServerWMSGetPrintExtra test_qgsserver_wms_getprint_extra.py)
ADD_PYTHON_TEST(PyQgsServerWMSGetPrintOutputs test_qgsserver_wms_getprint_outputs.py)
ADD_PYTHON_TEST(PyQgsServerWMSGetPrintAtlas test_qgsserver_wms_getprint_atlas.py)
ADD_PYTHON_TEST(PyQgsServerWMSGetPrintMapTheme, test_qgsserver_wms_getprint_maptheme.py)
ADD_PYTHON_TEST(PyQgsServerWMSDimension test_qgsserver_wms_dimension.py)
ADD_PYTHON_TEST(PyQgsServerSettings test_qgsserver_settings.py)
ADD_PYTHON_TEST(PyQgsServerProjectUtils test_qgsserver_projectutils.py)
@@ -0,0 +1,208 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsServer WMS GetPrint map theme.
From build dir, run: ctest -R PyQgsServerWMSGetPrintMapTheme -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__ = 'Alessandro Pasotti'
__date__ = '24/05/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 PyQgsServerWMSGetPrintMapTheme(QgsServerTestBase):
"""Tests for issue GH #34178 QGIS Server GetPrint:
HIGHLIGHT_GEOM is not printed if map layers are configured to follow a map theme"""

def test_wms_getprint_maptheme(self):
"""Test project has 2 layer: red and green and three templates:
red: follow map theme red
green: follow map theme green
blank: no map theme
"""

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

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

params = {
"SERVICE": "WMS",
"VERSION": "1.3",
"REQUEST": "GetPrint",
"TEMPLATE": "blank",
"FORMAT": "png",
"LAYERS": "",
"map0:EXTENT": "44.92867722467413216,7.097696894150993252,45.0714498943264914,7.378188333645007368",
"map0:LAYERS": "red",
"CRS": "EPSG:4326",
"DPI": '72'
}

polygon = 'POLYGON((7.09769689415099325 44.92867722467413216, 7.37818833364500737 44.92867722467413216, 7.37818833364500737 45.0714498943264914, 7.09769689415099325 45.0714498943264914, 7.09769689415099325 44.92867722467413216))'

######################################################
# Template map theme tests, no HIGHLIGHT

# blank template, specified layer 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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 255)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 0)

# blank template, specified layer 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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 255)
self.assertEqual(color.blue(), 0)

# red template, no specified layers
params["map0:LAYERS"] = ""
params["TEMPLATE"] = "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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 255)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 0)

# green template, no specified layers
params["map0:LAYERS"] = ""
params["TEMPLATE"] = "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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 255)
self.assertEqual(color.blue(), 0)

# green template, specified layer is red
# This is a conflict situation: the green template map is set to follow green theme
# but we tell the server to render the red layer, red is what we get.
params["map0:LAYERS"] = "red"
params["TEMPLATE"] = "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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 255)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 0)

######################################################
# Start HIGHLIGHT tests

params["TEMPLATE"] = "blank"
params["map0:LAYERS"] = "red"
params["map0:HIGHLIGHT_GEOM"] = polygon
params["map0:HIGHLIGHT_SYMBOL"] = r'<StyledLayerDescriptor><UserStyle><FeatureTypeStyle><Rule><PolygonSymbolizer><Fill><CssParameter name="fill">%230000FF</CssParameter></Fill></PolygonSymbolizer></Rule></FeatureTypeStyle></UserStyle></StyledLayerDescriptor>'

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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 255)

# Test highlight without layers
params["TEMPLATE"] = "blank"
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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 255)

# Test highlight on follow theme (issue GH #34178)
params["TEMPLATE"] = "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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 255)

# Test highlight on follow theme (issue GH #34178)
params["TEMPLATE"] = "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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 255)

# Test highlight on follow theme, but add LAYERS (issue GH #34178)
params["TEMPLATE"] = "green"
params["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")
color = image.pixelColor(100, 100)
self.assertEqual(color.red(), 0)
self.assertEqual(color.green(), 0)
self.assertEqual(color.blue(), 255)


if __name__ == '__main__':
unittest.main()
Binary file not shown.

0 comments on commit 3a4d437

Please sign in to comment.