AOT Builds

This commit is contained in:
2023-10-22 11:25:08 -07:00
commit f7573e95e4
56 changed files with 4270 additions and 0 deletions

View File

@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.AA.Metadata</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
</PropertyGroup>
<PropertyGroup Condition="'$(IsWindows)'=='true'">
<DefineConstants>Windows</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsOSX)'=='true'">
<DefineConstants>OSX</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsLinux)'=='true'">
<DefineConstants>Linux</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="System.Text.Json" Version="7.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\AA.Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,98 @@
using MetadataExtractor;
using System.Text.Json;
using View_by_Distance.Metadata.Models.Stateless;
using View_by_Distance.Metadata.Models.Stateless.Methods;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Metadata.Models;
public class A_Metadata
{
private readonly IAAConfiguration _AAConfiguration;
private readonly bool _PropertiesChangedForMetadata;
private readonly bool _ForceMetadataLastWriteTimeToCreationTime;
private readonly IReadOnlyDictionary<string, string[]> _FileGroups;
public A_Metadata(IAAConfiguration aAConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata)
{
_AAConfiguration = aAConfiguration;
_PropertiesChangedForMetadata = propertiesChangedForMetadata;
_ForceMetadataLastWriteTimeToCreationTime = forceMetadataLastWriteTimeToCreationTime;
string bResultsFullGroupDirectory = Shared.Models.Stateless.Methods.IResult.GetResultsFullGroupDirectory(_AAConfiguration,
nameof(A_Metadata),
string.Empty,
includeResizeGroup: false,
includeModel: false,
includePredictorModel: false);
_FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(aAConfiguration, bResultsFullGroupDirectory, [aAConfiguration.ResultSingleton]);
}
public ExifDirectory GetMetadataCollection(string file)
{
ExifDirectory? results;
string fileName = Path.GetFileName(file);
string fileExtensionLowered = Path.GetExtension(file).ToLower();
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_AAConfiguration.ResultAllInOneSubdirectoryLength, fileName);
FileInfo fileInfo = new(Path.Combine(_FileGroups[_AAConfiguration.ResultSingleton][directoryIndex], $"{fileNameWithoutExtension}{fileExtensionLowered}.json"));
if (_ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
fileInfo.Refresh();
}
if (_ForceMetadataLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
}
if (_PropertiesChangedForMetadata)
results = null;
else if (!fileInfo.Exists)
results = null;
else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old"))
throw new ArgumentException("must be a *.json file");
else
{
string json = File.ReadAllText(fileInfo.FullName);
try
{
results = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
if (results is null)
throw new Exception();
}
catch (Exception)
{
results = null;
}
}
if (results is null)
{
System.Drawing.Size? size;
try
{ size = Dimensions.GetDimensions(file); }
catch (Exception) { size = null; }
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(file);
results = Exif.Covert(file, fileInfo, size, directories);
string json = JsonSerializer.Serialize(results, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null) && _ForceMetadataLastWriteTimeToCreationTime)
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
}
}
return results;
}
public static Action<string> GetResultCollection(A_Metadata metadata, List<ExifDirectory> exifDirectories, Action tick)
{
return file =>
{
tick.Invoke();
lock (exifDirectories)
exifDirectories.Add(metadata.GetMetadataCollection(file));
};
}
}

View File

@ -0,0 +1,2 @@
[*.cs]
csharp_preserve_single_line_statements = true

View File

