Ifc Data Extraction
/install ifc-data-extraction
\r \r
IFC Data Extraction\r
\r
Overview\r
\r 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.\r \r Based on Open BIM Standards - Working with vendor-neutral IFC format for maximum interoperability.\r \r
"IFC является открытым стандартом для обмена BIM-данными, позволяющим извлекать информацию независимо от программного обеспечения."\r — DDC Methodology\r \r
Quick Start\r
\r
import ifcopenshell\r
import ifcopenshell.util.element as element_util\r
import pandas as pd\r
\r
# Open IFC file\r
ifc = ifcopenshell.open("model.ifc")\r
\r
# Get project info\r
project = ifc.by_type("IfcProject")[0]\r
print(f"Project: {project.Name}")\r
\r
# Extract all walls\r
walls = ifc.by_type("IfcWall")\r
print(f"Total walls: {len(walls)}")\r
\r
# Get wall data\r
wall_data = []\r
for wall in walls:\r
psets = element_util.get_psets(wall)\r
wall_data.append({\r
'GlobalId': wall.GlobalId,\r
'Name': wall.Name,\r
'Type': wall.is_a(),\r
'Level': get_level(wall),\r
'Properties': psets\r
})\r
\r
df = pd.DataFrame(wall_data)\r
print(df.head())\r
```\r
\r
## Core Extraction Functions\r
\r
### Element Extractor Class\r
\r
```python\r
import ifcopenshell\r
import ifcopenshell.util.element as element_util\r
import ifcopenshell.util.placement as placement_util\r
import ifcopenshell.geom\r
import pandas as pd\r
from typing import List, Dict, Optional, Any\r
\r
class IFCExtractor:\r
"""Extract data from IFC files"""\r
\r
def __init__(self, ifc_path: str):\r
self.model = ifcopenshell.open(ifc_path)\r
self.settings = ifcopenshell.geom.settings()\r
\r
def get_project_info(self) -> Dict:\r
"""Extract project metadata"""\r
project = self.model.by_type("IfcProject")[0]\r
site = self.model.by_type("IfcSite")\r
building = self.model.by_type("IfcBuilding")\r
\r
return {\r
'project_id': project.GlobalId,\r
'project_name': project.Name,\r
'description': project.Description,\r
'site_count': len(site),\r
'building_count': len(building),\r
'schema': self.model.schema\r
}\r
\r
def get_all_elements(self, element_types: List[str] = None) -> pd.DataFrame:\r
"""Extract all elements of specified types"""\r
if element_types is None:\r
element_types = [\r
'IfcWall', 'IfcSlab', 'IfcColumn', 'IfcBeam',\r
'IfcDoor', 'IfcWindow', 'IfcStair', 'IfcRoof'\r
]\r
\r
all_elements = []\r
\r
for ifc_type in element_types:\r
elements = self.model.by_type(ifc_type)\r
\r
for elem in elements:\r
data = self._extract_element_data(elem)\r
data['IFC_Type'] = ifc_type\r
all_elements.append(data)\r
\r
return pd.DataFrame(all_elements)\r
\r
def _extract_element_data(self, element) -> Dict:\r
"""Extract data from single element"""\r
# Basic info\r
data = {\r
'GlobalId': element.GlobalId,\r
'Name': element.Name,\r
'Description': element.Description,\r
'ObjectType': element.ObjectType if hasattr(element, 'ObjectType') else None\r
}\r
\r
# Get level/storey\r
data['Level'] = self._get_element_level(element)\r
\r
# Get material\r
data['Material'] = self._get_element_material(element)\r
\r
# Get type\r
data['TypeName'] = self._get_element_type(element)\r
\r
# Get all property sets\r
psets = element_util.get_psets(element)\r
data['PropertySets'] = psets\r
\r
# Extract common quantities\r
base_quantities = psets.get('BaseQuantities', {})\r
data.update({\r
'Length': base_quantities.get('Length'),\r
'Width': base_quantities.get('Width'),\r
'Height': base_quantities.get('Height'),\r
'Area': base_quantities.get('NetSideArea') or base_quantities.get('GrossArea'),\r
'Volume': base_quantities.get('NetVolume') or base_quantities.get('GrossVolume')\r
})\r
\r
return data\r
\r
def _get_element_level(self, element) -> Optional[str]:\r
"""Get the building storey for an element"""\r
if hasattr(element, 'ContainedInStructure'):\r
for rel in element.ContainedInStructure or []:\r
if rel.RelatingStructure.is_a('IfcBuildingStorey'):\r
return rel.RelatingStructure.Name\r
return None\r
\r
def _get_element_material(self, element) -> Optional[str]:\r
"""Get material name for element"""\r
if hasattr(element, 'HasAssociations'):\r
for rel in element.HasAssociations or []:\r
if rel.is_a('IfcRelAssociatesMaterial'):\r
material = rel.RelatingMaterial\r
if hasattr(material, 'Name'):\r
return material.Name\r
elif hasattr(material, 'ForLayerSet'):\r
layers = material.ForLayerSet.MaterialLayers\r
if layers:\r
return layers[0].Material.Name\r
return None\r
\r
def _get_element_type(self, element) -> Optional[str]:\r
"""Get element type name"""\r
if hasattr(element, 'IsTypedBy'):\r
for rel in element.IsTypedBy or []:\r
return rel.RelatingType.Name\r
return None\r
\r
def extract_quantities(self) -> pd.DataFrame:\r
"""Extract quantities for all elements"""\r
elements = self.get_all_elements()\r
\r
# Group by category and level\r
quantities = elements.groupby(['IFC_Type', 'Level']).agg({\r
'GlobalId': 'count',\r
'Volume': 'sum',\r
'Area': 'sum',\r
'Length': 'sum'\r
}).rename(columns={'GlobalId': 'Count'}).reset_index()\r
\r
return quantities\r
\r
def extract_levels(self) -> pd.DataFrame:\r
"""Extract building levels/storeys"""\r
storeys = self.model.by_type("IfcBuildingStorey")\r
\r
level_data = []\r
for storey in storeys:\r
level_data.append({\r
'GlobalId': storey.GlobalId,\r
'Name': storey.Name,\r
'Elevation': storey.Elevation,\r
'Description': storey.Description\r
})\r
\r
return pd.DataFrame(level_data).sort_values('Elevation')\r
\r
def extract_spaces(self) -> pd.DataFrame:\r
"""Extract spaces/rooms"""\r
spaces = self.model.by_type("IfcSpace")\r
\r
space_data = []\r
for space in spaces:\r
psets = element_util.get_psets(space)\r
base_qty = psets.get('BaseQuantities', {})\r
\r
space_data.append({\r
'GlobalId': space.GlobalId,\r
'Name': space.Name,\r
'LongName': space.LongName,\r
'Level': self._get_element_level(space),\r
'Area': base_qty.get('NetFloorArea'),\r
'Volume': base_qty.get('NetVolume'),\r
'Height': base_qty.get('Height')\r
})\r
\r
return pd.DataFrame(space_data)\r
\r
def extract_materials(self) -> pd.DataFrame:\r
"""Extract material summary"""\r
materials = {}\r
\r
for elem in self.model.by_type("IfcProduct"):\r
material = self._get_element_material(elem)\r
if material:\r
if material not in materials:\r
materials[material] = {'count': 0, 'volume': 0}\r
\r
materials[material]['count'] += 1\r
\r
psets = element_util.get_psets(elem)\r
volume = psets.get('BaseQuantities', {}).get('NetVolume', 0)\r
if volume:\r
materials[material]['volume'] += volume\r
\r
return pd.DataFrame.from_dict(materials, orient='index').reset_index()\r
\r
def extract_relationships(self) -> pd.DataFrame:\r
"""Extract element relationships"""\r
relationships = []\r
\r
# Spatial containment\r
for rel in self.model.by_type("IfcRelContainedInSpatialStructure"):\r
for elem in rel.RelatedElements:\r
relationships.append({\r
'Element': elem.GlobalId,\r
'Element_Type': elem.is_a(),\r
'Relationship': 'ContainedIn',\r
'Related_To': rel.RelatingStructure.GlobalId,\r
'Related_Type': rel.RelatingStructure.is_a()\r
})\r
\r
# Aggregation\r
for rel in self.model.by_type("IfcRelAggregates"):\r
for part in rel.RelatedObjects:\r
relationships.append({\r
'Element': part.GlobalId,\r
'Element_Type': part.is_a(),\r
'Relationship': 'PartOf',\r
'Related_To': rel.RelatingObject.GlobalId,\r
'Related_Type': rel.RelatingObject.is_a()\r
})\r
\r
return pd.DataFrame(relationships)\r
```\r
\r
## Geometry Extraction\r
\r
### Extract Geometry Data\r
\r
```python\r
import numpy as np\r
\r
class IFCGeometryExtractor:\r
"""Extract geometry data from IFC elements"""\r
\r
def __init__(self, ifc_path: str):\r
self.model = ifcopenshell.open(ifc_path)\r
self.settings = ifcopenshell.geom.settings()\r
self.settings.set(self.settings.USE_WORLD_COORDS, True)\r
\r
def get_element_geometry(self, element) -> Dict:\r
"""Extract geometry for single element"""\r
try:\r
shape = ifcopenshell.geom.create_shape(self.settings, element)\r
\r
verts = shape.geometry.verts\r
faces = shape.geometry.faces\r
\r
# Calculate bounding box\r
vertices = np.array(verts).reshape(-1, 3)\r
min_coords = vertices.min(axis=0)\r
max_coords = vertices.max(axis=0)\r
dimensions = max_coords - min_coords\r
\r
return {\r
'GlobalId': element.GlobalId,\r
'vertices_count': len(vertices),\r
'faces_count': len(faces) // 3,\r
'min_x': min_coords[0],\r
'min_y': min_coords[1],\r
'min_z': min_coords[2],\r
'max_x': max_coords[0],\r
'max_y': max_coords[1],\r
'max_z': max_coords[2],\r
'length': dimensions[0],\r
'width': dimensions[1],\r
'height': dimensions[2],\r
'center_x': (min_coords[0] + max_coords[0]) / 2,\r
'center_y': (min_coords[1] + max_coords[1]) / 2,\r
'center_z': (min_coords[2] + max_coords[2]) / 2\r
}\r
except:\r
return {'GlobalId': element.GlobalId, 'error': 'Geometry extraction failed'}\r
\r
def get_bounding_boxes(self, element_type: str) -> pd.DataFrame:\r
"""Get bounding boxes for all elements of type"""\r
elements = self.model.by_type(element_type)\r
boxes = [self.get_element_geometry(e) for e in elements]\r
return pd.DataFrame(boxes)\r
\r
def calculate_volumes(self, element_type: str) -> pd.DataFrame:\r
"""Calculate volumes using geometry"""\r
elements = self.model.by_type(element_type)\r
volumes = []\r
\r
for elem in elements:\r
try:\r
shape = ifcopenshell.geom.create_shape(self.settings, elem)\r
# Calculate volume from mesh (simplified)\r
verts = np.array(shape.geometry.verts).reshape(-1, 3)\r
bbox_volume = np.prod(verts.max(axis=0) - verts.min(axis=0))\r
\r
volumes.append({\r
'GlobalId': elem.GlobalId,\r
'Name': elem.Name,\r
'BBox_Volume': bbox_volume\r
})\r
except:\r
pass\r
\r
return pd.DataFrame(volumes)\r
```\r
\r
## Export Functions\r
\r
### Export to Various Formats\r
\r
```python\r
class IFCExporter:\r
"""Export IFC data to various formats"""\r
\r
def __init__(self, extractor: IFCExtractor):\r
self.extractor = extractor\r
\r
def to_excel(self, output_path: str, include_all: bool = True):\r
"""Export to Excel with multiple sheets"""\r
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:\r
# Project info\r
project_info = pd.DataFrame([self.extractor.get_project_info()])\r
project_info.to_excel(writer, sheet_name='Project', index=False)\r
\r
# All elements\r
if include_all:\r
elements = self.extractor.get_all_elements()\r
elements.to_excel(writer, sheet_name='Elements', index=False)\r
\r
# Quantities\r
quantities = self.extractor.extract_quantities()\r
quantities.to_excel(writer, sheet_name='Quantities', index=False)\r
\r
# Levels\r
levels = self.extractor.extract_levels()\r
levels.to_excel(writer, sheet_name='Levels', index=False)\r
\r
# Spaces\r
spaces = self.extractor.extract_spaces()\r
spaces.to_excel(writer, sheet_name='Spaces', index=False)\r
\r
# Materials\r
materials = self.extractor.extract_materials()\r
materials.to_excel(writer, sheet_name='Materials', index=False)\r
\r
return output_path\r
\r
def to_csv(self, output_dir: str):\r
"""Export to multiple CSV files"""\r
import os\r
os.makedirs(output_dir, exist_ok=True)\r
\r
exports = {\r
'elements.csv': self.extractor.get_all_elements(),\r
'quantities.csv': self.extractor.extract_quantities(),\r
'levels.csv': self.extractor.extract_levels(),\r
'spaces.csv': self.extractor.extract_spaces(),\r
'materials.csv': self.extractor.extract_materials()\r
}\r
\r
for filename, df in exports.items():\r
df.to_csv(os.path.join(output_dir, filename), index=False)\r
\r
return output_dir\r
\r
def to_json(self, output_path: str):\r
"""Export to JSON"""\r
import json\r
\r
data = {\r
'project': self.extractor.get_project_info(),\r
'elements': self.extractor.get_all_elements().to_dict('records'),\r
'quantities': self.extractor.extract_quantities().to_dict('records'),\r
'levels': self.extractor.extract_levels().to_dict('records'),\r
'materials': self.extractor.extract_materials().to_dict('records')\r
}\r
\r
with open(output_path, 'w', encoding='utf-8') as f:\r
json.dump(data, f, indent=2, default=str)\r
\r
return output_path\r
\r
def to_database(self, connection_string: str, table_prefix: str = 'ifc_'):\r
"""Export to SQL database"""\r
from sqlalchemy import create_engine\r
\r
engine = create_engine(connection_string)\r
\r
tables = {\r
f'{table_prefix}elements': self.extractor.get_all_elements(),\r
f'{table_prefix}quantities': self.extractor.extract_quantities(),\r
f'{table_prefix}levels': self.extractor.extract_levels(),\r
f'{table_prefix}spaces': self.extractor.extract_spaces(),\r
f'{table_prefix}materials': self.extractor.extract_materials()\r
}\r
\r
for table_name, df in tables.items():\r
# Remove complex columns for database storage\r
simple_df = df.select_dtypes(exclude=['object']).copy()\r
for col in df.columns:\r
if df[col].dtype == 'object':\r
simple_df[col] = df[col].astype(str)\r
\r
simple_df.to_sql(table_name, engine, if_exists='replace', index=False)\r
\r
return list(tables.keys())\r
```\r
\r
## Quick Reference\r
\r
| Element Type | Common Properties | Quantities |\r
|-------------|-------------------|------------|\r
| IfcWall | IsExternal, FireRating | Length, Height, Area, Volume |\r
| IfcSlab | IsExternal, LoadBearing | Area, Volume, Perimeter |\r
| IfcColumn | LoadBearing | Height, CrossSectionArea |\r
| IfcBeam | LoadBearing | Length, CrossSectionArea |\r
| IfcDoor | FireRating, AcousticRating | Width, Height |\r
| IfcWindow | ThermalTransmittance | Width, Height, Area |\r
\r
## Property Set Lookup\r
\r
```python\r
# Common IFC Property Sets\r
PSETS = {\r
'Pset_WallCommon': ['IsExternal', 'LoadBearing', 'FireRating'],\r
'Pset_SlabCommon': ['IsExternal', 'LoadBearing', 'AcousticRating'],\r
'Pset_ColumnCommon': ['IsExternal', 'LoadBearing'],\r
'Pset_BeamCommon': ['LoadBearing', 'FireRating'],\r
'Pset_DoorCommon': ['FireRating', 'AcousticRating', 'SecurityRating'],\r
'Pset_WindowCommon': ['ThermalTransmittance', 'GlazingType'],\r
'BaseQuantities': ['Length', 'Width', 'Height', 'Area', 'Volume']\r
}\r
```\r
\r
## Resources\r
\r
- **IfcOpenShell**: https://ifcopenshell.org\r
- **IFC Standard**: https://www.buildingsmart.org/standards/bsi-standards/industry-foundation-classes/\r
- **DDC Website**: https://datadrivenconstruction.io\r
\r
## Next Steps\r
\r
- See `bim-validation-pipeline` for validating extracted data\r
- See `qto-report` for quantity take-off reports\r
- See `4d-simulation` for linking to schedules\r
- 确保已安装 OpenClaw(本地或 Docker 部署)
- 在对话框中输入安装命令:
/install ifc-data-extraction - 安装完成后,直接呼叫该 Skill 的名称或使用
/ifc-data-extraction触发 - 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
Ifc Data Extraction 是什么?
Extract structured data from IFC (Industry Foundation Classes) files using IfcOpenShell. Parse BIM models, extract quantities, properties, spatial relationships, and export to various formats. 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 1263 次。
如何安装 Ifc Data Extraction?
在 OpenClaw 或 Claude Code 对话框中运行命令「/install ifc-data-extraction」即可一键安装,无需额外配置。
Ifc Data Extraction 是免费的吗?
是的,Ifc Data Extraction 完全免费(开源免费),可自由下载、安装和使用。
Ifc Data Extraction 支持哪些平台?
Ifc Data Extraction 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。
谁开发了 Ifc Data Extraction?
由 datadrivenconstruction(@datadrivenconstruction)开发并维护,当前版本 v2.0.0。