{
  "schemaVersion": "1.0",
  "item": {
    "slug": "ifc-data-extraction",
    "name": "Ifc Data Extraction",
    "source": "tencent",
    "type": "skill",
    "category": "AI 智能",
    "sourceUrl": "https://clawhub.ai/datadrivenconstruction/ifc-data-extraction",
    "canonicalUrl": "https://clawhub.ai/datadrivenconstruction/ifc-data-extraction",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/ifc-data-extraction",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=ifc-data-extraction",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "claw.json",
      "instructions.md",
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/ifc-data-extraction"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/ifc-data-extraction",
    "agentPageUrl": "https://openagent3.xyz/skills/ifc-data-extraction/agent",
    "manifestUrl": "https://openagent3.xyz/skills/ifc-data-extraction/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/ifc-data-extraction/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Overview",
        "body": "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.\n\nBased on Open BIM Standards - Working with vendor-neutral IFC format for maximum interoperability.\n\n\"IFC является открытым стандартом для обмена BIM-данными, позволяющим извлекать информацию независимо от программного обеспечения.\"\n— DDC Methodology"
      },
      {
        "title": "Quick Start",
        "body": "import ifcopenshell\nimport ifcopenshell.util.element as element_util\nimport pandas as pd\n\n# Open IFC file\nifc = ifcopenshell.open(\"model.ifc\")\n\n# Get project info\nproject = ifc.by_type(\"IfcProject\")[0]\nprint(f\"Project: {project.Name}\")\n\n# Extract all walls\nwalls = ifc.by_type(\"IfcWall\")\nprint(f\"Total walls: {len(walls)}\")\n\n# Get wall data\nwall_data = []\nfor wall in walls:\n    psets = element_util.get_psets(wall)\n    wall_data.append({\n        'GlobalId': wall.GlobalId,\n        'Name': wall.Name,\n        'Type': wall.is_a(),\n        'Level': get_level(wall),\n        'Properties': psets\n    })\n\ndf = pd.DataFrame(wall_data)\nprint(df.head())"
      },
      {
        "title": "Element Extractor Class",
        "body": "import ifcopenshell\nimport ifcopenshell.util.element as element_util\nimport ifcopenshell.util.placement as placement_util\nimport ifcopenshell.geom\nimport pandas as pd\nfrom typing import List, Dict, Optional, Any\n\nclass IFCExtractor:\n    \"\"\"Extract data from IFC files\"\"\"\n\n    def __init__(self, ifc_path: str):\n        self.model = ifcopenshell.open(ifc_path)\n        self.settings = ifcopenshell.geom.settings()\n\n    def get_project_info(self) -> Dict:\n        \"\"\"Extract project metadata\"\"\"\n        project = self.model.by_type(\"IfcProject\")[0]\n        site = self.model.by_type(\"IfcSite\")\n        building = self.model.by_type(\"IfcBuilding\")\n\n        return {\n            'project_id': project.GlobalId,\n            'project_name': project.Name,\n            'description': project.Description,\n            'site_count': len(site),\n            'building_count': len(building),\n            'schema': self.model.schema\n        }\n\n    def get_all_elements(self, element_types: List[str] = None) -> pd.DataFrame:\n        \"\"\"Extract all elements of specified types\"\"\"\n        if element_types is None:\n            element_types = [\n                'IfcWall', 'IfcSlab', 'IfcColumn', 'IfcBeam',\n                'IfcDoor', 'IfcWindow', 'IfcStair', 'IfcRoof'\n            ]\n\n        all_elements = []\n\n        for ifc_type in element_types:\n            elements = self.model.by_type(ifc_type)\n\n            for elem in elements:\n                data = self._extract_element_data(elem)\n                data['IFC_Type'] = ifc_type\n                all_elements.append(data)\n\n        return pd.DataFrame(all_elements)\n\n    def _extract_element_data(self, element) -> Dict:\n        \"\"\"Extract data from single element\"\"\"\n        # Basic info\n        data = {\n            'GlobalId': element.GlobalId,\n            'Name': element.Name,\n            'Description': element.Description,\n            'ObjectType': element.ObjectType if hasattr(element, 'ObjectType') else None\n        }\n\n        # Get level/storey\n        data['Level'] = self._get_element_level(element)\n\n        # Get material\n        data['Material'] = self._get_element_material(element)\n\n        # Get type\n        data['TypeName'] = self._get_element_type(element)\n\n        # Get all property sets\n        psets = element_util.get_psets(element)\n        data['PropertySets'] = psets\n\n        # Extract common quantities\n        base_quantities = psets.get('BaseQuantities', {})\n        data.update({\n            'Length': base_quantities.get('Length'),\n            'Width': base_quantities.get('Width'),\n            'Height': base_quantities.get('Height'),\n            'Area': base_quantities.get('NetSideArea') or base_quantities.get('GrossArea'),\n            'Volume': base_quantities.get('NetVolume') or base_quantities.get('GrossVolume')\n        })\n\n        return data\n\n    def _get_element_level(self, element) -> Optional[str]:\n        \"\"\"Get the building storey for an element\"\"\"\n        if hasattr(element, 'ContainedInStructure'):\n            for rel in element.ContainedInStructure or []:\n                if rel.RelatingStructure.is_a('IfcBuildingStorey'):\n                    return rel.RelatingStructure.Name\n        return None\n\n    def _get_element_material(self, element) -> Optional[str]:\n        \"\"\"Get material name for element\"\"\"\n        if hasattr(element, 'HasAssociations'):\n            for rel in element.HasAssociations or []:\n                if rel.is_a('IfcRelAssociatesMaterial'):\n                    material = rel.RelatingMaterial\n                    if hasattr(material, 'Name'):\n                        return material.Name\n                    elif hasattr(material, 'ForLayerSet'):\n                        layers = material.ForLayerSet.MaterialLayers\n                        if layers:\n                            return layers[0].Material.Name\n        return None\n\n    def _get_element_type(self, element) -> Optional[str]:\n        \"\"\"Get element type name\"\"\"\n        if hasattr(element, 'IsTypedBy'):\n            for rel in element.IsTypedBy or []:\n                return rel.RelatingType.Name\n        return None\n\n    def extract_quantities(self) -> pd.DataFrame:\n        \"\"\"Extract quantities for all elements\"\"\"\n        elements = self.get_all_elements()\n\n        # Group by category and level\n        quantities = elements.groupby(['IFC_Type', 'Level']).agg({\n            'GlobalId': 'count',\n            'Volume': 'sum',\n            'Area': 'sum',\n            'Length': 'sum'\n        }).rename(columns={'GlobalId': 'Count'}).reset_index()\n\n        return quantities\n\n    def extract_levels(self) -> pd.DataFrame:\n        \"\"\"Extract building levels/storeys\"\"\"\n        storeys = self.model.by_type(\"IfcBuildingStorey\")\n\n        level_data = []\n        for storey in storeys:\n            level_data.append({\n                'GlobalId': storey.GlobalId,\n                'Name': storey.Name,\n                'Elevation': storey.Elevation,\n                'Description': storey.Description\n            })\n\n        return pd.DataFrame(level_data).sort_values('Elevation')\n\n    def extract_spaces(self) -> pd.DataFrame:\n        \"\"\"Extract spaces/rooms\"\"\"\n        spaces = self.model.by_type(\"IfcSpace\")\n\n        space_data = []\n        for space in spaces:\n            psets = element_util.get_psets(space)\n            base_qty = psets.get('BaseQuantities', {})\n\n            space_data.append({\n                'GlobalId': space.GlobalId,\n                'Name': space.Name,\n                'LongName': space.LongName,\n                'Level': self._get_element_level(space),\n                'Area': base_qty.get('NetFloorArea'),\n                'Volume': base_qty.get('NetVolume'),\n                'Height': base_qty.get('Height')\n            })\n\n        return pd.DataFrame(space_data)\n\n    def extract_materials(self) -> pd.DataFrame:\n        \"\"\"Extract material summary\"\"\"\n        materials = {}\n\n        for elem in self.model.by_type(\"IfcProduct\"):\n            material = self._get_element_material(elem)\n            if material:\n                if material not in materials:\n                    materials[material] = {'count': 0, 'volume': 0}\n\n                materials[material]['count'] += 1\n\n                psets = element_util.get_psets(elem)\n                volume = psets.get('BaseQuantities', {}).get('NetVolume', 0)\n                if volume:\n                    materials[material]['volume'] += volume\n\n        return pd.DataFrame.from_dict(materials, orient='index').reset_index()\n\n    def extract_relationships(self) -> pd.DataFrame:\n        \"\"\"Extract element relationships\"\"\"\n        relationships = []\n\n        # Spatial containment\n        for rel in self.model.by_type(\"IfcRelContainedInSpatialStructure\"):\n            for elem in rel.RelatedElements:\n                relationships.append({\n                    'Element': elem.GlobalId,\n                    'Element_Type': elem.is_a(),\n                    'Relationship': 'ContainedIn',\n                    'Related_To': rel.RelatingStructure.GlobalId,\n                    'Related_Type': rel.RelatingStructure.is_a()\n                })\n\n        # Aggregation\n        for rel in self.model.by_type(\"IfcRelAggregates\"):\n            for part in rel.RelatedObjects:\n                relationships.append({\n                    'Element': part.GlobalId,\n                    'Element_Type': part.is_a(),\n                    'Relationship': 'PartOf',\n                    'Related_To': rel.RelatingObject.GlobalId,\n                    'Related_Type': rel.RelatingObject.is_a()\n                })\n\n        return pd.DataFrame(relationships)"
      },
      {
        "title": "Extract Geometry Data",
        "body": "import numpy as np\n\nclass IFCGeometryExtractor:\n    \"\"\"Extract geometry data from IFC elements\"\"\"\n\n    def __init__(self, ifc_path: str):\n        self.model = ifcopenshell.open(ifc_path)\n        self.settings = ifcopenshell.geom.settings()\n        self.settings.set(self.settings.USE_WORLD_COORDS, True)\n\n    def get_element_geometry(self, element) -> Dict:\n        \"\"\"Extract geometry for single element\"\"\"\n        try:\n            shape = ifcopenshell.geom.create_shape(self.settings, element)\n\n            verts = shape.geometry.verts\n            faces = shape.geometry.faces\n\n            # Calculate bounding box\n            vertices = np.array(verts).reshape(-1, 3)\n            min_coords = vertices.min(axis=0)\n            max_coords = vertices.max(axis=0)\n            dimensions = max_coords - min_coords\n\n            return {\n                'GlobalId': element.GlobalId,\n                'vertices_count': len(vertices),\n                'faces_count': len(faces) // 3,\n                'min_x': min_coords[0],\n                'min_y': min_coords[1],\n                'min_z': min_coords[2],\n                'max_x': max_coords[0],\n                'max_y': max_coords[1],\n                'max_z': max_coords[2],\n                'length': dimensions[0],\n                'width': dimensions[1],\n                'height': dimensions[2],\n                'center_x': (min_coords[0] + max_coords[0]) / 2,\n                'center_y': (min_coords[1] + max_coords[1]) / 2,\n                'center_z': (min_coords[2] + max_coords[2]) / 2\n            }\n        except:\n            return {'GlobalId': element.GlobalId, 'error': 'Geometry extraction failed'}\n\n    def get_bounding_boxes(self, element_type: str) -> pd.DataFrame:\n        \"\"\"Get bounding boxes for all elements of type\"\"\"\n        elements = self.model.by_type(element_type)\n        boxes = [self.get_element_geometry(e) for e in elements]\n        return pd.DataFrame(boxes)\n\n    def calculate_volumes(self, element_type: str) -> pd.DataFrame:\n        \"\"\"Calculate volumes using geometry\"\"\"\n        elements = self.model.by_type(element_type)\n        volumes = []\n\n        for elem in elements:\n            try:\n                shape = ifcopenshell.geom.create_shape(self.settings, elem)\n                # Calculate volume from mesh (simplified)\n                verts = np.array(shape.geometry.verts).reshape(-1, 3)\n                bbox_volume = np.prod(verts.max(axis=0) - verts.min(axis=0))\n\n                volumes.append({\n                    'GlobalId': elem.GlobalId,\n                    'Name': elem.Name,\n                    'BBox_Volume': bbox_volume\n                })\n            except:\n                pass\n\n        return pd.DataFrame(volumes)"
      },
      {
        "title": "Export to Various Formats",
        "body": "class IFCExporter:\n    \"\"\"Export IFC data to various formats\"\"\"\n\n    def __init__(self, extractor: IFCExtractor):\n        self.extractor = extractor\n\n    def to_excel(self, output_path: str, include_all: bool = True):\n        \"\"\"Export to Excel with multiple sheets\"\"\"\n        with pd.ExcelWriter(output_path, engine='openpyxl') as writer:\n            # Project info\n            project_info = pd.DataFrame([self.extractor.get_project_info()])\n            project_info.to_excel(writer, sheet_name='Project', index=False)\n\n            # All elements\n            if include_all:\n                elements = self.extractor.get_all_elements()\n                elements.to_excel(writer, sheet_name='Elements', index=False)\n\n            # Quantities\n            quantities = self.extractor.extract_quantities()\n            quantities.to_excel(writer, sheet_name='Quantities', index=False)\n\n            # Levels\n            levels = self.extractor.extract_levels()\n            levels.to_excel(writer, sheet_name='Levels', index=False)\n\n            # Spaces\n            spaces = self.extractor.extract_spaces()\n            spaces.to_excel(writer, sheet_name='Spaces', index=False)\n\n            # Materials\n            materials = self.extractor.extract_materials()\n            materials.to_excel(writer, sheet_name='Materials', index=False)\n\n        return output_path\n\n    def to_csv(self, output_dir: str):\n        \"\"\"Export to multiple CSV files\"\"\"\n        import os\n        os.makedirs(output_dir, exist_ok=True)\n\n        exports = {\n            'elements.csv': self.extractor.get_all_elements(),\n            'quantities.csv': self.extractor.extract_quantities(),\n            'levels.csv': self.extractor.extract_levels(),\n            'spaces.csv': self.extractor.extract_spaces(),\n            'materials.csv': self.extractor.extract_materials()\n        }\n\n        for filename, df in exports.items():\n            df.to_csv(os.path.join(output_dir, filename), index=False)\n\n        return output_dir\n\n    def to_json(self, output_path: str):\n        \"\"\"Export to JSON\"\"\"\n        import json\n\n        data = {\n            'project': self.extractor.get_project_info(),\n            'elements': self.extractor.get_all_elements().to_dict('records'),\n            'quantities': self.extractor.extract_quantities().to_dict('records'),\n            'levels': self.extractor.extract_levels().to_dict('records'),\n            'materials': self.extractor.extract_materials().to_dict('records')\n        }\n\n        with open(output_path, 'w', encoding='utf-8') as f:\n            json.dump(data, f, indent=2, default=str)\n\n        return output_path\n\n    def to_database(self, connection_string: str, table_prefix: str = 'ifc_'):\n        \"\"\"Export to SQL database\"\"\"\n        from sqlalchemy import create_engine\n\n        engine = create_engine(connection_string)\n\n        tables = {\n            f'{table_prefix}elements': self.extractor.get_all_elements(),\n            f'{table_prefix}quantities': self.extractor.extract_quantities(),\n            f'{table_prefix}levels': self.extractor.extract_levels(),\n            f'{table_prefix}spaces': self.extractor.extract_spaces(),\n            f'{table_prefix}materials': self.extractor.extract_materials()\n        }\n\n        for table_name, df in tables.items():\n            # Remove complex columns for database storage\n            simple_df = df.select_dtypes(exclude=['object']).copy()\n            for col in df.columns:\n                if df[col].dtype == 'object':\n                    simple_df[col] = df[col].astype(str)\n\n            simple_df.to_sql(table_name, engine, if_exists='replace', index=False)\n\n        return list(tables.keys())"
      },
      {
        "title": "Quick Reference",
        "body": "Element TypeCommon PropertiesQuantitiesIfcWallIsExternal, FireRatingLength, Height, Area, VolumeIfcSlabIsExternal, LoadBearingArea, Volume, PerimeterIfcColumnLoadBearingHeight, CrossSectionAreaIfcBeamLoadBearingLength, CrossSectionAreaIfcDoorFireRating, AcousticRatingWidth, HeightIfcWindowThermalTransmittanceWidth, Height, Area"
      },
      {
        "title": "Property Set Lookup",
        "body": "# Common IFC Property Sets\nPSETS = {\n    'Pset_WallCommon': ['IsExternal', 'LoadBearing', 'FireRating'],\n    'Pset_SlabCommon': ['IsExternal', 'LoadBearing', 'AcousticRating'],\n    'Pset_ColumnCommon': ['IsExternal', 'LoadBearing'],\n    'Pset_BeamCommon': ['LoadBearing', 'FireRating'],\n    'Pset_DoorCommon': ['FireRating', 'AcousticRating', 'SecurityRating'],\n    'Pset_WindowCommon': ['ThermalTransmittance', 'GlazingType'],\n    'BaseQuantities': ['Length', 'Width', 'Height', 'Area', 'Volume']\n}"
      },
      {
        "title": "Resources",
        "body": "IfcOpenShell: https://ifcopenshell.org\nIFC Standard: https://www.buildingsmart.org/standards/bsi-standards/industry-foundation-classes/\nDDC Website: https://datadrivenconstruction.io"
      },
      {
        "title": "Next Steps",
        "body": "See bim-validation-pipeline for validating extracted data\nSee qto-report for quantity take-off reports\nSee 4d-simulation for linking to schedules"
      }
    ],
    "body": "IFC Data Extraction\nOverview\n\nThis 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.\n\nBased on Open BIM Standards - Working with vendor-neutral IFC format for maximum interoperability.\n\n\"IFC является открытым стандартом для обмена BIM-данными, позволяющим извлекать информацию независимо от программного обеспечения.\" — DDC Methodology\n\nQuick Start\nimport ifcopenshell\nimport ifcopenshell.util.element as element_util\nimport pandas as pd\n\n# Open IFC file\nifc = ifcopenshell.open(\"model.ifc\")\n\n# Get project info\nproject = ifc.by_type(\"IfcProject\")[0]\nprint(f\"Project: {project.Name}\")\n\n# Extract all walls\nwalls = ifc.by_type(\"IfcWall\")\nprint(f\"Total walls: {len(walls)}\")\n\n# Get wall data\nwall_data = []\nfor wall in walls:\n    psets = element_util.get_psets(wall)\n    wall_data.append({\n        'GlobalId': wall.GlobalId,\n        'Name': wall.Name,\n        'Type': wall.is_a(),\n        'Level': get_level(wall),\n        'Properties': psets\n    })\n\ndf = pd.DataFrame(wall_data)\nprint(df.head())\n\nCore Extraction Functions\nElement Extractor Class\nimport ifcopenshell\nimport ifcopenshell.util.element as element_util\nimport ifcopenshell.util.placement as placement_util\nimport ifcopenshell.geom\nimport pandas as pd\nfrom typing import List, Dict, Optional, Any\n\nclass IFCExtractor:\n    \"\"\"Extract data from IFC files\"\"\"\n\n    def __init__(self, ifc_path: str):\n        self.model = ifcopenshell.open(ifc_path)\n        self.settings = ifcopenshell.geom.settings()\n\n    def get_project_info(self) -> Dict:\n        \"\"\"Extract project metadata\"\"\"\n        project = self.model.by_type(\"IfcProject\")[0]\n        site = self.model.by_type(\"IfcSite\")\n        building = self.model.by_type(\"IfcBuilding\")\n\n        return {\n            'project_id': project.GlobalId,\n            'project_name': project.Name,\n            'description': project.Description,\n            'site_count': len(site),\n            'building_count': len(building),\n            'schema': self.model.schema\n        }\n\n    def get_all_elements(self, element_types: List[str] = None) -> pd.DataFrame:\n        \"\"\"Extract all elements of specified types\"\"\"\n        if element_types is None:\n            element_types = [\n                'IfcWall', 'IfcSlab', 'IfcColumn', 'IfcBeam',\n                'IfcDoor', 'IfcWindow', 'IfcStair', 'IfcRoof'\n            ]\n\n        all_elements = []\n\n        for ifc_type in element_types:\n            elements = self.model.by_type(ifc_type)\n\n            for elem in elements:\n                data = self._extract_element_data(elem)\n                data['IFC_Type'] = ifc_type\n                all_elements.append(data)\n\n        return pd.DataFrame(all_elements)\n\n    def _extract_element_data(self, element) -> Dict:\n        \"\"\"Extract data from single element\"\"\"\n        # Basic info\n        data = {\n            'GlobalId': element.GlobalId,\n            'Name': element.Name,\n            'Description': element.Description,\n            'ObjectType': element.ObjectType if hasattr(element, 'ObjectType') else None\n        }\n\n        # Get level/storey\n        data['Level'] = self._get_element_level(element)\n\n        # Get material\n        data['Material'] = self._get_element_material(element)\n\n        # Get type\n        data['TypeName'] = self._get_element_type(element)\n\n        # Get all property sets\n        psets = element_util.get_psets(element)\n        data['PropertySets'] = psets\n\n        # Extract common quantities\n        base_quantities = psets.get('BaseQuantities', {})\n        data.update({\n            'Length': base_quantities.get('Length'),\n            'Width': base_quantities.get('Width'),\n            'Height': base_quantities.get('Height'),\n            'Area': base_quantities.get('NetSideArea') or base_quantities.get('GrossArea'),\n            'Volume': base_quantities.get('NetVolume') or base_quantities.get('GrossVolume')\n        })\n\n        return data\n\n    def _get_element_level(self, element) -> Optional[str]:\n        \"\"\"Get the building storey for an element\"\"\"\n        if hasattr(element, 'ContainedInStructure'):\n            for rel in element.ContainedInStructure or []:\n                if rel.RelatingStructure.is_a('IfcBuildingStorey'):\n                    return rel.RelatingStructure.Name\n        return None\n\n    def _get_element_material(self, element) -> Optional[str]:\n        \"\"\"Get material name for element\"\"\"\n        if hasattr(element, 'HasAssociations'):\n            for rel in element.HasAssociations or []:\n                if rel.is_a('IfcRelAssociatesMaterial'):\n                    material = rel.RelatingMaterial\n                    if hasattr(material, 'Name'):\n                        return material.Name\n                    elif hasattr(material, 'ForLayerSet'):\n                        layers = material.ForLayerSet.MaterialLayers\n                        if layers:\n                            return layers[0].Material.Name\n        return None\n\n    def _get_element_type(self, element) -> Optional[str]:\n        \"\"\"Get element type name\"\"\"\n        if hasattr(element, 'IsTypedBy'):\n            for rel in element.IsTypedBy or []:\n                return rel.RelatingType.Name\n        return None\n\n    def extract_quantities(self) -> pd.DataFrame:\n        \"\"\"Extract quantities for all elements\"\"\"\n        elements = self.get_all_elements()\n\n        # Group by category and level\n        quantities = elements.groupby(['IFC_Type', 'Level']).agg({\n            'GlobalId': 'count',\n            'Volume': 'sum',\n            'Area': 'sum',\n            'Length': 'sum'\n        }).rename(columns={'GlobalId': 'Count'}).reset_index()\n\n        return quantities\n\n    def extract_levels(self) -> pd.DataFrame:\n        \"\"\"Extract building levels/storeys\"\"\"\n        storeys = self.model.by_type(\"IfcBuildingStorey\")\n\n        level_data = []\n        for storey in storeys:\n            level_data.append({\n                'GlobalId': storey.GlobalId,\n                'Name': storey.Name,\n                'Elevation': storey.Elevation,\n                'Description': storey.Description\n            })\n\n        return pd.DataFrame(level_data).sort_values('Elevation')\n\n    def extract_spaces(self) -> pd.DataFrame:\n        \"\"\"Extract spaces/rooms\"\"\"\n        spaces = self.model.by_type(\"IfcSpace\")\n\n        space_data = []\n        for space in spaces:\n            psets = element_util.get_psets(space)\n            base_qty = psets.get('BaseQuantities', {})\n\n            space_data.append({\n                'GlobalId': space.GlobalId,\n                'Name': space.Name,\n                'LongName': space.LongName,\n                'Level': self._get_element_level(space),\n                'Area': base_qty.get('NetFloorArea'),\n                'Volume': base_qty.get('NetVolume'),\n                'Height': base_qty.get('Height')\n            })\n\n        return pd.DataFrame(space_data)\n\n    def extract_materials(self) -> pd.DataFrame:\n        \"\"\"Extract material summary\"\"\"\n        materials = {}\n\n        for elem in self.model.by_type(\"IfcProduct\"):\n            material = self._get_element_material(elem)\n            if material:\n                if material not in materials:\n                    materials[material] = {'count': 0, 'volume': 0}\n\n                materials[material]['count'] += 1\n\n                psets = element_util.get_psets(elem)\n                volume = psets.get('BaseQuantities', {}).get('NetVolume', 0)\n                if volume:\n                    materials[material]['volume'] += volume\n\n        return pd.DataFrame.from_dict(materials, orient='index').reset_index()\n\n    def extract_relationships(self) -> pd.DataFrame:\n        \"\"\"Extract element relationships\"\"\"\n        relationships = []\n\n        # Spatial containment\n        for rel in self.model.by_type(\"IfcRelContainedInSpatialStructure\"):\n            for elem in rel.RelatedElements:\n                relationships.append({\n                    'Element': elem.GlobalId,\n                    'Element_Type': elem.is_a(),\n                    'Relationship': 'ContainedIn',\n                    'Related_To': rel.RelatingStructure.GlobalId,\n                    'Related_Type': rel.RelatingStructure.is_a()\n                })\n\n        # Aggregation\n        for rel in self.model.by_type(\"IfcRelAggregates\"):\n            for part in rel.RelatedObjects:\n                relationships.append({\n                    'Element': part.GlobalId,\n                    'Element_Type': part.is_a(),\n                    'Relationship': 'PartOf',\n                    'Related_To': rel.RelatingObject.GlobalId,\n                    'Related_Type': rel.RelatingObject.is_a()\n                })\n\n        return pd.DataFrame(relationships)\n\nGeometry Extraction\nExtract Geometry Data\nimport numpy as np\n\nclass IFCGeometryExtractor:\n    \"\"\"Extract geometry data from IFC elements\"\"\"\n\n    def __init__(self, ifc_path: str):\n        self.model = ifcopenshell.open(ifc_path)\n        self.settings = ifcopenshell.geom.settings()\n        self.settings.set(self.settings.USE_WORLD_COORDS, True)\n\n    def get_element_geometry(self, element) -> Dict:\n        \"\"\"Extract geometry for single element\"\"\"\n        try:\n            shape = ifcopenshell.geom.create_shape(self.settings, element)\n\n            verts = shape.geometry.verts\n            faces = shape.geometry.faces\n\n            # Calculate bounding box\n            vertices = np.array(verts).reshape(-1, 3)\n            min_coords = vertices.min(axis=0)\n            max_coords = vertices.max(axis=0)\n            dimensions = max_coords - min_coords\n\n            return {\n                'GlobalId': element.GlobalId,\n                'vertices_count': len(vertices),\n                'faces_count': len(faces) // 3,\n                'min_x': min_coords[0],\n                'min_y': min_coords[1],\n                'min_z': min_coords[2],\n                'max_x': max_coords[0],\n                'max_y': max_coords[1],\n                'max_z': max_coords[2],\n                'length': dimensions[0],\n                'width': dimensions[1],\n                'height': dimensions[2],\n                'center_x': (min_coords[0] + max_coords[0]) / 2,\n                'center_y': (min_coords[1] + max_coords[1]) / 2,\n                'center_z': (min_coords[2] + max_coords[2]) / 2\n            }\n        except:\n            return {'GlobalId': element.GlobalId, 'error': 'Geometry extraction failed'}\n\n    def get_bounding_boxes(self, element_type: str) -> pd.DataFrame:\n        \"\"\"Get bounding boxes for all elements of type\"\"\"\n        elements = self.model.by_type(element_type)\n        boxes = [self.get_element_geometry(e) for e in elements]\n        return pd.DataFrame(boxes)\n\n    def calculate_volumes(self, element_type: str) -> pd.DataFrame:\n        \"\"\"Calculate volumes using geometry\"\"\"\n        elements = self.model.by_type(element_type)\n        volumes = []\n\n        for elem in elements:\n            try:\n                shape = ifcopenshell.geom.create_shape(self.settings, elem)\n                # Calculate volume from mesh (simplified)\n                verts = np.array(shape.geometry.verts).reshape(-1, 3)\n                bbox_volume = np.prod(verts.max(axis=0) - verts.min(axis=0))\n\n                volumes.append({\n                    'GlobalId': elem.GlobalId,\n                    'Name': elem.Name,\n                    'BBox_Volume': bbox_volume\n                })\n            except:\n                pass\n\n        return pd.DataFrame(volumes)\n\nExport Functions\nExport to Various Formats\nclass IFCExporter:\n    \"\"\"Export IFC data to various formats\"\"\"\n\n    def __init__(self, extractor: IFCExtractor):\n        self.extractor = extractor\n\n    def to_excel(self, output_path: str, include_all: bool = True):\n        \"\"\"Export to Excel with multiple sheets\"\"\"\n        with pd.ExcelWriter(output_path, engine='openpyxl') as writer:\n            # Project info\n            project_info = pd.DataFrame([self.extractor.get_project_info()])\n            project_info.to_excel(writer, sheet_name='Project', index=False)\n\n            # All elements\n            if include_all:\n                elements = self.extractor.get_all_elements()\n                elements.to_excel(writer, sheet_name='Elements', index=False)\n\n            # Quantities\n            quantities = self.extractor.extract_quantities()\n            quantities.to_excel(writer, sheet_name='Quantities', index=False)\n\n            # Levels\n            levels = self.extractor.extract_levels()\n            levels.to_excel(writer, sheet_name='Levels', index=False)\n\n            # Spaces\n            spaces = self.extractor.extract_spaces()\n            spaces.to_excel(writer, sheet_name='Spaces', index=False)\n\n            # Materials\n            materials = self.extractor.extract_materials()\n            materials.to_excel(writer, sheet_name='Materials', index=False)\n\n        return output_path\n\n    def to_csv(self, output_dir: str):\n        \"\"\"Export to multiple CSV files\"\"\"\n        import os\n        os.makedirs(output_dir, exist_ok=True)\n\n        exports = {\n            'elements.csv': self.extractor.get_all_elements(),\n            'quantities.csv': self.extractor.extract_quantities(),\n            'levels.csv': self.extractor.extract_levels(),\n            'spaces.csv': self.extractor.extract_spaces(),\n            'materials.csv': self.extractor.extract_materials()\n        }\n\n        for filename, df in exports.items():\n            df.to_csv(os.path.join(output_dir, filename), index=False)\n\n        return output_dir\n\n    def to_json(self, output_path: str):\n        \"\"\"Export to JSON\"\"\"\n        import json\n\n        data = {\n            'project': self.extractor.get_project_info(),\n            'elements': self.extractor.get_all_elements().to_dict('records'),\n            'quantities': self.extractor.extract_quantities().to_dict('records'),\n            'levels': self.extractor.extract_levels().to_dict('records'),\n            'materials': self.extractor.extract_materials().to_dict('records')\n        }\n\n        with open(output_path, 'w', encoding='utf-8') as f:\n            json.dump(data, f, indent=2, default=str)\n\n        return output_path\n\n    def to_database(self, connection_string: str, table_prefix: str = 'ifc_'):\n        \"\"\"Export to SQL database\"\"\"\n        from sqlalchemy import create_engine\n\n        engine = create_engine(connection_string)\n\n        tables = {\n            f'{table_prefix}elements': self.extractor.get_all_elements(),\n            f'{table_prefix}quantities': self.extractor.extract_quantities(),\n            f'{table_prefix}levels': self.extractor.extract_levels(),\n            f'{table_prefix}spaces': self.extractor.extract_spaces(),\n            f'{table_prefix}materials': self.extractor.extract_materials()\n        }\n\n        for table_name, df in tables.items():\n            # Remove complex columns for database storage\n            simple_df = df.select_dtypes(exclude=['object']).copy()\n            for col in df.columns:\n                if df[col].dtype == 'object':\n                    simple_df[col] = df[col].astype(str)\n\n            simple_df.to_sql(table_name, engine, if_exists='replace', index=False)\n\n        return list(tables.keys())\n\nQuick Reference\nElement Type\tCommon Properties\tQuantities\nIfcWall\tIsExternal, FireRating\tLength, Height, Area, Volume\nIfcSlab\tIsExternal, LoadBearing\tArea, Volume, Perimeter\nIfcColumn\tLoadBearing\tHeight, CrossSectionArea\nIfcBeam\tLoadBearing\tLength, CrossSectionArea\nIfcDoor\tFireRating, AcousticRating\tWidth, Height\nIfcWindow\tThermalTransmittance\tWidth, Height, Area\nProperty Set Lookup\n# Common IFC Property Sets\nPSETS = {\n    'Pset_WallCommon': ['IsExternal', 'LoadBearing', 'FireRating'],\n    'Pset_SlabCommon': ['IsExternal', 'LoadBearing', 'AcousticRating'],\n    'Pset_ColumnCommon': ['IsExternal', 'LoadBearing'],\n    'Pset_BeamCommon': ['LoadBearing', 'FireRating'],\n    'Pset_DoorCommon': ['FireRating', 'AcousticRating', 'SecurityRating'],\n    'Pset_WindowCommon': ['ThermalTransmittance', 'GlazingType'],\n    'BaseQuantities': ['Length', 'Width', 'Height', 'Area', 'Volume']\n}\n\nResources\nIfcOpenShell: https://ifcopenshell.org\nIFC Standard: https://www.buildingsmart.org/standards/bsi-standards/industry-foundation-classes/\nDDC Website: https://datadrivenconstruction.io\nNext Steps\nSee bim-validation-pipeline for validating extracted data\nSee qto-report for quantity take-off reports\nSee 4d-simulation for linking to schedules"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/datadrivenconstruction/ifc-data-extraction",
    "publisherUrl": "https://clawhub.ai/datadrivenconstruction/ifc-data-extraction",
    "owner": "datadrivenconstruction",
    "version": "2.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/ifc-data-extraction",
    "downloadUrl": "https://openagent3.xyz/downloads/ifc-data-extraction",
    "agentUrl": "https://openagent3.xyz/skills/ifc-data-extraction/agent",
    "manifestUrl": "https://openagent3.xyz/skills/ifc-data-extraction/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/ifc-data-extraction/agent.md"
  }
}