How To Create Views?

Views take considerable amount of time in Revit, so it makes perfect sense to automate it. Let's start with the basics of how to create new views in Revit API?

How To Create Views?

Views take considerable amount of time in Revit, so it makes perfect sense to automate it. Let's start with the basics of how to create new views in Revit API?

Summary

Intro

When we want to automate anything with views, we should visit the View Class and have a look at its methods and properties. In there, you will be able to find nearly everything related to views: Graphic Overrides, View Filters, View Settings, View Templates, and so on…

But you won't find any methods on how to create any of the views. Because you have to look at children classes. We can see all these classes in Inheritance Hierarchy:

These subclasses have their own additional methods including a method to create these views. We will focus primarily on the view creation in this lesson, but feel free to explore what's possible to do with your views!

Here are screenshots of all the Create Methods:

You will notice that they have a lot in common. All of them require a Document and a ViewFamilyTypeId, and some of them also have more arguments.

Get Variables: ViewFamilyType

While exploring the Revit API Documentation, you've probably noticed that we always need to provide the correct ViewFamilyType to create our views. Therefore it's important to understand how to get the correct one.

There are 2 ways we can do it:

  1. Get all ViewFamilyType with a FEC, and then filter them to what we need.

  2. Or, we can get the default ViewFamilyType by using doc.GetDefaultElementTypeId

Here is how to get all ViewFamilyTypes with FilteredElementCollector:

from Autodesk.Revit.DB import *

# ╦  ╦╔═╗╦═╗╦╔═╗╔╗ ╦  ╔═╗╔═╗
# ╚╗╔╝╠═╣╠╦╝║╠═╣╠╩╗║  ║╣ ╚═╗
#  ╚╝ ╩ ╩╩╚═╩╩ ╩╚═╝╩═╝╚═╝╚═╝ VARIABLES
#==================================================
uidoc  = __revit__.ActiveUIDocument
doc    = __revit__.ActiveUIDocument.Document #type:Document


# Get Active Level
active_view  = doc.ActiveView
active_level = active_view.GenLevel #Only for ViewPlans!

# ALL VIEW TYPES
view_types = FilteredElementCollector(doc).OfClass(ViewFamilyType).ToElements()

# FILTER CERTAIN VIEW TYPES
view_types_plans    = [vt for vt in view_types if vt.ViewFamily == ViewFamily.FloorPlan]
view_types_sections = [vt for vt in view_types if vt.ViewFamily == ViewFamily.Section]
view_types_3D       = [vt for vt in view_types if vt.ViewFamily == ViewFamily.ThreeDimensional]
view_types_legends  = [vt for vt in view_types if vt.ViewFamily == ViewFamily.Legend]
view_types_drafting = [vt for vt in view_types if vt.ViewFamily == ViewFamily.Drafting]
view_types_elev     = [vt for vt in view_types if vt.ViewFamily == ViewFamily.Elevation]
view_types_ceil     = [vt for vt in view_types if vt.ViewFamily == ViewFamily.CeilingPlan]
view_types_stru     = [vt for vt in view_types if vt.ViewFamily == ViewFamily.StructuralPlan]
view_types_area     = [vt for vt in view_types if vt.ViewFamily == ViewFamily.AreaPlan]

But to be honest, most of the time we don't care that much which view type we will use. So therefore I prefer to just get the default ViewTypeId for whatever viewtype I want to create like this:

plan_id    = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeFloorPlan)
section_id = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeSection)
elev_id    = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeElevation)
str_id     = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeStructuralPlan)
view3D_id  = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewType3D)
elev_id    = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeElevation)
draft_id   = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeDrafting)
...

Once we understood how to get different ViewTypes, let's go and create some views.

Create ViewPlan - FloorPlan

Let's start with a ViewPlan as it's very commonly used and it's simple to create.

public static ViewPlan Create(
	Document document,
	ElementId viewFamilyTypeId,
	ElementId levelId

The Create method takes 3 arguments:

document, viewFamilyTypeId and levelId

But keep in mind that the ViewPlan class is used for:

  • FloorPlan

  • CeilingPlan

  • StructuralPlan

  • AreaPlan

So it means that we need to provide the correct viewFamilyTypeId argument to create what we need.

Here is an example of how to create each one of them:

#1️⃣ CREATE FLOOR PLAN
view_type_plan_id = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeFloorPlan)
view_floor        = ViewPlan.Create(doc, view_type_plan_id, active_level.Id)
#2️⃣ STRUCTURAL PLAN
view_type_str_id = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeStructuralPlan)
view_floor        = ViewPlan.Create(doc, view_type_str_id, active_level.Id)
#3️⃣ CEILING PLAN
view_type_ceil_id = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeCeilingPlan)
view_floor        = ViewPlan.Create(doc, view_type_ceil_id, active_level.Id)
#4️⃣ AREA PLAN
view_type_area_id = view_types_area[0].Id
view_floor        = ViewPlan.Create(doc, view_type_area_id, active_level.Id)

