Fix #4638. You can now filter by parent (in the spatial hierarchy)

This commit is contained in:
Dion Moult
2024-06-01 15:10:45 +10:00
parent 0f9ea1e1e7
commit 6e30cd2ba7
5 changed files with 53 additions and 3 deletions
@@ -39,7 +39,7 @@ filter_elements_grammar = lark.Lark(
filter_group: facet_list ("+" facet_list)*
facet_list: facet ("," facet)*
facet: instance | entity | attribute | type | material | query | classification | location | property | group
facet: instance | entity | attribute | type | material | query | classification | location | property | group | parent
instance: not? globalid
globalid: /[0-3][a-zA-Z0-9_$]{21}/
@@ -51,6 +51,7 @@ filter_elements_grammar = lark.Lark(
classification: "classification" comparison value
location: "location" comparison value
group: "group" comparison value
parent: "parent" comparison value
query: "query:" keys comparison value
pset: quoted_string | regex_string | unquoted_string
@@ -681,6 +682,25 @@ class FacetTransformer(lark.Transformer):
self.elements = set(filter(filter_function, self.elements))
def parent(self, args):
comparison, value = args
def filter_function(element):
parents = []
result = False
if parent := ifcopenshell.util.element.get_parent(element):
parents.append(parent)
while parents:
parent = parents.pop()
if self.compare(parent.Name, comparison, value):
result = True
break
if grandparent := ifcopenshell.util.element.get_parent(parent):
parents.append(grandparent)
return result if comparison == "=" else not result
self.elements = set(filter(filter_function, self.elements))
def query(self, args):
keys, comparison, value = args
@@ -266,6 +266,25 @@ class TestFilterElements(test.bootstrap.IFC4):
assert subject.filter_elements(self.file, "IfcWall, group=Foo") == {element}
assert subject.filter_elements(self.file, "IfcWall, group!=Foo") == {element2}
def test_selecting_by_parent(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall", name="Element1")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall", name="Element2")
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall", name="Element3")
space = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSpace", name="Space")
storey = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuildingStorey", name="G")
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding", name="Building")
project = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject", name="Project")
ifcopenshell.api.run("spatial.assign_container", self.file, products=[element], relating_structure=space)
ifcopenshell.api.run("spatial.assign_container", self.file, products=[element2], relating_structure=storey)
ifcopenshell.api.run("aggregate.assign_object", self.file, products=[element3], relating_object=element2)
ifcopenshell.api.run("aggregate.assign_object", self.file, products=[space], relating_object=storey)
ifcopenshell.api.run("aggregate.assign_object", self.file, products=[storey], relating_object=building)
ifcopenshell.api.run("aggregate.assign_object", self.file, products=[building], relating_object=project)
assert subject.filter_elements(self.file, "IfcWall, parent=Project") == {element, element2, element3}
assert subject.filter_elements(self.file, "IfcWall, parent=Space") == {element}
assert subject.filter_elements(self.file, "IfcWall, parent=G") == {element, element2, element3}
assert subject.filter_elements(self.file, "IfcWall, parent=Element2") == {element3}
def test_selecting_multiple_filter_groups(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element.Name = "Foo"