Skip to content

Commit 5c0a7af

Browse files
committed
cobie24 - implement Coordinate tab #5926
1 parent a968b61 commit 5c0a7af

2 files changed

Lines changed: 106 additions & 9 deletions

File tree

src/ifcfm/ifcfm/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050

5151
ParserPreset = Literal["basic", "cobie24", "cobie24legacy"]
52+
GetElementDataCallBack = Callable[[ifcopenshell.file, ifcopenshell.entity_instance], dict[str, Any]]
5253
_parser_presets_configs = {}
5354

5455

@@ -69,9 +70,7 @@ def get_presets_configs() -> dict[ParserPreset, dict[str, Any]]:
6970
class Parser:
7071
config: dict[str, Any]
7172
categories: defaultdict[str, dict[str, Any]]
72-
get_custom_element_data: dict[
73-
str, Union[Callable[[ifcopenshell.file, ifcopenshell.entity_instance], dict[str, Any]], dict[str, Any]]
74-
]
73+
get_custom_element_data: dict[str, Union[GetElementDataCallBack, dict[str, Any]]]
7574
duplicate_keys: list[tuple[dict[str, Any], dict[str, Any]]]
7675

7776
def __init__(self, preset: Union[str, ParserPreset, dict[str, Any]] = "basic"):
@@ -93,6 +92,7 @@ def __init__(self, preset: Union[str, ParserPreset, dict[str, Any]] = "basic"):
9392
def parse(self, ifc_file: ifcopenshell.file, name=None):
9493
for category_name, category_config in self.config["categories"].items():
9594
for element in category_config["get_category_elements"](ifc_file):
95+
get_element_data: Union[GetElementDataCallBack, dict[str, Any]]
9696
get_element_data = category_config["get_element_data"]
9797

9898
if isinstance(get_element_data, dict):

src/ifcfm/ifcfm/cobie24.py

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
# along with IfcFM. If not, see <http://www.gnu.org/licenses/>.
1818

1919
import ifcopenshell
20+
import ifcopenshell.geom
2021
import ifcopenshell.util.classification
2122
import ifcopenshell.util.date
2223
import ifcopenshell.util.element
2324
import ifcopenshell.util.fm
2425
import ifcopenshell.util.placement
26+
import ifcopenshell.util.shape
2527
import ifcopenshell.util.system
26-
from typing import Any, Union, Optional
28+
from ifcopenshell.util.shape_builder import np_matrix_to_euler
29+
from typing import Any, Union, Optional, Generator
2730

2831

2932
# The original BIMServer plugin has a function called ifcToCOBie:
@@ -163,7 +166,7 @@ def get_documents(
163166

164167

165168
def get_attributes(ifc_file: ifcopenshell.file) -> list[dict[str, Any]]:
166-
results = []
169+
results: list[dict[str, Any]] = []
167170
history = get_history(ifc_file)
168171
created_by = get_email_from_history(history) if history else None
169172
created_on = ifcopenshell.util.date.ifc2datetime(history.CreationDate).isoformat() if history else None
@@ -850,12 +853,81 @@ def get_document_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_
850853
}
851854

852855

853-
def get_attribute_data(
854-
ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance
855-
) -> ifcopenshell.entity_instance:
856+
def pass_category_elements_as_data(ifc_file: ifcopenshell.file, element: dict[str, Any]) -> dict[str, Any]:
856857
return element
857858

858859

