Friday, March 15, 2013

Unit of measure conversions


Recently I have faced a simple problem of conversion between differents unit of measures Systems. In this case I found very useful to build a class that manages many of these passages. Every thing need to be oriented in the right way, I mean, a classes collection that can help our job. A tool box where to look for when necessary.

The class that I'm going to illustrate in this post is very simple to improve, and is very simple as well to use. In many years of programming experience, I find incredible surprising when I discover how clever solutions are always very easy. In this way, the object programming is a good logical instrument to use in all their aspects.

First of all I defined two Enum collections: one defines a unit of measure groups and another one the unit of measures adopted or to be used on our software. Obviously, the last collections can be increased.

  
public enum EnumUMType 
    { time, weight, lenght, temperature, volume, speed, 
      voltage, pressure, hardness, energy }
public enum EnumUnitOfMeasure 
    { K, C, F, mm, cm, m, km, ft, mi, gr, kg, lb, sec, min, hour, 
      m_s, km_h, mp_h, V, mV, J, Pa, MPa, psi, kpsi, kgfm2, ti2 }



The next step was to define a class that defines a unit of measure and its relations with the equivalent international systems um.

 
public class UMInfo
{
  public bool Standard { get; set; } //Is international standard UM
  public EnumUMType Type { get; set; }
  public EnumUnitOfMeasure UM { get; set;}
  public string Description { get; set;}
  public double Coefficient { get; set; } //coeficient to multiply in order to convert into International Standard UM equivalent 
  public double Constant { get; set; } //constant to add for the conversion

  public UMInfo(bool bStandard, EnumUMType oType, EnumUnitOfMeasure oUM, string sDescription, double fCoefficient, double fConstant = 0)
  {
    Standard = bStandard;
    Type = oType;
    UM = oUM;
    Description = sDescription;
    Coefficient = fCoefficient;
    Constant = fConstant;
  }
}


Every um can be transposed to an equivalent one, just multipling or dividing it to a coeficient and maybe summing it with a constant. I summarized all conversions to this simple formula:
Formula: [Standard UM] = [value] * [Coefficient] + [Constant]

The "Standard" property indicates if the unit of measure is adopted from the international standard. In this case the "Coeficient" property is always 1.

The core class is obviously a collection of "UMInfo" that exposes some methods to convert between one of them, towards an international one or towards another equivalent one.


 
public class UMConversion
{
    public List UMInfos = new List(); //List of all units

    public UMConversion()
    {
      Inizialize();
    }

    private void Inizialize()
    {
      //Space
      UMInfos.Add(new UMInfo(true, EnumUMType.lenght, EnumUnitOfMeasure.m, "meter", 1));
      UMInfos.Add(new UMInfo(false, EnumUMType.lenght, EnumUnitOfMeasure.cm, "millimeter", 0.001));
      UMInfos.Add(new UMInfo(false, EnumUMType.lenght, EnumUnitOfMeasure.mm, "centimeter", 0.01));
      UMInfos.Add(new UMInfo(false, EnumUMType.lenght, EnumUnitOfMeasure.mi, "mile", 1609.347));
      UMInfos.Add(new UMInfo(false, EnumUMType.lenght, EnumUnitOfMeasure.ft, "feet", 0.3048)); 

      //Weight
      UMInfos.Add(new UMInfo(true, EnumUMType.weight, EnumUnitOfMeasure.kg, "kg", 1));
      UMInfos.Add(new UMInfo(false, EnumUMType.weight, EnumUnitOfMeasure.gr, "gr", 0.001));
      UMInfos.Add(new UMInfo(false, EnumUMType.weight, EnumUnitOfMeasure.lb, "pound", 0.45359));

      //Temperature
      UMInfos.Add(new UMInfo(true, EnumUMType.temperature, EnumUnitOfMeasure.K, "Kelvin", 1));
      UMInfos.Add(new UMInfo(false, EnumUMType.temperature, EnumUnitOfMeasure.C, "Celsius", 1, 273.15)); 
      UMInfos.Add(new UMInfo(false, EnumUMType.temperature, EnumUnitOfMeasure.F, "Fahrenheit", 0.5555, 255.3722)); 

    }

    public object Convert(double value, EnumUnitOfMeasure um)
    {
      UMInfo umsource = UMInfos.FirstOrDefault( i => i.UM ==  um );
      if (umsource == null) return null;
      return value * umsource.Coefficient + umsource.Constant;
    }

    public object Convert(double value, UMInfo um)
    {
      return Convert(value, um.UM);
    }

    public object Convert(double value, EnumUnitOfMeasure umFrom, EnumUnitOfMeasure umTo)
    {
      UMInfo umsource = UMInfos.FirstOrDefault(i => i.UM == umFrom);
      UMInfo umtarget = UMInfos.FirstOrDefault(i => i.UM == umTo);

      if ((umsource == null) || (umtarget == null)) return null;
      if (umsource.Type != umtarget.Type) return null; 

      double std1 = value * umsource.Coefficient + umsource.Constant;
      return (std1 - umtarget.Constant) / umtarget.Coefficient;
    }

    public object Convert(double value, UMInfo umFrom, UMInfo umTo)
    {
      return Convert(value, umFrom.UM, umTo.UM);
    }

    public UMInfo GetStandard(EnumUnitOfMeasure um)
    {
      UMInfo umsource = UMInfos.FirstOrDefault(i => i.UM == um);
      return UMInfos.FirstOrDefault(i => i.Type == umsource.Type && i.Standard == true);
    }
}

On this example it is possible to decide the um type and filter the um assigned to it.


The code below shows how simple is the class usage, just one method for all conversions. Obviously this class it can be  improved but this was not the aim of this post.
 
UMInfo from = oUM.UMInfos.FirstOrDefault(i => i.Description == cmbFrom.Text);
UMInfo to = oUM.UMInfos.FirstOrDefault(i => i.Description == cmbTo.Text);

double val = double.Parse(txtValue.Text);
object res = oUM.Convert(val, from, to);

lblResult.Text = string.Format("{0:0.####}", res);


download source code

No comments :

Post a Comment