Finance Data Analysis
AI驱动的财务分析与可视化工具,支持KPI追踪与自动化报告生成。
从IFC文件中提取BIM模型的结构化数据,支持多格式导出。
openclaw skills install @datadrivenconstruction/ifc-data-extraction命令、参数、文件名以原文为准
此技能使用 IfcOpenShell 提供全面的 IFC 文件解析与数据提取功能。可从 BIM 模型中提取构件数据、工程量、属性及关系信息,用于分析与报告。
基于开放建筑信息模型(Open BIM)标准 —— 采用厂商无关的 IFC 格式,实现最大化的互操作性。
"IFC 是一种开放标准,用于交换 BIM 数据,使信息提取独立于软件平台。"
—— DDC 方法论
import ifcopenshell
import ifcopenshell.util.element as element_util
import pandas as pd
# 打开 IFC 文件
ifc = ifcopenshell.open("model.ifc")
# 获取项目信息
project = ifc.by_type("IfcProject")[0]
print(f"项目: {project.Name}")
# 提取所有墙体
walls = ifc.by_type("IfcWall")
print(f"墙体总数: {len(walls)}")
# 获取墙体数据
wall_data = []
for wall in walls:
psets = element_util.get_psets(wall)
wall_data.append({
'GlobalId': wall.GlobalId,
'Name': wall.Name,
'Type': wall.is_a(),
'Level': get_level(wall),
'Properties': psets
})
df = pd.DataFrame(wall_data)
print(df.head())markdown
本技能用于从 IFC 文件中提取建筑信息模型数据,包括元素属性、层级结构、材料、数量统计、空间信息和元素间关系。
ifcopenshell:IFC 文件解析库pandas:数据处理与表格化typing:类型提示支持IFCExtractordef __init__(self, ifc_path: str)打开指定路径的 IFC 文件,并初始化几何设置。
参数:
ifc_path (str):IFC 文件路径def get_project_info(self) -> Dict提取项目元数据。
返回值:
Dict 包含以下字段: - project_id:项目唯一标识符(GlobalId)
- project_name:项目名称
- description:项目描述
- site_count:场地数量
- building_count:建筑数量
- schema:使用的 IFC 标准版本
def get_all_elements(self, element_types: List[str] = None) -> pd.DataFrame提取指定类型的全部元素数据。
参数:
element_types (List[str], 可选):要提取的 IFC 元素类型列表。默认为: ['IfcWall', 'IfcSlab', 'IfcColumn', 'IfcBeam',
'IfcDoor', 'IfcWindow', 'IfcStair', 'IfcRoof']返回值:
pd.DataFrame:包含每个元素的详细信息,列包括: - GlobalId
- Name
- Description
- ObjectType
- Level(所在楼层)
- Material(材料名称)
- TypeName(类型名称)
- PropertySets(所有属性集)
- Length, Width, Height, Area, Volume(基本量)
def _extract_element_data(self, element) -> Dict从单个 IFC 元素中提取基础信息。
内部逻辑:
ContainedInStructure 关系获取HasAssociations 中的 IfcRelAssociatesMaterial 获取IsTypedBy 关系获取ifcopenshell.util.element.get_psets() 提取 - Length:长度
- Width:宽度
- Height:高度
- Area:净侧面积或总表面积
- Volume:净体积或总体积
def _get_element_level(self, element) -> Optional[str]根据元素的空间包含关系,获取其所在的建筑楼层名称。
返回值:
str:楼层名称,若无则返回 Nonedef _get_element_material(self, element) -> Optional[str]从元素关联的材料关系中提取材料名称。
优先级:
Material.Name)返回值:
str:材料名称,若未找到则返回 Nonedef _get_element_type(self, element) -> Optional[str]通过 IsTypedBy 关系获取元素的类型名称。
返回值:
str:类型名称,若无则返回 Nonedef extract_quantities(self) -> pd.DataFrame按元素类型和楼层分组,汇总数量信息。
输出字段:
IFC_Type:元素类型Level:所在楼层Count:该类型在该楼层的数量Volume:体积总和Area:面积总和Length:长度总和def extract_levels(self) -> pd.DataFrame提取所有建筑楼层(IfcBuildingStorey)的信息。
返回字段:
GlobalIdNameElevation:标高Description结果按标高升序排列。
def extract_spaces(self) -> pd.DataFrame提取所有空间(IfcSpace)的数据。
返回字段:
GlobalIdNameLongNameLevel:所在楼层Area:净地面面积Volume:净体积Height:高度def extract_materials(self) -> pd.DataFrame统计各材料在模型中的使用情况。
输出字段:
index:材料名称count:使用该材料的元素数量volume:该材料的总体积(基于 NetVolume)def extract_relationships(self) -> pd.DataFrame提取两类主要关系:
IfcRelContainedInSpatialStructure)表示元素被包含于某个结构中。
IfcRelAggregates)表示某元素是另一个对象的一部分。
返回字段:
Element:元素 GlobalIdElement_Type:元素类型(如 IfcWall)Relationship:关系类型("ContainedIn" 或 "PartOf")Related_To:被关联对象的 GlobalIdRelated_Type:被关联对象类型(待补充具体实现内容)
import numpy as np
class IFCGeometryExtractor:
"""从 IFC 元素中提取几何数据"""
def __init__(self, ifc_path: str):
self.model = ifcopenshell.open(ifc_path)
self.settings = ifcopenshell.geom.settings()
self.settings.set(self.settings.USE_WORLD_COORDS, True)
def get_element_geometry(self, element) -> Dict:
"""提取单个元素的几何信息"""
try:
shape = ifcopenshell.geom.create_shape(self.settings, element)
verts = shape.geometry.verts
faces = shape.geometry.faces
# 计算包围盒
vertices = np.array(verts).reshape(-1, 3)
min_coords = vertices.min(axis=0)
max_coords = vertices.max(axis=0)
dimensions = max_coords - min_coords
return {
'GlobalId': element.GlobalId,
'vertices_count': len(vertices),
'faces_count': len(faces) // 3,
'min_x': min_coords[0],
'min_y': min_coords[1],
'min_z': min_coords[2],
'max_x': max_coords[0],
'max_y': max_coords[1],
'max_z': max_coords[2],
'length': dimensions[0],
'width': dimensions[1],
'height': dimensions[2],
'center_x': (min_coords[0] + max_coords[0]) / 2,
'center_y': (min_coords[1] + max_coords[1]) / 2,
'center_z': (min_coords[2] + max_coords[2]) / 2
}
except:
return {'GlobalId': element.GlobalId, 'error': '几何提取失败'}
def get_bounding_boxes(self, element_type: str) -> pd.DataFrame:
"""获取指定类型所有元素的包围盒信息"""
elements = self.model.by_type(element_type)
boxes = [self.get_element_geometry(e) for e in elements]
return pd.DataFrame(boxes)
def calculate_volumes(self, element_type: str) -> pd.DataFrame:
"""基于几何数据计算体积"""
elements = self.model.by_type(element_type)
volumes = []
for elem in elements:
try:
shape = ifcopenshell.geom.create_shape(self.settings, elem)
# 通过网格简化计算体积
verts = np.array(shape.geometry.verts).reshape(-1, 3)
bbox_volume = np.prod(verts.max(axis=0) - verts.min(axis=0))
volumes.append({
'GlobalId': elem.GlobalId,
'Name': elem.Name,
'BBox_Volume': bbox_volume
})
except:
pass
return pd.DataFrame(volumes)markdown
该技能用于从 IFC 文件中提取结构化数据,并支持导出为多种格式,包括 Excel、CSV、JSON 和 SQL 数据库。
class IFCExporter:
"""将 IFC 数据导出到多种格式"""
def __init__(self, extractor: IFCExtractor):
self.extractor = extractor
def to_excel(self, output_path: str, include_all: bool = True):
"""导出为包含多个工作表的 Excel 文件"""
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
# 项目信息
project_info = pd.DataFrame([self.extractor.get_project_info()])
project_info.to_excel(writer, sheet_name='Project', index=False)
# 所有构件
if include_all:
elements = self.extractor.get_all_elements()
elements.to_excel(writer, sheet_name='Elements', index=False)
# 数量信息
quantities = self.extractor.extract_quantities()
quantities.to_excel(writer, sheet_name='Quantities', index=False)
# 层高信息
levels = self.extractor.extract_levels()
levels.to_excel(writer, sheet_name='Levels', index=False)
# 空间信息
spaces = self.extractor.extract_spaces()
spaces.to_excel(writer, sheet_name='Spaces', index=False)
# 材料信息
materials = self.extractor.extract_materials()
materials.to_excel(writer, sheet_name='Materials', index=False)
return output_path
def to_csv(self, output_dir: str):
"""导出为多个 CSV 文件"""
import os
os.makedirs(output_dir, exist_ok=True)
exports = {
'elements.csv': self.extractor.get_all_elements(),
'quantities.csv': self.extractor.extract_quantities(),
'levels.csv': self.extractor.extract_levels(),
'spaces.csv': self.extractor.extract_spaces(),
'materials.csv': self.extractor.extract_materials()
}
for filename, df in exports.items():
df.to_csv(os.path.join(output_dir, filename), index=False)
return output_dir
def to_json(self, output_path: str):
"""导出为 JSON 格式"""
import json
data = {
'project': self.extractor.get_project_info(),
'elements': self.extractor.get_all_elements().to_dict('records'),
'quantities': self.extractor.extract_quantities().to_dict('records'),
'levels': self.extractor.extract_levels().to_dict('records'),
'materials': self.extractor.extract_materials().to_dict('records')
}
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, default=str)
return output_path
def to_database(self, connection_string: str, table_prefix: str = 'ifc_'):
"""导出到 SQL 数据库"""
from sqlalchemy import create_engine
engine = create_engine(connection_string)
tables = {
f'{table_prefix}elements': self.extractor.get_all_elements(),
f'{table_prefix}quantities': self.extractor.extract_quantities(),
f'{table_prefix}levels': self.extractor.extract_levels(),
f'{table_prefix}spaces': self.extractor.extract_spaces(),
f'{table_prefix}materials': self.extractor.extract_materials()
}
for table_name, df in tables.items():
# 移除复杂列以适配数据库存储
simple_df = df.select_dtypes(exclude=['object']).copy()
for col in df.columns:
if df[col].dtype == 'object':
simple_df[col] = df[col].astype(str)
simple_df.to_sql(table_name, engine, if_exists='replace', index=False)
return list(tables.keys())| 构件类型 | 常见属性 | 数量属性 |
|---|---|---|
| IfcWall | IsExternal, FireRating | Length, Height, Area, Volume |
| IfcSlab | IsExternal, LoadBearing | Area, Volume, Perimeter |
| IfcColumn | LoadBearing | Height, CrossSectionArea |
| IfcBeam | LoadBearing | Length, CrossSectionArea |
| IfcDoor | FireRating, AcousticRating | Width, Height |
| IfcWindow | ThermalTransmittance | Width, Height, Area |
# 常见 IFC 属性集
PSETS = {
'Pset_WallCommon': ['IsExternal', 'LoadBearing', 'FireRating'],
'Pset_SlabCommon': ['IsExternal', 'LoadBearing', 'AcousticRating'],
'Pset_ColumnCommon': ['IsExternal', 'LoadBearing'],
'Pset_BeamCommon': ['LoadBearing', 'FireRating'],
'Pset_DoorCommon': ['FireRating', 'AcousticRating', 'SecurityRating'],
'Pset_WindowCommon': ['ThermalTransmittance', 'GlazingType'],
'BaseQuantities': ['Length', 'Width', 'Height', 'Area', 'Volume']
}bim-validation-pipeline 以验证提取的数据qto-report 以生成工程量清单报告4d-simulation 以实现与进度计划的关联已收录 2 个 Skill