Compare commits
2 Commits
35c09f0b3e
...
075a803f4f
Author | SHA1 | Date | |
---|---|---|---|
075a803f4f | |||
1c1b2877cf |
@ -2,10 +2,10 @@ namespace Svrnty.GeoManagement.Abstractions.Abstractions;
|
|||||||
|
|
||||||
public interface IAddress
|
public interface IAddress
|
||||||
{
|
{
|
||||||
public string Line1 { get; }
|
public string Line1 { get; set; }
|
||||||
public string? Line2 { get; }
|
public string? Line2 { get; set; }
|
||||||
public string City { get; }
|
public string City { get; set; }
|
||||||
public string Subdivision { get; }
|
public string Subdivision { get; set; }
|
||||||
public string PostalCode { get; }
|
public string PostalCode { get; set; }
|
||||||
public string Country { get; }
|
public string Country { get; set; }
|
||||||
}
|
}
|
@ -2,60 +2,17 @@ using Svrnty.GeoManagement.Abstractions.Abstractions;
|
|||||||
|
|
||||||
namespace Svrnty.GeoManagement.Abstractions.Models;
|
namespace Svrnty.GeoManagement.Abstractions.Models;
|
||||||
|
|
||||||
public record GeoPoint(double Latitude, double Longitude);
|
public record GeoPoint(decimal Latitude, decimal Longitude);
|
||||||
|
|
||||||
public record Address(
|
public record Address(bool Normalized = false) : IAddress
|
||||||
string Line1,
|
|
||||||
string? Line2,
|
|
||||||
string City,
|
|
||||||
string Subdivision,
|
|
||||||
string PostalCode,
|
|
||||||
string Country,
|
|
||||||
GeoPoint? Location,
|
|
||||||
string? Note,
|
|
||||||
bool IsNormalized = false
|
|
||||||
) : IAddress
|
|
||||||
{
|
{
|
||||||
public string GetFormattedAddress(
|
public required string Line1 { get; set; }
|
||||||
FormattedAddressType formatType = FormattedAddressType.Full
|
public string? Line2 { get; set; }
|
||||||
)
|
public required string City { get; set; }
|
||||||
{
|
public required string Subdivision { get; set; }
|
||||||
return formatType switch
|
public required string PostalCode { get; set; }
|
||||||
{
|
public required string Country { get; set; }
|
||||||
FormattedAddressType.Full => FormatFullOneLine(),
|
public GeoPoint? Location { get; set; }
|
||||||
FormattedAddressType.Compact => FormatCompactOneLine(),
|
public string? Note { get; set; }
|
||||||
FormattedAddressType.MultiLine => FormatMultiLine(),
|
public bool IsNormalized() => Normalized;
|
||||||
_ => FormatFullOneLine()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private string FormatFullOneLine()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(Line2))
|
|
||||||
{
|
|
||||||
return $"{Line1}, {City}, {Subdivision} {PostalCode}, {Country}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{Line2}, {Line1}, {City}, {Subdivision} {PostalCode}, {Country}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string FormatCompactOneLine()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(Line2))
|
|
||||||
{
|
|
||||||
return $"{Line1}, {City}, {Subdivision}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{Line2}, {Line1}, {City}, {Subdivision}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string FormatMultiLine()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(Line2))
|
|
||||||
{
|
|
||||||
return $"{Line1}\n{City}, {Subdivision} {PostalCode}\n{Country}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{Line2}, {Line1}\n{City}, {Subdivision} {PostalCode}\n{Country}";
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
using Svrnty.GeoManagement.Abstractions.Abstractions;
|
||||||
|
using Svrnty.GeoManagement.Abstractions.Models;
|
||||||
|
|
||||||
|
namespace Svrnty.GeoManagement.Abstractions;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static void To(this IAddress address, IAddress toAddress)
|
||||||
|
{
|
||||||
|
toAddress.Line1 = address.Line1;
|
||||||
|
toAddress.Line2 = address.Line2;
|
||||||
|
toAddress.City = address.City;
|
||||||
|
toAddress.PostalCode = address.PostalCode;
|
||||||
|
toAddress.Country = address.Country;
|
||||||
|
toAddress.Subdivision = address.Subdivision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void From(this IAddress address, IAddress fromAddress)
|
||||||
|
{
|
||||||
|
address.Line1 = fromAddress.Line1;
|
||||||
|
address.Line2 = fromAddress.Line2;
|
||||||
|
address.City = fromAddress.City;
|
||||||
|
address.PostalCode = fromAddress.PostalCode;
|
||||||
|
address.Country = fromAddress.Country;
|
||||||
|
address.Subdivision = fromAddress.Subdivision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetFormattedAddress(this IAddress address, FormattedAddressType formatType = FormattedAddressType.Full)
|
||||||
|
{
|
||||||
|
return formatType switch
|
||||||
|
{
|
||||||
|
FormattedAddressType.Full => FormatFullOneLine(address),
|
||||||
|
FormattedAddressType.Compact => FormatCompactOneLine(address),
|
||||||
|
FormattedAddressType.MultiLine => FormatMultiLine(address),
|
||||||
|
_ => FormatFullOneLine(address)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatFullOneLine(IAddress address)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(address.Line2))
|
||||||
|
{
|
||||||
|
return $"{address.Line1}, {address.City}, {address.Subdivision} {address.PostalCode}, {address.Country}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{address.Line2}, {address.Line1}, {address.City}, {address.Subdivision} {address.PostalCode}, {address.Country}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatCompactOneLine(IAddress address)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(address.Line2))
|
||||||
|
{
|
||||||
|
return $"{address.Line1}, {address.City}, {address.Subdivision}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{address.Line2}, {address.Line1}, {address.City}, {address.Subdivision}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatMultiLine(IAddress address)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(address.Line2))
|
||||||
|
{
|
||||||
|
return $"{address.Line1}\n{address.City}, {address.Subdivision} {address.PostalCode}\n{address.Country}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{address.Line2}, {address.Line1}\n{address.City}, {address.Subdivision} {address.PostalCode}\n{address.Country}";
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ using GoogleApi.Entities.Maps.Geocoding.Address.Request;
|
|||||||
using GoogleApi.Entities.Maps.Geocoding.Location.Request;
|
using GoogleApi.Entities.Maps.Geocoding.Location.Request;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Svrnty.GeoManagement.Abstractions;
|
||||||
using Svrnty.GeoManagement.Abstractions.Abstractions;
|
using Svrnty.GeoManagement.Abstractions.Abstractions;
|
||||||
using Svrnty.GeoManagement.Abstractions.Models;
|
using Svrnty.GeoManagement.Abstractions.Models;
|
||||||
using Svrnty.GeoManagement.Google.Configuration;
|
using Svrnty.GeoManagement.Google.Configuration;
|
||||||
@ -91,7 +92,7 @@ public class GeoManagementGoogleProvider : IGeoManagementProvider
|
|||||||
var request = new LocationGeocodeRequest
|
var request = new LocationGeocodeRequest
|
||||||
{
|
{
|
||||||
Key = _options.ApiKey,
|
Key = _options.ApiKey,
|
||||||
Location = new GoogleApi.Entities.Common.Coordinate(geoPoint.Latitude, geoPoint.Longitude)
|
Location = new GoogleApi.Entities.Common.Coordinate((double)geoPoint.Latitude, (double)geoPoint.Longitude)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(_options.Language) &&
|
if (!string.IsNullOrWhiteSpace(_options.Language) &&
|
||||||
|
@ -26,20 +26,20 @@ internal static class GoogleAddressMapper
|
|||||||
var country = GetComponentValue(components, "country") ?? string.Empty;
|
var country = GetComponentValue(components, "country") ?? string.Empty;
|
||||||
|
|
||||||
var location = googleResult.Geometry?.Location != null
|
var location = googleResult.Geometry?.Location != null
|
||||||
? new GeoPoint(googleResult.Geometry.Location.Latitude, googleResult.Geometry.Location.Longitude)
|
? new GeoPoint((decimal)googleResult.Geometry.Location.Latitude, (decimal)googleResult.Geometry.Location.Longitude)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return new Abstractions.Models.Address(
|
return new Abstractions.Models.Address(Normalized: true)
|
||||||
Line1: line1,
|
{
|
||||||
Line2: line2,
|
Line1 = line1,
|
||||||
City: city,
|
Line2 = line2,
|
||||||
Subdivision: subdivision,
|
City = city,
|
||||||
PostalCode: postalCode,
|
Subdivision = subdivision,
|
||||||
Country: country,
|
PostalCode = postalCode,
|
||||||
Location: location,
|
Country = country,
|
||||||
Note: null,
|
Location = location,
|
||||||
IsNormalized: true
|
Note = null
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string BuildLine1(IEnumerable<AddressComponent> components)
|
private static string BuildLine1(IEnumerable<AddressComponent> components)
|
||||||
@ -79,8 +79,8 @@ internal static class GoogleAddressMapper
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new GeoPoint(
|
return new GeoPoint(
|
||||||
googleResult.Geometry.Location.Latitude,
|
(decimal)googleResult.Geometry.Location.Latitude,
|
||||||
googleResult.Geometry.Location.Longitude
|
(decimal)googleResult.Geometry.Location.Longitude
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,12 +24,12 @@ public class AddressTestData
|
|||||||
|
|
||||||
public class GeoPointTestData
|
public class GeoPointTestData
|
||||||
{
|
{
|
||||||
public double Latitude { get; set; }
|
public decimal Latitude { get; set; }
|
||||||
public double Longitude { get; set; }
|
public decimal Longitude { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CoordinateRange
|
public class CoordinateRange
|
||||||
{
|
{
|
||||||
public double Min { get; set; }
|
public decimal Min { get; set; }
|
||||||
public double Max { get; set; }
|
public decimal Max { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -54,15 +54,17 @@ public class GoogleProviderTests : IDisposable
|
|||||||
public async Task GetGeoPointAsync_WithValidAddress_ReturnsCoordinates()
|
public async Task GetGeoPointAsync_WithValidAddress_ReturnsCoordinates()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var address = new Address(
|
var address = new Address
|
||||||
Line1: _testData.ValidAddress.Line1,
|
{
|
||||||
Line2: _testData.ValidAddress.Line2,
|
Line1 = _testData.ValidAddress.Line1,
|
||||||
City: _testData.ValidAddress.City,
|
Line2 = _testData.ValidAddress.Line2,
|
||||||
Subdivision: _testData.ValidAddress.Subdivision,
|
City = _testData.ValidAddress.City,
|
||||||
PostalCode: _testData.ValidAddress.PostalCode,
|
Subdivision = _testData.ValidAddress.Subdivision,
|
||||||
Country: _testData.ValidAddress.Country,
|
PostalCode = _testData.ValidAddress.PostalCode,
|
||||||
Location: null,
|
Country = _testData.ValidAddress.Country,
|
||||||
Note: null);
|
Location = null,
|
||||||
|
Note = null
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await _provider.GetGeoPointAsync(address);
|
var result = await _provider.GetGeoPointAsync(address);
|
||||||
@ -89,7 +91,7 @@ public class GoogleProviderTests : IDisposable
|
|||||||
Assert.NotNull(result.Line1);
|
Assert.NotNull(result.Line1);
|
||||||
Assert.NotNull(result.City);
|
Assert.NotNull(result.City);
|
||||||
Assert.NotNull(result.Country);
|
Assert.NotNull(result.Country);
|
||||||
Assert.True(result.IsNormalized);
|
Assert.True(result.IsNormalized());
|
||||||
Assert.NotNull(result.Location);
|
Assert.NotNull(result.Location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,22 +99,24 @@ public class GoogleProviderTests : IDisposable
|
|||||||
public async Task NormalizeAddressAsync_WithAddressObject_ReturnsNormalizedAddress()
|
public async Task NormalizeAddressAsync_WithAddressObject_ReturnsNormalizedAddress()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var address = new Address(
|
var address = new Address
|
||||||
Line1: _testData.NormalizeTestAddress.Line1,
|
{
|
||||||
Line2: _testData.NormalizeTestAddress.Line2,
|
Line1 = _testData.NormalizeTestAddress.Line1,
|
||||||
City: _testData.NormalizeTestAddress.City,
|
Line2 = _testData.NormalizeTestAddress.Line2,
|
||||||
Subdivision: _testData.NormalizeTestAddress.Subdivision,
|
City = _testData.NormalizeTestAddress.City,
|
||||||
PostalCode: _testData.NormalizeTestAddress.PostalCode,
|
Subdivision = _testData.NormalizeTestAddress.Subdivision,
|
||||||
Country: _testData.NormalizeTestAddress.Country,
|
PostalCode = _testData.NormalizeTestAddress.PostalCode,
|
||||||
Location: null,
|
Country = _testData.NormalizeTestAddress.Country,
|
||||||
Note: null);
|
Location = null,
|
||||||
|
Note = null
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await _provider.NormalizeAddressAsync(address);
|
var result = await _provider.NormalizeAddressAsync(address);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.True(result.IsNormalized);
|
Assert.True(result.IsNormalized());
|
||||||
Assert.NotNull(result.Location);
|
Assert.NotNull(result.Location);
|
||||||
Assert.NotNull(result.Line1);
|
Assert.NotNull(result.Line1);
|
||||||
Assert.Contains("Amphitheatre", result.Line1, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("Amphitheatre", result.Line1, StringComparison.OrdinalIgnoreCase);
|
||||||
@ -129,7 +133,7 @@ public class GoogleProviderTests : IDisposable
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.True(result.IsNormalized);
|
Assert.True(result.IsNormalized());
|
||||||
Assert.NotNull(result.Location);
|
Assert.NotNull(result.Location);
|
||||||
Assert.NotNull(result.Line1);
|
Assert.NotNull(result.Line1);
|
||||||
Assert.Contains("Mountain View", result.City, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("Mountain View", result.City, StringComparison.OrdinalIgnoreCase);
|
||||||
@ -139,15 +143,17 @@ public class GoogleProviderTests : IDisposable
|
|||||||
public async Task GetGeoPointAsync_WithInvalidAddress_ReturnsNull()
|
public async Task GetGeoPointAsync_WithInvalidAddress_ReturnsNull()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var address = new Address(
|
var address = new Address
|
||||||
Line1: _testData.InvalidAddress.Line1,
|
{
|
||||||
Line2: _testData.InvalidAddress.Line2,
|
Line1 = _testData.InvalidAddress.Line1,
|
||||||
City: _testData.InvalidAddress.City,
|
Line2 = _testData.InvalidAddress.Line2,
|
||||||
Subdivision: _testData.InvalidAddress.Subdivision,
|
City = _testData.InvalidAddress.City,
|
||||||
PostalCode: _testData.InvalidAddress.PostalCode,
|
Subdivision = _testData.InvalidAddress.Subdivision,
|
||||||
Country: _testData.InvalidAddress.Country,
|
PostalCode = _testData.InvalidAddress.PostalCode,
|
||||||
Location: null,
|
Country = _testData.InvalidAddress.Country,
|
||||||
Note: null);
|
Location = null,
|
||||||
|
Note = null
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await _provider.GetGeoPointAsync(address);
|
var result = await _provider.GetGeoPointAsync(address);
|
||||||
@ -172,7 +178,7 @@ public class GoogleProviderTests : IDisposable
|
|||||||
// This test just verifies it doesn't crash
|
// This test just verifies it doesn't crash
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Assert.True(result.IsNormalized);
|
Assert.True(result.IsNormalized());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
Svrnty.GeoManagement.sln.DotSettings.user
Normal file
2
Svrnty.GeoManagement.sln.DotSettings.user
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AResult_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F728eee8ccad23b8fff516dba093eb71b49fb5ee8839f2118392e9747de4ebb_003FResult_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
Loading…
Reference in New Issue
Block a user