I've an addin that inserts some values in istances converting from units stored in db in those of the project
e.g. millimeters in db -> meters in project
All values stored in db have an units field in "short" format: e.g. "mm" for millimeters, "m2" for cube meters
Till 2022 version I used LabelUtils.GetLabelFor(Autodesk.Revit.DB.FormatOptions.GetValidUnitSymbols(parameter .DisplayUnitType).Where(Function(ByVal u As UnitSymbolType) u <> UnitSymbolType.UST_NONE).First()) to retrieve the label for every parameter in "short" format as in db
In 2022 version, if I use the new LabelUtils.GetLabelForUnit(parameter.GetUnitTypeId), what I've is the "long" format: e.g. "metri quadri" for m2, "millimeters" for mm
is there a way to retrieve the "short" format again?
Solved! Go to Solution.
Solved by david_becroft. Go to Solution.
Solved by RPTHOMAS108. Go to Solution.
Before we discuss your question, one short quick clarification: no length data at all is stored in the db in millimetres. All lengths are always stored in the hard-coded internal database length unit, which happens to be imperial feet. You may be referring to your own external db when you say 'db'. In any case, all lengths in the Revit database end up in feet.
Hi Tammik,
that is clear to me, all the Revit conversion system is clear to me
sorry for misunderstanding, when I wrote "db" I intended my db (sqlserver) with some data and values: all records have a field with units expressed in short format e.g. "mm" but now the only units label I could retrieve from Revit give me a "long" value e.g. "millimeters" so I couldn't match them and verify if I've to convert my db values or not.
I found a workaround compiling an XML, with 2019 and 2021 API, cycling on all units and writing labels in their different format:
<UM>
<CODICE>2</CODICE>
<DESCRIZIONE>DUT_MILLIMETERS</DESCRIZIONE>
<LABEL>mm</LABEL>
<LABEL_2021>mm</LABEL_2021>
<LABEL_2022>Millimetri</LABEL_2022>
</UM>
<UM>
<CODICE>66</CODICE>
<DESCRIZIONE>DUT_CUBIC_METERS_PER_HOUR</DESCRIZIONE>
<LABEL>m³/h</LABEL>
<LABEL_2021>CMH</LABEL_2021>
<LABEL_2022>Metri cubi all'ora</LABEL_2022>
</UM>
LABEL is what I had in the past in Revit and what in my db
LABEL_2021-2022 is what I've in Revit 2021 and 2022
but I should prefer a way to retrieve directly the "old" (what in LABEL) short format if possible
thanks
hi Jeremy, have you seen my answer and have any idea how to retrieve the "old" short format? thanks
There is only the symbol of the unit and the name of the unit.
For each unit there can be multiple symbols such as ft, '. Although for metric units there is usually only one symbol for each e.g. mm.
You can get the possible unit symbols for each Spec via the Format options of that Spec, as in below code:
Private Function Obj_210716a(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result
Dim UIDoc As UIDocument = commandData.Application.ActiveUIDocument
If UIDoc Is Nothing Then Return Result.Cancelled Else
Dim IntDoc As Document = UIDoc.Document
Dim R As Reference = Nothing
Try
R = UIDoc.Selection.PickObject(Selection.ObjectType.Element, "Select element")
Catch ex As Exception
Return Result.Cancelled
End Try
Dim El As Element = IntDoc.GetElement(R)
For Each P As Parameter In El.Parameters
Dim Sb As New Text.StringBuilder()
If P.StorageType <> StorageType.Double Then Continue For Else
Dim Units As Units = IntDoc.GetUnits
If UnitUtils.IsMeasurableSpec(P.Definition.GetDataType) = False Then Continue For Else
Dim Fo As FormatOptions = Units.GetFormatOptions(P.Definition.GetDataType)
Sb.Append(P.Definition.Name & ",")
If Fo.CanHaveSymbol Then
Dim SymbolsSet As List(Of ForgeTypeId) = Fo.GetValidSymbols()
For i = 0 To SymbolsSet.Count - 1
Dim Fs As ForgeTypeId = SymbolsSet(i)
If Not String.IsNullOrEmpty(Fs.TypeId) Then ' No symbol option is empty ForgeTypeId.TypeId
If i = SymbolsSet.Count - 1 Then
Sb.Append(LabelUtils.GetLabelForSymbol(Fs))
Else
Sb.Append(LabelUtils.GetLabelForSymbol(Fs) & ",")
End If
End If
Next
If Not String.IsNullOrEmpty(Fo.GetSymbolTypeId().TypeId) Then
'When no unit symbol is set the ForgeTypeId.TypeId is empty string which will cause exception for below
Sb.Append(" (Current set = " & LabelUtils.GetLabelForSymbol(Fo.GetSymbolTypeId()) & ")")
End If
Else
Sb.Append("No symbol")
End If
Debug.WriteLine(Sb.ToString)
Next
Return Result.Succeeded
End Function
Example output:
Height 1,mm
Capital Top Offset,mm
Volume,m³ (Current set = m³)
Base Offset,mm
Top Offset,mm
Base Offset,mm
Length,mm
Top Offset,mm
In the example above I get Units object from the document. You can also create a Units instance by either specifying metric or imperial to the constructor of Units. However this doesn't keep sync with how the units are changed in the project (it just provides a default set).
In theory rather than looking at specific parameters as above you could iterate the Specs in the SpectTypeId class (via reflection), create FormatOptions during each iteration and then get the symbols for each via FormatOptions.GetValidSymbols.
For metric system as previously mentioned there is usually only one symbol for each (what you note as short form).
really thanks
they work both: for a single par and also reading SpecTypeId props from reflection
there are old short formats not equals but ok
Many thanks to Richard for the solution and to Jacobo for testing and verifying. Would you like to share the code you used and the complete output, in case anyone else has use for it as well? Thank you!
Hi Jeremy,
for the first step, read the unit label for parameters, I used the code provided by Richard
Dim ump As String = ""
Dim u As New Units(UnitSystem.Metric)
Dim fo As FormatOptions = u.GetFormatOptions(par.Definition.GetDataType)
If fo.CanHaveSymbol Then
Dim SymbolsSet As List(Of ForgeTypeId) = fo.GetValidSymbols()
For i = 0 To SymbolsSet.Count - 1
Dim ss As ForgeTypeId = SymbolsSet(i)
If Not String.IsNullOrEmpty(ss.TypeId) Then
If i = SymbolsSet.Count - 1 Then
ump = LabelUtils.GetLabelForSymbol(ss)
Else
If ump <> "" Then ump &= ","
ump &= LabelUtils.GetLabelForSymbol(ss)
End If
End If
Next
If Not String.IsNullOrEmpty(fo.GetSymbolTypeId().TypeId) Then
ump = LabelUtils.GetLabelForSymbol(fo.GetSymbolTypeId())
End If
End If
after that I tried to use reflection to read SpecTypeId class, to find the matching labels comes from parameters, but for some reason I cannot retrieve all the unit labels
Dim ST As Type = GetType(SpecTypeId)
For Each PI In ST.GetProperties(Reflection.BindingFlags.Public + Reflection.BindingFlags.Static)
Try
Dim utId As ForgeTypeId = PI.GetValue(ST)
If utId <> SpecTypeId.Custom Then
Dim u As New Units(UnitSystem.Metric)
Dim fo As FormatOptions = u.GetFormatOptions(utId)
If fo.CanHaveSymbol Then
Dim SymbolsSet As List(Of ForgeTypeId) = fo.GetValidSymbols()
For i = 0 To SymbolsSet.Count - 1
Dim ss As ForgeTypeId = SymbolsSet(i)
If Not String.IsNullOrEmpty(ss.TypeId) Then
If lblUM = LabelUtils.GetLabelForSymbol(ss) Then
codUM = utId
Exit For
End If
End If
Next
If Not String.IsNullOrEmpty(fo.GetSymbolTypeId().TypeId) Then
If lblUM = LabelUtils.GetLabelForSymbol(fo.GetSymbolTypeId()) Then
codUM = utId
Exit For
End If
End If
End If
End If
Catch ex As Exception
End Try
Next
so, for this final part, I used the XML file to find the 2022 "long" label and, then, retrieve the ForgeTypeId
For Each utId As ForgeTypeId In UnitUtils.GetAllUnits
Try
Dim lut As String = LabelUtils.GetLabelForUnit(utId)
If lut = lblUM Then
codUM = utId
Exit For
End If
Catch ex As Exception
End Try
Next
I know it's not he best way but it works
again I can't understand why GetLabelForUnit gives a totally useless long label instead of a short world wide used label
The Revit SDK Units sample does some magic to display the short units:
Hi Jeremy,
as far as I understand "units" is for old Revit versions and doesn't work with 2022 because of deprecated classes
but at the end it did the same I did in old versions of my addin (and it worked since 2022): UnitUtils.GetAllSpecs() and then LabelUtils.GetLabelForSpec(specTypeId)
again, my code now works fine for a lot of units (mm, m, L/s) but it doesn't find few of them like m³/h
Dim ST As Type = GetType(SpecTypeId)
For Each PI In ST.GetProperties(Reflection.BindingFlags.Public + Reflection.BindingFlags.Static)
Try
Dim utId As ForgeTypeId = PI.GetValue(ST)
If utId <> SpecTypeId.Custom Then
Dim u As New Units(UnitSystem.Metric)
Dim fo As FormatOptions = u.GetFormatOptions(utId)
If fo.CanHaveSymbol Then
Dim SymbolsSet As List(Of ForgeTypeId) = fo.GetValidSymbols()
For i = 0 To SymbolsSet.Count - 1
Dim ss As ForgeTypeId = SymbolsSet(i)
If Not String.IsNullOrEmpty(ss.TypeId) Then
If lblUM = LabelUtils.GetLabelForSymbol(ss) Then
codUM = fo.GetUnitTypeId() 'FIND IT!!!
Exit For
End If
End If
Next
End If
End If
Catch ex As Exception
End Try
Next
All the SDK samples are updated for the new version, every release, so Units has been updated for Revit 2022.
Yes, it is pretty frustrating and the documentation is not very helpful.
I found:
I thought I could retrieve the 'm^3/h' string by using
Units units_metric = new Units( UnitSystem.Metric );
IList<ForgeTypeId> units = UnitUtils.GetAllUnits();
foreach( ForgeTypeId fti in units )
{
UnitFormatUtils.Format( units_metric, fti, 1.0, false );
}
However, it throws an exception saying that the unit:cubicMetersPerHour unit is not measurable.
I am sure there is a simple solution, but no easy to find...
Some test code of mine is in The Building Coder samples Util.ListForgeTypeIds method.
I also asked the devteam for you.
Good morning,
The code posted above misses "m³/h" because it examines only the default unit for each spec (i.e., the result of Units.GetFormatOptions(spec).GetUnitTypeId().
Mr. Tammik, you get an exception from UnitFormatUtils.Format(...) because you pass it a unit identifier where it expects a spec identifier. Try iterating over UnitUtils.GetAllMeasurableSpecs() instead of UnitUtils.GetAllUnits().
I see you're working in VB so please forgive me for posting my reply in C#! Here's a macro to see the text of the first symbol for each unit that has one:
foreach (ForgeTypeId unit in UnitUtils.GetAllUnits())
{
foreach (ForgeTypeId symbol in FormatOptions.GetValidSymbols(unit))
{
if (!symbol.Empty())
{
TaskDialog.Show(unit.TypeId, LabelUtils.GetLabelForSymbol(symbol));
break;
}
}
}
I don't think that macro is exactly what you needed but I hope it helps illustrate the API. I wanted to point out that instead of using reflection to iterate the properties of class SpecTypeId you can more simply call UnitUtils.GetAllMeasurableSpecs() to iterate all parameter data types quantified by units of measurement. Incidentally, SpecUtils.GetAllSpecs() lets you iterate all parameter data types, including the ones that aren't quantified by units of measurement, such as Text or Yes/No. If you just want to know about units and their symbols, you can iterate over UnitUtils.GetAllUnits() and then use FormatOptions.GetValidSymbols(unit). The LabelUtils class methods let you exchange the ForgeTypeId identifier of a unit, symbol, or spec for the friendly name of that item as displayed in the user interface, e.g. LabelUtils.GetLabelForUnit(UnitTypeId.Millimeters) -> "Millimeters" or LabelUtils.GetLabelForSymbol(SymbolTypeId.Mm) -> "mm".
Hi David,
I used UnitUtils.GetAllMeasurableSpecs instead of use reflection and it works but gives me the same number of units labels (obviously).
For Each utId As ForgeTypeId In UnitUtils.GetAllMeasurableSpecs()
Dim u As New Units(UnitSystem.Metric)
Dim fo As FormatOptions = u.GetFormatOptions(utId)
If fo.CanHaveSymbol Then
Dim SymbolsSet As List(Of ForgeTypeId) = fo.GetValidSymbols()
For i = 0 To SymbolsSet.Count - 1
Dim ss As ForgeTypeId = SymbolsSet(i)
If Not String.IsNullOrEmpty(ss.TypeId) Then
TaskDialog.Show(utId.TypeId, LabelUtils.GetLabelForSymbol(ss))
End If
Next
End If
Next
I already used UnitUtils.GetAllUnits (no problems with C) but as I said before it works but doesn't retrieve some units label (again m³/h)
at the end there are many methods to retrieve units labels but seems that all of them miss to retrive some labels
thanks
I was wrong, just put the same "break;" of your code ... obviously it retrieves, as you said, the first label
but without it now I've all the units labels, also m³/h
thanks
For Each unit As ForgeTypeId In UnitUtils.GetAllUnits()
For Each symbol As ForgeTypeId In FormatOptions.GetValidSymbols(unit)
If Not symbol.Empty() Then
If lblUM = LabelUtils.GetLabelForSymbol(symbol) Then
codUM = unit 'FOUND!!!
Exit For
End If
End If
Next
If Not codUM Is Nothing Then Exit For
Next
only as a reference in case it may serve others
the above code, that read units labels from parameters , reading the definition doesn't reflect units setup in the project
to read family parameters units labels as set in the project this the code
For Each symbol As ForgeTypeId In FormatOptions.GetValidSymbols(par.GetUnitTypeId)
If Not symbol.Empty() Then
ump = LabelUtils.GetLabelForSymbol(symbol) 'FOUND
Exit For
End If
Next
Can't find what you're looking for? Ask the community or share your knowledge.