Thursday, April 19, 2007

How to read GPS metadata from image

Someone asks about how to get GPS (and other) metadata from Image properties. It's easy once we have Windows Imaging Component, which now the integrate part of .NET framework 3.0, so you can get meta information directly from your image. How to do it? First of all, you should get BitmapMetadata out from your source. In order to do it you should use BitmapDecoder, there are some of them, one for each image type (Jpg, Bmp, Png, Gif etc). So let's take Jpeg for now.

            using (Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
                JpegBitmapDecoder decoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.None);
                BitmapMetadata meta = (BitmapMetadata)decoder.Frames[0].Metadata;
                ulong[] latitude = meta.GetQuery("/app1/ifd/gps/subifd:{ulong=2}") as ulong[];
                ulong[] longitude = meta.GetQuery("/app1/ifd/gps/subifd:{ulong=4}") as ulong[];

Next step is to convert weird ulong array into readable coordinates. Simple calculation in static method for each array and you'r done. I will not enter the math calculation and explain them. Just use it "as-is". They are rather precise, trust me :)

static double ConvertCoordinate(ulong[] coordinates)
            int lDash = (int)(coordinates[0] - ((ulong)0x100000000));
            int lF = (int)(coordinates[1] - ((ulong)0x100000000));
            double lR = ((double)(coordinates[2] - ((ulong)0x6400000000))) / 100;
            double tRes = (lDash + (((double)lF) / 60)) + (lR / 3600);
            return (Math.Floor((double)(tRes * 1000000)) / 1000000);

We finished, you'r got your coordinates in decimal variation, e.g. 26.34433:37.332344. Have a nice day

1 comment:

Lee and Heather said...

This did not work with photos getotagged by google earth, because the divisor for seconds was not 100.

Found this code as part of flickrSync and it seems to work better.

private static double ConvertCoordinate(ulong[] coordinates)
int degrees;
int minutes;
double seconds;

degrees = (int)splitLongAndDivide(coordinates[0]);
minutes = (int)splitLongAndDivide(coordinates[1]);
seconds = splitLongAndDivide(coordinates[2]);

double coordinate = degrees + ((double)minutes / 60.0) + (seconds / 3600);

double roundedCoordinate = Math.Floor((double)(coordinate * 1000000)) / 1000000;

return roundedCoordinate;

private static double splitLongAndDivide(ulong number)
byte[] bytes = BitConverter.GetBytes(number);
int int1 = BitConverter.ToInt32(bytes, 0);
int int2 = BitConverter.ToInt32(bytes, 4);
return ((double)int1 / (double)int2);