AU went very well for me, and I think this was the one I liked most of all so far, to my own surprise. Now I am already at the next conference in Moscow, from where I continue to Tel Aviv tomorrow. December is always my monster travelling month, and I never get to prepare for Christmas or enjoy the dark and cosy celebration of Advent. But I really did have fun and enjoy AU in Las Vegas.
Sunday morning my colleague Marat Mirgaleev invited me to join him in his weekly volleyball game, which was a wonderful break from the conference presentation preparation. Marat is also a member of the ADN DevTech team and spelled Марат Миргалеев in Cyrillic. Another Autodesk colleague who also joined was Rustam Ibragimov, Рустам Ибрагимов. Here are Rustam, Marat, I and our all-time star, the volleyball herself:
Here we are now in the Autodesk Moscow office:
Fittingly enough, here is a question from Russia, by Victor Chekalin, or Виктор Чекалин in Cyrillic, on formatting a floating point number as a display string using the current project units. This issue has cropped up several times recently, and various solutions based on the same principle have been suggested, among others by Joe Offord of Enclos, who already shared insights on accessing curtain wall geometry, speeding up the interactive selection process, mirroring in a new family and changing the active view, and constructing a planar face transform.
All of the solutions to this problem I have seen revolve around stuffing in the value to format into an unused parameter picked up from some arbitrary database element and then calling the AsValueString method on it. The Parameter class provides this functionality, but unfortunately the API does not include any stand-alone access to it. Here is Victor's initial query:
Question: I need to convert a numeric value to a corresponding display string honouring the current Revit ProjectUnit setting. I cannot find how to do it in the Revit help and began search the answer in your amazing site. I found the Unit Conversion tool and thought it would fulfil my need, but I was wrong :(
Looking at the Unit Converter code, I discovered that retrieving scale factor to internal units is not easy and you used some trick to get it: you change ProjectUnit, write "1" to family parameter, read value from this parameter, change ProjectUnit back. It works but is really hard and is not an official way.
Answer: Try something like this on some otherwise unused length parameter 'p':
Dim value As String = "=2' + 4'/3" Dim t As New Transaction(doc, "Format Length") t.Start() p.SetValueString(value) value = p.AsValueString t.RollBack() Return value
In fact, Joe provided the following helper methods based on this idea implemented in VB:
- StringValueString – Converts a string to AsValueString equivalent.
- DblValueString – Converts a double to AsValueString equivalent.
- ValueStringDbl – Converts a ValueString to a double.
All three of these methods create and then roll back a temporary transaction to perform their task.
Here is the full implementation of the first of these, StringValueString:
Public Shared Function StringValueString( _ ByVal doc As Document, _ ByVal value As String) As String ' Locate the arbitrary Length parameter Dim p As Parameter _ = doc.ProjectInformation.Parameter( _ "Parameter Name") If p Is Nothing Then TaskDialog.Show( _ "Revit", _ "Missing Project Parameter: Parameter Name") Return value End If Dim tr As New Transaction(doc) tr.Start("Format Length") Try p.SetValueString(value) value = p.AsValueString Catch ex As Exception End Try tr.RollBack() Return value End Function
For the second, DblValueString, simply replace the input argument by a floating-point value 'ByVal value As Double' and the two lines to perform the actual conversion by
p.Set(value) sValueString = p.AsValueString
Finally, for the third, ValueStringDbl, the input argument 'value' is again a string and the conversion to the floating-point return value is performed by
p.SetValueString(value) dValue = p.AsDouble
Response: Thanks for the answer.
I wrote some simple extension methods for the Revit API Parameter class to get value in project units and in meters value. Now it works with Length, Volume and Area (now I don't need any more). It would take much time to add support for all unit conversions.
Here is the entire implementation of my ParameterUnitConverter class. It defines the following methods and data:
- AsProjectUnitTypeDouble – Parameter extension method to retrieve double value parameter in ProjectUnits.
- AsMetersValue – Parameter extension method to retrieve double value of parameter in meters unit, i.e. length in meters, area in square meters and volume in cubic meters.
- ConvertParameterTypeToUnitType – static method to return the corresponding UnitType for a given ParameterType.
- _map_parameter_type_to_unit_type – a dictionary mapping ParameterType enumeration values to the corresponding UnitType ones.
Here is the complete implementation of this:
public static class ParameterUnitConverter { private const double METERS_IN_FEET = 0.3048; public static double AsProjectUnitTypeDouble( this Parameter param ) { if( param.StorageType != StorageType.Double ) throw new NotSupportedException( "Parameter does not have double value" ); double imperialValue = param.AsDouble(); Document document = param.Element.Document; UnitType ut = ConvertParameterTypeToUnitType( param.Definition.ParameterType ); FormatOptions fo = document.ProjectUnit .get_FormatOptions( ut ); DisplayUnitType dut = fo.Units; // Unit Converter // http://www.asknumbers.com switch( dut ) { #region Length case DisplayUnitType.DUT_METERS: return imperialValue * METERS_IN_FEET; //feet case DisplayUnitType.DUT_CENTIMETERS: return imperialValue * METERS_IN_FEET * 100; case DisplayUnitType.DUT_DECIMAL_FEET: return imperialValue; case DisplayUnitType.DUT_DECIMAL_INCHES: return imperialValue * 12; case DisplayUnitType.DUT_FEET_FRACTIONAL_INCHES: NotSupported( dut ); break; case DisplayUnitType.DUT_FRACTIONAL_INCHES: NotSupported( dut ); break; case DisplayUnitType.DUT_METERS_CENTIMETERS: return imperialValue * METERS_IN_FEET; //feet case DisplayUnitType.DUT_MILLIMETERS: return imperialValue * METERS_IN_FEET * 1000; #endregion // Length #region Area case DisplayUnitType.DUT_SQUARE_FEET: return imperialValue; case DisplayUnitType.DUT_ACRES: return imperialValue * 1 / 43560.039; case DisplayUnitType.DUT_HECTARES: return imperialValue * 1 / 107639.104; case DisplayUnitType.DUT_SQUARE_CENTIMETERS: return imperialValue * Math.Pow( METERS_IN_FEET * 100, 2 ); case DisplayUnitType.DUT_SQUARE_INCHES: return imperialValue * Math.Pow( 12, 2 ); case DisplayUnitType.DUT_SQUARE_METERS: return imperialValue * Math.Pow( METERS_IN_FEET, 2 ); case DisplayUnitType.DUT_SQUARE_MILLIMETERS: return imperialValue * Math.Pow( METERS_IN_FEET * 1000, 2 ); #endregion // Area #region Volume case DisplayUnitType.DUT_CUBIC_FEET: return imperialValue; case DisplayUnitType.DUT_CUBIC_CENTIMETERS: return imperialValue * Math.Pow( METERS_IN_FEET * 100, 3 ); case DisplayUnitType.DUT_CUBIC_INCHES: return imperialValue * Math.Pow( 12, 3 ); case DisplayUnitType.DUT_CUBIC_METERS: return imperialValue * Math.Pow( METERS_IN_FEET, 3 ); case DisplayUnitType.DUT_CUBIC_MILLIMETERS: return imperialValue * Math.Pow( METERS_IN_FEET * 1000, 3 ); case DisplayUnitType.DUT_CUBIC_YARDS: return imperialValue * 1 / Math.Pow( 3, 3 ); case DisplayUnitType.DUT_GALLONS_US: return imperialValue * 7.5; case DisplayUnitType.DUT_LITERS: return imperialValue * 28.31684; #endregion // Volume default: NotSupported( dut ); break; } throw new NotSupportedException(); } public static double AsMetersValue( this Parameter param ) { if( param.StorageType != StorageType.Double ) throw new NotSupportedException( "Parameter does not have double value" ); double imperialValue = param.AsDouble(); UnitType ut = ConvertParameterTypeToUnitType( param.Definition.ParameterType ); switch( ut ) { case UnitType.UT_Length: return imperialValue * METERS_IN_FEET; case UnitType.UT_Area: return imperialValue * Math.Pow( METERS_IN_FEET, 2 ); case UnitType.UT_Volume: return imperialValue * Math.Pow( METERS_IN_FEET, 3 ); } throw new NotSupportedException(); } public static UnitType ConvertParameterTypeToUnitType( ParameterType parameterType ) { if( _map_parameter_type_to_unit_type.ContainsKey( parameterType ) ) { return _map_parameter_type_to_unit_type[ parameterType]; } else { // If we made it this far, there's // no entry in the dictionary. throw new ArgumentException( "Cannot convert ParameterType '" + parameterType.ToString() + "' to a UnitType." ); } } static Dictionary<ParameterType, UnitType> _map_parameter_type_to_unit_type = new Dictionary<ParameterType, UnitType>() { // This data could come from a file, // or (better yet) from Revit itself... {ParameterType.Angle, UnitType.UT_Angle}, {ParameterType.Area, UnitType.UT_Area}, {ParameterType.AreaForce, UnitType.UT_AreaForce}, {ParameterType.AreaForcePerLength, UnitType.UT_AreaForcePerLength}, //map.Add(UnitType.UT_AreaForceScale, ParameterType.???); {ParameterType.ColorTemperature, UnitType.UT_Color_Temperature}, {ParameterType.Currency, UnitType.UT_Currency}, //map.Add(UnitType.UT_DecSheetLength, ParameterType.???); {ParameterType.ElectricalApparentPower, UnitType.UT_Electrical_Apparent_Power}, {ParameterType.ElectricalCurrent, UnitType.UT_Electrical_Current}, {ParameterType.ElectricalEfficacy, UnitType.UT_Electrical_Efficacy}, {ParameterType.ElectricalFrequency, UnitType.UT_Electrical_Frequency}, {ParameterType.ElectricalIlluminance, UnitType.UT_Electrical_Illuminance}, {ParameterType.ElectricalLuminance, UnitType.UT_Electrical_Luminance}, {ParameterType.ElectricalLuminousFlux, UnitType.UT_Electrical_Luminous_Flux}, {ParameterType.ElectricalLuminousIntensity, UnitType.UT_Electrical_Luminous_Intensity}, {ParameterType.ElectricalPotential, UnitType.UT_Electrical_Potential}, {ParameterType.ElectricalPower, UnitType.UT_Electrical_Power}, {ParameterType.ElectricalPowerDensity, UnitType.UT_Electrical_Power_Density}, {ParameterType.ElectricalWattage, UnitType.UT_Electrical_Wattage}, {ParameterType.Force, UnitType.UT_Force}, {ParameterType.ForceLengthPerAngle, UnitType.UT_ForceLengthPerAngle}, {ParameterType.ForcePerLength, UnitType.UT_ForcePerLength}, //map.Add(UnitType.UT_ForceScale, ParameterType.???); {ParameterType.HVACAirflow, UnitType.UT_HVAC_Airflow}, {ParameterType.HVACAirflowDensity, UnitType.UT_HVAC_Airflow_Density}, {ParameterType.HVACAirflowDividedByCoolingLoad, UnitType.UT_HVAC_Airflow_Divided_By_Cooling_Load}, {ParameterType.HVACAirflowDividedByVolume, UnitType.UT_HVAC_Airflow_Divided_By_Volume}, {ParameterType.HVACAreaDividedByCoolingLoad, UnitType.UT_HVAC_Area_Divided_By_Cooling_Load}, {ParameterType.HVACAreaDividedByHeatingLoad, UnitType.UT_HVAC_Area_Divided_By_Heating_Load}, {ParameterType.HVACCoefficientOfHeatTransfer, UnitType.UT_HVAC_CoefficientOfHeatTransfer}, {ParameterType.HVACCoolingLoad, UnitType.UT_HVAC_Cooling_Load}, {ParameterType.HVACCoolingLoadDividedByArea, UnitType.UT_HVAC_Cooling_Load_Divided_By_Area}, {ParameterType.HVACCoolingLoadDividedByVolume, UnitType.UT_HVAC_Cooling_Load_Divided_By_Volume}, {ParameterType.HVACCrossSection, UnitType.UT_HVAC_CrossSection}, {ParameterType.HVACDensity, UnitType.UT_HVAC_Density}, {ParameterType.HVACDuctSize, UnitType.UT_HVAC_DuctSize}, {ParameterType.HVACEnergy, UnitType.UT_HVAC_Energy}, {ParameterType.HVACFactor, UnitType.UT_HVAC_Factor}, {ParameterType.HVACFriction, UnitType.UT_HVAC_Friction}, {ParameterType.HVACHeatGain, UnitType.UT_HVAC_HeatGain}, {ParameterType.HVACHeatingLoad, UnitType.UT_HVAC_Heating_Load}, {ParameterType.HVACHeatingLoadDividedByArea, UnitType.UT_HVAC_Heating_Load_Divided_By_Area}, {ParameterType.HVACHeatingLoadDividedByVolume, UnitType.UT_HVAC_Heating_Load_Divided_By_Volume}, {ParameterType.HVACPower, UnitType.UT_HVAC_Power}, {ParameterType.HVACPowerDensity, UnitType.UT_HVAC_Power_Density}, {ParameterType.HVACPressure, UnitType.UT_HVAC_Pressure}, {ParameterType.HVACRoughness, UnitType.UT_HVAC_Roughness}, {ParameterType.HVACSlope, UnitType.UT_HVAC_Slope}, {ParameterType.HVACTemperature, UnitType.UT_HVAC_Temperature}, {ParameterType.HVACVelocity, UnitType.UT_HVAC_Velocity}, {ParameterType.HVACViscosity, UnitType.UT_HVAC_Viscosity}, {ParameterType.Length, UnitType.UT_Length}, {ParameterType.LinearForce, UnitType.UT_LinearForce}, {ParameterType.LinearForceLengthPerAngle, UnitType.UT_LinearForceLengthPerAngle}, {ParameterType.LinearForcePerLength, UnitType.UT_LinearForcePerLength}, // map.Add(UnitType.UT_LinearForceScale, ParameterType.???); {ParameterType.LinearMoment, UnitType.UT_LinearMoment}, // map.Add(UnitType.UT_LinearMomentScale, ParameterType.???); {ParameterType.Moment, UnitType.UT_Moment}, ///map.Add(UnitType.UT_MomentScale, ParameterType.???); {ParameterType.Number, UnitType.UT_Number}, {ParameterType.PipeSize, UnitType.UT_PipeSize}, {ParameterType.PipingDensity, UnitType.UT_Piping_Density}, {ParameterType.PipingFlow, UnitType.UT_Piping_Flow}, {ParameterType.PipingFriction, UnitType.UT_Piping_Friction}, {ParameterType.PipingPressure, UnitType.UT_Piping_Pressure}, {ParameterType.PipingRoughness, UnitType.UT_Piping_Roughness}, {ParameterType.PipingSlope, UnitType.UT_Piping_Slope}, {ParameterType.PipingTemperature, UnitType.UT_Piping_Temperature}, {ParameterType.PipingVelocity, UnitType.UT_Piping_Velocity}, {ParameterType.PipingViscosity, UnitType.UT_Piping_Viscosity}, {ParameterType.PipingVolume, UnitType.UT_Piping_Volume}, //map.Add(UnitType.UT_SheetLength, ParameterType.???); //map.Add(UnitType.UT_SiteAngle, ParameterType.???); {ParameterType.Slope, UnitType.UT_Slope}, {ParameterType.Stress, UnitType.UT_Stress}, {ParameterType.TemperalExp, UnitType.UT_TemperalExp}, {ParameterType.UnitWeight, UnitType.UT_UnitWeight}, {ParameterType.Volume, UnitType.UT_Volume}, {ParameterType.WireSize, UnitType.UT_WireSize}, }; }
I used some functions from the Unit converter. Here is an external command sample to test it by iterating over and applying it to all floating point valued parameters on a selected element.
Hope you'll find this useful.
Many thanks to Joe and Victor for putting together and sharing these nice solutions!
I added the ParameterUnitConverter class to The Building Coder samples and defined a new external command CmdParameterUnitConverter based on Victor's code to test it. Here is version 2012.0.96.0 of The Building Coder samples including the new utility class and command.
This is the output generated by the command in the Visual Studio debug output window on selecting a wall element in the rac_basic_sample_project.rvt sample model:
Parameter name: Top Extension Distance Parameter value (imperial): 0 Parameter unit value: 0 Parameter AsValueString: 0.0 Parameter name: Length Parameter value (imperial): 45.5 Parameter unit value: 13868.4 Parameter AsValueString: 13868.4 Parameter name: Base Extension Distance Parameter value (imperial): 0 Parameter unit value: 0 Parameter AsValueString: 0.0 Parameter name: Top Offset Parameter value (imperial): 0 Parameter unit value: 0 Parameter AsValueString: 0.0 Parameter name: Volume Parameter value (imperial): 455.9586023831 Parameter unit value: 12.911309795985 Parameter AsValueString: 12.911 m³ Parameter name: Unconnected Height Parameter value (imperial): 18.0446194225722 Parameter unit value: 5500 Parameter AsValueString: 5500.0 Parameter name: Base Offset Parameter value (imperial): 0 Parameter unit value: 0 Parameter AsValueString: 0.0 Parameter name: Area Parameter value (imperial): 694.880910031848 Parameter unit value: 64.5565489799251 Parameter AsValueString: 64.557 m²
Here, the parameter value labelled 'imperial' is the internal Revit database unit, e.g. feet for length. Please note that not all internal database units are imperial. In fact, only length is measured in feet, and thus also area and volume. Other internal units are SI-based.