Coverage for gws-app/gws/base/ows/server/filter.py: 0%

44 statements  

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

1"""Search filters""" 

2 

3import re 

4 

5import gws 

6import gws.gis.bounds 

7import gws.base.shape 

8import gws.lib.xmlx as xmlx 

9 

10 

11class Error(gws.Error): 

12 pass 

13 

14 

15""" 

16OGC fes 2.0 filter 

17 

18http://docs.opengeospatial.org/is/09-026r2/09-026r2.html 

19 

20Supports 

21 

22 - Minimum Standard Filter 

23 PropertyIsEqualTo, PropertyIsNotEqualTo, PropertyIsLessThan, PropertyIsGreaterThan, 

24 PropertyIsLessThanOrEqualTo, PropertyIsGreaterThanOrEqualTo. 

25 Implements the logical operators. Does not implement any additional functions. 

26 

27 - Minimum Spatial Filter 

28 Implements only the BBOX spatial operator. 

29""" 

30 

31_SUPPORTED_OPS = { 

32 'propertyisequalto': '=', 

33 'propertyisnotequalto': '!=', 

34 'propertyislessthan': '<', 

35 'propertyisgreaterthan': '>', 

36 'propertyislessthanorequalto': '=<', 

37 'propertyisgreaterthanorequalto': '>=', 

38 'bbox': 'bbox', 

39} 

40 

41 

42def from_fes_string(src: str) -> gws.SearchFilter: 

43 try: 

44 el = xmlx.from_string(src) 

45 except Exception as exc: 

46 raise Error('invalid XML') from exc 

47 return from_fes_element(el) 

48 

49 

50def from_fes_element(el: gws.XmlElement) -> gws.SearchFilter: 

51 op = el.tag.lower() 

52 

53 if op == 'filter': 

54 # root element, only allow a single child predicate 

55 if len(el) != 1: 

56 raise Error(f'invalid root predicate') 

57 return from_fes_element(el.children[0]) 

58 

59 if op in ('and', 'or'): 

60 return gws.SearchFilter(operator=op, sub=[from_fes_element(c) for c in el.children]) 

61 

62 if op not in _SUPPORTED_OPS: 

63 raise Error(f'unsupported filter operation {el.name!r}') 

64 

65 f = gws.SearchFilter( 

66 operator=_SUPPORTED_OPS[op], 

67 ) 

68 

69 # @TODO support "prop = prop" 

70 

71 v = el.first('ValueReference', 'PropertyName') 

72 if not v or not v.text: 

73 raise Error(f'invalid property name') 

74 

75 # we only support `propName` or `ns:propName` 

76 m = re.match(r'^(\w+:)?(\w+)$', v.text) 

77 if not m: 

78 raise Error(f'invalid property name {v.text!r}') 

79 f.name = m.group(2) 

80 

81 if op == 'bbox': 

82 v = el.first('Envelope') 

83 if v: 

84 bounds = gws.gis.bounds.from_gml_envelope_element() 

85 f.shape = gws.base.shape.from_bounds(bounds) 

86 return f 

87 

88 v = el.first('Literal') 

89 if v: 

90 f.value = v.text.strip() 

91 return f 

92 

93 raise Error(f'unsupported filter')