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
origins, IEnumerable
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(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 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(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) + "]"; } } }