I modified several sets of code that I found around the web.
The code below takes a jpg image file that does not have exif tags for GPS location and adds the GPS information. As a test, the modified code is read back in and test for the presence of the GPS location tags.
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace AddGPSTagsToEXIF
{
class MetadataExample
{
private const string GPSLatitudeRefQuery = "/app1/ifd/gps/subifd:{ulong=1}";
private const string GPSLatitudeQuery = "/app1/ifd/gps/subifd:{ulong=2}";
private const string GPSLongitudeRefQuery = "/app1/ifd/gps/subifd:{ulong=3}";
private const string GPSLongitudeQuery = "/app1/ifd/gps/subifd:{ulong=4}";
private const string GPSAltitudeRefQuery = "/app1/ifd/gps/subifd:{ulong=5}";
private const string GPSAltitudeQuery = "/app1/ifd/gps/subifd:{ulong=6}";
static void Main( string[] args )
{
string originalPath = @"D:\Temp\test.jpg";
string outputPath = @"D:\Temp\testModified.jpg";
BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
uint paddingAmount = 2048;
using ( Stream originalFile = File.Open ( originalPath, FileMode.Open, FileAccess.Read ) )
{
BitmapDecoder original = BitmapDecoder.Create ( originalFile, createOptions, BitmapCacheOption.None );
JpegBitmapEncoder output = new JpegBitmapEncoder ( );
if ( original.Frames[0] != null && original.Frames[0].Metadata != null )
{
BitmapMetadata metadata = original.Frames[0].Metadata.Clone ( ) as BitmapMetadata;
metadata.SetQuery ( "/app1/ifd/PaddingSchema:Padding", paddingAmount );
metadata.SetQuery ( "/app1/ifd/exif/PaddingSchema:Padding", paddingAmount );
metadata.SetQuery ( "/xmp/PaddingSchema:Padding", paddingAmount );
double latitude = 30.0 + 15.0 / 60.0 + 22.0 / 3600.0;
double longitude = -(86.0 + 16.0 / 60.0 + 23.0 / 3600.0);
double altitude = 44;
GPSRational latitudeRational = new GPSRational(latitude);
GPSRational longitudeRational = new GPSRational(longitude);
metadata.SetQuery(GPSLatitudeQuery, latitudeRational.bytes);
metadata.SetQuery(GPSLongitudeQuery, longitudeRational.bytes);
if (latitude > 0) metadata.SetQuery(GPSLatitudeRefQuery, "N");
else metadata.SetQuery(GPSLatitudeRefQuery, "S");
if (longitude > 0) metadata.SetQuery(GPSLongitudeRefQuery, "E");
else metadata.SetQuery(GPSLongitudeRefQuery, "W");
Rational altitudeRational = new Rational((int)altitude, 1);
metadata.SetQuery(GPSAltitudeQuery, altitudeRational.bytes);
output.Frames.Add (
BitmapFrame.Create ( original.Frames[0], original.Frames[0].Thumbnail, metadata, original.Frames[0].ColorContexts ) );
}
using ( Stream outputFile = File.Open ( outputPath, FileMode.Create, FileAccess.ReadWrite ) )
{
output.Save ( outputFile );
}
}
using ( Stream savedFile = File.Open ( outputPath, FileMode.Open, FileAccess.Read ) )
{
BitmapDecoder output = BitmapDecoder.Create ( savedFile, BitmapCreateOptions.None, BitmapCacheOption.Default );
BitmapMetadata metadata = output.Frames[0].Metadata.Clone() as BitmapMetadata;
byte[] lat4 = (byte[])metadata.GetQuery(GPSLatitudeQuery);
byte[] lon4 = (byte[])metadata.GetQuery(GPSLongitudeQuery);
String latitudeRef = (String)metadata.GetQuery(GPSLatitudeRefQuery);
String longitudeRef = (String)metadata.GetQuery(GPSLongitudeRefQuery);
byte[] altitude = (byte[])metadata.GetQuery(GPSAltitudeQuery);
}
}
}
public class Rational
{
public Int32 _num;
public Int32 _denom;
public byte[] bytes;
public Rational(Int32 _Num, Int32 _Denom)
{
_num = _Num;
_denom = _Denom;
bytes = new byte[8];
BitConverter.GetBytes(_num).CopyTo(bytes, 0);
BitConverter.GetBytes(_denom).CopyTo(bytes, 4);
}
public Rational(byte[] _bytes)
{
byte[] n = new byte[4];
byte[] d = new byte[4];
Array.Copy(_bytes, 0, n, 0, 4);
Array.Copy(_bytes, 4, d, 0, 4);
_num = BitConverter.ToInt32(n, 0);
_denom = BitConverter.ToInt32(d, 0);
}
public double ToDouble()
{
return Math.Round(Convert.ToDouble(_num) / Convert.ToDouble(_denom), 5);
}
}
public class GPSRational
{
public Rational _degrees;
public Rational _minutes;
public Rational _seconds;
public byte[] bytes;
double angleInDegrees;
public GPSRational(double angleInDeg)
{
double absAngleInDeg = Math.Abs(angleInDeg);
int degreesInt = (int)(absAngleInDeg);
absAngleInDeg -= degreesInt;
int minutesInt = (int)(absAngleInDeg * 60.0);
absAngleInDeg -= minutesInt / 60.0;
int secondsInt = (int)(absAngleInDeg * 3600.0 + 0.50);
int denominator = 1;
_degrees = new Rational(degreesInt, denominator);
_minutes = new Rational(minutesInt, denominator);
_seconds = new Rational(secondsInt, denominator);
angleInDegrees = _degrees.ToDouble() + _minutes.ToDouble() / 60.0 + _seconds.ToDouble() / 3600.0;
bytes = new byte[24];
BitConverter.GetBytes(degreesInt).CopyTo(bytes, 0);
BitConverter.GetBytes(denominator).CopyTo(bytes, 4);
BitConverter.GetBytes(minutesInt).CopyTo(bytes, 8);
BitConverter.GetBytes(denominator).CopyTo(bytes, 12);
BitConverter.GetBytes(secondsInt).CopyTo(bytes, 16);
BitConverter.GetBytes(denominator).CopyTo(bytes, 20);
}
public GPSRational(byte[] _bytes)
{
byte[] degBytes = new byte[8]; byte[] minBytes = new byte[8]; byte[] secBytes = new byte[8];
Array.Copy(_bytes, 0, degBytes, 0, 8); Array.Copy(_bytes, 8, minBytes, 0, 8); Array.Copy(_bytes, 16, secBytes, 0, 8);
_degrees = new Rational(degBytes);
_minutes = new Rational(minBytes);
_seconds = new Rational(secBytes);
angleInDegrees = _degrees.ToDouble() + _minutes.ToDouble() / 60.0 + _seconds.ToDouble() / 3600.0;
bytes = new byte[24];
_bytes.CopyTo(bytes, 0);
}
}
}