← Back to Skills Marketplace
datadrivenconstruction

Ifc Data Extraction

cross-platform ⚠ suspicious
1263
Downloads
0
Stars
2
Active Installs
2
Versions
Install in OpenClaw
/install 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.
README (SKILL.md)

\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
Usage Guidance
This skill appears coherent and focused on local IFC/BIM data extraction. Before installing or running it, ensure: (1) you are comfortable granting filesystem access (it will read .ifc files and write exports); (2) your environment has IfcOpenShell and required Python packages installed from trusted sources (the package is non-trivial to build on some systems); (3) you only run it on IFC files you trust (large models may be resource-intensive). If you need the agent to auto-install missing Python packages, review and control any pip/install commands and their sources. Otherwise the skill itself does not request credentials or network endpoints and matches its stated purpose.
Capability Analysis
Type: OpenClaw Skill Name: ifc-data-extraction Version: 2.0.0 The skill is designed for IFC data extraction and export, requesting `filesystem` permissions which aligns with its stated purpose. However, the `IFCExporter.to_database` function in `SKILL.md` uses `sqlalchemy.create_engine` with a user-provided `connection_string`. While the `instructions.md` states 'No network access required', this capability allows connection to arbitrary SQL databases (local or remote). If an attacker can control the `connection_string`, this presents a significant vulnerability for potential unauthorized database access or data modification, classifying it as suspicious due to a high-risk capability without clear malicious intent from the skill's author.
Capability Assessment
Purpose & Capability
Name/description, instructions.md, and SKILL.md all describe IFC parsing via IfcOpenShell and extracting element/property/quantity data. The declared filesystem permission is appropriate for reading .ifc files and writing exports. Nothing requested appears unrelated to IFC extraction.
Instruction Scope
Runtime instructions only reference opening an IFC file, traversing IFC entities, extracting property sets and quantities, and exporting results (CSV/Excel/JSON/DataFrame). The docs explicitly state no network access is required and do not instruct reading unrelated files, environment variables, or posting data externally.
Install Mechanism
This is an instruction-only skill with no install spec. It expects IfcOpenShell and pandas to be available in the environment but does not provide or declare an installer. That is not malicious but may lead the agent or integrator to install Python packages manually (or via pip) before use—consider validating the source and method you use to install IfcOpenShell.
Credentials
No credentials, environment variables, or config paths are requested. The only declared permission is filesystem access, which is appropriate and proportionate for reading IFC files and writing exports.
Persistence & Privilege
always:false and user-invocable:true (normal). The skill does not request elevated or persistent platform privileges and does not modify other skills or global agent settings.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install ifc-data-extraction
  3. After installation, invoke the skill by name or use /ifc-data-extraction
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v2.0.0
Version 2.0.0 (ifc-data-extraction) - Major update: Introduces a full-featured IFCExtractor Python class for extracting, analyzing, and reporting BIM data from IFC files. - Supports extraction of project metadata, elements (walls, slabs, columns, etc.), levels, spaces, quantities, and materials. - Returns results as pandas DataFrames for easy data manipulation and export. - Utilizes IfcOpenShell utility functions for advanced BIM property and geometry extraction. - Expanded documentation and code samples for rapid adoption and integration.
v1.0.0
Initial release of IFC Data Extraction skill: - Parse and extract data from IFC files using IfcOpenShell. - Extract project metadata, element properties, quantities, and spatial relationships. - Export element data, levels, spaces, and material summaries as structured DataFrames. - Supports extraction for key BIM components like walls, slabs, columns, beams, doors, windows, stairs, and roofs. - Designed for interoperability using the open IFC format.
Metadata
Slug ifc-data-extraction
Version 2.0.0
License
All-time Installs 2
Active Installs 2
Total Versions 2
Frequently Asked Questions

What is 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. It is an AI Agent Skill for Claude Code / OpenClaw, with 1263 downloads so far.

How do I install Ifc Data Extraction?

Run "/install ifc-data-extraction" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Ifc Data Extraction free?

Yes, Ifc Data Extraction is completely free (open-source). You can download, install and use it at no cost.

Which platforms does Ifc Data Extraction support?

Ifc Data Extraction is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Ifc Data Extraction?

It is built and maintained by datadrivenconstruction (@datadrivenconstruction); the current version is v2.0.0.

💬 Comments