Coverage for bim2sim/elements/mapping/filter.py: 64%

73 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 17:09 +0000

1"""Module containing filters to identify IFC elements of interest""" 

2from typing import Iterable, Tuple, Dict, Any, Type, List 

3import logging 

4 

5from bim2sim.elements.base_elements import ProductBased 

6 

7 

8logger = logging.getLogger(__name__) 

9 

10 

11class Filter: 

12 """Base filter""" 

13 

14 def __init__(self): 

15 pass 

16 

17 def matches(self, ifcelement): 

18 """Check if element matches filter conditions""" 

19 raise NotImplementedError("Must overwride method 'matches'") 

20 

21 def run(self): 

22 """Apply the Filter on IFC File""" 

23 raise NotImplementedError("Must overwride method 'run'") 

24 

25 def __repr__(self): 

26 return "<%s>"%(self.__class__.__name__) 

27 

28 

29class TypeFilter(Filter): 

30 """Filter for subsets of IFC types""" 

31 

32 def __init__(self, ifc_types: Iterable): 

33 super().__init__() 

34 self.ifc_types = ifc_types 

35 

36 def matches(self, ifcelement): 

37 __doc__ = super().matches.__doc__ 

38 return ifcelement.type in self.ifc_types #TODO: string based 

39 

40 def run(self, ifc) -> Tuple[Dict[Any, Type[ProductBased]], List[Any]]: 

41 """Scan ifc by ifc_types. 

42 

43 :returns dict of ifc_types and suggested entities, list of unknown entities""" 

44 

45 unknown_ifc_entities = [] 

46 result = {} 

47 

48 for ifc_type in self.ifc_types: 

49 try: 

50 entities = ifc.by_type(ifc_type) 

51 except RuntimeError: 

52 logger.info("No entity of type '%s' found", ifc_type) 

53 entities = [] 

54 

55 for entity in entities: 

56 result[entity] = ifc_type 

57 

58 return result, unknown_ifc_entities 

59 

60 

61class TextFilter(Filter): 

62 """Filter for unknown properties by text fracments""" 

63 

64 def __init__(self, elements_classes: Iterable[ProductBased], 

65 ifc_units: dict, 

66 optional_locations: list = None, 

67 mode: int = 0): 

68 """" 

69 :param mode: 0 - include search in all ifc_types of previous filter 

70 1 - only search ifc_types of this filter 

71 :param optional_locations: additional locations to ifc_types to check patterns 

72 """ 

73 super().__init__() 

74 self.elements_classes = elements_classes 

75 self.ifc_units = ifc_units 

76 self.optional_locations = optional_locations 

77 self.mode = mode 

78 if self.mode not in [0, 1]: 

79 raise ValueError("TextFilter: 'Mode' not in [0, 1]") 

80 

81 def matches(self, ifcelement): 

82 __doc__ = super().matches.__doc__ 

83 raise NotImplementedError("Must overwride method 'matches'") 

84 if ifcelement: 

85 #Pseudocode: check if element contains Text_fracments[ifc_property] 

86 element = None 

87 return element 

88 else: 

89 #Pseudocode: filter self.components for text in Text_Fracments[ifc_property] 

90 elements = None 

91 return elements 

92 

93 def run(self, ifc_entities: list): 

94 __doc__ = super().run.__doc__ 

95 

96 filter_results = {} 

97 unknown = [] 

98 

99 # check matches for all entities on all classes 

100 for entity in ifc_entities: 

101 matches = [cls for cls in self.elements_classes 

102 if cls.filter_for_text_fragments( 

103 entity, self.ifc_units, self.optional_locations)] 

104 if matches: 

105 filter_results[entity] = matches 

106 else: 

107 unknown.append(entity) 

108 

109 return filter_results, unknown 

110 

111 for ifc_type in self.elements_classes: 

112 if ifc_type not in source_ifc_elements: 

113 source_ifc_elements[ifc_type] = ifc.by_type(ifc_type) or [] 

114 

115 if self.mode == 0: 

116 for ifc_type, ifc_elements in source_ifc_elements.items(): 

117 for ifc_element in ifc_elements: 

118 filter_results[ifc_element] = [cls for cls in Element._ifc_classes.values() if cls.filter_for_text_fragments(ifc_element, self.optional_locations)] 

119 

120 elif self.mode == 1: 

121 for ifc_type in self.elements_classes: 

122 for ifc_element in source_ifc_elements[ifc_type]: 

123 filter_results[ifc_element] = [cls for cls in Element._ifc_classes.values() if cls.filter_for_text_fragments(ifc_element, self.optional_locations)] 

124 

125 return source_ifc_elements, filter_results 

126 

127 

128class GeometricFilter(Filter): 

129 """Filter based on geometric position""" 

130 

131 def __init__(self, 

132 x_min: float = None, x_max: float = None, 

133 y_min: float = None, y_max: float = None, 

134 z_min: float = None, z_max: float = None): 

135 """None = unlimited""" 

136 super().__init__() 

137 

138 assert any([not lim is None for lim in [x_min, x_max, y_min, y_max, z_min, z_max]]), \ 

139 "Filter without limits has no effect." 

140 assert (x_min is None or x_max is None) or x_min < x_max, \ 

141 "Invalid arguments for x_min and x_max" 

142 assert (y_min is None or y_max is None) or y_min < y_max, \ 

143 "Invalid arguments for y_min and y_max" 

144 assert (z_min is None or z_max is None) or z_min < z_max, \ 

145 "Invalid arguments for z_min and z_max" 

146 

147 self.x_min = x_min 

148 self.x_max = x_max 

149 self.y_min = y_min 

150 self.y_max = y_max 

151 self.z_min = z_min 

152 self.z_max = z_max 

153 

154 def matches(self, ifcelement): 

155 __doc__ = super().matches.__doc__ 

156 raise NotImplementedError("ToDo") #TODO 

157 

158 

159class ZoneFilter(GeometricFilter): 

160 """Filter elements within given zone""" 

161 

162 def __init__(self, zone): 

163 raise NotImplementedError("ToDo") #TODO 

164 #super().__init__(x_min, x_max, y_min, y_max, z_min, z_max)