588 lines
22 KiB
C#
588 lines
22 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Threading.Tasks;
|
|
using gehGassi.Core.Interfaces;
|
|
using gehGassi.Domain.Common;
|
|
|
|
namespace gehGassi.External.Services
|
|
{
|
|
/// <summary>
|
|
/// Service der zu einer Adresse Längen- und Breitengrad ermittelt, oder zu Längen- und Breitengrad eine Adresse
|
|
/// </summary>
|
|
public class GeoLocationService : IGeoLocationService
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="httpClient">Instanz eines HttpClient</param>
|
|
public GeoLocationService(HttpClient httpClient)
|
|
{
|
|
_httpClient = httpClient;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Längen und Breitengrad zu einer Adresse suchen.
|
|
/// Zuerst wird versucht die Position mit Strasse, PLZ, Ort und Land zu finden.
|
|
/// Wenn das nicht klappt, dann mit PLZ, Ort, Land.
|
|
/// Wenn das nicht klappt, dann PLZ Land.
|
|
/// </summary>
|
|
/// <param name="address">Adresse</param>
|
|
/// <param name="language">Gewünschte Sprache ISO2</param>
|
|
/// <returns>Geo-Position</returns>
|
|
public async Task<GeoLocation> GetLocationAsync(Address address, string language)
|
|
{
|
|
var result = new GeoLocation()
|
|
{
|
|
Latitude = 0,
|
|
Longitude = 0,
|
|
Success = false,
|
|
ErrorMessage = "No matching address was found."
|
|
};
|
|
|
|
if (address == null)
|
|
{
|
|
result.ErrorMessage = "No address provided.";
|
|
return result;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(address.CountryCode))
|
|
{
|
|
result.ErrorMessage = "No country was specified.";
|
|
return result;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(address.State))
|
|
{
|
|
result.ErrorMessage = "No state code was specified.";
|
|
return result;
|
|
}
|
|
|
|
var innerAddress = new Address
|
|
{
|
|
AddressLine1 = address.AddressLine1,
|
|
AddressLine2 = address.AddressLine2,
|
|
Zip = address.Zip,
|
|
City = address.City,
|
|
State = address.State,
|
|
CountryCode = address.CountryCode
|
|
};
|
|
|
|
|
|
try
|
|
{
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Clear();
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue(language));
|
|
|
|
List<ApiGeoResult> locations = new List<ApiGeoResult>();
|
|
|
|
var queryLine = GenerateQueryLine(innerAddress);
|
|
var query = WebUtility.UrlEncode(queryLine);
|
|
|
|
var apiResult = await _httpClient.GetAsync($"search?q={query}&format=jsonv2&limit=1&addressdetails=1");
|
|
if (apiResult.IsSuccessStatusCode)
|
|
{
|
|
locations = await apiResult.Content.ReadFromJsonAsync<List<ApiGeoResult>>();
|
|
}
|
|
|
|
if (locations == null || locations.Count == 0)
|
|
{
|
|
await Task.Delay(1000);
|
|
innerAddress.AddressLine1 = string.Empty;
|
|
queryLine = GenerateQueryLine(innerAddress);
|
|
query = WebUtility.UrlEncode(queryLine);
|
|
apiResult = await _httpClient.GetAsync($"search?q={query}&format=jsonv2&limit=1&addressdetails=1");
|
|
if (apiResult.IsSuccessStatusCode)
|
|
{
|
|
locations = await apiResult.Content.ReadFromJsonAsync<List<ApiGeoResult>>();
|
|
}
|
|
}
|
|
|
|
if (locations == null || locations.Count == 0)
|
|
{
|
|
await Task.Delay(1000);
|
|
innerAddress.City = string.Empty;
|
|
queryLine = GenerateQueryLine(innerAddress);
|
|
query = WebUtility.UrlEncode(queryLine);
|
|
apiResult = await _httpClient.GetAsync($"search?q={query}&format=jsonv2&limit=1&addressdetails=1");
|
|
if (apiResult.IsSuccessStatusCode)
|
|
{
|
|
locations = await apiResult.Content.ReadFromJsonAsync<List<ApiGeoResult>>();
|
|
}
|
|
}
|
|
|
|
if (locations == null || locations.Count == 0)
|
|
{
|
|
await Task.Delay(1000);
|
|
innerAddress.Zip = string.Empty;
|
|
queryLine = GenerateQueryLine(innerAddress);
|
|
query = WebUtility.UrlEncode(queryLine);
|
|
apiResult = await _httpClient.GetAsync($"search?q={query}&format=jsonv2&limit=1&addressdetails=1");
|
|
if (apiResult.IsSuccessStatusCode)
|
|
{
|
|
locations = await apiResult.Content.ReadFromJsonAsync<List<ApiGeoResult>>();
|
|
}
|
|
}
|
|
|
|
if (locations != null && locations.Count > 0)
|
|
{
|
|
if (locations is { Count: 1 })
|
|
{
|
|
result.Success = true;
|
|
result.ErrorMessage = string.Empty;
|
|
result.Latitude = double.Parse(locations[0].Latitude, CultureInfo.InvariantCulture);
|
|
result.Longitude = double.Parse(locations[0].Longitude, CultureInfo.InvariantCulture);
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "No matching address was found.";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "Error in communication with geo service. Code: " + apiResult.StatusCode;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = ex.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Längen und Breitengrad zu einer Adresse suchen.
|
|
/// </summary>
|
|
/// <param name="address">Adresse als Text-Zeile</param>
|
|
/// <param name="language">Gewünschte Sprache ISO2</param>
|
|
/// <returns>Geo-Position</returns>
|
|
public async Task<GeoLocation> GetLocationAsync(string address, string language)
|
|
{
|
|
var result = new GeoLocation()
|
|
{
|
|
Latitude = 0,
|
|
Longitude = 0,
|
|
Success = false,
|
|
ErrorMessage = "No matching address was found."
|
|
};
|
|
|
|
if (string.IsNullOrWhiteSpace(address))
|
|
{
|
|
result.ErrorMessage = "No address provided.";
|
|
return result;
|
|
}
|
|
|
|
try
|
|
{
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Clear();
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue(language));
|
|
|
|
var query = WebUtility.UrlEncode($"{address}");
|
|
var apiResult = await _httpClient.GetAsync($"search?q={query}&format=jsonv2&limit=1&addressdetails=1");
|
|
if (apiResult.IsSuccessStatusCode)
|
|
{
|
|
var locations = await apiResult.Content.ReadFromJsonAsync<List<ApiGeoResult>>();
|
|
if (locations is { Count: 1 })
|
|
{
|
|
result.Success = true;
|
|
result.ErrorMessage = string.Empty;
|
|
result.Latitude = double.Parse(locations[0].Latitude, CultureInfo.InvariantCulture);
|
|
result.Longitude = double.Parse(locations[0].Longitude, CultureInfo.InvariantCulture);
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "No matching address was found.";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "Error in communication with geo service. Code: " + apiResult.StatusCode;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = ex.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Längen und Breitengrad zu einer Adresse suchen. Adresse wird mit Details mitgeliefert.
|
|
/// Zuerst wird versucht die Position mit Strasse, PLZ, Ort und Land zu finden.
|
|
/// Wenn das nicht klappt, dann mit PLZ, Ort, Land.
|
|
/// Wenn das nicht klappt, dann PLZ Land.
|
|
/// </summary>
|
|
/// <param name="address">Adresse</param>
|
|
/// <param name="language">Gewünschte Sprache ISO2</param>
|
|
/// <returns>Geo-Position</returns>
|
|
public async Task<GeoLocationFill> GetLocationFillAsync(Address address, string language)
|
|
{
|
|
var result = new GeoLocationFill()
|
|
{
|
|
Latitude = 0,
|
|
Longitude = 0,
|
|
Success = false,
|
|
ErrorMessage = "No matching address was found."
|
|
};
|
|
|
|
if (address == null)
|
|
{
|
|
result.ErrorMessage = "No address provided.";
|
|
return result;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(address.CountryCode))
|
|
{
|
|
result.ErrorMessage = "No country was specified.";
|
|
return result;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(address.State))
|
|
{
|
|
result.ErrorMessage = "No state code was specified.";
|
|
return result;
|
|
}
|
|
|
|
try
|
|
{
|
|
var queryLine = GenerateQueryLine(address);
|
|
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Clear();
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue(language));
|
|
|
|
var query = WebUtility.UrlEncode(queryLine);
|
|
var apiResult = await _httpClient.GetAsync($"search?q={query}&format=jsonv2&limit=1&addressdetails=1");
|
|
if (apiResult.IsSuccessStatusCode)
|
|
{
|
|
var locations = await apiResult.Content.ReadFromJsonAsync<List<ApiGeoFillResult>>();
|
|
if (locations is { Count: 1 })
|
|
{
|
|
result.Success = true;
|
|
result.ErrorMessage = string.Empty;
|
|
result.Latitude = double.Parse(locations[0].Latitude, CultureInfo.InvariantCulture);
|
|
result.Longitude = double.Parse(locations[0].Longitude, CultureInfo.InvariantCulture);
|
|
result.Address.AddressLine1 = $"{locations[0].Address.AddressLine1} {locations[0].Address.HouseNumber}";
|
|
result.Address.AddressLine2 = string.Empty;
|
|
result.Address.City = locations[0].Address.City;
|
|
result.Address.Zip = locations[0].Address.Zip;
|
|
result.Address.State = locations[0].Address.StateCode;
|
|
result.Address.CountryCode = locations[0].Address.CountryIso;
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "No matching address was found.";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "Error in communication with geo service. Code: " + apiResult.StatusCode;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = ex.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Längen und Breitengrad zu einer Adresse suchen. Adresse wird mit Details mitgeliefert.
|
|
/// </summary>
|
|
/// <param name="address">Adresse als Text-Zeile</param>
|
|
/// <param name="language">Gewünschte Sprache ISO2</param>
|
|
/// <returns>Geo-Position</returns>
|
|
public async Task<GeoLocationFill> GetLocationFillAsync(string address, string language)
|
|
{
|
|
var result = new GeoLocationFill()
|
|
{
|
|
Latitude = 0,
|
|
Longitude = 0,
|
|
Success = false,
|
|
ErrorMessage = "No matching address was found."
|
|
};
|
|
|
|
if (string.IsNullOrWhiteSpace(address))
|
|
{
|
|
result.ErrorMessage = "No address provided.";
|
|
return result;
|
|
}
|
|
|
|
try
|
|
{
|
|
|
|
//Es darf immer nur alle 1 Sekunden eine Anfrage gesendet werden. Daher "emulieren" wir das hier.
|
|
await Task.Delay(1000);
|
|
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Clear();
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue(language));
|
|
|
|
var query = WebUtility.UrlEncode($"{address}");
|
|
var apiResult = await _httpClient.GetAsync($"search?q={query}&format=jsonv2&limit=1addressdetails=1");
|
|
if (apiResult.IsSuccessStatusCode)
|
|
{
|
|
var locations = await apiResult.Content.ReadFromJsonAsync<List<ApiGeoFillResult>>();
|
|
if (locations is { Count: 1 })
|
|
{
|
|
result.Success = true;
|
|
result.ErrorMessage = string.Empty;
|
|
result.Latitude = double.Parse(locations[0].Latitude, CultureInfo.InvariantCulture);
|
|
result.Longitude = double.Parse(locations[0].Longitude, CultureInfo.InvariantCulture);
|
|
result.Address.AddressLine1 = $"{locations[0].Address.AddressLine1} {locations[0].Address.HouseNumber}";
|
|
result.Address.AddressLine2 = string.Empty;
|
|
result.Address.City = locations[0].Address.City;
|
|
result.Address.Zip = locations[0].Address.Zip;
|
|
result.Address.State = locations[0].Address.StateCode;
|
|
result.Address.CountryCode = locations[0].Address.CountryIso;
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "No matching address was found.";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "Error in communication with geo service. Code: " + apiResult.StatusCode;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = ex.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reverse-Geocoding
|
|
/// </summary>
|
|
/// <param name="lat">Breitengrad</param>
|
|
/// <param name="lng">Längengrad</param>
|
|
/// <param name="language">Gewünschte Sprache ISO2</param>
|
|
/// <returns>ReverseLocation</returns>
|
|
public async Task<ReverseLocation> GetReverseAsync(double lat, double lng, string language)
|
|
{
|
|
var result = new ReverseLocation()
|
|
{
|
|
|
|
Success = false,
|
|
ErrorMessage = "No matching address was found."
|
|
};
|
|
|
|
try
|
|
{
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Clear();
|
|
_httpClient.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue(language));
|
|
|
|
var apiResult = await _httpClient.GetAsync($"reverse?lat={lat.ToString(CultureInfo.InvariantCulture)}&lon={lng.ToString(CultureInfo.InvariantCulture)}&format=jsonv2");
|
|
if (apiResult.IsSuccessStatusCode)
|
|
{
|
|
var reverseResult = await apiResult.Content.ReadFromJsonAsync<ApiReverseResult>();
|
|
if (reverseResult != null)
|
|
{
|
|
result.Success = true;
|
|
result.ErrorMessage = string.Empty;
|
|
result.Address.AddressLine1 = $"{reverseResult.Address.AddressLine1} {reverseResult.Address.HouseNumber}";
|
|
result.Address.AddressLine2 = string.Empty;
|
|
result.Address.City = reverseResult.Address.City;
|
|
result.Address.Zip = reverseResult.Address.Zip;
|
|
result.Address.State = reverseResult.Address.StateCode;
|
|
result.Address.CountryCode = reverseResult.Address.CountryIso;
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "No matching address was found.";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = "Error in communication with geo service. Code: " + apiResult.StatusCode;
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.ErrorMessage = ex.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#region Helper
|
|
|
|
/// <summary>
|
|
/// Erstellt aus einer Adresse einen Anfrage-String
|
|
/// </summary>
|
|
/// <param name="address">Adresse</param>
|
|
/// <returns>Anfrage-String</returns>
|
|
private static string GenerateQueryLine(Address address)
|
|
{
|
|
var queryLine = string.Empty;
|
|
if (!string.IsNullOrWhiteSpace(address.AddressLine1))
|
|
{
|
|
if (queryLine.Length > 0)
|
|
queryLine += ", ";
|
|
|
|
queryLine += $"{address.AddressLine1}";
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(address.Zip))
|
|
{
|
|
if (queryLine.Length > 0)
|
|
queryLine += ", ";
|
|
|
|
queryLine += $"{address.Zip}";
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(address.City))
|
|
{
|
|
if (queryLine.Length > 0)
|
|
queryLine += ", ";
|
|
|
|
queryLine += $"{address.City}";
|
|
}
|
|
|
|
if (queryLine.Length == 0)
|
|
queryLine += $"{address.State}";
|
|
if (queryLine.Length > 0)
|
|
queryLine += ", ";
|
|
queryLine += $"{address.CountryCode}";
|
|
return queryLine;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hilfsklasse für die Zuordnung von Tags
|
|
/// </summary>
|
|
internal class ApiGeoResult
|
|
{
|
|
/// <summary>
|
|
/// Openstreetmap Platz-ID
|
|
/// </summary>
|
|
[JsonPropertyName("place_id")]
|
|
public long PlaceId { get; set; }
|
|
|
|
/// <summary>
|
|
/// Breitengrad
|
|
/// </summary>
|
|
[JsonPropertyName("lat")]
|
|
public string Latitude { get; set; }
|
|
|
|
/// <summary>
|
|
/// Längengrad
|
|
/// </summary>
|
|
[JsonPropertyName("lon")]
|
|
public string Longitude { get; set; }
|
|
|
|
/// <summary>
|
|
/// Voller Name dargestellt
|
|
/// </summary>
|
|
[JsonPropertyName("display_name")]
|
|
public string DisplayName { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hilfsklasse für die ERgänzung einer Adresse
|
|
/// </summary>
|
|
internal class ApiGeoFillResult : ApiGeoResult
|
|
{
|
|
/// <summary>
|
|
/// Adresse
|
|
/// </summary>
|
|
[JsonPropertyName("address")]
|
|
public ApiReverseAddress Address { get; set; }
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
public ApiGeoFillResult()
|
|
{
|
|
Address = new ApiReverseAddress();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hilfsklasse für die Zuordnung von Tags
|
|
/// </summary>
|
|
internal class ApiReverseResult
|
|
{
|
|
/// <summary>
|
|
/// Openstreetmap Platz-ID
|
|
/// </summary>
|
|
[JsonPropertyName("place_id")]
|
|
public long PlaceId { get; set; }
|
|
|
|
/// <summary>
|
|
/// Adresse
|
|
/// </summary>
|
|
[JsonPropertyName("address")]
|
|
public ApiReverseAddress Address { get; set; }
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
public ApiReverseResult()
|
|
{
|
|
Address = new ApiReverseAddress();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adress-Mapping
|
|
/// </summary>
|
|
internal class ApiReverseAddress
|
|
{
|
|
[JsonPropertyName("road")]
|
|
public string AddressLine1 { get; set; }
|
|
|
|
[JsonPropertyName("house_number")]
|
|
public string HouseNumber { get; set; }
|
|
|
|
[JsonPropertyName("city")]
|
|
public string City { get; set; }
|
|
|
|
[JsonPropertyName("postcode")]
|
|
public string Zip { get; set; }
|
|
|
|
[JsonPropertyName("state")]
|
|
public string StateName { get; set; }
|
|
|
|
[JsonPropertyName("ISO3166-2-lvl4")]
|
|
public string StateCode { get; set; }
|
|
|
|
[JsonPropertyName("country")]
|
|
public string CountryName { get; set; }
|
|
|
|
[JsonPropertyName("country_code")]
|
|
public string CountryIso { get; set; }
|
|
}
|
|
}
|