Coverage for gws-app/gws/plugin/ows_client/wmts/provider.py: 0%

40 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-05 13:35 +0200

1"""WMTS service provider.""" 

2 

3from typing import Optional, cast 

4 

5import gws 

6import gws.base.layer 

7import gws.base.ows.client 

8import gws.config.util 

9import gws.lib.uom 

10import gws.lib.net 

11 

12from . import caps 

13 

14 

15class Config(gws.base.ows.client.provider.Config): 

16 """Connection to a WMTS service.""" 

17 

18 

19class Object(gws.base.ows.client.provider.Object): 

20 """WMTS service provider. 

21 

22 Reads the capabilities of a WMTS service, builds tile URL templates and 

23 fetches tiles for the layers that use the service. 

24 """ 

25 

26 protocol = gws.OwsProtocol.WMTS 

27 

28 tileMatrixSets: list[gws.TileMatrixSet] 

29 """Tile matrix sets offered by the service.""" 

30 

31 def configure(self): 

32 cc = caps.parse(self.get_capabilities()) 

33 

34 self.metadata = cc.metadata 

35 self.sourceLayers = cc.sourceLayers 

36 self.version = cc.version 

37 self.tileMatrixSets = cc.tileMatrixSets 

38 

39 self.configure_operations(cc.operations) 

40 

41 def tile_url_template(self, sl: gws.SourceLayer, tms: gws.TileMatrixSet, style: gws.SourceStyle) -> str: 

42 """Create a tile URL template for a source layer. 

43 

44 If the layer has a ``ResourceURL`` of type ``tile``, its template is used, 

45 with ``{TileMatrixSet}`` and ``{Style}`` filled in. Otherwise, a KVP GetTile 

46 URL is built, with the layer format or ``image/jpeg``. In both cases the 

47 result contains the ``{TileMatrix}``, ``{TileCol}`` and ``{TileRow}`` placeholders. 

48 

49 Args: 

50 sl: Source layer. 

51 tms: Tile matrix set. 

52 style: Source style. 

53 

54 Returns: 

55 The URL template. 

56 """ 

57 ru = sl.resourceUrls 

58 resource_url = ru.get('tile') if ru else None 

59 

60 if resource_url: 

61 return ( 

62 resource_url 

63 .replace('{TileMatrixSet}', tms.identifier) 

64 .replace('{Style}', style.name)) 

65 

66 params = { 

67 'SERVICE': gws.OwsProtocol.WMTS, 

68 'REQUEST': gws.OwsVerb.GetTile, 

69 'VERSION': self.version, 

70 'LAYER': sl.name, 

71 'FORMAT': sl.imageFormat or 'image/jpeg', 

72 'TILEMATRIXSET': tms.identifier, 

73 'STYLE': style.name, 

74 'TILEMATRIX': '{TileMatrix}', 

75 'TILECOL': '{TileCol}', 

76 'TILEROW': '{TileRow}', 

77 } 

78 

79 op = self.get_operation(gws.OwsVerb.GetTile) 

80 args = self.prepare_operation(op, params=params) 

81 url = gws.lib.net.add_params(args.url, args.params) 

82 

83 # {} should not be encoded 

84 return url.replace('%7B', '{').replace('%7D', '}') 

85 

86 def get_tile(self, url_template: str, matrix_uid: str, col: int, row: int) -> bytes: 

87 """Fetch a tile. 

88 

89 Args: 

90 url_template: URL template from ``tile_url_template``. 

91 matrix_uid: Tile matrix identifier. 

92 col: Tile column. 

93 row: Tile row. 

94 

95 Returns: 

96 The tile image content. 

97 

98 Raises: 

99 ``gws.ExternalServiceError``: If the request fails or the response is not an image. 

100 """ 

101 url = url_template 

102 url = url.replace('{TileMatrix}', matrix_uid) 

103 url = url.replace('{TileCol}', str(col)) 

104 url = url.replace('{TileRow}', str(row)) 

105 

106 res = gws.lib.net.http_request(url) 

107 if not res.ok: 

108 raise gws.ExternalServiceError(f'tile request failed: status={res.status_code} url={url!r}') 

109 if not res.content_type.startswith('image/'): 

110 raise gws.ExternalServiceError(f'tile request failed: content type {res.content_type!r} url={url!r}') 

111 return res.content