From 5454c5b1e7fdc17db91942485b7d89a4bfd7c56d Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 19 Mar 2020 18:16:30 +1100 Subject: [PATCH] Allow manually specified qto values to be exported --- src/ifcblenderexport/blenderbim/__init__.py | 1 + src/ifcblenderexport/blenderbim/export_ifc.py | 98 +++++++++++-------- src/ifcblenderexport/blenderbim/operator.py | 16 +++ src/ifcblenderexport/blenderbim/prop.py | 1 + src/ifcblenderexport/blenderbim/ui.py | 4 + 5 files changed, 79 insertions(+), 41 deletions(-) diff --git a/src/ifcblenderexport/blenderbim/__init__.py b/src/ifcblenderexport/blenderbim/__init__.py index dd181cc8fd..2071d085e2 100644 --- a/src/ifcblenderexport/blenderbim/__init__.py +++ b/src/ifcblenderexport/blenderbim/__init__.py @@ -66,6 +66,7 @@ if bpy is not None: operator.RemovePset, operator.AddOverridePset, operator.RemoveOverridePset, + operator.AddQto, operator.RemoveQto, operator.AddMaterialPset, operator.RemoveMaterialPset, diff --git a/src/ifcblenderexport/blenderbim/export_ifc.py b/src/ifcblenderexport/blenderbim/export_ifc.py index e8af8fd736..46e25069da 100644 --- a/src/ifcblenderexport/blenderbim/export_ifc.py +++ b/src/ifcblenderexport/blenderbim/export_ifc.py @@ -141,7 +141,6 @@ class IfcParser(): self.load_representations() self.materials = self.get_materials() self.styled_items = self.get_styled_items() - self.qtos = self.get_qtos() self.spatial_structure_elements = self.get_spatial_structure_elements() self.structural_analysis_models = self.get_structural_analysis_models() @@ -448,17 +447,8 @@ class IfcParser(): self.rel_defines_by_pset.setdefault( '{}/{}'.format(pset.name, pset.file), []).append(product) - for pset in obj.BIMObjectProperties.override_psets: - pset_key = '{}/{}'.format(pset.name, obj.name) - raw = {p.name: p.string_value for p in pset.properties if p.string_value} - if not raw: - continue - self.psets[pset_key] = { - 'ifc': None, - 'raw': raw, - 'attributes': { 'Name': pset.name } - } - self.rel_defines_by_pset.setdefault(pset_key, []).append(product) + self.get_product_psets_qtos(product, obj, is_pset=True) + self.get_product_psets_qtos(product, obj, is_qto=True) for document in obj.BIMObjectProperties.documents: self.rel_associates_document_object.setdefault( @@ -490,6 +480,27 @@ class IfcParser(): return product + def get_product_psets_qtos(self, product, obj, is_pset=False, is_qto=False): + if is_pset: + psets_qtos = obj.BIMObjectProperties.override_psets + results = self.psets + relationships = self.rel_defines_by_pset + if is_qto: + psets_qtos = obj.BIMObjectProperties.qtos + results = self.qtos + relationships = self.rel_defines_by_qto + for item in psets_qtos: + item_key = '{}/{}'.format(item.name, obj.name) + raw = {p.name: p.string_value for p in item.properties if p.string_value} + if not raw: + continue + results[item_key] = { + 'ifc': None, + 'raw': raw, + 'attributes': { 'Name': item.name } + } + relationships.setdefault(item_key, []).append(product) + def get_product_relating_structure(self, product, obj): relating_structure = obj.BIMObjectProperties.relating_structure if relating_structure: @@ -918,28 +929,6 @@ class IfcParser(): }) return results - def get_qtos(self): - if not self.ifc_export_settings.has_quantities: - return {} - results = {} - for product in self.selected_products + self.type_products: - obj = product['raw'] - if not obj.data: - continue - for property in obj.keys(): - if property[0:4] != 'Qto_': - continue - results[obj.name] = { - 'ifc': None, - 'raw': obj, - 'class': property, - 'attributes': { - 'Name': property, - 'MethodOfMeasurement': obj[property] - } - } - return results - def get_type_products(self): results = [] index = 0 @@ -1316,10 +1305,6 @@ class IfcExporter(): for pset in self.ifc_parser.psets.values(): properties = self.create_pset_properties(pset) if not properties: - self.ifc_export_settings.logger.error( - 'No properties could be detected for the pset {}/{}'.format( - pset['attributes']['Name'], - pset['attributes']['Description'])) continue pset['attributes'].update({ 'GlobalId': ifcopenshell.guid.new(), @@ -1338,6 +1323,10 @@ class IfcExporter(): 'Material': material['ifc'] }) + def create_qto_properties(self, qto): + if qto['attributes']['Name'] in schema.ifc.qtos: + return self.create_templated_qto_properties(qto) + def create_pset_properties(self, pset): if pset['attributes']['Name'] in schema.ifc.psets: return self.create_templated_pset_properties(pset) @@ -1382,6 +1371,30 @@ class IfcExporter(): invalid_pset_keys)) return properties + def create_templated_qto_properties(self, qto): + properties = [] + templates = schema.ifc.qtos[qto['attributes']['Name']]['HasPropertyTemplates'] + for name, data in templates.items(): + if name not in qto['raw']: + continue + if data.TemplateType[0:2] == 'Q_': + value_basename = data.TemplateType[2:].title() + value_name = f'{value_basename}Value' + class_name = f'IfcQuantity{value_basename}' + properties.append( + self.file.create_entity(class_name, **{ + 'Name': name, + value_name: float(qto['raw'][name]) + })) + invalid_qto_keys = [k for k in qto['raw'].keys() if k not in templates.keys()] + if invalid_qto_keys: + self.ifc_export_settings.logger.error( + 'One or more properties were invalid in the qto {}/{}: {}'.format( + qto['attributes']['Name'], + qto['attributes']['Description'], + invalid_qto_keys)) + return properties + def cast_to_base_type(self, var_type, value): if var_type not in schema.ifc.type_map: return value @@ -1676,12 +1689,15 @@ class IfcExporter(): self.create_product(product) def create_qtos(self): - for object_name, qto in self.ifc_parser.qtos.items(): - quantities = self.calculate_quantities(qto['class'], qto['raw']) + # TODO: re-introduce calculated quantities + for qto in self.ifc_parser.qtos.values(): + properties = self.create_qto_properties(qto) + if not properties: + continue qto['attributes'].update({ 'GlobalId': ifcopenshell.guid.new(), 'OwnerHistory': self.owner_history, - 'Quantities': quantities + 'Quantities': properties }) qto['ifc'] = self.file.create_entity('IfcElementQuantity', **qto['attributes']) diff --git a/src/ifcblenderexport/blenderbim/operator.py b/src/ifcblenderexport/blenderbim/operator.py index 981394ef01..52ebf6366b 100644 --- a/src/ifcblenderexport/blenderbim/operator.py +++ b/src/ifcblenderexport/blenderbim/operator.py @@ -480,6 +480,22 @@ class RemovePset(bpy.types.Operator): return {'FINISHED'} +class AddQto(bpy.types.Operator): + bl_idname = 'bim.add_qto' + bl_label = 'Add Qto' + + def execute(self, context): + name = bpy.context.active_object.BIMObjectProperties.qto_name + if name not in schema.ifc.qtos: + return {'FINISHED'} + qto = bpy.context.active_object.BIMObjectProperties.qtos.add() + qto.name = name + for prop_name in schema.ifc.qtos[name]['HasPropertyTemplates'].keys(): + prop = qto.properties.add() + prop.name = prop_name + return {'FINISHED'} + + class AddOverridePset(bpy.types.Operator): bl_idname = 'bim.add_override_pset' bl_label = 'Add Override Pset' diff --git a/src/ifcblenderexport/blenderbim/prop.py b/src/ifcblenderexport/blenderbim/prop.py index 4b5c96c8a9..0932ed55cb 100644 --- a/src/ifcblenderexport/blenderbim/prop.py +++ b/src/ifcblenderexport/blenderbim/prop.py @@ -490,6 +490,7 @@ class BIMObjectProperties(PropertyGroup): material_type: EnumProperty(items=getMaterialTypes, name="Material Type") override_psets: CollectionProperty(name="Override Psets", type=PsetQto) override_pset_name: StringProperty(name="Override Pset Name") + qto_name: StringProperty(name="Qto Name") has_boundary_condition: BoolProperty(name='Has Boundary Condition') boundary_condition: PointerProperty(name='Boundary Condition', type=BoundaryCondition) structural_member_connection: PointerProperty(name='Structural Member Connection', type=bpy.types.Object) diff --git a/src/ifcblenderexport/blenderbim/ui.py b/src/ifcblenderexport/blenderbim/ui.py index 4ce1c41d71..fd8fdcba8c 100644 --- a/src/ifcblenderexport/blenderbim/ui.py +++ b/src/ifcblenderexport/blenderbim/ui.py @@ -74,6 +74,10 @@ class BIM_PT_object(Panel): layout.label(text="Quantities:") + row = layout.row(align=True) + row.prop(props, 'qto_name', text='') + row.operator('bim.add_qto') + for index, qto in enumerate(props.qtos): row = layout.row(align=True) row.prop(qto, 'name', text='')