261 lines
8.8 KiB
C#
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) + "]";
|
|
}
|
|
}
|
|
}
|