NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - AI模型在线查看 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割
在这篇文章中,我将逐步介绍从IFC文件中提取数据以创建 Shift IFC4PowerBI 函数的步骤。通过了解此工作流程,你能够更轻松地将 IFC 数据合并到自己的流程中。
在本文中,我们将重点关注文件中的元数据,但你也可以应用相同的逻辑来提取几何图形。 当然你也可以利用NSDT 3DConvert这个强大的在线工具预览IFC几何模型(企业版可查看IFC属性数据并提供API)或者将IFC文件转换为GLTF、OBJ、STL等其他3D格式,而无需安装任何本地文件:
![](http://www.bimant.com/blog/content/images/2023/10/image-315.png)
1、文件格式
关于 IFC 文件,首先要了解的是它们是文本文件,非常长且复杂的文本文件,但仍然是文本文件。 从数据的角度来看,这非常好,因为你通常会在建筑业遇到几种类型的本机文件:
- 基于文本的文件,例如 IFC 和 P6 XML
- 数据库文件,例如 Asta Powerproject 文件
- 二进制文件,例如 Revit 或 DWG
可以使用标准工具读取文本和数据库文件,并将其引入 PowerBI 等工具或合并到数据工作流程中,而二进制文件通常只能由其源工具或其他经过认证的工具进行解释。 这使得基于文本的文件格式成为长期存储的绝佳解决方案,因为我们不依赖特定的软件来稍后查询数据。 Microsoft 的免费 VSCode 是我打开文本文件的首选工具,但你始终可以使用记事本、notepadd++ 或任何其他工具。 下面可以看到Github 上的一个 IFC 文件部分的屏幕截图:
![](http://www.bimant.com/blog/content/images/2023/10/image-310.png)
IFC 文件以 EXPRESS 结构编写,该结构涉及一大堆使用行号相互引用的行。 有一些与该文件相关的序言,但数据的主要内容是 DATA;
行之后的所有内容。
2、处理 IFC
第一步是使用以下 PowerQuery 将数据从简单行转换为 PowerBI 中的智能表:
let
Source = Table.FromColumns({Lines.FromBinary(#"IFC Binary", null, null, 1252)}),
// Split by Equal Sign
SplitByEquals = Table.SplitColumn(
Source,
"Column1",
Splitter.SplitTextByEachDelimiter({"="}, QuoteStyle.Csv, false),
{"Element ID", "Values"}
),
// Replace Single Quotes
ReplaceSingleQuotes = Table.ReplaceValue(
SplitByEquals,
"'",
"""",
Replacer.ReplaceText,
{"Values"}
),
// Trim Text
TrimValues = Table.TransformColumns(
ReplaceSingleQuotes,
{{"Values", Text.Trim, type text}, {"Element ID", Text.Trim, type text}}
),
// Filter Non-null Rows
FilterNonNullValues = Table.SelectRows(TrimValues, each ([Values] <> null)),
// Split by Open Parenthesis
SplitByOpenParenthesis = Table.SplitColumn(
FilterNonNullValues,
"Values",
Splitter.SplitTextByEachDelimiter({"("}, QuoteStyle.None, false),
{"Category", "Values"}
),
// Extract Data Before Close Parenthesis
ExtractBeforeCloseParenthesis = Table.TransformColumns(
SplitByOpenParenthesis,
{{"Values", each Text.BeforeDelimiter(_, ")", {0, RelativePosition.FromEnd}), type text}}
)
采取的基本步骤是:
- 加载 IFC 文件
- 使用
=
符号作为分隔符分割每一行。 在此步骤中,包含QuoteStyle.csv
参数非常重要,以确保忽略行实际值中包含的任何=
。 这为我们提供了一列包含行号的列和一列包含该行内容的列。 - 将单引号替换为双引号,因为 PowerBI 理解引号的方式有点奇怪,这有助于我们稍后拆分数据。
- 修剪值可以帮助我们删除前导或尾随空格,以保持一切整洁。
- 过滤掉空值以可靠地删除与行数据无关的任何内容,你可以选择单独处理此标题数据。
- 从值字段中删除前括号和尾括号。
现在我们有一个可爱的干净数据集,显示行号、数据类别和该类别中的数据值。
![](http://www.bimant.com/blog/content/images/2023/10/image-311.png)
由于我们希望以最有效的方式处理该文件,因此我们将稍微改变一下数据。
![](http://www.bimant.com/blog/content/images/2023/10/image-312.png)
由于 IFC 数据使用一对多方法,这意味着相同的参数可以应用于多个父项,因此从参数开始并返回到几何元素更有意义,而不是循环遍历每个元素并多次重新处理相同的参数。
// Filtering & Transformation for IFCPROPERTYSINGLEVALUE
FilterSingleValue = Table.SelectRows(
ExtractBeforeCloseParenthesis,
each ([Category] = "IFCPROPERTYSINGLEVALUE")
),
SplitSingleValueDetails = Table.SplitColumn(
FilterSingleValue,
"Values",
Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv),
{"Property Name", "Property Description", "Property Value", "Property Unit"}
),
ExtractSingleValue = Table.TransformColumns(
SplitSingleValueDetails,
{{"Property Value", each Text.BetweenDelimiters(_, "(", ")"), type text}}
),
因此,我们将首先过滤所有 IFCPROPERTYSINGLEVALUE 行,然后从那里开始:
- 用逗号分割值(记住之前的 QuoteStyle.csv)以获取属性名称、说明、值和度量单位。
- 从“属性值”列中提取值,目前我们不担心值类型,但我们可能会在将来的版本中返回并检索它以帮助自动计算。
这给我们留下了一组很好的属性:
![](http://www.bimant.com/blog/content/images/2023/10/image-313.png)
PowerBI 的有趣之处在于,我们现在可以一路回到第一步并从那里继续,将该表保留在内存中并稍后调用。 因此,接下来我们对 IFCPROPERTYSET
值执行相同的操作,最后合并到 IFCPROPERTYSINGLEVALUE
表中:
// Filtering & Transformation for IFCPROPERTYSET
FilterPropertySet = Table.SelectRows(
ExtractBeforeCloseParenthesis,
each [Category] = "IFCPROPERTYSET"
),
ExtractPropertyID = Table.AddColumn(
FilterPropertySet,
"Property ID",
each Text.BetweenDelimiters([Values], "(", ")"),
type text
),
ExtractPsetName = Table.TransformColumns(
ExtractPropertyID,
{{"Values", each Text.BetweenDelimiters(_, ",", ",", 1, 0), type text}}
),
RemoveQuotes = Table.ReplaceValue(ExtractPsetName, """", "", Replacer.ReplaceText, {"Values"}),
ExpandPropertyID = Table.ExpandListColumn(
Table.TransformColumns(
RemoveQuotes,
{
{
"Property ID",
Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv),
let
itemType = (type nullable text) meta [Serialized.Text = true]
in
type {itemType}
}
}
),
"Property ID"
),
RenamePsetColumns = Table.RenameColumns(ExpandPropertyID, {{"Values", "Pset Name"}}),
JoinSingleValueAndPset = Table.NestedJoin(
RenamePsetColumns,
{"Property ID"},
ExtractSingleValue,
{"Element ID"},
"Properties",
JoinKind.LeftOuter
),
ExpandProperties = Table.ExpandTableColumn(
JoinSingleValueAndPset,
"Properties",
{"Property Name", "Property Value"},
{"Property Name", "Property Value"}
),
然后我们再做两次同样的事情,一直回到 IFCBUILDINGELEMENT
和他们所有的子节点。
// Filtering & Transformation for IFCRELDEFINESBYPROPERTIES
FilterRelDefines = Table.SelectRows(
ExtractBeforeCloseParenthesis,
each [Category] = "IFCRELDEFINESBYPROPERTIES"
),
ExtractObjectID = Table.AddColumn(
FilterRelDefines,
"Object ID",
each Text.BetweenDelimiters([Values], "(", ")", {0, RelativePosition.FromEnd}, 0),
type text
),
ExtractPsetID = Table.TransformColumns(
ExtractObjectID,
{{"Values", each Text.AfterDelimiter(_, ",", {0, RelativePosition.FromEnd}), type text}}
),
RenameForRelDefines = Table.RenameColumns(ExtractPsetID, {{"Values", "Pset ID"}}),
ExpandObjectID = Table.ExpandListColumn(
Table.TransformColumns(
RenameForRelDefines,
{
{
"Object ID",
Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv),
let
itemType = (type nullable text) meta [Serialized.Text = true]
in
type {itemType}
}
}
),
"Object ID"
),
JoinRelDefinesAndPset = Table.NestedJoin(
ExpandObjectID,
{"Pset ID"},
ExpandProperties,
{"Element ID"},
"Property Sets",
JoinKind.LeftOuter
),
ExpandPropertySets = Table.ExpandTableColumn(
JoinRelDefinesAndPset,
"Property Sets",
{"Pset Name", "Property Name", "Property Value"},
{"Pset Name", "Property Name", "Property Value"}
),
// Final Join and Expansion
JoinMain = Table.NestedJoin(
ExpandPropertySets,
{"Object ID"},
ExtractBeforeCloseParenthesis,
{"Element ID"},
"Model",
JoinKind.LeftOuter
),
ExpandModel = Table.ExpandTableColumn(
JoinMain,
"Model",
{"Category", "Values"},
{"Ifc Type", "Values"}
),
FinalSplit = Table.SplitColumn(
ExpandModel,
"Values",
Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv),
{"GUID"}
)
in
FinalSplit
剩下的最终表是每个属性引用其所有父项,一直到 IFC 元素的 IFCGUID。
![](http://www.bimant.com/blog/content/images/2023/10/image-314.png)
此时,你可能想知道为什么我们有一个很长的属性列表,而不是每个项目单独一行,并在列中包含各种参数。 有几个原因:
- 它可能会导致无限数量的列,具体取决于 IFC 文件
- 不能再将属性集合并到数据集中
- 过滤和利用仪表板内的数据要容易得多
发布项目涉及的最后一件事是使用 PowerBI 中的 let
指令将其转换为函数:
let
Source = (#"IFC File" as any) => let
// REST OF THE CODE GOES HERE
in
FinalSplit
in
Source
原文链接:How to extract data from IFC files (in PowerBI)
BimAnt翻译整理,转载请标明出处