def main(self): # Create a selection sel = self.uidoc.Selection # Prompt the user to pick the required external link try: ref = sel.PickObject(ObjectType.Element, "Please pick a linked model instance") # Get AR RVT link rvt_link = self.doc.GetElement(ref.ElementId) if not self.rvt_link_check(rvt_link): WinForms.MessageBox.Show("The operation was cancelled!", "Error!", WinForms.MessageBoxButtons.OK, WinForms.MessageBoxIcon.Information) return self.transform = rvt_link.GetTotalTransform() linkedDoc = rvt_link.GetLinkDocument() # Create a room collector instance room_collector = FilteredElementCollector(linkedDoc) # Create a space collector instance self._space_collector = FilteredElementCollector( self.doc).WhereElementIsNotElementType().OfCategory( BuiltInCategory.OST_MEPSpaces) # Collect levels from the current document levels = FilteredElementCollector( self.doc).WhereElementIsNotElementType().OfCategory( BuiltInCategory.OST_Levels) # For each level in the current model define its elevation and create level elevation:Level dictionary self.lvls_dict = {level.Elevation: level for level in levels} # Collect rooms from RVT link if room_collector and room_collector.GetElementCount() != 0: self.rooms_list = room_collector.WhereElementIsNotElementType( ).OfCategory(BuiltInCategory.OST_Rooms) self.counter = 0 self.startProgress.emit(room_collector.GetElementCount() + self.spaces_count + \ (2*(room_collector.GetElementCount()*len(self._excel_parameters)))) self.__main() self.endProgress.emit() except Exception as e: logger.error(e, exc_info=True) WinForms.MessageBox.Show("The operation was cancelled!", "Error!", WinForms.MessageBoxButtons.OK, WinForms.MessageBoxIcon.Information) return
class Model(object): def __init__(self, __revit__, search_id=None, excel_parameters=None, xl_file_path=None, xl_write_flag=False): # region Get Document and Application self.doc = __revit__.ActiveUIDocument.Document self.uidoc = UIDocument(self.doc) self.app = __revit__.Application self.uiapp = UIApplication(self.app) self.excel = Excel.ApplicationClass() System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo( "en-US") # endregion # region Initial parameters self._search_id = search_id self._excel_parameters = excel_parameters self._xl_file_path = xl_file_path self._xl_write_flag = xl_write_flag self.New_MEPSpaces = {} self.Exist_MEPSpaces = {} # Create a space collector instance self._space_collector = FilteredElementCollector( self.doc).WhereElementIsNotElementType().OfCategory( BuiltInCategory.OST_MEPSpaces) # endregion # region Custom Events self.startProgress = Event() self.ReportProgress = Event() self.endProgress = Event() # endregion # region Getters and Setters @property def search_id(self): return self._search_id @search_id.setter def search_id(self, value): self._search_id = value @property def xl_file_path(self): return self._xl_file_path @xl_file_path.setter def xl_file_path(self, value): self._xl_file_path = value @property def xl_write_flag(self): return self._xl_write_flag @xl_write_flag.setter def xl_write_flag(self, value): self._xl_write_flag = value @property def excel_parameters(self): return self._excel_parameters @excel_parameters.setter def excel_parameters(self, par_list): self._excel_parameters = par_list @property def spaces_count(self): self._spaces_count = self._space_collector.GetElementCount() return self._spaces_count # endregion Getters and Setters def main(self): # Create a selection sel = self.uidoc.Selection # Prompt the user to pick the required external link try: ref = sel.PickObject(ObjectType.Element, "Please pick a linked model instance") # Get AR RVT link rvt_link = self.doc.GetElement(ref.ElementId) if not self.rvt_link_check(rvt_link): WinForms.MessageBox.Show("The operation was cancelled!", "Error!", WinForms.MessageBoxButtons.OK, WinForms.MessageBoxIcon.Information) return self.transform = rvt_link.GetTotalTransform() linkedDoc = rvt_link.GetLinkDocument() # Create a room collector instance room_collector = FilteredElementCollector(linkedDoc) # Create a space collector instance self._space_collector = FilteredElementCollector( self.doc).WhereElementIsNotElementType().OfCategory( BuiltInCategory.OST_MEPSpaces) # Collect levels from the current document levels = FilteredElementCollector( self.doc).WhereElementIsNotElementType().OfCategory( BuiltInCategory.OST_Levels) # For each level in the current model define its elevation and create level elevation:Level dictionary self.lvls_dict = {level.Elevation: level for level in levels} # Collect rooms from RVT link if room_collector and room_collector.GetElementCount() != 0: self.rooms_list = room_collector.WhereElementIsNotElementType( ).OfCategory(BuiltInCategory.OST_Rooms) self.counter = 0 self.startProgress.emit(room_collector.GetElementCount() + self.spaces_count + \ (2*(room_collector.GetElementCount()*len(self._excel_parameters)))) self.__main() self.endProgress.emit() except Exception as e: logger.error(e, exc_info=True) WinForms.MessageBox.Show("The operation was cancelled!", "Error!", WinForms.MessageBoxButtons.OK, WinForms.MessageBoxIcon.Information) return def __main(self): try: # Create spaces by rooms self.create_spaces_by_rooms(self.rooms_list) # Write data to an excel file if self._xl_write_flag: self.__write_to_excel(self._excel_parameters) except Exception as e: logger.error(e, exc_info=True) pass def rvt_link_check(self, rvt_link_instance): rvt_link_type = self.doc.GetElement(rvt_link_instance.GetTypeId()) try: room_bound = rvt_link_type.get_Parameter( BuiltInParameter.WALL_ATTR_ROOM_BOUNDING) if not room_bound.AsInteger(): result = WinForms.MessageBox.Show( "Room Bounding is turned off! Would you like to turn it on?", "Warning!", WinForms.MessageBoxButtons.YesNo, WinForms.MessageBoxIcon.Question) if result == WinForms.DialogResult.Yes: with Transaction(self.doc, "Set Room Bounding") as tr: tr.Start() room_bound.Set(1) tr.Commit() return True return False return True except Exception as e: logger.error(e, exc_info=True) return False def create_spaces_by_rooms(self, rooms): # Initiate the transacton group with TransactionGroup(self.doc, "Batch create spaces/Transfer parameters") as tg: tg.Start() Room_UniqueIds = [ room.UniqueId for room in rooms if room.Area > 0 and room.Location != None ] self.counter += 1 self.ReportProgress.emit(self.counter) # Define if there're spaces in a model if self._space_collector.GetElementCount() == 0: # If there are no spaces # Create a space for room in rooms: if room.Area > 0 and room.UniqueId not in self.New_MEPSpaces: self.space_creator(room, self.lvls_dict) self.counter += 1 self.ReportProgress.emit(self.counter) # If there are spaces in the model else: self.space_check(Room_UniqueIds) # For each room in RVT link rooms take room UniqueId and check if there's space with the same Id among the existing MEP spaces for room in rooms: # If there's such space get it if room.UniqueId in self.Exist_MEPSpaces: exst_space = self.Exist_MEPSpaces[room.UniqueId] self.space_updater(room, exst_space) self.counter += 1 self.ReportProgress.emit(self.counter) # If there's no such space else: # Create a space if room.Area > 0 and room.UniqueId not in self.New_MEPSpaces: self.space_creator(room, self.lvls_dict) self.counter += 1 self.ReportProgress.emit(self.counter) tg.Assimilate() def space_creator(self, room, lvls): ''' Function creates new spaces room: Revit Room, lvls: level elevations and levels dictionary ''' try: # Get the room level room_lvl = room.Level # Get a level from the lvls dictionary by room level elevation lvl = lvls.get(room_lvl.Elevation) # Create space by coordinates and level taken from room with Transaction(self.doc, "Batch create spaces") as tr: tr.Start() options = tr.GetFailureHandlingOptions() failureHandler = ErrorSwallower() options.SetFailuresPreprocessor(failureHandler) options.SetClearAfterRollback(True) tr.SetFailureHandlingOptions(options) room_coords = self.transform.OfPoint(room.Location.Point) space = self.doc.Create.NewSpace( lvl, UV(room_coords.X, room_coords.Y)) # Get "REFERENCED_ROOM_UNIQUE_ID" parameter ref_id_par = space.GetParameters(self._search_id)[0] # Assign room UniqueID to "REFERENCED_ROOM_UNIQUE_ID" parameter if ref_id_par: if ref_id_par.StorageType == StorageType.String: ref_id_par.Set(room.UniqueId) self.New_MEPSpaces[ room.UniqueId] = self.New_MEPSpaces.get( room.UniqueId, space) self.para_setter(room, space) else: ref_id_par.Set(room.Id.IntegerValue) self.New_MEPSpaces[ room.Id.IntegerValue] = self.New_MEPSpaces.get( room.Id.IntegerValue, space) self.para_setter(room, space) tr.Commit() except Exception as e: logger.error(e, exc_info=True) pass try: with Transaction(self.doc, "Set space Id") as tr: tr.Start() # Get "ID_revit" parameter of a MEP space space_id = space.GetParameters("ID_revit")[0] # Assign space ID to "ID_revit" parameter if space_id: space_id.Set(space.Id.IntegerValue) tr.Commit() except Exception as e: logger.error(e, exc_info=True) pass def space_updater(self, room, exst_space): ''' Function updates existing spaces and moves them if necessary ''' try: # Extract space coordinates exst_space_coords = exst_space.Location.Point # Get room coordinates room_coords = self.transform.OfPoint(room.Location.Point) # Compare two sets of coordinates # If they are almost the same if exst_space_coords.IsAlmostEqualTo(room_coords): with Transaction(self.doc, "Update parameters") as tr: tr.Start() options = tr.GetFailureHandlingOptions() failureHandler = ErrorSwallower() options.SetFailuresPreprocessor(failureHandler) options.SetClearAfterRollback(True) tr.SetFailureHandlingOptions(options) # Transfer room parameters to a corresponding space self.para_setter(room, exst_space) tr.Commit() # Otherwise, move the existing space according to room coordinates else: move_vector = room_coords - exst_space_coords with Transaction(self.doc, "Move spaces") as tr: tr.Start() options = tr.GetFailureHandlingOptions() failureHandler = ErrorSwallower() options.SetFailuresPreprocessor(failureHandler) options.SetClearAfterRollback(True) tr.SetFailureHandlingOptions(options) exst_space.Location.Move(move_vector) # Transfer room parameters to a corresponding space self.para_setter(room, exst_space) tr.Commit() except System.MissingMemberException as e: logger.error(e, exc_info=True) pass def space_check(self, Room_UniqueIds): ''' Function checks for not placed spaces, obsolete spaces and deletes them ''' # Collect all existing MEP spaces in this document for space in self._space_collector.ToElements(): # Get "REFERENCED_ROOM_UNIQUE_ID" parameter of each space id_par_list = space.GetParameters(self._search_id) ref_id_par_val = id_par_list[0].AsString() # Check if REFERENCED_ROOM_UNIQUE_ID is in room Room_UniqueIds # If it's not the case delete the corresponding space if space.Area == 0 or ref_id_par_val not in Room_UniqueIds: with Transaction(self.doc, "Delete spaces") as tr: tr.Start() try: self.doc.Delete(space.Id) except Exception as e: logger.error(e, exc_info=True) pass tr.Commit() else: # Otherwise cast it into Existing MEP spaces dictionary self.Exist_MEPSpaces[ ref_id_par_val] = self.Exist_MEPSpaces.get( ref_id_par_val, space) self.counter += 1 self.ReportProgress.emit(self.counter) def para_setter(self, room, space): ''' Function transers parameters from the room to newly created spaces room: Revit Room, space: MEPSpace ''' # For each parameter in room parameters define if it's shared or builtin parameter for par in room.Parameters: par_name = par.Definition.Name if par.IsShared and par_name in self._excel_parameters: # If room parameter is shared get space from Spaces dictionary by UniqueId and extract corresponding space parameter from it by room parameter GUID # Depending on the room parameter storage type set its value to the space parameter if not par.IsReadOnly: try: space_par = space.get_Parameter(par.GUID) if par.StorageType == StorageType.String and par.HasValue: space_par.Set(par.AsString()) elif par.StorageType == StorageType.Integer and par.HasValue: space_par.Set(par.AsInteger()) elif par.StorageType == StorageType.Double and par.HasValue: space_par.Set(par.AsDouble()) except Exception as e: logger.error(e, exc_info=True) pass self.counter += 1 self.ReportProgress.emit(self.counter) elif par.Definition.BuiltInParameter != BuiltInParameter.INVALID\ and LabelUtils.GetLabelFor(par.Definition.BuiltInParameter) in self._excel_parameters: # If room parameter is builtin get space from Spaces dictionary by UniqueId and extract corresponding space parameter from it by builtin parameter # Depending on the room parameter storage type set its value to the space parameter if not par.IsReadOnly: try: space_par = space.get_Parameter(par.Definition) if par.StorageType == StorageType.String and par.HasValue: space_par.Set(par.AsString()) elif par.StorageType == StorageType.Integer and par.HasValue: space_par.Set(par.AsInteger()) elif par.StorageType == StorageType.Double and par.HasValue: space_par.Set(par.AsDouble()) except Exception as e: logger.error(e, exc_info=True) pass self.counter += 1 self.ReportProgress.emit(self.counter) def delete_spaces(self): ''' Function deletes spaces ''' self.counter = 0 space_collector = FilteredElementCollector( self.doc).WhereElementIsNotElementType().OfCategory( BuiltInCategory.OST_MEPSpaces) spaces = space_collector.ToElements() self.startProgress.emit(len(spaces)) with Transaction(self.doc, "Delete spaces") as tr: tr.Start() for space in spaces: try: self.doc.Delete(space.Id) self.counter += 1 self.ReportProgress.emit(self.counter) except Exception as e: logger.error(e, exc_info=True) pass tr.Commit() self.New_MEPSpaces = {} self.Exist_MEPSpaces = {} self.endProgress.emit() def merge_two_dicts(self, d1, d2): ''' Merges two dictionaries Returns a merged dictionary ''' d_merged = d1.copy() d_merged.update(d2) return d_merged def write_to_excel(self): """ Writing parameters to an Excel workbook """ self.counter = 0 self.startProgress.emit(self._space_collector.GetElementCount() + \ ((self._space_collector.GetElementCount()*len(self._excel_parameters)))) # Obtaining existing spaces self.__get_exist_spaces() # Write data to an excel file self.__write_to_excel(self._excel_parameters) def __get_exist_spaces(self): """ Obtains existing spaces Fills the Exist_MEPSpaces dictionary: key: reference_room_id, value: MEP space """ self.Exist_MEPSpaces = {} # Collect all existing MEP spaces in this document for space in self._space_collector: try: # Get "REFERENCED_ROOM_UNIQUE_ID" parameter of each space id_par_list = space.GetParameters(self._search_id) ref_id_par_val = id_par_list[0].AsString() # Cast space into Existing MEP spaces dictionary self.Exist_MEPSpaces[ ref_id_par_val] = self.Exist_MEPSpaces.get( ref_id_par_val, space) self.counter += 1 self.ReportProgress.emit(self.counter) except Exception as e: logger.error(e, exc_info=True) pass def __write_to_excel(self, params_to_write): """ Writing parameters to an Excel workbook (private method) """ units = { UnitType.UT_Length: DisplayUnitType.DUT_METERS, UnitType.UT_Area: DisplayUnitType.DUT_SQUARE_METERS, UnitType.UT_Volume: DisplayUnitType.DUT_CUBIC_METERS, UnitType.UT_Number: DisplayUnitType.DUT_PERCENTAGE, UnitType.UT_HVAC_Heating_Load: DisplayUnitType.DUT_KILOWATTS, UnitType.UT_HVAC_Cooling_Load: DisplayUnitType.DUT_KILOWATTS, UnitType.UT_HVAC_Airflow_Density: DisplayUnitType.DUT_CUBIC_FEET_PER_MINUTE_SQUARE_FOOT, UnitType.UT_HVAC_Airflow: DisplayUnitType.DUT_CUBIC_METERS_PER_HOUR } columns = list(string.ascii_uppercase) + [ "".join(i) for i in product(string.ascii_uppercase, repeat=2) ] params = dict(izip(params_to_write, columns)) try: workBook = self.excel.Workbooks.Open(r'{}'.format( self._xl_file_path)) workSheet = workBook.Worksheets(1) except Exception as e: logger.error(e, exc_info=True) pass for par, col in params.items(): workSheet.Cells[1, col] = par row = 2 MEPSpaces = self.merge_two_dicts(self.New_MEPSpaces, self.Exist_MEPSpaces) for space_id, space in MEPSpaces.items(): try: workSheet.Cells[row, "A"] = space_id for par in space.Parameters: par_name = par.Definition.Name if par_name in params.keys(): if par.StorageType == StorageType.String: workSheet.Cells[row, params[par_name]] = par.AsString() elif par.StorageType == StorageType.Integer: workSheet.Cells[ row, params[par_name]] = par.AsInteger() else: conv_val = UnitUtils.ConvertFromInternalUnits( par.AsDouble(), units.get(par.Definition.UnitType, DisplayUnitType.DUT_GENERAL)) workSheet.Cells[row, params[par_name]] = conv_val self.counter += 1 self.ReportProgress.emit(self.counter) row += 1 except Exception as e: logger.error(e, exc_info=True) pass # makes the Excel application visible to the user self.excel.Visible = True