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.RelatedElemen