195 lines
8.6 KiB
Python
195 lines
8.6 KiB
Python
import FreeCAD, os, json
|
|
|
|
"This modules sets up and manages the IFC-related properties, types and attributes of Arch/BIM objects"
|
|
|
|
if FreeCAD.GuiUp:
|
|
from PySide.QtCore import QT_TRANSLATE_NOOP
|
|
else:
|
|
def QT_TRANSLATE_NOOP(ctx,txt):
|
|
return txt
|
|
|
|
import ArchIFCSchema
|
|
|
|
IfcTypes = [''.join(map(lambda x: x if x.islower() else " "+x, t[3:]))[1:] for t in ArchIFCSchema.IfcProducts.keys()]
|
|
|
|
class IfcRoot:
|
|
def setProperties(self, obj):
|
|
if not "IfcData" in obj.PropertiesList:
|
|
obj.addProperty("App::PropertyMap","IfcData","IFC",QT_TRANSLATE_NOOP("App::Property","IFC data"))
|
|
|
|
if not "IfcType" in obj.PropertiesList:
|
|
obj.addProperty("App::PropertyEnumeration","IfcType","IFC",QT_TRANSLATE_NOOP("App::Property","The type of this object"))
|
|
obj.IfcType = self.getCanonicalisedIfcTypes()
|
|
|
|
if not "IfcProperties" in obj.PropertiesList:
|
|
obj.addProperty("App::PropertyMap","IfcProperties","IFC",QT_TRANSLATE_NOOP("App::Property","IFC properties of this object"))
|
|
|
|
self.migrateDeprecatedAttributes(obj)
|
|
|
|
def onChanged(self, obj, prop):
|
|
if prop == "IfcType":
|
|
self.setupIfcAttributes(obj)
|
|
self.setupIfcComplexAttributes(obj)
|
|
if prop in obj.PropertiesList:
|
|
if obj.getGroupOfProperty(prop) == "IFC Attributes":
|
|
self.setObjIfcAttributeValue(obj, prop, obj.getPropertyByName(prop))
|
|
|
|
def setupIfcAttributes(self, obj):
|
|
ifcTypeSchema = self.getIfcTypeSchema(obj.IfcType)
|
|
if ifcTypeSchema is None:
|
|
return
|
|
self.purgeUnusedIfcAttributesFromPropertiesList(ifcTypeSchema, obj)
|
|
self.addIfcAttributes(ifcTypeSchema, obj)
|
|
|
|
def setupIfcComplexAttributes(self, obj):
|
|
ifcTypeSchema = self.getIfcTypeSchema(obj.IfcType)
|
|
if ifcTypeSchema is None:
|
|
return
|
|
IfcData = obj.IfcData
|
|
if "complex_attributes" not in IfcData:
|
|
IfcData["complex_attributes"] = "{}"
|
|
ifcComplexAttributes = json.loads(IfcData["complex_attributes"])
|
|
for attribute in ifcTypeSchema["complex_attributes"]:
|
|
if attribute["name"] not in ifcComplexAttributes.keys():
|
|
ifcComplexAttributes[attribute["name"]] = {}
|
|
IfcData["complex_attributes"] = json.dumps(ifcComplexAttributes)
|
|
obj.IfcData = IfcData
|
|
|
|
def getIfcTypeSchema(self, IfcType):
|
|
name = "Ifc" + IfcType.replace(" ", "")
|
|
if IfcType == "Undefined":
|
|
name = "IfcBuildingElementProxy"
|
|
if name in self.getIfcSchema():
|
|
return self.getIfcSchema()[name]
|
|
return None
|
|
|
|
def getIfcSchema(self):
|
|
return {}
|
|
|
|
def getCanonicalisedIfcTypes(self):
|
|
schema = self.getIfcSchema()
|
|
return [''.join(map(lambda x: x if x.islower() else " "+x, t[3:]))[1:] for t in schema.keys()]
|
|
|
|
def getIfcAttributeSchema(self, ifcTypeSchema, name):
|
|
for attribute in ifcTypeSchema["attributes"]:
|
|
if attribute["name"].replace(' ', '') == name:
|
|
return attribute
|
|
return None
|
|
|
|
def addIfcAttributes(self, ifcTypeSchema, obj):
|
|
for attribute in ifcTypeSchema["attributes"]:
|
|
if attribute["name"] in obj.PropertiesList \
|
|
or attribute["name"] == "RefLatitude" \
|
|
or attribute["name"] == "RefLongitude" \
|
|
or attribute["name"] == "Name":
|
|
continue
|
|
self.addIfcAttribute(obj, attribute)
|
|
self.addIfcAttributeValueExpressions(obj, attribute)
|
|
|
|
def addIfcAttribute(self, obj, attribute):
|
|
if not hasattr(obj, "IfcData"):
|
|
return
|
|
IfcData = obj.IfcData
|
|
if "attributes" not in IfcData:
|
|
IfcData["attributes"] = "{}"
|
|
IfcAttributes = json.loads(IfcData["attributes"])
|
|
IfcAttributes[attribute["name"]] = attribute
|
|
IfcData["attributes"] = json.dumps(IfcAttributes)
|
|
obj.IfcData = IfcData
|
|
if attribute["is_enum"]:
|
|
obj.addProperty("App::PropertyEnumeration", attribute["name"], "IFC Attributes", QT_TRANSLATE_NOOP("App::Property", "Description of IFC attributes are not yet implemented"))
|
|
setattr(obj, attribute["name"], attribute["enum_values"])
|
|
else:
|
|
import ArchIFCSchema
|
|
propertyType = "App::" + ArchIFCSchema.IfcTypes[attribute["type"]]["property"]
|
|
obj.addProperty(propertyType, attribute["name"], "IFC Attributes", QT_TRANSLATE_NOOP("App::Property", "Description of IFC attributes are not yet implemented"))
|
|
|
|
def addIfcAttributeValueExpressions(self, obj, attribute):
|
|
if obj.getGroupOfProperty(attribute["name"]) != "IFC Attributes" \
|
|
or attribute["name"] not in obj.PropertiesList:
|
|
return
|
|
if attribute["name"] == "OverallWidth":
|
|
if "Length" in obj.PropertiesList:
|
|
obj.setExpression("OverallWidth", "Length.Value")
|
|
elif "Width" in obj.PropertiesList:
|
|
obj.setExpression("OverallWidth", "Width.Value")
|
|
elif obj.Shape and (obj.Shape.BoundBox.XLength > obj.Shape.BoundBox.YLength):
|
|
obj.setExpression("OverallWidth", "Shape.BoundBox.XLength")
|
|
elif obj.Shape:
|
|
obj.setExpression("OverallWidth", "Shape.BoundBox.YLength")
|
|
elif attribute["name"] == "OverallHeight":
|
|
if "Height" in obj.PropertiesList:
|
|
obj.setExpression("OverallHeight", "Height.Value")
|
|
else:
|
|
obj.setExpression("OverallHeight", "Shape.BoundBox.ZLength")
|
|
elif attribute["name"] == "ElevationWithFlooring" and "Shape" in obj.PropertiesList:
|
|
obj.setExpression("ElevationWithFlooring", "Shape.BoundBox.ZMin")
|
|
elif attribute["name"] == "Elevation" and "Placement" in obj.PropertiesList:
|
|
obj.setExpression("Elevation", "Placement.Base.z")
|
|
elif attribute["name"] == "NominalDiameter" and "Diameter" in obj.PropertiesList:
|
|
obj.setExpression("NominalDiameter", "Diameter.Value")
|
|
elif attribute["name"] == "BarLength" and "Length" in obj.PropertiesList:
|
|
obj.setExpression("BarLength", "Length.Value")
|
|
elif attribute["name"] == "RefElevation" and "Elevation" in obj.PropertiesList:
|
|
obj.setExpression("RefElevation", "Elevation.Value")
|
|
elif attribute["name"] == "LongName":
|
|
obj.LongName = obj.Label
|
|
|
|
def setObjIfcAttributeValue(self, obj, attributeName, value):
|
|
IfcData = obj.IfcData
|
|
if "attributes" not in IfcData:
|
|
IfcData["attributes"] = "{}"
|
|
IfcAttributes = json.loads(IfcData["attributes"])
|
|
if isinstance(value, FreeCAD.Units.Quantity):
|
|
value = float(value)
|
|
if not attributeName in IfcAttributes:
|
|
IfcAttributes[attributeName] = {}
|
|
IfcAttributes[attributeName]["value"] = value
|
|
IfcData["attributes"] = json.dumps(IfcAttributes)
|
|
obj.IfcData = IfcData
|
|
|
|
def setObjIfcComplexAttributeValue(self, obj, attributeName, value):
|
|
IfcData = obj.IfcData
|
|
IfcAttributes = json.loads(IfcData["complex_attributes"])
|
|
IfcAttributes[attributeName] = value
|
|
IfcData["complex_attributes"] = json.dumps(IfcAttributes)
|
|
obj.IfcData = IfcData
|
|
|
|
def getObjIfcComplexAttribute(self, obj, attributeName):
|
|
return json.loads(obj.IfcData["complex_attributes"])[attributeName]
|
|
|
|
def purgeUnusedIfcAttributesFromPropertiesList(self, ifcTypeSchema, obj):
|
|
for property in obj.PropertiesList:
|
|
if obj.getGroupOfProperty(property) != "IFC Attributes":
|
|
continue
|
|
ifcAttribute = self.getIfcAttributeSchema(ifcTypeSchema, property)
|
|
if ifcAttribute is None or ifcAttribute["is_enum"] is True:
|
|
obj.removeProperty(property)
|
|
|
|
def migrateDeprecatedAttributes(self, obj):
|
|
if "Role" in obj.PropertiesList:
|
|
r = obj.Role
|
|
obj.removeProperty("Role")
|
|
if r in IfcTypes:
|
|
obj.IfcType = r
|
|
FreeCAD.Console.PrintMessage("Upgrading "+obj.Label+" Role property to IfcType\n")
|
|
|
|
if "IfcRole" in obj.PropertiesList:
|
|
r = obj.IfcRole
|
|
obj.removeProperty("IfcRole")
|
|
if r in IfcTypes:
|
|
obj.IfcType = r
|
|
FreeCAD.Console.PrintMessage("Upgrading "+obj.Label+" IfcRole property to IfcType\n")
|
|
|
|
if "IfcAttributes"in obj.PropertiesList:
|
|
obj.IfcData = obj.IfcAttributes
|
|
obj.removeProperty("IfcAttributes")
|
|
|
|
class IfcProduct(IfcRoot):
|
|
def getIfcSchema(self):
|
|
return ArchIFCSchema.IfcProducts
|
|
|
|
class IfcContext(IfcRoot):
|
|
def getIfcSchema(self):
|
|
return ArchIFCSchema.IfcContexts
|