Arch: Initial support to export quantities to IFC

This commit is contained in:
Yorik van Havre
2018-08-10 18:37:13 -03:00
parent ebe0d1aa76
commit b172c234a8

View File

@@ -114,9 +114,9 @@ END-ISO-10303-21;
def decode(filename,utf=False):
"turns unicodes into strings"
if isinstance(filename,unicode):
# workaround since ifcopenshell currently can't handle unicode filenames
if utf:
@@ -129,9 +129,9 @@ def decode(filename,utf=False):
def doubleClickTree(item,column):
"a double-click callback function for the IFC explorer tool"
txt = item.text(column)
if "Entity #" in txt:
eid = txt.split("#")[1].split(":")[0]
@@ -142,13 +142,13 @@ def doubleClickTree(item,column):
def dd2dms(dd):
"converts decimal degrees to degrees,minutes,seconds"
dd = abs(dd)
minutes,seconds = divmod(dd*3600,60)
degrees,minutes = divmod(minutes,60)
if dd < 0:
if dd < 0:
degrees = -degrees
return (int(degrees),int(minutes),int(seconds))
@@ -161,9 +161,9 @@ def dms2dd(degrees, minutes, seconds, milliseconds=0):
def getPreferences():
"""retrieves IFC preferences"""
global DEBUG, PREFIX_NUMBERS, SKIP, SEPARATE_OPENINGS
global ROOT_ELEMENT, GET_EXTRUSIONS, MERGE_MATERIALS
global MERGE_MODE_ARCH, MERGE_MODE_STRUCT, CREATE_CLONES
@@ -199,7 +199,7 @@ def getPreferences():
def explore(filename=None):
"""explore([filename]): opens a dialog showing
the contents of an IFC file. If no filename is given, a dialog will
pop up to choose a file."""
@@ -359,7 +359,7 @@ def explore(filename=None):
def open(filename,skip=[],only=[],root=None):
"opens an IFC file in a new document"
docname = os.path.splitext(os.path.basename(filename))[0]
@@ -371,7 +371,7 @@ def open(filename,skip=[],only=[],root=None):
def insert(filename,docname,skip=[],only=[],root=None):
"""insert(filename,docname,skip=[],only=[],root=None): imports the contents of an IFC file.
skip can contain a list of ids of objects to be skipped, only can restrict the import to
certain object ids (will also get their children) and root can be used to
@@ -395,15 +395,15 @@ def insert(filename,docname,skip=[],only=[],root=None):
if DEBUG: print("done.")
global ROOT_ELEMENT, parametrics
if root:
ROOT_ELEMENT = root
#global ifcfile # keeping global for debugging purposes
filename = decode(filename,utf=True)
ifcfile = ifcopenshell.open(filename)
# set default ifcopenshell optionss to work in brep mode
from ifcopenshell import geom
settings = ifcopenshell.geom.settings()
@@ -414,7 +414,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS,True)
if SPLIT_LAYERS and hasattr(settings,"APPLY_LAYERSETS"):
settings.set(settings.APPLY_LAYERSETS,True)
# gather easy entity types
sites = ifcfile.by_type("IfcSite")
buildings = ifcfile.by_type("IfcBuilding")
@@ -441,7 +441,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
sharedobjects = {} # { representationmapid:object }
parametrics = [] # a list of imported objects whose parametric relationships need processing after all objects have been created
profiles = {} # to store reused extrusion profiles {ifcid:fcobj,...}
for r in ifcfile.by_type("IfcRelContainedInSpatialStructure"):
additions.setdefault(r.RelatingStructure.id(),[]).extend([e.id() for e in r.RelatedElements])
for r in ifcfile.by_type("IfcRelAggregates"):
@@ -511,7 +511,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
products = tp
# only import a list of IDs and their children
if only:
if only:
ids = []
while only:
currentid = only.pop()
@@ -609,7 +609,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
else:
sharedobjects[bid] = None
store = bid
# additional setting for structural entities
if hasattr(settings,"INCLUDE_CURVES"):
if structobj:
@@ -660,7 +660,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
if DEBUG: print(shape.Solids," ",end="")
baseobj = shape
else:
# create base shape object
if clone:
if DEBUG: print("clone ",end="")
@@ -963,9 +963,9 @@ def insert(filename,docname,skip=[],only=[],root=None):
if r.RepresentationIdentifier == "FootPrint":
annotations.append(product)
break
# additional properties for specific types
if product.is_a("IfcSite"):
if product.RefElevation:
obj.Elevation = product.RefElevation*1000
@@ -1283,7 +1283,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
class recycler:
"the compression engine - a mechanism to reuse ifc entities if needed"
# this object has some methods identical to corresponding ifcopenshell methods,
# but it checks if a similar entity already exists before creating a new one
# to compress a new type, just add the necessary method here
@@ -1711,7 +1711,7 @@ def export(exportList,filename):
prod2 = ifcfile.createIfcBuildingElementProxy(ifcopenshell.guid.compress(uuid.uuid1().hex),history,o.Label.encode("utf8"),None,None,p2,r2,None,"ELEMENT")
subproducts[o.Name] = prod2
ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'Addition','',product,[prod2])
# subtractions
@@ -1882,6 +1882,26 @@ def export(exportList,filename):
#if DEBUG : print("no ifc properties to export")
pass
# Quantities
if hasattr(obj,"IfcAttributes"):
quantities = []
if ("ExportHeight" in obj.IfcAttributes) and obj.IfcAttributes["ExportHeight"] and hasattr(obj,"Height"):
quantities.append(ifcfile.createIfcQuantityLength('Height',None,None,obj.Height.Value/1000.0))
if ("ExportWidth" in obj.IfcAttributes) and obj.IfcAttributes["ExportWidth"] and hasattr(obj,"Width"):
quantities.append(ifcfile.createIfcQuantityLength('Width',None,None,obj.Width.Value/1000.0))
if ("ExportLength" in obj.IfcAttributes) and obj.IfcAttributes["ExportLength"] and hasattr(obj,"Length"):
quantities.append(ifcfile.createIfcQuantityLength('Length',None,None,obj.Length.Value/1000.0))
if ("ExportHorizontalArea" in obj.IfcAttributes) and obj.IfcAttributes["ExportHorizontalArea"] and hasattr(obj,"HorizontalArea"):
quantities.append(ifcfile.createIfcQuantityArea('HorizontalArea',None,None,obj.HorizontalArea.Value/1000000.0))
if ("ExportVerticalArea" in obj.IfcAttributes) and obj.IfcAttributes["ExportVerticalArea"] and hasattr(obj,"VerticalArea"):
quantities.append(ifcfile.createIfcQuantityArea('VerticalArea',None,None,obj.VerticalArea.Value/1000000.0))
if ("ExportVolume" in obj.IfcAttributes) and obj.IfcAttributes["ExportVolume"] and obj.isDerivedFrom("Part::Feature"):
quantities.append(ifcfile.createIfcQuantityVolume('Volume',None,None,obj.Shape.Volume/1000000000.0))
if quantities:
eltq = ifcfile.createIfcElementQuantity(ifcopenshell.guid.compress(uuid.uuid1().hex),history,"ElementQuantities",None,"FreeCAD",quantities)
ifcfile.createIfcRelDefinesByProperties(ifcopenshell.guid.compress(uuid.uuid1().hex),history,None,None,[product],eltq)
if FULL_PARAMETRIC:
# exporting all the object properties
FreeCADProps = []
@@ -1960,6 +1980,7 @@ def export(exportList,filename):
defaulthost = []
# buildingParts can be exported as any "normal" IFC type. In that case, gather their elements first
for bp in Draft.getObjectsOfType(objectslist,"BuildingPart"):
if not bp.IfcRole in ["Site","Building","Building Storey","Space","Undefined"]:
if bp.Name in products:
@@ -1972,6 +1993,7 @@ def export(exportList,filename):
ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'Assembly','',products[bp.Name],subs)
# floors/buildingparts
for floor in Draft.getObjectsOfType(objectslist,"Floor")+Draft.getObjectsOfType(objectslist,"BuildingPart"):
if (Draft.getType(floor) == "Floor") or (hasattr(floor,"IfcRole") and floor.IfcRole == "Building Storey"):
objs = Draft.getGroupContents(floor,walls=True,addgroups=True)
@@ -1990,6 +2012,7 @@ def export(exportList,filename):
defaulthost = f
# buildings
for building in Draft.getObjectsOfType(objectslist,"Building")+Draft.getObjectsOfType(objectslist,"BuildingPart"):
if (Draft.getType(building) == "Building") or (hasattr(building,"IfcRole") and building.IfcRole == "Building"):
objs = Draft.getGroupContents(building,walls=True,addgroups=True)
@@ -2016,6 +2039,7 @@ def export(exportList,filename):
defaulthost = b
# sites
for site in Draft.getObjectsOfType(objectslist,"Site"):
objs = Draft.getGroupContents(site,walls=True,addgroups=True)
objs = Arch.pruneIncluded(objs)
@@ -2209,8 +2233,8 @@ def export(exportList,filename):
def buildAddress(obj,ifcfile):
a = obj.Address.encode("utf8") or None
p = obj.PostalCode.encode("utf8") or None
t = obj.City.encode("utf8") or None