Ifc4x3 - entity-types mapping

- added mapping entity-types mapping based on iso release of ifc4x3

- added some manual corrections for `generate_util_type_json.py` if someone would regenerate the map for ifc4.

- manual correction in ifc4x3 mapping for IfcCivilElement. It could be removed in the future releases of IFC if https://github.com/buildingSMART/IFC4.3.x-development/issues/583 is resolved

If curious to look what were the changes for mapping between ifc4 and ifc4x3:
https://github.com/IfcOpenShell/IfcOpenShell/commit/da51253fb0a9d08aa93f5fe3c074d3592b3aa997#diff-98f525c91bb47b9820e603fba61cc015d3e6ae7261593d727c51114e3ff73aa7
This commit is contained in:
Andrej730
2023-02-17 15:29:10 +05:00
parent 743bd2c48f
commit d52618277e
3 changed files with 506 additions and 90 deletions
+109 -80
View File
@@ -1,5 +1,5 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2020, 2021, 2023 Dion Moult <dion@thinkmoult.com>, @Andrej730
#
# This file is part of BlenderBIM Add-on.
#
@@ -22,101 +22,130 @@ import ifcopenshell
import ifcopenshell.util.schema
filepath = "IFC4.exp"
schema2x3 = ifcopenshell.ifcopenshell_wrapper.schema_by_name("IFC2X3")
schema4 = ifcopenshell.ifcopenshell_wrapper.schema_by_name("IFC4")
def generate_ifc4_entity_map(filepath, schema_name, manual_corrections={}):
filepath = filepath
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name)
entity_to_type_map = {}
entity_to_type_map = {}
entity = None
is_in_where_rule = False
entity = None
is_in_where_rule = False
with open(filepath) as fp:
line = fp.readline()
while line:
if "ENTITY " in line:
entity = line[len("ENTITY ") : -1]
elif "END_ENTITY" in line:
is_in_where_rule = False
elif entity and ("CorrectTypeAssigned" in line or "CorrectStyleAssigned" in line):
is_in_where_rule = True
elif entity and is_in_where_rule and "IN TYPEOF" in line and "RelatingType" in line:
type_class = line.split("'")[1].split(".")[1]
if type_class == "IFCTRANFORMERTYPE":
# Fix typo in schema
type_class = "IFCTRANSFORMERTYPE"
type_class = schema4.declaration_by_name(type_class).name()
entity_to_type_map.setdefault(entity, []).append(type_class)
with open(filepath) as fp:
line = fp.readline()
while line:
if "ENTITY " in line:
entity = line[len("ENTITY ") : -1]
elif "END_ENTITY" in line:
is_in_where_rule = False
elif entity and ("CorrectTypeAssigned" in line or "CorrectStyleAssigned" in line):
is_in_where_rule = True
elif entity and is_in_where_rule and "IN TYPEOF" in line and "RelatingType" in line:
type_class = line.split("'")[1].split(".")[1]
if type_class == "IFCTRANFORMERTYPE":
# Fix typo in schema
type_class = "IFCTRANSFORMERTYPE"
type_class = schema.declaration_by_name(type_class).name()
entity_to_type_map.setdefault(entity, []).append(type_class)
line = fp.readline()
# This type of hacky express parsing doesn't accommodate subtypes, so...
for entity in manual_corrections:
for ifc_type in manual_corrections[entity]:
entity_to_type_map.setdefault(entity, []).append(ifc_type)
# This type of hacky express parsing doesn't accommodate subtypes, so...
def get_inherited_map(declaration):
if not ifcopenshell.util.schema.is_a(declaration, "IfcObject"):
return
if declaration.supertype().name() in entity_to_type_map:
return entity_to_type_map[declaration.supertype().name()]
return get_inherited_map(declaration.supertype())
def get_inherited_map(declaration):
if not ifcopenshell.util.schema.is_a(declaration, "IfcObject"):
return
if declaration.supertype().name() in entity_to_type_map:
return entity_to_type_map[declaration.supertype().name()]
return get_inherited_map(declaration.supertype())
for declaration in schema.declarations():
if not hasattr(declaration, "supertype"):
continue
if not ifcopenshell.util.schema.is_a(declaration, "IfcObject"):
continue
if declaration.is_abstract():
continue
if declaration.name() in entity_to_type_map:
continue
inherited_map = get_inherited_map(declaration)
if inherited_map:
entity_to_type_map[declaration.name()] = inherited_map
type_to_entity_map = {value: [key] for key in entity_to_type_map for value in entity_to_type_map[key]}
for declaration in schema4.declarations():
if not hasattr(declaration, "supertype"):
continue
if not ifcopenshell.util.schema.is_a(declaration, "IfcObject"):
continue
if declaration.is_abstract():
continue
if declaration.name() in entity_to_type_map:
continue
inherited_map = get_inherited_map(declaration)
if inherited_map:
entity_to_type_map[declaration.name()] = inherited_map
schema_version = schema_name.lower().strip("ifc")
with open(f"src/ifcopenshell-python/ifcopenshell/util/entity_to_type_map_{schema_version}.json", "w") as f:
json.dump(entity_to_type_map, f, indent=4)
return entity_to_type_map
type_to_entity_map = {value: [key] for key in entity_to_type_map for value in entity_to_type_map[key]}
# IFC2X3 doesn't seem to define this in EXPRESS, so let's just guess
# TODO: do we need some other way to validate 2x3 mapping?
def generate_ifc2x3_entity_map():
entity_to_type_map = {}
schema2x3 = ifcopenshell.ifcopenshell_wrapper.schema_by_name("IFC2X3")
entity_to_type_map2x3 = {}
def guess_type_declaration(declaration):
if not ifcopenshell.util.schema.is_a(declaration, "IfcObject"):
return
try:
type_declaration = schema2x3.declaration_by_name(declaration.name() + "Type")
return type_declaration
except:
pass
try:
type_declaration = schema2x3.declaration_by_name(declaration.name() + "Style")
return type_declaration
except:
pass
return guess_type_declaration(declaration.supertype())
for declaration in schema2x3.declarations():
if not hasattr(declaration, "supertype"):
continue
type_declaration = guess_type_declaration(declaration)
if not type_declaration:
continue
if not type_declaration.is_abstract():
entity_to_type_map.setdefault(declaration.name(), []).append(type_declaration.name())
for subtype in type_declaration.subtypes():
if not subtype.is_abstract():
entity_to_type_map.setdefault(declaration.name(), []).append(subtype.name())
type_to_entity_map2x3 = {value: [key] for key in entity_to_type_map for value in entity_to_type_map[key]}
with open("src/ifcopenshell-python/ifcopenshell/util/entity_to_type_map_2x3.json", "w") as f:
json.dump(entity_to_type_map, f, indent=4)
return entity_to_type_map
def guess_type_declaration(declaration):
if not ifcopenshell.util.schema.is_a(declaration, "IfcObject"):
return
# specs: https://technical.buildingsmart.org/standards/ifc/ifc-schema-specifications
# ifc4x3 - https://github.com/buildingSMART/IFC4.3-html/releases/tag/sep-13-release (inside .zip)
# ifc4 - https://standards.buildingsmart.org/IFC/RELEASE/IFC4/FINAL/EXPRESS/IFC4.exp
try:
type_declaration = schema2x3.declaration_by_name(declaration.name() + "Type")
return type_declaration
except:
pass
# IfcCivilElement correction could be removed in the future releases of IFC
# if https://github.com/buildingSMART/IFC4.3.x-development/issues/583 is solved
try:
type_declaration = schema2x3.declaration_by_name(declaration.name() + "Style")
return type_declaration
except:
pass
ifc4x3_corrections = {
"IfcCivilElement": ["IfcCivilElementType"],
}
entity_to_type_map4x3 = generate_ifc4_entity_map("IFC4X3_TC1.exp", "ifc4x3", ifc4x3_corrections)
return guess_type_declaration(declaration.supertype())
# some manual IFC4 corrections (more details in entity_to_type_map_4.json commits history)
ifc4_corrections = {
"IfcDoor": ["IfcDoorStyle"], # also will apply change to IfcDoorStandardCase
"IfcWindow": ["IfcWindowStyle"], # also will aply change to IfcWindowStandardCase
"IfcCivilElement": ["IfcCivilElementType"],
}
#
entity_to_type_map4 = generate_ifc4_entity_map("IFC4.exp", "ifc4", ifc4_corrections)
for declaration in schema2x3.declarations():
if not hasattr(declaration, "supertype"):
continue
type_declaration = guess_type_declaration(declaration)
if not type_declaration:
continue
if not type_declaration.is_abstract():
entity_to_type_map2x3.setdefault(declaration.name(), []).append(type_declaration.name())
for subtype in type_declaration.subtypes():
if not subtype.is_abstract():
entity_to_type_map2x3.setdefault(declaration.name(), []).append(subtype.name())
type_to_entity_map2x3 = {value: [key] for key in entity_to_type_map2x3 for value in entity_to_type_map2x3[key]}
with open("entity_to_type_map_4.json", "w") as f:
json.dump(entity_to_type_map, f, indent=4)
with open("entity_to_type_map_2x3.json", "w") as f:
json.dump(entity_to_type_map2x3, f, indent=4)
entity_to_type_map2x3 = generate_ifc2x3_entity_map()