/// <summary>
        /// Finds the spatial element to its associated level.
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="element">
        /// The element.
        /// </param>
        /// <returns>
        /// The handle.
        /// </returns>
        public override IFCAnyHandle RedirectDescription(ExporterIFC exporterIFC, Element element)
        {
            SpatialElement spatialElem = element as SpatialElement;
            if (spatialElem != null)
            {
                ElementId levelId = spatialElem.LevelId;
                IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);
                if (levelInfo != null)
                    return levelInfo.GetBuildingStorey();
            }

            return null;
        }
        /// <summary>
        /// Finds the spatial element to its associated level.
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="element">
        /// The element.
        /// </param>
        /// <returns>
        /// The handle.
        /// </returns>
        public override IFCAnyHandle RedirectDescription(ExporterIFC exporterIFC, Element element)
        {
            SpatialElement spatialElem = element as SpatialElement;

            if (spatialElem != null)
            {
                ElementId    levelId   = spatialElem.LevelId;
                IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);
                if (levelInfo != null)
                {
                    return(levelInfo.GetBuildingStorey());
                }
            }

            return(null);
        }
        /// <summary>
        /// Exports a family instance as a mapped item.
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="familyInstance">
        /// The family instance to be exported.
        /// </param>
        /// <param name="exportType">
        /// The export type.
        /// </param>
        /// <param name="ifcEnumType">
        /// The string value represents the IFC type.
        /// </param>
        /// <param name="wrapper">
        /// The ProductWrapper.
        /// </param>
        /// <param name="overrideLevelId">
        /// The level id.
        /// </param>
        /// <param name="range">
        /// The range of this family instance to be exported.
        /// </param>
        public static void ExportFamilyInstanceAsMappedItem(ExporterIFC exporterIFC,
            FamilyInstance familyInstance, IFCExportType exportType, string ifcEnumType,
            ProductWrapper wrapper, ElementId overrideLevelId, IFCRange range, IFCAnyHandle parentLocalPlacement)
        {
            bool exportParts = PartExporter.CanExportParts(familyInstance);
            bool isSplit = range != null;
            if (exportParts && !PartExporter.CanExportElementInPartExport(familyInstance, isSplit ? overrideLevelId : familyInstance.Level.Id, isSplit))
                return;

            Document doc = familyInstance.Document;
            IFCFile file = exporterIFC.GetFile();

            FamilySymbol familySymbol = ExporterIFCUtils.GetOriginalSymbol(familyInstance);
            if (familySymbol == null)
                return;

            ProductWrapper familyProductWrapper = ProductWrapper.Create(wrapper);
            double scale = exporterIFC.LinearScale;
            Options options = GeometryUtil.GetIFCExportGeometryOptions();

            IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle();

            HostObject hostElement = familyInstance.Host as HostObject; //hostElement could be null
            ElementId categoryId = CategoryUtil.GetSafeCategoryId(familySymbol);

            //string emptyString = "";
            string familyName = familySymbol.Name;
            string objectType = familyName;

            // A Family Instance can have its own copy of geometry, or use the symbol's copy with a transform.
            // The routine below tells us whether to use the Instance's copy or the Symbol's copy.
            bool useInstanceGeometry = ExporterIFCUtils.UsesInstanceGeometry(familyInstance);

            IList<IFCExtrusionData> cutPairOpeningsForColumns = new List<IFCExtrusionData>();
            using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData())
            {
                Transform trf = familyInstance.GetTransform();

                // Extra information if we are exporting a door or a window.
                IFCDoorWindowInfo doorWindowInfo = null;
                if (exportType == IFCExportType.ExportDoorType)
                    doorWindowInfo = IFCDoorWindowInfo.CreateDoorInfo(exporterIFC, familyInstance, familySymbol, hostElement, overrideLevelId, trf);
                else if (exportType == IFCExportType.ExportWindowType)
                    doorWindowInfo = IFCDoorWindowInfo.CreateWindowInfo(exporterIFC, familyInstance, familySymbol, hostElement, overrideLevelId, trf);

                FamilyTypeInfo typeInfo = new FamilyTypeInfo();
                XYZ extraOffset = XYZ.Zero;

                bool flipped = doorWindowInfo != null ? doorWindowInfo.IsSymbolFlipped : false;
                FamilyTypeInfo currentTypeInfo = ExporterCacheManager.TypeObjectsCache.Find(familySymbol.Id, flipped);
                bool found = currentTypeInfo.IsValid();

                Family family = familySymbol.Family;

                // TODO: this code to be removed by ExtrusionAnalyzer code.
                bool trySpecialColumnCreation = ((exportType == IFCExportType.ExportColumnType) && (!family.IsInPlace));
                IList<GeometryObject> geomObjects = new List<GeometryObject>();
                Transform brepOffsetTransform = null;

                Transform doorWindowTrf = Transform.Identity;
                // We will create a new mapped type if:
                // 1.  We are exporting part of a column or in-place wall (range != null), OR
                // 2.  We are using the instance's copy of the geometry (that it, it has unique geometry), OR
                // 3.  We haven't already created the type.
                bool creatingType = ((range != null) || useInstanceGeometry || !found);
                if (creatingType)
                {
                    IFCAnyHandle bodyRepresentation = null;
                    IFCAnyHandle planRepresentation = null;

                    // If we are using the instance geometry, ignore the transformation.
                    if (useInstanceGeometry)
                        trf = Transform.Identity;

                    // TODO: this code to be removed by ExtrusionAnalyzer code.
                    if (trySpecialColumnCreation)
                    {
                        XYZ rangeOffset = trf.Origin;
                        IFCFamilyInstanceExtrusionExportResults results;

                        results = ExporterIFCUtils.ExportFamilyInstanceAsExtrusion(exporterIFC, familyInstance, useInstanceGeometry, range, overrideLevelId, extraParams);

                        bodyRepresentation = results.GetExtrusionHandle();
                        extraOffset = results.ExtraOffset;
                        cutPairOpeningsForColumns = results.GetCutPairOpenings();

                        if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation))
                        {
                            typeInfo.MaterialIds.Add(results.MaterialId);
                            // add in level for real columns, not in-place ones.
                            Element actualLevel =
                               (overrideLevelId == ElementId.InvalidElementId) ? familyInstance.Level : doc.GetElement(overrideLevelId);
                            if (actualLevel != null)
                            {
                                IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(actualLevel.Id);
                                double nonStoryLevelOffset = LevelUtil.GetNonStoryLevelOffsetIfAny(exporterIFC, actualLevel as Level);
                                if (range != null)
                                {
                                    rangeOffset = new XYZ(rangeOffset.X, rangeOffset.Y, levelInfo.Elevation + nonStoryLevelOffset);
                                }
                            }
                        }

                        rangeOffset += extraOffset;
                        trf.Origin = rangeOffset;
                    }

                    IFCAnyHandle dummyPlacement = null;
                    if (doorWindowInfo != null)
                    {
                        doorWindowTrf = ExporterIFCUtils.GetTransformForDoorOrWindow(familyInstance, familySymbol, doorWindowInfo);
                    }
                    else
                    {
                        dummyPlacement = IFCInstanceExporter.CreateLocalPlacement(file, null, ExporterUtil.CreateAxis2Placement3D(file));
                        extraParams.SetLocalPlacement(dummyPlacement);
                    }

                    bool needToCreate2d = ExporterCacheManager.ExportOptionsCache.ExportAnnotations;
                    bool needToCreate3d = IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation);

                    if (needToCreate2d || needToCreate3d)
                    {
                        using (IFCTransformSetter trfSetter = IFCTransformSetter.Create())
                        {
                            if (doorWindowInfo != null)
                            {
                                trfSetter.Initialize(exporterIFC, doorWindowTrf);
                            }

                            GeometryElement exportGeometry =
                               useInstanceGeometry ? familyInstance.get_Geometry(options) : familySymbol.get_Geometry(options);
                            if (exportGeometry == null)
                                return;

                            if (needToCreate3d)
                            {
                                SolidMeshGeometryInfo solidMeshCapsule = null;

                                if (range == null)
                                {
                                    solidMeshCapsule = GeometryUtil.GetSolidMeshGeometry(exportGeometry, Transform.Identity);
                                }
                                else
                                {
                                    solidMeshCapsule = GeometryUtil.GetClippedSolidMeshGeometry(exportGeometry, range);
                                }

                                IList<Solid> solids = solidMeshCapsule.GetSolids();
                                IList<Mesh> polyMeshes = solidMeshCapsule.GetMeshes();

                                if (range != null && (solids.Count == 0 && polyMeshes.Count == 0))
                                    return; // no proper split geometry

                                geomObjects = FamilyExporterUtil.RemoveSolidsAndMeshesSetToDontExport(doc, exporterIFC, solids, polyMeshes);
                                if (geomObjects.Count == 0 && (solids.Count > 0 || polyMeshes.Count > 0))
                                    return;

                                bool tryToExportAsExtrusion = (!exporterIFC.ExportAs2x2 || (exportType == IFCExportType.ExportColumnType));

                                if (exportType == IFCExportType.ExportColumnType)
                                {
                                    extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ;

                                    if (ExporterCacheManager.ExportOptionsCache.ExportAs2x3CoordinationView2 &&
                                        solids.Count > 0)
                                    {
                                        LocationPoint point = familyInstance.Location as LocationPoint;
                                        XYZ orig = XYZ.Zero;
                                        if (point != null)
                                            orig = point.Point;

                                        Plane plane = new Plane(XYZ.BasisX, XYZ.BasisY, orig);
                                        bool completelyClipped = false;
                                        HashSet<ElementId> materialIds = null;
                                        bodyRepresentation = ExtrusionExporter.CreateExtrusionWithClipping(exporterIFC, familyInstance,
                                            categoryId, solids, plane, XYZ.BasisZ, null, out completelyClipped, out materialIds);
                                        typeInfo.MaterialIds = materialIds;
                                    }
                                }
                                else
                                {
                                    extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryXYZ;
                                }

                                BodyData bodyData = null;
                                if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation))
                                {
                                    BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(tryToExportAsExtrusion);
                                    if (geomObjects.Count > 0)
                                    {
                                        bodyData = BodyExporter.ExportBody(familyInstance.Document.Application, exporterIFC, familyInstance, categoryId, ElementId.InvalidElementId,
                                            geomObjects, bodyExporterOptions, extraParams);
                                        typeInfo.MaterialIds = bodyData.MaterialIds;
                                    }
                                    else
                                    {
                                        IList<GeometryObject> exportedGeometries = new List<GeometryObject>();
                                        exportedGeometries.Add(exportGeometry);
                                        bodyData = BodyExporter.ExportBody(familyInstance.Document.Application, exporterIFC, familyInstance, categoryId, ElementId.InvalidElementId,
                                            exportedGeometries, bodyExporterOptions, extraParams);
                                    }
                                    bodyRepresentation = bodyData.RepresentationHnd;
                                    brepOffsetTransform = bodyData.BrepOffsetTransform;
                                }

                                if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation))
                                {
                                    extraParams.ClearOpenings();
                                    return;
                                }
                            }

                            // By default: if exporting IFC2x3 or later, export 2D plan rep of family, if it exists, unless we are exporting Coordination View V2.
                            // This default can be overridden in the export options.
                            if (needToCreate2d)
                            {
                                XYZ curveOffset = new XYZ(0, 0, 0);
                                if (brepOffsetTransform != null)
                                    curveOffset = -brepOffsetTransform.Origin / scale;

                                HashSet<IFCAnyHandle> curveSet = new HashSet<IFCAnyHandle>();
                                {
                                    Transform planeTrf = doorWindowTrf.Inverse;
                                    Plane plane = new Plane(planeTrf.get_Basis(0), planeTrf.get_Basis(1), planeTrf.Origin);
                                    XYZ projDir = new XYZ(0, 0, 1);

                                    IFCGeometryInfo IFCGeometryInfo = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, plane, projDir, true);
                                    ExporterIFCUtils.CollectGeometryInfo(exporterIFC, IFCGeometryInfo, exportGeometry, curveOffset, false);

                                    IList<IFCAnyHandle> curves = IFCGeometryInfo.GetCurves();
                                    foreach (IFCAnyHandle curve in curves)
                                        curveSet.Add(curve);

                                    if (curveSet.Count > 0)
                                    {
                                        IFCAnyHandle contextOfItems2d = exporterIFC.Get2DContextHandle();
                                        IFCAnyHandle curveRepresentationItem = IFCInstanceExporter.CreateGeometricSet(file, curveSet);
                                        HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>();
                                        bodyItems.Add(curveRepresentationItem);
                                        planRepresentation = RepresentationUtil.CreateGeometricSetRep(exporterIFC, familyInstance, categoryId, "Annotation",
                                           contextOfItems2d, bodyItems);
                                    }
                                }
                            }
                        }
                    }

                    if (doorWindowInfo != null)
                    {
                        typeInfo.StyleTransform = doorWindowTrf.Inverse;
                    }
                    else
                    {
                        if (!MathUtil.IsAlmostZero(extraOffset.DotProduct(extraOffset)))
                        {
                            Transform newTransform = typeInfo.StyleTransform;
                            XYZ newOrig = newTransform.Origin + extraOffset;
                            newTransform.Origin = newOrig;
                            typeInfo.StyleTransform = newTransform;
                        }
                        typeInfo.StyleTransform = ExporterIFCUtils.GetUnscaledTransform(exporterIFC, extraParams.GetLocalPlacement());
                    }

                    IFCAnyHandle origin = ExporterUtil.CreateAxis2Placement3D(file);
                    IFCAnyHandle repMap2dHnd = null;
                    IFCAnyHandle repMap3dHnd = IFCInstanceExporter.CreateRepresentationMap(file, origin, bodyRepresentation);

                    IList<IFCAnyHandle> repMapList = new List<IFCAnyHandle>();
                    repMapList.Add(repMap3dHnd);
                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(planRepresentation))
                    {
                        repMap2dHnd = IFCInstanceExporter.CreateRepresentationMap(file, origin, planRepresentation);
                        repMapList.Add(repMap2dHnd);
                    }

                    // for Door, Window
                    bool paramTakesPrecedence = false; // For Revit, this is currently always false.
                    bool sizeable = false;

                    // for many
                    HashSet<IFCAnyHandle> propertySets = new HashSet<IFCAnyHandle>();

                    string guid = GUIDUtil.CreateGUID(familySymbol);
                    string symId = NamingUtil.CreateIFCElementId(familySymbol);

                    // This covers many generic types.  If we can't find it in the list here, do custom exports.
                    IFCAnyHandle typeStyle = FamilyExporterUtil.ExportGenericType(file, exportType, ifcEnumType, guid,
                       ownerHistory, objectType, null, null, propertySets, repMapList, symId, objectType,
                       familyInstance, familySymbol);

                    // Cover special cases not covered above.
                    if (IFCAnyHandleUtil.IsNullOrHasNoValue(typeStyle))
                    {
                        switch (exportType)
                        {
                            case IFCExportType.ExportColumnType:
                                {
                                    // If we are using the instance GRep, then we have to create a generic GUID for the
                                    // column type, as they share the same ElementId.
                                    string colGUID = null;
                                    string colElemId = null;

                                    if (useInstanceGeometry)
                                        colGUID = GUIDUtil.CreateGUID();
                                    else
                                        colGUID = guid;
                                    colElemId = NamingUtil.CreateIFCElementId(familySymbol);

                                    string columnType = "Column";
                                    typeStyle = IFCInstanceExporter.CreateColumnType(file, colGUID, ownerHistory, objectType,
                                       null, null, propertySets, repMapList, colElemId,
                                       objectType, GetColumnType(familyInstance, columnType));

                                    break;
                                }
                            case IFCExportType.ExportDoorType:
                                {
                                    string constructionType = string.Empty;
                                    ParameterUtil.GetStringValueFromElementOrSymbol(familyInstance, "Construction", out constructionType);

                                    IFCAnyHandle doorLining = DoorWindowUtil.CreateDoorLiningProperties(exporterIFC, familyInstance);
                                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(doorLining))
                                        propertySets.Add(doorLining);

                                    IList<IFCAnyHandle> doorPanels = DoorWindowUtil.CreateDoorPanelProperties(exporterIFC, doorWindowInfo,
                                       familyInstance);
                                    propertySets.UnionWith(doorPanels);

                                    string doorStyleGUID = GUIDUtil.CreateGUID();
                                    string doorStyleElemId = NamingUtil.CreateIFCElementId(familySymbol);
                                    typeStyle = IFCInstanceExporter.CreateDoorStyle(file, doorStyleGUID, ownerHistory, objectType,
                                       null, null, propertySets, repMapList, doorStyleElemId,
                                       GetDoorStyleOperation(doorWindowInfo.DoorOperationType), GetDoorStyleConstruction(familyInstance, constructionType),
                                       paramTakesPrecedence, sizeable);
                                    break;
                                }
                            case IFCExportType.ExportSystemFurnitureElementType:
                                {
                                    string furnitureId = NamingUtil.CreateIFCElementId(familySymbol);
                                    typeStyle = IFCInstanceExporter.CreateSystemFurnitureElementType(file, guid, ownerHistory, objectType,
                                       null, null, propertySets, repMapList, furnitureId,
                                       objectType);
                                    break;
                                }
                            case IFCExportType.ExportWindowType:
                                {
                                    Toolkit.IFCWindowStyleOperation operationType = DoorWindowUtil.GetIFCWindowStyleOperation(familySymbol);
                                    IFCWindowStyleConstruction constructionType = DoorWindowUtil.GetIFCWindowStyleConstruction(familyInstance, "");

                                    IFCAnyHandle windowLining = DoorWindowUtil.CreateWindowLiningProperties(exporterIFC, familyInstance, null);
                                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(windowLining))
                                        propertySets.Add(windowLining);

                                    IList<IFCAnyHandle> windowPanels =
                                       DoorWindowUtil.CreateWindowPanelProperties(exporterIFC, familyInstance, null);
                                    propertySets.UnionWith(windowPanels);

                                    string windowStyleGUID = GUIDUtil.CreateGUID();
                                    string windowStyleElemId = NamingUtil.CreateIFCElementId(familySymbol);
                                    typeStyle = IFCInstanceExporter.CreateWindowStyle(file, windowStyleGUID, ownerHistory, objectType,
                                       null, null, propertySets, repMapList, windowStyleElemId,
                                       constructionType, operationType, paramTakesPrecedence, sizeable);
                                    break;
                                }
                            case IFCExportType.ExportBuildingElementProxy:
                            default:
                                {
                                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(repMap2dHnd))
                                        typeInfo.Map2DHandle = repMap2dHnd;
                                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(repMap3dHnd))
                                        typeInfo.Map3DHandle = repMap3dHnd;
                                    break;
                                }
                        }
                    }

                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(typeStyle))
                    {
                        CategoryUtil.CreateMaterialAssociations(doc, exporterIFC, typeStyle, typeInfo.MaterialIds);

                        typeInfo.Style = typeStyle;

                        if (((exportType == IFCExportType.ExportColumnType) && trySpecialColumnCreation) ||
                           (exportType == IFCExportType.ExportMemberType))
                        {
                            typeInfo.ScaledArea = extraParams.ScaledArea;
                            typeInfo.ScaledDepth = extraParams.ScaledLength;
                            typeInfo.ScaledInnerPerimeter = extraParams.ScaledInnerPerimeter;
                            typeInfo.ScaledOuterPerimeter = extraParams.ScaledOuterPerimeter;
                        }

                        ClassificationUtil.CreateUniformatClassification(exporterIFC, file, familySymbol, typeStyle);
                    }
                }
                else if (!creatingType && (trySpecialColumnCreation))
                {
                    // still need to modify instance trf for columns.
                    trf.Origin += GetLevelOffsetForExtrudedColumns(exporterIFC, familyInstance, overrideLevelId, extraParams);
                }

                if (found && !typeInfo.IsValid())
                {
                    typeInfo = currentTypeInfo;
                }

                // we'll pretend we succeeded, but we'll do nothing.
                if (!typeInfo.IsValid())
                    return;

                // add to the map, as long as we are not using range, not using instance geometry, and don't have extra openings.
                if ((range == null) && !useInstanceGeometry && (extraParams.GetOpenings().Count == 0))
                    ExporterCacheManager.TypeObjectsCache.Register(familySymbol.Id, flipped, typeInfo);

                Transform oldTrf = new Transform(trf);
                XYZ scaledMapOrigin = XYZ.Zero;

                trf = trf.Multiply(typeInfo.StyleTransform);

                // create instance.
                IList<IFCAnyHandle> shapeReps = new List<IFCAnyHandle>();
                {
                    IFCAnyHandle contextOfItems2d = exporterIFC.Get2DContextHandle();
                    IFCAnyHandle contextOfItems3d = exporterIFC.Get3DContextHandle("Body");

                    // for proxies, we store the IfcRepresentationMap directly since there is no style.
                    IFCAnyHandle style = typeInfo.Style;
                    IList<IFCAnyHandle> repMapList = !IFCAnyHandleUtil.IsNullOrHasNoValue(style) ?
                        GeometryUtil.GetRepresentationMaps(style) : null;
                    int numReps = repMapList != null ? repMapList.Count : 0;

                    IFCAnyHandle repMap2dHnd = typeInfo.Map2DHandle;
                    IFCAnyHandle repMap3dHnd = typeInfo.Map3DHandle;
                    if (IFCAnyHandleUtil.IsNullOrHasNoValue(repMap3dHnd) && (numReps > 0))
                        repMap3dHnd = repMapList[0];
                    if (IFCAnyHandleUtil.IsNullOrHasNoValue(repMap2dHnd) && (numReps > 1))
                        repMap2dHnd = repMapList[1];

                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(repMap3dHnd))
                    {
                        IList<IFCAnyHandle> representations = new List<IFCAnyHandle>();
                        representations.Add(ExporterUtil.CreateDefaultMappedItem(file, repMap3dHnd, scaledMapOrigin));
                        IFCAnyHandle shapeRep = RepresentationUtil.CreateBodyMappedItemRep(exporterIFC, familyInstance, categoryId, contextOfItems3d,
                            representations);
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep))
                            return;
                        shapeReps.Add(shapeRep);
                    }

                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(repMap2dHnd))
                    {
                        HashSet<IFCAnyHandle> representations = new HashSet<IFCAnyHandle>();
                        representations.Add(ExporterUtil.CreateDefaultMappedItem(file, repMap2dHnd, scaledMapOrigin));
                        IFCAnyHandle shapeRep = RepresentationUtil.CreatePlanMappedItemRep(exporterIFC, familyInstance, categoryId, contextOfItems2d,
                            representations);
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep))
                            return;
                        shapeReps.Add(shapeRep);
                    }
                }

                IFCAnyHandle boundingBoxRep = null;
                Transform boundingBoxTrf = (brepOffsetTransform != null) ? brepOffsetTransform.Inverse : Transform.Identity;
                if (geomObjects.Count > 0)
                    boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geomObjects, boundingBoxTrf);
                else
                {
                    boundingBoxTrf = boundingBoxTrf.Multiply(trf.Inverse);
                    boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, familyInstance.get_Geometry(options), boundingBoxTrf);
                }

                if (boundingBoxRep != null)
                    shapeReps.Add(boundingBoxRep);

                IFCAnyHandle rep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapeReps);

                IFCAnyHandle instanceHandle = null;
                using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, familyInstance, trf, null, overrideLevelId))
                {
                    string instanceGUID = GUIDUtil.CreateGUID(familyInstance);
                    string instanceName = NamingUtil.GetIFCName(familyInstance);
                    string instanceDescription = NamingUtil.GetDescriptionOverride(familyInstance, null);
                    string instanceObjectType = NamingUtil.GetObjectTypeOverride(familyInstance, objectType);
                    string instanceElemId = NamingUtil.CreateIFCElementId(familyInstance);

                    IFCAnyHandle localPlacement = setter.GetPlacement();
                    IFCAnyHandle overrideLocalPlacement = null;

                    if (parentLocalPlacement != null)
                    {
                        Transform relTrf = ExporterIFCUtils.GetRelativeLocalPlacementOffsetTransform(parentLocalPlacement, localPlacement);
                        Transform inverseTrf = relTrf.Inverse;

                        IFCAnyHandle relativePlacement = ExporterUtil.CreateAxis2Placement3D(file, inverseTrf.Origin, inverseTrf.BasisZ, inverseTrf.BasisX);
                        IFCAnyHandle plateLocalPlacement = IFCInstanceExporter.CreateLocalPlacement(file, parentLocalPlacement, relativePlacement);
                        overrideLocalPlacement = plateLocalPlacement;
                    }

                    instanceHandle = FamilyExporterUtil.ExportGenericInstance(exportType, exporterIFC, familyInstance,
                       wrapper, setter, extraParams, instanceGUID, ownerHistory, instanceName, instanceDescription, instanceObjectType,
                       exportParts ? null : rep, instanceElemId, overrideLocalPlacement);

                    if (exportParts)
                    {
                        PartExporter.ExportHostPart(exporterIFC, familyInstance, instanceHandle, familyProductWrapper, setter, setter.GetPlacement(), overrideLevelId);
                    }

                    if (ElementFilteringUtil.IsMEPType(exportType))
                        ExporterCacheManager.MEPCache.Register(familyInstance, instanceHandle);

                    switch (exportType)
                    {
                        case IFCExportType.ExportColumnType:
                            {
                                IFCAnyHandle placementToUse = localPlacement;
                                if (!useInstanceGeometry)
                                {
                                    bool needToCreateOpenings =
                                        (cutPairOpeningsForColumns.Count != 0) || OpeningUtil.NeedToCreateOpenings(instanceHandle, extraParams);
                                    if (needToCreateOpenings)
                                    {
                                        Transform openingTrf = new Transform(oldTrf);
                                        Transform extraRot = new Transform(oldTrf);
                                        extraRot.Origin = XYZ.Zero;
                                        openingTrf = openingTrf.Multiply(extraRot);
                                        openingTrf = openingTrf.Multiply(typeInfo.StyleTransform);

                                        IFCAnyHandle openingRelativePlacement = ExporterUtil.CreateAxis2Placement3D(file, openingTrf.Origin * scale,
                                           openingTrf.get_Basis(2), openingTrf.get_Basis(0));
                                        IFCAnyHandle openingPlacement = ExporterUtil.CopyLocalPlacement(file, localPlacement);
                                        GeometryUtil.SetRelativePlacement(openingPlacement, openingRelativePlacement);
                                        placementToUse = openingPlacement;
                                    }
                                }

                                OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, cutPairOpeningsForColumns,
                                   exporterIFC, placementToUse, setter, wrapper);
                                OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                                   placementToUse, setter, wrapper);

                                //export Base Quantities.
                                PropertyUtil.CreateBeamColumnBaseQuantities(exporterIFC, instanceHandle, familyInstance, typeInfo);

                                PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper);
                                break;
                            }
                        case IFCExportType.ExportDoorType:
                        case IFCExportType.ExportWindowType:
                            {
                                double doorHeight = doorWindowInfo.OpeningHeight;
                                if (doorHeight < MathUtil.Eps())
                                    doorHeight = GetMinSymbolHeight(familySymbol);
                                double doorWidth = doorWindowInfo.OpeningWidth;
                                if (doorWidth < MathUtil.Eps())
                                    doorWidth = GetMinSymbolWidth(familySymbol);

                                double height = doorHeight * scale;
                                double width = doorWidth * scale;

                                if (IFCAnyHandleUtil.IsNullOrHasNoValue(doorWindowInfo.GetLocalPlacement()))
                                    doorWindowInfo.SetLocalPlacement(localPlacement);

                                IFCAnyHandle doorWindowOrigLocalPlacement = doorWindowInfo.GetLocalPlacement();
                                Transform relTrf = ExporterIFCUtils.GetRelativeLocalPlacementOffsetTransform(localPlacement, doorWindowOrigLocalPlacement);

                                IFCAnyHandle doorWindowRelativePlacement = ExporterUtil.CreateAxis2Placement3D(file, relTrf.Origin, relTrf.BasisZ, relTrf.BasisX);
                                IFCAnyHandle doorWindowLocalPlacement =
                                   IFCInstanceExporter.CreateLocalPlacement(file, doorWindowOrigLocalPlacement, doorWindowRelativePlacement);
                                if (exportType == IFCExportType.ExportDoorType)
                                    instanceHandle = IFCInstanceExporter.CreateDoor(file, instanceGUID, ownerHistory,
                                       instanceName, instanceDescription, instanceObjectType, doorWindowLocalPlacement,
                                       rep, instanceElemId, height, width);
                                else if (exportType == IFCExportType.ExportWindowType)
                                    instanceHandle = IFCInstanceExporter.CreateWindow(file, instanceGUID, ownerHistory,
                                       instanceName, instanceDescription, instanceObjectType, doorWindowLocalPlacement,
                                       rep, instanceElemId, height, width);
                                wrapper.AddElement(instanceHandle, setter, extraParams, true);

                                exporterIFC.RegisterSpaceBoundingElementHandle(instanceHandle, familyInstance.Id, setter.LevelId);

                                IFCAnyHandle placementToUse = doorWindowLocalPlacement;
                                if (!useInstanceGeometry)
                                {
                                    // correct the placement to the symbol space
                                    bool needToCreateOpenings = OpeningUtil.NeedToCreateOpenings(instanceHandle, extraParams);
                                    if (needToCreateOpenings)
                                    {
                                        Transform openingTrf = Transform.Identity;
                                        openingTrf.Origin = new XYZ(0, 0, setter.Offset);
                                        openingTrf = openingTrf.Multiply(doorWindowTrf);
                                        XYZ scaledOrigin = openingTrf.Origin * exporterIFC.LinearScale;
                                        IFCAnyHandle openingRelativePlacement = ExporterUtil.CreateAxis2Placement3D(file, scaledOrigin, openingTrf.BasisZ, openingTrf.BasisX);
                                        IFCAnyHandle openingLocalPlacement =
                                           IFCInstanceExporter.CreateLocalPlacement(file, doorWindowLocalPlacement, openingRelativePlacement);
                                        placementToUse = openingLocalPlacement;
                                    }
                                }
                                // only necessary when exporting as possible breps.
                                OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                                   placementToUse, setter, wrapper);
                                if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities)
                                    ExporterIFCUtils.CreateDoorWindowBaseQuantities(exporterIFC, instanceHandle, (doorHeight * scale), (doorWidth * scale));

                                PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper);
                                break;
                            }
                        case IFCExportType.ExportMemberType:
                            {
                                OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                                   localPlacement, setter, wrapper);

                                //export Base Quantities.
                                PropertyUtil.CreateBeamColumnBaseQuantities(exporterIFC, instanceHandle, familyInstance, typeInfo);
                                // TODO: create PropertySet!
                                //createMemberPropertySet(exporter, pFamInst, pWrapper, extraParams);
                                PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper);
                                break;
                            }
                        case IFCExportType.ExportPlateType:
                            {
                                OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                                   localPlacement, setter, wrapper);

                                // TODO: create PropertySet!
                                //createPlatePropertySet(exporter, pFamInst, pWrapper, extraParams);
                                PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper);
                                break;
                            }
                        case IFCExportType.ExportTransportElementType:
                            {
                                IFCAnyHandle localPlacementToUse;
                                ElementId roomId = setter.UpdateRoomRelativeCoordinates(familyInstance, out localPlacementToUse);

                                instanceHandle = IFCInstanceExporter.CreateTransportElement(file, instanceGUID, ownerHistory,
                                   instanceName, instanceDescription, instanceObjectType,
                                   localPlacementToUse, rep, instanceElemId, null, null, null);

                                if (roomId == ElementId.InvalidElementId)
                                {
                                    wrapper.AddElement(instanceHandle, setter, extraParams, true);
                                }
                                else
                                {
                                    exporterIFC.RelateSpatialElement(roomId, instanceHandle);
                                    wrapper.AddElement(instanceHandle, setter, extraParams, false);
                                }

                                PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper);
                                break;
                            }
                        case IFCExportType.ExportBuildingElementProxy:
                        default:
                            {
                                bool isBuildingElementProxy = (exportType == IFCExportType.ExportBuildingElementProxy);

                                IFCAnyHandle localPlacementToUse;
                                ElementId roomId = setter.UpdateRoomRelativeCoordinates(familyInstance, out localPlacementToUse);

                                if (!isBuildingElementProxy)
                                {
                                    if (FamilyExporterUtil.IsDistributionControlElementSubType(exportType))
                                    {
                                        instanceHandle = IFCInstanceExporter.CreateDistributionControlElement(file, instanceGUID,
                                           ownerHistory, instanceName, instanceDescription, instanceObjectType,
                                           localPlacementToUse, rep, instanceElemId, null);

                                        if (roomId == ElementId.InvalidElementId)
                                        {
                                            wrapper.AddElement(instanceHandle, setter, extraParams, true);
                                        }
                                        else
                                        {
                                            exporterIFC.RelateSpatialElement(roomId, instanceHandle);
                                            wrapper.AddElement(instanceHandle, setter, extraParams, false);
                                        }
                                    }
                                    else if (IFCAnyHandleUtil.IsNullOrHasNoValue(instanceHandle))
                                    {
                                        isBuildingElementProxy = true;
                                    }
                                }

                                if (isBuildingElementProxy)
                                {
                                    Toolkit.IFCElementComposition proxyType = Toolkit.IFCElementComposition.Element;

                                    instanceHandle = IFCInstanceExporter.CreateBuildingElementProxy(file, instanceGUID,
                                       ownerHistory, instanceName, instanceDescription, instanceObjectType,
                                       localPlacementToUse, rep, instanceElemId, proxyType);

                                    if (roomId == ElementId.InvalidElementId)
                                    {
                                        wrapper.AddElement(instanceHandle, setter, extraParams, true);
                                    }
                                    else
                                    {
                                        exporterIFC.RelateSpatialElement(roomId, instanceHandle);
                                        wrapper.AddElement(instanceHandle, setter, extraParams, false);
                                    }
                                }

                                IFCAnyHandle placementToUse = localPlacement;
                                if (!useInstanceGeometry)
                                {
                                    bool needToCreateOpenings = OpeningUtil.NeedToCreateOpenings(instanceHandle, extraParams);
                                    if (needToCreateOpenings)
                                    {
                                        Transform openingTrf = new Transform(oldTrf);
                                        Transform extraRot = new Transform(oldTrf);
                                        extraRot.Origin = XYZ.Zero;
                                        openingTrf = openingTrf.Multiply(extraRot);
                                        openingTrf = openingTrf.Multiply(typeInfo.StyleTransform);

                                        IFCAnyHandle openingRelativePlacement = ExporterUtil.CreateAxis2Placement3D(file, openingTrf.Origin * scale,
                                           openingTrf.get_Basis(2), openingTrf.get_Basis(0));
                                        IFCAnyHandle openingPlacement = ExporterUtil.CopyLocalPlacement(file, localPlacement);
                                        GeometryUtil.SetRelativePlacement(openingPlacement, openingRelativePlacement);
                                        placementToUse = openingPlacement;
                                    }
                                }

                                OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                                   placementToUse, setter, wrapper);
                                PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, familyInstance, wrapper);
                                break;
                            }
                    }

                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(instanceHandle))
                    {
                        if (doorWindowInfo != null)
                        {
                            if (!IFCAnyHandleUtil.IsNullOrHasNoValue(doorWindowInfo.GetOpening()))
                            {
                                string relGUID = GUIDUtil.CreateGUID();
                                IFCInstanceExporter.CreateRelFillsElement(file, relGUID, ownerHistory, null, null, doorWindowInfo.GetOpening(), instanceHandle);
                            }
                            else if (doorWindowInfo.NeedsOpening)
                            {
                                bool added = doorWindowInfo.SetDelayedFamilyInstance(instanceHandle, localPlacement, doorWindowInfo.AssignedLevelId);
                                if (added)
                                    exporterIFC.RegisterDoorWindowForOpeningUpdate(doorWindowInfo);
                                else
                                {
                                    // we need to fill a later opening.
                                    exporterIFC.RegisterDoorWindowForUncreatedOpening(familyInstance.Id, instanceHandle);
                                }
                            }
                        }

                        if (!exportParts)
                            CategoryUtil.CreateMaterialAssociations(doc, exporterIFC, instanceHandle, typeInfo.MaterialIds);

                        if (!IFCAnyHandleUtil.IsNullOrHasNoValue(typeInfo.Style))
                            ExporterCacheManager.TypeRelationsCache.Add(typeInfo.Style, instanceHandle);
                    }
                }
            }
        }
