## Convert Latitude/Longitude Between Decimal and Degrees/Minutes/Seconds in C#

Latitude and longitude are usually represented in one of two formats: signed decimal and degrees/minutes/seconds (DMS). Signed decimal values range from -90 to 90 for latitude and -180 to 180 for longitude. DMS values, on the other hand, are broken into four parts: degrees, minutes, and seconds–obviously–plus a compass direction.

Converting between the two formats is not terribly difficult if you keep the following formula in mind:

```Decimal Degrees = Degrees + minutes/60 + seconds/3600
```

Computers are good at math and converting things, so let’s write a converter to do the work for us! First, we need to define some types to work with. We have two different formats, so it makes sense for us to have a DecimalLocation and a DmsLocation. We can also get some re-use out of a DmsPoint type, and we’ll need an enumeration for PointType to help with displaying N/S & E/W directional indicators. I threw in some overloads for ToString to help with displaying results, too. Also note that N/S/E/W are determined based on the sign of Degrees for DMS points.

```class DecimalLocation
{
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }

public override string ToString()
{
return string.Format("{0:f5}, {1:f5}",
Latitude, Longitude);
}
}

class DmsLocation
{
public DmsPoint Latitude { get; set; }
public DmsPoint Longitude { get; set; }

public override string ToString()
{
return string.Format("{0}, {1}",
Latitude, Longitude);
}
}

class DmsPoint
{
public int Degrees { get; set; }
public int Minutes { get; set; }
public int Seconds { get; set; }
public PointType Type { get; set; }

public override string ToString()
{
return string.Format("{0} {1} {2} {3}",
Math.Abs(Degrees),
Minutes,
Seconds,
Type == PointType.Lat
? Degrees < 0 ? "S" : "N"
: Degrees < 0 ? "W" : "E");
}
}

enum PointType
{
Lat,
Lon
}
```

Now that we’ve got our data types out of the way, we can work on the conversion. Our conversion formula shows how to calculate decimal degrees from DMS, so let’s do that first. It’s really straightforward–we just plug the formula into a function and call it for both latitude and longitude.

```DecimalLocation Convert(DmsLocation dmsLocation)
{
if (dmsLocation == null)
{
return null;
}

return new DecimalLocation
{
Latitude = CalculateDecimal(dmsLocation.Latitude),
Longitude = CalculateDecimal(dmsLocation.Longitude)
};
}

decimal CalculateDecimal(DmsPoint point)
{
if (point == null)
{
return default(decimal);
}
return point.Degrees + (decimal)point.Minutes/60 + (decimal)point.Seconds/3600;
}
```

Going the other direction is a little more complicated. Ultimately, we’re just working backwards. Peel off the degrees, then calculate minutes, and seconds are left.

```DmsLocation Convert(DecimalLocation decimalLocation)
{
if (decimalLocation == null)
{
return null;
}

return new DmsLocation
{
Latitude = new DmsPoint
{
Degrees = ExtractDegrees(decimalLocation.Latitude),
Minutes = ExtractMinutes(decimalLocation.Latitude),
Seconds = ExtractSeconds(decimalLocation.Latitude),
Type = PointType.Lat
},
Longitude = new DmsPoint
{
Degrees = ExtractDegrees(decimalLocation.Longitude),
Minutes = ExtractMinutes(decimalLocation.Longitude),
Seconds = ExtractSeconds(decimalLocation.Longitude),
Type = PointType.Lon
}
};
}

int ExtractDegrees(decimal value)
{
return (int)value;
}

int ExtractMinutes(decimal value)
{
value = Math.Abs(value);
return (int)((value - ExtractDegrees(value)) * 60);
}

int ExtractSeconds(decimal value)
{
value = Math.Abs(value);
decimal minutes = (value - ExtractDegrees(value)) * 60;
return (int)Math.Round((minutes - ExtractMinutes(value)) * 60);
}
```

That’s it–we’ve got some lightweight methods to go between the two formats. Let’s whip up a quick test to verify the conversions:

