Ifc To Excel
/install ifc-to-excel
\r \r
IFC to Excel Conversion\r
\r
Business Case\r
\r
Problem Statement\r
IFC (Industry Foundation Classes) is the open BIM standard, but:\r
- Reading IFC requires specialized software\r
- Property extraction needs programming knowledge\r
- Batch processing is manual and time-consuming\r
- Integration with analytics tools is complex\r \r
Solution\r
IfcExporter.exe converts IFC files to structured Excel databases, making BIM data accessible for analysis, validation, and reporting.\r \r
Business Value\r
- Open standard - Process any IFC file (2x3, 4x, 4.3)\r
- No licenses - Works offline without BIM software\r
- Data extraction - All properties, quantities, materials\r
- 3D geometry - Export to Collada DAE format\r
- Pipeline ready - Integrate with ETL workflows\r \r
Technical Implementation\r
\r
CLI Syntax\r
IfcExporter.exe \x3Cinput_ifc> [options]\r
```\r
\r
### Supported IFC Versions\r
| Version | Schema | Description |\r
|---------|--------|-------------|\r
| IFC2x3 | MVD | Most common exchange format |\r
| IFC4 | ADD1 | Enhanced properties |\r
| IFC4x1 | Alignment | Infrastructure support |\r
| IFC4x3 | Latest | Full infrastructure |\r
\r
### Output Formats\r
| Output | Description |\r
|--------|-------------|\r
| `.xlsx` | Excel database with elements and properties |\r
| `.dae` | Collada 3D geometry with matching IDs |\r
\r
### Options\r
| Option | Description |\r
|--------|-------------|\r
| `bbox` | Include element bounding boxes |\r
| `-no-xlsx` | Skip Excel export |\r
| `-no-collada` | Skip 3D geometry export |\r
\r
### Examples\r
\r
```bash\r
# Basic conversion (XLSX + DAE)\r
IfcExporter.exe "C:\Models\Building.ifc"\r
\r
# With bounding boxes\r
IfcExporter.exe "C:\Models\Building.ifc" bbox\r
\r
# Excel only (no 3D geometry)\r
IfcExporter.exe "C:\Models\Building.ifc" -no-collada\r
\r
# Batch processing\r
for /R "C:\IFC_Models" %f in (*.ifc) do IfcExporter.exe "%f" bbox\r
```\r
\r
### Python Integration\r
\r
```python\r
import subprocess\r
import pandas as pd\r
from pathlib import Path\r
from typing import List, Optional, Dict, Any, Set\r
from dataclasses import dataclass, field\r
from enum import Enum\r
import json\r
\r
\r
class IFCVersion(Enum):\r
"""IFC schema versions."""\r
IFC2X3 = "IFC2X3"\r
IFC4 = "IFC4"\r
IFC4X1 = "IFC4X1"\r
IFC4X3 = "IFC4X3"\r
\r
\r
class IFCEntityType(Enum):\r
"""Common IFC entity types."""\r
IFCWALL = "IfcWall"\r
IFCWALLSTANDARDCASE = "IfcWallStandardCase"\r
IFCSLAB = "IfcSlab"\r
IFCCOLUMN = "IfcColumn"\r
IFCBEAM = "IfcBeam"\r
IFCDOOR = "IfcDoor"\r
IFCWINDOW = "IfcWindow"\r
IFCROOF = "IfcRoof"\r
IFCSTAIR = "IfcStair"\r
IFCRAILING = "IfcRailing"\r
IFCFURNISHINGELEMENT = "IfcFurnishingElement"\r
IFCSPACE = "IfcSpace"\r
IFCBUILDINGSTOREY = "IfcBuildingStorey"\r
IFCBUILDING = "IfcBuilding"\r
IFCSITE = "IfcSite"\r
\r
\r
@dataclass\r
class IFCElement:\r
"""Represents an IFC element."""\r
global_id: str\r
ifc_type: str\r
name: str\r
description: Optional[str]\r
object_type: Optional[str]\r
level: Optional[str]\r
\r
# Quantities\r
area: Optional[float] = None\r
volume: Optional[float] = None\r
length: Optional[float] = None\r
height: Optional[float] = None\r
width: Optional[float] = None\r
\r
# Bounding box (if exported)\r
bbox_min_x: Optional[float] = None\r
bbox_min_y: Optional[float] = None\r
bbox_min_z: Optional[float] = None\r
bbox_max_x: Optional[float] = None\r
bbox_max_y: Optional[float] = None\r
bbox_max_z: Optional[float] = None\r
\r
# Properties\r
properties: Dict[str, Any] = field(default_factory=dict)\r
materials: List[str] = field(default_factory=list)\r
\r
\r
@dataclass\r
class IFCProperty:\r
"""Represents an IFC property."""\r
pset_name: str\r
property_name: str\r
value: Any\r
value_type: str\r
\r
\r
@dataclass\r
class IFCMaterial:\r
"""Represents an IFC material."""\r
name: str\r
category: Optional[str]\r
thickness: Optional[float]\r
layer_position: Optional[int]\r
\r
\r
class IFCExporter:\r
"""IFC to Excel converter using DDC IfcExporter CLI."""\r
\r
def __init__(self, exporter_path: str = "IfcExporter.exe"):\r
self.exporter = Path(exporter_path)\r
if not self.exporter.exists():\r
raise FileNotFoundError(f"IfcExporter not found: {exporter_path}")\r
\r
def convert(self, ifc_file: str,\r
include_bbox: bool = True,\r
export_xlsx: bool = True,\r
export_collada: bool = True) -> Path:\r
"""Convert IFC file to Excel."""\r
ifc_path = Path(ifc_file)\r
if not ifc_path.exists():\r
raise FileNotFoundError(f"IFC file not found: {ifc_file}")\r
\r
cmd = [str(self.exporter), str(ifc_path)]\r
\r
if include_bbox:\r
cmd.append("bbox")\r
if not export_xlsx:\r
cmd.append("-no-xlsx")\r
if not export_collada:\r
cmd.append("-no-collada")\r
\r
result = subprocess.run(cmd, capture_output=True, text=True)\r
\r
if result.returncode != 0:\r
raise RuntimeError(f"Export failed: {result.stderr}")\r
\r
return ifc_path.with_suffix('.xlsx')\r
\r
def batch_convert(self, folder: str,\r
include_subfolders: bool = True,\r
include_bbox: bool = True) -> List[Dict[str, Any]]:\r
"""Convert all IFC files in folder."""\r
folder_path = Path(folder)\r
pattern = "**/*.ifc" if include_subfolders else "*.ifc"\r
\r
results = []\r
for ifc_file in folder_path.glob(pattern):\r
try:\r
output = self.convert(str(ifc_file), include_bbox)\r
results.append({\r
'input': str(ifc_file),\r
'output': str(output),\r
'status': 'success'\r
})\r
print(f"✓ Converted: {ifc_file.name}")\r
except Exception as e:\r
results.append({\r
'input': str(ifc_file),\r
'output': None,\r
'status': 'failed',\r
'error': str(e)\r
})\r
print(f"✗ Failed: {ifc_file.name} - {e}")\r
\r
return results\r
\r
def read_elements(self, xlsx_file: str) -> pd.DataFrame:\r
"""Read converted Excel as DataFrame."""\r
return pd.read_excel(xlsx_file, sheet_name="Elements")\r
\r
def get_element_types(self, xlsx_file: str) -> pd.DataFrame:\r
"""Get element type summary."""\r
df = self.read_elements(xlsx_file)\r
\r
if 'IfcType' not in df.columns:\r
raise ValueError("IfcType column not found")\r
\r
summary = df.groupby('IfcType').agg({\r
'GlobalId': 'count',\r
'Volume': 'sum' if 'Volume' in df.columns else 'count',\r
'Area': 'sum' if 'Area' in df.columns else 'count'\r
}).reset_index()\r
\r
summary.columns = ['IFC_Type', 'Count', 'Total_Volume', 'Total_Area']\r
return summary.sort_values('Count', ascending=False)\r
\r
def get_levels(self, xlsx_file: str) -> pd.DataFrame:\r
"""Get building level summary."""\r
df = self.read_elements(xlsx_file)\r
\r
level_col = None\r
for col in ['Level', 'BuildingStorey', 'IfcBuildingStorey']:\r
if col in df.columns:\r
level_col = col\r
break\r
\r
if level_col is None:\r
return pd.DataFrame(columns=['Level', 'Element_Count'])\r
\r
summary = df.groupby(level_col).agg({\r
'GlobalId': 'count'\r
}).reset_index()\r
summary.columns = ['Level', 'Element_Count']\r
return summary\r
\r
def get_materials(self, xlsx_file: str) -> pd.DataFrame:\r
"""Get material summary."""\r
df = self.read_elements(xlsx_file)\r
\r
if 'Material' not in df.columns:\r
return pd.DataFrame(columns=['Material', 'Count'])\r
\r
summary = df.groupby('Material').agg({\r
'GlobalId': 'count'\r
}).reset_index()\r
summary.columns = ['Material', 'Element_Count']\r
return summary.sort_values('Element_Count', ascending=False)\r
\r
def get_quantities(self, xlsx_file: str,\r
group_by: str = 'IfcType') -> pd.DataFrame:\r
"""Get quantity takeoff summary."""\r
df = self.read_elements(xlsx_file)\r
\r
if group_by not in df.columns:\r
raise ValueError(f"Column {group_by} not found")\r
\r
agg_dict = {'GlobalId': 'count'}\r
\r
# Add numeric columns for aggregation\r
numeric_cols = ['Volume', 'Area', 'Length', 'Width', 'Height']\r
for col in numeric_cols:\r
if col in df.columns:\r
agg_dict[col] = 'sum'\r
\r
summary = df.groupby(group_by).agg(agg_dict).reset_index()\r
return summary\r
\r
def filter_by_type(self, xlsx_file: str,\r
ifc_types: List[str]) -> pd.DataFrame:\r
"""Filter elements by IFC type."""\r
df = self.read_elements(xlsx_file)\r
return df[df['IfcType'].isin(ifc_types)]\r
\r
def get_properties(self, xlsx_file: str,\r
element_id: str) -> Dict[str, Any]:\r
"""Get all properties for specific element."""\r
df = self.read_elements(xlsx_file)\r
element = df[df['GlobalId'] == element_id]\r
\r
if element.empty:\r
return {}\r
\r
# Convert row to dictionary, excluding NaN values\r
props = element.iloc[0].dropna().to_dict()\r
return props\r
\r
def validate_ifc_data(self, xlsx_file: str) -> Dict[str, Any]:\r
"""Validate IFC data quality."""\r
df = self.read_elements(xlsx_file)\r
\r
validation = {\r
'total_elements': len(df),\r
'issues': []\r
}\r
\r
# Check for missing GlobalIds\r
if 'GlobalId' in df.columns:\r
missing_ids = df['GlobalId'].isna().sum()\r
if missing_ids > 0:\r
validation['issues'].append(f"{missing_ids} elements missing GlobalId")\r
\r
# Check for missing names\r
if 'Name' in df.columns:\r
missing_names = df['Name'].isna().sum()\r
if missing_names > 0:\r
validation['issues'].append(f"{missing_names} elements missing Name")\r
\r
# Check for zero quantities\r
for col in ['Volume', 'Area']:\r
if col in df.columns:\r
zero_qty = (df[col] == 0).sum()\r
if zero_qty > 0:\r
validation['issues'].append(f"{zero_qty} elements with zero {col}")\r
\r
# Check for duplicate GlobalIds\r
if 'GlobalId' in df.columns:\r
duplicates = df['GlobalId'].duplicated().sum()\r
if duplicates > 0:\r
validation['issues'].append(f"{duplicates} duplicate GlobalIds")\r
\r
validation['is_valid'] = len(validation['issues']) == 0\r
return validation\r
\r
\r
class IFCQuantityTakeoff:\r
"""Quantity takeoff from IFC data."""\r
\r
def __init__(self, exporter: IFCExporter):\r
self.exporter = exporter\r
\r
def generate_qto(self, ifc_file: str) -> Dict[str, pd.DataFrame]:\r
"""Generate complete quantity takeoff."""\r
xlsx = self.exporter.convert(ifc_file, include_bbox=True)\r
df = self.exporter.read_elements(str(xlsx))\r
\r
qto = {}\r
\r
# Walls\r
walls = df[df['IfcType'].str.contains('Wall', case=False, na=False)]\r
if not walls.empty:\r
qto['Walls'] = self._summarize_elements(walls, 'Type Name')\r
\r
# Slabs\r
slabs = df[df['IfcType'].str.contains('Slab', case=False, na=False)]\r
if not slabs.empty:\r
qto['Slabs'] = self._summarize_elements(slabs, 'Type Name')\r
\r
# Columns\r
columns = df[df['IfcType'].str.contains('Column', case=False, na=False)]\r
if not columns.empty:\r
qto['Columns'] = self._summarize_elements(columns, 'Type Name')\r
\r
# Beams\r
beams = df[df['IfcType'].str.contains('Beam', case=False, na=False)]\r
if not beams.empty:\r
qto['Beams'] = self._summarize_elements(beams, 'Type Name')\r
\r
# Doors\r
doors = df[df['IfcType'].str.contains('Door', case=False, na=False)]\r
if not doors.empty:\r
qto['Doors'] = self._summarize_elements(doors, 'Type Name')\r
\r
# Windows\r
windows = df[df['IfcType'].str.contains('Window', case=False, na=False)]\r
if not windows.empty:\r
qto['Windows'] = self._summarize_elements(windows, 'Type Name')\r
\r
return qto\r
\r
def _summarize_elements(self, df: pd.DataFrame,\r
group_col: str) -> pd.DataFrame:\r
"""Summarize elements by grouping column."""\r
if group_col not in df.columns:\r
group_col = 'IfcType'\r
\r
agg_dict = {'GlobalId': 'count'}\r
for col in ['Volume', 'Area', 'Length']:\r
if col in df.columns:\r
agg_dict[col] = 'sum'\r
\r
summary = df.groupby(group_col).agg(agg_dict).reset_index()\r
summary.rename(columns={'GlobalId': 'Count'}, inplace=True)\r
return summary\r
\r
def export_to_excel(self, qto: Dict[str, pd.DataFrame],\r
output_file: str):\r
"""Export QTO to multi-sheet Excel."""\r
with pd.ExcelWriter(output_file, engine='openpyxl') as writer:\r
for sheet_name, df in qto.items():\r
df.to_excel(writer, sheet_name=sheet_name, index=False)\r
\r
\r
# Convenience functions\r
def convert_ifc_to_excel(ifc_file: str,\r
exporter_path: str = "IfcExporter.exe") -> str:\r
"""Quick conversion of IFC to Excel."""\r
exporter = IFCExporter(exporter_path)\r
output = exporter.convert(ifc_file)\r
return str(output)\r
\r
\r
def get_ifc_summary(xlsx_file: str) -> Dict[str, Any]:\r
"""Get summary of converted IFC data."""\r
df = pd.read_excel(xlsx_file, sheet_name="Elements")\r
\r
return {\r
'total_elements': len(df),\r
'ifc_types': df['IfcType'].nunique() if 'IfcType' in df.columns else 0,\r
'levels': df['Level'].nunique() if 'Level' in df.columns else 0,\r
'total_volume': df['Volume'].sum() if 'Volume' in df.columns else 0,\r
'total_area': df['Area'].sum() if 'Area' in df.columns else 0\r
}\r
```\r
\r
## Output Structure\r
\r
### Excel Sheets\r
| Sheet | Content |\r
|-------|---------|\r
| Elements | All IFC elements with properties |\r
| Types | Element types summary |\r
| Levels | Building storey data |\r
| Materials | Material assignments |\r
| PropertySets | IFC property sets |\r
\r
### Element Columns\r
| Column | Type | Description |\r
|--------|------|-------------|\r
| GlobalId | string | IFC GUID |\r
| IfcType | string | IFC entity type |\r
| Name | string | Element name |\r
| Description | string | Element description |\r
| Level | string | Building storey |\r
| Material | string | Primary material |\r
| Volume | float | Volume (m³) |\r
| Area | float | Surface area (m²) |\r
| Length | float | Length (m) |\r
| Height | float | Height (m) |\r
| Width | float | Width (m) |\r
\r
## Quick Start\r
\r
```python\r
# Initialize exporter\r
exporter = IFCExporter("C:/DDC/IfcExporter.exe")\r
\r
# Convert IFC to Excel\r
xlsx = exporter.convert("C:/Models/Building.ifc", include_bbox=True)\r
\r
# Read elements\r
df = exporter.read_elements(str(xlsx))\r
print(f"Total elements: {len(df)}")\r
\r
# Get element types\r
types = exporter.get_element_types(str(xlsx))\r
print(types)\r
\r
# Get quantities by type\r
qto = exporter.get_quantities(str(xlsx), group_by='IfcType')\r
print(qto)\r
```\r
\r
## Common Use Cases\r
\r
### 1. Model Validation\r
```python\r
exporter = IFCExporter()\r
xlsx = exporter.convert("model.ifc")\r
validation = exporter.validate_ifc_data(str(xlsx))\r
\r
if not validation['is_valid']:\r
print("Issues found:")\r
for issue in validation['issues']:\r
print(f" - {issue}")\r
```\r
\r
### 2. Quantity Takeoff\r
```python\r
qto_generator = IFCQuantityTakeoff(exporter)\r
qto = qto_generator.generate_qto("building.ifc")\r
\r
for category, data in qto.items():\r
print(f"\
{category}:")\r
print(data.to_string(index=False))\r
```\r
\r
### 3. Material Schedule\r
```python\r
xlsx = exporter.convert("building.ifc")\r
materials = exporter.get_materials(str(xlsx))\r
print(materials)\r
```\r
\r
## Integration with DDC Pipeline\r
\r
```python\r
# Full pipeline: IFC → Excel → Validation → Cost Estimate\r
exporter = IFCExporter("C:/DDC/IfcExporter.exe")\r
\r
# 1. Convert IFC\r
xlsx = exporter.convert("project.ifc", include_bbox=True)\r
\r
# 2. Validate data\r
validation = exporter.validate_ifc_data(str(xlsx))\r
print(f"Valid: {validation['is_valid']}")\r
\r
# 3. Generate QTO\r
qto = IFCQuantityTakeoff(exporter)\r
quantities = qto.generate_qto("project.ifc")\r
\r
# 4. Export for cost estimation\r
qto.export_to_excel(quantities, "project_qto.xlsx")\r
```\r
\r
## Resources\r
\r
- **GitHub**: [cad2data Pipeline](https://github.com/datadrivenconstruction/cad2data-Revit-IFC-DWG-DGN-pipeline-with-conversion-validation-qto)\r
- **IFC Standard**: [buildingSMART](https://www.buildingsmart.org/standards/bsi-standards/industry-foundation-classes/)\r
- **DDC Book**: Chapter 2.4 - CAD/BIM Data Extraction\r
- Make sure OpenClaw is installed (local or Docker)
- Run the install command in chat:
/install ifc-to-excel - After installation, invoke the skill by name or use
/ifc-to-excel - Provide required inputs per the skill's parameter spec and get structured output
What is Ifc To Excel?
Convert IFC files (2x3, 4x1, 4x3) to Excel databases using IfcExporter CLI. Extract BIM data, properties, and geometry without proprietary software. It is an AI Agent Skill for Claude Code / OpenClaw, with 1220 downloads so far.
How do I install Ifc To Excel?
Run "/install ifc-to-excel" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.
Is Ifc To Excel free?
Yes, Ifc To Excel is completely free (open-source). You can download, install and use it at no cost.
Which platforms does Ifc To Excel support?
Ifc To Excel is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).
Who created Ifc To Excel?
It is built and maintained by datadrivenconstruction (@datadrivenconstruction); the current version is v2.0.0.