cobie24 - implement Coordinate tab #5926

This commit is contained in:
Andrej730
2025-01-13 13:22:26 +05:00
parent a968b61d02
commit 5c0a7af830
2 changed files with 106 additions and 9 deletions
+3 -3
View File
@@ -49,6 +49,7 @@ __version__ = version = "0.0.0"
ParserPreset = Literal["basic", "cobie24", "cobie24legacy"] ParserPreset = Literal["basic", "cobie24", "cobie24legacy"]
GetElementDataCallBack = Callable[[ifcopenshell.file, ifcopenshell.entity_instance], dict[str, Any]]
_parser_presets_configs = {} _parser_presets_configs = {}
@@ -69,9 +70,7 @@ def get_presets_configs() -> dict[ParserPreset, dict[str, Any]]:
class Parser: class Parser:
config: dict[str, Any] config: dict[str, Any]
categories: defaultdict[str, dict[str, Any]] categories: defaultdict[str, dict[str, Any]]
get_custom_element_data: dict[ get_custom_element_data: dict[str, Union[GetElementDataCallBack, dict[str, Any]]]
str, Union[Callable[[ifcopenshell.file, ifcopenshell.entity_instance], dict[str, Any]], dict[str, Any]]
]
duplicate_keys: list[tuple[dict[str, Any], dict[str, Any]]] duplicate_keys: list[tuple[dict[str, Any], dict[str, Any]]]
def __init__(self, preset: Union[str, ParserPreset, dict[str, Any]] = "basic"): def __init__(self, preset: Union[str, ParserPreset, dict[str, Any]] = "basic"):
@@ -93,6 +92,7 @@ class Parser:
def parse(self, ifc_file: ifcopenshell.file, name=None): def parse(self, ifc_file: ifcopenshell.file, name=None):
for category_name, category_config in self.config["categories"].items(): for category_name, category_config in self.config["categories"].items():
for element in category_config["get_category_elements"](ifc_file): for element in category_config["get_category_elements"](ifc_file):
get_element_data: Union[GetElementDataCallBack, dict[str, Any]]
get_element_data = category_config["get_element_data"] get_element_data = category_config["get_element_data"]
if isinstance(get_element_data, dict): if isinstance(get_element_data, dict):
+103 -6
View File
@@ -17,13 +17,16 @@
# along with IfcFM. If not, see <http://www.gnu.org/licenses/>. # along with IfcFM. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.classification import ifcopenshell.util.classification
import ifcopenshell.util.date import ifcopenshell.util.date
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.fm import ifcopenshell.util.fm
import ifcopenshell.util.placement import ifcopenshell.util.placement
import ifcopenshell.util.shape
import ifcopenshell.util.system import ifcopenshell.util.system
from typing import Any, Union, Optional from ifcopenshell.util.shape_builder import np_matrix_to_euler
from typing import Any, Union, Optional, Generator
# The original BIMServer plugin has a function called ifcToCOBie: # The original BIMServer plugin has a function called ifcToCOBie:
@@ -163,7 +166,7 @@ def get_documents(
def get_attributes(ifc_file: ifcopenshell.file) -> list[dict[str, Any]]: def get_attributes(ifc_file: ifcopenshell.file) -> list[dict[str, Any]]:
results = [] results: list[dict[str, Any]] = []
history = get_history(ifc_file) history = get_history(ifc_file)
created_by = get_email_from_history(history) if history else None created_by = get_email_from_history(history) if history else None
created_on = ifcopenshell.util.date.ifc2datetime(history.CreationDate).isoformat() if history else None 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_
} }
def get_attribute_data( def pass_category_elements_as_data(ifc_file: ifcopenshell.file, element: dict[str, Any]) -> dict[str, Any]:
ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
return element return element
def get_coordinate_elements(ifc_file: ifcopenshell.file) -> list[dict[str, Any]]:
elements = get_floors(ifc_file) + get_spaces(ifc_file)
results: list[dict[str, Any]] = []
for element in elements:
for data in get_coordinate_data_(element):
results.append(data)
return results
def get_coordinate_data_(element: ifcopenshell.entity_instance) -> Generator[dict[str, Any], None, None]:
M_TRANSLATION = (slice(0, 3), 3)
element_name = val(element.Name)
element_class = element.is_a()
base_data = {
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
"RowName": element_name,
"ExtSystem": "IfcFm",
"ExtObject": element_class,
"ExtIdentifier": element.GlobalId,
}
if element_class == "IfcBuildingStorey":
matrix = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
categories = ("points",)
rotation = np_matrix_to_euler(matrix)
translation = matrix[M_TRANSLATION]
# Rotation axes as explained in https://www.nibs.org/nbims/v3/cobie.
points_data = {
"Name": element_name,
"Category": "point",
"SheetName": "Floor",
"CoordinateXAxis": translation[0],
"CoordinateYAxis": translation[1],
"CoordinateZAxis": translation[2],
"ClockwiseRotation": rotation[2] * 180,
"ElevationalRotation": rotation[0] * 180,
"YawRotation": rotation[1] * 180,
}
yield base_data | points_data
return
# IfcSpaces.
if element.Representation is None:
return
settings = ifcopenshell.geom.settings()
shape: ifcopenshell.geom.ShapeElementType
shape = ifcopenshell.geom.create_shape(settings, element)
verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry)
categories = ("box-lowerleft", "box-upperright")
bbox = ifcopenshell.util.shape.get_bbox(verts)
base_data = base_data | {
"Category": "point",
"SheetName": "Space",
# Spaces don't need rotation as box points are already in world space.
"ClockwiseRotation": None,
"ElevationalRotation": None,
"YawRotation": None,
}
for category, point in zip(categories, bbox):
box_point_data = {
"Name": element_name + category,
"CoordinateXAxis": point[0],
"CoordinateYAxis": point[1],
"CoordinateZAxis": point[2],
}
yield base_data | box_point_data
def get_unit_type_name(ifc_file: ifcopenshell.file, unit_type: str) -> Union[str, None]: def get_unit_type_name(ifc_file: ifcopenshell.file, unit_type: str) -> Union[str, None]:
for unit in ifc_file.by_type("IfcUnitAssignment")[0].Units: for unit in ifc_file.by_type("IfcUnitAssignment")[0].Units:
if unit.is_a("IfcNamedUnit") and unit.UnitType == unit_type: if unit.is_a("IfcNamedUnit") and unit.UnitType == unit_type:
@@ -936,6 +1008,7 @@ def get_facility_parent(
def val(x: Any) -> Any: def val(x: Any) -> Any:
"""Replace n/a and empty strings with `None`."""
return x if x not in ("", "n/a") else None return x if x not in ("", "n/a") else None
@@ -1428,7 +1501,31 @@ config = {
"colours": "ririiirreeeoo", "colours": "ririiirreeeoo",
"sort": [{"name": "Category", "order": "ASC"}, {"name": "Name", "order": "ASC"}], "sort": [{"name": "Category", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
"get_category_elements": get_attributes, "get_category_elements": get_attributes,
"get_element_data": get_attribute_data, "get_element_data": pass_category_elements_as_data,
},
"Coordinate": {
"keys": ["Name", "SheetName", "RowName"],
"headers": (
"Name",
"CreatedBy",
"CreatedOn",
"Category",
"SheetName",
"RowName",
"CoordinateXAxis",
"CoordinateYAxis",
"CoordinateZAxis",
"ExtSystem",
"ExtObject",
"ExtIdentifier",
"ClockwiseRotation",
"ElevationalRotation",
"YawRotation",
),
"colours": "ririiirrreeerrr",
"sort": [{"name": "Category", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
"get_category_elements": get_coordinate_elements,
"get_element_data": pass_category_elements_as_data,
}, },
}, },
} }