IFC 数据提取:BIM 数据解析与导出 - Openclaw Skills
作者:互联网
2026-03-25
什么是 IFC 数据提取?
IFC 数据提取技能利用 IfcOpenShell 填补了复杂 BIM 模型与可用数据之间的空白。它允许开发人员和 BIM 经理通过编程方式访问中立厂商 IFC 文件中的项目元数据、构件属性和空间层级。
通过使用此 Openclaw Skills 实现,用户可以自动检索建筑信息,确保不同建筑、工程和施工 (AEC) 软件平台之间的互操作性。该技能将原始 IFC 数据转换为干净、结构化的格式,适用于报告和进一步的技术分析。
下载入口:https://github.com/openclaw/skills/tree/main/skills/datadrivenconstruction/ifc-data-extraction
安装与下载
1. ClawHub CLI
从源直接安装技能的最快方式。
npx clawhub@latest install ifc-data-extraction
2. 手动安装
将技能文件夹复制到以下位置之一
全局模式~/.openclaw/skills/
工作区
/skills/
优先级:工作区 > 本地 > 内置
3. 提示词安装
将此提示词复制到 OpenClaw 即可自动安装。
请帮我使用 Clawhub 安装 ifc-data-extraction。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。
IFC 数据提取 应用场景
- 用于建筑成本估算和采购的自动工程量清单 (QTO)。
- 提取属性集和材料数据,用于可持续性审计和碳足迹计算。
- 生成空间关系图,以了解建筑层级和包含关系。
- 将 BIM 模型转换为 CSV 或 SQL 等结构化格式,用于下游数据分析。
- 根据项目要求和建筑标准验证模型数据。
- 使用开源 IfcOpenShell 库加载 IFC 文件来初始化提取器。
- 根据项目内部架构向模型查询特定的 IFC 类型,如墙、楼板或空间。
- 遍历构件关系以识别空间包含、材料关联和分配的属性集。
- 计算几何数据,包括包围盒和体积(如果空间或碰撞分析需要)。
- 将处理后的数据导出为结构化格式,如 pandas DataFrame、Excel 表格或 SQL 数据库,用于外部报告。
IFC 数据提取 配置指南
要开始使用此技能,请使用 pip 安装所需的依赖项:
pip install ifcopenshell pandas numpy sqlalchemy openpyxl
确保您的工作目录中有一个 IFC 文件(架构版本 IFC2x3 或 IFC4),以便开始使用此 Openclaw Skills 资源进行提取。
IFC 数据提取 数据架构与分类体系
该技能将提取的 BIM 数据组织成以下架构:
| 类别 | 关键数据点 |
|---|---|
| 项目信息 | GlobalId, 名称, 描述, 架构版本 |
| 构件 | GlobalId, IFC类型, 楼层, 材料, 属性集 |
| 工程量 | 长度, 宽度, 高度, 净面积, 净体积 |
| 关系 | 构件 ID, 关系类型 (包含于, 属于), 相关对象 |
| 几何 | 顶点, 面, 包围盒 (最小/最大 X,Y,Z), 中心点 |
name: "ifc-data-extraction" description: "Extract structured data from IFC (Industry Foundation Classes) files using IfcOpenShell. Parse BIM models, extract quantities, properties, spatial relationships, and export to various formats."
IFC Data Extraction
Overview
This skill provides comprehensive IFC file parsing and data extraction using IfcOpenShell. Extract element data, quantities, properties, and relationships from BIM models for analysis and reporting.
Based on Open BIM Standards - Working with vendor-neutral IFC format for maximum interoperability.
"IFC является открытым стандартом для обмена BIM-данными, позволяющим извлекать информацию независимо от программного обеспечения." — DDC Methodology
Quick Start
import ifcopenshell
import ifcopenshell.util.element as element_util
import pandas as pd
# Open IFC file
ifc = ifcopenshell.open("model.ifc")
# Get project info
project = ifc.by_type("IfcProject")[0]
print(f"Project: {project.Name}")
# Extract all walls
walls = ifc.by_type("IfcWall")
print(f"Total walls: {len(walls)}")
# Get wall data
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())
Core Extraction Functions
Element Extractor Class
import ifcopenshell
import ifcopenshell.util.element as element_util
import ifcopenshell.util.placement as placement_util
import ifcopenshell.geom
import pandas as pd
from typing import List, Dict, Optional, Any
class IFCExtractor:
"""Extract data from IFC files"""
def __init__(self, ifc_path: str):
self.model = ifcopenshell.open(ifc_path)
self.settings = ifcopenshell.geom.settings()
def get_project_info(self) -> Dict:
"""Extract project metadata"""
project = self.model.by_type("IfcProject")[0]
site = self.model.by_type("IfcSite")
building = self.model.by_type("IfcBuilding")
return {
'project_id': project.GlobalId,
'project_name': project.Name,
'description': project.Description,
'site_count': len(site),
'building_count': len(building),
'schema': self.model.schema
}
def get_all_elements(self, element_types: List[str] = None) -> pd.DataFrame:
"""Extract all elements of specified types"""
if element_types is None:
element_types = [
'IfcWall', 'IfcSlab', 'IfcColumn', 'IfcBeam',
'IfcDoor', 'IfcWindow', 'IfcStair', 'IfcRoof'
]
all_elements = []
for ifc_type in element_types:
elements = self.model.by_type(ifc_type)
for elem in elements:
data = self._extract_element_data(elem)
data['IFC_Type'] = ifc_type
all_elements.append(data)
return pd.DataFrame(all_elements)
def _extract_element_data(self, element) -> Dict:
"""Extract data from single element"""
# Basic info
data = {
'GlobalId': element.GlobalId,
'Name': element.Name,
'Description': element.Description,
'ObjectType': element.ObjectType if hasattr(element, 'ObjectType') else None
}
# Get level/storey
data['Level'] = self._get_element_level(element)
# Get material
data['Material'] = self._get_element_material(element)
# Get type
data['TypeName'] = self._get_element_type(element)
# Get all property sets
psets = element_util.get_psets(element)
data['PropertySets'] = psets
# Extract common quantities
base_quantities = psets.get('BaseQuantities', {})
data.update({
'Length': base_quantities.get('Length'),
'Width': base_quantities.get('Width'),
'Height': base_quantities.get('Height'),
'Area': base_quantities.get('NetSideArea') or base_quantities.get('GrossArea'),
'Volume': base_quantities.get('NetVolume') or base_quantities.get('GrossVolume')
})
return data
def _get_element_level(self, element) -> Optional[str]:
"""Get the building storey for an element"""
if hasattr(element, 'ContainedInStructure'):
for rel in element.ContainedInStructure or []:
if rel.RelatingStructure.is_a('IfcBuildingStorey'):
return rel.RelatingStructure.Name
return None
def _get_element_material(self, element) -> Optional[str]:
"""Get material name for element"""
if hasattr(element, 'HasAssociations'):
for rel in element.HasAssociations or []:
if rel.is_a('IfcRelAssociatesMaterial'):
material = rel.RelatingMaterial
if hasattr(material, 'Name'):
return material.Name
elif hasattr(material, 'ForLayerSet'):
layers = material.ForLayerSet.MaterialLayers
if layers:
return layers[0].Material.Name
return None
def _get_element_type(self, element) -> Optional[str]:
"""Get element type name"""
if hasattr(element, 'IsTypedBy'):
for rel in element.IsTypedBy or []:
return rel.RelatingType.Name
return None
def extract_quantities(self) -> pd.DataFrame:
"""Extract quantities for all elements"""
elements = self.get_all_elements()
# Group by category and level
quantities = elements.groupby(['IFC_Type', 'Level']).agg({
'GlobalId': 'count',
'Volume': 'sum',
'Area': 'sum',
'Length': 'sum'
}).rename(columns={'GlobalId': 'Count'}).reset_index()
return quantities
def extract_levels(self) -> pd.DataFrame:
"""Extract building levels/storeys"""
storeys = self.model.by_type("IfcBuildingStorey")
level_data = []
for storey in storeys:
level_data.append({
'GlobalId': storey.GlobalId,
'Name': storey.Name,
'Elevation': storey.Elevation,
'Description': storey.Description
})
return pd.DataFrame(level_data).sort_values('Elevation')
def extract_spaces(self) -> pd.DataFrame:
"""Extract spaces/rooms"""
spaces = self.model.by_type("IfcSpace")
space_data = []
for space in spaces:
psets = element_util.get_psets(space)
base_qty = psets.get('BaseQuantities', {})
space_data.append({
'GlobalId': space.GlobalId,
'Name': space.Name,
'LongName': space.LongName,
'Level': self._get_element_level(space),
'Area': base_qty.get('NetFloorArea'),
'Volume': base_qty.get('NetVolume'),
'Height': base_qty.get('Height')
})
return pd.DataFrame(space_data)
def extract_materials(self) -> pd.DataFrame:
"""Extract material summary"""
materials = {}
for elem in self.model.by_type("IfcProduct"):
material = self._get_element_material(elem)
if material:
if material not in materials:
materials[material] = {'count': 0, 'volume': 0}
materials[material]['count'] += 1
psets = element_util.get_psets(elem)
volume = psets.get('BaseQuantities', {}).get('NetVolume', 0)
if volume:
materials[material]['volume'] += volume
return pd.DataFrame.from_dict(materials, orient='index').reset_index()
def extract_relationships(self) -> pd.DataFrame:
"""Extract element relationships"""
relationships = []
# Spatial containment
for rel in self.model.by_type("IfcRelContainedInSpatialStructure"):
for elem in rel.RelatedElements:
relationships.append({
'Element': elem.GlobalId,
'Element_Type': elem.is_a(),
'Relationship': 'ContainedIn',
'Related_To': rel.RelatingStructure.GlobalId,
'Related_Type': rel.RelatingStructure.is_a()
})
# Aggregation
for rel in self.model.by_type("IfcRelAggregates"):
for part in rel.RelatedObjects:
relationships.append({
'Element': part.GlobalId,
'Element_Type': part.is_a(),
'Relationship': 'PartOf',
'Related_To': rel.RelatingObject.GlobalId,
'Related_Type': rel.RelatingObject.is_a()
})
return pd.DataFrame(relationships)
Geometry Extraction
Extract Geometry Data
import numpy as np
class IFCGeometryExtractor:
"""Extract geometry data from IFC elements"""
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:
"""Extract geometry for single element"""
try:
shape = ifcopenshell.geom.create_shape(self.settings, element)
verts = shape.geometry.verts
faces = shape.geometry.faces
# Calculate bounding box
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': 'Geometry extraction failed'}
def get_bounding_boxes(self, element_type: str) -> pd.DataFrame:
"""Get bounding boxes for all elements of type"""
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:
"""Calculate volumes using geometry"""
elements = self.model.by_type(element_type)
volumes = []
for elem in elements:
try:
shape = ifcopenshell.geom.create_shape(self.settings, elem)
# Calculate volume from mesh (simplified)
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)
Export Functions
Export to Various Formats
class IFCExporter:
"""Export IFC data to various formats"""
def __init__(self, extractor: IFCExtractor):
self.extractor = extractor
def to_excel(self, output_path: str, include_all: bool = True):
"""Export to Excel with multiple sheets"""
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
# Project info
project_info = pd.DataFrame([self.extractor.get_project_info()])
project_info.to_excel(writer, sheet_name='Project', index=False)
# All elements
if include_all:
elements = self.extractor.get_all_elements()
elements.to_excel(writer, sheet_name='Elements', index=False)
# Quantities
quantities = self.extractor.extract_quantities()
quantities.to_excel(writer, sheet_name='Quantities', index=False)
# Levels
levels = self.extractor.extract_levels()
levels.to_excel(writer, sheet_name='Levels', index=False)
# Spaces
spaces = self.extractor.extract_spaces()
spaces.to_excel(writer, sheet_name='Spaces', index=False)
# Materials
materials = self.extractor.extract_materials()
materials.to_excel(writer, sheet_name='Materials', index=False)
return output_path
def to_csv(self, output_dir: str):
"""Export to multiple CSV files"""
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):
"""Export to 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_'):
"""Export to SQL database"""
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():
# Remove complex columns for database storage
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())
Quick Reference
| Element Type | Common Properties | Quantities |
|---|---|---|
| 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 |
Property Set Lookup
# Common IFC Property Sets
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']
}
Resources
- IfcOpenShell: https://ifcopenshell.org
- IFC Standard: https://www.buildingsmart.org/standards/bsi-standards/industry-foundation-classes/
- DDC Website: https://datadrivenconstruction.io
Next Steps
- See
bim-validation-pipelinefor validating extracted data - See
qto-reportfor quantity take-off reports - See
4d-simulationfor linking to schedules
相关推荐
专题
+ 收藏
+ 收藏
+ 收藏
+ 收藏
+ 收藏
最新数据
相关文章
信号管道:自动化营销情报工具 - Openclaw Skills
技能收益追踪器:监控 Openclaw 技能并实现变现
AI 合规准备就绪度:评估与治理工具 - Openclaw Skills
FOSMVVM ServerRequest 测试生成器:自动化 API 测试 - Openclaw Skills
酒店搜索器:AI 赋能的住宿与位置情报 - Openclaw Skills
Dub 链接 API:程序化链接管理 - Openclaw Skills
IntercomSwap:P2P BTC 与 USDT 跨链兑换 - Openclaw Skills
spotplay:macOS 原生 Spotify 播放控制 - Openclaw Skills
DeepSeek OCR:AI驱动的图像文本识别 - Openclaw Skills
Web Navigator:自动化网页研究与浏览 - Openclaw Skills
AI精选
