openclaw 网盘下载
OpenClaw

技能详情(站内镜像,无评论)

首页 > 技能库 > 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.

数据与表格

许可证:MIT-0

MIT-0 ·免费使用、修改和重新分发。无需归因。

版本:v2.0.0

统计:⭐ 0 · 1k · 2 current installs · 2 all-time installs

0

安装量(当前) 2

🛡 VirusTotal :可疑 · OpenClaw :良性

Package:datadrivenconstruction/ifc-data-extraction

安全扫描(ClawHub)

  • VirusTotal :可疑
  • OpenClaw :良性

OpenClaw 评估

The skill's declared purpose (IFC/BIM data extraction) matches its instructions and requested filesystem access; there are no unexplained credentials, network endpoints, or install downloads in the bundle.

目的

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.

说明范围

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.

安装机制

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.

证书

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.

持久

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.

综合结论

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)…

安装(复制给龙虾 AI)

将下方整段复制到龙虾中文库对话中,由龙虾按 SKILL.md 完成安装。

请把本段交给龙虾中文库(龙虾 AI)执行:为本机安装 OpenClaw 技能「Ifc Data Extraction」。简介:Extract structured data from IFC (Industry Foundation Classes) files using IfcO…。
请 fetch 以下地址读取 SKILL.md 并按文档完成安装:https://raw.githubusercontent.com/openclaw/skills/refs/heads/main/skills/datadrivenconstruction/ifc-data-extraction/SKILL.md
(来源:yingzhi8.cn 技能库)

SKILL.md

打开原始 SKILL.md(GitHub raw)

---
name: "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."
---

# 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

```python
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

```python
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.RelatedElements:
                relationships.append({
                    'Element': elem.GlobalId,
                    'Element_Type': elem.is_a(),
                    'Relationship': 'ContainedIn',
                    'Related_To': rel.RelatingStructure.GlobalId,
                    'Related_Type': rel.RelatingStructure.is_a()
                })

        # Aggregation
        for rel in self.model.by_type("IfcRelAggregates"):
            for part in rel.RelatedObjects:
                relationships.append({
                    'Element': part.GlobalId,
                    'Element_Type': part.is_a(),
                    'Relationship': 'PartOf',
                    'Related_To': rel.RelatingObject.GlobalId,
                    'Related_Type': rel.RelatingObject.is_a()
                })

        return pd.DataFrame(relationships)
```

## Geometry Extraction

### Extract Geometry Data

```python
import numpy as np

class IFCGeometryExtractor:
    """Extract geometry data from IFC elements"""

    def __init__(self, ifc_path: str):
        self.model = ifcopenshell.open(ifc_path)
        self.settings = ifcopenshell.geom.settings()
        self.settings.set(self.settings.USE_WORLD_COORDS, True)

    def get_element_geometry(self, element) -> Dict:
        """Extract geometry for single element"""
        try:
            shape = ifcopenshell.geom.create_shape(self.settings, element)

            verts = shape.geometry.verts
            faces = shape.geometry.faces

            # Calculate bounding box
            vertices = np.array(verts).reshape(-1, 3)
            min_coords = vertices.min(axis=0)
            max_coords = vertices.max(axis=0)
            dimensions = max_coords - min_coords

            return {
                'GlobalId': element.GlobalId,
                'vertices_count': len(vertices),
                'faces_count': len(faces) // 3,
                'min_x': min_coords[0],
                'min_y': min_coords[1],
                'min_z': min_coords[2],
                'max_x': max_coords[0],
                'max_y': max_coords[1],
                'max_z': max_coords[2],
                'length': dimensions[0],
                'width': dimensions[1],
                'height': dimensions[2],
                'center_x': (min_coords[0] + max_coords[0]) / 2,
                'center_y': (min_coords[1] + max_coords[1]) / 2,
                'center_z': (min_coords[2] + max_coords[2]) / 2
            }
        except:
            return {'GlobalId': element.GlobalId, 'error': 'Geometry extraction failed'}

    def get_bounding_boxes(self, element_type: str) -> pd.DataFrame:
        """Get bounding boxes for all elements of type"""
        elements = self.model.by_type(element_type)
        boxes = [self.get_element_geometry(e) for e in elements]
        return pd.DataFrame(boxes)

    def calculate_volumes(self, element_type: str) -> pd.DataFrame:
        """Calculate volumes using geometry"""
        elements = self.model.by_type(element_type)
        volumes = []

        for elem in elements:
            try:
                shape = ifcopenshell.geom.create_shape(self.settings, elem)
                # Calculate volume from mesh (simplified)
                verts = np.array(shape.geometry.verts).reshape(-1, 3)
                bbox_volume = np.prod(verts.max(axis=0) - verts.min(axis=0))

                volumes.append({
                    'GlobalId': elem.GlobalId,
                    'Name': elem.Name,
                    'BBox_Volume': bbox_volume
                })
            except:
                pass

        return pd.DataFrame(volumes)
