IFC導出Excel空間報表文件html
本篇將向您展現從IFC文件讀取數據所需的一些概念。它使用IFC4接口,適用於IFC2x3和IFC4型號。要建立Excel文件,咱們使用NPOI。在這個例子中你只須要 xBIM Essentials 組件。包含樣本數據的全部代碼都可在此處得到。git
此示例的結果以下所示:github
您將須要如下using
聲明:編輯器
using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System.Diagnostics; using System.IO; using System.Linq; using Xbim.Ifc; using Xbim.Ifc4.Interfaces;
主要功能以下:函數
//從模板初始化NPOI工做簿 var workbook = new XSSFWorkbook("template.xlsx"); var sheet = workbook.GetSheet("Spaces");//用單位建立漂亮的數字格式。 現實中須要更多的關心的是單位。
//咱們只知道咱們如今的模型有空間面積以立方米和空間體積爲單位 //請注意從Revit導出的原始數據是錯誤的,由於數據量比應該大1000倍。 //在這個例子中,數據是使用xBIM修復的。 var areaFormat = workbook.CreateDataFormat(); var areaFormatId = areaFormat.GetFormat("# ##0.00 [$m²]"); var areaStyle = workbook.CreateCellStyle(); areaStyle.DataFormat = areaFormatId; var volumeFormat = workbook.CreateDataFormat(); var volumeFormatId = volumeFormat.GetFormat("# ##0.00 [$m³]"); var volumeStyle = workbook.CreateCellStyle(); volumeStyle.DataFormat = volumeFormatId; //打開IFC模型。 不會改變模型中的任何東西,因此咱們能夠把編輯器的信息保留下來。 using (var model = IfcStore.Open("SampleHouse.ifc")) { //獲取模型中的全部空間. //須要 ToList() 方便使用 Foreach var spaces = model.Instances.OfType<IIfcSpace>().ToList(); //設置報表標題 sheet.GetRow(0).GetCell(0) .SetCellValue($"Space Report ({spaces.Count} spaces)"); foreach (var space in spaces) { //寫報表數據 WriteSpaceRow(space, sheet, areaStyle, volumeStyle); } } //保存 報表 using (var stream = File.Create("spaces.xlsx")) { workbook.Write(stream); stream.Close(); } //打開保存的EXCEL 文件 Process.Start("spaces.xlsx");
此代碼使用如下函數從IFC讀取數據併爲每一個空間寫入一行ui
private static void WriteSpaceRow(IIfcSpace space, ISheet sheet, ICellStyle areaStyle, ICellStyle volumeStyle) { var row = sheet.CreateRow(sheet.LastRowNum + 1); var name = space.Name; row.CreateCell(0).SetCellValue(name); var floor = GetFloor(space); row.CreateCell(1).SetCellValue(floor?.Name); var area = GetArea(space); if (area != null) { var cell = row.CreateCell(2); cell.CellStyle = areaStyle; // 若是來自屬性而不是數量,那麼僵不能保證它是數字 if (area.UnderlyingSystemType == typeof(double)) cell.SetCellValue((double)(area.Value)); else cell.SetCellValue(area.ToString()); } var volume = GetVolume(space); if (volume != null) { var cell = row.CreateCell(3); cell.CellStyle = volumeStyle; // 若是來自屬性而不是數量,那麼將不能保證它是數字 if (volume.UnderlyingSystemType == typeof(double)) cell.SetCellValue((double)(volume.Value)); else cell.SetCellValue(volume.ToString()); } }
要得到包含空間的樓層,您須要執行此操做:spa
private static IIfcBuildingStorey GetFloor(IIfcSpace space) { return //獲取全部對象化的關係,這些關係將按此空間進行分解 space.Decomposes //選擇分解的對象(這些對象多是其餘空間或建築樓層) .Select(r => r.RelatingObject) //僅獲取樓層 .OfType<IIfcBuildingStorey>() //獲取第一個 .FirstOrDefault(); }
IFC包含數據基礎結構,用於存儲與產品及其類型相關的任意數據。這種基礎設施至關複雜。存儲數據的兩種主要方式是數量或屬性。數量是明確的,它們包含的值的類型,其中屬性能夠包含許多不一樣的數據類型做爲值。對於面積和體積,若是定義了數量,則最好從數量中獲取值code
private static IIfcValue GetArea(IIfcProduct product) { //嘗試先從數量中獲取 var area = //獲取能夠定義屬性和數量集的全部關係 product.IsDefinedBy //在全部屬性和數量集之間搜索。 //您可能還但願按名稱搜索特定數量 .SelectMany(r => r.RelatingPropertyDefinition.PropertySetDefinitions) //數量集合 .OfType<IIfcElementQuantity>() //從數量集獲取全部數量 .SelectMany(qset => qset.Quantities) //咱們只對面積感興趣 .OfType<IIfcQuantityArea>() //咱們將採起第一個。顯然有一個以上的面積屬性 //因此, 要檢查的名稱。可是,咱們將保持它簡單的這個例子。 .FirstOrDefault()? .AreaValue; if (area != null) return area; //從屬性中獲取值 return GetProperty(product, "Area"); } private static IIfcValue GetVolume(IIfcProduct product) { var volume = product.IsDefinedBy .SelectMany(r => r.RelatingPropertyDefinition.PropertySetDefinitions) .OfType<IIfcElementQuantity>() .SelectMany(qset => qset.Quantities) .OfType<IIfcQuantityVolume>() .FirstOrDefault()?.VolumeValue; if (volume != null) return volume; return GetProperty(product, "Volume"); }
更常見的屬性在屬性集中搜索orm
private static IIfcValue GetProperty(IIfcProduct product, string name) { return //獲取能夠定義屬性和數量集的全部關係 product.IsDefinedBy //在全部屬性和數量集之間搜索。您可能還但願在特定屬性集中搜索 .SelectMany(r => r.RelatingPropertyDefinition.PropertySetDefinitions) //在這種狀況下, 只考慮屬性集。 .OfType<IIfcPropertySet>() //從全部屬性集中獲取全部屬性 .SelectMany(pset => pset.HasProperties) //只容許考慮單個值屬性。還有枚舉屬性, //表屬性、引用屬性、複雜屬性和其餘 .OfType<IIfcPropertySingleValue>() .Where(p => string.Equals(p.Name, name, System.StringComparison.OrdinalIgnoreCase) || p.Name.ToString().ToLower().Contains(name.ToLower())) .FirstOrDefault()?.NominalValue; }