bdd030ba86
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled
260 lines
12 KiB
Python
260 lines
12 KiB
Python
# =============================================================================
|
|
# mapper.py
|
|
# Maps Speckle objects → IFC entity classes.
|
|
#
|
|
# Strategy (priority order):
|
|
# 1. builtInCategory (OST_ enum from properties.builtInCategory) — most reliable
|
|
# 2. speckle_type prefix match — for typed Speckle objects
|
|
# 3. category_name string (traversal context) — display name fallback
|
|
# 4. IfcBuildingElementProxy — last resort
|
|
#
|
|
# builtInCategory values: https://www.revitapidocs.com/2019/ba1c5b30-242f-5fdc-8ea9-ec3b61e6e722.htm
|
|
# =============================================================================
|
|
|
|
|
|
# --- OST_ BuiltInCategory → IFC class (primary lookup) ---
|
|
BUILTIN_CATEGORY_MAP: dict[str, str] = {
|
|
# Architectural - Walls
|
|
"OST_Walls": "IfcWall",
|
|
"OST_CurtainWallPanels": "IfcCurtainWall",
|
|
"OST_CurtainWallMullions": "IfcMember",
|
|
"OST_Fascia": "IfcCovering",
|
|
"OST_Gutters": "IfcPipeSegment",
|
|
|
|
# Architectural - Floors / Roofs / Ceilings
|
|
"OST_Floors": "IfcSlab",
|
|
"OST_Roofs": "IfcRoof",
|
|
"OST_Ceilings": "IfcCovering",
|
|
"OST_RoofSoffit": "IfcCovering",
|
|
|
|
# Architectural - Doors / Windows / Openings
|
|
"OST_Doors": "IfcDoor",
|
|
"OST_Windows": "IfcWindow",
|
|
"OST_CurtainWallFamilies": "IfcCurtainWall",
|
|
"OST_Skylights": "IfcWindow",
|
|
|
|
# Architectural - Stairs / Ramps / Railings
|
|
"OST_Stairs": "IfcStair",
|
|
"OST_StairsRailing": "IfcRailing",
|
|
"OST_Ramps": "IfcRamp",
|
|
"OST_StairsLandings": "IfcStairFlight",
|
|
"OST_StairsRuns": "IfcStairFlight",
|
|
"OST_StairsSupports": "IfcMember",
|
|
|
|
# Architectural - Rooms / Spaces
|
|
"OST_Rooms": "IfcSpace",
|
|
"OST_Parking": "IfcSpace",
|
|
"OST_Areas": "IfcSpace",
|
|
|
|
# Architectural - Furniture / Casework
|
|
"OST_Furniture": "IfcFurnishingElement",
|
|
"OST_FurnitureSystems": "IfcFurnishingElement",
|
|
"OST_Casework": "IfcFurnishingElement",
|
|
"OST_SpecialtyEquipment": "IfcFurnishingElement",
|
|
"OST_Entourage": "IfcFurnishingElement",
|
|
|
|
# Structural
|
|
"OST_StructuralColumns": "IfcColumn",
|
|
"OST_Columns": "IfcColumn",
|
|
"OST_StructuralFraming": "IfcBeam",
|
|
"OST_StructuralFoundation": "IfcFooting",
|
|
"OST_FoundationSlab": "IfcSlab",
|
|
"OST_StructuralStiffener": "IfcMember",
|
|
"OST_StructuralTruss": "IfcMember",
|
|
"OST_StructuralConnectionModel": "IfcMechanicalFastener",
|
|
"OST_Rebar": "IfcReinforcingBar",
|
|
"OST_FabricAreas": "IfcReinforcingMesh",
|
|
"OST_FabricReinforcement": "IfcReinforcingMesh",
|
|
|
|
# MEP - HVAC
|
|
"OST_DuctCurves": "IfcDuctSegment",
|
|
"OST_DuctFitting": "IfcDuctFitting",
|
|
"OST_DuctAccessory": "IfcDuctSegment",
|
|
"OST_DuctTerminal": "IfcAirTerminal",
|
|
"OST_FlexDuctCurves": "IfcDuctSegment",
|
|
"OST_MechanicalEquipment": "IfcUnitaryEquipment",
|
|
"OST_AirTerminal": "IfcAirTerminal",
|
|
|
|
# MEP - Plumbing
|
|
"OST_PipeCurves": "IfcPipeSegment",
|
|
"OST_PipeFitting": "IfcPipeFitting",
|
|
"OST_PipeAccessory": "IfcPipeSegment",
|
|
"OST_FlexPipeCurves": "IfcPipeSegment",
|
|
"OST_PlumbingFixtures": "IfcSanitaryTerminal",
|
|
"OST_Sprinklers": "IfcFireSuppressionTerminal",
|
|
|
|
# MEP - Electrical
|
|
"OST_ElectricalEquipment": "IfcElectricDistributionBoard",
|
|
"OST_ElectricalFixtures": "IfcElectricAppliance",
|
|
"OST_LightingFixtures": "IfcLightFixture",
|
|
"OST_LightingDevices": "IfcLightFixture",
|
|
"OST_CableTray": "IfcCableCarrierSegment",
|
|
"OST_CableTrayFitting": "IfcCableCarrierFitting",
|
|
"OST_Conduit": "IfcCableCarrierSegment",
|
|
"OST_ConduitFitting": "IfcCableCarrierFitting",
|
|
"OST_CommunicationDevices": "IfcElectricAppliance",
|
|
"OST_DataDevices": "IfcElectricAppliance",
|
|
"OST_FireAlarmDevices": "IfcAlarm",
|
|
"OST_SecurityDevices": "IfcAlarm",
|
|
"OST_NurseCallDevices": "IfcElectricAppliance",
|
|
|
|
# Site / Civil
|
|
"OST_Site": "IfcSite",
|
|
"OST_Topography": "IfcGeographicElement",
|
|
"OST_Roads": "IfcRoad",
|
|
"OST_Hardscape": "IfcPavement",
|
|
"OST_Planting": "IfcGeographicElement",
|
|
"OST_SiteSurface": "IfcGeographicElement",
|
|
|
|
# Generic / Annotation (skip or proxy)
|
|
"OST_GenericModel": "IfcBuildingElementProxy",
|
|
"OST_Mass": "IfcBuildingElementProxy",
|
|
"OST_DetailComponents": "IfcAnnotation",
|
|
"OST_Lines": "IfcAnnotation",
|
|
"OST_Grids": "IfcGrid",
|
|
"OST_Levels": "IfcBuildingStorey",
|
|
"OST_Views": "IfcAnnotation",
|
|
}
|
|
|
|
|
|
# --- speckle_type → IFC class (secondary lookup) ---
|
|
SPECKLE_TYPE_MAP: dict[str, str] = {
|
|
"Objects.BuiltElements.Wall": "IfcWall",
|
|
"Objects.BuiltElements.Floor": "IfcSlab",
|
|
"Objects.BuiltElements.Roof": "IfcRoof",
|
|
"Objects.BuiltElements.Column": "IfcColumn",
|
|
"Objects.BuiltElements.Beam": "IfcBeam",
|
|
"Objects.BuiltElements.Brace": "IfcMember",
|
|
"Objects.BuiltElements.Duct": "IfcDuctSegment",
|
|
"Objects.BuiltElements.Pipe": "IfcPipeSegment",
|
|
"Objects.BuiltElements.Wire": "IfcCableCarrierSegment",
|
|
"Objects.BuiltElements.Opening": "IfcOpeningElement",
|
|
"Objects.BuiltElements.Room": "IfcSpace",
|
|
"Objects.BuiltElements.Ceiling": "IfcCovering",
|
|
"Objects.BuiltElements.Stair": "IfcStair",
|
|
"Objects.BuiltElements.Ramp": "IfcRamp",
|
|
"Objects.BuiltElements.Foundation": "IfcFooting",
|
|
"Objects.BuiltElements.Grid": "IfcGrid",
|
|
"Objects.BuiltElements.Level": "IfcBuildingStorey",
|
|
"Objects.BuiltElements.Revit.RevitWall": "IfcWall",
|
|
"Objects.BuiltElements.Revit.RevitFloor": "IfcSlab",
|
|
"Objects.BuiltElements.Revit.RevitRoof": "IfcRoof",
|
|
"Objects.BuiltElements.Revit.RevitColumn": "IfcColumn",
|
|
"Objects.BuiltElements.Revit.RevitBeam": "IfcBeam",
|
|
"Objects.BuiltElements.Revit.RevitBrace": "IfcMember",
|
|
"Objects.BuiltElements.Revit.RevitDuct": "IfcDuctSegment",
|
|
"Objects.BuiltElements.Revit.RevitPipe": "IfcPipeSegment",
|
|
"Objects.BuiltElements.Revit.RevitRoom": "IfcSpace",
|
|
"Objects.BuiltElements.Revit.RevitStair": "IfcStair",
|
|
"Objects.BuiltElements.Revit.RevitRailing": "IfcRailing",
|
|
"Objects.BuiltElements.Revit.RevitCeiling": "IfcCovering",
|
|
"Objects.BuiltElements.Revit.RevitTopography": "IfcGeographicElement",
|
|
"Objects.BuiltElements.Revit.RevitElementType": "IfcBuildingElementProxy",
|
|
"Objects.Geometry.Mesh": "IfcBuildingElementProxy",
|
|
"Objects.Geometry.Brep": "IfcBuildingElementProxy",
|
|
}
|
|
|
|
# --- Display category name → IFC class (tertiary fallback) ---
|
|
CATEGORY_MAP: dict[str, str] = {
|
|
"Walls": "IfcWall",
|
|
"Floors": "IfcSlab",
|
|
"Roofs": "IfcRoof",
|
|
"Structural Columns": "IfcColumn",
|
|
"Columns": "IfcColumn",
|
|
"Structural Framing": "IfcBeam",
|
|
"Beams": "IfcBeam",
|
|
"Ducts": "IfcDuctSegment",
|
|
"Pipes": "IfcPipeSegment",
|
|
"Conduits": "IfcCableCarrierSegment",
|
|
"Cable Trays": "IfcCableCarrierSegment",
|
|
"Rooms": "IfcSpace",
|
|
"Spaces": "IfcSpace",
|
|
"Ceilings": "IfcCovering",
|
|
"Stairs": "IfcStair",
|
|
"Ramps": "IfcRamp",
|
|
"Railings": "IfcRailing",
|
|
"Curtain Panels": "IfcCurtainWall",
|
|
"Curtain Wall Mullions": "IfcMember",
|
|
"Doors": "IfcDoor",
|
|
"Windows": "IfcWindow",
|
|
"Furniture": "IfcFurnishingElement",
|
|
"Furniture Systems": "IfcFurnishingElement",
|
|
"Casework": "IfcFurnishingElement",
|
|
"Plumbing Fixtures": "IfcSanitaryTerminal",
|
|
"Electrical Fixtures": "IfcElectricAppliance",
|
|
"Lighting Fixtures": "IfcLightFixture",
|
|
"Mechanical Equipment": "IfcUnitaryEquipment",
|
|
"Electrical Equipment": "IfcElectricDistributionBoard",
|
|
"Structural Foundations": "IfcFooting",
|
|
"Foundation Slabs": "IfcSlab",
|
|
"Topography": "IfcGeographicElement",
|
|
"Site": "IfcSite",
|
|
"Parking": "IfcSpace",
|
|
"Generic Models": "IfcBuildingElementProxy",
|
|
"Mass": "IfcBuildingElementProxy",
|
|
"Specialty Equipment": "IfcFurnishingElement",
|
|
}
|
|
|
|
|
|
def _get_builtin_category(obj) -> str | None:
|
|
"""
|
|
Read builtInCategory from obj.properties.builtInCategory.
|
|
Returns the OST_ string or None.
|
|
"""
|
|
try:
|
|
props = obj["properties"] or getattr(obj, "properties", None)
|
|
if props is None:
|
|
return None
|
|
if hasattr(props, "__getitem__"):
|
|
val = props["builtInCategory"]
|
|
else:
|
|
val = getattr(props, "builtInCategory", None)
|
|
if val and isinstance(val, str):
|
|
return val.strip()
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
|
|
def classify(obj, category_name: str = "") -> str:
|
|
"""
|
|
Determine the IFC class for a Speckle object.
|
|
|
|
Priority:
|
|
1. properties.builtInCategory (OST_ enum) — definitive Revit classification
|
|
2. speckle_type prefix match
|
|
3. category_name from traversal context (display string)
|
|
4. obj.category field
|
|
5. IfcBuildingElementProxy fallback
|
|
"""
|
|
# 1. builtInCategory — most reliable, direct Revit enum
|
|
bic = _get_builtin_category(obj)
|
|
if bic and bic in BUILTIN_CATEGORY_MAP:
|
|
return BUILTIN_CATEGORY_MAP[bic]
|
|
|
|
# 2. speckle_type
|
|
speckle_type = getattr(obj, "speckle_type", "") or ""
|
|
if speckle_type in SPECKLE_TYPE_MAP:
|
|
return SPECKLE_TYPE_MAP[speckle_type]
|
|
for key, ifc_class in SPECKLE_TYPE_MAP.items():
|
|
if speckle_type.startswith(key):
|
|
return ifc_class
|
|
|
|
# 3. category_name from traversal context
|
|
if category_name:
|
|
if category_name in CATEGORY_MAP:
|
|
return CATEGORY_MAP[category_name]
|
|
for key, ifc_class in CATEGORY_MAP.items():
|
|
if key.lower() in category_name.lower():
|
|
return ifc_class
|
|
|
|
# 4. obj.category field
|
|
obj_category = getattr(obj, "category", None)
|
|
if obj_category and isinstance(obj_category, str):
|
|
if obj_category in CATEGORY_MAP:
|
|
return CATEGORY_MAP[obj_category]
|
|
for key, ifc_class in CATEGORY_MAP.items():
|
|
if key.lower() in obj_category.lower():
|
|
return ifc_class
|
|
|
|
return "IfcBuildingElementProxy" |