Files
BeWoPlaner/Service/ServiceUtils/DistanceCalculator/OpenrouteServiceAPI.cs
2025-06-02 11:16:35 +02:00

261 lines
8.8 KiB
C#

using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Service.Core;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Task = System.Threading.Tasks.Task;
namespace BeWo.Service.ServiceUtils.DistanceCalculator
{
public class OpenrouteServiceAPI : IDistanceAPI
{
public class ResponseGeo
{
public Feature[] Features { get; set; }
public class Feature
{
public string Type { get; set; }
public Geometry Geometry { get; set; }
}
public class Geometry
{
public string Type { get; set; }
public decimal[] Coordinates { get; set; }
}
}
public class ResponseMatrix
{
public decimal[,] Durations { get; set; }
public decimal[,] Distances { get; set; }
}
private string Key { get; set; }
private string GeoUrl { get; set; }
private string Url { get; set; }
private AddressRouteMatrix Matrix { get; set; }
private Address[] OriginAddresses { get; set; }
private Address[] DestinationAddresses { get; set; }
public OpenrouteServiceAPI()
{
var appSettings = ConfigurationManager.AppSettings;
Url = appSettings["OpenrouteServiceApiUrl"];
if (string.IsNullOrEmpty(Url))
{
throw new Exception("OpenrouteServiceApiUrl is not set in AppSettings.");
}
Key = MergedConfig.GetSecretSetting("OpenrouteServiceApiKey");
if (string.IsNullOrEmpty(Key))
{
throw new Exception("OpenrouteServiceApiKey is not set.");
}
GeoUrl = appSettings["OpenrouteServiceApiUrlGeocode"];
if (string.IsNullOrEmpty(GeoUrl))
{
throw new Exception("OpenrouteServiceApiUrlGeocode is not set in AppSettings.");
}
}
public void RequestMissingData(ref AddressRouteMatrix matrix, IEnumerable<Address> origins, IEnumerable<Address> destinations)
{
Matrix = matrix;
OriginAddresses = origins.ToArray();
DestinationAddresses = destinations.ToArray();
if (!(origins.Any() && destinations.Any()))
throw new NotImplementedException("Keine Addresse angefragt");
LoadCoordinates().Wait();
var task = GetMatrix();
task.Wait();
if (task.Exception is object)
{
throw task.Exception;
}
}
public async Task LoadCoordinates()
{
// Update Koordinaten
var items = OriginAddresses.Union(DestinationAddresses).ToHashSet();
foreach (var item in items)
{
if (item.L_Version is object && item.L_Version == item.Version)
continue;
await LoadCoordinate(item);
DAOFactory.GenericDAO.Update(item);
}
}
public async Task LoadCoordinate(Address address)
{
using (var client = new HttpClient())
{
var uri = new Uri(GetGeocodeUrl(address));
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.TryAddWithoutValidation("accept", "application/json; charset=utf-8");
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
using (var response = await client.GetAsync(uri))
{
string responseData = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
try
{
var data = JsonConvert.DeserializeObject<ResponseGeo>(responseData);
address.Longitude = data.Features[0].Geometry.Coordinates[0];
address.Latitude = data.Features[0].Geometry.Coordinates[1];
address.L_Version = address.Version.Value + 1;
return;
}
catch (Exception e)
{
throw e;
}
}
else
{
throw new Exception("OpenrouteServiceAPI failed with status code: " + response.StatusCode + " |||Data: " + responseData);
}
}
}
}
public async Task<AddressRouteMatrix> GetMatrix()
{
using (var client = new HttpClient())
{
var uri = new Uri(Url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.TryAddWithoutValidation("accept", "application/json; charset=utf-8");
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Key);
using (var content = new StringContent(GetMatrixBodyContentString()))
//using (var content = new StringContent("{\"locations\":[[48.477473,9.70093],[49.153868,9.207916]]}"))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var response = await client.PostAsync(uri, content))
{
string responseData = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var data = JsonConvert.DeserializeObject<ResponseMatrix>(responseData);
return FillMatrix(data);
}
else
{
throw new Exception("OpenrouteServiceAPI failed with status code: " + response.StatusCode + " |||Data: " + responseData);
}
}
}
}
}
public AddressRouteMatrix FillMatrix(ResponseMatrix response)
{
for (int i = 0; i < response.Durations.GetLength(0); i++)
{
for (int j = 0; j < response.Durations.GetLength(1); j++)
{
var distance = response.Distances[i, j];
var duration = response.Durations[i, j];
var origin = OriginAddresses[i];
var destination = DestinationAddresses[j];
var route = Matrix.GetRoute(origin, destination);
route.Distance_in_meter = (int) (distance * 1000);
route.Time_in_seconds = (int) duration;
}
}
return Matrix;
}
private string GetGeocodeUrl(Address address)
{
var html = HttpUtility.UrlEncode(AddressRouteManager.AddressToString(address));
return $"{GeoUrl}?api_key={Key}&text={html}&sources=openstreetmap&size=1";
}
private string GetMatrixBodyContentString()
{
var sb = new StringBuilder();
var origins = OriginAddresses.Select(GetCoordinates);
var olen = OriginAddresses.Count();
var destination = DestinationAddresses.Select(GetCoordinates);
var dlen = DestinationAddresses.Count();
sb.Append("{\"locations\":");
sb.Append("[");
sb.Append(string.Join(",", origins.Union(destination)));
sb.Append("]");
sb.Append(",\"metrics\":[\"distance\",\"duration\"]");
sb.Append(",\"units\":\"km\"");
sb.Append(",\"sources\":");
sb.Append(GetList(0, olen - 1));
sb.Append(",\"destinations\":");
sb.Append(GetList(olen, olen + dlen - 1));
sb.Append("}");
return sb.ToString();
}
private string GetCoordinates(Address add) => $"[{add.Longitude.Value.ToString(new CultureInfo("en-US"))},{add.Latitude.Value.ToString(new CultureInfo("en-US"))}]";
private string GetList(int x, int y)
{
if (y < x)
throw new ArgumentException($"y({y}) darf nicht kleiner als x({x}) sein");
var diff = y - x;
var list = new int[diff + 1];
for (int i = 0; i <= diff; i++)
{
list[i] = x + i;
}
return "[" + string.Join(",", list) + "]";
}
}
}