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

42 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-23 20:06 +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 if not op: 

81 raise gws.Error(f'WMTS: no GetTile operation and no ResourceURL for layer {sl.name!r}') 

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

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

84 

85 # {} should not be encoded 

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

87 

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

89 """Fetch a tile. 

90 

91 Args: 

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

93 matrix_uid: Tile matrix identifier. 

94 col: Tile column. 

95 row: Tile row. 

96 

97 Returns: 

98 The tile image content. 

99 

100 Raises: 

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

102 """ 

103 url = url_template 

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

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

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

107 

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

109 if not res.ok: 

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

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

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

113 return res.content