← 返回 Skills 市场
datadrivenconstruction

Ifc Data Extraction

作者 datadrivenconstruction · GitHub ↗ · v2.0.0
cross-platform ⚠ suspicious
1263
总下载
0
收藏
2
当前安装
2
版本数
在 OpenClaw 中安装
/install 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.
使用说明 (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
安全使用建议
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.
功能分析
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.
能力评估
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.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install ifc-data-extraction
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /ifc-data-extraction 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
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.
元数据
Slug ifc-data-extraction
版本 2.0.0
许可证
累计安装 2
当前安装数 2
历史版本数 2
常见问题

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。

💬 留言讨论