Arch: Added option to export IFC files in imperial units
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>463</width>
|
||||
<height>910</height>
|
||||
<height>937</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -634,6 +634,40 @@ A building storey is not mandatory but a common practice to have at least one in
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>IFC file units</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefComboBox" name="comboBox_3">
|
||||
<property name="toolTip">
|
||||
<string>The units you want your IFC file to be exported to. Note that IFC file are ALWAYS written in metric units. Imperial units are only a conversion applied on top of it. But some BIM applications will use this to choose which unit to work with when opening the file.</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>ifcUnit</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Arch</cstring>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Metric</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Imperial</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -99,7 +99,6 @@ DATA;
|
||||
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
"""
|
||||
@@ -116,6 +115,16 @@ def getPreferences():
|
||||
if FreeCAD.GuiUp and p.GetBool("ifcShowDialog",False):
|
||||
import FreeCADGui
|
||||
FreeCADGui.showPreferences("Import-Export",0)
|
||||
ifcunit = p.GetInt("ifcUnit",0)
|
||||
f = 0.001
|
||||
u = "metre"
|
||||
if ifcunit == 1:
|
||||
f = 0.00328084
|
||||
u = "foot"
|
||||
#if ifcunit == "inch":
|
||||
# f = 0.03937008
|
||||
# not yet implemented, and I don't even know if it is interesting to do it.
|
||||
# the only real use of these units is to make revit choose which mode to work with
|
||||
|
||||
preferences = {
|
||||
'DEBUG': p.GetBool("ifcDebug",False),
|
||||
@@ -127,7 +136,9 @@ def getPreferences():
|
||||
'FULL_PARAMETRIC': p.GetBool("IfcExportFreeCADProperties",False),
|
||||
'ADD_DEFAULT_SITE': p.GetBool("IfcAddDefaultSite",False),
|
||||
'ADD_DEFAULT_STOREY': p.GetBool("IfcAddDefaultStorey",False),
|
||||
'ADD_DEFAULT_BUILDING': p.GetBool("IfcAddDefaultBuilding",True)
|
||||
'ADD_DEFAULT_BUILDING': p.GetBool("IfcAddDefaultBuilding",True),
|
||||
'IFC_UNIT': u,
|
||||
'SCALE_FACTOR': f
|
||||
}
|
||||
|
||||
return preferences
|
||||
@@ -188,6 +199,7 @@ def export(exportList,filename,colors=None,preferences=None):
|
||||
os.close(templatefilehandle)
|
||||
global ifcfile, surfstyles, clones, sharedobjects, profiledefs, shapedefs
|
||||
ifcfile = ifcopenshell.open(templatefile)
|
||||
ifcfile = exportIFCHelper.writeUnits(ifcfile,preferences["IFC_UNIT"])
|
||||
history = ifcfile.by_type("IfcOwnerHistory")[0]
|
||||
objectslist = Draft.getGroupContents(exportList,walls=True,addgroups=True)
|
||||
annotations = []
|
||||
@@ -307,8 +319,8 @@ def export(exportList,filename,colors=None,preferences=None):
|
||||
for axg in axgroups:
|
||||
ifcaxg = []
|
||||
for ax in axg:
|
||||
p1 = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[0]).multiply(0.001)[:2]))
|
||||
p2 = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[1]).multiply(0.001)[:2]))
|
||||
p1 = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[0]).multiply(preferences['SCALE_FACTOR'])[:2]))
|
||||
p2 = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[1]).multiply(preferences['SCALE_FACTOR'])[:2]))
|
||||
pol = ifcbin.createIfcPolyline([p1,p2])
|
||||
ifcpols.append(pol)
|
||||
axis = ifcfile.createIfcGridAxis(ax[2],pol,True)
|
||||
@@ -383,14 +395,14 @@ def export(exportList,filename,colors=None,preferences=None):
|
||||
kwargs.update({
|
||||
"RefLatitude":dd2dms(obj.Latitude),
|
||||
"RefLongitude":dd2dms(obj.Longitude),
|
||||
"RefElevation":obj.Elevation.Value/1000.0,
|
||||
"RefElevation":obj.Elevation.Value*preferences['SCALE_FACTOR'],
|
||||
"SiteAddress":buildAddress(obj,ifcfile),
|
||||
"CompositionType": "ELEMENT"
|
||||
})
|
||||
if schema == "IFC2X3":
|
||||
kwargs = exportIFC2X3Attributes(obj, kwargs)
|
||||
kwargs = exportIFC2X3Attributes(obj, kwargs, preferences['SCALE_FACTOR'])
|
||||
else:
|
||||
kwargs = exportIfcAttributes(obj, kwargs)
|
||||
kwargs = exportIfcAttributes(obj, kwargs, preferences['SCALE_FACTOR'])
|
||||
|
||||
# creating the product
|
||||
|
||||
@@ -639,17 +651,17 @@ def export(exportList,filename,colors=None,preferences=None):
|
||||
if hasattr(obj,"IfcData"):
|
||||
quantities = []
|
||||
if ("ExportHeight" in obj.IfcData) and obj.IfcData["ExportHeight"] and hasattr(obj,"Height"):
|
||||
quantities.append(ifcfile.createIfcQuantityLength('Height',None,None,obj.Height.Value/1000.0))
|
||||
quantities.append(ifcfile.createIfcQuantityLength('Height',None,None,obj.Height.Value*preferences['SCALE_FACTOR']))
|
||||
if ("ExportWidth" in obj.IfcData) and obj.IfcData["ExportWidth"] and hasattr(obj,"Width"):
|
||||
quantities.append(ifcfile.createIfcQuantityLength('Width',None,None,obj.Width.Value/1000.0))
|
||||
quantities.append(ifcfile.createIfcQuantityLength('Width',None,None,obj.Width.Value*preferences['SCALE_FACTOR']))
|
||||
if ("ExportLength" in obj.IfcData) and obj.IfcData["ExportLength"] and hasattr(obj,"Length"):
|
||||
quantities.append(ifcfile.createIfcQuantityLength('Length',None,None,obj.Length.Value/1000.0))
|
||||
quantities.append(ifcfile.createIfcQuantityLength('Length',None,None,obj.Length.Value*preferences['SCALE_FACTOR']))
|
||||
if ("ExportHorizontalArea" in obj.IfcData) and obj.IfcData["ExportHorizontalArea"] and hasattr(obj,"HorizontalArea"):
|
||||
quantities.append(ifcfile.createIfcQuantityArea('HorizontalArea',None,None,obj.HorizontalArea.Value/1000000.0))
|
||||
quantities.append(ifcfile.createIfcQuantityArea('HorizontalArea',None,None,obj.HorizontalArea.Value*(preferences['SCALE_FACTOR']**2)))
|
||||
if ("ExportVerticalArea" in obj.IfcData) and obj.IfcData["ExportVerticalArea"] and hasattr(obj,"VerticalArea"):
|
||||
quantities.append(ifcfile.createIfcQuantityArea('VerticalArea',None,None,obj.VerticalArea.Value/1000000.0))
|
||||
quantities.append(ifcfile.createIfcQuantityArea('VerticalArea',None,None,obj.VerticalArea.Value*(preferences['SCALE_FACTOR']**2)))
|
||||
if ("ExportVolume" in obj.IfcData) and obj.IfcData["ExportVolume"] and obj.isDerivedFrom("Part::Feature"):
|
||||
quantities.append(ifcfile.createIfcQuantityVolume('Volume',None,None,obj.Shape.Volume/1000000000.0))
|
||||
quantities.append(ifcfile.createIfcQuantityVolume('Volume',None,None,obj.Shape.Volume*(preferences['SCALE_FACTOR']**3)))
|
||||
if quantities:
|
||||
eltq = ifcfile.createIfcElementQuantity(
|
||||
ifcopenshell.guid.new(),
|
||||
@@ -1132,7 +1144,7 @@ def export(exportList,filename,colors=None,preferences=None):
|
||||
if anno.isDerivedFrom("Part::Feature"):
|
||||
reps = []
|
||||
sh = anno.Shape.copy()
|
||||
sh.scale(0.001) # to meters
|
||||
sh.scale(preferences['SCALE_FACTOR']) # to meters
|
||||
ehc = []
|
||||
curves = []
|
||||
for w in sh.Wires:
|
||||
@@ -1148,7 +1160,7 @@ def export(exportList,filename,colors=None,preferences=None):
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
elif anno.isDerivedFrom("App::Annotation"):
|
||||
l = FreeCAD.Vector(anno.Position).multiply(0.001)
|
||||
l = FreeCAD.Vector(anno.Position).multiply(preferences['SCALE_FACTOR'])
|
||||
pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
|
||||
tpl = ifcbin.createIfcAxis2Placement3D(pos,None,None)
|
||||
s = ";".join(anno.LabelText)
|
||||
@@ -1157,7 +1169,7 @@ def export(exportList,filename,colors=None,preferences=None):
|
||||
txt = ifcfile.createIfcTextLiteral(s,tpl,"LEFT")
|
||||
reps = [txt]
|
||||
elif Draft.getType(anno) == "DraftText":
|
||||
l = FreeCAD.Vector(anno.Placement.Base).multiply(0.001)
|
||||
l = FreeCAD.Vector(anno.Placement.Base).multiply(preferences['SCALE_FACTOR'])
|
||||
pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
|
||||
tpl = ifcbin.createIfcAxis2Placement3D(pos,None,None)
|
||||
s = ";".join(anno.Text)
|
||||
@@ -1494,7 +1506,7 @@ def getIfcTypeFromObj(obj):
|
||||
return "Ifc" + ifctype
|
||||
|
||||
|
||||
def exportIFC2X3Attributes(obj, kwargs):
|
||||
def exportIFC2X3Attributes(obj, kwargs, scale=0.001):
|
||||
|
||||
ifctype = getIfcTypeFromObj(obj)
|
||||
if ifctype in ["IfcSlab", "IfcFooting"]:
|
||||
@@ -1515,7 +1527,7 @@ def exportIFC2X3Attributes(obj, kwargs):
|
||||
kwargs.update({
|
||||
"CompositionType": "ELEMENT",
|
||||
"InteriorOrExteriorSpace": internal,
|
||||
"ElevationWithFlooring": obj.Shape.BoundBox.ZMin/1000.0
|
||||
"ElevationWithFlooring": obj.Shape.BoundBox.ZMin*scale
|
||||
})
|
||||
elif ifctype == "IfcReinforcingBar":
|
||||
kwargs.update({
|
||||
@@ -1523,11 +1535,11 @@ def exportIFC2X3Attributes(obj, kwargs):
|
||||
"BarLength": obj.Length.Value
|
||||
})
|
||||
elif ifctype == "IfcBuildingStorey":
|
||||
kwargs.update({"Elevation": obj.Placement.Base.z/1000.0})
|
||||
kwargs.update({"Elevation": obj.Placement.Base.z*scale})
|
||||
return kwargs
|
||||
|
||||
|
||||
def exportIfcAttributes(obj, kwargs):
|
||||
def exportIfcAttributes(obj, kwargs, scale=0.001):
|
||||
|
||||
for property in obj.PropertiesList:
|
||||
if obj.getGroupOfProperty(property) == "IFC Attributes" and obj.getPropertyByName(property):
|
||||
@@ -1535,7 +1547,7 @@ def exportIfcAttributes(obj, kwargs):
|
||||
if isinstance(value, FreeCAD.Units.Quantity):
|
||||
value = float(value)
|
||||
if property in ["ElevationWithFlooring","Elevation"]:
|
||||
value = value/1000 # some properties must be changed to meters
|
||||
value = value*scale # some properties must be changed to meters
|
||||
kwargs.update({property: value})
|
||||
return kwargs
|
||||
|
||||
@@ -1756,7 +1768,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
axis1 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(1,0,0))))
|
||||
axis2 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,1,0))))
|
||||
axis3 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,0,1))))
|
||||
origin = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(pla.Base).multiply(0.001)))
|
||||
origin = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(pla.Base).multiply(preferences['SCALE_FACTOR'])))
|
||||
transf = ifcbin.createIfcCartesianTransformationOperator3D(axis1,axis2,origin,1.0,axis3)
|
||||
mapitem = ifcfile.createIfcMappedItem(repmap,transf)
|
||||
shapes = [mapitem]
|
||||
@@ -1779,9 +1791,9 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
rdata = obj.Proxy.getRebarData(obj)
|
||||
if rdata:
|
||||
# convert to meters
|
||||
r = rdata[1] * 0.001
|
||||
r = rdata[1] * preferences['SCALE_FACTOR']
|
||||
for w in rdata[0]:
|
||||
w.scale(0.001)
|
||||
w.scale(preferences['SCALE_FACTOR'])
|
||||
cur = createCurve(ifcfile,w)
|
||||
shape = ifcfile.createIfcSweptDiskSolid(cur,r)
|
||||
shapes.append(shape)
|
||||
@@ -1803,17 +1815,17 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
pl = [pl]
|
||||
for i in range(len(p)):
|
||||
pi = p[i]
|
||||
pi.scale(0.001)
|
||||
pi.scale(preferences['SCALE_FACTOR'])
|
||||
if i < len(ev):
|
||||
evi = FreeCAD.Vector(ev[i])
|
||||
else:
|
||||
evi = FreeCAD.Vector(ev[-1])
|
||||
evi.multiply(0.001)
|
||||
evi.multiply(preferences['SCALE_FACTOR'])
|
||||
if i < len(pl):
|
||||
pli = pl[i].copy()
|
||||
else:
|
||||
pli = pl[-1].copy()
|
||||
pli.Base = pli.Base.multiply(0.001)
|
||||
pli.Base = pli.Base.multiply(preferences['SCALE_FACTOR'])
|
||||
pstr = str([v.Point for v in p[i].Vertexes])
|
||||
if pstr in profiledefs:
|
||||
profile = profiledefs[pstr]
|
||||
@@ -1828,7 +1840,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
if not tostore:
|
||||
# add the object placement to the profile placement. Otherwise it'll be done later at map insert
|
||||
pl2 = obj.getGlobalPlacement()
|
||||
pl2.Base = pl2.Base.multiply(0.001)
|
||||
pl2.Base = pl2.Base.multiply(preferences['SCALE_FACTOR'])
|
||||
pli = pl2.multiply(pli)
|
||||
xvc = ifcbin.createIfcDirection(tuple(pli.Rotation.multVec(FreeCAD.Vector(1,0,0))))
|
||||
zvc = ifcbin.createIfcDirection(tuple(pli.Rotation.multVec(FreeCAD.Vector(0,0,1))))
|
||||
@@ -1891,7 +1903,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
if obj.Shape.Faces:
|
||||
sh = obj.Shape.copy()
|
||||
sh.Placement = obj.getGlobalPlacement()
|
||||
sh.scale(0.001) # to meters
|
||||
sh.scale(preferences['SCALE_FACTOR']) # to meters
|
||||
p = geom.serialise(sh.exportBrepToString())
|
||||
if p:
|
||||
productdef = ifcfile.add(p)
|
||||
@@ -1923,7 +1935,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
#if preferences['DEBUG']: print("Warning! object contains no solids")
|
||||
|
||||
for fcsolid in dataset:
|
||||
fcsolid.scale(0.001) # to meters
|
||||
fcsolid.scale(preferences['SCALE_FACTOR']) # to meters
|
||||
faces = []
|
||||
curves = False
|
||||
shapetype = "brep"
|
||||
@@ -2017,7 +2029,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
pla = obj.getGlobalPlacement()
|
||||
axis1 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(1,0,0))))
|
||||
axis2 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,1,0))))
|
||||
origin = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(pla.Base).multiply(0.001)))
|
||||
origin = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(pla.Base).multiply(preferences['SCALE_FACTOR'])))
|
||||
axis3 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,0,1))))
|
||||
transf = ifcbin.createIfcCartesianTransformationOperator3D(axis1,axis2,origin,1.0,axis3)
|
||||
mapitem = ifcfile.createIfcMappedItem(repmap,transf)
|
||||
|
||||
@@ -39,6 +39,27 @@ def getObjectsOfIfcType(objects, ifcType):
|
||||
return results
|
||||
|
||||
|
||||
def writeUnits(ifcfile,unit="metre"):
|
||||
|
||||
"""adds additional units settings to the given ifc file if needed"""
|
||||
# so far, only metre or foot possible (which is all revit knows anyway)
|
||||
|
||||
if unit == "foot":
|
||||
d1 = ifcfile.createIfcDimensionalExponents(1,0,0,0,0,0,0);
|
||||
d2 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.3048),ifcfile[13])
|
||||
d3 = ifcfile.createIfcConversionBasedUnit(d1,'LENGTHUNIT','FOOT',d2)
|
||||
d4 = ifcfile.createIfcDimensionalExponents(2,0,0,0,0,0,0);
|
||||
d5 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.09290304000000001),ifcfile[14])
|
||||
d6 = ifcfile.createIfcConversionBasedUnit(d4,'AREAUNIT','SQUARE FOOT',d5)
|
||||
d7 = ifcfile.createIfcDimensionalExponents(3,0,0,0,0,0,0);
|
||||
d8 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.028316846592),ifcfile[15])
|
||||
d9 = ifcfile.createIfcConversionBasedUnit(d7,'VOLUMEUNIT','CUBIC FOOT',d8)
|
||||
ifcfile.createIfcUnitAssignment((d3,d6,d9,ifcfile[18]))
|
||||
else: # default = metre, no need to add anything
|
||||
ifcfile.createIfcUnitAssignment((ifcfile[13],ifcfile[14],ifcfile[15],ifcfile[18]))
|
||||
return ifcfile
|
||||
|
||||
|
||||
class SIUnitCreator:
|
||||
def __init__(self, file, text, type):
|
||||
self.prefixes = [
|
||||
|
||||
Reference in New Issue
Block a user