Aligning Spot Elevations
Two things have always been of great annoyance to me with Revit:
- Inability to add variable labels above/below the displayed elevation (T.O. CONCRETE, etc).
- This is fairly easily solved with custom annotation symbols.
- Inability to left/right align the displayed elevation relative to the chosen symbol.
- This is not so easily solved since Revit does not offset a way to left or right align the displayed elevation.
I managed to find one particularly helpful post from The Building Coder that started me down a path for a solution. In the end, I decided to leverage pyRevit's hooks to auto-adjust all spot elevations matching a specific family/type name each time a view with those elements is opened. More info on how pyRevit hooks work can be found here.
The general outline of the script operation is as such:
- Verify the currently active view is a Plan View.
- Verify that spot elevations named
DB Elevation Tag
are present in the active view. - Calculate the expected width of the exact displayed elevation string using methods outlined in the blog post linked above. Note that I (and most of our designers) use very high DPI monitors--so the 5.0 adjustment to the font point size ("secret sauce") may need to be adjusted. But I have found this works very well in my testing.
- Also note that our elevation tags are prefixed with "EL. "
- Get the midpoint of the calculated width and add a 0.125" offset (since this will align well to our label, if used).
- Calculate the required vertical offset using the text height plus a small gap to visually align it above the leader line. The text height offset is only needed if the leader is above the origin (as in the displayed elevation is above the leader line), otherwise only the gap is included.
- Setup a new XYZ object with the new location of the text using the leader end position as a reference.
- Compare the current text position to the new location's X value, and only adjust if the difference is off by more than 2 decimal places.
A preview of the tool is shown below, as well as the relevant code:

view-activated.py
import sys
from pyrevit import revit, DB
from pyrevit import forms
from pyrevit import script
from pyrevit import EXEC_PARAMS
from System import IntPtr
from System.Drawing import Font, FontStyle, Graphics, StringFormat, Text
logger = script.get_logger()
output = script.get_output()
doc = revit.doc
if script.get_envvar("DB Elevation Tags Auto Updater") != 0:
if "Plan" in str(EXEC_PARAMS.event_args.CurrentActiveView):
spot_dims = None
try:
spot_dims = [x for x in DB.FilteredElementCollector(doc, doc.ActiveView.Id)\
.OfCategory(DB.BuiltInCategory.OST_SpotElevations).WhereElementIsNotElementType().ToElements()
if (str(x.Name).startswith("DB Elevation Tag") == True)]
except:
pass
if spot_dims is not None:
if len(spot_dims) > 0:
try:
with revit.Transaction("DB Tools - Autoupdating spot elevations...", clear_after_rollback=True, swallow_errors=True, log_errors=True):
base_point = DB.BasePoint.GetProjectBasePoint(doc).Position
survey_point = DB.BasePoint.GetSurveyPoint(doc).Position
view_scale_factor = float(doc.ActiveView.Scale)/12.0
x_dpi = Graphics.FromHwnd(IntPtr.Zero).DpiX
y_dpi = Graphics.FromHwnd(IntPtr.Zero).DpiY
for element in spot_dims:
elevation = float(element.Origin[2])
spot_dim_type = revit.query.get_type(element)
text_height = spot_dim_type.Parameter[DB.BuiltInParameter.TEXT_SIZE].AsDouble()*12.0
text_width = spot_dim_type.Parameter[DB.BuiltInParameter.TEXT_WIDTH_SCALE].AsDouble()
elev_base = spot_dim_type.Parameter[DB.BuiltInParameter.SPOT_ELEV_BASE].AsValueString()
if (elev_base == "Project Base Point" and base_point[2] != survey_point[2]):
elevation = elevation - base_point[2]
if elev_base == "Relative":
continue
font_dpi = float(96)
point_size = float(text_height*font_dpi)
point_size = (point_size*10.0) #multiply by 10 then divide out later for more accuracy
font = Font("Arial", point_size, FontStyle.Regular)
spot_dim_format = DB.FormatValueOptions()
spot_dim_format.SetFormatOptions(spot_dim_type.GetUnitsFormatOptions())
spot_dim_string = "EL. " + str(DB.UnitFormatUtils.Format(doc.GetUnits(),
DB.SpecTypeId.Length,
elevation,
False,
spot_dim_format))
g = Graphics.FromHwnd(IntPtr.Zero)
g.TextRenderingHint = Text.TextRenderingHint.AntiAlias
px_width = g.MeasureString(spot_dim_string, font, int(sys.maxsize), StringFormat.GenericTypographic).Width
px_height = g.MeasureString(spot_dim_string, font, int(sys.maxsize), StringFormat.GenericTypographic).Height
in_width = float((px_width/10.0) / x_dpi)*text_width #divide out the 10 from earlier
in_height = float((px_height/10.0) / y_dpi) #divide out the 10 from earlier
horizontal_offset = float((in_width/2.0)*view_scale_factor) + (2*(view_scale_factor/128.0)) + (view_scale_factor/8.0) #this creates a 0.125" offset from leader to start of text and accounts for a 1/128" Revit adds to the text width
vertical_offset = (view_scale_factor/64.0) #this adjusts for text height offset if the leader is above origin
try:
horizontal_offset = horizontal_offset if element.LeaderEndPosition[0] < element.Origin[0] else (horizontal_offset*-1.0) #adjust for direction of leader vs origin
vertical_offset = (((float(in_height)*view_scale_factor) + vertical_offset)*-1.0) if element.LeaderEndPosition[1] < element.Origin[1] else vertical_offset #adjust for direction of leader vs origin
new_location = DB.XYZ(element.LeaderEndPosition[0] + horizontal_offset, element.LeaderEndPosition[1] - vertical_offset, element.TextPosition[2])
if round(float(element.TextPosition[0]), 2) != round(float(new_location[0]), 2):
element.TextPosition = new_location
except:
continue
except:
result = forms.alert("Unknown error when updating DB Elevation Tags. Do you want to disable auto-updates?",
title="DB Tools - Elevation Tags Auto Updater",
options = ["Yes", "No"])
if result == "Yes":
script.set_envvar("DB Elevation Tags Auto Updater", 0)
Let me know if anyone has any comments or criticisms!
AK