- restaurar derivaciones - añadido la pedida de anchura de calles y cariiles - correcion paradas bus - desglose del score - historial no cambia la hora - resuarar no cambia modo de historial
137 lines
4.6 KiB
C#
137 lines
4.6 KiB
C#
using System.Net.Http.Json;
|
|
using System.Text.Json.Serialization;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
|
|
namespace RutasDBUS.Servicios.Caminata;
|
|
|
|
public sealed class ClienteAtributosOsm : IClienteAtributosOsm
|
|
{
|
|
private const int MaximoCaminosPorConsulta = 100;
|
|
private readonly HttpClient _httpClient;
|
|
private readonly IMemoryCache _cache;
|
|
private readonly string _baseUrl;
|
|
|
|
public ClienteAtributosOsm(
|
|
HttpClient httpClient,
|
|
IMemoryCache cache,
|
|
IConfiguration configuracion)
|
|
{
|
|
_httpClient = httpClient;
|
|
_cache = cache;
|
|
_baseUrl = (configuracion["Caminata:Osm:BaseUrl"]
|
|
?? "https://api.openstreetmap.org/api/0.6").Trim().TrimEnd('/');
|
|
}
|
|
|
|
public async Task<IReadOnlyDictionary<long, AnchuraViaOsm>> ObtenerAnchurasAsync(
|
|
IEnumerable<long> idsCamino,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var ids = idsCamino
|
|
.Where(id => id > 0)
|
|
.Distinct()
|
|
.Take(MaximoCaminosPorConsulta)
|
|
.ToList();
|
|
|
|
var resultado = new Dictionary<long, AnchuraViaOsm>();
|
|
var pendientes = new List<long>();
|
|
foreach (var id in ids)
|
|
{
|
|
if (_cache.TryGetValue<ResultadoAnchuraCache>(ClaveCache(id), out var cacheado))
|
|
{
|
|
if (cacheado?.Dato is not null)
|
|
resultado[id] = cacheado.Dato;
|
|
}
|
|
else
|
|
{
|
|
pendientes.Add(id);
|
|
}
|
|
}
|
|
|
|
if (pendientes.Count == 0)
|
|
return resultado;
|
|
|
|
try
|
|
{
|
|
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
timeout.CancelAfter(TimeSpan.FromSeconds(4));
|
|
|
|
var idsConsulta = string.Join(',', pendientes);
|
|
using var peticion = new HttpRequestMessage(
|
|
HttpMethod.Get,
|
|
$"{_baseUrl}/ways.json?ways={Uri.EscapeDataString(idsConsulta)}");
|
|
peticion.Headers.UserAgent.ParseAdd("RutasDBUS/1.0");
|
|
|
|
using var respuesta = await _httpClient.SendAsync(peticion, timeout.Token);
|
|
if (!respuesta.IsSuccessStatusCode)
|
|
return resultado;
|
|
|
|
var datos = await respuesta.Content.ReadFromJsonAsync<RespuestaOsm>(timeout.Token);
|
|
var encontrados = new HashSet<long>();
|
|
foreach (var elemento in datos?.Elementos ?? [])
|
|
{
|
|
encontrados.Add(elemento.Id);
|
|
var anchura = ObtenerAnchura(elemento.Etiquetas);
|
|
GuardarCache(elemento.Id, anchura);
|
|
if (anchura is not null)
|
|
resultado[elemento.Id] = anchura;
|
|
}
|
|
|
|
foreach (var id in pendientes.Where(id => !encontrados.Contains(id)))
|
|
GuardarCache(id, null);
|
|
}
|
|
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
|
|
{
|
|
// La anchura es informativa: un timeout no debe bloquear el tooltip.
|
|
}
|
|
catch (HttpRequestException)
|
|
{
|
|
// La información principal de v2 sigue siendo válida si OSM no responde.
|
|
}
|
|
catch (System.Text.Json.JsonException)
|
|
{
|
|
// Una respuesta no válida tampoco debe ocultar la información principal de v2.
|
|
}
|
|
|
|
return resultado;
|
|
}
|
|
|
|
private void GuardarCache(long id, AnchuraViaOsm? dato)
|
|
=> _cache.Set(
|
|
ClaveCache(id),
|
|
new ResultadoAnchuraCache(dato),
|
|
dato is null ? TimeSpan.FromHours(6) : TimeSpan.FromDays(1));
|
|
|
|
private static AnchuraViaOsm? ObtenerAnchura(Dictionary<string, string>? etiquetas)
|
|
{
|
|
if (etiquetas is null)
|
|
return null;
|
|
|
|
if (etiquetas.TryGetValue("width", out var anchura) && !string.IsNullOrWhiteSpace(anchura))
|
|
return new AnchuraViaOsm(anchura.Trim(), false);
|
|
|
|
if (etiquetas.TryGetValue("est_width", out var estimada) && !string.IsNullOrWhiteSpace(estimada))
|
|
return new AnchuraViaOsm(estimada.Trim(), true);
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string ClaveCache(long id) => $"osm:anchura:{id}";
|
|
|
|
private sealed record ResultadoAnchuraCache(AnchuraViaOsm? Dato);
|
|
|
|
private sealed class RespuestaOsm
|
|
{
|
|
[JsonPropertyName("elements")]
|
|
public List<ElementoOsm>? Elementos { get; set; }
|
|
}
|
|
|
|
private sealed class ElementoOsm
|
|
{
|
|
[JsonPropertyName("id")]
|
|
public long Id { get; set; }
|
|
|
|
[JsonPropertyName("tags")]
|
|
public Dictionary<string, string>? Etiquetas { get; set; }
|
|
}
|
|
}
|