Units

The two main classes in the Revit API for working with units are Units and FormatOptions. The Units class represents a document's default settings for formatting numbers with units as strings. It contains a FormatOptions object for each unit type as well as settings related to decimal symbol and digit grouping.

The Units class stores a FormatOptions object for every valid unit type, but not all of them can be directly modified. Some, like UT_Number and UT_SiteAngle, have fixed definitions. Others have definitions which are automatically derived from other unit types. For example, UT_SheetLength is derived from UT_Length and UT_ForceScale is derived from UT_Force.

The FormatOptions class contains settings that control how to format numbers with units as strings. It contains those settings that are typically chosen by an end-user in the Format dialog and stored in the document, such as rounding, accuracy, display units, and whether to suppress spaces or leading or trailing zeros.

The FormatOptions class is used in two different ways. A FormatOptions object in the Units class represents the default settings for the document. A FormatOptions object used elsewhere represents settings that may optionally override the default settings.

The UseDefault property controls whether a FormatOptions object represents default or custom formatting. If UseDefault is true, formatting will be according to the default settings in the Units class, and none of the other settings in the object are meaningful. If UseDefault is false, the object contains custom settings that override the default settings in the Units class. UseDefault is always false for FormatOptions objects in the Units class.

Important unit-related enumerations in the Revit API include:

The Revit API provides utility classes to facilitate working with quantities in Revit. The UnitUtils class makes it easy to convert unit data to and from Revit's internal units.

Revit has seven base quantities, each with its own internal unit. These internal units are identified in the following table.

Table 9: 7 Base Units in Revit Unit System

Base Unit Unit In Revit Unit System
Length Feet (ft) Imperial
Angle Radian Metric
Mass Kilogram (kg) Metric
Time Seconds (s) Metric
Electric Current Ampere (A) Metric
Temperature Kelvin (K) Metric
Luminous Intensity Candela (cd) Metric
Note: Since Revit stores lengths in feet and other basic quantities in metric units, a derived unit involving length will be a non-standard unit based on both the Imperial and the Metric systems. For example, since a force is measured in "mass-length per time squared", it is stored in kg-ft / s2. The following example uses the UnitUtils.ConvertFromInternalUnits() method to get the minimum yield stress for a material in kips per square inch.
Code Region: Converting from Revit's internal units
double GetYieldStressInKsi(Material material)
{
    double dMinYieldStress = 0;
    // Get the structural asset for the material
    ElementId strucAssetId = material.StructuralAssetId;
    if (strucAssetId != ElementId.InvalidElementId)
    {
        PropertySetElement pse = material.Document.GetElement(strucAssetId) as PropertySetElement;
        if (pse != null)
        {
            StructuralAsset asset = pse.GetStructuralAsset();

            // Get the min yield stress and convert to ksi
            dMinYieldStress = asset.MinimumYieldStress;
            dMinYieldStress = UnitUtils.ConvertFromInternalUnits(dMinYieldStress,
                DisplayUnitType.DUT_KIPS_PER_SQUARE_INCH);
        }
    }

    return dMinYieldStress;
}
The UnitUtils can also be used to convert a value from one unit type to another, such as square feet to square meters. In the following example, a wall's top offset value that was entered in inches is converted to feet, the expected unit for setting that value.
Code Region: Converting between units
void SetTopOffset(Wall wall, double dOffsetInches)
{
    // convert user-defined offset value to feet from inches prior to setting
    double dOffsetFeet = UnitUtils.Convert(dOffsetInches,
                                            DisplayUnitType.DUT_DECIMAL_INCHES,
                                            DisplayUnitType.DUT_DECIMAL_FEET);

    Parameter paramTopOffset = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET);
    paramTopOffset.Set(dOffsetFeet);
}

Unit formatting and parsing

Another utility class, UnitFormatUtils, can format data or parse formatted unit data.

The overloaded method Format() can be used to format a value into a string based on formatting options as the following example demonstrates. The material density is retrieved and then the value is then converted to a user-friendly value with unit using the Format() method.

Code Region: Format value to string
void DisplayDensityOfMaterial(Material material)
{
    double density = 0;
    // get structural asset of material in order to get the density
    ElementId strucAssetId = material.StructuralAssetId;
    if (strucAssetId != ElementId.InvalidElementId)
    {
        PropertySetElement pse = material.Document.GetElement(strucAssetId) as PropertySetElement;
        if (pse != null)
        {
            StructuralAsset asset = pse.GetStructuralAsset();

            density = asset.Density;
            // convert the density value to a user readable string that includes the units
            Units units = material.Document.GetUnits();
            // false for maxAccuracy means accuracy specified by the FormatOptions should be used
            // false for forEditing since this will be for display only and no formatting modifications are necessary
            string strDensity = UnitFormatUtils.Format(units, UnitType.UT_UnitWeight, density, false, false);
            string msg = string.Format("Raw Value: {0}\r\nFormatted Value: {1}", density, strDensity);
            TaskDialog.Show("Material Density", msg);
        }
    }
}
The overloaded UnitFormatUtils.TryParse() method parses a formatted string, including units, into a value if possible, using the Revit internal units of the specified unit type. The following example takes a user entered length value, assumed to be a number and length unit, and attempts to parse it into a length value. The result is compared with the input string in a TaskDialog for demonstration purposes.
Code Region: Parse string
double GetLengthInput(Document document, String userInputLength)
{
    double dParsedLength = 0;
    Units units = document.GetUnits();
    // try to parse a user entered string (i.e. 100 mm, 1'6")
    bool parsed = UnitFormatUtils.TryParse(units, UnitType.UT_Length, userInputLength, out dParsedLength);
    if (parsed == true)
    {
        string msg = string.Format("User Input: {0}\r\nParsed value: {1}", userInputLength, dParsedLength);
        TaskDialog.Show("Parsed Data", msg);
    }

    return dParsedLength;
}