Skip to content

Commit

Permalink
Merge pull request mapproxy#945 from simonseyock/wmts-legend-url
Browse files Browse the repository at this point in the history
feat: include WMS legend url in WMTS capabilities
  • Loading branch information
simonseyock authored Jul 17, 2024
2 parents e170438 + 546202f commit 5ee2227
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 8 deletions.
2 changes: 1 addition & 1 deletion doc/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ Please read :ref:`scale vs. resolution <scale_resolution>` for some notes on `sc
``legendurl``
"""""""""""""

Configure a URL to an image that should be returned as the legend for this layer. Local URLs (``file://``) are also supported. MapProxy ignores the legends from the sources of this layer if you configure a ``legendurl`` here.
Configure a URL to an image that should be returned as the legend for this layer. Local URLs (``file://``) are also supported. MapProxy ignores the legends from the sources of this layer if you configure a ``legendurl`` here. If WMS and WMTS are enabled the address to the WMS `GetLegendGraphic` endpoint will be included in the WMTS capabilities as the legend url.

.. _layer_metadata:

Expand Down
9 changes: 9 additions & 0 deletions mapproxy/config/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import warnings
from copy import deepcopy, copy
from functools import partial
from packaging.version import Version

import logging
from urllib.parse import urlparse
Expand Down Expand Up @@ -1950,13 +1951,21 @@ def tile_layers(self, grid_name_as_path=False):
md['format'] = self.context.caches[cache_name].image_opts().format
md['cache_name'] = cache_name
md['extent'] = extent
legend_version = None
if 'legendurl' in self.conf:
wms_conf = self.context.services.conf.get('wms')
if wms_conf is not None:
versions = wms_conf.get('versions', ['1.3.0'])
versions.sort(key=Version)
legend_version = versions[-1]
tile_layers.append(
TileLayer(
self.conf['name'], self.conf['title'],
info_sources=fi_sources,
md=md,
tile_manager=cache_source,
dimensions=dimensions,
legend_version=legend_version
)
)

Expand Down
8 changes: 7 additions & 1 deletion mapproxy/service/templates/wmts100capabilities.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ows:Keyword{{if kw.vocabulary}} vocabulary="{{kw.vocabulary}}"{{endif}}>{{keyword}}</ows:Keyword>
{{endfor}}
{{endfor}}
</ows:Keywords>
</ows:Keywords>
{{endif}}
<ows:ServiceType>OGC WMTS</ows:ServiceType>
<ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
Expand Down Expand Up @@ -97,6 +97,12 @@
<ows:Identifier>{{layer.name}}</ows:Identifier>
<Style>
<ows:Identifier>default</ows:Identifier>
{{if layer.legend_version}}
<LegendURL
format="image/png"
xlink:href="{{wms_service_url}}?service=WMS&amp;request=GetLegendGraphic&amp;version={{layer.legend_version}}&amp;format=image%2Fpng&amp;layer={{layer.name}}"
/>
{{endif}}
</Style>
<Format>image/{{layer.format}}</Format>
{{if layer.queryable}}
Expand Down
3 changes: 2 additions & 1 deletion mapproxy/service/tile.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def _render_root_resource_template(self, service):


class TileLayer(object):
def __init__(self, name, title, md, tile_manager, info_sources=[], dimensions=None):
def __init__(self, name, title, md, tile_manager, info_sources=[], dimensions=None, legend_version=None):
"""
:param md: the layer metadata
:param tile_manager: the layer tile manager
Expand All @@ -220,6 +220,7 @@ def __init__(self, name, title, md, tile_manager, info_sources=[], dimensions=No
self._empty_tile = None
self._mixed_format = True if self.md.get('format', False) == 'mixed' else False
self.empty_response_as_png = True
self.legend_version = legend_version

@property
def bbox(self):
Expand Down
11 changes: 8 additions & 3 deletions mapproxy/service/wmts.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"""
from __future__ import print_function

import re
from functools import partial

from mapproxy.request.wmts import (
Expand Down Expand Up @@ -273,11 +274,13 @@ def render(self, _map_request):
return self._render_template(_map_request.capabilities_template)

def template_context(self):
return dict(service=bunch(default='', **self.service),
service = bunch(default='', **self.service)
return dict(service=service,
restful=False,
layers=self.layers,
info_formats=self.info_formats,
tile_matrix_sets=self.matrix_sets)
tile_matrix_sets=self.matrix_sets,
wms_service_url=service.url)

def _render_template(self, template):
template = get_template(template)
Expand All @@ -294,7 +297,8 @@ def __init__(self, server_md, layers, matrix_sets, url_converter, fi_url_convert
self.fi_url_converter = fi_url_converter

def template_context(self):
return dict(service=bunch(default='', **self.service),
service = bunch(default='', **self.service)
return dict(service=service,
restful=True,
layers=self.layers,
info_formats=self.info_formats,
Expand All @@ -306,6 +310,7 @@ def template_context(self):
dimension_keys=dict((k.lower(), k) for k in self.url_converter.dimensions),
format_resource_template=format_resource_template,
format_info_resource_template=format_info_resource_template,
wms_service_url=re.sub(r'wmts$', 'service', service.url)
)


Expand Down
16 changes: 16 additions & 0 deletions mapproxy/test/system/fixture/legendgraphic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ services:
email: [email protected]
access_constraints:
Here be dragons.
wmts:
restful: true
kvp: true

layers:
- name: wms_legend
Expand All @@ -45,6 +48,13 @@ layers:
title: Layer with a static LegendURL
legendurl: http://localhost:42423/staticlegend_layer.png
sources: [legendurl_static_2]
- name: wmts_layer_legendurl
title: WMTS Layer with a static LegendURL
legendurl: http://localhost:42423/staticlegend_layer.png
sources: [tile_cache]
- name: wmts_layer_no_legendurl
title: WMTS Layer without a static LegendURL
sources: [tile_cache]

sources:
legend_cache:
Expand Down Expand Up @@ -91,3 +101,9 @@ sources:
url: http://localhost:42423/service
layers: foo

caches:
tile_cache:
cache:
type: file
grids: [GLOBAL_WEBMERCATOR]
sources: []
40 changes: 38 additions & 2 deletions mapproxy/test/system/test_legendgraphic.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,22 @@
import json

from mapproxy.compat.image import Image
from mapproxy.request.base import BaseRequest
from mapproxy.request.wms import (
WMS111MapRequest,
WMS111CapabilitiesRequest,
WMS130CapabilitiesRequest,
WMS111LegendGraphicRequest,
WMS130LegendGraphicRequest,
)
from mapproxy.request.wmts import WMTS100CapabilitiesRequest

from mapproxy.test.image import is_png, tmp_image
from mapproxy.test.helper import validate_with_dtd, validate_with_xsd
from mapproxy.test.http import mock_httpd
from mapproxy.test.system import SysTest
from mapproxy.test.system.test_wms import is_111_capa, assert_xpath_wms130, ns130
from mapproxy.test.system.test_wmts import assert_xpath_wmts, ns_wmts


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -64,6 +67,13 @@ def setup_method(self):
url="/service?",
param=dict(format="image/png", layer="wms_legend", sld_version="1.1.0"),
)
self.common_wmts_cap_req = WMTS100CapabilitiesRequest(
url="/service?",
param=dict(service="WMTS", version="1.0.0", request="GetCapabilities"),
)
self.common_wmts_rest_cap_req = BaseRequest(
url="/wmts/1.0.0/WMTSCapabilities.xml"
)

# test_00, test_01, test_02 need to run first in order to run the other tests properly
def test_00_get_legendgraphic_multiple_sources_111(self, app):
Expand Down Expand Up @@ -145,8 +155,8 @@ def test_capabilities_111(self, app):
xml.xpath("//Layer/Style/LegendURL/@height"),
)
assert legend_sizes == (
["256", "256", "256", "256"],
["512", "768", "256", "256"],
["256", "256", "256", "256", "256"],
["512", "768", "256", "256", "256"],
)
layer_urls = xml.xpath(
"//Layer/Style/LegendURL/OnlineResource/@xlink:href", namespaces=ns130
Expand Down Expand Up @@ -306,3 +316,29 @@ def test_get_legendgraphic_json_single_source_multiple_sub_layers(self, app):
json_data = json.loads(json_str)
assert json_data['Legend'][0]['layerName'] == 'foo'
assert json_data['Legend'][1]['layerName'] == 'bar'

def test_wmts_legend_url(self, app):
resp = app.get(self.common_wmts_cap_req)
assert resp.content_type == "application/xml"
xml = resp.lxml
assert_xpath_wmts(
xml,
'//wmts:Layer[1]/wmts:Style/wmts:LegendURL/@xlink:href',
'http://localhost/service?service=WMS&request=GetLegendGraphic&version=1.3.0&format=image%2Fpng'
'&layer=wmts_layer_legendurl'
)
assert xml.xpath('count(//wmts:Layer)', namespaces=ns_wmts) == 2
assert xml.xpath('count(//wmts:LegendURL)', namespaces=ns_wmts) == 1

def test_wmts_rest_legend_url(self, app):
resp = app.get(self.common_wmts_rest_cap_req)
assert resp.content_type == "application/xml"
xml = resp.lxml
assert_xpath_wmts(
xml,
'//wmts:Layer[1]/wmts:Style/wmts:LegendURL/@xlink:href',
'http://localhost/service?service=WMS&request=GetLegendGraphic&version=1.3.0&format=image%2Fpng'
'&layer=wmts_layer_legendurl'
)
assert xml.xpath('count(//wmts:Layer)', namespaces=ns_wmts) == 2
assert xml.xpath('count(//wmts:LegendURL)', namespaces=ns_wmts) == 1

0 comments on commit 5ee2227

Please sign in to comment.