Archicad Python API
About automating tasks in Archicad using the Python API.
SOLVED!

python slabs in zones

Dano_
Participant
Hello, I'm working on a script that prints the IDs of all slabs in the zone. However, I have a problem. The script prints out all slabs only if the slab is in the zone or the zone is in the slab; if I provide only a part, it doesn't work.
 
For some reason, it is not possible to align all the slabs with the zone boundary.
 
Can someone please help me?

my code 

from archicad import ACConnection

# Connecting to Archicad
conn = ACConnection.connect()

# 🔹 Defining the layer we want to filter
floor_layer_name = "floor" # Adjust according to the layer name in your project

# Retrieving all zones in the project
zones = conn.commands.GetElementsByType('Zone')

# Printing the number of zones
print(f"found zones: {len(zones)}")

# Retrieving the PropertyId for "Zone Number" instead of "Element ID"
zone_number_property = conn.utilities.GetBuiltInPropertyId('Zone_ZoneNumber')
zone_name_property = conn.utilities.GetBuiltInPropertyId('Zone_ZoneName')

# Retrieving property values for all zones
property_values = conn.commands.GetPropertyValuesOfElements(
[zone.elementId for zone in zones],
[zone_number_property, zone_name_property]
)

# Retrieving the IDs of elements that are boundaries to the zones
boundary_data = conn.commands.GetElementsRelatedToZones([zone.elementId for zone in zones])

# Retrieving the PropertyId for "Element ID" and "ModelView_LayerName" of the boundary elements
element_id_property = conn.utilities.GetBuiltInPropertyId('General_ElementID')
layer_name_property = conn.utilities.GetBuiltInPropertyId('ModelView_LayerName')

# Iterating through zones and printing information
for zone, prop_value, boundaries in zip(zones, property_values, boundary_data):
zone_number = prop_value.propertyValues[0].propertyValue.value
zone_name = prop_value.propertyValues[1].propertyValue.value if prop_value.propertyValues[1].propertyValue else "Unknown zone"

# Retrieving the IDs of elements that are boundaries to the zone
boundary_elements = [b.elementId for b in boundaries.elements]

# 🆕 List to store all floors for the given zone (to ensure none are skipped)
filtered_boundary_ids = []

# Retrieving the actual IDs of boundary elements and their layer names
boundary_properties = conn.commands.GetPropertyValuesOfElements(boundary_elements, [element_id_property, layer_name_property])

# 🆕 Using a traditional loop instead of a list comprehension
for bp in boundary_properties:
element_id_value = bp.propertyValues[0].propertyValue.value # Element ID
layer_value = bp.propertyValues[1].propertyValue.value if bp.propertyValues[1].propertyValue else None # Element layer

# If the element is in the "floor" layer, add it to the list (no skipping)
if layer_value == floor_layer_name:
filtered_boundary_ids.append(element_id_value)

# Printing correct values including all floors for each zone
print(f"zone number: {zone_number}, name: {zone_name}, Boundary elements in the layer '{floor_layer_name}' (Archicad ID): {filtered_boundary_ids}")




slab zone 1.jpg
1 ACCEPTED SOLUTION

Accepted Solutions
Solution

Hi!

I think the function GetElementsRelatedToZones just doesn't consider a slab part of a zone if one is not contained wholly in the other. So you've ran into a limit with that function.

One alternative approach which could work for your situation is to use Get2DBoundingBoxes on each zone and slab and compare if there's any overlap. (Or even use the 3D bounding box).

 


PS: Please use the code sample block for your Python code. Otherwise we have to guess the indentation levels (important for loops and "if" etc.) and it just looks better. Here's how I think you wanted your code to look like:

from archicad import ACConnection

# Connecting to Archicad
conn = ACConnection.connect()

# Defining the layer we want to filter
floor_layer_name = "Structural - Bearing" # Adjust according to the layer name in your project

# Retrieving all zones in the project
zones = conn.commands.GetElementsByType('Zone')

# Printing the number of zones
print(f"found zones: {len(zones)}")

# Retrieving the PropertyId for "Zone Number" instead of "Element ID"
zone_number_property = conn.utilities.GetBuiltInPropertyId('Zone_ZoneNumber')
zone_name_property = conn.utilities.GetBuiltInPropertyId('Zone_ZoneName')

# Retrieving property values for all zones
property_values = conn.commands.GetPropertyValuesOfElements(
    [zone.elementId for zone in zones],
    [zone_number_property, zone_name_property]
)

# Retrieving the IDs of elements that are boundaries to the zones
boundary_data = conn.commands.GetElementsRelatedToZones([zone.elementId for zone in zones])

