using MetadataExtractor; using MetadataExtractor.Formats.Exif; using System.Collections.ObjectModel; using System.Text.Json; using static View_by_Distance.Metadata.Models.Stateless.Methods.IMetadata; namespace View_by_Distance.Metadata.Models.Stateless.Methods; internal partial class Metadata { internal static Dictionary GetKeyValuePairs(IReadOnlyList directories) { Dictionary results = []; MetadataExtractorTag metadataExtractorTag; MetadataExtractorDirectory? metadataExtractorDirectory; Dictionary metadataExtractorTags; foreach (MetadataExtractor.Directory directory in directories) { metadataExtractorTags = []; if (results.TryGetValue(directory.Name, out metadataExtractorDirectory)) continue; foreach (Tag tag in directory.Tags) { metadataExtractorTag = new(tag.Type, tag.Description, tag.HasName, tag.Name); metadataExtractorTags.Add(tag.Type, metadataExtractorTag); } metadataExtractorDirectory = new(directory.Name, directory.HasError, new(directory.Errors.ToArray()), new(metadataExtractorTags)); results.Add(directory.Name, metadataExtractorDirectory); } return results; } internal static Dictionary Deserialize(string json) { Dictionary results = []; Record? record; MetadataExtractorDirectory metadataExtractorDirectory; Dictionary? keyValuePairs = JsonSerializer.Deserialize>(json); if (keyValuePairs is null) throw new NullReferenceException(nameof(keyValuePairs)); foreach (KeyValuePair keyValuePair in keyValuePairs) { record = JsonSerializer.Deserialize(keyValuePair.Value.ToString(), RecordSourceGenerationContext.Default.Record); if (record is null) throw new NullReferenceException(nameof(record)); metadataExtractorDirectory = new(record.Name, record.HasError, new(record.Errors), new(record.Tags)); results.Add(record.Name, metadataExtractorDirectory); } return results; } internal static string? GetFaceEncoding(IReadOnlyList directories) { string? result; List results = []; const string comment = "Comment: "; foreach (MetadataExtractor.Directory directory in directories) { if (directory.Name != "PNG-tEXt") continue; foreach (Tag tag in directory.Tags) { if (tag.Name != "Textual Data" || string.IsNullOrEmpty(tag.Description)) continue; if (!tag.Description.StartsWith(comment)) continue; results.Add(tag.Description); } } result = results.Count != 0 ? results[0][comment.Length..] : null; return result; } internal static string? GetOutputResolution(IReadOnlyList directories) { string? result; List results = []; const string artist = "Artist: "; foreach (MetadataExtractor.Directory directory in directories) { if (directory.Name != "PNG-tEXt") continue; foreach (Tag tag in directory.Tags) { if (tag.Name != "Textual Data" || string.IsNullOrEmpty(tag.Description)) continue; if (!tag.Description.StartsWith(artist)) continue; results.Add(tag.Description); } } result = results.Count != 0 ? results[0][artist.Length..] : null; return result; } internal static string? GetModel(IReadOnlyList directories) { string? result; ExifDirectoryBase? exifDirectoryBase = directories.OfType().FirstOrDefault(); if (exifDirectoryBase is null) result = null; else result = exifDirectoryBase.GetString(ExifDirectoryBase.TagModel); return result; } internal static GeoLocation? GeoLocation(IReadOnlyList directories) { GeoLocation? result; GpsDirectory? gpsDirectory = directories.OfType().FirstOrDefault(); if (gpsDirectory is null) result = null; else result = gpsDirectory.GetGeoLocation(); return result; } private static bool CoordinateValidatorValidate(double latitude, double longitude) { if (latitude is < (-90) or > 90) return false; if (longitude is < (-180) or > 180) return false; return true; } private static double GetRadius(DistanceUnit distanceUnit) { return distanceUnit switch { DistanceUnit.Kilometers => 6371.0, // EarthRadiusInKilometers; DistanceUnit.Meters => 6371000.0, // EarthRadiusInMeters; DistanceUnit.NauticalMiles => 3440.0, // EarthRadiusInNauticalMiles; DistanceUnit.Miles => 3959.0, // EarthRadiusInMiles; _ => throw new NotSupportedException() }; } private static double ToRadian(double d) => d * (Math.PI / 180); private static double DiffRadian(double val1, double val2) => ToRadian(val2) - ToRadian(val1); internal static double GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) { if (!CoordinateValidatorValidate(originLatitude, originLongitude)) throw new ArgumentException("Invalid origin coordinates supplied."); if (!CoordinateValidatorValidate(destinationLatitude, destinationLongitude)) throw new ArgumentException("Invalid destination coordinates supplied."); double radius = GetRadius(distanceUnit); return Math.Round( radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt( Math.Pow(Math.Sin(DiffRadian(originLatitude, destinationLatitude) / 2.0), 2.0) + Math.Cos(ToRadian(originLatitude)) * Math.Cos(ToRadian(destinationLatitude)) * Math.Pow(Math.Sin(DiffRadian(originLongitude, destinationLongitude) / 2.0), 2.0)))), decimalPlaces); } private static double ParseValueFromDmsString(string value) { double result; if (string.IsNullOrEmpty(value)) return double.MinValue; double secondsValue; string[] degrees = value.Split('°'); if (degrees.Length != 2) return double.MinValue; if (!double.TryParse(degrees[0], out double degreesValue)) return double.MinValue; string[] minutes = degrees[1].Split('\''); if (minutes.Length != 2) return double.MinValue; if (!double.TryParse(minutes[0], out double minutesValue)) return double.MinValue; string[] seconds = minutes[1].Split('"'); if (seconds.Length != 2) secondsValue = 0; else { if (!double.TryParse(seconds[0], out secondsValue)) return double.MinValue; } result = Math.Abs(degreesValue) + (minutesValue / 60) + (secondsValue / 3600); if (degreesValue < 0) result *= -1; return result; } internal static GeoLocation? GeoLocation(ReadOnlyDictionary metadataExtractorDirectories) { GeoLocation? result; if (!metadataExtractorDirectories.TryGetValue("GPS", out MetadataExtractorDirectory? metadataExtractorDirectory)) result = null; else { MetadataExtractorTag? metadataExtractorTag; if (!metadataExtractorDirectory.Tags.TryGetValue((int)Shared.Models.Stateless.IExif.Tags.GPSLatitude, out metadataExtractorTag) || string.IsNullOrEmpty(metadataExtractorTag.Description)) result = null; else { string latitudeDMS = metadataExtractorTag.Description; double latitude = ParseValueFromDmsString(latitudeDMS); if (!metadataExtractorDirectory.Tags.TryGetValue((int)Shared.Models.Stateless.IExif.Tags.GPSLongitude, out metadataExtractorTag) || string.IsNullOrEmpty(metadataExtractorTag.Description)) result = null; else { string longitudeDMS = metadataExtractorTag.Description; double longitude = ParseValueFromDmsString(longitudeDMS); result = new(latitude, longitude); string dms = result.ToDmsString(); if ($"{latitudeDMS}, {longitudeDMS}" != dms) result = null; } } } return result; } }