Esempio n. 4
0
        void commonInit(ExporterIFC exporterIFC, Element elem, Transform familyTrf, Transform orientationTrf, ElementId overrideLevelId)
        {
            m_ExporterIFC = exporterIFC;

            overrideLevelId = overrideLevelId != null ? overrideLevelId : ElementId.InvalidElementId;

            Document  doc        = elem.Document;
            Element   hostElem   = elem;
            ElementId elemId     = elem.Id;
            ElementId newLevelId = overrideLevelId;

            bool useOverrideOrigin = false;
            XYZ  overrideOrigin    = XYZ.Zero;

            IDictionary <ElementId, IFCLevelInfo> levelInfos = exporterIFC.GetLevelInfos();

            if (overrideLevelId == ElementId.InvalidElementId)
            {
                if (familyTrf == null)
                {
                    // Override for CurveElems -- base level calculation on origin of sketch Plane.
                    if (elem is CurveElement)
                    {
                        SketchPlane sketchPlane = (elem as CurveElement).SketchPlane;
                        if (sketchPlane != null)
                        {
                            useOverrideOrigin = true;
                            overrideOrigin    = sketchPlane.GetPlane().Origin;
                        }
                    }
                    else
                    {
                        ElementId hostElemId = ElementId.InvalidElementId;
                        // a bit of a hack.  If we have a railing, we want it to have the same level base as its host Stair (because of
                        // the way the stairs place railings and stair flights together).
                        if (elem is Railing)
                        {
                            hostElemId = (elem as Railing).HostId;
                        }
                        else if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Assemblies)
                        {
                            hostElemId = elem.AssemblyInstanceId;
                        }

                        if (hostElemId != ElementId.InvalidElementId)
                        {
                            hostElem = doc.GetElement(hostElemId);
                        }

                        newLevelId = hostElem != null ? hostElem.LevelId : ElementId.InvalidElementId;

                        if (newLevelId == ElementId.InvalidElementId)
                        {
                            ExporterIFCUtils.GetLevelIdByHeight(exporterIFC, hostElem);
                        }
                    }
                }

                // todo: store.
                double    bottomHeight  = double.MaxValue;
                ElementId bottomLevelId = ElementId.InvalidElementId;
                if ((newLevelId == ElementId.InvalidElementId) || orientationTrf != null)
                {
                    // if we have a trf, it might geometrically push the instance to a new level.  Check that case.
                    // actually, we should ALWAYS check the bbox vs the settings
                    newLevelId = ElementId.InvalidElementId;
                    XYZ  originToUse   = XYZ.Zero;
                    bool originIsValid = useOverrideOrigin;

                    if (useOverrideOrigin)
                    {
                        originToUse = overrideOrigin;
                    }
                    else
                    {
                        BoundingBoxXYZ bbox = elem.get_BoundingBox(null);
                        if (bbox != null)
                        {
                            originToUse   = bbox.Min;
                            originIsValid = true;
                        }
                        else if (hostElem.Id != elemId)
                        {
                            bbox = hostElem.get_BoundingBox(null);
                            if (bbox != null)
                            {
                                originToUse   = bbox.Min;
                                originIsValid = true;
                            }
                        }
                    }


                    // The original heuristic here was that the origin determined the level containment based on exact location:
                    // if the Z of the origin was higher than the current level but lower than the next level, it was contained
                    // on that level.
                    // However, in some places (e.g. Germany), the containment is thought to start just below the level, because floors
                    // are placed before the level, not above.  So we have made a small modification so that anything within
                    // 10cm of the 'next' level is on that level.

                    double leveExtension = 10.0 / (12.0 * 2.54);
                    foreach (KeyValuePair <ElementId, IFCLevelInfo> levelInfoPair in levelInfos)
                    {
                        // the cache contains levels from all the exported documents
                        // if the export is performed for a linked document, filter the levels that are not from this document
                        if (ExporterCacheManager.ExportOptionsCache.ExportingLink)
                        {
                            Element levelElem = doc.GetElement(levelInfoPair.Key);
                            if (levelElem == null || !(levelElem is Level))
                            {
                                continue;
                            }
                        }

                        IFCLevelInfo levelInfo   = levelInfoPair.Value;
                        double       startHeight = levelInfo.Elevation - leveExtension;
                        double       height      = levelInfo.DistanceToNextLevel;
                        bool         useHeight   = !MathUtil.IsAlmostZero(height);
                        double       endHeight   = startHeight + height;

                        if (originIsValid && ((originToUse[2] > (startHeight - MathUtil.Eps())) && (!useHeight || originToUse[2] < (endHeight - MathUtil.Eps()))))
                        {
                            newLevelId = levelInfoPair.Key;
                        }

                        if (startHeight < (bottomHeight + MathUtil.Eps()))
                        {
                            bottomLevelId = levelInfoPair.Key;
                            bottomHeight  = startHeight;
                        }
                    }
                }

                if (newLevelId == ElementId.InvalidElementId)
                {
                    newLevelId = bottomLevelId;
                }
            }

            m_LevelInfo = exporterIFC.GetLevelInfo(newLevelId);
            if (m_LevelInfo == null)
            {
                foreach (KeyValuePair <ElementId, IFCLevelInfo> levelInfoPair in levelInfos)
                {
                    // the cache contains levels from all the exported documents
                    // if the export is performed for a linked document, filter the levels that are not from this document
                    if (ExporterCacheManager.ExportOptionsCache.ExportingLink)
                    {
                        Element levelElem = doc.GetElement(levelInfoPair.Key);
                        if (levelElem == null || !(levelElem is Level))
                        {
                            continue;
                        }
                    }
                    m_LevelInfo = levelInfoPair.Value;
                    break;
                }
                //m_LevelInfo = levelInfos.Values.First<IFCLevelInfo>();
            }

            double       elevation      = m_LevelInfo.Elevation;
            IFCAnyHandle levelPlacement = m_LevelInfo.GetLocalPlacement();

            IFCFile file = exporterIFC.GetFile();

            Transform trf = Transform.Identity;

            if (familyTrf != null)
            {
                XYZ origin, xDir, yDir, zDir;

                xDir = familyTrf.BasisX; yDir = familyTrf.BasisY; zDir = familyTrf.BasisZ;

                Transform origOffsetTrf  = Transform.Identity;
                XYZ       negLevelOrigin = new XYZ(0, 0, -elevation);
                origOffsetTrf.Origin = negLevelOrigin;

                Transform newTrf = origOffsetTrf * familyTrf;

                origin = newTrf.Origin;

                trf.BasisX = xDir; trf.BasisY = yDir; trf.BasisZ = zDir;
                trf        = trf.Inverse;

                origin           = UnitUtil.ScaleLength(origin);
                m_LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir);
            }
            else if (orientationTrf != null)
            {
                XYZ origin, xDir, yDir, zDir;

                xDir = orientationTrf.BasisX; yDir = orientationTrf.BasisY; zDir = orientationTrf.BasisZ; origin = orientationTrf.Origin;

                XYZ levelOrigin = new XYZ(0, 0, elevation);
                origin = origin - levelOrigin;

                trf.BasisX = xDir; trf.BasisY = yDir; trf.BasisZ = zDir; trf.Origin = origin;
                trf        = trf.Inverse;

                origin           = UnitUtil.ScaleLength(origin);
                m_LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir);
            }
            else
            {
                m_LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, null, null, null);
            }

            Transform origOffsetTrf2  = Transform.Identity;
            XYZ       negLevelOrigin2 = new XYZ(0, 0, -elevation);

            origOffsetTrf2.Origin = negLevelOrigin2;
            Transform newTrf2 = trf * origOffsetTrf2;

            m_ExporterIFC.PushTransform(newTrf2);
            m_Offset  = elevation;
            m_LevelId = newLevelId;
        }
        /// <summary>
        /// Exports spatial elements, including rooms, areas and spaces. 1st level space boundaries.
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="spatialElement">
        /// The spatial element.
        /// </param>
        /// <param name="productWrapper">
        /// The ProductWrapper.
        /// </param>
        public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement spatialElement, ProductWrapper productWrapper)
        {
            IFCFile file = exporterIFC.GetFile();
            using (IFCTransaction transaction = new IFCTransaction(file))
            {
                using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, spatialElement, null, null))
                {
                    SpatialElementGeometryResults spatialElemGeomResult = null;
                    if (!CreateIFCSpace(exporterIFC, spatialElement, productWrapper, setter, out spatialElemGeomResult))
                        return;

                    bool isArea = (spatialElement is Area);

                    // Do not create boundary information for areas.
                    if (!isArea && (ExporterCacheManager.ExportOptionsCache.SpaceBoundaryLevel == 1))
                    {
                        Document document = spatialElement.Document;
                        ElementId levelId = spatialElement.LevelId;
                        IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);
                  double baseHeightNonScaled = (levelInfo != null) ? levelInfo.Elevation : 0.0;

                        try
                        {
                            // This can throw an exception.  If it does, continue to export element without boundary information.
                            // We will re-use the previously generated value, if we have it.
                            // TODO: warn user.
                            if (spatialElemGeomResult == null)
                                spatialElemGeomResult = s_SpatialElementGeometryCalculator.CalculateSpatialElementGeometry(spatialElement);

                            Solid spatialElemGeomSolid = spatialElemGeomResult.GetGeometry();
                            FaceArray faces = spatialElemGeomSolid.Faces;
                            foreach (Face face in faces)
                            {
                                IList<SpatialElementBoundarySubface> spatialElemBoundarySubfaces = spatialElemGeomResult.GetBoundaryFaceInfo(face);
                                foreach (SpatialElementBoundarySubface spatialElemBSubface in spatialElemBoundarySubfaces)
                                {
                                    if (spatialElemBSubface.SubfaceType == SubfaceType.Side)
                                        continue;

                                    if (spatialElemBSubface.GetSubface() == null)
                                        continue;

                                    ElementId elemId = spatialElemBSubface.SpatialBoundaryElement.LinkInstanceId;
                                    if (elemId == ElementId.InvalidElementId)
                                    {
                                        elemId = spatialElemBSubface.SpatialBoundaryElement.HostElementId;
                                    }

                                    Element boundingElement = document.GetElement(elemId);
                                    if (boundingElement == null)
                                        continue;

                                    bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement);

                                    IFCGeometryInfo info = IFCGeometryInfo.CreateSurfaceGeometryInfo(spatialElement.Document.Application.VertexTolerance);

                                    Face subFace = spatialElemBSubface.GetSubface();
                                    ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, subFace, XYZ.Zero, false);

                                    foreach (IFCAnyHandle surfaceHnd in info.GetSurfaces())
                                    {
                                        IFCAnyHandle connectionGeometry = IFCInstanceExporter.CreateConnectionSurfaceGeometry(file, surfaceHnd, null);

                                        SpaceBoundary spaceBoundary = new SpaceBoundary(spatialElement.Id, boundingElement.Id, setter.LevelId, connectionGeometry, IFCPhysicalOrVirtual.Physical,
                                            isObjectExt ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal);

                                        if (!ProcessIFCSpaceBoundary(exporterIFC, spaceBoundary, file))
                                            ExporterCacheManager.SpaceBoundaryCache.Add(spaceBoundary);
                                    }
                                }
                            }
                        }
                        catch
                        {
                        }

                        IList<IList<BoundarySegment>> roomBoundaries = spatialElement.GetBoundarySegments(GetSpatialElementBoundaryOptions(spatialElement));
                        double scaledRoomHeight = GetScaledHeight(spatialElement, levelId, levelInfo);
                        double unscaledHeight = UnitUtil.UnscaleLength(scaledRoomHeight);

                        Plane xyPlane = new Plane(XYZ.BasisZ, XYZ.Zero);

                        foreach (IList<BoundarySegment> roomBoundaryList in roomBoundaries)
                        {
                            foreach (BoundarySegment roomBoundary in roomBoundaryList)
                            {
                                Element boundingElement = roomBoundary.Element;

                                if (boundingElement == null)
                                    continue;

                                ElementId buildingElemId = boundingElement.Id;
                                Curve trimmedCurve = roomBoundary.Curve;

                                if (trimmedCurve == null)
                                    continue;

                                //trimmedCurve.Visibility = Visibility.Visible; readonly
                                IFCAnyHandle connectionGeometry = ExtrusionExporter.CreateConnectionSurfaceGeometry(
                                   exporterIFC, trimmedCurve, xyPlane, scaledRoomHeight, baseHeightNonScaled);

                                IFCPhysicalOrVirtual physOrVirt = IFCPhysicalOrVirtual.Physical;
                                if (boundingElement is CurveElement)
                                    physOrVirt = IFCPhysicalOrVirtual.Virtual;
                                else if (boundingElement is Autodesk.Revit.DB.Architecture.Room)
                                    physOrVirt = IFCPhysicalOrVirtual.NotDefined;

                                bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement);
                                bool isObjectPhys = (physOrVirt == IFCPhysicalOrVirtual.Physical);

                                ElementId actualBuildingElemId = isObjectPhys ? buildingElemId : ElementId.InvalidElementId;

                                SpaceBoundary spaceBoundary = new SpaceBoundary(spatialElement.Id, actualBuildingElemId, setter.LevelId, !IFCAnyHandleUtil.IsNullOrHasNoValue(connectionGeometry) ? connectionGeometry : null,
                                    physOrVirt, isObjectExt ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal);

                                if (!ProcessIFCSpaceBoundary(exporterIFC, spaceBoundary, file))
                                    ExporterCacheManager.SpaceBoundaryCache.Add(spaceBoundary);

                                // try to add doors and windows for host objects if appropriate.
                                if (isObjectPhys && boundingElement is HostObject)
                                {
                                    HostObject hostObj = boundingElement as HostObject;
                                    HashSet<ElementId> elemIds = new HashSet<ElementId>();
                                    elemIds.UnionWith(hostObj.FindInserts(false, false, false, false));
                                    if (elemIds.Count == 0)
                                    {
                                        CurtainGridSet curtainGridSet = CurtainSystemExporter.GetCurtainGridSet(hostObj);
                                        if (curtainGridSet != null)
                                        {
                                            foreach (CurtainGrid curtainGrid in curtainGridSet)
                                                elemIds.UnionWith(CurtainSystemExporter.GetVisiblePanelsForGrid(curtainGrid));
                                        }
                                    }

                                    foreach (ElementId elemId in elemIds)
                                    {
                                        // we are going to do a simple bbox export, not complicated geometry.
                                        Element instElem = document.GetElement(elemId);
                                        if (instElem == null)
                                            continue;

                                        BoundingBoxXYZ instBBox = instElem.get_BoundingBox(null);
                                        if (instBBox == null)
                                            continue;

                                        // make copy of original trimmed curve.
                                        Curve instCurve = trimmedCurve.Clone();
                                        XYZ instOrig = instCurve.GetEndPoint(0);

                                        // make sure that the insert is on this level.
                                        if (instBBox.Max.Z < instOrig.Z)
                                            continue;
                                        if (instBBox.Min.Z > instOrig.Z + unscaledHeight)
                                            continue;

                                        double insHeight = Math.Min(instBBox.Max.Z, instOrig.Z + unscaledHeight) - Math.Max(instOrig.Z, instBBox.Min.Z);
                                        if (insHeight < (1.0 / (12.0 * 16.0)))
                                            continue;

                                        // move base curve to bottom of bbox.
                                        XYZ moveDir = new XYZ(0.0, 0.0, instBBox.Min.Z - instOrig.Z);
                                        Transform moveTrf = Transform.CreateTranslation(moveDir);
                                        instCurve = instCurve.CreateTransformed(moveTrf);

                                        bool isHorizOrVert = false;
                                        if (instCurve is Line)
                                        {
                                            Line instLine = instCurve as Line;
                                            XYZ lineDir = instLine.Direction;
                                            if (MathUtil.IsAlmostEqual(Math.Abs(lineDir.X), 1.0) || (MathUtil.IsAlmostEqual(Math.Abs(lineDir.Y), 1.0)))
                                                isHorizOrVert = true;
                                        }

                                        double[] parameters = new double[2];
                                        double[] origEndParams = new double[2];
                                        bool paramsSet = false;

                                        if (!isHorizOrVert)
                                        {
                                            FamilyInstance famInst = instElem as FamilyInstance;
                                            if (famInst == null)
                                                continue;

                                            ElementType elementType = document.GetElement(famInst.GetTypeId()) as ElementType;
                                            if (elementType == null)
                                                continue;

                                            BoundingBoxXYZ symBBox = elementType.get_BoundingBox(null);
                                            if (symBBox != null)
                                            {
                                                Curve symCurve = trimmedCurve.Clone();
                                                Transform trf = famInst.GetTransform();
                                                Transform invTrf = trf.Inverse;
                                                Curve trfCurve = symCurve.CreateTransformed(invTrf);
                                                parameters[0] = trfCurve.Project(symBBox.Min).Parameter;
                                                parameters[1] = trfCurve.Project(symBBox.Max).Parameter;
                                                paramsSet = true;
                                            }
                                        }

                                        if (!paramsSet)
                                        {
                                            parameters[0] = instCurve.Project(instBBox.Min).Parameter;
                                            parameters[1] = instCurve.Project(instBBox.Max).Parameter;
                                        }

                                        // ignore if less than 1/16".
                                        if (Math.Abs(parameters[1] - parameters[0]) < 1.0 / (12.0 * 16.0))
                                            continue;
                                        if (parameters[0] > parameters[1])
                                        {
                                            //swap
                                            double tempParam = parameters[0];
                                            parameters[0] = parameters[1];
                                            parameters[1] = tempParam;
                                        }

                                        origEndParams[0] = instCurve.GetEndParameter(0);
                                        origEndParams[1] = instCurve.GetEndParameter(1);

                                        if (origEndParams[0] > parameters[1] - (1.0 / (12.0 * 16.0)))
                                            continue;
                                        if (origEndParams[1] < parameters[0] + (1.0 / (12.0 * 16.0)))
                                            continue;

                                        instCurve.MakeBound(parameters[0] > origEndParams[0] ? parameters[0] : origEndParams[0],
                                                            parameters[1] < origEndParams[1] ? parameters[1] : origEndParams[1]);

                                        double insHeightScaled = UnitUtil.ScaleLength(insHeight);
                                        IFCAnyHandle insConnectionGeom = ExtrusionExporter.CreateConnectionSurfaceGeometry(exporterIFC, instCurve, xyPlane,
                                           insHeightScaled, baseHeightNonScaled);

                                        SpaceBoundary instBoundary = new SpaceBoundary(spatialElement.Id, elemId, setter.LevelId, !IFCAnyHandleUtil.IsNullOrHasNoValue(insConnectionGeom) ? insConnectionGeom : null, physOrVirt,
                                            isObjectExt ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal);
                                        if (!ProcessIFCSpaceBoundary(exporterIFC, instBoundary, file))
                                            ExporterCacheManager.SpaceBoundaryCache.Add(instBoundary);
                                    }
                                }
                            }
                        }
                    }

                    CreateZoneInfos(exporterIFC, file, spatialElement, productWrapper);
                    CreateSpaceOccupantInfo(exporterIFC, file, spatialElement, productWrapper);
                }
                transaction.Commit();
            }
        }
        /// <summary>
        /// Creates IFC room/space/area item, not include boundaries. 
        /// </summary>
        /// <param name="exporterIFC">The ExporterIFC object.</param>
        /// <param name="spatialElement">The spatial element.</param>
        /// <param name="productWrapper">The ProductWrapper.</param>
        /// <param name="setter">The PlacementSetter.</param>
        /// <returns>True if created successfully, false otherwise.</returns>
        static bool CreateIFCSpace(ExporterIFC exporterIFC, SpatialElement spatialElement, ProductWrapper productWrapper, 
            PlacementSetter setter, out SpatialElementGeometryResults results)
        {
            results = null;

            IList<CurveLoop> curveLoops = null;
            try
            {
                // Avoid throwing for a spatial element with no location.
                if (spatialElement.Location == null)
                    return false;

                SpatialElementBoundaryOptions options = GetSpatialElementBoundaryOptions(spatialElement);
                curveLoops = ExporterIFCUtils.GetRoomBoundaryAsCurveLoopArray(spatialElement, options, true);
            }
            catch (Autodesk.Revit.Exceptions.InvalidOperationException)
            {
                //Some spatial elements are not placed that have no boundary loops. Don't export them.
                return false;
            }

            Autodesk.Revit.DB.Document document = spatialElement.Document;
            ElementId levelId = spatialElement.LevelId;

            ElementId catId = spatialElement.Category != null ? spatialElement.Category.Id : ElementId.InvalidElementId;

            double dArea = 0.0;
            if (ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_AREA, out dArea) != null)
                dArea = UnitUtil.ScaleArea(dArea);

            IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);

            string strSpaceNumber = null;
            string strSpaceName = null;
            string strSpaceDesc = null;

            if (ParameterUtil.GetStringValueFromElement(spatialElement, BuiltInParameter.ROOM_NUMBER, out strSpaceNumber) == null)
                strSpaceNumber = null;

            if (ParameterUtil.GetStringValueFromElement(spatialElement, BuiltInParameter.ROOM_NAME, out strSpaceName) == null)
                strSpaceName = null;

            if (ParameterUtil.GetStringValueFromElement(spatialElement, BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS, out strSpaceDesc) == null)
                strSpaceDesc = null;

            string name = strSpaceNumber;
            string longName = strSpaceName;
            string desc = strSpaceDesc;

            IFCFile file = exporterIFC.GetFile();

            IFCAnyHandle localPlacement = setter.LocalPlacement;
            ElementType elemType = document.GetElement(spatialElement.GetTypeId()) as ElementType;
            IFCInternalOrExternal internalOrExternal = CategoryUtil.IsElementExternal(spatialElement) ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal;

            double scaledRoomHeight = GetScaledHeight(spatialElement, levelId, levelInfo);
            if (scaledRoomHeight <= 0.0)
                return false;

            double bottomOffset;
            ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_LOWER_OFFSET, out bottomOffset);

         double elevation = (levelInfo != null) ? levelInfo.Elevation : 0.0;
            XYZ zDir = new XYZ(0, 0, 1);
         XYZ orig = new XYZ(0, 0, elevation + bottomOffset);

            Plane plane = new Plane(zDir, orig); // room calculated as level offset.

            GeometryElement geomElem = null;
            bool isArea = (spatialElement is Area);
            Area spatialElementAsArea = isArea ? (spatialElement as Area) : null;

            if (spatialElement is Autodesk.Revit.DB.Architecture.Room)
            {
                Autodesk.Revit.DB.Architecture.Room room = spatialElement as Autodesk.Revit.DB.Architecture.Room;
                geomElem = room.ClosedShell;
            }
            else if (spatialElement is Autodesk.Revit.DB.Mechanical.Space)
            {
                Autodesk.Revit.DB.Mechanical.Space space = spatialElement as Autodesk.Revit.DB.Mechanical.Space;
                geomElem = space.ClosedShell;
            }
            else if (isArea)
            {
                Options geomOptions = GeometryUtil.GetIFCExportGeometryOptions();
                geomElem = spatialElementAsArea.get_Geometry(geomOptions);
            }

            IFCAnyHandle spaceHnd = null;
            string spatialElementName = null;
            using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData())
            {
                extraParams.SetLocalPlacement(localPlacement);
                extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ;

                using (IFCTransaction transaction2 = new IFCTransaction(file))
                {
                    IFCAnyHandle repHnd = null;
                    if (!ExporterCacheManager.ExportOptionsCache.Use2DRoomBoundaryForRoomVolumeCreation && geomElem != null)
                    {
                        BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true);
                        bodyExporterOptions.TessellationLevel = BodyExporter.GetTessellationLevel();
                        repHnd = RepresentationUtil.CreateAppropriateProductDefinitionShape(exporterIFC, spatialElement,
                            catId, geomElem, bodyExporterOptions, null, extraParams, false);
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(repHnd))
                            extraParams.ClearOpenings();
                    }
                    else
                    {
                        IFCAnyHandle shapeRep = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, curveLoops, plane, zDir, scaledRoomHeight);
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep))
                            return false;
                        IFCAnyHandle styledItemHnd = BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document,
                            shapeRep, ElementId.InvalidElementId);

                        HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>();
                        bodyItems.Add(shapeRep);
                        shapeRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, spatialElement, catId, exporterIFC.Get3DContextHandle("Body"), bodyItems, null);
                        IList<IFCAnyHandle> shapeReps = new List<IFCAnyHandle>();
                        shapeReps.Add(shapeRep);

                        IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geomElem, Transform.Identity);
                        if (boundingBoxRep != null)
                            shapeReps.Add(boundingBoxRep);

                        repHnd = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapeReps);
                    }

                    extraParams.ScaledHeight = scaledRoomHeight;
                    extraParams.ScaledArea = dArea;

                    spatialElementName = NamingUtil.GetNameOverride(spatialElement, name);
                    string spatialElementDescription = NamingUtil.GetDescriptionOverride(spatialElement, desc);
                    string spatialElementObjectType = NamingUtil.GetObjectTypeOverride(spatialElement, null);
                    string spatialElementLongName = NamingUtil.GetLongNameOverride(spatialElement, longName);
                    
                    double? spaceElevationWithFlooring = null;
                    double elevationWithFlooring = 0.0;
                    if (ParameterUtil.GetDoubleValueFromElement(spatialElement, null, "IfcElevationWithFlooring", out elevationWithFlooring) != null)
                        spaceElevationWithFlooring = UnitUtil.ScaleLength(elevationWithFlooring);
                    spaceHnd = IFCInstanceExporter.CreateSpace(file, GUIDUtil.CreateGUID(spatialElement),
                                                  ExporterCacheManager.OwnerHistoryHandle,
                                                  spatialElementName, spatialElementDescription, spatialElementObjectType,
                                                  extraParams.GetLocalPlacement(), repHnd, spatialElementLongName, Toolkit.IFCElementComposition.Element,
                                                  internalOrExternal, spaceElevationWithFlooring);

                    transaction2.Commit();
                }

                if (spaceHnd != null)
                {
                    productWrapper.AddSpace(spatialElement, spaceHnd, levelInfo, extraParams, true);
                    if (isArea)
                    {
                        Element areaScheme = spatialElementAsArea.AreaScheme;
                        if (areaScheme != null)
                        {
                            ElementId areaSchemeId = areaScheme.Id;
                            HashSet<IFCAnyHandle> areas = null;
                            if (!ExporterCacheManager.AreaSchemeCache.TryGetValue(areaSchemeId, out areas))
                            {
                                areas = new HashSet<IFCAnyHandle>();
                                ExporterCacheManager.AreaSchemeCache[areaSchemeId] = areas;
                            }
                            areas.Add(spaceHnd);
                        }
                    }
                }
            }

            // Save room handle for later use/relationships
            ExporterCacheManager.SpaceInfoCache.SetSpaceHandle(spatialElement, spaceHnd);

            // Find Ceiling as a Space boundary and keep the relationship in a cache for use later
            bool ret = GetCeilingSpaceBoundary(spatialElement, out results);

         if (!MathUtil.IsAlmostZero(dArea) && !(ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE) &&
                !ExporterCacheManager.ExportOptionsCache.ExportAs2x3CoordinationView2 && !ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities)
            {
                bool isDesignGrossArea = (string.Compare(spatialElementName, "GSA Design Gross Area") > 0);
                PropertyUtil.CreatePreCOBIEGSAQuantities(exporterIFC, spaceHnd, "GSA Space Areas", (isDesignGrossArea ? "GSA Design Gross Area" : "GSA BIM Area"), dArea);
            }

            // Export Classifications for SpatialElement for GSA/COBIE.
         if (ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE)
            {
                ProjectInfo projectInfo = document.ProjectInformation;
                if (projectInfo != null)
                    CreateCOBIESpaceClassifications(exporterIFC, file, spaceHnd, projectInfo, spatialElement);
            }

            return true;
        }
      /// <summary>
      /// Attempt to determine the local placement of the element based on the element type and initial input.
      /// </summary>
      /// <param name="exporterIFC">The ExporterIFC class.</param>
      /// <param name="elem">The element being exported.</param>
      /// <param name="familyTrf">The optional family transform.</param>
      /// <param name="orientationTrf">The optional orientation of the element based on IFC standards or agreements.</param>
      /// <param name="overrideLevelId">The optional level to place the element, to be used instead of heuristics.</param>
      private void commonInit(ExporterIFC exporterIFC, Element elem, Transform familyTrf, Transform orientationTrf, ElementId overrideLevelId)
      {
         ExporterIFC = exporterIFC;

         // Convert null value to InvalidElementId.
         if (overrideLevelId == null)
            overrideLevelId = ElementId.InvalidElementId;

         Document doc = elem.Document;
         Element hostElem = elem;
         ElementId elemId = elem.Id;
         ElementId newLevelId = overrideLevelId;

         bool useOverrideOrigin = false;
         XYZ overrideOrigin = XYZ.Zero;

         IDictionary<ElementId, IFCLevelInfo> levelInfos = exporterIFC.GetLevelInfos();

         if (overrideLevelId == ElementId.InvalidElementId)
         {
            if (familyTrf == null)
            {
               // Override for CurveElems -- base level calculation on origin of sketch Plane.
               if (elem is CurveElement)
               {
                  SketchPlane sketchPlane = (elem as CurveElement).SketchPlane;
                  if (sketchPlane != null)
                  {
                     useOverrideOrigin = true;
                     overrideOrigin = sketchPlane.GetPlane().Origin;
                  }
               }
               else
               {
                  ElementId hostElemId = ElementId.InvalidElementId;
                  // a bit of a hack.  If we have a railing, we want it to have the same level base as its host Stair (because of
                  // the way the stairs place railings and stair flights together).
                  if (elem is Railing)
                  {
                     hostElemId = (elem as Railing).HostId;
                  }
                  else if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Assemblies)
                  {
                     hostElemId = elem.AssemblyInstanceId;
                  }

                  if (hostElemId != ElementId.InvalidElementId)
                  {
                     hostElem = doc.GetElement(hostElemId);
                  }

                  newLevelId = hostElem != null ? hostElem.LevelId : ElementId.InvalidElementId;

                  if (newLevelId == ElementId.InvalidElementId)
                  {
                     ExporterIFCUtils.GetLevelIdByHeight(exporterIFC, hostElem);
                  }
               }
            }

            // todo: store.
            double bottomHeight = double.MaxValue;
            ElementId bottomLevelId = ElementId.InvalidElementId;
            if ((newLevelId == ElementId.InvalidElementId) || orientationTrf != null)
            {
               // if we have a trf, it might geometrically push the instance to a new level.  Check that case.
               // actually, we should ALWAYS check the bbox vs the settings
               newLevelId = ElementId.InvalidElementId;
               XYZ originToUse = XYZ.Zero;
               bool originIsValid = useOverrideOrigin;

               if (useOverrideOrigin)
               {
                  originToUse = overrideOrigin;
               }
               else
               {
                  BoundingBoxXYZ bbox = elem.get_BoundingBox(null);
                  if (bbox != null)
                  {
                     originToUse = bbox.Min;
                     originIsValid = true;
                  }
                  else if (hostElem.Id != elemId)
                  {
                     bbox = hostElem.get_BoundingBox(null);
                     if (bbox != null)
                     {
                        originToUse = bbox.Min;
                        originIsValid = true;
                     }
                  }
               }


               // The original heuristic here was that the origin determined the level containment based on exact location:
               // if the Z of the origin was higher than the current level but lower than the next level, it was contained
               // on that level.
               // However, in some places (e.g. Germany), the containment is thought to start just below the level, because floors
               // are placed before the level, not above.  So we have made a small modification so that anything within
               // 10cm of the 'next' level is on that level.

               double leveExtension = 10.0 / (12.0 * 2.54);
               foreach (KeyValuePair<ElementId, IFCLevelInfo> levelInfoPair in levelInfos)
               {
                  // the cache contains levels from all the exported documents
                  // if the export is performed for a linked document, filter the levels that are not from this document
                  if (ExporterCacheManager.ExportOptionsCache.ExportingLink)
                  {
                     Element levelElem = doc.GetElement(levelInfoPair.Key);
                     if (levelElem == null || !(levelElem is Level))
                        continue;
                  }

                  IFCLevelInfo levelInfo = levelInfoPair.Value;
                  double startHeight = levelInfo.Elevation - leveExtension;
                  double height = levelInfo.DistanceToNextLevel;
                  bool useHeight = !MathUtil.IsAlmostZero(height);
                  double endHeight = startHeight + height;

                  if (originIsValid && ((originToUse[2] > (startHeight - MathUtil.Eps())) && (!useHeight || originToUse[2] < (endHeight - MathUtil.Eps()))))
                  {
                     newLevelId = levelInfoPair.Key;
                  }

                  if (startHeight < (bottomHeight + MathUtil.Eps()))
                  {
                     bottomLevelId = levelInfoPair.Key;
                     bottomHeight = startHeight;
                  }
               }
            }

            if (newLevelId == ElementId.InvalidElementId)
               newLevelId = bottomLevelId;
         }

         LevelInfo = exporterIFC.GetLevelInfo(newLevelId);
         if (LevelInfo == null)
         {
            foreach (KeyValuePair<ElementId, IFCLevelInfo> levelInfoPair in levelInfos)
            {
               // the cache contains levels from all the exported documents
               // if the export is performed for a linked document, filter the levels that are not from this document
               if (ExporterCacheManager.ExportOptionsCache.ExportingLink)
               {
                  Element levelElem = doc.GetElement(levelInfoPair.Key);
                  if (levelElem == null || !(levelElem is Level))
                     continue;
               }
               LevelInfo = levelInfoPair.Value;
               break;
            }
            //LevelInfo = levelInfos.Values.First<IFCLevelInfo>();
         }

         double elevation = (LevelInfo != null) ? LevelInfo.Elevation : 0.0;
         IFCAnyHandle levelPlacement = (LevelInfo != null) ? LevelInfo.GetLocalPlacement() : null;

         IFCFile file = exporterIFC.GetFile();

         Transform trf = Transform.Identity;

         if (familyTrf != null)
         {
            XYZ origin, xDir, yDir, zDir;

            xDir = familyTrf.BasisX; yDir = familyTrf.BasisY; zDir = familyTrf.BasisZ;

            Transform origOffsetTrf = Transform.Identity;
            XYZ negLevelOrigin = new XYZ(0, 0, -elevation);
            origOffsetTrf.Origin = negLevelOrigin;

            Transform newTrf = origOffsetTrf * familyTrf;

            origin = newTrf.Origin;

            trf.BasisX = xDir; trf.BasisY = yDir; trf.BasisZ = zDir;
            trf = trf.Inverse;

            origin = UnitUtil.ScaleLength(origin);
            LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir);
         }
         else if (orientationTrf != null)
         {
            XYZ origin, xDir, yDir, zDir;

            xDir = orientationTrf.BasisX; yDir = orientationTrf.BasisY; zDir = orientationTrf.BasisZ; origin = orientationTrf.Origin;

            XYZ levelOrigin = new XYZ(0, 0, elevation);
            origin = origin - levelOrigin;

            trf.BasisX = xDir; trf.BasisY = yDir; trf.BasisZ = zDir; trf.Origin = origin;
            trf = trf.Inverse;

            origin = UnitUtil.ScaleLength(origin);
            LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, origin, zDir, xDir);
         }
         else
         {
            LocalPlacement = ExporterUtil.CreateLocalPlacement(file, levelPlacement, null, null, null);
         }

         Transform origOffsetTrf2 = Transform.Identity;
         XYZ negLevelOrigin2 = new XYZ(0, 0, -elevation);
         origOffsetTrf2.Origin = negLevelOrigin2;
         Transform newTrf2 = trf * origOffsetTrf2;

         ExporterIFC.PushTransform(newTrf2);
         Offset = elevation;
         LevelId = newLevelId;
      }
        /// <summary>
        /// Creates IFC room/space/area item, not include boundaries. 
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="spatialElement">
        /// The spatial element.
        /// </param>
        /// <param name="productWrapper">
        /// The ProductWrapper.
        /// </param>
        /// <param name="setter">
        /// The IFCPlacementSetter.
        /// </param>
        /// <returns>
        /// True if created successfully, false otherwise.
        /// </returns>
        static bool CreateIFCSpace(ExporterIFC exporterIFC, SpatialElement spatialElement, ProductWrapper productWrapper, IFCPlacementSetter setter)
        {
            IList<CurveLoop> curveLoops = null;
            try
            {
                SpatialElementBoundaryOptions options = ExporterIFCUtils.GetSpatialElementBoundaryOptions(exporterIFC, spatialElement);
                curveLoops = ExporterIFCUtils.GetRoomBoundaryAsCurveLoopArray(spatialElement, options, true);
            }
            catch (Autodesk.Revit.Exceptions.InvalidOperationException)
            {
                //Some spatial elements are not placed that have no boundary loops. Don't export them.
                return false;
            }

            Autodesk.Revit.DB.Document document = spatialElement.Document;
            ElementId levelId = spatialElement.Level != null ? spatialElement.Level.Id : ElementId.InvalidElementId;
            double scale = exporterIFC.LinearScale;

            ElementId catId = spatialElement.Category != null ? spatialElement.Category.Id : ElementId.InvalidElementId;

            double dArea = 0.0;
            if (ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_AREA, out dArea))
                dArea *= (scale * scale);

            IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);

            string strSpaceNumber = null;
            string strSpaceName = null;
            string strSpaceDesc = null;

            bool isArea = spatialElement is Area;
            if (!isArea)
            {
                if (!ParameterUtil.GetStringValueFromElement(spatialElement, BuiltInParameter.ROOM_NUMBER, out strSpaceNumber))
                    strSpaceNumber = null;

                if (!ParameterUtil.GetStringValueFromElement(spatialElement, BuiltInParameter.ROOM_NAME, out strSpaceName))
                    strSpaceName = null;

                if (!ParameterUtil.GetStringValueFromElement(spatialElement, BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS, out strSpaceDesc))
                    strSpaceDesc = null;
            }
            else
            {
                // Default to true to preserve previous behavior.
                bool? exportGSADesignGrossArea = ExporterCacheManager.ExportOptionsCache.ExportGSAGrossDesignArea;
                if (!exportGSADesignGrossArea.HasValue || exportGSADesignGrossArea.Value)
                {
                    Element level = document.GetElement(levelId);
                    if (level != null)
                    {
                        strSpaceNumber = level.Name + " GSA Design Gross Area";
                    }
                }
            }

            string name = strSpaceNumber;
            string longName = strSpaceName;
            string desc = strSpaceDesc;

            IFCFile file = exporterIFC.GetFile();

            IFCAnyHandle localPlacement = setter.GetPlacement();
            ElementType elemType = document.GetElement(spatialElement.GetTypeId()) as ElementType;
            IFCInternalOrExternal internalOrExternal = CategoryUtil.IsElementExternal(spatialElement, true) ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal;

            double roomHeight = 0.0;

            roomHeight = GetHeight(spatialElement, scale, levelId, levelInfo);
            if (roomHeight <= 0.0)
                return false;

            double bottomOffset;
            ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_LOWER_OFFSET, out bottomOffset);

            XYZ zDir = new XYZ(0, 0, 1);
            XYZ orig = new XYZ(0, 0, levelInfo.Elevation + bottomOffset);

            Plane plane = new Plane(zDir, orig); // room calculated as level offset.

            GeometryElement geomElem = null;
            if (spatialElement is Autodesk.Revit.DB.Architecture.Room)
            {
                Autodesk.Revit.DB.Architecture.Room room = spatialElement as Autodesk.Revit.DB.Architecture.Room;
                geomElem = room.ClosedShell;
            }
            else if (spatialElement is Autodesk.Revit.DB.Mechanical.Space)
            {
                Autodesk.Revit.DB.Mechanical.Space space = spatialElement as Autodesk.Revit.DB.Mechanical.Space;
                geomElem = space.ClosedShell;
            }

            IFCAnyHandle spaceHnd = null;
            using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData())
            {
                extraParams.SetLocalPlacement(localPlacement);
                extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ;

                using (IFCTransaction transaction2 = new IFCTransaction(file))
                {
                    IFCAnyHandle repHnd = null;
                    if (!ExporterCacheManager.ExportOptionsCache.Use2DRoomBoundaryForRoomVolumeCreation && geomElem != null)
                    {
                        BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true);
                        bodyExporterOptions.TessellationLevel = BodyExporterOptions.BodyTessellationLevel.Coarse;
                        repHnd = RepresentationUtil.CreateAppropriateProductDefinitionShape(exporterIFC, spatialElement,
                            catId, geomElem, bodyExporterOptions, null, extraParams);
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(repHnd))
                            extraParams.ClearOpenings();
                    }
                    else
                    {
                        IFCAnyHandle shapeRep = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, curveLoops, plane, zDir, roomHeight);
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep))
                            return false;
                        IFCAnyHandle styledItemHnd = BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document,
                            shapeRep, ElementId.InvalidElementId);

                        HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>();
                        bodyItems.Add(shapeRep);
                        shapeRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, spatialElement, catId, exporterIFC.Get3DContextHandle("Body"), bodyItems, null);
                        IList<IFCAnyHandle> shapeReps = new List<IFCAnyHandle>();
                        shapeReps.Add(shapeRep);

                        IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geomElem, Transform.Identity);
                        if (boundingBoxRep != null)
                            shapeReps.Add(boundingBoxRep);

                        repHnd = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapeReps);
                    }

                    extraParams.ScaledHeight = roomHeight;
                    extraParams.ScaledArea = dArea;

                    string spatialElementName = NamingUtil.GetNameOverride(spatialElement, name);
                    string spatialElementDescription = NamingUtil.GetDescriptionOverride(spatialElement, desc);
                    string spatialElementObjectType = NamingUtil.GetObjectTypeOverride(spatialElement, null);

                    double? spaceElevationWithFlooring = null;
                    double elevationWithFlooring = 0.0;
                    if (ParameterUtil.GetDoubleValueFromElement(spatialElement, "IfcElevationWithFlooring", out elevationWithFlooring) == true)
                        spaceElevationWithFlooring = elevationWithFlooring;
                    spaceHnd = IFCInstanceExporter.CreateSpace(file, GUIDUtil.CreateGUID(spatialElement),
                                                  exporterIFC.GetOwnerHistoryHandle(),
                                                  spatialElementName,spatialElementDescription, spatialElementObjectType,
                                                  extraParams.GetLocalPlacement(), repHnd, longName, Toolkit.IFCElementComposition.Element,
                                                  internalOrExternal, spaceElevationWithFlooring);

                    transaction2.Commit();
                }

                productWrapper.AddSpace(spaceHnd, levelInfo, extraParams, true);
            }

            // Save room handle for later use/relationships
            ExporterCacheManager.SpatialElementHandleCache.Register(spatialElement.Id, spaceHnd);
            exporterIFC.RegisterSpatialElementHandle(spatialElement.Id, spaceHnd);

            // Find Ceiling as a Space boundary and keep the relationship in a cache for use later
            Boolean ret = getCeilingSpaceBoundary(spatialElement);

            if (!MathUtil.IsAlmostZero(dArea) && !(ExporterCacheManager.ExportOptionsCache.FileVersion == IFCVersion.IFCCOBIE) &&
                !ExporterCacheManager.ExportOptionsCache.ExportAs2x3CoordinationView2 && !ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities)
            {
                ExporterIFCUtils.CreatePreCOBIEGSAQuantities(exporterIFC, spaceHnd, "GSA Space Areas", (isArea ? "GSA Design Gross Area" : "GSA BIM Area"), dArea);
            }

            // Export BaseQuantities for SpatialElement
            if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities && !(ExporterCacheManager.ExportOptionsCache.FileVersion == IFCVersion.IFCCOBIE))
            {
                // Skip this step. The "standard" quantities will be exported at the end of export element process in exportElement (Exporter.cs)
                // ExporterIFCUtils.CreateNonCOBIERoomQuantities(exporterIFC, spaceHnd, spatialElement, dArea, roomHeight);
            }

            // Create general classification for Spatial element from ClassificationCode(s). This is not done here but rather at the end of exportElement process
            // ClassificationUtil.CreateClassification(exporterIFC, file, spatialElement, spaceHnd, "");

            // Export Classifications for SpatialElement for GSA/COBIE.
            if (ExporterCacheManager.ExportOptionsCache.FileVersion == IFCVersion.IFCCOBIE)
            {
                CreateCOBIESpaceClassifications(exporterIFC, file, spaceHnd, document.ProjectInformation, spatialElement);
            }

            return true;
        }
        /// <summary>
        /// Create IFC room/space/area item, not include boundaries. 
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="spatialElement">
        /// The spatial element.
        /// </param>
        /// <param name="productWrapper">
        /// The IFCProductWrapper.
        /// </param>
        /// <param name="setter">
        /// The IFCPlacementSetter.
        /// </param>
        /// <returns>
        /// True if created successfully, false otherwise.
        /// </returns>
        static void CreateIFCSpace(ExporterIFC exporterIFC, SpatialElement spatialElement, IFCProductWrapper productWrapper, IFCPlacementSetter setter)
        {
            Autodesk.Revit.DB.Document document = spatialElement.Document;
            ElementId levelId = spatialElement.Level != null ? spatialElement.Level.Id : ElementId.InvalidElementId;
            double scale = exporterIFC.LinearScale;

            ElementId catId = spatialElement.Category != null ? spatialElement.Category.Id : ElementId.InvalidElementId;

            double dArea = 0.0;

            Parameter param = spatialElement.get_Parameter(BuiltInParameter.ROOM_AREA);
            if (param != null)
            {
                dArea = param.AsDouble();
                dArea *= (scale * scale);
            }


            SpatialElementBoundaryOptions options = ExporterIFCUtils.GetSpatialElementBoundaryOptions(exporterIFC, spatialElement);
            IList<CurveLoop> curveLoops = ExporterIFCUtils.GetRoomBoundaryAsCurveLoopArray(spatialElement, options, true);

            IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);

            string strSpaceNumber = null;
            string strSpaceName = null;
            string strSpaceDesc = null;

            bool isArea = spatialElement is Area;
            if (!isArea)
            {
                Parameter paramRoomNum = spatialElement.get_Parameter(BuiltInParameter.ROOM_NUMBER);
                if (paramRoomNum != null)
                {
                    strSpaceNumber = paramRoomNum.AsString();
                }

                Parameter paramRoomName = spatialElement.get_Parameter(BuiltInParameter.ROOM_NAME);
                if (paramRoomName != null)
                {
                    strSpaceName = paramRoomName.AsString();
                }

                Parameter paramRoomComm = spatialElement.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);
                if (paramRoomComm != null)
                {
                    strSpaceDesc = paramRoomComm.AsString();
                }
            }
            else
            {
                Element level = document.get_Element(levelId);
                if (level != null)
                {
                    strSpaceNumber = level.Name + " GSA Design Gross Area";
                }
            }

            //assign empty string if it is null
            if (strSpaceNumber == null) strSpaceNumber = "";
            if (strSpaceName == null) strSpaceName = "";
            if (strSpaceDesc == null) strSpaceDesc = "";
            IFCLabel name = IFCLabel.Create(strSpaceNumber);
            IFCLabel longName = IFCLabel.Create(strSpaceName);
            IFCLabel desc = IFCLabel.Create(strSpaceDesc);

            IFCFile file = exporterIFC.GetFile();

            IFCAnyHandle localPlacement = setter.GetPlacement();
            ElementType elemType = document.get_Element(spatialElement.GetTypeId()) as ElementType;
            bool isObjectExternal = CategoryUtil.IsElementExternal(spatialElement);
            IFCMeasureValue elevationWithFlooring = IFCMeasureValue.Create();

            double roomHeight = 0.0;

            roomHeight = GetHeight(spatialElement, scale, levelInfo);

            double bottomOffset = 0.0;
            Parameter paramBottomOffset = spatialElement.get_Parameter(BuiltInParameter.ROOM_LOWER_OFFSET);
            bottomOffset = paramBottomOffset != null ? paramBottomOffset.AsDouble() : 0.0;

            XYZ zDir = new XYZ(0, 0, 1);
            XYZ orig = new XYZ(0, 0, levelInfo.Elevation + bottomOffset);

            Plane plane = new Plane(zDir, orig); // room calculated as level offset.

            GeometryElement geomElem = null;
            if (spatialElement is Autodesk.Revit.DB.Architecture.Room)
            {
                Autodesk.Revit.DB.Architecture.Room room = spatialElement as Autodesk.Revit.DB.Architecture.Room;
                geomElem = room.ClosedShell;
            }
            else if (spatialElement is Autodesk.Revit.DB.Mechanical.Space)
            {
                Autodesk.Revit.DB.Mechanical.Space space = spatialElement as Autodesk.Revit.DB.Mechanical.Space;
                geomElem = space.ClosedShell;
            }

            IFCAnyHandle spaceHnd = null;
            IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData();
            extraParams.SetLocalPlacement(localPlacement);
            extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ;

            using (IFCTransaction tr2 = new IFCTransaction(file))
            {
                IFCAnyHandle repHnd = null;
                if (!(exporterIFC.ExportAs2x2 || Use2DRoomBoundaryForRoomVolumeCalculation()) && geomElem != null)
                {

                    IFCSolidMeshGeometryInfo solidMeshInfo = ExporterIFCUtils.GetSolidMeshGeometry(exporterIFC, geomElem, Transform.Identity);
                    IList<Solid> solids = solidMeshInfo.GetSolids();
                    IList<Mesh> polyMeshes = solidMeshInfo.GetMeshes();

                    bool tryToExportAsExtrusion = true;
                    if (solids.Count != 1 || polyMeshes.Count != 0)
                        tryToExportAsExtrusion = false;

                    IList<GeometryObject> geomObjects = new List<GeometryObject>();

                    foreach (Solid solid in solids)
                        geomObjects.Add(solid);

                    IFCAnyHandle shapeRep = BodyExporter.ExportBody(spatialElement.Document.Application, exporterIFC, catId, geomObjects, tryToExportAsExtrusion, extraParams);
                    IList<IFCAnyHandle> shapeReps = new List<IFCAnyHandle>();
                    shapeReps.Add(shapeRep);
                    repHnd = file.CreateProductDefinitionShape(IFCLabel.Create(), IFCLabel.Create(), shapeReps);
                }
                else
                {
                    IFCAnyHandle shapeRep = file.CreateExtrudedSolidFromCurveLoop(exporterIFC, catId, curveLoops, plane, zDir, roomHeight); //pScaledOrig?
                    HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>();
                    bodyItems.Add(shapeRep);
                    shapeRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, catId, exporterIFC.Get3DContextHandle(), bodyItems, IFCAnyHandle.Create());
                    IList<IFCAnyHandle> shapeReps = new List<IFCAnyHandle>();
                    shapeReps.Add(shapeRep);
                    repHnd = file.CreateProductDefinitionShape(IFCLabel.Create(), IFCLabel.Create(), shapeReps);
                }

                extraParams.ScaledHeight = roomHeight;
                extraParams.ScaledArea = dArea;

                spaceHnd = file.CreateSpace(IFCLabel.CreateGUID(spatialElement),
                                                  exporterIFC.GetOwnerHistoryHandle(),
                                                  NamingUtil.GetNameOverride(spatialElement, name),
                                                  NamingUtil.GetDescriptionOverride(spatialElement, desc),
                                                  NamingUtil.GetObjectTypeOverride(spatialElement, IFCLabel.Create()),
                                                  extraParams.GetLocalPlacement(), repHnd, longName, IFCElementComposition.Element
                                                  , isObjectExternal, elevationWithFlooring);
                tr2.Commit();
            }

            productWrapper.AddSpace(spaceHnd, levelInfo, extraParams, true);

            // Save room handle for later use/relationships
            exporterIFC.RegisterSpatialElementHandle(spatialElement.Id, spaceHnd);

            if (!MathUtil.IsAlmostZero(dArea) && !(exporterIFC.FileVersion == IFCVersion.IFCCOBIE))
            {
                ExporterIFCUtils.CreatePreCOBIEGSAQuantities(exporterIFC, spaceHnd, "GSA Space Areas", (isArea ? "GSA Design Gross Area" : "GSA BIM Area"), dArea);
            }

            // Export BaseQuantities for RoomElem
            if (exporterIFC.ExportBaseQuantities && !(exporterIFC.FileVersion == IFCVersion.IFCCOBIE))
            {
                ExporterIFCUtils.CreateNonCOBIERoomQuantities(exporterIFC, spaceHnd, spatialElement, dArea, roomHeight);
            }
        }
        /// <summary>
        /// Export spatial elements, including rooms, areas and spaces. 1st level space boundaries.
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="spatialElement">
        /// The spatial element.
        /// </param>
        /// <param name="productWrapper">
        /// The IFCProductWrapper.
        /// </param>
        public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement spatialElement, IFCProductWrapper productWrapper)
        {
            //quick reject
            bool isArea = spatialElement is Area;
            if (isArea)
            {
                if (!IsAreaGrossInterior(exporterIFC, spatialElement))
                    return;
            }

            IFCFile file = exporterIFC.GetFile();
            using (IFCTransaction tr = new IFCTransaction(file))
            {
                ElementId levelId = spatialElement.Level != null ? spatialElement.Level.Id : ElementId.InvalidElementId;
                using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, spatialElement, null, null, levelId))
                {
                    CreateIFCSpace(exporterIFC, spatialElement, productWrapper, setter);

                    // Do not create boundary information, or extra property sets.
                    if (spatialElement is Area)
                    {
                        tr.Commit();
                        return;
                    }

                    if (exporterIFC.SpaceBoundaryLevel == 1)
                    {
                        Document document = spatialElement.Document;
                        IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);
                        double baseHeightNonScaled = levelInfo.Elevation;

                        SpatialElementGeometryResults spatialElemGeomResult = s_SpatialElemGeometryCalculator.CalculateSpatialElementGeometry(spatialElement);

                        Solid spatialElemGeomSolid = spatialElemGeomResult.GetGeometry();
                        FaceArray faces = spatialElemGeomSolid.Faces;
                        foreach (Face face in faces)
                        {
                            IList<SpatialElementBoundarySubface> spatialElemBoundarySubfaces = spatialElemGeomResult.GetBoundaryFaceInfo(face);
                            foreach (SpatialElementBoundarySubface spatialElemBSubface in spatialElemBoundarySubfaces)
                            {
                                if (spatialElemBSubface.SubfaceType == SubfaceType.Side)
                                    continue;

                                if (spatialElemBSubface.GetSubface() == null)
                                    continue;

                                ElementId elemId = spatialElemBSubface.SpatialBoundaryElement.LinkInstanceId;
                                if (elemId == ElementId.InvalidElementId)
                                {
                                    elemId = spatialElemBSubface.SpatialBoundaryElement.HostElementId;
                                }

                                Element boundingElement = document.get_Element(elemId);
                                if (boundingElement == null)
                                    continue;

                                bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement);

                                IFCGeometryInfo info = IFCGeometryInfo.CreateSurfaceGeometryInfo(spatialElement.Document.Application.VertexTolerance);

                                Face subFace = spatialElemBSubface.GetSubface();
                                ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, subFace, XYZ.Zero, false);

                                IFCAnyHandle ifcOptionalHnd = IFCAnyHandle.Create();
                                foreach (IFCAnyHandle surfaceHnd in info.GetSurfaces())
                                {
                                    IFCAnyHandle connectionGeometry = file.CreateConnectionSurfaceGeometry(surfaceHnd, ifcOptionalHnd);

                                    IFCSpaceBoundary spaceBoundary = IFCSpaceBoundary.Create(spatialElement.Id, boundingElement.Id, connectionGeometry, IFCSpaceBoundaryType.Physical, isObjectExt);

                                    if (!ProcessIFCSpaceBoundary(exporterIFC, spaceBoundary, file))
                                        exporterIFC.RegisterIFCSpaceBoundary(spaceBoundary);
                                }
                            }
                        }

                        IList<IList<BoundarySegment>> roomBoundaries = spatialElement.GetBoundarySegments(ExporterIFCUtils.GetSpatialElementBoundaryOptions(exporterIFC, spatialElement));
                        double roomHeight = GetHeight(spatialElement, exporterIFC.LinearScale, levelInfo);
                        XYZ zDir = new XYZ(0, 0, 1);

                        foreach (IList<BoundarySegment> roomBoundaryList in roomBoundaries)
                        {
                            foreach (BoundarySegment roomBoundary in roomBoundaryList)
                            {
                                Element boundingElement = roomBoundary.Element;

                                if (boundingElement == null)
                                    continue;

                                ElementId buildingElemId = boundingElement.Id;
                                Curve trimmedCurve = roomBoundary.Curve;

                                if (trimmedCurve == null)
                                    continue;

                                //trimmedCurve.Visibility = Visibility.Visible; readonly
                                IFCAnyHandle connectionGeometry = ExporterIFCUtils.CreateExtrudedSurfaceFromCurve(
                                   exporterIFC, trimmedCurve, zDir, roomHeight, baseHeightNonScaled);

                                IFCSpaceBoundaryType physOrVirt = IFCSpaceBoundaryType.Physical;
                                if (boundingElement is CurveElement)
                                    physOrVirt = IFCSpaceBoundaryType.Virtual;
                                else if (boundingElement is Autodesk.Revit.DB.Architecture.Room)
                                    physOrVirt = IFCSpaceBoundaryType.Undefined;

                                bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement);
                                bool isObjectPhys = (physOrVirt == IFCSpaceBoundaryType.Physical);

                                ElementId actualBuildingElemId = isObjectPhys ? buildingElemId : ElementId.InvalidElementId;
                                IFCSpaceBoundary boundary = IFCSpaceBoundary.Create(spatialElement.Id, actualBuildingElemId, connectionGeometry, physOrVirt, isObjectExt);

                                if (!ProcessIFCSpaceBoundary(exporterIFC, boundary, file))
                                    exporterIFC.RegisterIFCSpaceBoundary(boundary);

                                // try to add doors and windows for host objects if appropriate.
                                if (isObjectPhys && boundingElement is HostObject)
                                {
                                    HostObject hostObj = boundingElement as HostObject;
                                    IList<ElementId> elemIds = hostObj.FindInserts(false, false, false, false);
                                    foreach (ElementId elemId in elemIds)
                                    {
                                        // we are going to do a simple bbox export, not complicated geometry.
                                        Element instElem = document.get_Element(elemId);
                                        if (instElem == null)
                                            continue;

                                        BoundingBoxXYZ instBBox = instElem.get_BoundingBox(null);

                                        // make copy of original trimmed curve.
                                        Curve instCurve = trimmedCurve.Clone();
                                        XYZ instOrig = instCurve.get_EndPoint(0);

                                        // make sure that the insert is on this level.
                                        if (instBBox.Max.Z < instOrig.Z)
                                            continue;
                                        if (instBBox.Min.Z > instOrig.Z + roomHeight)
                                            continue;

                                        double insHeight = Math.Min(instBBox.Max.Z, instOrig.Z + roomHeight) - Math.Max(instOrig.Z, instBBox.Min.Z);
                                        if (insHeight < (1.0 / (12.0 * 16.0)))
                                            continue;

                                        // move base curve to bottom of bbox.
                                        XYZ moveDir = new XYZ(0.0, 0.0, instBBox.Min.Z - instOrig.Z);
                                        Transform moveTrf = Transform.get_Translation(moveDir);
                                        instCurve = instCurve.get_Transformed(moveTrf);

                                        bool isHorizOrVert = false;
                                        if (instCurve is Line)
                                        {
                                            Line instLine = instCurve as Line;
                                            XYZ lineDir = instLine.Direction;
                                            if (MathUtil.IsAlmostEqual(Math.Abs(lineDir.X), 1.0) || (MathUtil.IsAlmostEqual(Math.Abs(lineDir.Y), 1.0)))
                                                isHorizOrVert = true;
                                        }

                                        double[] parameters = new double[2];
                                        double[] origEndParams = new double[2];
                                        if (isHorizOrVert)
                                        {
                                            parameters[0] = instCurve.Project(instBBox.Min).Parameter;
                                            parameters[1] = instCurve.Project(instBBox.Max).Parameter;
                                        }
                                        else
                                        {
                                            FamilyInstance famInst = instElem as FamilyInstance;
                                            if (famInst == null)
                                                continue;

                                            ElementType elementType = document.get_Element(famInst.GetTypeId()) as ElementType;
                                            if (elementType == null)
                                                continue;

                                            BoundingBoxXYZ symBBox = elementType.get_BoundingBox(null);
                                            Curve symCurve = trimmedCurve.Clone();
                                            Transform trf = famInst.GetTransform();
                                            Transform invTrf = trf.Inverse;
                                            Curve trfCurve = symCurve.get_Transformed(invTrf);
                                            parameters[0] = trfCurve.Project(symBBox.Min).Parameter;
                                            parameters[1] = trfCurve.Project(symBBox.Max).Parameter;
                                        }

                                        // ignore if less than 1/16".
                                        if (Math.Abs(parameters[1] - parameters[0]) < 1.0 / (12.0 * 16.0))
                                            continue;
                                        if (parameters[0] > parameters[1])
                                        {
                                            //swap
                                            double tempParam = parameters[0];
                                            parameters[0] = parameters[1];
                                            parameters[1] = tempParam;
                                        }

                                        origEndParams[0] = instCurve.get_EndParameter(0);
                                        origEndParams[1] = instCurve.get_EndParameter(1);

                                        if (origEndParams[0] > parameters[1] - (1.0 / (12.0 * 16.0)))
                                            continue;
                                        if (origEndParams[1] < parameters[0] + (1.0 / (12.0 * 16.0)))
                                            continue;

                                        if (parameters[0] > origEndParams[0])
                                            instCurve.set_EndParameter(0, parameters[0]);
                                        if (parameters[1] < origEndParams[1])
                                            instCurve.set_EndParameter(1, parameters[1]);

                                        IFCAnyHandle insConnectionGeom = ExporterIFCUtils.CreateExtrudedSurfaceFromCurve(exporterIFC, instCurve, zDir,
                                           insHeight, baseHeightNonScaled);

                                        IFCSpaceBoundary instBoundary = IFCSpaceBoundary.Create(spatialElement.Id, elemId, insConnectionGeom, physOrVirt, isObjectExt);
                                        if (!ProcessIFCSpaceBoundary(exporterIFC, instBoundary, file))
                                            exporterIFC.RegisterIFCSpaceBoundary(instBoundary);
                                    }
                                }
                            }
                        }
                    }
                    ExporterIFCUtils.CreateSpatialElementPropertySet(exporterIFC, spatialElement, productWrapper);
                }
                tr.Commit();
            }
        }
        /// <summary>
        /// Exports a family instance as a mapped item.
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="familyInstance">
        /// The family instance to be exported.
        /// </param>
        /// <param name="exportType">
        /// The export type.
        /// </param>
        /// <param name="ifcEnumType">
        /// The string value represents the IFC type.
        /// </param>
        /// <param name="wrapper">
        /// The IFCProductWrapper.
        /// </param>
        /// <param name="overrideLevelId">
        /// The level id.
        /// </param>
        /// <param name="range">
        /// The range of this family instance to be exported.
        /// </param>
        public static void ExportFamilyInstanceAsMappedItem(ExporterIFC exporterIFC,
           FamilyInstance familyInstance, IFCExportType exportType, string ifcEnumType,
           IFCProductWrapper wrapper, ElementId overrideLevelId, UV range)
        {
            Document doc = familyInstance.Document;
            IFCFile file = exporterIFC.GetFile();

            FamilySymbol familySymbol = ExporterIFCUtils.GetOriginalSymbol(familyInstance);
            if (familySymbol == null)
                return;

            IFCProductWrapper familyProductWrapper = IFCProductWrapper.Create(wrapper);
            double scale = exporterIFC.LinearScale;

            IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle();

            HostObject hostElement = familyInstance.Host as HostObject; //hostElement could be null
            ElementId categoryId = CategoryUtil.GetSafeCategoryId(familySymbol);

            //string emptyString = "";
            string familyName = familySymbol.Name;
            IFCLabel objectType = IFCLabel.Create(familyName);

            // A Family Instance can have its own copy of geometry, or use the symbol's copy with a transform.
            // The routine below tells us whether to use the Instance's copy or the Symbol's copy.
            bool useInstanceGeometry = ExporterIFCUtils.UsesInstanceGeometry(familyInstance);

            IList<IFCExtrusionData> cutPairOpeningsForColumns = new List<IFCExtrusionData>();
            IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData();

            Transform trf = familyInstance.GetTransform();

            // Extra information if we are exporting a door or a window.
            IFCDoorWindowInfo doorWindowInfo = null;
            if (exportType == IFCExportType.ExportDoorType)
                doorWindowInfo = IFCDoorWindowInfo.CreateDoorInfo(exporterIFC, familyInstance, familySymbol, hostElement, overrideLevelId, trf);
            else if (exportType == IFCExportType.ExportWindowType)
                doorWindowInfo = IFCDoorWindowInfo.CreateWindowInfo(exporterIFC, familyInstance, familySymbol, hostElement, overrideLevelId, trf);

            IFCTypeInfo typeInfo = new IFCTypeInfo();
            XYZ extraOffset = XYZ.Zero;

            bool flipped = doorWindowInfo != null ? doorWindowInfo.IsSymbolFlipped : false;
            IFCTypeInfo currentTypeInfo = exporterIFC.FindType(familySymbol.Id, flipped);
            bool found = currentTypeInfo.IsValid();

            Family family = familySymbol.Family;

            // TODO: this code to be removed by ExtrusionAnalyzer code.
            bool trySpecialColumnCreation = ((exportType == IFCExportType.ExportColumnType) && (!family.IsInPlace));

            // We will create a new mapped type if:
            // 1.  We are exporting part of a column or in-place wall (range != null), OR
            // 2.  We are using the instance's copy of the geometry (that it, it has unique geometry), OR
            // 3.  We haven't already created the type.
            bool creatingType = ((range != null) || useInstanceGeometry || !found);
            if (creatingType)
            {
                IFCAnyHandle bodyRepresentation = IFCAnyHandle.Create();
                IFCAnyHandle planRepresentation = IFCAnyHandle.Create();

                // If we are using the instance geometry, ignore the transformation.
                if (useInstanceGeometry)
                    trf = Transform.Identity;

                // TODO: this code to be removed by ExtrusionAnalyzer code.
                if (trySpecialColumnCreation)
                {
                    XYZ rangeOffset = trf.Origin;
                    IFCFamilyInstanceExtrusionExportResults results;
                    if (range != null)
                    {
                        results = ExporterIFCUtils.ExportFamilyInstanceAsExtrusion(exporterIFC, familyInstance, useInstanceGeometry, range, overrideLevelId, extraParams);
                    }
                    else
                    {
                        results = ExporterIFCUtils.ExportFamilyInstanceAsExtrusion(exporterIFC, familyInstance, useInstanceGeometry, overrideLevelId, extraParams);
                    }
                    bodyRepresentation = results.GetExtrusionHandle();
                    extraOffset = results.ExtraOffset;
                    cutPairOpeningsForColumns = results.GetCutPairOpenings();

                    if (bodyRepresentation.HasValue)
                    {
                        typeInfo.MaterialId = results.MaterialId;
                        // add in level for real columns, not in-place ones.
                        Element actualLevel =
                           (overrideLevelId == ElementId.InvalidElementId) ? familyInstance.Level : doc.get_Element(overrideLevelId);
                        if (actualLevel != null)
                        {
                            IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(actualLevel.Id);
                            double nonStoryLevelOffset = LevelUtil.GetNonStoryLevelOffsetIfAny(exporterIFC, actualLevel as Level);
                            if (range != null)
                            {
                                rangeOffset = new XYZ(rangeOffset.X, rangeOffset.Y, levelInfo.Elevation + nonStoryLevelOffset);
                            }
                        }
                    }

                    rangeOffset += extraOffset;
                    trf.Origin = rangeOffset;
                }

                Transform doorWindowTrf = Transform.Identity;
                IFCAnyHandle dummyPlacement = IFCAnyHandle.Create();
                if (doorWindowInfo != null)
                {
                    doorWindowTrf = ExporterIFCUtils.GetTransformForDoorOrWindow(familyInstance, familySymbol, doorWindowInfo);
                }
                else
                {
                    dummyPlacement = file.CreateLocalPlacement(IFCAnyHandle.Create(), file.CreateAxis2Placement3D());
                    extraParams.SetLocalPlacement(dummyPlacement);
                }

                bool needToCreate2d = (!exporterIFC.ExportAs2x2);
                bool needToCreate3d = (!bodyRepresentation.HasValue);

                if (needToCreate2d || needToCreate3d)
                {
                    using (IFCTransformSetter trfSetter = IFCTransformSetter.Create())
                    {
                        if (doorWindowInfo != null)
                        {
                            trfSetter.Initialize(exporterIFC, doorWindowTrf);
                        }

                        Options options = new Options();
                        GeometryElement exportGeometry =
                           useInstanceGeometry ? familyInstance.get_Geometry(options) : familySymbol.get_Geometry(options);
                        if (exportGeometry == null)
                            return;

                        if (needToCreate3d)
                        {
                            IFCSolidMeshGeometryInfo solidMeshInfo;

                            if (range == null)
                                solidMeshInfo = ExporterIFCUtils.GetSolidMeshGeometry(exporterIFC, exportGeometry, Transform.Identity);
                            else
                                solidMeshInfo = ExporterIFCUtils.GetClippedSolidMeshGeometry(exporterIFC, range, exportGeometry);

                            IList<Solid> solids = solidMeshInfo.GetSolids();
                            IList<Mesh> polyMeshes = solidMeshInfo.GetMeshes();

                            bool tryToExportAsExtrusion = (!exporterIFC.ExportAs2x2 || (exportType == IFCExportType.ExportColumnType));

                            if (exportType == IFCExportType.ExportColumnType)
                            {
                                extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ;
                            }
                            else
                            {
                                extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryXYZ;
                            }

                            if (solids.Count > 0 || polyMeshes.Count > 0)
                            {
                                bodyRepresentation = BodyExporter.ExportBody(familyInstance.Document.Application, exporterIFC, categoryId, solids, polyMeshes,
                                    tryToExportAsExtrusion, extraParams);
                                typeInfo.MaterialId = BodyExporter.GetBestMaterialIdForGeometry(solids, polyMeshes);
                            }
                            else
                            {
                                IList<GeometryObject> exportedGeometries = new List<GeometryObject>();
                                exportedGeometries.Add(exportGeometry);
                                bodyRepresentation = BodyExporter.ExportBody(familyInstance.Document.Application, exporterIFC, categoryId, exportedGeometries,
                                   tryToExportAsExtrusion, extraParams);
                            }

                            if (!bodyRepresentation.HasValue)
                            {
                                extraParams.ClearOpenings();
                                return;
                            }
                        }

                        // if exporting IFC2x3 (or later), export 2D plan rep of family (if it exists).
                        if (needToCreate2d)
                        {
                            HashSet<IFCAnyHandle> curveSet = new HashSet<IFCAnyHandle>();
                            {
                                Transform planeTrf = doorWindowTrf.Inverse;
                                Plane plane = new Plane(planeTrf.get_Basis(0), planeTrf.get_Basis(1), planeTrf.Origin);
                                XYZ projDir = new XYZ(0, 0, 1);

                                IFCGeometryInfo IFCGeometryInfo = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, plane, projDir, true);
                                ExporterIFCUtils.CollectGeometryInfo(exporterIFC, IFCGeometryInfo, exportGeometry, planeTrf.Origin, false);

                                IList<IFCAnyHandle> curves = IFCGeometryInfo.GetCurves();
                                foreach (IFCAnyHandle curve in curves)
                                    curveSet.Add(curve);

                                if (curveSet.Count > 0)
                                {
                                    IFCAnyHandle contextOfItems2d = exporterIFC.Get2DContextHandle();
                                    IFCAnyHandle curveRepresentationItem = file.CreateGeometricSet(curveSet);
                                    HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>();
                                    bodyItems.Add(curveRepresentationItem);
                                    planRepresentation = RepresentationUtil.CreateGeometricSetRep(exporterIFC, categoryId, "Annotation",
                                       contextOfItems2d, bodyItems);
                                }
                            }
                        }
                    }
                }

                if (doorWindowInfo != null)
                {
                    typeInfo.SetStyleTransform(doorWindowTrf.Inverse);
                }
                else
                {
                    if (!MathUtil.IsAlmostZero(extraOffset.DotProduct(extraOffset)))
                    {
                        Transform newTransform = typeInfo.GetStyleTransform();
                        XYZ newOrig = newTransform.Origin + extraOffset;
                        newTransform.Origin = newOrig;
                        typeInfo.SetStyleTransform(newTransform);
                    }
                    typeInfo.SetStyleTransform(ExporterIFCUtils.GetUnscaledTransform(exporterIFC, extraParams.GetLocalPlacement()));
                }

                IFCLabel descriptionOpt = IFCLabel.Create();
                IFCLabel applicableOccurrenceOpt = IFCLabel.Create();

                IFCAnyHandle origin = file.CreateAxis2Placement3D();
                IFCAnyHandle repMap2dHnd = IFCAnyHandle.Create();
                IFCAnyHandle repMap3dHnd = file.CreateRepresentationMap(origin, bodyRepresentation);

                IList<IFCAnyHandle> repMapList = new List<IFCAnyHandle>();
                repMapList.Add(repMap3dHnd);
                if (planRepresentation.HasValue)
                {
                    repMap2dHnd = file.CreateRepresentationMap(origin, planRepresentation);
                    repMapList.Add(repMap2dHnd);
                }

                // for Door, Window
                bool paramTakesPrecedence = false; // For Revit, this is currently always false.
                bool sizeable = false;

                // for many
                HashSet<IFCAnyHandle> propertySets = new HashSet<IFCAnyHandle>();

                IFCLabel guid = IFCLabel.CreateGUID(familySymbol);
                IFCLabel symIdAsLabel = NamingUtil.CreateIFCElementId(familySymbol);

                // This covers many generic types.  If we can't find it in the list here, do custom exports.
                IFCAnyHandle typeStyle = FamilyExporterUtil.ExportGenericType(file, exportType, ifcEnumType, guid,
                   ownerHistory, objectType, descriptionOpt, applicableOccurrenceOpt, propertySets, repMapList, symIdAsLabel, objectType,
                   familyInstance, familySymbol);

                // Cover special cases not covered above.
                if (!typeStyle.HasValue)
                {
                    switch (exportType)
                    {
                        case IFCExportType.ExportColumnType:
                            {
                                // If we are using the instance GRep, then we have to create a generic GUID for the
                                // column type, as they share the same ElementId.
                                IFCLabel colGUID = IFCLabel.Create();
                                IFCLabel colElemId = IFCLabel.Create();

                                if (useInstanceGeometry)
                                {
                                    colGUID = IFCLabel.CreateGUID();
                                    colElemId = NamingUtil.CreateIFCElementId(familyInstance);
                                }
                                else
                                {
                                    colGUID = guid;
                                    colElemId = NamingUtil.CreateIFCElementId(familySymbol);
                                }

                                string columnType = "Column";
                                typeStyle = file.CreateColumnType(columnType, colGUID, ownerHistory, objectType,
                                   descriptionOpt, applicableOccurrenceOpt, propertySets, repMapList, colElemId,
                                   objectType, familyInstance, familySymbol);

                                break;
                            }
                        case IFCExportType.ExportDoorType:
                            {
                                string constructionType = string.Empty;
                                ParameterUtil.GetStringValueFromElementOrSymbol(familyInstance, "Construction", out constructionType);

                                IFCAnyHandle doorLining = DoorWindowUtil.CreateDoorLiningProperties(exporterIFC, familyInstance);
                                if (doorLining.HasValue)
                                    propertySets.Add(doorLining);

                                IList<IFCAnyHandle> doorPanels = DoorWindowUtil.CreateDoorPanelProperties(exporterIFC, doorWindowInfo,
                                   familyInstance);
                                propertySets.UnionWith(doorPanels);

                                IFCLabel doorStyleGUID = IFCLabel.CreateGUID();
                                IFCLabel doorStyleElemId = NamingUtil.CreateIFCElementId(familyInstance);
                                typeStyle = file.CreateDoorStyle(doorStyleGUID, ownerHistory, objectType,
                                   descriptionOpt, applicableOccurrenceOpt, propertySets, repMapList, doorStyleElemId,
                                   doorWindowInfo.DoorOperationType, constructionType, paramTakesPrecedence, sizeable);
                                break;
                            }
                        case IFCExportType.ExportSystemFurnitureElementType:
                            {
                                IFCLabel furnitureId = NamingUtil.CreateIFCElementId(familyInstance);
                                typeStyle = file.CreateSystemFurnitureElementType(guid, ownerHistory, objectType,
                                   descriptionOpt, applicableOccurrenceOpt, propertySets, repMapList, furnitureId,
                                   objectType);
                                break;
                            }
                        case IFCExportType.ExportWindowType:
                            {
                                IFCWindowStyleOperation operationType = DoorWindowUtil.GetIFCWindowStyleOperation(familySymbol);
                                string constructionType = DoorWindowUtil.GetIFCWindowStyleConstruction(familyInstance, "");

                                IFCAnyHandle windowLining = DoorWindowUtil.CreateWindowLiningProperties(exporterIFC, familyInstance, descriptionOpt);
                                if (windowLining.HasValue)
                                    propertySets.Add(windowLining);

                                IList<IFCAnyHandle> windowPanels =
                                   DoorWindowUtil.CreateWindowPanelProperties(exporterIFC, familyInstance, descriptionOpt);
                                propertySets.UnionWith(windowPanels);

                                IFCLabel windowStyleGUID = IFCLabel.CreateGUID();
                                IFCLabel windowStyleElemId = NamingUtil.CreateIFCElementId(familyInstance);
                                typeStyle = file.CreateWindowStyle(windowStyleGUID, ownerHistory, objectType,
                                   descriptionOpt, applicableOccurrenceOpt, propertySets, repMapList, windowStyleElemId,
                                   operationType, constructionType, paramTakesPrecedence, sizeable);
                                break;
                            }
                        case IFCExportType.ExportBuildingElementProxy:
                        default:
                            {
                                typeInfo.Set2DMapHandle(repMap2dHnd);
                                typeInfo.Set3DMapHandle(repMap3dHnd);
                                break;
                            }
                    }
                }

                typeInfo.SetStyle(typeStyle);

                // Transfer extraParams information for certain types.
                if (typeStyle.HasValue)
                {
                    if (((exportType == IFCExportType.ExportColumnType) && trySpecialColumnCreation) ||
                       (exportType == IFCExportType.ExportMemberType))
                    {
                        typeInfo.ScaledArea = extraParams.ScaledArea;
                        typeInfo.ScaledDepth = extraParams.ScaledLength;
                        typeInfo.ScaledInnerPerimeter = extraParams.ScaledInnerPerimeter;
                        typeInfo.ScaledOuterPerimeter = extraParams.ScaledOuterPerimeter;
                    }
                }
            }
            else if (!creatingType && (trySpecialColumnCreation))
            {
                // still need to modify instance trf for columns.
                trf.Origin += GetLevelOffsetForExtrudedColumns(exporterIFC, familyInstance, overrideLevelId, extraParams);
            }

            if (found && !typeInfo.GetStyle().HasValue)
            {
                typeInfo = currentTypeInfo;
            }

            // we'll pretend we succeeded, but we'll do nothing.
            if (!typeInfo.GetStyle().HasValue && !typeInfo.Get2DMapHandle().HasValue && !typeInfo.Get3DMapHandle().HasValue)
                return;

            // add to the map, as long as we are not using range, not using instance geometry, and don't have extra openings.
            if ((range == null) && !useInstanceGeometry && (extraParams.GetOpenings().Count == 0))
                exporterIFC.AddType(familySymbol.Id, flipped, typeInfo);

            Transform oldTrf = new Transform(trf);
            XYZ scaledMapOrigin = XYZ.Zero;

            trf = trf.Multiply(typeInfo.GetStyleTransform());

            // create instance.  
            IList<IFCAnyHandle> shapeReps = new List<IFCAnyHandle>();
            {
                IFCAnyHandle contextOfItems2d = exporterIFC.Get2DContextHandle();
                IFCAnyHandle contextOfItems3d = exporterIFC.Get3DContextHandle();

                // for proxies, we store the IfcRepresentationMap directly since there is no style.
                IList<IFCAnyHandle> repMapList = IFCGeometryUtils.GetRepresentationMaps(typeInfo.GetStyle());
                int numReps = repMapList.Count;

                IFCAnyHandle repMap2dHnd = typeInfo.Get2DMapHandle();
                IFCAnyHandle repMap3dHnd = typeInfo.Get3DMapHandle();
                if (!repMap3dHnd.HasValue && (numReps > 0))
                    repMap3dHnd = repMapList[0];
                if (!repMap2dHnd.HasValue && (numReps > 1))
                    repMap2dHnd = repMapList[1];

                if (repMap3dHnd.HasValue)
                {
                    HashSet<IFCAnyHandle> representations = new HashSet<IFCAnyHandle>();
                    representations.Add(ExporterUtil.CreateDefaultMappedItem(file, repMap3dHnd, scaledMapOrigin));
                    IFCAnyHandle shapeRep = RepresentationUtil.CreateBodyMappedItemRep(exporterIFC, categoryId, contextOfItems3d, representations);
                    if (!shapeRep.HasValue)
                        return;
                    shapeReps.Add(shapeRep);
                }

                if (repMap2dHnd.HasValue)
                {
                    HashSet<IFCAnyHandle> representations = new HashSet<IFCAnyHandle>();
                    representations.Add(ExporterUtil.CreateDefaultMappedItem(file, repMap2dHnd, scaledMapOrigin));
                    IFCAnyHandle shapeRep = RepresentationUtil.CreatePlanMappedItemRep(exporterIFC, categoryId, contextOfItems2d, representations);
                    if (!shapeRep.HasValue)
                        return;
                    shapeReps.Add(shapeRep);
                }
            }

            IFCLabel noDescriptionOpt = IFCLabel.Create();
            IFCLabel noNameOpt = IFCLabel.Create();
            IFCAnyHandle rep = file.CreateProductDefinitionShape(noNameOpt, noDescriptionOpt, shapeReps);

            IFCAnyHandle instanceHandle = IFCAnyHandle.Create();
            using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, familyInstance, trf, null, overrideLevelId))
            {
                IFCLabel instanceGUID = IFCLabel.CreateGUID(familyInstance);
                IFCLabel origInstanceName = NamingUtil.CreateIFCName(exporterIFC, -1);
                IFCLabel instanceName = NamingUtil.GetNameOverride(familyInstance, origInstanceName);
                IFCLabel instanceDescription = NamingUtil.GetDescriptionOverride(familyInstance, noDescriptionOpt);
                IFCLabel instanceObjectType = NamingUtil.GetObjectTypeOverride(familyInstance, objectType);
                IFCLabel instanceElemId = NamingUtil.CreateIFCElementId(familyInstance);

                IFCAnyHandle localPlacement = setter.GetPlacement();
                instanceHandle = FamilyExporterUtil.ExportGenericInstance(exportType, exporterIFC, familyInstance,
                   wrapper, setter, extraParams, instanceGUID, ownerHistory, instanceName, instanceDescription, instanceObjectType,
                   rep, instanceElemId);

                switch (exportType)
                {
                    case IFCExportType.ExportColumnType:
                        {
                            IFCAnyHandle placementToUse = localPlacement;
                            if (!useInstanceGeometry)
                            {
                                Transform openingTrf = new Transform(oldTrf);
                                Transform extraRot = new Transform(oldTrf);
                                extraRot.Origin = XYZ.Zero;
                                openingTrf = openingTrf.Multiply(extraRot);
                                openingTrf = openingTrf.Multiply(typeInfo.GetStyleTransform());

                                IFCAnyHandle openingRelativePlacement = file.CreateAxis2Placement3D(openingTrf.Origin * scale,
                                   openingTrf.get_Basis(2), openingTrf.get_Basis(0));
                                IFCAnyHandle openingPlacement = ExporterUtil.CopyLocalPlacement(file, localPlacement);
                                IFCGeometryUtils.SetRelativePlacement(openingPlacement, openingRelativePlacement);
                                placementToUse = openingPlacement;
                            }

                            OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, cutPairOpeningsForColumns,
                               exporterIFC, placementToUse, setter, wrapper);
                            OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                               placementToUse, setter, wrapper);

                            //export Base Quantities.
                            ExporterIFCUtils.CreateBeamColumnBaseQuantities(exporterIFC, instanceHandle, familyInstance, typeInfo);

                            CategoryUtil.CreateMaterialAssociation(doc, exporterIFC, instanceHandle, typeInfo.MaterialId);

                            ExporterIFCUtils.CreateColumnPropertySet(exporterIFC, familyInstance, extraParams, wrapper);
                            break;
                        }
                    case IFCExportType.ExportDoorType:
                    case IFCExportType.ExportWindowType:
                        {
                            double doorHeight = doorWindowInfo.OpeningHeight;
                            if (doorHeight < MathUtil.Eps())
                                doorHeight = GetMinSymbolHeight(familySymbol);
                            double doorWidth = doorWindowInfo.OpeningWidth;
                            if (doorWidth < MathUtil.Eps())
                                doorWidth = GetMinSymbolWidth(familySymbol);

                            IFCMeasureValue height = IFCMeasureValue.Create(doorHeight * scale);
                            IFCMeasureValue width = IFCMeasureValue.Create(doorWidth * scale);

                            if (!doorWindowInfo.GetLocalPlacement().HasValue)
                                doorWindowInfo.SetLocalPlacement(localPlacement);

                            IFCAnyHandle doorWindowOrigLocalPlacement = doorWindowInfo.GetLocalPlacement();
                            Transform relTrf = ExporterIFCUtils.GetRelativeLocalPlacementOffsetTransform(localPlacement, doorWindowOrigLocalPlacement);

                            IFCAnyHandle doorWindowRelativePlacement = file.CreateAxis2Placement3D(relTrf);
                            IFCAnyHandle doorWindowLocalPlacement =
                               file.CreateLocalPlacement(doorWindowOrigLocalPlacement, doorWindowRelativePlacement);
                            if (exportType == IFCExportType.ExportDoorType)
                                instanceHandle = file.CreateDoor(instanceGUID, ownerHistory,
                                   instanceName, instanceDescription, instanceObjectType, doorWindowLocalPlacement,
                                   rep, instanceElemId, height, width);
                            else if (exportType == IFCExportType.ExportWindowType)
                                instanceHandle = file.CreateWindow(instanceGUID, ownerHistory,
                                   instanceName, instanceDescription, instanceObjectType, doorWindowLocalPlacement,
                                   rep, instanceElemId, height, width);
                            wrapper.AddElement(instanceHandle, setter, extraParams, true);

                            exporterIFC.RegisterSpaceBoundingElementHandle(instanceHandle, familyInstance.Id, setter.LevelId);

                            // only necessary when exporting as possible breps.
                            OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                               doorWindowLocalPlacement, setter, wrapper);
                            if (exporterIFC.ExportBaseQuantities)
                                ExporterIFCUtils.CreateDoorWindowBaseQuantities(exporterIFC, instanceHandle, (doorHeight * scale), (doorWidth * scale));

                            if (exportType == IFCExportType.ExportDoorType)
                                ExporterIFCUtils.CreateDoorPropertySet(exporterIFC, familyInstance, wrapper);
                            else
                                ExporterIFCUtils.CreateWindowPropertySet(exporterIFC, familyInstance, wrapper);
                            break;
                        }
                    case IFCExportType.ExportMemberType:
                        {
                            OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                              localPlacement, setter, wrapper);
                            CategoryUtil.CreateMaterialAssociation(doc, exporterIFC, instanceHandle, typeInfo.MaterialId);

                            //export Base Quantities.
                            ExporterIFCUtils.CreateBeamColumnBaseQuantities(exporterIFC, instanceHandle, familyInstance, typeInfo);
                            // TODO: create PropertySet!
                            //createMemberPropertySet(exporter, pFamInst, pWrapper, extraParams);
                            ExporterIFCUtils.CreateGenericElementPropertySet(exporterIFC, familyInstance, wrapper);
                            break;
                        }
                    case IFCExportType.ExportPlateType:
                        {
                            OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                              localPlacement, setter, wrapper);
                            CategoryUtil.CreateMaterialAssociation(doc, exporterIFC, instanceHandle, typeInfo.MaterialId);

                            // TODO: create PropertySet!
                            //createPlatePropertySet(exporter, pFamInst, pWrapper, extraParams);
                            ExporterIFCUtils.CreateGenericElementPropertySet(exporterIFC, familyInstance, wrapper);
                            break;
                        }
                    case IFCExportType.ExportTransportElementType:
                        {
                            string operationTypeOpt = "";
                            IFCMeasureValue capByWeightOpt = IFCMeasureValue.Create();
                            IFCMeasureValue capByNumOpt = IFCMeasureValue.Create();

                            IFCAnyHandle localPlacementToUse;
                            ElementId roomId = setter.UpdateRoomRelativeCoordinates(familyInstance, out localPlacementToUse);

                            instanceHandle = file.CreateTransportElement(instanceGUID, ownerHistory,
                               instanceName, instanceDescription, instanceObjectType,
                               localPlacementToUse, rep, instanceElemId, operationTypeOpt, capByWeightOpt, capByNumOpt,
                               familyInstance, familySymbol);

                            if (roomId == ElementId.InvalidElementId)
                            {
                                wrapper.AddElement(instanceHandle, setter, extraParams, true);
                            }
                            else
                            {
                                exporterIFC.RelateSpatialElement(roomId, instanceHandle);
                                wrapper.AddElement(instanceHandle, setter, extraParams, false);
                            }

                            ExporterIFCUtils.CreateGenericElementPropertySet(exporterIFC, familyInstance, wrapper);
                            break;
                        }
                    case IFCExportType.ExportBuildingElementProxy:
                    default:
                        {
                            bool isBuildingElementProxy = (exportType == IFCExportType.ExportBuildingElementProxy);

                            if (!isBuildingElementProxy)
                            {
                                if (FamilyExporterUtil.IsDistributionControlElementSubType(exportType))
                                {
                                    IFCLabel controlElementId = IFCLabel.Create();
                                    IFCAnyHandle localPlacementToUse;
                                    ElementId roomId = setter.UpdateRoomRelativeCoordinates(familyInstance, out localPlacementToUse);

                                    instanceHandle = file.CreateDistributionControlElement(instanceGUID,
                                       ownerHistory, instanceName, instanceDescription, instanceObjectType,
                                       localPlacement, rep, instanceElemId, controlElementId);

                                    if (roomId == ElementId.InvalidElementId)
                                    {
                                        wrapper.AddElement(instanceHandle, setter, extraParams, true);
                                    }
                                    else
                                    {
                                        exporterIFC.RelateSpatialElement(roomId, instanceHandle);
                                        wrapper.AddElement(instanceHandle, setter, extraParams, false);
                                    }

                                    OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                                       localPlacement, setter, wrapper);
                                }
                                else if (!instanceHandle.HasValue)
                                {
                                    isBuildingElementProxy = true;
                                }
                            }

                            if (isBuildingElementProxy)
                            {
                                IFCElementComposition proxyType = IFCElementComposition.Element;
                                IFCAnyHandle localPlacementToUse;
                                ElementId roomId = setter.UpdateRoomRelativeCoordinates(familyInstance, out localPlacementToUse);

                                instanceHandle = file.CreateBuildingElementProxy(instanceGUID,
                                   ownerHistory, instanceName, instanceDescription, instanceObjectType,
                                   localPlacementToUse, rep, instanceElemId, proxyType);

                                if (roomId == ElementId.InvalidElementId)
                                {
                                    wrapper.AddElement(instanceHandle, setter, extraParams, true);
                                }
                                else
                                {
                                    exporterIFC.RelateSpatialElement(roomId, instanceHandle);
                                    wrapper.AddElement(instanceHandle, setter, extraParams, false);
                                }

                                OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, exporterIFC,
                                   localPlacement, setter, wrapper);
                            }

                            ExporterIFCUtils.CreateGenericElementPropertySet(exporterIFC, familyInstance, wrapper);
                            break;
                        }
                }

                if (instanceHandle.HasValue)
                {
                    if (doorWindowInfo != null)
                    {
                        if (doorWindowInfo.GetOpening().HasValue)
                        {
                            IFCLabel relGUID = IFCLabel.CreateGUID();
                            file.CreateRelFillsElement(relGUID, ownerHistory, IFCLabel.Create(), IFCLabel.Create(), doorWindowInfo.GetOpening(), instanceHandle);
                        }
                        else if (doorWindowInfo.NeedsOpening)
                        {
                            bool added = doorWindowInfo.SetDelayedFamilyInstance(instanceHandle, localPlacement, doorWindowInfo.AssignedLevelId);
                            if (added)
                                exporterIFC.RegisterDoorWindowForOpeningUpdate(doorWindowInfo);
                            else
                            {
                                // we need to fill a later opening.
                                exporterIFC.RegisterDoorWindowForUncreatedOpening(familyInstance.Id, instanceHandle);
                            }
                        }
                    }

                    if (typeInfo.GetStyle().HasValue)
                        exporterIFC.AddTypeRelation(typeInfo.GetStyle(), instanceHandle);
                }
            }
        }
