Extracting IFC Wall Geometries to Shapely: A Production-Ready Pipeline
Extracting IFC wall geometries to Shapely requires projecting 3D B-Rep or SweptSolid representations onto a 2D plane, resolving mesh topology, and constructing valid Polygon or MultiPolygon objects. The most reliable pipeline uses ifcopenshell to resolve native IFC geometry, extracts vertex/face arrays, projects them to a target axis (typically Z=0 for floor plans), and passes the resulting coordinate sequences to Shapely. You must explicitly handle invalid rings, self-intersections, and IFC2x3 vs IFC4 representation differences before feeding geometries into GIS/CAD workflows.
Prerequisites & Environment Setup
| Component | Requirement |
|---|---|
| Python | 3.8–3.11 (3.12+ requires ifcopenshell ≥0.8.0) |
| Core Libraries | ifcopenshell>=0.7.0, shapely>=2.0.0, numpy>=1.24.0 |
| OS | Linux/macOS/Windows (precompiled geometry kernel; no external CAD dependencies) |
| Geometry Kernel | OpenCASCADE (bundled with ifcopenshell-python) |
Install dependencies via pip:
pip install ifcopenshell shapely numpy
Core Extraction Pipeline
The extraction process follows a deterministic sequence. Understanding each stage prevents downstream topology failures when integrating with spatial databases or CAD renderers.
- Model Loading & Query: Parse the IFC file and filter
IfcWall(orIfcWallStandardCase) entities. Avoid recursive type traversal unless you explicitly needIfcProxyor legacy classifications. - Geometry Settings Configuration: Enable world coordinates, disable material application, and exclude curves. This forces the kernel to return pure mesh data, which is optimal for 2D projection.
- Mesh Extraction: Retrieve flattened vertex arrays and face index lists. IFC geometry is typically triangulated during tessellation, meaning each face contains exactly three vertex indices.
- Axis Projection: Drop the target axis (usually Z for plan views) and map remaining coordinates to
(x, y)tuples. Maintain consistent winding order to preserve interior/exterior ring orientation. - Topology Assembly: Convert projected triangles to Shapely polygons, merge overlapping faces using
unary_union, and run validation routines to repair self-intersections or collapsed rings.
This workflow aligns with established ifcopenshell Workflow patterns, ensuring deterministic output across different authoring platforms.
Complete Working Implementation
The following script handles projection, validation, and merging in a single pass. It is optimized for batch processing and includes explicit error routing.
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.shape
from shapely.geometry import Polygon
import shapely
import shapely.ops
import numpy as np
def extract_walls_to_shapely(
ifc_path: str,
projection_axis: str = "Z",
min_area: float = 0.001
) -> list[dict]:
"""Extract IfcWall geometries and convert them to validated Shapely Polygons."""
model = ifcopenshell.open(ifc_path)
walls = model.by_type("IfcWall")
results = []
# Configure geometry kernel for pure mesh extraction
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_WORLD_COORDS, True)
settings.set(settings.EXCLUDE_SOLIDS_AND_SURFACES, False)
settings.set(settings.INCLUDE_CURVES, False)
settings.set(settings.APPLY_DEFAULT_MATERIALS, False)
axis_map = {"X": 0, "Y": 1, "Z": 2}
proj_idx = axis_map.get(projection_axis.upper(), 2)
other_axes = [i for i in range(3) if i != proj_idx]
for wall in walls:
try:
shape = ifcopenshell.geom.create_shape(settings, wall)
verts = np.array(ifcopenshell.util.shape.get_vertices(shape)).reshape(-1, 3)
faces = ifcopenshell.util.shape.get_faces(shape)
polygons = []
for face in faces:
# Project 3D vertices to 2D plane
coords = [(verts[i][other_axes[0]], verts[i][other_axes[1]]) for i in face]
try:
poly = Polygon(coords)
if poly.is_valid and poly.area > min_area:
polygons.append(poly)
except Exception:
continue
if polygons:
# Merge adjacent triangles into coherent wall footprints
merged_geom = shapely.ops.unary_union(polygons)
# Ensure valid topology for GIS/CAD consumption
valid_geom = shapely.make_valid(merged_geom)
results.append({
"id": wall.GlobalId,
"geometry": valid_geom,
"status": "success"
})
else:
results.append({
"id": wall.GlobalId,
"geometry": None,
"status": "no_faces"
})
except Exception as e:
results.append({
"id": wall.GlobalId,
"geometry": None,
"status": "error",
"message": str(e)
})
return results
Critical Validation & Edge Cases
Raw IFC geometry rarely translates cleanly to 2D without intervention. Production deployments must account for three primary failure modes:
1. Triangulation vs. Original Polygons
ifcopenshell tessellates complex solids into triangles by default. When projecting, adjacent triangles share edges but may produce sliver polygons or micro-gaps. Using shapely.ops.unary_union resolves these boundaries automatically. For strict footprint extraction, consider filtering by face normal direction to exclude vertical or sloped surfaces before projection.
2. IFC2x3 vs. IFC4 Representation Mapping
Legacy IFC2x3 files frequently use IfcExtrudedAreaSolid or IfcSweptDiskSolid with implicit sweep paths. IFC4 introduces IfcSurfaceCurveSweptAreaSolid and explicit B-Rep definitions. The geometry kernel abstracts these differences, but coordinate precision varies. Always normalize units (IFC defaults to meters) and apply a tolerance threshold (min_area) to discard projection artifacts. Refer to the official IFC specification for entity mapping details.
3. Self-Intersections & Invalid Rings
Walls with complex openings, intersecting cores, or non-planar base curves often produce invalid Shapely geometries after projection. The shapely.make_valid() routine repairs most topological defects by snapping vertices and splitting overlapping segments. For strict GIS compliance, run is_valid checks post-merge and log failures for manual review. Detailed validation strategies are documented in the Shapely manual.
Integration Notes for GIS/CAD Workflows
Once validated, export geometries using shapely.to_wkt() or shapely.to_geojson() for direct ingestion into PostGIS, QGIS, or CAD plugins. Maintain the GlobalId as a persistent foreign key to preserve BIM-to-GIS traceability. When scaling to full-model extraction, batch geometry creation in chunks of 500–1,000 elements to manage OpenCASCADE memory overhead. For advanced filtering, combine this pipeline with Python Parsing & Geometry Extraction utilities to isolate structural, architectural, or MEP-specific wall classifications before spatial processing.