```void Main()
{
DecimalLocation decimalLocation;
DmsLocation dmsLocation;

decimalLocation = new DecimalLocation
{
Latitude = 38.898611m,
Longitude = -77.037778m
};
dmsLocation = Convert(decimalLocation);
Console.WriteLine("{0} -> {1}", decimalLocation, dmsLocation);

dmsLocation = new DmsLocation
{
Latitude = new DmsPoint
{
Degrees = 38,
Minutes = 53,
Seconds = 55,
Type = PointType.Lat
},
Longitude = new DmsPoint
{
Degrees = -77,
Minutes = 2,
Seconds = 16,
Type = PointType.Lon
}
};
decimalLocation = Convert(dmsLocation);
Console.WriteLine("{0} -> {1}", dmsLocation, decimalLocation);
}

// output:
//    38.89861, -77.03778 -> 38 53 55 N, 77 2 16 W
//    38 53 55 N, 77 2 16 W -> 38.89861, -76.96222
```

The converted values are off slightly, presumably due to some loss of precision, but the values produced are in the same general vicinity. (See here, here, and here.)

Full example:

```void Main()
{
DecimalLocation decimalLocation;
DmsLocation dmsLocation;

decimalLocation = new DecimalLocation
{
Latitude = 38.898611m,
Longitude = -77.037778m
};
dmsLocation = Convert(decimalLocation);
Console.WriteLine("{0} -> {1}", decimalLocation, dmsLocation);

dmsLocation = new DmsLocation
{
Latitude = new DmsPoint
{
Degrees = 38,
Minutes = 53,
Seconds = 55,
Type = PointType.Lat
},
Longitude = new DmsPoint
{
Degrees = -77,
Minutes = 2,
Seconds = 16,
Type = PointType.Lon
}
};
decimalLocation = Convert(dmsLocation);
Console.WriteLine("{0} -> {1}", dmsLocation, decimalLocation);
}

class DecimalLocation
{
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }

public override string ToString()
{
return string.Format("{0:f5}, {1:f5}",
Latitude, Longitude);
}
}

class DmsLocation
{
public DmsPoint Latitude { get; set; }
public DmsPoint Longitude { get; set; }

public override string ToString()
{
return string.Format("{0}, {1}",
Latitude, Longitude);
}
}

class DmsPoint
{
public int Degrees { get; set; }
public int Minutes { get; set; }
public int Seconds { get; set; }
public PointType Type { get; set; }

public override string ToString()
{
return string.Format("{0} {1} {2} {3}",
Math.Abs(Degrees),
Minutes,
Seconds,
Type == PointType.Lat
? Degrees < 0 ? "S" : "N"
: Degrees < 0 ? "W" : "E");
}
}

enum PointType
{
Lat,
Lon
}

DecimalLocation Convert(DmsLocation dmsLocation)
{
if (dmsLocation == null)
{
return null;
}

return new DecimalLocation
{
Latitude = CalculateDecimal(dmsLocation.Latitude),
Longitude = CalculateDecimal(dmsLocation.Longitude)
};
}

DmsLocation Convert(DecimalLocation decimalLocation)
{
if (decimalLocation == null)
{
return null;
}

return new DmsLocation
{
Latitude = new DmsPoint
{
Degrees = ExtractDegrees(decimalLocation.Latitude),
Minutes = ExtractMinutes(decimalLocation.Latitude),
Seconds = ExtractSeconds(decimalLocation.Latitude),
Type = PointType.Lat
},
Longitude = new DmsPoint
{
Degrees = ExtractDegrees(decimalLocation.Longitude),
Minutes = ExtractMinutes(decimalLocation.Longitude),
Seconds = ExtractSeconds(decimalLocation.Longitude),
Type = PointType.Lon
}
};
}

decimal CalculateDecimal(DmsPoint point)
{
if (point == null)
{
return default(decimal);
}
return point.Degrees + (decimal)point.Minutes/60 + (decimal)point.Seconds/3600;
}

int ExtractDegrees(decimal value)
{
return (int)value;
}

int ExtractMinutes(decimal value)
{
value = Math.Abs(value);
return (int)((value - ExtractDegrees(value)) * 60);
}

int ExtractSeconds(decimal value)
{
value = Math.Abs(value);
decimal minutes = (value - ExtractDegrees(value)) * 60;
return (int)Math.Round((minutes - ExtractMinutes(value)) * 60);
}
```