Apr 8, 2024

Convert Coordinates in Revit API [Internal, Project, Survey]

Learn how to convert Coordinates in Revit API.

Coordinate Systems in Revit

Coordinate Systems in Revit is not a simple topic, especially with Revit API.

But I want to simplify it for you so you don't spend countless hours on trial and error. And of course I will provide an easy to use python code in the end!

First of all let's have an overview of Coordinate Systems in Revit.
There are 3 systems available:

  • Internal (Used as the internal system of Revit, used in Revit API)

  • Project (Based on Project Base Point)

  • Survey (Based on Survey Point)

Also keep in mind that in Revit we can set True North rotation, which will not affect all systems similar.

  • Internal - True North Rotation is not applied

  • Project - True North Rotation is not applied

  • Survey - All coordinates will be affected by True North Rotation.  

Create Tags

I highly encourage you to spend some time and create proper tags for Internal, Project and Survey coordinates so you can see the right values all the time. This will help you understand what happens with your coordinates and compare with results in your code much faster.

πŸ’‘Keep in mind that Coordinates are shown as North/East/Elevation instead of XYZ

Coordinates in Revit API

Also keep in mind, that when we work with Revit API we are working with Internal Coordinate System in feet units.

When you get any coordinates or you want to place an element in your project, we work with Internal Coordinates in XYZ class in feet units. So if you want to take a set of Project or Survey points for placing your elements you will need to convert it to Internal to place it correctly.

Now let's dive into code.

Coordinate Converter Class

I've written this PointConverter Class that will convert any units between each other. I hope you will find it useful!

πŸ’‘ Note that values for X, Y, Z are provided in meters, and it will be converted into Internal units(feet) inside of the class. Adjust to your own needs if necessary.

# -*- coding: utf-8 -*-
__title__ = 'CoordSystems'

# Imports
from Autodesk.Revit.DB import *

# ╦  ╦╔═╗╦═╗╦╔═╗╔╗ ╦  ╔═╗╔═╗
# β•šβ•—β•”β•β• β•β•£β• β•¦β•β•‘β• β•β•£β• β•©β•—β•‘  β•‘β•£ β•šβ•β•—
#  β•šβ• β•© β•©β•©β•šβ•β•©β•© β•©β•šβ•β•β•©β•β•β•šβ•β•β•šβ•β• VARIABLES
#--------------------------------------------------
uidoc     = __revit__.ActiveUIDocument
doc       = __revit__.ActiveUIDocument.Document  #type: Document

# ╔═╗╦ ╦╔╗╔╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
# β• β•£ β•‘ β•‘β•‘β•‘β•‘β•‘   β•‘ β•‘β•‘ β•‘β•‘β•‘β•‘β•šβ•β•—
# β•š  β•šβ•β•β•β•šβ•β•šβ•β• β•© β•©β•šβ•β•β•β•šβ•β•šβ•β• Functions
#--------------------------------------------------
def convert_internal_units(value, get_internal=True):
    # type: (float, bool) -> float
    """Function to convert Internal units to meters or vice versa.
    :param value:        Value to convert
    :param get_internal: True - Convert TO Internal / Flase - Convert FROM Internal
    :return:             Length in Internal units or Meters."""
    if get_internal:
        return UnitUtils.ConvertToInternalUnits(value, UnitTypeId.Meters)
    return UnitUtils.ConvertFromInternalUnits(value, UnitTypeId.Meters)


