diff --git a/Photo.cs b/Photo.cs index e6e6ab1..258e2a9 100644 --- a/Photo.cs +++ b/Photo.cs @@ -9,6 +9,88 @@ using System.Xml.Linq; namespace SemiColinGames; +// https://exiftool.org/TagNames/GPS.html +public struct GpsInfo { + public byte[] VersionId; + public string Status; + public string Datestamp; + public Rational[] Timestamp; + public Rational[] Latitude; + public string LatitudeRef; + public Rational[] Longitude; + public string LongitudeRef; + public Rational Altitude; + public byte AltitudeRef; + + // GpsStatus? DateStamp and TimeStamp? + public static GpsInfo? ParseExif(ExifProfile exif) { + GpsInfo gps; + + IExifValue? versionId; + IExifValue? status; + IExifValue? datestamp; + IExifValue? timestamp; + IExifValue? latitude; + IExifValue? latitudeRef; + IExifValue? longitude; + IExifValue? longitudeRef; + IExifValue? altitude; + IExifValue? altitudeRef; + + if (!exif.TryGetValue(ExifTag.GPSVersionID, out versionId)) { + return null; + } + gps.VersionId = versionId.Value ?? throw new NullReferenceException(); + + if (!exif.TryGetValue(ExifTag.GPSStatus, out status)) { + return null; + } + gps.Status = status.Value ?? throw new NullReferenceException(); + + if (!exif.TryGetValue(ExifTag.GPSDateStamp, out datestamp)) { + return null; + } + gps.Datestamp = datestamp.Value ?? throw new NullReferenceException(); + + if (!exif.TryGetValue(ExifTag.GPSTimestamp, out timestamp)) { + return null; + } + gps.Timestamp = timestamp.Value ?? throw new NullReferenceException(); + + if (!exif.TryGetValue(ExifTag.GPSLatitude, out latitude)) { + return null; + } + gps.Latitude = latitude.Value ?? throw new NullReferenceException(); + + if (!exif.TryGetValue(ExifTag.GPSLatitudeRef, out latitudeRef)) { + return null; + } + gps.LatitudeRef = latitudeRef.Value ?? throw new NullReferenceException(); + + if (!exif.TryGetValue(ExifTag.GPSLongitude, out longitude)) { + return null; + } + gps.Longitude = longitude.Value ?? throw new NullReferenceException(); + + if (!exif.TryGetValue(ExifTag.GPSLongitudeRef, out longitudeRef)) { + return null; + } + gps.LongitudeRef = longitudeRef.Value ?? throw new NullReferenceException(); + + if (!exif.TryGetValue(ExifTag.GPSAltitude, out altitude)) { + return null; + } + gps.Altitude = altitude.Value; + + if (!exif.TryGetValue(ExifTag.GPSAltitudeRef, out altitudeRef)) { + return null; + } + gps.AltitudeRef = altitudeRef.Value; + + return gps; + } +} + public class Photo { public string Filename; public bool Loaded = false; @@ -24,8 +106,7 @@ public class Photo { public string IsoSpeed = ""; public int Rating = 0; public ushort Orientation = 1; - public Rational[]? GpsLatitude = null; - public Rational[]? GpsLongitude = null; + public GpsInfo? Gps = null; public Rectangle CropRectangle = Rectangle.Empty; private static long touchCounter = 0; @@ -93,7 +174,6 @@ public class Photo { string filename = System.IO.Path.Combine(directory, System.IO.Path.GetFileName(Filename)); Console.WriteLine("saving " + filename); // FIXME: add comments / captions as ImageDescription? - // FIXME: strip some Exif tags for privacy reasons? // FIXME: warn if the file already exists? using (Image image = await Image.LoadAsync(Filename)) { Util.RotateImageFromExif(image, Orientation); @@ -112,8 +192,20 @@ public class Photo { "{0:D4}:{1:D2}:{2:D2} {3:D2}:{4:D2}:{5:D2}", now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); exif.SetValue(ExifTag.DateTime, datetime); - exif.SetValue(ExifTag.GPSLatitude, GpsLatitude); - exif.SetValue(ExifTag.GPSLongitude, GpsLongitude); + + if (Gps != null) { + GpsInfo gps = (GpsInfo) Gps; + exif.SetValue(ExifTag.GPSVersionID, gps.VersionId); + exif.SetValue(ExifTag.GPSStatus, gps.Status); + exif.SetValue(ExifTag.GPSDateStamp, gps.Datestamp); + exif.SetValue(ExifTag.GPSTimestamp, gps.Timestamp); + exif.SetValue(ExifTag.GPSLatitude, gps.Latitude); + exif.SetValue(ExifTag.GPSLatitudeRef, gps.LatitudeRef); + exif.SetValue(ExifTag.GPSLongitude, gps.Longitude); + exif.SetValue(ExifTag.GPSLongitudeRef, gps.LongitudeRef); + exif.SetValue(ExifTag.GPSAltitude, gps.Altitude); + exif.SetValue(ExifTag.GPSAltitudeRef, gps.AltitudeRef); + } image.Metadata.XmpProfile = UpdateXmp(image.Metadata.XmpProfile); @@ -272,15 +364,7 @@ public class Photo { } } - IExifValue? gpsLatitude; - if (exifs.TryGetValue(ExifTag.GPSLatitude, out gpsLatitude)) { - GpsLatitude = gpsLatitude.Value; - } - - IExifValue? gpsLongitude; - if (exifs.TryGetValue(ExifTag.GPSLongitude, out gpsLongitude)) { - GpsLongitude = gpsLongitude.Value; - } + Gps = GpsInfo.ParseExif(exifs); } public string GetShortLensModel(string lensModel) { diff --git a/Program.cs b/Program.cs index d027409..be8ca54 100644 --- a/Program.cs +++ b/Program.cs @@ -386,8 +386,8 @@ public class Game : GameWindow { geometry = new UiGeometry(nwSettings.Size, STAR_FILLED.Size.X); } - private static string outputRoot = @"c:\users\colin\desktop\totte-output"; - // private static string outputRoot = @"c:\users\colin\pictures\photos"; + // private static string outputRoot = @"c:\users\colin\desktop\totte-output"; + private static string outputRoot = @"c:\users\colin\pictures\photos"; private static Texture TEXTURE_WHITE = new(new Image(1, 1, new Rgba32(255, 255, 255))); private static Texture TEXTURE_BLACK = new(new Image(1, 1, new Rgba32(0, 0, 0))); @@ -687,8 +687,8 @@ public class Game : GameWindow { // string[] files = Directory.GetFiles(@"c:\users\colin\desktop\photos-test\"); // string[] files = Directory.GetFiles(@"c:\users\colin\pictures\photos\2023\07\14\"); // string[] files = Directory.GetFiles(@"c:\users\colin\pictures\photos\2023\07\23\"); - string[] files = Directory.GetFiles(@"G:\DCIM\100EOSR6\"); - // string[] files = Directory.GetFiles(@"c:\users\colin\desktop\totte-output\2023\07\31"); + // string[] files = Directory.GetFiles(@"G:\DCIM\100EOSR6\"); + string[] files = Directory.GetFiles(@"c:\users\colin\desktop\totte-output\2023\08\29"); // string[] files = Directory.GetFiles(@"c:\users\colin\desktop\import"); // string[] files = Directory.GetFiles(@"C:\Users\colin\Pictures\photos\2018\06\23"); // string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\Germany all\104D7000"); @@ -705,17 +705,15 @@ public class Game : GameWindow { allPhotos.Sort(ComparePhotosByDate); // Fix up photos with missing GPS. - Rational[]? lastLatitude = null; - Rational[]? lastLongitude = null; + GpsInfo? lastGps = null; foreach (Photo p in allPhotos) { - if (p.GpsLatitude != null && p.GpsLongitude != null) { - lastLatitude = p.GpsLatitude; - lastLongitude = p.GpsLongitude; + if (p.Gps != null) { + lastGps = p.Gps; } - if (p.GpsLatitude == null || p.GpsLongitude == null) { + if (p.Gps == null) { + // FIXME: should we take from the photo immediately _before_, or after? Console.WriteLine("fixing GPS for " + p.Filename); - p.GpsLatitude = lastLatitude; - p.GpsLongitude = lastLongitude; + p.Gps = lastGps; } } photos = allPhotos;