areaSchemeId = FilteredElementCollector(doc).OfClass(AreaScheme).FirstElementId()
ViewPlan.CreateAreaPlan(doc,areaSchemeId,active_level.Id )

You will also notice that we can't get a ViewTypeArea with GetDefaultElementTypeId. So, we have to get it with a FEC as I showed earlier, or we can also use another method: ViewPlan.CreateAreaPlan, which takes an AreaSchemeId

Rename Views

Also, if you decide that you want to change the View.Name, we can use its property to override that. It's very simple, but keep in mind that it should always be unique, or you will get an error.

To avoid it, I like to use a for loops, and then keep adding '*' at the end until it becomes a new unique name.


#*️⃣ RENAME NEW VIEW
new_name =  'EF New View'
for i in range(10):
    try:
        view_floor.Name = new_name
        break
    except:
        new_name += '*'

💡You might be tempted to use 'while True' here, but if you forget to break, then you will create an infinite loop and crash your Revit. Be careful ;)

Change View CropBox

Another common thing to do with ViewPlans, is to change their CropBox to isolate certain elements in a view. To do that, we can provide a new BoundingBox to a CropBox property, and it will change it.

Here is a simple snippet where we will get a random room in the project, get its BoundingBox, then add a little bit of offset and update View's CropBox property.


#📦 GET ROOM BB
my_room = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).FirstElement()
room_bb = my_room.get_BoundingBox(view_floor)

#🔳 Add BB Offset
O = 5
room_bb_offset = BoundingBoxXYZ()
room_bb_offset.Min = XYZ(room_bb.Min.X - O, room_bb.Min.Y -O, room_bb.Min.Z)
room_bb_offset.Max = XYZ(room_bb.Max.X + O, room_bb.Max.Y +O, room_bb.Max.Z)


#⏹️ CHANGE VIEW CROP BOX
view_floor.CropBoxActive  = True
view_floor.CropBoxVisible = True
view_floor.CropBox        = room_bb_offset

💡 Make sure that you activate CropBoxActive, so your new CropBox is actually used.

Create View3D

Alright, let's move to the next type of view - View3D.

There are 2 methods and they have the same arguments:

view_type_3D_id = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewType3D)
view_3d_iso     = View3D.CreateIsometric(doc, view_type_3D_id)
view_3d_per     = View3D.CreatePerspective(doc, view_type_3D_id)

Also if you decide to change its SectionBox, you should use the SetSectionBox method instead of the CropBox property!

view_3d_iso.SetSectionBox(room_bb_offset)
Create - ViewDrafting

The next ViewType is ViewDrafting, and it's as simple as the previous examples:

view_type_draft_id = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeDrafting)
view_draft         = ViewDrafting.Create(doc, view_type_draft_id)
Create - Legend View

Now let's look at legend views in Revit. You will realize that it's a tricky one, because there is no such thing as ViewLegend. Instead, Legends use the regular View Class, which doesn't have any create methods…

But there is a workaround! We can take any legend in the project, and duplicate it, and it will create an empty Legend View like we need.

💡And also keep in mind that if there are no legends in the project, then we can't continue with the script, so we need to add this check too!

Here is a snippet to create new Legend Views:

#Get All Legends
all_views   = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).ToElements()
all_views   = [view for view in all_views if not view.IsTemplate]
all_legends = [view for view in all_views if view.ViewType == ViewType.Legend]

#✅ Check Legend in the Project
if not all_legends:
    from pyrevit.forms import alert
    alert('There has to be at least 1 legend view in the project! '
          'Please Create a legend and try again', exitscript=True)

#👉 GET RANDOM LEGEND
random_legend      = all_legends[0]

#🆕 CREATE NEW LEGEND VIEW
new_legend_view_id = random_legend.Duplicate(ViewDuplicateOption.Duplicate)
new_legend         = doc.GetElement(new_legend_view_id)

#🔎 Change Scale
new_legend.Scale   = 100
Sections

Next we have ViewSection, and it's not as straight-forward. The Create method is simple: we need a Document, a ViewFamilyTypeId and a SectionBox.

But the SectionBox is not as simple to create because we need to make it with the correct size, and also locate it in the correct place with the direct rotation.

Overall I find it quite simple to do, after I've done it a few times. But the first time it will look very confusing. So I hope I will do a good job to explain it to you, to make it much simpler than it was for me…

