mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 09:08:40 +00:00
Bug #663 - Implement support for COBie types spreadsheet
This commit is contained in:
+136
-13
@@ -12,12 +12,47 @@ class IfcCobieCsv():
|
|||||||
self.floors = {}
|
self.floors = {}
|
||||||
self.spaces = {}
|
self.spaces = {}
|
||||||
self.zones = {}
|
self.zones = {}
|
||||||
|
self.types = {}
|
||||||
|
self.type_assets = [
|
||||||
|
'IfcDoorStyle',
|
||||||
|
'IfcBuildingElementProxyType',
|
||||||
|
'IfcChimneyType',
|
||||||
|
'IfcCoveringType',
|
||||||
|
'IfcDoorType',
|
||||||
|
'IfcFootingType',
|
||||||
|
'IfcPileType',
|
||||||
|
'IfcRoofType',
|
||||||
|
'IfcShadingDeviceType',
|
||||||
|
'IfcWindowType',
|
||||||
|
'IfcDistributionControlElementType',
|
||||||
|
'IfcDistributionChamberElementType',
|
||||||
|
'IfcEnergyConversionDeviceType',
|
||||||
|
'IfcFlowControllerType',
|
||||||
|
'IfcFlowMovingDeviceType',
|
||||||
|
'IfcFlowStorageDeviceType',
|
||||||
|
'IfcFlowTerminalType',
|
||||||
|
'IfcFlowTreatmentDeviceType',
|
||||||
|
'IfcElementAssemblyType',
|
||||||
|
'IfcBuildingElementPartType',
|
||||||
|
'IfcDiscreteAccessoryType',
|
||||||
|
'IfcMechanicalFastenerType',
|
||||||
|
'IfcReinforcingElementType',
|
||||||
|
'IfcVibrationIsolatorType',
|
||||||
|
'IfcFurnishingElementType',
|
||||||
|
'IfcGeographicElementType',
|
||||||
|
'IfcTransportElementType',
|
||||||
|
'IfcSpatialZoneType',
|
||||||
|
'IfcWindowStyle',
|
||||||
|
]
|
||||||
self.picklists = {
|
self.picklists = {
|
||||||
'Category-Role': [],
|
'Category-Role': [],
|
||||||
'Category-Facility': [],
|
'Category-Facility': [],
|
||||||
'FloorType': [],
|
'FloorType': [],
|
||||||
'Category-Space': [],
|
'Category-Space': [],
|
||||||
'ZoneType': [],
|
'ZoneType': [],
|
||||||
|
'Category-Product': [],
|
||||||
|
'AssetType': [],
|
||||||
|
'DurationUnit': ['day'], # See note about hardcoded day below
|
||||||
'objType': []
|
'objType': []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +63,7 @@ class IfcCobieCsv():
|
|||||||
self.get_floors()
|
self.get_floors()
|
||||||
self.get_spaces()
|
self.get_spaces()
|
||||||
self.get_zones()
|
self.get_zones()
|
||||||
|
self.get_types()
|
||||||
|
|
||||||
def get_contacts(self):
|
def get_contacts(self):
|
||||||
histories = self.file.by_type('IfcOwnerHistory')
|
histories = self.file.by_type('IfcOwnerHistory')
|
||||||
@@ -115,7 +151,7 @@ class IfcCobieCsv():
|
|||||||
'ExtSystem': self.get_ext_system_from_history(space.OwnerHistory),
|
'ExtSystem': self.get_ext_system_from_history(space.OwnerHistory),
|
||||||
'ExtObject': self.get_ext_object(space),
|
'ExtObject': self.get_ext_object(space),
|
||||||
'ExtIdentifier': space.GlobalId,
|
'ExtIdentifier': space.GlobalId,
|
||||||
'RoomTag': self.get_room_tag_from_space(space),
|
'RoomTag': self.get_pset_value_from_object(space, 'COBie_Space', 'RoomTag', 'n/a'),
|
||||||
'UsableHeight': self.get_usable_height_from_space(space),
|
'UsableHeight': self.get_usable_height_from_space(space),
|
||||||
'GrossArea': self.get_gross_area_from_space(space),
|
'GrossArea': self.get_gross_area_from_space(space),
|
||||||
'NetArea': self.get_net_area_from_space(space),
|
'NetArea': self.get_net_area_from_space(space),
|
||||||
@@ -136,6 +172,92 @@ class IfcCobieCsv():
|
|||||||
'Description': self.get_object_attribute(zone, 'Description'),
|
'Description': self.get_object_attribute(zone, 'Description'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_types(self):
|
||||||
|
types = self.file.by_type('IfcTypeObject')
|
||||||
|
for type in types:
|
||||||
|
is_a_type_asset = False
|
||||||
|
for type_asset in self.type_assets:
|
||||||
|
if type.is_a(type_asset):
|
||||||
|
is_a_type_asset = True
|
||||||
|
if not is_a_type_asset:
|
||||||
|
logging.warning('A type which is not an asset was found for {}'.format(type))
|
||||||
|
continue
|
||||||
|
# The responsibility matrix states to parse IfcMaterial and
|
||||||
|
# IfcMaterialLayerSet too, but it doesn't make much sense, so I
|
||||||
|
# don't parse it.
|
||||||
|
type_name = self.get_object_name(type)
|
||||||
|
self.types[type_name] = {
|
||||||
|
'CreatedBy': self.get_email_from_history(type.OwnerHistory),
|
||||||
|
'CreatedOn': self.get_created_on_from_history(type.OwnerHistory),
|
||||||
|
'Category': self.get_category_from_object(type, 'Category-Product'),
|
||||||
|
# The responsibility matrix states two possible fallbacks. I
|
||||||
|
# choose the 'n/a' option as opposed to repeating the name.
|
||||||
|
'Description': self.get_object_attribute(type, 'Description'),
|
||||||
|
'AssetType': self.get_pset_value_from_object(type, 'COBie_Asset', 'AssetType', 'n/a', 'AssetType'),
|
||||||
|
'Manufacturer': self.get_contact_pset_value_from_object(type, 'Pset_ManufacturerTypeInformation', 'Manufacturer'),
|
||||||
|
'ModelNumber': self.get_pset_value_from_object(type, 'Pset_ManufacturerTypeInformation', 'ModelLabel', 'n/a'),
|
||||||
|
# The responsibility matrix talks about using the Pset_Warranty
|
||||||
|
# values, but Pset_Warranty only applies to objects, not types,
|
||||||
|
# and so they are ignored.
|
||||||
|
'WarrantyGuarantorParts': self.get_contact_pset_value_from_object(type, 'COBie_Warranty', 'WarrantyGuarantorParts'),
|
||||||
|
'WarrantyDurationParts': self.get_pset_value_from_object(type, 'COBie_Warranty', 'WarrantyDurationParts', 0),
|
||||||
|
'WarrantyGuarantorLabor': self.get_contact_pset_value_from_object(type, 'COBie_Warranty', 'WarrantyGuarantorLabor'),
|
||||||
|
'WarrantyDurationLabor': self.get_pset_value_from_object(type, 'COBie_Warranty', 'WarrantyDurationLabor', 0),
|
||||||
|
# TODO: this may be derived from the duration values above, but
|
||||||
|
# until it is clarified, it will be hardcoded as 'day'
|
||||||
|
'WarrantyDurationUnit': 'day',
|
||||||
|
'ExtSystem': self.get_ext_system_from_history(type.OwnerHistory),
|
||||||
|
'ExtObject': self.get_ext_object(type),
|
||||||
|
'ExtIdentifier': type.GlobalId,
|
||||||
|
'ReplacementCost': self.get_pset_value_from_object(type, 'COBie_EconomicImpactValues', 'ReplacementCost', 'n/a'),
|
||||||
|
'ExpectedLife': self.get_expected_life_from_type(type),
|
||||||
|
# See note about WarrantyDurationUnit above
|
||||||
|
'DurationUnit': 'day',
|
||||||
|
'NominalLength': self.get_pset_value_from_object(type, 'COBie_Specification', 'NominalLength', 0),
|
||||||
|
'NominalWidth': self.get_pset_value_from_object(type, 'COBie_Specification', 'NominalWidth', 0),
|
||||||
|
'NominalHeight': self.get_pset_value_from_object(type, 'COBie_Specification', 'NominalHeight', 0),
|
||||||
|
'ModelReference': self.get_pset_value_from_object(type, 'Pset_ManufacturerTypeInformation', 'ModelReference', 'n/a'),
|
||||||
|
'Shape': self.get_pset_value_from_object(type, 'COBie_Specification', 'Shape', 'n/a'),
|
||||||
|
'Size': self.get_pset_value_from_object(type, 'COBie_Specification', 'Size', 'n/a'),
|
||||||
|
# The responsbility matrix allows the British spelling of
|
||||||
|
# "colour". I, however, do not.
|
||||||
|
'Color': self.get_pset_value_from_object(type, 'COBie_Specification', 'Color', 'n/a'),
|
||||||
|
'Finish': self.get_pset_value_from_object(type, 'COBie_Specification', 'Finish', 'n/a'),
|
||||||
|
'Grade': self.get_pset_value_from_object(type, 'COBie_Specification', 'Grade', 'n/a'),
|
||||||
|
'Material': self.get_pset_value_from_object(type, 'COBie_Specification', 'Material', 'n/a'),
|
||||||
|
'Constituents': self.get_pset_value_from_object(type, 'COBie_Specification', 'Constituents', 'n/a'),
|
||||||
|
'Features': self.get_pset_value_from_object(type, 'COBie_Specification', 'Features', 'n/a'),
|
||||||
|
'AccessibilityPerformance': self.get_pset_value_from_object(type, 'COBie_Specification', 'AccessibilityPerformance', 'n/a'),
|
||||||
|
'CodePerformance': self.get_pset_value_from_object(type, 'COBie_Specification', 'CodePerformance', 'n/a'),
|
||||||
|
'SustainabilityPerformance': self.get_pset_value_from_object(type, 'COBie_Specification', 'SustainabilityPerformance', 'n/a'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_expected_life_from_type(self, type):
|
||||||
|
if self.file.schema == 'IFC2X3':
|
||||||
|
return self.get_pset_value_from_object(type, 'COBie_ServiceLife', 'ServiceLifeDuration', 'n/a')
|
||||||
|
return self.get_pset_value_from_object(type, 'Pset_ServiceLife', 'ServiceLifeDuration', 'n/a')
|
||||||
|
|
||||||
|
def get_contact_pset_value_from_object(self, object, pset_name, property_name):
|
||||||
|
result = self.get_pset_value_from_object(object, pset_name, property_name)
|
||||||
|
if not result:
|
||||||
|
logging.error('No property {} in {} was found for {}'.format(property_name, pset_name, object))
|
||||||
|
if result not in self.contacts:
|
||||||
|
logging.error('A coresponding {} contact in {} was not found for {}'.format(property_name, pset_name, object))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_pset_value_from_object(self, object, pset_name, property_name, default=None, picklist=None):
|
||||||
|
print(object)
|
||||||
|
print(pset_name)
|
||||||
|
pset = self.get_pset_from_object(object, pset_name)
|
||||||
|
if not pset:
|
||||||
|
if picklist:
|
||||||
|
self.picklists[picklist].append(default)
|
||||||
|
return default
|
||||||
|
property = self.get_property_from_pset(pset, property_name, default)
|
||||||
|
if picklist:
|
||||||
|
self.picklists[picklist].append(property)
|
||||||
|
return property
|
||||||
|
|
||||||
def get_space_names_from_zone(self, zone):
|
def get_space_names_from_zone(self, zone):
|
||||||
spaces = []
|
spaces = []
|
||||||
for relationship in zone.IsGroupedBy:
|
for relationship in zone.IsGroupedBy:
|
||||||
@@ -177,24 +299,24 @@ class IfcCobieCsv():
|
|||||||
logging.warning('The quantity value {} was not found for {}'.format(name, qto))
|
logging.warning('The quantity value {} was not found for {}'.format(name, qto))
|
||||||
return 'n/a'
|
return 'n/a'
|
||||||
|
|
||||||
def get_room_tag_from_space(self, space):
|
def get_property_from_pset(self, pset, name, default=None):
|
||||||
pset = self.get_pset_from_object(space, 'COBie_Space')
|
|
||||||
if not pset:
|
|
||||||
return 'n/a'
|
|
||||||
return self.get_property_from_pset(pset, 'RoomTag')
|
|
||||||
|
|
||||||
def get_property_from_pset(self, pset, name):
|
|
||||||
for property in pset.HasProperties:
|
for property in pset.HasProperties:
|
||||||
if property.Name == name:
|
if property.Name == name:
|
||||||
return property.NominalValue
|
return property.NominalValue
|
||||||
logging.warning('The property {} was not found for {}'.format(name, pset))
|
logging.warning('The property {} was not found for {}'.format(name, pset))
|
||||||
return 'n/a'
|
return default
|
||||||
|
|
||||||
def get_pset_from_object(self, object, name):
|
def get_pset_from_object(self, object, name):
|
||||||
for relationship in object.IsDefinedBy:
|
if object.is_a('IfcTypeObject'):
|
||||||
if relationship.RelatingPropertyDefinition.is_a('IfcPropertySet') \
|
for pset in object.HasPropertySets:
|
||||||
and relationship.RelatingPropertyDefinition.Name == name:
|
if pset.is_a('IfcPropertySet') \
|
||||||
return relationship.RelatingPropertyDefinition
|
and pset.Name == name:
|
||||||
|
return pset
|
||||||
|
else:
|
||||||
|
for relationship in object.IsDefinedBy:
|
||||||
|
if relationship.RelatingPropertyDefinition.is_a('IfcPropertySet') \
|
||||||
|
and relationship.RelatingPropertyDefinition.Name == name:
|
||||||
|
return relationship.RelatingPropertyDefinition
|
||||||
logging.warning('The pset {} was not found for {}'.format(name, object))
|
logging.warning('The pset {} was not found for {}'.format(name, object))
|
||||||
|
|
||||||
def get_height_from_storey(self, storey):
|
def get_height_from_storey(self, storey):
|
||||||
@@ -439,4 +561,5 @@ pprint.pprint(ifc_cobie_csv.facilities)
|
|||||||
pprint.pprint(ifc_cobie_csv.floors)
|
pprint.pprint(ifc_cobie_csv.floors)
|
||||||
pprint.pprint(ifc_cobie_csv.spaces)
|
pprint.pprint(ifc_cobie_csv.spaces)
|
||||||
pprint.pprint(ifc_cobie_csv.zones)
|
pprint.pprint(ifc_cobie_csv.zones)
|
||||||
|
pprint.pprint(ifc_cobie_csv.types)
|
||||||
print('# Finished conversion in {}s'.format(time.time() - start))
|
print('# Finished conversion in {}s'.format(time.time() - start))
|
||||||
|
|||||||
Reference in New Issue
Block a user