@ -0,0 +1,119 @@
using Microsoft.Extensions.Configuration;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Metadata.Models.Binder;
public class Configuration
{
public string? DateGroup { get; set; }
public string? FileNameDirectorySeparator { get; set; }
public bool? ForcePropertyLastWriteTimeToCreationTime { get; set; }
public string[]? IgnoreExtensions { get; set; }
public string[]? IgnoreRulesKeyWords { get; set; }
public int? MaxImagesInDirectoryForTopLevelFirstPass { get; set; }
public string? ModelName { init; get; }
public int? NumberOfJitters { init; get; }
public int? NumberOfTimesToUpsample { init; get; }
public int? Offset { init; get; }
public string? Pattern { get; set; }
public string? PersonBirthdayFormat { get; set; }
public bool? PopulatePropertyId { get; set; }
public string? PredictorModelName { get; set; }
public bool? PropertiesChangedForProperty { get; set; }
public string[]? PropertyContentCollectionFiles { get; set; }
public string? ResultAllInOne { get; set; }
public int? ResultAllInOneSubdirectoryLength { get; set; }
public string? ResultCollection { get; set; }
public string? ResultContent { get; set; }
public string? ResultSingleton { get; set; }
public string? RootDirectory { get; set; }
public string[]? ValidImageFormatExtensions { get; set; }
public override string ToString()
{
string result = JsonSerializer.Serialize(this, BinderMetadataConfigurationSourceGenerationContext.Default.Configuration);
return result;
}
private static MetadataConfiguration Get(Configuration? configuration)
{
MetadataConfiguration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.DateGroup is null) throw new NullReferenceException(nameof(configuration.DateGroup));
if (configuration.FileNameDirectorySeparator is null) throw new NullReferenceException(nameof(configuration.FileNameDirectorySeparator));
if (configuration.ForcePropertyLastWriteTimeToCreationTime is null) throw new NullReferenceException(nameof(configuration.ForcePropertyLastWriteTimeToCreationTime));
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.IgnoreRulesKeyWords is null) throw new NullReferenceException(nameof(configuration.IgnoreRulesKeyWords));
if (configuration.MaxImagesInDirectoryForTopLevelFirstPass is null) throw new NullReferenceException(nameof(configuration.MaxImagesInDirectoryForTopLevelFirstPass));
// if (configuration.ModelName is null) throw new NullReferenceException(nameof(configuration.ModelName));
// if (configuration.NumberOfJitters is null) throw new NullReferenceException(nameof(configuration.NumberOfJitters));
// if (configuration.NumberOfTimesToUpsample is null) throw new NullReferenceException(nameof(configuration.NumberOfTimesToUpsample));
if (configuration.Offset is null) throw new NullReferenceException(nameof(configuration.Offset)); ;
if (configuration.Pattern is null) throw new NullReferenceException(nameof(configuration.Pattern));
if (configuration.PersonBirthdayFormat is null) throw new NullReferenceException(nameof(configuration.PersonBirthdayFormat));
if (configuration.PopulatePropertyId is null) throw new NullReferenceException(nameof(configuration.PopulatePropertyId));
// if (configuration.PredictorModelName is null) throw new NullReferenceException(nameof(configuration.PredictorModelName));
if (configuration.PropertiesChangedForProperty is null) throw new NullReferenceException(nameof(configuration.PropertiesChangedForProperty));
// if (configuration.PropertyContentCollectionFiles is null) throw new NullReferenceException(nameof(configuration.PropertyContentCollectionFiles));
if (configuration.ResultAllInOne is null) throw new NullReferenceException(nameof(configuration.ResultAllInOne));
if (configuration.ResultAllInOneSubdirectoryLength is null) throw new NullReferenceException(nameof(configuration.ResultAllInOneSubdirectoryLength));
if (configuration.ResultCollection is null) throw new NullReferenceException(nameof(configuration.ResultCollection));
if (configuration.ResultContent is null) throw new NullReferenceException(nameof(configuration.ResultContent));
if (configuration.ResultSingleton is null) throw new NullReferenceException(nameof(configuration.ResultSingleton));
if (configuration.RootDirectory is null) throw new NullReferenceException(nameof(configuration.RootDirectory));
if (configuration.ValidImageFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidImageFormatExtensions));
result = new(configuration.DateGroup,
configuration.FileNameDirectorySeparator,
configuration.ForcePropertyLastWriteTimeToCreationTime.Value,
configuration.IgnoreExtensions,
configuration.IgnoreRulesKeyWords,
configuration.MaxImagesInDirectoryForTopLevelFirstPass.Value,
configuration.ModelName,
configuration.NumberOfJitters,
configuration.NumberOfTimesToUpsample,
configuration.Offset.Value,
configuration.Pattern,
configuration.PersonBirthdayFormat,
configuration.PopulatePropertyId.Value,
configuration.PredictorModelName,
configuration.PropertiesChangedForProperty.Value,
configuration.PropertyContentCollectionFiles ?? [],
configuration.ResultAllInOne,
configuration.ResultAllInOneSubdirectoryLength.Value,
configuration.ResultCollection,
configuration.ResultContent,
configuration.ResultSingleton,
Path.GetFullPath(configuration.RootDirectory),
configuration.ValidImageFormatExtensions);
return result;
}
public static MetadataConfiguration Get(IConfigurationRoot configurationRoot)
{
MetadataConfiguration result;
#if Linux
string environmentName = "Linux";
#elif OSX
string environmentName = "OSX";
#elif Windows
string environmentName = "Windows";
#endif
string section = string.Concat(environmentName, ":", nameof(Configuration));
IConfigurationSection configurationSection = configurationRoot.GetSection(section);
#pragma warning disable IL3050, IL2026
Configuration? configuration = configurationSection.Get<Configuration>();
#pragma warning restore IL3050, IL2026
if (configuration is null) throw new NullReferenceException(nameof(configuration));
result = Get(configuration);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Configuration))]
internal partial class BinderMetadataConfigurationSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,127 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Metadata.Models;
public class MetadataConfiguration : Shared.Models.Properties.IAAConfiguration
{
protected string _RootDirectory;
public string RootDirectory => _RootDirectory;
public string DateGroup { init; get; }
public string FileNameDirectorySeparator { init; get; }
public bool ForcePropertyLastWriteTimeToCreationTime { init; get; }
public string[] IgnoreExtensions { init; get; }
public string[] IgnoreRulesKeyWords { init; get; }
public int MaxImagesInDirectoryForTopLevelFirstPass { init; get; }
public string? ModelName { init; get; }
public int? NumberOfJitters { init; get; }
public int? NumberOfTimesToUpsample { init; get; }
public int Offset { init; get; }
public string Pattern { init; get; }
public string PersonBirthdayFormat { init; get; }
public bool PopulatePropertyId { init; get; }
public string? PredictorModelName { init; get; }
public bool PropertiesChangedForProperty { init; get; }
public string[] PropertyContentCollectionFiles { init; get; }
public string ResultAllInOne { init; get; }
public int ResultAllInOneSubdirectoryLength { init; get; }
public string ResultCollection { init; get; }
public string ResultContent { init; get; }
public string ResultSingleton { init; get; }
public string[] ValidImageFormatExtensions { init; get; }
[JsonConstructor]
public MetadataConfiguration(string dateGroup,
string fileNameDirectorySeparator,
bool forcePropertyLastWriteTimeToCreationTime,
string[] ignoreExtensions,
string[] ignoreRulesKeyWords,
int maxImagesInDirectoryForTopLevelFirstPass,
string? modelName,
int? numberOfJitters,
int? numberOfTimesToUpsample,
int offset,
string pattern,
string personBirthdayFormat,
bool populatePropertyId,
string? predictorModelName,
bool propertiesChangedForProperty,
string[] propertyContentCollectionFiles,
string resultAllInOne,
int resultAllInOneSubdirectoryLength,
string resultCollection,
string resultContent,
string resultSingleton,
string rootDirectory,
string[] validImageFormatExtensions)
{
DateGroup = dateGroup;
FileNameDirectorySeparator = fileNameDirectorySeparator;
ForcePropertyLastWriteTimeToCreationTime = forcePropertyLastWriteTimeToCreationTime;
IgnoreExtensions = ignoreExtensions;
IgnoreRulesKeyWords = ignoreRulesKeyWords;
MaxImagesInDirectoryForTopLevelFirstPass = maxImagesInDirectoryForTopLevelFirstPass;
ModelName = modelName;
NumberOfJitters = numberOfJitters;
NumberOfTimesToUpsample = numberOfTimesToUpsample;
Offset = offset;
Pattern = pattern;
PersonBirthdayFormat = personBirthdayFormat;
PredictorModelName = predictorModelName;
PopulatePropertyId = populatePropertyId;
PropertiesChangedForProperty = propertiesChangedForProperty;
PropertyContentCollectionFiles = propertyContentCollectionFiles;
ResultAllInOne = resultAllInOne;
ResultAllInOneSubdirectoryLength = resultAllInOneSubdirectoryLength;
ResultCollection = resultCollection;
ResultContent = resultContent;
ResultSingleton = resultSingleton;
_RootDirectory = rootDirectory;
ValidImageFormatExtensions = validImageFormatExtensions;
}
public override string ToString()
{
string result = JsonSerializer.Serialize(this, MetadataConfigurationSourceGenerationContext.Default.MetadataConfiguration);
return result;
}
public void ChangeRootDirectory(string rootDirectory) =>
_RootDirectory = Path.GetFullPath(rootDirectory);
public static void Verify(MetadataConfiguration propertyConfiguration, bool requireExist)
{
if (propertyConfiguration is null)
throw new NullReferenceException(nameof(propertyConfiguration));
if (propertyConfiguration.IgnoreExtensions is null || propertyConfiguration.IgnoreExtensions.Length == 0)
throw new NullReferenceException(nameof(propertyConfiguration.IgnoreExtensions));
if (propertyConfiguration.IgnoreRulesKeyWords is null || propertyConfiguration.IgnoreRulesKeyWords.Length == 0)
throw new NullReferenceException(nameof(propertyConfiguration.IgnoreRulesKeyWords));
if (propertyConfiguration.PropertyContentCollectionFiles is null)
throw new NullReferenceException(nameof(propertyConfiguration.PropertyContentCollectionFiles));
if (propertyConfiguration.ValidImageFormatExtensions is null || propertyConfiguration.ValidImageFormatExtensions.Length == 0)
throw new NullReferenceException(nameof(propertyConfiguration.ValidImageFormatExtensions));
if (propertyConfiguration is null)
throw new NullReferenceException(nameof(propertyConfiguration));
if (string.IsNullOrEmpty(propertyConfiguration.DateGroup))
throw new NullReferenceException(nameof(propertyConfiguration.DateGroup));
if (string.IsNullOrEmpty(propertyConfiguration.FileNameDirectorySeparator))
throw new NullReferenceException(nameof(propertyConfiguration.FileNameDirectorySeparator));
if (string.IsNullOrEmpty(propertyConfiguration.Pattern))
throw new NullReferenceException(nameof(propertyConfiguration.Pattern));
if (string.IsNullOrEmpty(propertyConfiguration.RootDirectory) || (requireExist && !Directory.Exists(propertyConfiguration.RootDirectory)))
throw new NullReferenceException(nameof(propertyConfiguration.RootDirectory));
if (propertyConfiguration.RootDirectory != Path.GetFullPath(propertyConfiguration.RootDirectory))
throw new Exception();
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(MetadataConfiguration))]
internal partial class MetadataConfigurationSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,173 @@
using System.Drawing;
namespace View_by_Distance.Metadata.Models.Stateless.Methods;
internal static class Dimensions
{
const string _ErrorMessage = "Could not recognize image format.";
#pragma warning disable IDE0230
private static readonly Dictionary<byte[], Func<BinaryReader, Size>> _ImageFormatDecoders = new()
{
{ new byte[] { 0x42, 0x4D }, DecodeBitmap },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif },
{ new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng },
{ new byte[] { 0xff, 0xd8 }, DecodeJfif },
{ new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP },
};
#pragma warning restore IDE0230
private static bool StartsWith(byte[] thisBytes, byte[] thatBytes)
{
for (int i = 0; i < thatBytes.Length; i += 1)
{
if (thisBytes[i] != thatBytes[i])
{
return false;
}
}
return true;
}
private static short ReadLittleEndianInt16(BinaryReader binaryReader)
{
byte[] bytes = new byte[sizeof(short)];
for (int i = 0; i < sizeof(short); i += 1)
{
bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte();
}
return BitConverter.ToInt16(bytes, 0);
}
private static int ReadLittleEndianInt32(BinaryReader binaryReader)
{
byte[] bytes = new byte[sizeof(int)];
for (int i = 0; i < sizeof(int); i += 1)
{
bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte();
}
return BitConverter.ToInt32(bytes, 0);
}
private static Size DecodeBitmap(BinaryReader binaryReader)
{
_ = binaryReader.ReadBytes(16);
int width = binaryReader.ReadInt32();
int height = binaryReader.ReadInt32();
return new Size(width, height);
}
private static Size DecodeGif(BinaryReader binaryReader)
{
int width = binaryReader.ReadInt16();
int height = binaryReader.ReadInt16();
return new Size(width, height);
}
private static Size DecodePng(BinaryReader binaryReader)
{
_ = binaryReader.ReadBytes(8);
int width = ReadLittleEndianInt32(binaryReader);
int height = ReadLittleEndianInt32(binaryReader);
return new Size(width, height);
}
private static Size DecodeJfif(BinaryReader binaryReader)
{
while (binaryReader.ReadByte() == 0xff)
{
byte marker = binaryReader.ReadByte();
short chunkLength = ReadLittleEndianInt16(binaryReader);
if (marker == 0xc0)
{
_ = binaryReader.ReadByte();
int height = ReadLittleEndianInt16(binaryReader);
int width = ReadLittleEndianInt16(binaryReader);
return new Size(width, height);
}
if (chunkLength < 0)
{
ushort uChunkLength = (ushort)chunkLength;
_ = binaryReader.ReadBytes(uChunkLength - 2);
}
else
{
_ = binaryReader.ReadBytes(chunkLength - 2);
}
}
throw new ArgumentException(_ErrorMessage);
}
private static Size DecodeWebP(BinaryReader binaryReader)
{
_ = binaryReader.ReadUInt32(); // Size
_ = binaryReader.ReadBytes(15); // WEBP, VP8 + more
_ = binaryReader.ReadBytes(3); // SYNC
int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width
int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height
return new Size(width, height);
}
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
public static Size GetDimensions(BinaryReader binaryReader)
{
int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length;
byte[] magicBytes = new byte[maxMagicBytesLength];
for (int i = 0; i < maxMagicBytesLength; i += 1)
{
magicBytes[i] = binaryReader.ReadByte();
foreach (KeyValuePair<byte[], Func<BinaryReader, Size>> kvPair in _ImageFormatDecoders)
{
if (StartsWith(magicBytes, kvPair.Key))
{
return kvPair.Value(binaryReader);
}
}
}
throw new ArgumentException(_ErrorMessage, nameof(binaryReader));
}
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
public static Size GetDimensions(string path)
{
using BinaryReader binaryReader = new(File.OpenRead(path));
try
{
return GetDimensions(binaryReader);
}
catch (ArgumentException e)
{
if (e.Message.StartsWith(_ErrorMessage))
{
throw new ArgumentException(_ErrorMessage, nameof(path), e);
}
else
{
throw;
}
}
}
}

