0

I'm programmatically updating exif data for a bunch of scanned jpg images

I'm not having much trouble updating most exif data, but I am struggling to set gps coordinates

For example I'm trying to save the latitude coordinate as follows

PropertyItem PropertyTagGpsLatitude = srcImg.GetPropertyItem(2);
PropertyTagGpsLatitude.Value = System.Text.ASCIIEncoding.ASCII.GetBytes("42/1,5/1,33/1");
srcImg.SetPropertyItem(PropertyTagGpsLatitude);

Per the documentation it states

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.

https://msdn.microsoft.com/en-us/library/windows/desktop/ms534416(v=vs.85).aspx

I am able to set other gps related data, for example this translates over correctly

 PropertyItem PropertyTagGpsLatitudeRef = srcImg.GetPropertyItem(1);
 PropertyTagGpsLatitudeRef.Value = System.Text.ASCIIEncoding.ASCII.GetBytes("N");
 srcImg.SetPropertyItem(PropertyTagGpsLatitudeRef);

I'm not getting any exceptions, I'm just verifying the exif data using the Exif Pilot utility and can see it's not working correctly

mrb398
  • 1,277
  • 4
  • 24
  • 32
  • If you found a solution; you should post your solution as answer and accept it, not post it into the question. That way other people with similar problems will see that this question is marked as solved. – Nyerguds Apr 23 '18 at 08:34

2 Answers2

0

I've finally came up with a solution that work. I loaded up an existing image that held GPS info to inspect how the data is strucutred. Ultimatley I was able to come up with this method here

 /// <summary>
 /// Prepares a byte array that hold EXIF latitude/longitude data
 /// </summary>
 /// <param name="degrees">There are 360° of longitude (180° E ↔ 180° W) and 180° of latitude (90° N ↔ 90° S)</param>
 /// <param name="minutes">Each degree can be broken into 60 minutes</param>
 /// <param name="seconds">Each minute can be divided into 60 seconds (”). For finer accuracy, fractions of seconds given by a decimal point are used. Seconds unit multiplied by 100. For example 33.77 seconds is passed as 3377. This gives adequate GPS precision</param>
 /// <returns></returns>
 private static byte[] FloatToExifGps(int degrees, int minutes, int seconds)
 {
      var secBytes = BitConverter.GetBytes(seconds);
      var secDivisor = BitConverter.GetBytes(100);
      byte[] rv = { (byte)degrees, 0, 0, 0, 1, 0, 0, 0, (byte)minutes, 0, 0, 0, 1, 0, 0, 0, secBytes[0], secBytes[1], 0, 0, secDivisor[0], 0, 0, 0 };
      return rv;
 }

And use it like this

 PropertyItem PropertyTagGpsLongitude = srcImg.GetPropertyItem(4);
 PropertyTagGpsLongitude.Value = FloatToExifGps(79, 48, 3377);
 srcImg.SetPropertyItem(PropertyTagGpsLongitude);

enter image description here

I hope this helps someone in the future

mrb398
  • 1,277
  • 4
  • 24
  • 32
0

I came across the same problem recently and would like to share solution that worked for me. Variables like LongitudeDegreeNumerator etc have uint type and calculated in advance.

// Longitude: Build a whole property value
byte[] propertyTagGpsLongitude = new byte[24];
Buffer.BlockCopy(BitConverter.GetBytes(LongitudeDegreeNumerator), 0, propertyTagGpsLongitude, 0, 4);
Buffer.BlockCopy(BitConverter.GetBytes(LongitudeDegreeDenominator), 0, propertyTagGpsLongitude, 4, 4);
Buffer.BlockCopy(BitConverter.GetBytes(LongitudeMinuteNumerator), 0, propertyTagGpsLongitude, 8, 4);
Buffer.BlockCopy(BitConverter.GetBytes(LongitudeMinuteDenominator), 0, propertyTagGpsLongitude, 12, 4);
Buffer.BlockCopy(BitConverter.GetBytes(LongitudeSecondNumerator), 0, propertyTagGpsLongitude, 16, 4);
Buffer.BlockCopy(BitConverter.GetBytes(LongitudeSecondDenominator), 0, propertyTagGpsLongitude, 20, 4);

Maybe that would be useful for someone.

Svetlana
  • 163
  • 1
  • 15