860+
def get_coordinate_elements(ifc_file: ifcopenshell.file) -> list[dict[str, Any]]:
861+
elements = get_floors(ifc_file) + get_spaces(ifc_file)
862+
results: list[dict[str, Any]] = []
863+
for element in elements:
864+
for data in get_coordinate_data_(element):
865+
results.append(data)
866+
return results
867+
868+
869+
def get_coordinate_data_(element: ifcopenshell.entity_instance) -> Generator[dict[str, Any], None, None]:
870+
M_TRANSLATION = (slice(0, 3), 3)
871+
element_name = val(element.Name)
872+
element_class = element.is_a()
873+
874+
base_data = {
875+
"CreatedBy": get_created_by(element),
876+
"CreatedOn": get_created_on(element),
877+
"RowName": element_name,
878+
"ExtSystem": "IfcFm",
879+
"ExtObject": element_class,
880+
"ExtIdentifier": element.GlobalId,
881+
}
882+
883+
if element_class == "IfcBuildingStorey":
884+
matrix = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
885+
categories = ("points",)
886+
rotation = np_matrix_to_euler(matrix)
887+
translation = matrix[M_TRANSLATION]
888+
# Rotation axes as explained in https://www.nibs.org/nbims/v3/cobie.
889+
points_data = {
890+
"Name": element_name,
891+
"Category": "point",
892+
"SheetName": "Floor",
893+
"CoordinateXAxis": translation[0],
894+
"CoordinateYAxis": translation[1],
895+
"CoordinateZAxis": translation[2],
896+
"ClockwiseRotation": rotation[2] * 180,
897+
"ElevationalRotation": rotation[0] * 180,
898+
"YawRotation": rotation[1] * 180,
899+
}
900+
yield base_data | points_data
901+
return
902+
903+
# IfcSpaces.
904+
if element.Representation is None:
905+
return
906+
907+
settings = ifcopenshell.geom.settings()
908+
shape: ifcopenshell.geom.ShapeElementType
909+
shape = ifcopenshell.geom.create_shape(settings, element)
910+
verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry)
911+
categories = ("box-lowerleft", "box-upperright")
912+
bbox = ifcopenshell.util.shape.get_bbox(verts)
913+
base_data = base_data | {
914+
"Category": "point",
915+
"SheetName": "Space",
916+
# Spaces don't need rotation as box points are already in world space.
917+
"ClockwiseRotation": None,
918+
"ElevationalRotation": None,
919+
"YawRotation": None,
920+
}
921+
for category, point in zip(categories, bbox):
922+
box_point_data = {
923+
"Name": element_name + category,
924+
"CoordinateXAxis": point[0],
925+
"CoordinateYAxis": point[1],
926+
"CoordinateZAxis": point[2],
927+
}
928+
yield base_data | box_point_data
929+
930+
859931
def get_unit_type_name(ifc_file: ifcopenshell.file, unit_type: str) -> Union[str, None]:
860932
for unit in ifc_file.by_type("IfcUnitAssignment")[0].Units:
861933
if unit.is_a("IfcNamedUnit") and unit.UnitType == unit_type:
@@ -936,6 +1008,7 @@ def get_facility_parent(
9361008

9371009

9381010
def val(x: Any) -> Any:
1011+
"""Replace n/a and empty strings with `None`."""
9391012
return x if x not in ("", "n/a") else None
9401013

9411014

@@ -1428,7 +1501,31 @@ def get_sheet_name(element: ifcopenshell.entity_instance) -> Union[str, None]:
14281501
"colours": "ririiirreeeoo",
14291502
"sort": [{"name": "Category", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
14301503
"get_category_elements": get_attributes,
1431-
"get_element_data": get_attribute_data,
1504+
"get_element_data": pass_category_elements_as_data,
1505+
},
1506+
"Coordinate": {
1507+
"keys": ["Name", "SheetName", "RowName"],
1508+
"headers": (
1509+
"Name",
1510+
"CreatedBy",
1511+
"CreatedOn",
1512+
"Category",
1513+
"SheetName",
1514+
"RowName",
1515+
"CoordinateXAxis",
1516+
"CoordinateYAxis",
1517+
"CoordinateZAxis",
1518+
"ExtSystem",
1519+
"ExtObject",
1520+
"ExtIdentifier",
1521+
"ClockwiseRotation",
1522+
"ElevationalRotation",
1523+
"YawRotation",
1524+
),
1525+
"colours": "ririiirrreeerrr",
1526+
"sort": [{"name": "Category", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
1527+
"get_category_elements": get_coordinate_elements,
1528+
"get_element_data": pass_category_elements_as_data,
14321529
},
14331530
},
14341531
}

0 commit comments

Comments
 (0)