Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle invalid layer other than vector in the legend #77

Merged
merged 1 commit into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ test/data/*.gpkg-wal
test/data/*.qgs~
test/data/*.qgz~
test/data/*_attachments.zip
test/data/*.asc.aux.xml
26 changes: 17 additions & 9 deletions lizmap_server/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,11 @@ def find_vector_layer_from_params(params, project):
return True, layer


def find_vector_layer(layer_name: str, project: QgsProject) -> Optional[QgsVectorLayer]:
""" Find vector layer with name, short name or layer id. """
def find_layer(layer_name: str, project: QgsProject) -> Optional[QgsMapLayer]:
""" Find layer with name, short name or layer ID. """
found = None
for layer in project.mapLayers().values():

# only vector layer
if layer.type() != QgsMapLayer.VectorLayer:
continue

# check name
if layer.name() == layer_name:
found = layer
Expand All @@ -75,18 +71,30 @@ def find_vector_layer(layer_name: str, project: QgsProject) -> Optional[QgsVecto

if not found:
Logger.warning(
"The vector layer '{}' has not been found in the project '{}'".format(layer_name, project.fileName()))
"The layer '{}' has not been found in the project '{}'".format(layer_name, project.fileName()))
return None

found: QgsVectorLayer
found: QgsMapLayer
if not found.isValid():
Logger.warning(
f"The vector layer '{layer_name}' has been found but it is not valid in the project "
f"The layer '{layer_name}' has been found but it is not valid in the project "
f"'{project.fileName()}'",
)
return found


def find_vector_layer(layer_name: str, project: QgsProject) -> Optional[QgsVectorLayer]:
""" Find vector layer with name, short name or layer ID. """
layer = find_layer(layer_name, project)
if not layer:
return None

if not layer.type() == QgsMapLayer.VectorLayer:
return None

return layer


def get_server_fid(feature: QgsFeature, pk_attributes: list) -> str:
""" Build server feature ID. """
if not pk_attributes:
Expand Down
14 changes: 10 additions & 4 deletions lizmap_server/get_legend_graphic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
from collections import namedtuple
from typing import Optional

from qgis.core import Qgis, QgsProject, QgsVectorLayer
from qgis.core import Qgis, QgsMapLayer, QgsProject, QgsVectorLayer
from qgis.PyQt.QtCore import QBuffer, QIODevice
from qgis.PyQt.QtGui import QImage
from qgis.server import QgsServerFilter

from lizmap_server.core import find_vector_layer
from lizmap_server.core import find_layer
from lizmap_server.logger import Logger, exception_handler
from lizmap_server.tools import to_bool

Expand Down Expand Up @@ -86,11 +86,11 @@ def responseComplete(self):
show_feature_count = to_bool(params.get('SHOWFEATURECOUNT'), default_value=False)

current_style = ''
layer = find_vector_layer(layer_name, project)
layer = find_layer(layer_name, project)
if not layer:
logger.info("Skipping the layer '{}' because it's not a vector layer".format(layer_name))
return

layer: QgsMapLayer
if not layer.isValid():
logger.warning(
f"Layer '{layer_name}' is not valid, returning a warning icon in the legend for project "
Expand All @@ -109,6 +109,12 @@ def responseComplete(self):
handler.appendBody(json.dumps(json_data).encode('utf8'))
return

if layer.type() != QgsMapLayer.VectorLayer:
logger.info("Skipping the layer '{}' because it's not a vector layer".format(layer_name))
return

layer: QgsVectorLayer

try:
current_style = layer.styleManager().currentStyle()

Expand Down
4,605 changes: 2,545 additions & 2,060 deletions test/data/legend.qgs

Large diffs are not rendered by default.

4,228 changes: 2,316 additions & 1,912 deletions test/data/legend_invalid.qgs

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions test/data/raster.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
NCOLS 16
NROWS 5
XLLCENTER 0
YLLCENTER 47.618
DX 0.011
DY 0.011
NODATA_VALUE -1
050 050 050 050 050 050 050 050 050 050 050 050 050 050 050 050
075 075 075 075 075 075 075 075 075 075 075 075 075 075 075 075
100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100
125 125 125 125 125 125 125 -1 -1 -1 -1 -1 125 125 125 125
125 125 125 125 125 125 125 -1 -1 -1 -1 -1 125 125 125 125

CRS
NOTES
102 changes: 102 additions & 0 deletions test/test_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,78 @@ def test_simple_rule_based_feature_count(client):
assert b['nodes'][0]['title'] == 'rule_based [4]', b['nodes'][0]['title']


def test_valid_raster_layer(client):
""" Test valid raster layer. """
qs = dict(BASE_QUERY)
qs['MAP'] = PROJECT
qs['LAYER'] = 'raster'
rv = client.get(_build_query_string(qs), PROJECT)
b = _check_request(rv)

# Answer straight from QGIS Server
if Qgis.QGIS_VERSION_INT < 32200:
expected = {
'nodes': [
{
'symbols': [
{
# 'icon': '',
'title': '50',
}, {
# 'icon': '',
'title': '125',
},
],
'title': qs['LAYER'],
'type': 'layer',
},
],
'title': '',
}
del b['nodes'][0]['symbols'][0]['icon']
del b['nodes'][0]['symbols'][1]['icon']
elif 32200 <= Qgis.QGIS_VERSION_INT < 33400:
expected = {
'nodes': [
{
'symbols': [
{
'title': 'Band 1',
}, {
'title': '',
},
],
'title': qs['LAYER'],
'type': 'layer',
},
],
'title': '',
}
else:
expected = {
'nodes': [
{
'symbols': [
{
'title': 'Band 1',
}, {
# 'icon': '',
'max': 125,
'min': 50,
'title': '',
},
],
'title': qs['LAYER'],
'type': 'layer',
},
],
'title': '',
}
del b['nodes'][0]['symbols'][1]['icon']

assert b == expected, b


def test_invalid_layer_symbol_layer(client):
""" Test unique symbol for layer. """
qs = dict(BASE_QUERY)
Expand All @@ -183,6 +255,36 @@ def test_invalid_layer_symbol_layer(client):
assert 'symbols' not in b['nodes'][0]


def test_invalid_layer_raster_layer(client):
""" Test invalid raster layer. """
qs = dict(BASE_QUERY)
qs['MAP'] = PROJECT_INVALID
qs['LAYER'] = 'raster'
rv = client.get(_build_query_string(qs), PROJECT_INVALID)
b = _check_request(rv)

expected = {
'title': '',
'nodes': [
{
'type': 'layer',
'title': qs['LAYER'],
'icon': GetLegendGraphicFilter.warning_icon(),
'valid': False,
},
],
}
assert b == expected, b

assert b['title'] == '', b
assert len(b['nodes']) == 1, b
assert b['nodes'][0]['title'] == qs['LAYER']
assert b['nodes'][0].get('valid') is False
assert b['nodes'][0].get('icon') == GetLegendGraphicFilter.warning_icon()
assert 'icon' in b['nodes'][0]
assert 'symbols' not in b['nodes'][0]


def test_invalid_layer_categorized_symbol_layer(client):
""" Test categorized symbol for layer. """
qs = dict(BASE_QUERY)
Expand Down
4 changes: 3 additions & 1 deletion test/test_lizmap_accesscontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ def test_invalid_layer(client):

layers = rv.xpath('//wms:Layer')
assert layers is not None
assert len(layers) == 4, len(layers)
assert len(layers) == 5, len(layers)

# Layer is WMS
layer = "unique_symbol"
assert layer in rv.content.decode('utf-8')

assert 'raster' in rv.content.decode('utf-8')

qs['SERVICE'] = "WFS"
rv = client.get(_build_query_string(qs), project_invalid)
_check_request(rv, 'text/xml')
Expand Down