Number doors after rooms in Revit using a convenient macro

, ,

There is no denial that Revit has increased productivity and reduced costs in medium and big projects compared to CAD. It has helped the construction process in several ways; track the progress of a project, the coordination, and execution, as well as the lifecycle of a building in a single platform or with very compatible software.

Below this macro view of Revit’s capabilities, there is a microcosmos of freedom that allows you to adapt the software to your needs. Often, it may seem that Autodesk left the software incomplete, but more often than not, Revit allows you to use its API to extract and do what you need, as seen previously in this post: Create your first macro in C#

Documentation standards are left to users’ implementation

Today, we are going to solve one of the headaches that arise from the way Revit number doors. Revit numbers the doors using the Mark parameter, incrementing the last number by one. For example, if 1 is the first number, the next doors’ numbers are 2, 3, and so on. Once a door is deleted, its number will not come back unless inputted manually.

This is tedious to fix manually, and confusing as door numbers can jump floors or buildings as changes occur in the design process. So, this breaks one of the most basic rules to issue documentation sets, which is to keep coherent standards of annotation and naming.

To solve this issue, we are going to create a macro that automatically renames doors after room numbers, check our post about starting with macros if you missed it.

Create the macro

Here is the code you need to create this macro:

public void NumberDoors()
{
	Document doc = Application.ActiveUIDocument.Document;

	// Create dictionary to store door-room values
	Dictionary<FamilyInstance, string> doorNumbers = new Dictionary<FamilyInstance, string>();
	// Create dictionary to store number of doors in room
	Dictionary<string, int> doorsInRoomCount = new Dictionary<string, int>();

	// Collect all doors in project
	using (FilteredElementCollector doors = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Doors)
									.WhereElementIsNotElementType())
	{
		// Retrieve rooms from doors
		foreach (FamilyInstance door in doors)
		{
			if (door.ToRoom != null)
			{
				string roomNumber = door.ToRoom.Number;
				ClassifyDoors(doorsInRoomCount, roomNumber, doorNumbers, door);
			}
			else if (door.FromRoom != null)
			{
				string roomNumber = door.FromRoom.Number;
				ClassifyDoors(doorsInRoomCount, roomNumber, doorNumbers, door);
			}
		}
	}

	// Create transaction and make changes in Revit
	using (Transaction t = new Transaction(doc, "Number Doors"))
	{
		t.Start();

		// Empty Mark parameter to avoid duplicated values
		foreach (KeyValuePair<FamilyInstance, string> entry in doorNumbers)
		{
			Parameter doorMark = entry.Key.get_Parameter(BuiltInParameter.DOOR_NUMBER);
			doorMark.Set("");
		}

		// Populate door numbers in Mark parameter
		foreach (KeyValuePair<FamilyInstance, string> entry in doorNumbers)
		{
			Parameter doorMark = entry.Key.get_Parameter(BuiltInParameter.DOOR_NUMBER);
			doorMark.Set(entry.Value);
		}

		t.Commit();
	}
}

private void ClassifyDoors(Dictionary<string, int> doorsInRoomCount, string roomNumber, 
                    Dictionary<FamilyInstance, string> doorNumbers, FamilyInstance door)
{
	// Letters to map from integers
	const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

	// Classify door
	if (doorsInRoomCount.ContainsKey(roomNumber))
	{
		// Check door count is one, a prefix will be added to the number
		if (doorsInRoomCount[roomNumber] == 1)
		{
			// Retrieve door with single count from the dictionary that stores door-room relationship
			FamilyInstance keyDoor = doorNumbers.FirstOrDefault(x => x.Value == roomNumber).Key;
			// Change single count door prefix
			doorNumbers[keyDoor] = roomNumber + "-" + letters[doorsInRoomCount[roomNumber] - 1];
			// Store current door and room in dict
			doorNumbers[door] = roomNumber + "-" + letters[doorsInRoomCount[roomNumber]];
		}
		else
		{
			// Store current door and room in dict
			doorNumbers[door] = roomNumber + "-" + letters[doorsInRoomCount[roomNumber]];
		}
		// Increase the door count in room
		doorsInRoomCount[roomNumber] = doorsInRoomCount[roomNumber] + 1;
	}
	else
	{
		// No door is in room , add door-room to dict
		doorNumbers.Add(door, roomNumber);
		doorsInRoomCount.Add(roomNumber, 1);
	}
}

Macro logic

To solve this problem, we are going to break the macro into small and easy to accomplish tasks:

  1. Collect all door in the project
  2. Retrieve the ToRoom or FromRoom property of the door FamilyInstance
  3. Store the door number according to room number in a dictionary
  4. Store the number of doors in a room in a dictionary
  5. Modify the door-room dictionary defined in step 3 with the new input from dictionary 4
  6. Populate the door Mark parameter with the data contained in the door-room number dictionary

A point that is worth mentioning is the private method. Private methods are not visible in the macro manager and act as helpers for public methods that every Revit user can execute.

DEVELOPMENT IDEAS

As good practice, macros, plugins, or scripts should consider any case that can happen in the model. Some ideas to further develop this macro are:

  • Option to number doors according to Phases
  • Create a parameter in the doors that allows users to reverse FromRoom to ToRoom
  • Take into account doors that are in different Design Options

Leave a Reply

Your email address will not be published. Required fields are marked *