Coverage for gws-app/gws/plugin/ows_client/wmts/caps.py: 100%

55 statements  

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

1"""WMTS capabilities parser.""" 

2 

3import gws 

4import gws.base.ows.client 

5import gws.lib.crs 

6import gws.gis.source 

7import gws.gis.zoom 

8import gws.lib.xmlx as xmlx 

9import gws.base.ows.client.parseutil as u 

10 

11 

12# http://portal.opengeospatial.org/files/?artifact_id=35326 

13 

14 

15def parse(xml: str) -> gws.OwsCapabilities: 

16 """Read WMTS capabilities from the GetCapabilities XML. 

17 

18 Matrices in each tile matrix set are sorted from the coarsest to the finest 

19 scale. Each source layer refers to the tile matrix sets it is linked to. 

20 

21 Args: 

22 xml: GetCapabilities XML. 

23 

24 Returns: 

25 The parsed capabilities, including the tile matrix sets. 

26 """ 

27 caps_el = xmlx.from_string(xml, gws.XmlOptions(compactWhitespace=True, removeNamespaces=True)) 

28 tms_lst = [_tile_matrix_set(el) for el in caps_el.findall('Contents/TileMatrixSet')] 

29 tms_dct = {tms.identifier: tms for tms in tms_lst} 

30 sls = gws.gis.source.check_layers(_layer(el, tms_dct) for el in caps_el.findall('Contents/Layer')) 

31 return gws.OwsCapabilities( 

32 tileMatrixSets=tms_lst, 

33 metadata=u.service_metadata(caps_el), 

34 operations=u.service_operations(caps_el), 

35 sourceLayers=sls, 

36 version=caps_el.get('version'), 

37 ) 

38 

39 

40def _layer(layer_el: gws.XmlElement, tms_dct): 

41 """Create a source layer from a ``Layer`` element.""" 

42 # <Layer> 

43 # <ows:Title>... 

44 # <Style>... 

45 # <Format>... 

46 # <TileMatrixSetLink> 

47 # <TileMatrixSet>... 

48 

49 sl = gws.SourceLayer() 

50 

51 sl.metadata = u.element_metadata(layer_el) 

52 sl.name = sl.metadata.get('name', '') 

53 sl.title = sl.metadata.get('title', '') 

54 

55 sl.styles = [u.parse_style(e) for e in layer_el.findall('Style')] 

56 sl.defaultStyle = u.default_style(sl.styles) 

57 if sl.defaultStyle: 

58 sl.legendUrl = sl.defaultStyle.legendUrl 

59 

60 sl.tileMatrixIds = [el.textof('TileMatrixSet') for el in layer_el.findall('TileMatrixSetLink')] 

61 sl.tileMatrixSets = [tms_dct[tid] for tid in sl.tileMatrixIds] 

62 

63 extra_crsids = [tms.crs.srid for tms in sl.tileMatrixSets] 

64 wgs_extent = u.wgs_extent(layer_el) 

65 crs_list = u.supported_crs(layer_el, extra_crsids) 

66 

67 sl.supportedCrs = crs_list or [gws.lib.crs.WGS84] 

68 sl.wgsExtent = wgs_extent or gws.lib.crs.WGS84.extent 

69 

70 sl.isImage = True 

71 sl.isVisible = True 

72 

73 sl.imageFormat = layer_el.textof('Format') 

74 

75 sl.resourceUrls = {e.get('resourceType'): e.get('template') for e in layer_el.findall('ResourceURL')} 

76 

77 return sl 

78 

79 

80def _tile_matrix_set(tms_el: gws.XmlElement): 

81 """Create a tile matrix set from a ``TileMatrixSet`` element.""" 

82 # <TileMatrixSet> 

83 # <ows:Identifier>... 

84 # <ows:SupportedCRS>... 

85 # <TileMatrix> 

86 # ... 

87 

88 tms = gws.TileMatrixSet() 

89 

90 tms.identifier = tms_el.textof('Identifier') 

91 tms.crs = gws.lib.crs.require(tms_el.textof('SupportedCRS')) 

92 tms.matrices = sorted([_tile_matrix(e, tms.crs) for e in tms_el.findall('TileMatrix')], key=lambda m: -m.scale) 

93 

94 return tms 

95 

96 

97def _tile_matrix(tm_el: gws.XmlElement, crs: gws.Crs): 

98 """Create a tile matrix from a ``TileMatrix`` element.""" 

99 # <TileMatrix> 

100 # <ows:Identifier> 

101 # <ScaleDenominator> 

102 # ... 

103 

104 tm = gws.TileMatrix() 

105 tm.identifier = tm_el.textof('Identifier') 

106 tm.scale = u.to_float(tm_el.textof('ScaleDenominator')) 

107 tm.resolution = gws.gis.zoom.scale_to_res(tm.scale, crs) 

108 

109 p = u.to_float_pair(tm_el.textof('TopLeftCorner')) 

110 tm.x = p[0] 

111 tm.y = p[1] 

112 

113 tm.width = u.to_int(tm_el.textof('MatrixWidth')) 

114 tm.height = u.to_int(tm_el.textof('MatrixHeight')) 

115 

116 tm.tileWidth = u.to_int(tm_el.textof('TileWidth')) 

117 tm.tileHeight = u.to_int(tm_el.textof('TileHeight')) 

118 

119 # see http://portal.opengeospatial.org/files/?artifact_id=35326 page 8 

120 res = tm.resolution 

121 tm.extent = ( 

122 tm.x, 

123 tm.y - res * tm.height * tm.tileHeight, 

124 tm.x + res * tm.width * tm.tileWidth, 

125 tm.y, 

126 ) 

127 

128 return tm