# ╔═╗╦  ╔═╗╔═╗╔═╗
# β•‘  β•‘  β• β•β•£β•šβ•β•—β•šβ•β•—
# β•šβ•β•β•©β•β•β•© β•©β•šβ•β•β•šβ•β• CLASS
#--------------------------------------------------
class PointConverter:
    pt_internal = None
    pt_survey   = None
    pt_project  = None

    def __init__(self, x, y, z, coord_sys='internal', input_units = 'm'):
        # type:( float, float, float, str, str)
        """ PointConverter - Convert coordinate into desired coordinate system.
        Args:
            x : Float in meters representing the x-coordinate.
            y : Float in meters representing the y-coordinate.
            z : Float in meters representing the z-coordinate.
            coord_sys : Coordinate System of provided coordinates.
                        Possible values: 'internal'/'project'/'survey'
            input_units : Float in meters representing the z-coordinate.
                          Possible Values: 'm' / 'ft' """

        # Get Systems Transform
        srvTrans  = self.GetSurveyTransform()
        projTrans = self.GetProjectTransform()


        # Convert to Internal Units
        if input_units == 'm':
            x = convert_internal_units(x, get_internal=True) # Convert Units to Internal
            y = convert_internal_units(y, get_internal=True) # Convert Units to Internal
            z = convert_internal_units(z, get_internal=True) # Convert Units to Internal

        # 1️⃣INTERNAL COORDINATE SYSTEM
        if coord_sys.lower() == 'internal':
            self.pt_internal = XYZ(x, y, z)
            self.pt_survey   = self.ApplyInverseTransformation(srvTrans , self.pt_internal)
            self.pt_project  = self.ApplyInverseTransformation(projTrans, self.pt_internal)

        # 2️⃣PROJECT COORDINATE SYSTEM
        elif coord_sys.lower() == 'project':
            self.pt_project    = XYZ(x, y, z)
            self.pt_internal   = self.ApplyTransformation(projTrans, self.pt_project)
            self.pt_survey     = self.ApplyInverseTransformation(srvTrans, self.pt_internal)

        # 3️⃣SURVEY COORDINATE SYSTEM
        elif coord_sys.lower() == 'survey':
            self.pt_survey     = XYZ(x, y, z)
            self.pt_internal   = self.ApplyTransformation(srvTrans, self.pt_survey)
            self.pt_project     = self.ApplyInverseTransformation(projTrans, self.pt_internal)

        else: raise Exception("Wrong argument value for 'coord_sys' in PointConverter class.")

    #β›‘ HELPING METHODS
    def GetSurveyTransform(self):
        """Gets the Active Project Locations Transform (Survey)."""
        return doc.ActiveProjectLocation.GetTotalTransform()

    def GetProjectTransform(self):
        """Get the Project Base Points Transform."""
        basePtLoc = next((l for l in FilteredElementCollector(doc) \
                         .OfClass(ProjectLocation) \
                         .WhereElementIsNotElementType() \
                         .ToElements() if l.Name in ['Project', 'Projekt']), None)
        return basePtLoc.GetTotalTransform()

    def ApplyInverseTransformation(self, t, pt):
        """Applies the inverse transformation of
        the given Transform to the given point."""
        return t.Inverse.OfPoint(pt)

    def ApplyTransformation(self, t, pt):
        """Applies the transformation of
        the given Transform to the given point."""
        return t.OfPoint(pt)


Author = 'Erik Frits'


How to use it?

The Class is very simple to use.

πŸ’‘But please pay attention to units used in your Revit Project and in the Code! 

As I mentioned, Revit API always uses Internal units for everything in feet. So pay attention when you work with a set of metric data either when you want to import or export it.

Here is a sample code on how to use this code. Also you will notice that I've added print_coord_in_m function to help me with displaying the right nubmers when I print it.

# ╔╦╗╔═╗╦╔╗╔
# ║║║╠═╣║║║║
# β•© β•©β•© β•©β•©β•β•šβ•
# EXAMPLE ON HOW TO USE:

# πŸ˜‰ Helper Function
def print_coord_in_m(pt, prefix=""):
    #type: (XYZ, str)
    """Helper Function to display Point Coordinates
    in Meters to compare to Coordinates displayed in Revit."""
    x = round(convert_internal_units(pt.X, get_internal=False), 4)
    y = round(convert_internal_units(pt.Y, get_internal=False), 4)
    z = round(convert_internal_units(pt.Z, get_internal=False), 4)
    # print(prefix, 'N/S:{}'.format(y), 'E/W:{}'.format(x), 'Elev:{}'.format(z))
    print(prefix, 'X:{}'.format(x), 'Y:{}'.format(y), 'Z:{}'.format(z))
# --------------------------------------------------------------

# 1️⃣ Define Coordinates Manually
# X = 0    # EW Coordinate
# Y = 0    # NS Coordinate
# Z = 0    # Elevation

# 1️⃣ Pick Point
picked_point = uidoc.Selection.PickPoint()
X = picked_point.X
Y = picked_point.Y
Z = picked_point.Z

# 2️⃣ Create PointConverter instance
converter = PointConverter(X, Y, Z, coord_sys='internal', input_units='ft')

# 3️⃣ Converted Points
pt_internal = converter.pt_internal
pt_project  = converter.pt_project
pt_survey   = converter.pt_survey

# πŸ‘€ Display Results
print_coord_in_m(pt_internal, prefix='Internal: ')
print_coord_in_m(pt_project,  prefix='Project: ')
print_coord_in_m(pt_survey,   prefix='Survey: ')

Join Newsletter

πŸ“© You will be added to Revit API Newsletter

Join Us!

which is already read by 7400+ people!

Ready to become Revit Hero for your office? Learn Revit API!

Join this comprehensive course that will guide you step by step on how to create your dream tools for Revit that save time.