Blog | Photography | Highlights | Contact | About | Atom & RSS Feeds
Reading Geotagging in Photos Using Windows Imaging Component
  • Blog
  • Reading Geotagging in Photos Using Windows Imaging Component

In this blog I’m going to explain how Geotagging metadata can be read and written using Windows Imaging Component. First lets look at all the queries. These are the common ones, there are plenty more but I have not found any immediate need to use them.
// GPS Altitude
public static readonly string GpsAltitude = "/app1/ifd/Gps/subifd:{uint=6}";
// 0 = Above sea level, 1 = Below sea level
public static readonly string GpsAltitudeRef = "/app1/ifd/Gps/subifd:{uint=5}";
// ASCII count 'N' indicates north latitude, and 'S' is south latitude
public static readonly string GpsLatitudeRef = "/app1/ifd/Gps/subifd:{uint=1}";
public static readonly string GpsLatitude = "/app1/ifd/Gps/subifd:{uint=2}";
// ASCII 'E' indicates east longitude, and 'W' is west longitude
public static readonly string GpsLongitudeRef = "/app1/ifd/Gps/subifd:{uint=3}";
public static readonly string GpsLongitude = "/app1/ifd/Gps/subifd:{uint=4}";
// Indicates the Gps measurement mode. '2' means two-dimensional measurement and '3' means three-dimensional
public static readonly string GpsMeasureMode = "/app1/ifd/Gps/subifd:{uint=10}";
// A character string recording the name of the method used for place finding.
public static readonly string GpsProcessingMethod = "/app1/ifd/Gps/subifd:{uint=27}";
// Byte sequence 2, 2, 0, 0 to indicate version 2.2
public static readonly string GpsVersionID = "/app1/ifd/Gps/subifd:{uint=0}";
// GPSTimeStamp rational64u[3]
public static readonly string GpsTimeStamp = "/app1/ifd/Gps/subifd:{uint=7}";
public static readonly string GpsDateStamp = "/app1/ifd/Gps/subifd:{uint=29}";
First lets cover the basics of retrieving the GPS data using my ReadMetadata method.
// Grab copy of BitmapMetadata
BitmapMetadata bitmapMetadata = this.ReadMetadata(inputFile);
// Grab the GpsTimeStamp
string gpsTimeStamp = bitmapMetadata.GetQuery("/app1/ifd/Gps/subifd:{uint=7}").ToString();
Simple as that, but if you run this code you’ll see the value make no sense at all. So we’ll go through each of property in turn, starting with the easy ones. GpsMeasureMode This value has two possible values the number 2 or 3. They represents the number of dimensions for the GPS coordinates. I use 3 when I get the Geotagging data from a GPS because it includes the Altitude. And 2 when I’m manually adding the data and I don’t have an altitude. GpsProcessingMethod This is a string value and there appears to be no standard for possible values. I use it to store the source, which is the make & model of the GPS tracker or Bing Maps APIs. GpsVersionID This is a string and is always set to 2200, meaning version 2.2. GpsAltitude and GpsAltitudeRef Now it starts to get complicated. The GpsAltitudeRef is either 0 for above sea level or 1 for below. The GpsAltitude is a rational representing the altitude which is positive or negative based on the GpsAltitudeRef. Here’s how you go about reading the Altitude and converting it into a double. For more details on reading and writing Rationals check out this blog.
// Grab copy of BitmapMetadata
BitmapMetadata bitmapMetadata = this.ReadMetadata(inputFile);
// Grab the GpsAltitudeRef
string altitudeRef = bitmapMetadata.GetQuery("/app1/ifd/Gps/subifd:{uint=5}").ToString();
// Grab GpsAltitude as a ulong
ulong rational = (ulong)bitmapMetadata.GetQuery("/app1/ifd/Gps/subifd:{uint=6}");
// Now shift & mask out the upper and lower parts to get the numerator and the denominator
uint numerator = (uint)(rational & 0xFFFFFFFFL);
uint denominator = (uint)((rational & 0xFFFFFFFF00000000L) >> 32);
// And finally turn it into a double
double altitude = Math.Round(Convert.ToDouble(numerator) / Convert.ToDouble(denominator), 3);
When debugging this you should some something like this.  image Clearly 4294967313464 makes no sense but once you rip out the numerator and denominator you get 17464/1000 which is 17.464 meters. With an altitudeRef of 1 it really means -17.464 meters. GpsLatitudeRef, GpsLatitude, GpsLongitudeRef and GpsLongitude Now on to the meat of the data, and also the hardest to work with. The Latitude and Longitude are stored as three rationals representing the hours, minutes and seconds. Each also has a Ref field that represents North\South or East\West for their corresponding fields.
// Grab copy of BitmapMetadata
BitmapMetadata bitmapMetadata = this.ReadMetadata(inputFile);
// Grab the GpsLatitudeRef
// 'N' indicates north latitude, and 'S' is south latitude
string latitudeRef = bitmapMetadata.GetQuery("/app1/ifd/Gps/subifd:{uint=1}").ToString();
// Grab GpsLatitude as a ulong array
ulong[] rational = (ulong[])bitmapMetadata.GetQuery("/app1/ifd/Gps/subifd:{uint=2}");
double[] latitude = new double[3];
// Read and convert each of the rationals into a double
for (int i = 0; i < 3; i++)
{
    // Now shift & mask out the upper and lower parts to get the numerator and the denominator
    uint numerator = (uint)(rational[i] & 0xFFFFFFFFL);
    uint denominator = (uint)((rational[i] & 0xFFFFFFFF00000000L) >> 32);
    latitude[i] = Math.Round(Convert.ToDouble(numerator) / Convert.ToDouble(denominator), 3);
}
The output from this should look something like this: image This represents the Latitude 37° 48.417′ N. Longitude will look much the same but with E or W for the Ref field. GpsDateStamp and GpsTimeStamp These two values store when the GPS coordinate was captured. The GPSDateStamp is a simple string with a semi-colon as a delimiter. Just like Latitude, GPSTimeStamp is stored as three rationals, representing hours, minutes & seconds. Here’s the debug output for 10th Sept 2009 at 9:46pm.  image Well that covers reading GPS data. You can find my entire library for reading & writing metadata, Fotofly, on Codeplex.

This website, all photography & other content is Copyright © Ben Vincent. Unauthorised use of images is strictly prohibited.
Last Updated: Wed, 14 Dec 2011, 16:30:58    |    Website Version v4.0.4138.41239    |    Content v7.002