Ifc Data Extraction

从IFC文件中提取BIM模型的结构化数据,支持多格式导出。

已扫描
适合谁
建筑工程师、BIM建模师
不适合谁
无BIM项目需求的普通用户、不熟悉IFC格式的初学者
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @datadrivenconstruction/ifc-data-extraction

Skill 说明

命令、参数、文件名以原文为准

IFC 数据提取

概述

此技能使用 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 数据提取

概述

本技能用于从 IFC 文件中提取建筑信息模型数据,包括元素属性、层级结构、材料、数量统计、空间信息和元素间关系。

依赖库

  • ifcopenshell:IFC 文件解析库
  • pandas:数据处理与表格化
  • typing:类型提示支持

类定义:IFCExtractor

初始化

def __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 元素中提取基础信息。

内部逻辑:

  • 基础属性:GlobalId、Name、Description、ObjectType
  • 所在楼层:通过 ContainedInStructure 关系获取
  • 材料信息:通过 HasAssociations 中的 IfcRelAssociatesMaterial 获取
  • 类型名称:通过 IsTypedBy 关系获取
  • 属性集(Psets):使用 ifcopenshell.util.element.get_psets() 提取
  • 常见数量字段:

- Length:长度

- Width:宽度

- Height:高度

- Area:净侧面积或总表面积

- Volume:净体积或总体积


获取元素所在楼层

def _get_element_level(self, element) -> Optional[str]

根据元素的空间包含关系,获取其所在的建筑楼层名称。

返回值:

  • str:楼层名称,若无则返回 None

获取元素材料

def _get_element_material(self, element) -> Optional[str]

从元素关联的材料关系中提取材料名称。

优先级:

  1. 直接材料名称(Material.Name
  2. 层状材料集合中的第一层材料名称

返回值:

  • str:材料名称,若未找到则返回 None

获取元素类型名称

def _get_element_type(self, element) -> Optional[str]

通过 IsTypedBy 关系获取元素的类型名称。

返回值:

  • str:类型名称,若无则返回 None

提取数量统计

def extract_quantities(self) -> pd.DataFrame

按元素类型和楼层分组,汇总数量信息。

输出字段:

  • IFC_Type:元素类型
  • Level:所在楼层
  • Count:该类型在该楼层的数量
  • Volume:体积总和
  • Area:面积总和
  • Length:长度总和

提取楼层信息

def extract_levels(self) -> pd.DataFrame

提取所有建筑楼层(IfcBuildingStorey)的信息。

返回字段:

  • GlobalId
  • Name
  • Elevation:标高
  • Description

结果按标高升序排列。


提取空间/房间信息

def extract_spaces(self) -> pd.DataFrame

提取所有空间(IfcSpace)的数据。

返回字段:

  • GlobalId
  • Name
  • LongName
  • Level:所在楼层
  • Area:净地面面积
  • Volume:净体积
  • Height:高度

提取材料汇总

def extract_materials(self) -> pd.DataFrame

统计各材料在模型中的使用情况。

输出字段:

  • index:材料名称
  • count:使用该材料的元素数量
  • volume:该材料的总体积(基于 NetVolume)

提取元素间关系

def extract_relationships(self) -> pd.DataFrame

提取两类主要关系:

  1. 空间包含关系IfcRelContainedInSpatialStructure

表示元素被包含于某个结构中。

  1. 聚合关系IfcRelAggregates

表示某元素是另一个对象的一部分。

返回字段:

  • Element:元素 GlobalId
  • Element_Type:元素类型(如 IfcWall)
  • Relationship:关系类型("ContainedIn" 或 "PartOf")
  • Related_To:被关联对象的 GlobalId
  • Related_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 数据提取

概述

该技能用于从 IFC 文件中提取结构化数据,并支持导出为多种格式,包括 Excel、CSV、JSON 和 SQL 数据库。

类定义:IFCExporter

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())

快速参考

构件类型常见属性数量属性
IfcWallIsExternal, FireRatingLength, Height, Area, Volume
IfcSlabIsExternal, LoadBearingArea, Volume, Perimeter
IfcColumnLoadBearingHeight, CrossSectionArea
IfcBeamLoadBearingLength, CrossSectionArea
IfcDoorFireRating, AcousticRatingWidth, Height
IfcWindowThermalTransmittanceWidth, 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']
}

参考资源

  • IfcOpenShell: https://ifcopenshell.org
  • IFC 标准: https://www.buildingsmart.org/standards/bsi-standards/industry-foundation-classes/
  • DDC 官方网站: https://datadrivenconstruction.io

后续步骤

  • 查看 bim-validation-pipeline 以验证提取的数据
  • 查看 qto-report 以生成工程量清单报告
  • 查看 4d-simulation 以实现与进度计划的关联
D
@datadrivenconstruction

已收录 2 个 Skill

相关推荐