```

## Export Functions

### Export to Various Formats

```python
class IFCExporter:
    """Export IFC data to various formats"""

    def __init__(self, extractor: IFCExtractor):
        self.extractor = extractor

    def to_excel(self, output_path: str, include_all: bool = True):
        """Export to Excel with multiple sheets"""
        with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
            # Project info
            project_info = pd.DataFrame([self.extractor.get_project_info()])
            project_info.to_excel(writer, sheet_name='Project', index=False)

            # All elements
            if include_all:
                elements = self.extractor.get_all_elements()
                elements.to_excel(writer, sheet_name='Elements', index=False)

            # Quantities
            quantities = self.extractor.extract_quantities()
            quantities.to_excel(writer, sheet_name='Quantities', index=False)

            # Levels
            levels = self.extractor.extract_levels()
            levels.to_excel(writer, sheet_name='Levels', index=False)

            # Spaces
            spaces = self.extractor.extract_spaces()
            spaces.to_excel(writer, sheet_name='Spaces', index=False)

            # Materials
            materials = self.extractor.extract_materials()
            materials.to_excel(writer, sheet_name='Materials', index=False)

        return output_path

    def to_csv(self, output_dir: str):
        """Export to multiple CSV files"""
        import os
        os.makedirs(output_dir, exist_ok=True)

        exports = {
            'elements.csv': self.extractor.get_all_elements(),
            'quantities.csv': self.extractor.extract_quantities(),
            'levels.csv': self.extractor.extract_levels(),
            'spaces.csv': self.extractor.extract_spaces(),
            'materials.csv': self.extractor.extract_materials()
        }

        for filename, df in exports.items():
            df.to_csv(os.path.join(output_dir, filename), index=False)

        return output_dir

    def to_json(self, output_path: str):
        """Export to JSON"""
        import json

        data = {
            'project': self.extractor.get_project_info(),
            'elements': self.extractor.get_all_elements().to_dict('records'),
            'quantities': self.extractor.extract_quantities().to_dict('records'),
            'levels': self.extractor.extract_levels().to_dict('records'),
            'materials': self.extractor.extract_materials().to_dict('records')
        }

        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=2, default=str)

        return output_path

    def to_database(self, connection_string: str, table_prefix: str = 'ifc_'):
        """Export to SQL database"""
        from sqlalchemy import create_engine

        engine = create_engine(connection_string)

        tables = {
            f'{table_prefix}elements': self.extractor.get_all_elements(),
            f'{table_prefix}quantities': self.extractor.extract_quantities(),
            f'{table_prefix}levels': self.extractor.extract_levels(),
            f'{table_prefix}spaces': self.extractor.extract_spaces(),
            f'{table_prefix}materials': self.extractor.extract_materials()
        }

        for table_name, df in tables.items():
            # Remove complex columns for database storage
            simple_df = df.select_dtypes(exclude=['object']).copy()
            for col in df.columns:
                if df[col].dtype == 'object':
                    simple_df[col] = df[col].astype(str)

            simple_df.to_sql(table_name, engine, if_exists='replace', index=False)

        return list(tables.keys())
```

## Quick Reference

| Element Type | Common Properties | Quantities |
|-------------|-------------------|------------|
| IfcWall | IsExternal, FireRating | Length, Height, Area, Volume |
| IfcSlab | IsExternal, LoadBearing | Area, Volume, Perimeter |
| IfcColumn | LoadBearing | Height, CrossSectionArea |
| IfcBeam | LoadBearing | Length, CrossSectionArea |
| IfcDoor | FireRating, AcousticRating | Width, Height |
| IfcWindow | ThermalTransmittance | Width, Height, Area |

## Property Set Lookup

```python
# Common IFC Property Sets
PSETS = {
    'Pset_WallCommon': ['IsExternal', 'LoadBearing', 'FireRating'],
    'Pset_SlabCommon': ['IsExternal', 'LoadBearing', 'AcousticRating'],
    'Pset_ColumnCommon': ['IsExternal', 'LoadBearing'],
    'Pset_BeamCommon': ['LoadBearing', 'FireRating'],
    'Pset_DoorCommon': ['FireRating', 'AcousticRating', 'SecurityRating'],
    'Pset_WindowCommon': ['ThermalTransmittance', 'GlazingType'],
    'BaseQuantities': ['Length', 'Width', 'Height', 'Area', 'Volume']
}
```

## Resources

- **IfcOpenShell**: https://ifcopenshell.org
- **IFC Standard**: https://www.buildingsmart.org/standards/bsi-standards/industry-foundation-classes/
- **DDC Website**: https://datadrivenconstruction.io

## Next Steps

- See `bim-validation-pipeline` for validating extracted data
- See `qto-report` for quantity take-off reports
- See `4d-simulation` for linking to schedules