From 5f8efecdccb8c644a595c18d2fad99141dc3af24 Mon Sep 17 00:00:00 2001 From: bimgeek Date: Tue, 19 Aug 2025 21:54:40 +0300 Subject: [PATCH] move to existing traversal --- .../converter/data_object_converter.py | 9 ++++- .../converter/spatial_element_converter.py | 18 +++++++-- src/speckleifc/importer.py | 22 +++++++++-- src/speckleifc/property_extraction.py | 39 ------------------- 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/speckleifc/converter/data_object_converter.py b/src/speckleifc/converter/data_object_converter.py index ba484e7..9df9793 100644 --- a/src/speckleifc/converter/data_object_converter.py +++ b/src/speckleifc/converter/data_object_converter.py @@ -11,13 +11,20 @@ def data_object_to_speckle( display_value: list[Base], step_element: entity_instance, children: list[Base], + current_storey: str | None = None, ) -> DataObject: guid = cast(str, step_element.GlobalId) name = cast(str, step_element.Name or guid) + properties = extract_properties(step_element) + + # Add building storey information if available and not a building storey itself + if current_storey and not step_element.is_a("IfcBuildingStorey"): + properties["Building Storey"] = current_storey + data_object = DataObject( applicationId=guid, - properties=extract_properties(step_element), + properties=properties, name=name or guid, displayValue=display_value, ) diff --git a/src/speckleifc/converter/spatial_element_converter.py b/src/speckleifc/converter/spatial_element_converter.py index 8ef6750..2b6b18a 100644 --- a/src/speckleifc/converter/spatial_element_converter.py +++ b/src/speckleifc/converter/spatial_element_converter.py @@ -12,8 +12,11 @@ def spatial_element_to_speckle( display_value: list[Base], step_element: entity_instance, relational_children: list[Base], + current_storey: str | None = None, ) -> Collection: - direct_geometry = _convert_as_data_object(display_value, step_element) + direct_geometry = _convert_as_data_object( + display_value, step_element, current_storey + ) all_children = [direct_geometry] + relational_children guid = cast(str, step_element.GlobalId) @@ -26,13 +29,22 @@ def spatial_element_to_speckle( def _convert_as_data_object( - display_value: list[Base], step_element: entity_instance + display_value: list[Base], + step_element: entity_instance, + current_storey: str | None = None, ) -> DataObject: guid = cast(str, step_element.GlobalId) name = cast(str, step_element.Name or step_element.LongName or guid) + + properties = extract_properties(step_element) + + # Add building storey information if available and not a building storey itself + if current_storey and not step_element.is_a("IfcBuildingStorey"): + properties["Building Storey"] = current_storey + data_object = DataObject( applicationId=guid, - properties=extract_properties(step_element), + properties=properties, name=name, displayValue=display_value, ) diff --git a/src/speckleifc/importer.py b/src/speckleifc/importer.py index d68e76c..ec607a4 100644 --- a/src/speckleifc/importer.py +++ b/src/speckleifc/importer.py @@ -26,8 +26,16 @@ class ImportJob: ) geometries_count: int = 0 geometries_used: int = 0 + _current_storey: str | None = field(default=None, init=False) def convert_element(self, step_element: entity_instance) -> Base: + # Track current storey context + previous_storey = self._current_storey + if step_element.is_a("IfcBuildingStorey"): + self._current_storey = ( + step_element.Name or step_element.LongName or step_element.GlobalId + ) + children = self._convert_children(step_element) display_value = self.cached_display_values.get(step_element.id(), []) @@ -35,11 +43,19 @@ class ImportJob: self.geometries_used += 1 if step_element.is_a("IfcProject"): - return project_to_speckle(step_element, children) + result = project_to_speckle(step_element, children) elif step_element.is_a("IfcSpatialStructureElement"): - return spatial_element_to_speckle(display_value, step_element, children) + result = spatial_element_to_speckle( + display_value, step_element, children, self._current_storey + ) else: - return data_object_to_speckle(display_value, step_element, children) + result = data_object_to_speckle( + display_value, step_element, children, self._current_storey + ) + + # Restore previous storey context + self._current_storey = previous_storey + return result def _convert_children(self, step_element: entity_instance) -> list[Base]: return [ diff --git a/src/speckleifc/property_extraction.py b/src/speckleifc/property_extraction.py index 4f57e1d..40e87e2 100644 --- a/src/speckleifc/property_extraction.py +++ b/src/speckleifc/property_extraction.py @@ -10,10 +10,6 @@ def extract_properties(element: entity_instance) -> dict[str, object]: "Property Sets": _get_ifc_object_properties(element), } - # Add building storey information if element is contained in a storey - if storey_name := _get_containing_storey(element): - properties["Building Storey"] = storey_name - if (ifc_type := get_type(element)) is not None: properties["Element Type Property Sets"] = _get_ifc_element_type_properties( ifc_type, @@ -94,38 +90,3 @@ def _get_properties(properties: entity_instance) -> dict[str, Any]: # elif prop.is_a("IfcPropertyTableValue"): # properties[name] = #not sure if we want to support these... return result - - -def _get_containing_storey(element: entity_instance) -> str | None: - """ - Find the containing IfcBuildingStorey for an element by traversing up - the spatial hierarchy. Returns the storey name if found, None otherwise. - """ - # Check if element has spatial containment relationships - containment_rels = getattr(element, "ContainedInStructure", None) - if not containment_rels: - return None - - # Traverse the spatial containment relationships - for rel in containment_rels: - if not rel.is_a("IfcRelContainedInSpatialStructure"): - continue - - relating_structure = getattr(rel, "RelatingStructure", None) - if not relating_structure: - continue - - # Check if this structure is a building storey - if relating_structure.is_a("IfcBuildingStorey"): - return ( - relating_structure.Name - or relating_structure.LongName - or relating_structure.GlobalId - ) - - # If not, recursively check the parent structure - parent_storey = _get_containing_storey(relating_structure) - if parent_storey: - return parent_storey - - return None