Esempio n. 12
0
        /// <summary>
        /// Creates IFC room/space/area item, not include boundaries. 
        /// </summary>
        /// <param name="exporterIFC">
        /// The ExporterIFC object.
        /// </param>
        /// <param name="spatialElement">
        /// The spatial element.
        /// </param>
        /// <param name="productWrapper">
        /// The IFCProductWrapper.
        /// </param>
        /// <param name="setter">
        /// The IFCPlacementSetter.
        /// </param>
        /// <returns>
        /// True if created successfully, false otherwise.
        /// </returns>
        static bool CreateIFCSpace(ExporterIFC exporterIFC, SpatialElement spatialElement, IFCProductWrapper productWrapper, IFCPlacementSetter setter)
        {
            IList<CurveLoop> curveLoops = null;
            try
            {
                SpatialElementBoundaryOptions options = ExporterIFCUtils.GetSpatialElementBoundaryOptions(exporterIFC, spatialElement);
                curveLoops = ExporterIFCUtils.GetRoomBoundaryAsCurveLoopArray(spatialElement, options, true);
            }
            catch (Autodesk.Revit.Exceptions.InvalidOperationException)
            {
                //Some spatial elements are not placed that have no boundary loops. Don't export them.
                return false;
            }

            Autodesk.Revit.DB.Document document = spatialElement.Document;
            ElementId levelId = spatialElement.Level != null ? spatialElement.Level.Id : ElementId.InvalidElementId;
            double scale = exporterIFC.LinearScale;

            ElementId catId = spatialElement.Category != null ? spatialElement.Category.Id : ElementId.InvalidElementId;

            double dArea = 0.0;

            Parameter param = spatialElement.get_Parameter(BuiltInParameter.ROOM_AREA);
            if (param != null)
            {
                dArea = param.AsDouble();
                dArea *= (scale * scale);
            }

            IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId);

            string strSpaceNumber = null;
            string strSpaceName = null;
            string strSpaceDesc = null;

            bool isArea = spatialElement is Area;
            if (!isArea)
            {
                Parameter paramRoomNum = spatialElement.get_Parameter(BuiltInParameter.ROOM_NUMBER);
                if (paramRoomNum != null)
                {
                    strSpaceNumber = paramRoomNum.AsString();
                }

                Parameter paramRoomName = spatialElement.get_Parameter(BuiltInParameter.ROOM_NAME);
                if (paramRoomName != null)
                {
                    strSpaceName = paramRoomName.AsString();
                }

                Parameter paramRoomComm = spatialElement.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);
                if (paramRoomComm != null)
                {
                    strSpaceDesc = paramRoomComm.AsString();
                }
            }
            else
            {
                Element level = document.GetElement(levelId);
                if (level != null)
                {
                    strSpaceNumber = level.Name + " GSA Design Gross Area";
                }
            }

            string name = strSpaceNumber;
            string longName = strSpaceName;
            string desc = strSpaceDesc;

            IFCFile file = exporterIFC.GetFile();

            IFCAnyHandle localPlacement = setter.GetPlacement();
            ElementType elemType = document.GetElement(spatialElement.GetTypeId()) as ElementType;
            IFCInternalOrExternal internalOrExternal = CategoryUtil.IsElementExternal(spatialElement) ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal;

            double roomHeight = 0.0;

            roomHeight = GetHeight(spatialElement, scale, levelId, levelInfo);

            double bottomOffset = 0.0;
            Parameter paramBottomOffset = spatialElement.get_Parameter(BuiltInParameter.ROOM_LOWER_OFFSET);
            bottomOffset = paramBottomOffset != null ? paramBottomOffset.AsDouble() : 0.0;

            XYZ zDir = new XYZ(0, 0, 1);
            XYZ orig = new XYZ(0, 0, levelInfo.Elevation + bottomOffset);

            Plane plane = new Plane(zDir, orig); // room calculated as level offset.

            GeometryElement geomElem = null;
            if (spatialElement is Autodesk.Revit.DB.Architecture.Room)
            {
                Autodesk.Revit.DB.Architecture.Room room = spatialElement as Autodesk.Revit.DB.Architecture.Room;
                geomElem = room.ClosedShell;
            }
            else if (spatialElement is Autodesk.Revit.DB.Mechanical.Space)
            {
                Autodesk.Revit.DB.Mechanical.Space space = spatialElement as Autodesk.Revit.DB.Mechanical.Space;
                geomElem = space.ClosedShell;
            }

            IFCAnyHandle spaceHnd = null;
            using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData())
            {
                extraParams.SetLocalPlacement(localPlacement);
                extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ;

                using (IFCTransaction transaction2 = new IFCTransaction(file))
                {
                    IFCAnyHandle repHnd = null;
                    if (!ExporterCacheManager.ExportOptionsCache.Use2DRoomBoundaryForRoomVolumeCreation && geomElem != null)
                    {
                        BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true);
                        bodyExporterOptions.TessellationLevel = BodyExporterOptions.BodyTessellationLevel.Coarse;
                        repHnd = RepresentationUtil.CreateBRepProductDefinitionShape(spatialElement.Document.Application, exporterIFC, spatialElement,
                            catId, geomElem, bodyExporterOptions, null, extraParams);
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(repHnd))
                            extraParams.ClearOpenings();
                    }
                    else
                    {
                        IFCAnyHandle shapeRep = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, curveLoops, plane, zDir, roomHeight);
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep))
                            return false;
                        IFCAnyHandle styledItemHnd = BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document,
                            shapeRep, ElementId.InvalidElementId);

                        HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>();
                        bodyItems.Add(shapeRep);
                        shapeRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, spatialElement, catId, exporterIFC.Get3DContextHandle("Body"), bodyItems, null);
                        IList<IFCAnyHandle> shapeReps = new List<IFCAnyHandle>();
                        shapeReps.Add(shapeRep);
                        repHnd = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapeReps);
                    }

                    extraParams.ScaledHeight = roomHeight;
                    extraParams.ScaledArea = dArea;

                    spaceHnd = IFCInstanceExporter.CreateSpace(file, ExporterIFCUtils.CreateGUID(spatialElement),
                                                      exporterIFC.GetOwnerHistoryHandle(),
                                                      NamingUtil.GetNameOverride(spatialElement, name),
                                                      NamingUtil.GetDescriptionOverride(spatialElement, desc),
                                                      NamingUtil.GetObjectTypeOverride(spatialElement, null),
                                                      extraParams.GetLocalPlacement(), repHnd, longName, Toolkit.IFCElementComposition.Element
                                                      , internalOrExternal, null);
                    transaction2.Commit();
                }

                productWrapper.AddSpace(spaceHnd, levelInfo, extraParams, true);
            }

            // Save room handle for later use/relationships
            ExporterCacheManager.SpatialElementHandleCache.Register(spatialElement.Id, spaceHnd);
            exporterIFC.RegisterSpatialElementHandle(spatialElement.Id, spaceHnd);

            if (!MathUtil.IsAlmostZero(dArea) && !(ExporterCacheManager.ExportOptionsCache.FileVersion == IFCVersion.IFCCOBIE))
            {
                ExporterIFCUtils.CreatePreCOBIEGSAQuantities(exporterIFC, spaceHnd, "GSA Space Areas", (isArea ? "GSA Design Gross Area" : "GSA BIM Area"), dArea);
            }

            // Export BaseQuantities for SpatialElement
            if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities && !(ExporterCacheManager.ExportOptionsCache.FileVersion == IFCVersion.IFCCOBIE))
            {
                ExporterIFCUtils.CreateNonCOBIERoomQuantities(exporterIFC, spaceHnd, spatialElement, dArea, roomHeight);
            }

            // Export Classifications for SpatialElement for GSA/COBIE.
            if (ExporterCacheManager.ExportOptionsCache.FileVersion == IFCVersion.IFCCOBIE)
            {
                CreateCOBIESpaceClassifications(exporterIFC, file, spaceHnd, document.ProjectInformation, spatialElement);
            }

            return true;
        }