So, we will get a wall with PickObject so we have some element to work with. We will be able to get the line from the wall and use its middle point as the Section Origin. Then we will use the line to create the Direction Vector and get Height and Width of the wall so we can size it correctly too.

Then we will have to put it all together into a BoundingBox with the correct Transform.

To make it a bit simpler, try visualizing this:

Sections are just 3D Boxes in 3D Space. They define the area of where you cut, what do you see and how far. So it means that we need to create a box with the correct sizes and then place it into the correct place with the right rotation.

Here is a clip from one of the videos, which can hopefully help you understand the role of the Transform in this.

Now let's put it all into a code:


#👉 Pick a Wall
from Autodesk.Revit.UI.Selection import ObjectType
el_ref = uidoc.Selection.PickObject(ObjectType.Element)
wall   = doc.GetElement(el_ref)

if type(wall) == Wall:

    # Get Curve
    curve = wall.Location.Curve

    # Get Origin Point
    pt_start = curve.GetEndPoint(0)
    pt_end   = curve.GetEndPoint(1)
    pt_mid   = (pt_start + pt_end) /2 # Origin

    # Get Direction Vector
    vector   = pt_end - pt_start # XYZ (10,20,0)
    curvedir = vector.Normalize() *-1# To Flip vector -> Multiply by -1

    # Get Dimensions
    W = curve.Length
    H = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble() # In Feet
    D = 10
    D_= 5
    O = 1

    # Create Transform
    trans        = Transform.Identity
    trans.Origin = pt_mid

    trans.BasisX = curvedir
    trans.BasisY = XYZ.BasisZ
    trans.BasisZ = curvedir.CrossProduct(XYZ.BasisZ)

    # Create SectionBox
    sectionBox           = BoundingBoxXYZ()
    sectionBox.Transform = trans

    sectionBox.Min = XYZ(-W/2-O  , 0 -O     , -D_)
    sectionBox.Max = XYZ(W/2 +O  , H +O     , D)
                    # Left/Right  Up/Down   Forward/Backwards


    # Create Section
    view_type_section_id = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeSection)
    view_section = ViewSection.CreateSection(doc, view_type_section_id, sectionBox)

This code does the following:

  • Pick a Wall

  • Get Wall Curve

  • Get Middle Point of the Wall's Curve

  • Create Direction Vector

  • Get Dimensions of the Wall

  • Create Transform (Origin + Vectors)

  • Create SectionBox (Dimensions + Transform)

  • Create a Wall Section

It might look like a lot of steps, but it actually makes sense because we want our section to be in a correct place with the correct size, therefore, we need to go through these steps.

Elevations

Lastly, I will show you how to create Elevations. First of all, keep in mind that the elevation itself will have exactly the same ViewSection class. So Personally I don't use Elevations, I just default to use regular sections.

However, I know that some of you would want to create Elevations anyway. And the biggest difference here is that Elevations have an ElevationMarker.

So, we have to use the CreateElevationMarker(doc, view_type_el_id, pt, scale) method to create the marker. And then we can activate its elevations with CreateElevation (doc, view_id, index)

# Get a Room
my_room = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).FirstElement()

# Create Elevation Marker
view_type_el_id = doc.GetDefaultElementTypeId(ElementTypeGroup.ViewTypeElevation)
room_pt         = my_room.Location.Point
scale           = 50

marker = ElevationMarker.CreateElevationMarker(doc, view_type_el_id, room_pt, scale)

elev_view_0 = marker.CreateElevation(doc, active_view.Id, 0)
elev_view_1 = marker.CreateElevation(doc, active_view.Id, 1)
elev_view_2 = marker.CreateElevation(doc, active_view.Id, 2)
elev_view_3 = marker.CreateElevation(doc, active_view.Id, 3)

Keep in mind that you would also need to figure out how to change the size of the Elevations and if you need to rotate the ElevationMarker. I hope you will find how to do that without me.

Summary

Alright, we've covered how to create all types of views in the Revit API (except for Schedules and Sheets). Now we are ready to dive a bit deeper in to views.

In the next lesson you will learn how to create Sheets and how to place your views on them!

Happy Coding!

HomeWork

As I've mentioned View Class and all the subclasses (ViewPlan, ViewSection…) have a lot of methods and properties.

I want to encourage you to scroll through them and see what's available. Don't try to understand how to use every one of them. Just read their name and it will give you a good idea of what's available in these classes.

Also you might not need to check every single class. Just think of what kind of views are you going to automate and look at their properties and methods.

Here are the links:

⌨️ Happy Coding!

Discuss the lesson:

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API