Revit Room from Boundary

Has anyone used a Python function to generate Revit room(s) from a room boundary(s)? Any suggestions on whether to start with a template Revit room?



1 Like

Is this to programmatically create a Room to receive in Revit or do something from a Revit room boundary?

1 Like

I am using a fictional architectural program with room names, lengths, widths, heights etc.

Name Room Type Room No Room Length Room Width Room Centroid x Room Centroid y Room Centroid z Units Room Height
Exam_1 Exam 1 12 12 6 6 0 ft 15
Exam_2 Exam 2 12 12 18 6 0 ft 15
Exam_3 Exam 3 14 12 31 6 0 ft 15
Exam_4 Exam 4 14 12 45 6 0 ft 15
Exam_5 Exam 5 12 12 58 6 0 ft 15
Exam_6 Exam 6 12 12 70 6 0 ft 15
Exam_7 Exam 7 12 12 6 25 0 ft 15
Exam_8 Exam 8 12 12 18 25 0 ft 15
Conference_1 Conference 1 20 12 41 25 0 ft 15
Nursing Station_1 Nursing Station 1 14 12 58 25 0 ft 15

I am trying to generate these rooms within Speckle using Python. Here is my code:

# Import necessary classes from the Specklepy library for working with geometry and collections.
from specklepy.objects.geometry import Point, Line, Polycurve
from specklepy.objects.other import Collection
from specklepy.objects.other import RevitParameter
from specklepy.objects import Base
# Import the pandas library for data manipulation.
import pandas as pd

# Define the path to the CSV file containing program requirements.
program_requirement_file = 'ProgramExample.csv'

# Read room program requirements from the CSV file into a pandas DataFrame.
df_rooms_program = pd.read_csv(program_requirement_file)
# Calculate the area for each room by multiplying its length by its width and store it in a new column.
df_rooms_program['Room Area'] = df_rooms_program['Room Length'] * df_rooms_program['Room Width']
# Calculate the volume for each room by multiplying its area by its height and store it in a new column.
df_rooms_program['Room Volume'] = df_rooms_program['Room Area'] * df_rooms_program['Room Height']

# Define a function to create the boundary of a room given its length, width, and a base point.
def create_room_boundary(length, width, basePoint):
        # Calculate the centroid coordinates of the room based on the provided base point.
        x_centroid = basePoint.x
        y_centroid = basePoint.y
        z_centroid = basePoint.z
        # Calculate the coordinates for each corner of the room boundary rectangle.
        bottom_left = Point(x=x_centroid - length/2, y=y_centroid - width/2)
        bottom_right = Point(x=x_centroid + length/2, y=y_centroid - width/2)
        top_right = Point(x=x_centroid + length/2, y=y_centroid + width/2)
        top_left = Point(x=x_centroid - length/2, y=y_centroid + width/2)

        # Create line segments that represent each side of the room boundary.
        bottom_segment = Line(start=bottom_left, end=bottom_right)  # Bottom side of the rectangle.
        right_segment = Line(start=bottom_right, end=top_right)     # Right side of the rectangle.
        top_segment = Line(start=top_right, end=top_left)           # Top side of the rectangle.
        left_segment = Line(start=top_left, end=bottom_left)        # Left side of the rectangle.

        # Combine the segments into a Polycurve to represent the complete room boundary.
        temp_room_boundary = Polycurve(segments=[bottom_segment, right_segment, top_segment, left_segment])
        # Return the constructed room boundary Polycurve.
        return temp_room_boundary

# Define a RoomObject class that inherits from the Base class, to represent a room.
class RoomObject(Base, speckle_type='Objects.BuiltElements.Room'):
    def __init__(self):
        # Initialize properties of the room object. = None
        self.area = None
        self.units = None
        self.height = None
        self.category = 'Rooms'
        self.number = None
        self.volume = None
        self.builtInCategory = 'OST_Rooms'
        self.isRevitLinkedModel = False

# Create an empty list to store the room objects.
room_list = []
# Iterate over each row in the DataFrame to create a RoomObject for each room.
for i, row in df_rooms_program.iterrows():
    # Instantiate a RoomObject.
    room = RoomObject()
    # Set the properties of the room object based on the current row's data. = row['Name']
    room.area = row['Room Area']
    room.units = row['Units']
    room.height = row['Room Height']
    room.number = row['Room No']
    room.volume = row['Room Volume']

    # Create a RevitParameter object to store custom parameters.
    temp_parameters = RevitParameter()
    temp_parameters['Unbounded Height'] = row['Room Height']
    temp_parameters['Name'] = row['Name']
    # Assign the parameters to the room object.
    room.parameters = temp_parameters
    # Create a Point object for the room's base point.
    temp_base_point = Point()
    temp_base_point.x = float(row['Room Centroid x'])
    temp_base_point.y = float(row['Room Centroid y'])
    temp_base_point.z = float(row['Room Centroid z'])
    # Set the base point of the room.
    room.basePoint = temp_base_point

    # Generate and assign the room's boundary.
    room.outline = create_room_boundary(row['Room Length'], row['Room Width'], room.basePoint)

    # Add the room object to the list.

# Instantiate a Collection object to group all room objects.
main_collection = Collection() = "Rooms"  # Name the collection.
main_collection.applicationId = "Rooms"  # Set an application ID for the collection.
main_collection.collectionType = "Revit Category"  # Specify the type of collection.

# Assign the list of room objects to the collection's elements.
main_collection.elements = room_list

The problem, I am facing is when the collection is sent to Speckle, parameters and basePoint information is not coming in fully (.e.g. basePoint: x, y, z or parameters. room height etc.). When I try to receive this information from Revit, only lines are coming in.

Thanks in advance.


1 Like

This is a limitation/feature of Revit, where rooms only exist (mostly) if they are bounded by geometry.

The way our conversion currently exists doesn’t make use of the boundaries you have created but does use the positional centre(ish) point of the room.

We have a boundary object property on the room class because a common use case is to externalise that information from Revit, so for now, it is a Revit artefact, as it were.

That said, if you have existing rooms in a revit model, then the centre-point information should be enough to recreate the rooms, with the Revit room essentially created by “bleeding” outward from that point to the next “room-bounding” elements, typically walls.

1 Like

I don’t know if this helps but, you can create Room separation lines using the Edges and place rooms in the centeroid.

1 Like

Indeed, I’m part way through an experiment if Revit connector could make these from boundary lines if the imported room comes in “unplaced”.

I was looking at that possibility because if you are recreating room layouts, with new parameters say into a revised wall geometry, room division would be an unwanted behaviour.

Publishing and receiving of room separation lines isn’t quite supported yet, could use more investigation

Thanks for the ideas.
We have taken the following approach:

Take an existing room object imported from Revit → update base point → update room outline → create a room box with faces and vertices (under displayValue[0].faces and displayValue[0].vertices) → repeat this process for number of rooms.

The rooms came back to Revit but non of the information like room names or updated dimensions. Basepoints were correct.

1 Like

My colleague suggested that Revit needs the room separation lines to understand it’s boundaries.