using MetadataExtractor; using View_by_Distance.Metadata.Models.Stateless.Methods; using View_by_Distance.Shared.Models; namespace View_by_Distance.Metadata.Models.Stateless; internal abstract class GPS { 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(IMetadata.DistanceUnit distanceUnit) { return distanceUnit switch { IMetadata.DistanceUnit.Kilometers => 6371.0, // EarthRadiusInKilometers; IMetadata.DistanceUnit.Meters => 6371000.0, // EarthRadiusInMeters; IMetadata.DistanceUnit.NauticalMiles => 3440.0, // EarthRadiusInNauticalMiles; IMetadata.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, IMetadata.DistanceUnit distanceUnit = IMetadata.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(GpsDirectory[]? gpsDirectories) { GeoLocation? result = null; if (gpsDirectories is not null) { foreach (GpsDirectory gpsDirectory in gpsDirectories) { if (string.IsNullOrEmpty(gpsDirectory?.Latitude)) result = null; else { string latitudeDMS = gpsDirectory.Latitude; double latitude = ParseValueFromDmsString(latitudeDMS); if (string.IsNullOrEmpty(gpsDirectory.Longitude)) result = null; else { string longitudeDMS = gpsDirectory.Longitude; double longitude = ParseValueFromDmsString(longitudeDMS); result = new(latitude, longitude); string dms = result.ToDmsString(); if ($"{latitudeDMS}, {longitudeDMS}" != dms) result = null; } } if (result is not null) break; } } return result; } }