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
« 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
5from bim2sim.elements.base_elements import ProductBased
8logger = logging.getLogger(__name__)
11class Filter:
12 """Base filter"""
14 def __init__(self):
15 pass
17 def matches(self, ifcelement):
18 """Check if element matches filter conditions"""
19 raise NotImplementedError("Must overwride method 'matches'")
21 def run(self):
22 """Apply the Filter on IFC File"""
23 raise NotImplementedError("Must overwride method 'run'")
25 def __repr__(self):
26 return "<%s>"%(self.__class__.__name__)
29class TypeFilter(Filter):
30 """Filter for subsets of IFC types"""
32 def __init__(self, ifc_types: Iterable):
33 super().__init__()
34 self.ifc_types = ifc_types
36 def matches(self, ifcelement):
37 __doc__ = super().matches.__doc__
38 return ifcelement.type in self.ifc_types #TODO: string based
40 def run(self, ifc) -> Tuple[Dict[Any, Type[ProductBased]], List[Any]]:
41 """Scan ifc by ifc_types.
43 :returns dict of ifc_types and suggested entities, list of unknown entities"""
45 unknown_ifc_entities = []
46 result = {}
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 = []
55 for entity in entities:
56 result[entity] = ifc_type
58 return result, unknown_ifc_entities
61class TextFilter(Filter):
62 """Filter for unknown properties by text fracments"""
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]")
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
93 def run(self, ifc_entities: list):
94 __doc__ = super().run.__doc__
96 filter_results = {}
97 unknown = []
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)
109 return filter_results, unknown
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 []
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)]
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)]
125 return source_ifc_elements, filter_results
128class GeometricFilter(Filter):
129 """Filter based on geometric position"""
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__()
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"
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
154 def matches(self, ifcelement):
155 __doc__ = super().matches.__doc__
156 raise NotImplementedError("ToDo") #TODO
159class ZoneFilter(GeometricFilter):
160 """Filter elements within given zone"""
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)