Apr 8, 2024

Convert Coordinates Systems 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

Internal

Based on Internal Point.

True North rotation is not applied

Used in Revit APIBased on Internal Point.
True North rotation is not applied
Used in Revit API

Project

Based on Project Base Point. 

True North rotation  is not applied.

Survey

Based on Survey Point. 

All coordinates will be affected by True North Rotation.  


Summary?

When we work with Revit API we are working with Internal Coordinate System. When you get any coordinates or you want to place an element in your project, we provide coordinates in XYZ class as internal.

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.

Blog Posts that helped me

While researching the topic 2 articles from danimosite website have helped me the most. Link 1, Link 2

I would recommend you read them if you want to go into this topic deeper. I still spent hours making it work across all 3 coordinate systems back and forth, but you don't have to. I will provide my final code for you!

Coordinate Converter Class

I have written this PointConverter Class to 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.



# -*- 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. So when you get your converted point and you want to compare to Revit , don't forget to convert to match Revit's display units.

That's why print_coord_in_m function is used in my example.

# ╔╦╗╔═╗╦╔╗╔
# ║║║╠═╣║║║║
# ╩ ╩╩ ╩╩╝╚╝
# 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: ')






Want to Learn More?

🔥 Amazing Revit API Resources!

Join Revit API

Newsletter Today!

Join Us!

which is already read by 5000+ people!

Get short Revit API Lessons and Tips directly in your mail!