View File

@ -0,0 +1,357 @@
using MetadataExtractor;
using MetadataExtractor.Formats.Exif;
using System.Globalization;
namespace View_by_Distance.Metadata.Models.Stateless;
internal abstract class Exif
{
private static DateTime? GetDateTime(string? value)
{
DateTime? result;
string dateTimeFormat = "yyyy:MM:dd HH:mm:ss";
string alternateFormat = "ddd MMM dd HH:mm:ss yyyy";
if (value is not null && DateTime.TryParse(value, out DateTime dateTime))
result = dateTime;
else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else
result = null;
return result;
}
private static Shared.Models.AviDirectory GetAviDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.AviDirectory result;
MetadataExtractor.Formats.Avi.AviDirectory? aviDirectory = directories.OfType<MetadataExtractor.Formats.Avi.AviDirectory>().FirstOrDefault();
if (aviDirectory is null)
result = new(null, null, null, null);
else
{
DateTime? dateTimeOriginal;
string? duration = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration);
string? height = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight);
string? width = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagWidth);
if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal, out DateTime checkDateTime))
dateTimeOriginal = checkDateTime;
else
dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal));
result = new(dateTimeOriginal, duration, height, width);
}
return result;
}
private static Shared.Models.ExifDirectoryBase GetExifDirectoryBase(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.ExifDirectoryBase result;
ExifDirectoryBase? exifDirectoryBase = directories.OfType<ExifDirectoryBase>().FirstOrDefault();
if (exifDirectoryBase is null)
result = new(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
else
{
DateTime? dateTime;
DateTime checkDateTime;
DateTime? dateTimeOriginal;
DateTime? dateTimeDigitized;
string? aperture = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagAperture);
string? applicationNotes = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagApplicationNotes);
string? artist = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagArtist);
string? bitsPerSample = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBitsPerSample);
string? bodySerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBodySerialNumber);
string? cameraOwnerName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCameraOwnerName);
string? compressedAverageBitsPerPixel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompressedAverageBitsPerPixel);
string? compression = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompression);
string? copyright = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCopyright);
string? documentName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagDocumentName);
string? exifVersion = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExifVersion);
string? exposureTime = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExposureTime);
string? fileSource = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagFileSource);
string? imageDescription = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageDescription);
string? imageHeight = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageHeight);
string? imageNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageNumber);
string? imageUniqueId = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageUniqueId);
string? imageWidth = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageWidth);
string? isoSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagIsoSpeed);
string? lensMake = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensMake);
string? lensModel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensModel);
string? lensSerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensSerialNumber);
string? make = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMake);
string? makerNote = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMakernote);
string? model = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagModel);
string? orientation = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagOrientation);
int? orientationValue = orientation is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagOrientation);
string? rating = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRating);
string? ratingPercent = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRatingPercent);
string? securityClassification = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSecurityClassification);
string? shutterSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagShutterSpeed);
string? software = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSoftware);
string? timeZone = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZone);
string? timeZoneDigitized = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneDigitized);
string? timeZoneOriginal = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneOriginal);
string? userComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagUserComment);
string? winAuthor = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinAuthor);
string? winComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinComment);
string? winKeywords = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinKeywords);
string? winSubject = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinSubject);
string? winTitle = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinTitle);
string? xResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagXResolution);
string? yResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagYResolution);
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime))
dateTime = checkDateTime;
else
dateTime = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTime));
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime))
dateTimeOriginal = checkDateTime;
else
dateTimeOriginal = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeOriginal));
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime))
dateTimeDigitized = checkDateTime;
else
dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized));
result = new(aperture,
applicationNotes,
artist,
bitsPerSample,
bodySerialNumber,
cameraOwnerName,
compressedAverageBitsPerPixel,
compression,
copyright,
dateTime,
dateTimeDigitized,
dateTimeOriginal,
documentName,
exifVersion,
exposureTime,
fileSource,
imageDescription,
imageHeight,
imageNumber,
imageUniqueId,
imageWidth,
isoSpeed,
lensMake,
lensModel,
lensSerialNumber,
make,
makerNote,
model,
orientation,
orientationValue,
rating,
ratingPercent,
securityClassification,
shutterSpeed,
software,
timeZone,
timeZoneDigitized,
timeZoneOriginal,
userComment,
winAuthor,
winComment,
winKeywords,
winSubject,
winTitle,
xResolution,
yResolution);
}
return result;
}
private static Shared.Models.FileMetadataDirectory GetFileMetadataDirectory(string file, IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.FileMetadataDirectory result;
MetadataExtractor.Formats.FileSystem.FileMetadataDirectory? fileMetadataDirectory = directories.OfType<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory>().FirstOrDefault();
if (fileMetadataDirectory is null)
result = new(null, null, null);
else
{
DateTime? fileModifiedDate;
string? fileName = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName);
string? fileSize = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize);
if (fileMetadataDirectory.TryGetDateTime(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate, out DateTime checkDateTime))
fileModifiedDate = checkDateTime;
else
fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate));
if (fileName is null || !file.EndsWith(fileName))
throw new NotSupportedException($"!{file}.EndsWith({fileName})");
result = new(fileModifiedDate, fileName, fileSize);
}
return result;
}
private static Shared.Models.GifHeaderDirectory GetGifHeaderDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.GifHeaderDirectory result;
MetadataExtractor.Formats.Gif.GifHeaderDirectory? gifHeaderDirectory = directories.OfType<MetadataExtractor.Formats.Gif.GifHeaderDirectory>().FirstOrDefault();
if (gifHeaderDirectory is null)
result = new(null, null);
else
{
string? imageHeight = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight);
string? imageWidth = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth);
result = new(imageHeight, imageWidth);
}
return result;
}
private static Shared.Models.PhotoshopDirectory GetPhotoshopDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.PhotoshopDirectory result;
MetadataExtractor.Formats.Photoshop.PhotoshopDirectory? PhotoshopDirectory = directories.OfType<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory>().FirstOrDefault();
if (PhotoshopDirectory is null)
result = new(null, null);
else
{
string? jpegQuality = PhotoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality);
string? url = PhotoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl);
result = new(jpegQuality, url);
}
return result;
}
private static Shared.Models.GpsDirectory GetGpsDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.GpsDirectory result;
GpsDirectory? gpsDirectory = directories.OfType<GpsDirectory>().FirstOrDefault();
if (gpsDirectory is null)
result = new(null, null, null, null, null, null);
else
{
DateTime? timeStamp;
string? altitude = gpsDirectory.GetDescription(GpsDirectory.TagAltitude);
string? latitude = gpsDirectory.GetDescription(GpsDirectory.TagLatitude);
string? latitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLatitudeRef);
string? longitude = gpsDirectory.GetDescription(GpsDirectory.TagLongitude);
string? longitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLongitudeRef);
if (gpsDirectory.TryGetDateTime(GpsDirectory.TagTimeStamp, out DateTime checkDateTime))
timeStamp = checkDateTime;
else
timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp));
result = new(altitude,
latitude,
latitudeRef,
longitude,
longitudeRef,
timeStamp);
}
return result;
}
private static Shared.Models.JpegDirectory GetJpegDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.JpegDirectory result;
MetadataExtractor.Formats.Jpeg.JpegDirectory? jpegDirectory = directories.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>().FirstOrDefault();
if (jpegDirectory is null)
result = new(null, null);
else
{
string? imageHeight = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight);
string? imageWidth = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth);
result = new(imageHeight, imageWidth);
}
return result;
}
private static Shared.Models.PngDirectory GetPngDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.PngDirectory result;
MetadataExtractor.Formats.Png.PngDirectory? pngDirectory = directories.OfType<MetadataExtractor.Formats.Png.PngDirectory>().FirstOrDefault();
if (pngDirectory is null)
result = new(null, null);
else
{
string? imageHeight = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight);
string? imageWidth = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth);
result = new(imageHeight, imageWidth);
}
return result;
}
private static Shared.Models.QuickTimeMovieHeaderDirectory GetQuickTimeMovieHeaderDirectoryDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.QuickTimeMovieHeaderDirectory result;
MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory? aviDirectory = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory>().FirstOrDefault();
if (aviDirectory is null)
result = new(null);
else
{
DateTime? created;
if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime))
created = checkDateTime;
else
created = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated));
result = new(created);
}
return result;
}
private static Shared.Models.QuickTimeTrackHeaderDirectory GetQuickTimeTrackHeaderDirectoryDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.QuickTimeTrackHeaderDirectory result;
MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory? aviDirectory = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory>().FirstOrDefault();
if (aviDirectory is null)
result = new(null);
else
{
DateTime? created;
if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime))
created = checkDateTime;
else
created = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated));
result = new(created);
}
return result;
}
private static Shared.Models.WebPDirectory GetWebPDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.WebPDirectory result;
MetadataExtractor.Formats.WebP.WebPDirectory? WebPDirectory = directories.OfType<MetadataExtractor.Formats.WebP.WebPDirectory>().FirstOrDefault();
if (WebPDirectory is null)
result = new(null, null);
else
{
string? imageHeight = WebPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight);
string? imageWidth = WebPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth);
result = new(imageHeight, imageWidth);
}
return result;
}
internal static Shared.Models.ExifDirectory Covert(string file, FileInfo fileInfo, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.ExifDirectory results;
Shared.Models.AviDirectory aviDirectory = GetAviDirectory(directories);
Shared.Models.GpsDirectory gpsDirectory = GetGpsDirectory(directories);
Shared.Models.PngDirectory pngDirectory = GetPngDirectory(directories);
Shared.Models.JpegDirectory jpegDirectory = GetJpegDirectory(directories);
Shared.Models.WebPDirectory webPDirectory = GetWebPDirectory(directories);
Shared.Models.ExifDirectoryBase exifDirectoryBase = GetExifDirectoryBase(directories);
Shared.Models.GifHeaderDirectory gifHeaderDirectory = GetGifHeaderDirectory(directories);
Shared.Models.PhotoshopDirectory photoshopDirectory = GetPhotoshopDirectory(directories);
Shared.Models.FileMetadataDirectory fileMetadataDirectory = GetFileMetadataDirectory(file, directories);
Shared.Models.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory = GetQuickTimeMovieHeaderDirectoryDirectory(directories);
Shared.Models.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory = GetQuickTimeTrackHeaderDirectoryDirectory(directories);
results = new(aviDirectory,
exifDirectoryBase,
file,
fileMetadataDirectory,
gifHeaderDirectory,
gpsDirectory,
size?.Height,
fileInfo.FullName,
jpegDirectory,
photoshopDirectory,
pngDirectory,
quickTimeMovieHeaderDirectory,
quickTimeTrackHeaderDirectory,
webPDirectory,
size?.Width);
return results;
}
}

View File

@ -0,0 +1,118 @@
using View_by_Distance.Metadata.Models.Stateless.Methods;
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(ReadOnlyDictionary<string, MetadataExtractorDirectory> 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;
// }
}

View File

@ -0,0 +1,24 @@
namespace View_by_Distance.Metadata.Models.Stateless.Methods;
public interface IMetadata
{
enum DistanceUnit
{
Miles,
NauticalMiles,
Kilometers,
Meters
}
Shared.Models.ExifDirectory TestStatic_Convert(string file, FileInfo fileInfo, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories) =>
Convert(file, fileInfo, size, directories);
static Shared.Models.ExifDirectory Convert(string file, FileInfo fileInfo, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories) =>
Exif.Covert(file, fileInfo, size, directories);
double? TestStatic_GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) =>
GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit);
static double? GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) =>
GPS.GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit);
}