Hi all, I am trying to get Room that is inside the Area(rentable) to fill the Parameter of area"Color" to Room
# Add this import at the beginning of your script
from Autodesk.Revit.DB import ElementSet
from pyrevit import revit, DB
def get_rooms():
return DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Rooms).ToElements()
def get_area():
return DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Areas).ToElements()
def set_room_department(room, color_parameter_value):
room.Parameter[DB.BuiltInParameter.ROOM_DEPARTMENT].Set(color_parameter_value)
def process_views():
views = DB.FilteredElementCollector(revit.doc).OfClass(DB.View).ToElements()
for view in views:
# Execute the main logic for each view
main()
def main():
rooms = get_rooms()
areas = get_area()
for area in areas:
# Access the Color parameter
color_parameter = area.LookupParameter("Color")
if color_parameter is not None:
# Get the color information
color_parameter_value = color_parameter.AsString() # Assuming Color is an integer parameter
for room in rooms:
room_location = room.Location.Point
# Check if the area has a valid bounding box
area_bounding_box = area.get_BoundingBox(revit.doc.ActiveView)
if area_bounding_box is not None:
# Check if the room location is inside the bounding box of the associated area plan
if area_bounding_box.IsInside(room_location):
# Set the department parameter of the room with the color information
set_room_department(room, color_parameter_value)
if __name__ == "__main__":
process_views()
# Add this import at the beginning of your script
from Autodesk.Revit.DB import ElementSet
from pyrevit import revit, DB
from Autodesk.Revit.DB import Transaction
def get_rooms():
return DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Rooms).ToElements()
def get_areas():
return DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Areas).ToElements()
def set_room_department(room, color_parameter_value):
# Start a new transaction
transaction = Transaction(revit.doc, "Set Room Department")
transaction.Start()
try:
value = color_parameter_value
value_as_string = str(value)
room.LookupParameter("Department").Set(value_as_string)
# Commit the transaction if everything is successful
transaction.Commit()
except Exception as e:
# Rollback the transaction if an exception occurs
transaction.RollBack()
raise e
def process_views():
views = DB.FilteredElementCollector(revit.doc).OfClass(DB.View).ToElements()
for view in views:
# Execute the main logic for each view
main()
def main():
rooms = get_rooms()
areas = get_areas()
for area in areas:
color_parameter = area.LookupParameter("Color")
if color_parameter is not None:
color_parameter_value = color_parameter.AsString()
for room in rooms:
room_location = room.Location.Point
area_bounding_box = area.get_BoundingBox(revit.doc.ActiveView)
if area_bounding_box is not None:
# Check if the room location is inside the bounding box of the associated area plan
if (area_bounding_box.Min.X <= room_location.X <= area_bounding_box.Max.X and
area_bounding_box.Min.Y <= room_location.Y <= area_bounding_box.Max.Y):
# Set the department parameter of the room with the color information
set_room_department(room, color_parameter_value)
if __name__ == "__main__":
process_views()
Hi @saurabhsjha,
I’m glad you found out the problem.
I suggest you to take advantage of the pyrevit library functions and classes:
ger_elements_by_categories([category], doc=doc) is a shortcut for the FilteredElementCollector(doc).OfCategory(category).WhereElementIsNotElementType().ToElements()
get_elements_by_class is the shortcut for… well, you guessed it
pyrevit.revit.Transaction class is a context manager that handles the commit or rollback for you:
with Transaction(doc):
value = color_parameter_value
value_as_string = str(value)
room.LookupParameter("Department").Set(value_as_string)
Also, it might be better to open only one transaction outside of the loop to avoid a potentially very long undo history! I would put the with Transaction inside the process_views just before the loop.
I see that you’re always using the ActiveView to get the bounding box, but shouldn’t you use the view which are you looping on (by passing it as parameters to the main function)?
On that note, I see from the revit api that the FilterElementCollector can also take the view Id to limit the search to the elements you need, it might remove the need to check id the bounding box is none. Of course, if you go that way you can’t use the first shortcut function I mentioned because it doesn’t support the view id parameter (yet).
Thanks @sanzoghenzo thank you for the Suggestion you are correct i was going on Active view in each process. One thing that I was not able to get is this function get_elements_by_categories get_elements_by_categories([DB.BuiltInCategory.OST_Areas]) is in pyrevit or any other library
from pyrevit import revit, DB
from Autodesk.Revit.DB import Transaction
def get_rooms():
return DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Rooms).ToElements()
def get_areas():
return DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Areas).ToElements()
def set_room_parameter(room, area_area_value, area_unit_value):
# Set Room Area_area & Area_unit in a single transaction
with revit.Transaction("Set Room Area_area & Area_unit") as tr:
try:
value1 = area_area_value
value2 = area_unit_value
value_as_string = str(value2)
value_as_float = float(value1)
room.LookupParameter("Area_area").Set(value_as_float)
# Check if area_unit_value is not None before setting it
if value2 is not None:
room.LookupParameter("Area_unit").Set(value_as_string)
except Exception as e:
# Exception will automatically rollback the transaction
raise e
def get_area_plan_views():
# Modify the area plan type filter based on your type
area_plan_type = "AreaPlan"
return [view for view in DB.FilteredElementCollector(revit.doc).OfClass(DB.ViewPlan).ToElements()
if area_plan_type in view.ViewType.ToString()]
def process_area_plans():
area_plan_views = get_area_plan_views()
for area_plan_view in area_plan_views:
# Execute the main logic for each Area Plan
process_all_elements(area_plan_view)
def process_all_elements(area_plan_view):
rooms = get_rooms()
areas = get_areas()
for area in areas:
area_parameter = area.Area
unit_parameter = area.LookupParameter("Unit Type")
if area_parameter is not None and unit_parameter is not None:
area_area_value = area_parameter
area_unit_value = unit_parameter.AsString() # Use AsString() to get the parameter value as a string
for room in rooms:
room_location = room.Location.Point
area_bounding_box = area.get_BoundingBox(area_plan_view)
if area_bounding_box is not None:
# Check if the room location is inside the bounding box of the associated area plan
if (area_bounding_box.Min.X <= room_location.X <= area_bounding_box.Max.X and
area_bounding_box.Min.Y <= room_location.Y <= area_bounding_box.Max.Y):
# Set the department parameter of the room with the color information
set_room_parameter(room, area_area_value, area_unit_value)
if __name__ == "__main__":
process_area_plans()
Do you know that this forum has a search functionality ? You could have searched for the function name there to quickly discover that it is in the pyrevit.revit.query module.
Also, the pinned quick start post has a lot of links/references to documentation and tutorials tha should help you discover and unlock pyrevit capabilities.
hi I tried the above function it is working for rectangular rooms and all but if I try non rectangular rooms it functions weirdly(getting data from other Area ) so I updated the function to get boundaries and point .Still not showing correctly can someone shed light on what i am doing wrong here
# coding: utf8
# Metadata
__title__ = "AreatoRoom"
__author__ = "Saurabh S Jha"
__version__ = 'Version = 1.2'
__doc__ = """
Description:
It will put area calculation in Room for room to Tag based on corresponding area
How-to:
-> Run the script
Last update:
- [02.01.2024] - 1.0 RELEASE
"""
import clr
from pyrevit import revit, DB
# Revit API references
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from Autodesk.Revit.DB import XYZ
class PointInPoly:
"""Determines if a point is inside a given polygon"""
def is_point_in_polygon(self, polygon, test_point):
result = False
j = len(polygon) - 1
for i in range(len(polygon)):
if ((polygon[i].Y < test_point.Y and polygon[j].Y >= test_point.Y) or
(polygon[j].Y < test_point.Y and polygon[i].Y >= test_point.Y)):
if (polygon[i].X + (test_point.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) *
(polygon[j].X - polygon[i].X)) < test_point.X:
result = not result
j = i
return result
def get_rooms():
"""Fetches all room elements"""
return DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Rooms).ToElements()
def get_areas():
"""Fetches all area elements"""
return DB.FilteredElementCollector(revit.doc).OfCategory(DB.BuiltInCategory.OST_Areas).ToElements()
def set_room_parameters(room, area):
"""Sets parameters for a single room based on a corresponding area"""
with revit.Transaction("Update Room Parameters"):
area_parameters = (area.Area, area.LookupParameter("Unit Type").AsString(),
area.LookupParameter("Area Type").AsString(), area.LookupParameter("Color").AsString())
room.LookupParameter("Area_area").Set(float(area_parameters[0]))
room.LookupParameter("Area_unit").Set(str(area_parameters[1]))
room.LookupParameter("Department").Set(str(area_parameters[2]))
room.LookupParameter("Area_color").Set(str(area_parameters[3]))
def room_contains_point(room, point):
"""Checks if a room contains a given point"""
boundary_points = get_room_boundary_points(room)
point_in_poly = PointInPoly()
return point_in_poly.is_point_in_polygon(boundary_points, point)
def get_room_boundary_points(room):
"""Returns a list of XYZ points representing the room's boundary"""
boundary_options = DB.SpatialElementBoundaryOptions()
boundary_options.SpatialElementBoundaryLocation = DB.SpatialElementBoundaryLocation.Center
boundaries = room.GetBoundarySegments(boundary_options)
points = []
if boundaries: # Check if boundaries list is not empty
for boundary_list in boundaries:
for segment in boundary_list:
curve = segment.GetCurve()
start_point, end_point = curve.GetEndPoint(0), curve.GetEndPoint(1)
add_point_if_unique(points, start_point)
add_point_if_unique(points, end_point)
if points: # Check if points list is not empty
points.append(points[0]) # Close the loop
return points
def add_point_if_unique(points_list, point):
"""Adds a point to the list if it's not already present"""
if not any(p.IsAlmostEqualTo(point) for p in points_list):
points_list.append(point)
def process_all_elements():
"""Main function to process rooms and areas based on corresponding area"""
rooms = get_rooms()
areas = get_areas()
for room in rooms:
if room.Location:
room_point = room.Location.Point
for area in areas:
area_boundary_points = get_room_boundary_points(area)
if area_boundary_points and room_contains_point(area, room_point): # Check if area_boundary_points is not empty before proceeding
set_room_parameters(room, area)
break # Move to the next room after finding its corresponding area
if __name__ == "__main__":
process_all_elements()
Here’s my take at your code to clean it up, with comments of what I did:
# coding: utf8
# Metadata
__title__ = "AreatoRoom"
__author__ = "Saurabh S Jha"
__version__ = 'Version = 1.2'
__doc__ = """
Description:
It will put area calculation in Room for room to Tag based on corresponding area
How-to:
-> Run the script
Last update:
- [02.01.2024] - 1.0 RELEASE
"""
from pyrevit import revit, DB
# get_rooms and get_areas can be shortened as I mentioned before with this function
from pyrevit.revit.query import get_elements_by_categories
# no need to import clr, and add references
# XYZ is not used anywhere directly, noo need to import it
# The class PointInPoly only contains a method and doesn't hold any state,
# so it is better to turn it into a function
def is_point_in_polygon(polygon, test_point):
"""Determines if a point is inside a given polygon"""
result = False
# a more pythonic way to get 2 adjacent items in a list is to use zip
for prev, next in zip(polygon[:-1], polygon[1:]):
# here I'm using min/max for readability, it could be less performant
if (min(prev.Y, next.Y) < test_point.Y <= max(prev.Y, next.Y)):
if (prev.X + (test_point.Y - prev.Y) / (next.Y - prev.Y) *
(next.X - prev.X)) < test_point.X:
result = not result
return result
def set_room_parameters(room, area):
"""Sets parameters for a single room based on a corresponding area"""
with revit.Transaction("Update Room Parameters"):
# lists are not memory efficient, just pass the value to Parameter.Set
room.LookupParameter("Area_area").Set(area.Area)
room.LookupParameter("Area_unit").Set(area.LookupParameter("Unit Type").AsString())
room.LookupParameter("Department").Set(area.LookupParameter("Area Type").AsString())
room.LookupParameter("Area_color").Set(area.LookupParameter("Color").AsString())
def room_contains_point(room, point):
"""Checks if a room contains a given point"""
boundary_points = get_room_boundary_points(room)
# moving the emptiness check here, see below
if not boundary_points:
return False
# here we call the extracted function directly
return is_point_in_polygon(boundary_points, point)
def get_room_boundary_points(room):
"""Returns a list of XYZ points representing the room's boundary"""
boundary_options = DB.SpatialElementBoundaryOptions()
boundary_options.SpatialElementBoundaryLocation = DB.SpatialElementBoundaryLocation.Center
boundaries = room.GetBoundarySegments(boundary_options)
points = []
# non need to check if the list is empty, the loop will exit anyway
for boundary_list in boundaries:
for segment in boundary_list:
curve = segment.GetCurve()
# just a cosmetic change here, I hate unneeded tuples packing and unpacking ;)
add_point_if_unique(points, curve.GetEndPoint(0))
add_point_if_unique(points, curve.GetEndPoint(1))
if points: # Check if points list is not empty
points.append(points[0]) # Close the loop
return points
def add_point_if_unique(points_list, point):
"""Adds a point to the list if it's not already present"""
if not any(p.IsAlmostEqualTo(point) for p in points_list):
points_list.append(point)
def process_all_elements():
"""Main function to process rooms and areas based on corresponding area"""
rooms = get_elements_by_categories([DB.BuiltInCategory.OST_Rooms])
areas = get_elements_by_categories([DB.BuiltInCategory.OST_Areas])
for room in rooms:
# here we exit early to avoid deep indentations
if not room.Location:
continue
room_point = room.Location.Point
for area in areas:
# you already extract the points in the room_contains_point function,
# there's no need to do it here
if room_contains_point(area, room_point):
set_room_parameters(room, area)
break # Move to the next room after finding its corresponding area
if __name__ == "__main__":
process_all_elements()