Getting GPS data from an image's EXIF in C#
According to the link posted above by tomfanning, property item 0x0002 is the latitude expressed as a PropertyTagTypeRational
. The rational type is defined as...
Specifies that the value data member is an array of pairs of unsigned long integers. Each pair represents a fraction; the first integer is the numerator and the second integer is the denominator.
You are trying to parse it as a string when it's actually just a series of bytes. According to the above, there should be 3 pairs of 32-bit unsigned integers packed into that byte array, which you can retrieve using the following:
uint degreesNumerator = BitConverter.ToUInt32(propItem.Value, 0);
uint degreesDenominator = BitConverter.ToUInt32(propItem.Value, 4);
uint minutesNumerator = BitConverter.ToUInt32(propItem.Value, 8);
uint minutesDenominator = BitConverter.ToUInt32(propItem.Value, 12);
uint secondsNumerator = BitConverter.ToUInt32(propItem.Value, 16);
uint secondsDenominator = BitConverter.ToUInt32(propItem.Value, 20);
What you do with these values after you've got them is for you to work out :) Here's what the docs say:
Latitude is expressed as three rational values giving the degrees, minutes, and seconds respectively. When degrees, minutes, and seconds are expressed, the format is dd/1, mm/1, ss/1. When degrees and minutes are used and, for example, fractions of minutes are given up to two decimal places, the format is dd/1, mmmm/100, 0/1.
A simple (and fast!) way is to use my open source MetadataExtractor library:
var gps = ImageMetadataReader.ReadMetadata(path)
.OfType<GpsDirectory>()
.FirstOrDefault();
var location = gps.GetGeoLocation();
Console.WriteLine("Image at {0},{1}", location.Latitude, location.Longitude);
The library is written in pure C# and supports many image formats and decodes data specific to many camera models.
It's available via NuGet or GitHub.
I ran across this looking for a way to get the EXIF GPS data as a set of floats. I've adapted the code from Jon Grant as follows...
public static float? GetLatitude(Image targetImg)
{
try
{
//Property Item 0x0001 - PropertyTagGpsLatitudeRef
PropertyItem propItemRef = targetImg.GetPropertyItem(1);
//Property Item 0x0002 - PropertyTagGpsLatitude
PropertyItem propItemLat = targetImg.GetPropertyItem(2);
return ExifGpsToFloat(propItemRef, propItemLat);
}
catch (ArgumentException)
{
return null;
}
}
public static float? GetLongitude(Image targetImg)
{
try
{
///Property Item 0x0003 - PropertyTagGpsLongitudeRef
PropertyItem propItemRef = targetImg.GetPropertyItem(3);
//Property Item 0x0004 - PropertyTagGpsLongitude
PropertyItem propItemLong = targetImg.GetPropertyItem(4);
return ExifGpsToFloat(propItemRef, propItemLong);
}
catch (ArgumentException)
{
return null;
}
}
private static float ExifGpsToFloat(PropertyItem propItemRef, PropertyItem propItem)
{
uint degreesNumerator = BitConverter.ToUInt32(propItem.Value, 0);
uint degreesDenominator = BitConverter.ToUInt32(propItem.Value, 4);
float degrees = degreesNumerator / (float)degreesDenominator;
uint minutesNumerator = BitConverter.ToUInt32(propItem.Value, 8);
uint minutesDenominator = BitConverter.ToUInt32(propItem.Value, 12);
float minutes = minutesNumerator / (float)minutesDenominator;
uint secondsNumerator = BitConverter.ToUInt32(propItem.Value, 16);
uint secondsDenominator = BitConverter.ToUInt32(propItem.Value, 20);
float seconds = secondsNumerator / (float)secondsDenominator;
float coorditate = degrees + (minutes / 60f) + (seconds / 3600f);
string gpsRef = System.Text.Encoding.ASCII.GetString(new byte[1] { propItemRef.Value[0] } ); //N, S, E, or W
if (gpsRef == "S" || gpsRef == "W")
coorditate = 0 - coorditate;
return coorditate;
}