# Retrieving the PropertyId for "Element ID" and "ModelView_LayerName" of the boundary elements
element_id_property = conn.utilities.GetBuiltInPropertyId('General_ElementID')
layer_name_property = conn.utilities.GetBuiltInPropertyId('ModelView_LayerName')

# Iterating through zones and printing information
for zone, prop_value, boundaries in zip(zones, property_values, boundary_data):
    zone_number = prop_value.propertyValues[0].propertyValue.value
    zone_name = prop_value.propertyValues[1].propertyValue.value if prop_value.propertyValues[1].propertyValue else "Unknown zone"

    # Retrieving the IDs of elements that are boundaries to the zone
    boundary_elements = [b.elementId for b in boundaries.elements]

    # List to store all floors for the given zone (to ensure none are skipped)
    filtered_boundary_ids = []

    # Retrieving the actual IDs of boundary elements and their layer names
    boundary_properties = conn.commands.GetPropertyValuesOfElements(boundary_elements, [element_id_property, layer_name_property])

    # Using a traditional loop instead of a list comprehension
    for bp in boundary_properties:
        element_id_value = bp.propertyValues[0].propertyValue.value # Element ID
        layer_value = bp.propertyValues[1].propertyValue.value if bp.propertyValues[1].propertyValue else None # Element layer

        # If the element is in the "floor" layer, add it to the list (no skipping)
        if layer_value == floor_layer_name:
            filtered_boundary_ids.append(element_id_value)

    # Printing correct values including all floors for each zone
    print(f"zone number: {zone_number}, name: {zone_name}, Boundary elements in the layer '{floor_layer_name}' (Archicad ID): {filtered_boundary_ids}")

 

View solution in original post

1 REPLY 1
Solution

Hi!

I think the function GetElementsRelatedToZones just doesn't consider a slab part of a zone if one is not contained wholly in the other. So you've ran into a limit with that function.

One alternative approach which could work for your situation is to use Get2DBoundingBoxes on each zone and slab and compare if there's any overlap. (Or even use the 3D bounding box).

 


PS: Please use the code sample block for your Python code. Otherwise we have to guess the indentation levels (important for loops and "if" etc.) and it just looks better. Here's how I think you wanted your code to look like:

from archicad import ACConnection

# Connecting to Archicad
conn = ACConnection.connect()

# Defining the layer we want to filter
floor_layer_name = "Structural - Bearing" # Adjust according to the layer name in your project

# Retrieving all zones in the project
zones = conn.commands.GetElementsByType('Zone')

# Printing the number of zones
print(f"found zones: {len(zones)}")

# Retrieving the PropertyId for "Zone Number" instead of "Element ID"
zone_number_property = conn.utilities.GetBuiltInPropertyId('Zone_ZoneNumber')
zone_name_property = conn.utilities.GetBuiltInPropertyId('Zone_ZoneName')

# Retrieving property values for all zones
property_values = conn.commands.GetPropertyValuesOfElements(
    [zone.elementId for zone in zones],
    [zone_number_property, zone_name_property]
)

# Retrieving the IDs of elements that are boundaries to the zones
boundary_data = conn.commands.GetElementsRelatedToZones([zone.elementId for zone in zones])

# Retrieving the PropertyId for "Element ID" and "ModelView_LayerName" of the boundary elements
element_id_property = conn.utilities.GetBuiltInPropertyId('General_ElementID')
layer_name_property = conn.utilities.GetBuiltInPropertyId('ModelView_LayerName')

# Iterating through zones and printing information
for zone, prop_value, boundaries in zip(zones, property_values, boundary_data):
    zone_number = prop_value.propertyValues[0].propertyValue.value
    zone_name = prop_value.propertyValues[1].propertyValue.value if prop_value.propertyValues[1].propertyValue else "Unknown zone"

    # Retrieving the IDs of elements that are boundaries to the zone
    boundary_elements = [b.elementId for b in boundaries.elements]

    # List to store all floors for the given zone (to ensure none are skipped)
    filtered_boundary_ids = []

    # Retrieving the actual IDs of boundary elements and their layer names
    boundary_properties = conn.commands.GetPropertyValuesOfElements(boundary_elements, [element_id_property, layer_name_property])

    # Using a traditional loop instead of a list comprehension
    for bp in boundary_properties:
        element_id_value = bp.propertyValues[0].propertyValue.value # Element ID
        layer_value = bp.propertyValues[1].propertyValue.value if bp.propertyValues[1].propertyValue else None # Element layer

        # If the element is in the "floor" layer, add it to the list (no skipping)
        if layer_value == floor_layer_name:
            filtered_boundary_ids.append(element_id_value)

    # Printing correct values including all floors for each zone
    print(f"zone number: {zone_number}, name: {zone_name}, Boundary elements in the layer '{floor_layer_name}' (Archicad ID): {filtered_boundary_ids}")