Coverage for gws-app/gws/plugin/ows_client/wms/caps.py: 0%

50 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-17 01:37 +0200

1"""WMS Capabilities parser.""" 

2 

3from typing import Optional 

4 

5import gws 

6import gws.base.ows.client 

7import gws.gis.crs 

8import gws.gis.source 

9import gws.lib.xmlx as xmlx 

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

11 

12 

13 

14def parse(xml: str, bottom_first: bool=False) -> gws.OwsCapabilities: 

15 """Read WMS capabilities from the GetCapabilities XML. 

16 

17 Args: 

18 xml: GetCapabilities XML 

19 bottom_first: True if layers are listed bottom-first 

20 

21 Returns: 

22 The Capabilities object. 

23 """ 

24 

25 caps_el = xmlx.from_string(xml, compact_whitespace=True, remove_namespaces=True) 

26 source_layers = gws.gis.source.check_layers( 

27 [_layer(el) for el in caps_el.findall('Capability/Layer')], 

28 revert=bottom_first 

29 ) 

30 return gws.OwsCapabilities( 

31 metadata=u.service_metadata(caps_el), 

32 operations=u.service_operations(caps_el), 

33 sourceLayers=source_layers, 

34 version=caps_el.get('version')) 

35 

36 

37def _layer(layer_el: gws.XmlElement, parent: Optional[gws.SourceLayer] = None) -> gws.SourceLayer: 

38 sl = gws.SourceLayer() 

39 

40 sl.isQueryable = layer_el.get('queryable') == '1' 

41 sl.isVisible = True 

42 sl.isExpanded = False 

43 sl.metadata = u.element_metadata(layer_el) 

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

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

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

47 

48 # @TODO: support ScaleHint (WMS 1.1) 

49 

50 smin = layer_el.textof('MinScaleDenominator') 

51 smax = layer_el.textof('MaxScaleDenominator') 

52 if smax: 

53 sl.scaleRange = [u.to_int(smin), u.to_int(smax)] 

54 

55 wgs_extent = u.wgs_extent(layer_el) 

56 crs_list = u.supported_crs(layer_el) 

57 

58 if not parent: 

59 sl.supportedCrs = crs_list or [gws.gis.crs.WGS84] 

60 sl.wgsExtent = wgs_extent 

61 

62 else: 

63 # OGC 06-042, 7.2.4.8 Inheritance of layer properties 

64 

65 # Style -> add 

66 m = {s.name: s for s in sl.styles} 

67 for s in parent.styles: 

68 m[s.name] = s 

69 sl.styles = list(m.values()) 

70 

71 # CRS -> add 

72 sl.supportedCrs = list(parent.supportedCrs) 

73 for crs in crs_list: 

74 if crs not in sl.supportedCrs: 

75 sl.supportedCrs.append(crs) 

76 

77 # EX_GeographicBoundingBox -> replace 

78 sl.wgsExtent = wgs_extent or parent.wgsExtent 

79 

80 # Dimension -> replace 

81 # @TODO 

82 

83 # Attribution -> replace 

84 sl.metadata.attribution = sl.metadata.attribution or parent.metadata.attribution 

85 

86 # AuthorityURL -> add 

87 # @TODO 

88 

89 # MinScaleDenominator -> replace 

90 sl.scaleRange = sl.scaleRange or parent.scaleRange 

91 

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

93 if sl.defaultStyle: 

94 sl.legendUrl = sl.defaultStyle.legendUrl 

95 

96 sl.layers = [_layer(e, sl) for e in layer_el.findall('Layer')] 

97 sl.isGroup = len(sl.layers) > 0 

98 sl.isImage = len(sl.layers) == 0 

99 

100 if not sl.name: 

101 # some folks have unnamed layers in their caps 

102 # we can't render or query them 

103 sl.isQueryable = False 

104 sl.isImage = False 

105 

106 return sl