|
|
@ -22,73 +22,66 @@ public struct GpsInfo { |
|
|
|
public Rational Altitude; |
|
|
|
public byte AltitudeRef; |
|
|
|
|
|
|
|
// GpsStatus? DateStamp and TimeStamp?
|
|
|
|
public static GpsInfo? ParseExif(ExifProfile exif) { |
|
|
|
GpsInfo gps; |
|
|
|
|
|
|
|
IExifValue<byte[]>? versionId; |
|
|
|
IExifValue<string>? status; |
|
|
|
IExifValue<string>? datestamp; |
|
|
|
IExifValue<Rational[]>? timestamp; |
|
|
|
IExifValue<Rational[]>? latitude; |
|
|
|
IExifValue<string>? latitudeRef; |
|
|
|
IExifValue<Rational[]>? longitude; |
|
|
|
IExifValue<string>? longitudeRef; |
|
|
|
IExifValue<Rational>? altitude; |
|
|
|
IExifValue<byte>? altitudeRef; |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSVersionID, out versionId)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSVersionID, out gps.VersionId)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.VersionId = versionId.Value ?? throw new NullReferenceException(); |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSStatus, out status)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSStatus, out gps.Status)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.Status = status.Value ?? throw new NullReferenceException(); |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSDateStamp, out datestamp)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSDateStamp, out gps.Datestamp)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.Datestamp = datestamp.Value ?? throw new NullReferenceException(); |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSTimestamp, out timestamp)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSTimestamp, out gps.Timestamp)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.Timestamp = timestamp.Value ?? throw new NullReferenceException(); |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSLatitude, out latitude)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSLatitude, out gps.Latitude)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.Latitude = latitude.Value ?? throw new NullReferenceException(); |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSLatitudeRef, out latitudeRef)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSLatitudeRef, out gps.LatitudeRef)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.LatitudeRef = latitudeRef.Value ?? throw new NullReferenceException(); |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSLongitude, out longitude)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSLongitude, out gps.Longitude)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.Longitude = longitude.Value ?? throw new NullReferenceException(); |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSLongitudeRef, out longitudeRef)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSLongitudeRef, out gps.LongitudeRef)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.LongitudeRef = longitudeRef.Value ?? throw new NullReferenceException(); |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSAltitude, out altitude)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSAltitude, out gps.Altitude)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.Altitude = altitude.Value; |
|
|
|
|
|
|
|
if (!exif.TryGetValue(ExifTag.GPSAltitudeRef, out altitudeRef)) { |
|
|
|
if (!Parse(exif, ExifTag.GPSAltitudeRef, out gps.AltitudeRef)) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
gps.AltitudeRef = altitudeRef.Value; |
|
|
|
|
|
|
|
return gps; |
|
|
|
} |
|
|
|
|
|
|
|
// FIXME: use this Parse() function in Photo.ParseExif() as well?
|
|
|
|
private static bool Parse<T>(ExifProfile exif, ExifTag<T> tag, out T result) { |
|
|
|
IExifValue<T>? data; |
|
|
|
if (exif.TryGetValue(tag, out data)) { |
|
|
|
if (data != null && data.Value != null) { |
|
|
|
result = data.Value; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
#pragma warning disable CS8601
|
|
|
|
result = default(T); |
|
|
|
#pragma warning restore CS8601
|
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class Photo { |
|
|
@ -284,30 +277,25 @@ public class Photo { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
IExifValue<ushort>? orientation; |
|
|
|
if (exifs.TryGetValue(ExifTag.Orientation, out orientation)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.Orientation, out var orientation)) { |
|
|
|
Orientation = orientation.Value; |
|
|
|
} |
|
|
|
|
|
|
|
IExifValue<string>? model; |
|
|
|
if (exifs.TryGetValue(ExifTag.Model, out model)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.Model, out var model)) { |
|
|
|
CameraModel = model.Value ?? ""; |
|
|
|
} |
|
|
|
|
|
|
|
IExifValue<string>? lensModel; |
|
|
|
if (exifs.TryGetValue(ExifTag.LensModel, out lensModel)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.LensModel, out var lensModel)) { |
|
|
|
LensModel = lensModel.Value ?? ""; |
|
|
|
ShortLensModel = GetShortLensModel(LensModel); |
|
|
|
} |
|
|
|
|
|
|
|
IExifValue<Rational>? focalLength; |
|
|
|
if (exifs.TryGetValue(ExifTag.FocalLength, out focalLength)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.FocalLength, out var focalLength)) { |
|
|
|
Rational r = focalLength.Value; |
|
|
|
FocalLength = $"{r.Numerator / r.Denominator}mm"; |
|
|
|
} |
|
|
|
|
|
|
|
IExifValue<Rational>? fNumber; |
|
|
|
if (exifs.TryGetValue(ExifTag.FNumber, out fNumber)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.FNumber, out var fNumber)) { |
|
|
|
Rational r = fNumber.Value; |
|
|
|
if (r.Numerator % r.Denominator == 0) { |
|
|
|
FNumber = $"f/{r.Numerator / r.Denominator}"; |
|
|
@ -317,9 +305,8 @@ public class Photo { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// FIXME: could also show ExposureBiasValue, ExposureMode, ExposureProgram?
|
|
|
|
IExifValue<Rational>? exposureTime; |
|
|
|
if (exifs.TryGetValue(ExifTag.ExposureTime, out exposureTime)) { |
|
|
|
// FIXME: could also show ExposureProgram.
|
|
|
|
if (exifs.TryGetValue(ExifTag.ExposureTime, out var exposureTime)) { |
|
|
|
Rational r = exposureTime.Value; |
|
|
|
if (r.Numerator == 1) { |
|
|
|
ExposureTime = $"1/{r.Denominator}"; |
|
|
@ -335,8 +322,7 @@ public class Photo { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
IExifValue<ushort[]>? isoSpeed; |
|
|
|
if (exifs.TryGetValue(ExifTag.ISOSpeedRatings, out isoSpeed)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.ISOSpeedRatings, out var isoSpeed)) { |
|
|
|
ushort[]? iso = isoSpeed.Value; |
|
|
|
if (iso != null) { |
|
|
|
if (iso.Length != 1) { |
|
|
@ -349,8 +335,7 @@ public class Photo { |
|
|
|
} |
|
|
|
|
|
|
|
// FIXME: I think the iPhone stores time in UTC but other cameras report it in local time.
|
|
|
|
IExifValue<string>? dateTimeOriginal; |
|
|
|
if (exifs.TryGetValue(ExifTag.DateTimeOriginal, out dateTimeOriginal)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.DateTimeOriginal, out var dateTimeOriginal)) { |
|
|
|
DateTime date; |
|
|
|
if (DateTime.TryParseExact( |
|
|
|
dateTimeOriginal.Value ?? "", |
|
|
@ -364,15 +349,13 @@ public class Photo { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
IExifValue<string>? subsecTimeOriginal; |
|
|
|
if (exifs.TryGetValue(ExifTag.SubsecTimeOriginal, out subsecTimeOriginal)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.SubsecTimeOriginal, out var subsecTimeOriginal)) { |
|
|
|
double fractionalSeconds; |
|
|
|
Double.TryParse("0." + subsecTimeOriginal.Value, out fractionalSeconds); |
|
|
|
DateTimeOriginal = DateTimeOriginal.AddSeconds(fractionalSeconds); |
|
|
|
} |
|
|
|
|
|
|
|
IExifValue<SignedRational>? exposureBiasValue; |
|
|
|
if (exifs.TryGetValue(ExifTag.ExposureBiasValue, out exposureBiasValue)) { |
|
|
|
if (exifs.TryGetValue(ExifTag.ExposureBiasValue, out var exposureBiasValue)) { |
|
|
|
SignedRational r = exposureBiasValue.Value; |
|
|
|
ExposureBiasValue = r.ToString(); |
|
|
|
if (r.Numerator >= 0) { |
|
|
@ -426,7 +409,7 @@ public class Photo { |
|
|
|
public string Description() { |
|
|
|
string date = DateTimeOriginal.ToString("yyyy-MM-dd HH:mm:ss.ff"); |
|
|
|
return String.Format( |
|
|
|
"{0,6} {1,-5} {2,-7} {3,-10} {9,-4} {7,4}x{8,-4} {4} {5,-20} {6}", |
|
|
|
"{0,6} {1,-5} {2,-7} {3,-10} EV {9,-4} {7,4}x{8,-4} {4} {5,-20} {6}", |
|
|
|
FocalLength, FNumber, ExposureTime, IsoSpeed, date, ShortLensModel, Filename, Size.X, Size.Y, ExposureBiasValue); |
|
|
|
} |
|
|
|
} |