- 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
440 lines
23 KiB
C#
440 lines
23 KiB
C#
using System.Reflection;
|
|
using System.Text.Json;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using RutasDBUS.Components.Pages;
|
|
using RutasDBUS.Modelos.Historial;
|
|
using RutasDBUS.Modelos.Planificacion;
|
|
using RutasDBUS.Servicios.Caminata;
|
|
using RutasDBUS.Servicios.Configuracion;
|
|
using RutasDBUS.Servicios.Planificacion;
|
|
|
|
static void Check(bool result, string message)
|
|
{
|
|
if (!result) throw new Exception(message);
|
|
Console.WriteLine("OK " + message);
|
|
}
|
|
|
|
var baseMethod = typeof(PlanificadorRutas).GetMethod("CrearConfiguracionBaseHistorial", BindingFlags.Static | BindingFlags.NonPublic)!;
|
|
var defaults = (ConfiguracionHistorialRuta)baseMethod.Invoke(null, null)!;
|
|
Check(defaults.PesoTransbordoRankingMinutos == 6, "Penalizacion inicial de 6 minutos");
|
|
|
|
var folder = Path.Combine(Path.GetTempPath(), "rutasdbus-regression-" + Guid.NewGuid().ToString("N"));
|
|
IConfiguration Config() => new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["ValoresFabrica:Directorio"] = folder
|
|
}).Build();
|
|
ServicioValoresFabrica Service() => new(Config(), NullLogger<ServicioValoresFabrica>.Instance);
|
|
|
|
try
|
|
{
|
|
defaults.PesoTransbordoRankingMinutos = 9;
|
|
defaults.FechaSimulada = null;
|
|
defaults.HoraSimulada = null;
|
|
Directory.CreateDirectory(folder);
|
|
var file = Path.Combine(folder, "configuracion-fabrica.json");
|
|
await File.WriteAllTextAsync(file, JsonSerializer.Serialize(defaults));
|
|
var loaded = await Service().CargarAsync();
|
|
Check(loaded?.PesoTransbordoRankingMinutos == 9, "Valores compartidos entre instancias");
|
|
Check(loaded is { FechaSimulada: null, HoraSimulada: null }, "Los valores de fabrica no fijan la fecha y hora");
|
|
File.WriteAllText(file, "{incompleto");
|
|
Check(await Service().CargarAsync() is null, "JSON danado permite usar valores base");
|
|
}
|
|
finally
|
|
{
|
|
// Solo datos temporales creados por esta prueba.
|
|
if (Directory.Exists(folder)) Directory.Delete(folder, recursive: true);
|
|
}
|
|
|
|
var fake = new FakeValhalla();
|
|
var planner = new PlanificadorRutasServicio(null!, null!, fake, null!, null!, null!, null!);
|
|
var walk = typeof(PlanificadorRutasServicio).GetMethod("WalkValhallaCached", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
|
async Task<(double[,]? linea, double distancia, double duracion)> Walk(List<List<double[]>>? zones = null) =>
|
|
await (Task<(double[,]?, double, double)>)walk.Invoke(planner, new object?[] { 43.31, -1.99, 43.32, -1.98, zones, null })!;
|
|
List<double[]> Ring(double lon, double lat, double size) =>
|
|
new() { new[] { lon, lat }, new[] { lon + size, lat }, new[] { lon + size, lat + size },
|
|
new[] { lon, lat + size }, new[] { lon, lat } };
|
|
string Polyline6(params (double lat, double lon)[] puntos)
|
|
{
|
|
var texto = new System.Text.StringBuilder();
|
|
var latitudAnterior = 0;
|
|
var longitudAnterior = 0;
|
|
foreach (var punto in puntos)
|
|
{
|
|
Codificar((int)Math.Round(punto.lat * 1_000_000) - latitudAnterior, texto);
|
|
Codificar((int)Math.Round(punto.lon * 1_000_000) - longitudAnterior, texto);
|
|
latitudAnterior = (int)Math.Round(punto.lat * 1_000_000);
|
|
longitudAnterior = (int)Math.Round(punto.lon * 1_000_000);
|
|
}
|
|
return texto.ToString();
|
|
|
|
static void Codificar(int diferencia, System.Text.StringBuilder texto)
|
|
{
|
|
var valor = diferencia < 0 ? ~(diferencia << 1) : diferencia << 1;
|
|
while (valor >= 0x20)
|
|
{
|
|
texto.Append((char)((0x20 | (valor & 0x1f)) + 63));
|
|
valor >>= 5;
|
|
}
|
|
texto.Append((char)(valor + 63));
|
|
}
|
|
}
|
|
|
|
await Walk();
|
|
var far = new List<List<double[]>> { Ring(-2.3, 43.0, .01) };
|
|
await Walk(far);
|
|
Check(fake.Calls == 2 && fake.Zones == 1, "Toda zona activa se envia a V2 aunque este lejos de la ruta previa");
|
|
var near = new List<List<double[]>> { Ring(-1.985, 43.315, .001), Ring(-1.982, 43.317, .001) };
|
|
await Walk(near);
|
|
Check(fake.Zones == 2, "Se envian todos los poligonos");
|
|
await Walk(near);
|
|
Check(fake.Calls == 3, "Mismas zonas reutilizan cache restringida");
|
|
near[0][0][0] += .00001;
|
|
await Walk(near);
|
|
Check(fake.Calls == 4, "Mover un vertice invalida esa entrada de cache");
|
|
await Walk(new() { Ring(-2.2, 43.1, .5) });
|
|
Check(fake.Calls == 5, "Una zona que rodea la ruta no se ignora aunque sus vertices esten lejos");
|
|
fake.Fail = true;
|
|
var retry = new List<List<double[]>> { Ring(-1.984, 43.312, .0002) };
|
|
await Walk(retry);
|
|
fake.Fail = false;
|
|
await Walk(retry);
|
|
Check(fake.Calls == 7, "Un fallo temporal no queda guardado en cache de zonas");
|
|
var puntos = new List<double[]> { new[] { 43.314, -1.984 }, new[] { 43.315, -1.983 } };
|
|
await (Task<(double[,]?, double, double)>)walk.Invoke(planner,
|
|
new object?[] { 43.31, -1.99, 43.32, -1.98, near, puntos })!;
|
|
Check(fake.Zones == 2 && fake.Points == 2, "Se envian juntos todos los poligonos y puntos activos");
|
|
var retryHandler = new RetryValhallaHandler();
|
|
var retryConfig = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Caminata:Valhalla:BaseUrl"] = "http://v2.test"
|
|
}).Build();
|
|
var retryClient = new ClienteValhalla(new HttpClient(retryHandler), retryConfig);
|
|
await retryClient.ObtenerRutaAsync(
|
|
43.316344, -1.983193, 43.315282, -1.985457,
|
|
excludePolygons: new() { Ring(-1.9848, 43.3158, .0001) });
|
|
Check(retryHandler.Bodies.Count == 8,
|
|
"V2 amplia progresivamente la busqueda y prueba la alternativa puntual cuando recibe 442");
|
|
using (var primerIntento = JsonDocument.Parse(retryHandler.Bodies[0]))
|
|
using (var segundoIntento = JsonDocument.Parse(retryHandler.Bodies[1]))
|
|
using (var tercerIntento = JsonDocument.Parse(retryHandler.Bodies[2]))
|
|
using (var cuartoIntento = JsonDocument.Parse(retryHandler.Bodies[3]))
|
|
{
|
|
var primeraUbicacionInicial = primerIntento.RootElement.GetProperty("locations")[0];
|
|
var primeraUbicacionReintento = segundoIntento.RootElement.GetProperty("locations")[0];
|
|
var primeraUbicacionSegundoReintento = tercerIntento.RootElement.GetProperty("locations")[0];
|
|
var primeraUbicacionTercerReintento = cuartoIntento.RootElement.GetProperty("locations")[0];
|
|
Check(!primeraUbicacionInicial.TryGetProperty("radius", out _) &&
|
|
primeraUbicacionReintento.GetProperty("radius").GetInt32() == 50 &&
|
|
primeraUbicacionSegundoReintento.GetProperty("radius").GetInt32() == 100 &&
|
|
primeraUbicacionTercerReintento.GetProperty("radius").GetInt32() == 200,
|
|
"Los reintentos buscan una arista alternativa hasta un radio controlado de 200 metros");
|
|
}
|
|
var polygonFallbackHandler = new PolygonNoPathThenPointSuccessHandler(
|
|
Polyline6((43.31, -1.99), (43.32, -1.98)),
|
|
Polyline6((43.31, -1.99), (43.31, -1.98), (43.32, -1.98)));
|
|
var polygonFallbackClient = new ClienteValhalla(new HttpClient(polygonFallbackHandler), retryConfig);
|
|
var rutaConPoligonoConvertido = await polygonFallbackClient.ObtenerRutaAsync(
|
|
43.31, -1.99, 43.32, -1.98,
|
|
excludePolygons: new() { Ring(-1.9855, 43.3145, .001) });
|
|
Check(rutaConPoligonoConvertido.Linea is not null && polygonFallbackHandler.Bodies.Count == 6,
|
|
"Un poligono que devuelve 442 se sustituye por puntos y encuentra una alternativa");
|
|
using (var peticionConPuntos = JsonDocument.Parse(polygonFallbackHandler.Bodies[^1]))
|
|
{
|
|
Check(peticionConPuntos.RootElement.GetProperty("exclude_polygons").ValueKind == JsonValueKind.Null &&
|
|
peticionConPuntos.RootElement.GetProperty("exclude_locations").GetArrayLength() > 0,
|
|
"El fallback conserva la zona como validacion y envia a v2 las aristas afectadas");
|
|
}
|
|
var poligonoValidacion = Ring(-1.9855, 43.3145, .001);
|
|
var validationHandler = new PolygonValidationValhallaHandler(
|
|
Polyline6((43.31, -1.99), (43.32, -1.98)),
|
|
Polyline6((43.31, -1.99), (43.31, -1.98), (43.32, -1.98)));
|
|
var validationClient = new ClienteValhalla(new HttpClient(validationHandler), retryConfig);
|
|
var rutaValidada = await validationClient.ObtenerRutaAsync(
|
|
43.31, -1.99, 43.32, -1.98, excludePolygons: new() { poligonoValidacion });
|
|
Check(validationHandler.Bodies.Count == 2 && rutaValidada.Linea is not null,
|
|
"V2 recalcula una ruta que atraviesa una zona activa");
|
|
using (var peticionReforzada = JsonDocument.Parse(validationHandler.Bodies[1]))
|
|
{
|
|
Check(peticionReforzada.RootElement.GetProperty("exclude_locations").GetArrayLength() > 0,
|
|
"La ruta conflictiva refuerza la exclusion sobre las aristas atravesadas");
|
|
}
|
|
var parametrosConExclusion = new ParametrosPlanificadorRuta
|
|
{
|
|
UsarValhallaParaCaminatas = true,
|
|
PoligonosExclusionCaminata = new() { Ring(-1.985, 43.315, .001) }
|
|
};
|
|
var caminataNoResuelta = typeof(PlanificadorRutasServicio).GetMethod(
|
|
"CaminataNoResueltaConExclusiones", BindingFlags.Static | BindingFlags.NonPublic)!;
|
|
bool RechazarCaminata(ParametrosPlanificadorRuta parametros, double directa, double[,]? linea, double distancia) =>
|
|
(bool)caminataNoResuelta.Invoke(null, new object?[] { parametros, directa, linea, distancia })!;
|
|
Check(RechazarCaminata(parametrosConExclusion, 100, null, 0),
|
|
"Una caminata V2 sin ruta se descarta cuando hay exclusiones");
|
|
Check(!RechazarCaminata(new ParametrosPlanificadorRuta { UsarValhallaParaCaminatas = true }, 100, null, 0),
|
|
"Sin exclusiones se conserva el fallback historico");
|
|
Check(!RechazarCaminata(parametrosConExclusion, 100, new[,] { { 43.31, -1.99 }, { 43.32, -1.98 } }, 120),
|
|
"Una caminata V2 valida se conserva con exclusiones");
|
|
var puntoDentro = typeof(PlanificadorRutasServicio).GetMethod(
|
|
"PuntoDentroDePoligonoExclusion", BindingFlags.Static | BindingFlags.NonPublic)!;
|
|
Check((bool)puntoDentro.Invoke(null, new object?[] { 43.3155, -1.9845, parametrosConExclusion.PoligonosExclusionCaminata })!,
|
|
"Las paradas interiores a una zona se detectan antes de calcular");
|
|
var page = new PlanificadorRutas();
|
|
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
|
|
var fabricaHistorial = (ConfiguracionHistorialRuta)baseMethod.Invoke(null, null)!;
|
|
fabricaHistorial.FactorEquilibradoAndarBus = 6;
|
|
typeof(PlanificadorRutas).GetField("_configuracionFabrica", flags)!.SetValue(page, fabricaHistorial);
|
|
var crearPredeterminadaHistorial = typeof(PlanificadorRutas).GetMethod(
|
|
"CrearConfiguracionPredeterminadaParaHistorial", flags)!;
|
|
var configuracionGuardada = fabricaHistorial.Clonar();
|
|
configuracionGuardada.UsarHoraSimulada = true;
|
|
configuracionGuardada.FechaSimulada = "2026-04-08";
|
|
configuracionGuardada.HoraSimulada = "08:46:17";
|
|
configuracionGuardada.FactorEquilibradoAndarBus = 99;
|
|
var registroConHora = new RegistroHistorialRuta { Configuracion = configuracionGuardada };
|
|
var predeterminadaConHora = (ConfiguracionHistorialRuta)crearPredeterminadaHistorial.Invoke(
|
|
page, new object[] { registroConHora })!;
|
|
Check(predeterminadaConHora.UsarHoraSimulada &&
|
|
predeterminadaConHora.FechaSimulada == "2026-04-08" &&
|
|
predeterminadaConHora.HoraSimulada == "08:46:17" &&
|
|
predeterminadaConHora.FactorEquilibradoAndarBus == 6,
|
|
"Predeterminada conserva solo la fecha y hora simuladas del historial");
|
|
configuracionGuardada.UsarHoraSimulada = false;
|
|
configuracionGuardada.FechaSimulada = "2020-01-01";
|
|
configuracionGuardada.HoraSimulada = "01:02:03";
|
|
var predeterminadaSinHora = (ConfiguracionHistorialRuta)crearPredeterminadaHistorial.Invoke(
|
|
page, new object[] { registroConHora })!;
|
|
Check(!predeterminadaSinHora.UsarHoraSimulada &&
|
|
predeterminadaSinHora.FechaSimulada == DateOnly.FromDateTime(DateTime.Now)
|
|
.ToString("yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture) &&
|
|
predeterminadaSinHora.HoraSimulada != "01:02:03",
|
|
"Predeterminada usa la hora actual cuando el historial no tenia simulacion");
|
|
typeof(PlanificadorRutas).GetField("_factorEquilibradoAndarBus", flags)!.SetValue(page, 6.0);
|
|
typeof(PlanificadorRutas).GetField("_factorPenalizacionDistanciaPie", flags)!.SetValue(page, 1.8);
|
|
typeof(PlanificadorRutas).GetField("_parametroPenalizacionDistanciaPieMetros", flags)!.SetValue(page, 200.0);
|
|
typeof(PlanificadorRutas).GetField("_pesoTransbordoRankingMinutos", flags)!.SetValue(page, 6.0);
|
|
typeof(PlanificadorRutas).GetField("_velocidadBusKmH", flags)!.SetValue(page, 16.0);
|
|
typeof(PlanificadorRutas).GetField("_mostrarElevacionesRuta", flags)!.SetValue(page, true);
|
|
var alternativaDesglose = new AlternativaRuta
|
|
{
|
|
AlternativaBus = new RutaBusAlternativa { DistanciaTotalBus = 6000 },
|
|
DistanciasPiePorTramoAprox = new List<double> { 100, 300 },
|
|
EsfuerzoExtraPendienteMetros = 25,
|
|
NumeroTransbordosReales = 1
|
|
};
|
|
var calcularScore = typeof(PlanificadorRutas).GetMethod("CalcularScoreEquilibradoFactor", flags)!;
|
|
var crearDesglose = typeof(PlanificadorRutas).GetMethod("CrearDesgloseScoreEquilibrado", flags)!;
|
|
var scoreCalculado = (double)calcularScore.Invoke(page, new object?[] { alternativaDesglose })!;
|
|
var desgloseCalculado = crearDesglose.Invoke(page, new object?[] { alternativaDesglose })!;
|
|
var scoreDesglosado = (double)desgloseCalculado.GetType().GetProperty("ScoreTotal")!
|
|
.GetValue(desgloseCalculado)!;
|
|
Check(Math.Abs(scoreCalculado - scoreDesglosado) < 0.0001,
|
|
"El desglose y el ranking usan exactamente el mismo score");
|
|
var formatearEtiqueta = typeof(PlanificadorRutas).GetMethod("FormatearEtiquetaAlternativaTarjeta", flags)!;
|
|
Check((string)formatearEtiqueta.Invoke(page, new object[]
|
|
{ new AlternativaRuta { Etiqueta = "Equilibrada" }, 0 })! == "Opción 1",
|
|
"Las alternativas de bus se muestran numeradas sin etiquetas de criterio");
|
|
Check((string)formatearEtiqueta.Invoke(page, new object[]
|
|
{ new AlternativaRuta { EsSoloAPie = true, Etiqueta = "Solo a pie (v2)" }, 3 })! == "Solo a pie (v2)",
|
|
"Las dos alternativas solo a pie conservan su identificacion");
|
|
var maximoExclusiones = typeof(PlanificadorRutas).GetField(
|
|
"MaximoExclusionesCaminata", BindingFlags.Static | BindingFlags.NonPublic)!;
|
|
Check((int)maximoExclusiones.GetRawConstantValue()! == 25,
|
|
"Las exclusiones quedan limitadas a 25 elementos");
|
|
typeof(PlanificadorRutas).GetField("_configuracionFabrica", flags)!.SetValue(page, defaults.Clonar());
|
|
typeof(PlanificadorRutas).GetField("_activarRangoLineas", flags)!.SetValue(page, true);
|
|
typeof(PlanificadorRutas).GetField("_lineaMin", flags)!.SetValue(page, 40);
|
|
typeof(PlanificadorRutas).GetField("_lineaMax", flags)!.SetValue(page, 40);
|
|
typeof(PlanificadorRutas).GetField("_activarRangoItinerarios", flags)!.SetValue(page, false);
|
|
typeof(PlanificadorRutas).GetField("_itinerarioMin", flags)!.SetValue(page, 7);
|
|
typeof(PlanificadorRutas).GetField("_itinerarioMax", flags)!.SetValue(page, 7);
|
|
typeof(PlanificadorRutas).GetMethod("AplicarValoresFabricaConservandoSeleccion", flags)!.Invoke(page, null);
|
|
Check(!(bool)typeof(PlanificadorRutas).GetField("_activarRangoLineas", flags)!.GetValue(page)! &&
|
|
(int)typeof(PlanificadorRutas).GetField("_lineaMin", flags)!.GetValue(page)! == defaults.LineaMin &&
|
|
(int)typeof(PlanificadorRutas).GetField("_lineaMax", flags)!.GetValue(page)! == defaults.LineaMax &&
|
|
(bool)typeof(PlanificadorRutas).GetField("_activarRangoItinerarios", flags)!.GetValue(page)! &&
|
|
(int)typeof(PlanificadorRutas).GetField("_itinerarioMin", flags)!.GetValue(page)! == defaults.ItinerarioMin &&
|
|
(int)typeof(PlanificadorRutas).GetField("_itinerarioMax", flags)!.GetValue(page)! == defaults.ItinerarioMax,
|
|
"Restaurar aplica tambien los valores de lineas e itinerarios");
|
|
var campoModoHistorial = typeof(PlanificadorRutas).GetField("_modoConfiguracionHistorial", flags)!;
|
|
var modoGuardada = Enum.Parse(campoModoHistorial.FieldType, "Guardada");
|
|
campoModoHistorial.SetValue(page, modoGuardada);
|
|
typeof(PlanificadorRutas).GetMethod("AplicarValoresFabricaConservandoSeleccion", flags)!.Invoke(page, null);
|
|
Check(Equals(campoModoHistorial.GetValue(page), modoGuardada),
|
|
"Restaurar valores conserva el modo de configuracion del historial");
|
|
var options = (List<AlternativaRuta>)typeof(PlanificadorRutas).GetField("_opcionesRuta", flags)!.GetValue(page)!;
|
|
var selected = typeof(PlanificadorRutas).GetField("_indiceAlternativaSeleccionada", flags)!;
|
|
var remember = typeof(PlanificadorRutas).GetMethod("ObtenerProveedorSoloAPieParaRecalculo", flags)!;
|
|
string? Remember(bool keep) => (string?)remember.Invoke(page, new object[] { keep });
|
|
options.Add(new() { EsSoloAPie = true, ProveedorRuta = "v1" });
|
|
options.Add(new() { EsSoloAPie = true, ProveedorRuta = "v2" });
|
|
selected.SetValue(page, 1);
|
|
Check(Remember(true) == "v2", "Se conserva la version andando seleccionada");
|
|
options.Clear();
|
|
Check(Remember(true) == "v2", "Otro cambio de punto durante el calculo conserva v2");
|
|
options.Add(new() { EsSoloAPie = false });
|
|
selected.SetValue(page, 0);
|
|
Check(Remember(true) is null, "Seleccionar bus libera la preferencia andando");
|
|
options.Clear();
|
|
Check(Remember(false) is null, "Nueva seleccion de criterio no hereda la preferencia andando");
|
|
var osmHandler = new OsmWidthHandler();
|
|
using var osmCache = new MemoryCache(new MemoryCacheOptions());
|
|
var osmConfig = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Caminata:Osm:BaseUrl"] = "https://osm.test/api/0.6"
|
|
}).Build();
|
|
var osmClient = new ClienteAtributosOsm(new HttpClient(osmHandler), osmCache, osmConfig);
|
|
var anchos = await osmClient.ObtenerAnchurasAsync([101, 102, 103]);
|
|
Check(anchos[101] == new AnchuraViaOsm("4.5", false) &&
|
|
anchos[102] == new AnchuraViaOsm("2", true) &&
|
|
!anchos.ContainsKey(103),
|
|
"La anchura exacta y la estimada se leen de las etiquetas OSM");
|
|
await osmClient.ObtenerAnchurasAsync([101, 102, 103]);
|
|
Check(osmHandler.Calls == 1, "Las anchuras OSM se reutilizan desde cache");
|
|
var formatearSuperficieSinDatos = typeof(PlanificadorRutas).GetMethod(
|
|
"FormatearSuperficieSegmento", BindingFlags.Static | BindingFlags.NonPublic)!;
|
|
var superficieSinDatos = (string)formatearSuperficieSinDatos.Invoke(null, [null])!;
|
|
Check(superficieSinDatos.Contains("Carriles: -") &&
|
|
superficieSinDatos.Contains("Anchura: -"),
|
|
"El tooltip mantiene todos los atributos cuando no hay datos de via");
|
|
Console.WriteLine("Pruebas completadas.");
|
|
|
|
sealed class FakeValhalla : IClienteValhalla
|
|
{
|
|
public Task<InformacionSueloTramo?> ObtenerInformacionSueloAsync(
|
|
double[,] linea, CancellationToken cancellationToken = default)
|
|
=> Task.FromResult<InformacionSueloTramo?>(null);
|
|
|
|
public int Calls { get; private set; }
|
|
public int Zones { get; private set; }
|
|
public int Points { get; private set; }
|
|
public bool Fail { get; set; }
|
|
public Task<(double[,]? Linea, double DistanciaMetros, double DuracionSegundos)> ObtenerRutaAsync(
|
|
double latitudDesde, double longitudDesde, double latitudHasta, double longitudHasta,
|
|
string costing = "pedestrian", List<List<double[]>>? excludePolygons = null,
|
|
List<double[]>? excludeLocations = null)
|
|
{
|
|
Calls++;
|
|
Zones = excludePolygons?.Count ?? 0;
|
|
Points = excludeLocations?.Count ?? 0;
|
|
return Task.FromResult<(double[,]?, double, double)>(Fail ? (null, 0, 0) :
|
|
(new[,] { { latitudDesde, longitudDesde }, { latitudHasta, longitudHasta } }, 100, 100));
|
|
}
|
|
}
|
|
|
|
sealed class OsmWidthHandler : HttpMessageHandler
|
|
{
|
|
public int Calls { get; private set; }
|
|
|
|
protected override Task<HttpResponseMessage> SendAsync(
|
|
HttpRequestMessage request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
Calls++;
|
|
const string contenido = """
|
|
{
|
|
"elements": [
|
|
{ "type": "way", "id": 101, "tags": { "width": "4.5" } },
|
|
{ "type": "way", "id": 102, "tags": { "est_width": "2" } },
|
|
{ "type": "way", "id": 103, "tags": { "highway": "residential" } }
|
|
]
|
|
}
|
|
""";
|
|
return Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)
|
|
{
|
|
Content = new StringContent(contenido, System.Text.Encoding.UTF8, "application/json")
|
|
});
|
|
}
|
|
}
|
|
|
|
sealed class RetryValhallaHandler : HttpMessageHandler
|
|
{
|
|
public List<string> Bodies { get; } = new();
|
|
|
|
protected override async Task<HttpResponseMessage> SendAsync(
|
|
HttpRequestMessage request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
Bodies.Add(await request.Content!.ReadAsStringAsync(cancellationToken));
|
|
return new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
Content = new StringContent(
|
|
"{\"error_code\":442,\"error\":\"No path could be found for input\"}",
|
|
System.Text.Encoding.UTF8,
|
|
"application/json")
|
|
};
|
|
}
|
|
}
|
|
|
|
sealed class PolygonValidationValhallaHandler(string primeraForma, string segundaForma) : HttpMessageHandler
|
|
{
|
|
public List<string> Bodies { get; } = new();
|
|
|
|
protected override async Task<HttpResponseMessage> SendAsync(
|
|
HttpRequestMessage request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
Bodies.Add(await request.Content!.ReadAsStringAsync(cancellationToken));
|
|
var forma = Bodies.Count == 1 ? primeraForma : segundaForma;
|
|
var contenido = JsonSerializer.Serialize(new
|
|
{
|
|
trip = new
|
|
{
|
|
units = "kilometers",
|
|
summary = new { length = 1.0, time = 600.0 },
|
|
legs = new[] { new { shape = forma } }
|
|
}
|
|
});
|
|
return new HttpResponseMessage(System.Net.HttpStatusCode.OK)
|
|
{
|
|
Content = new StringContent(contenido, System.Text.Encoding.UTF8, "application/json")
|
|
};
|
|
}
|
|
}
|
|
|
|
sealed class PolygonNoPathThenPointSuccessHandler(string formaConflictiva, string formaAlternativa)
|
|
: HttpMessageHandler
|
|
{
|
|
public List<string> Bodies { get; } = new();
|
|
|
|
protected override async Task<HttpResponseMessage> SendAsync(
|
|
HttpRequestMessage request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var cuerpo = await request.Content!.ReadAsStringAsync(cancellationToken);
|
|
Bodies.Add(cuerpo);
|
|
using var documento = JsonDocument.Parse(cuerpo);
|
|
var raiz = documento.RootElement;
|
|
var tienePoligonos = raiz.TryGetProperty("exclude_polygons", out var poligonos) &&
|
|
poligonos.ValueKind == JsonValueKind.Array &&
|
|
poligonos.GetArrayLength() > 0;
|
|
if (tienePoligonos)
|
|
{
|
|
return new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
|
|
{
|
|
Content = new StringContent(
|
|
"{\"error_code\":442,\"error\":\"No path could be found for input\"}",
|
|
System.Text.Encoding.UTF8,
|
|
"application/json")
|
|
};
|
|
}
|
|
|
|
var tienePuntos = raiz.TryGetProperty("exclude_locations", out var puntos) &&
|
|
puntos.ValueKind == JsonValueKind.Array &&
|
|
puntos.GetArrayLength() > 0;
|
|
var contenido = JsonSerializer.Serialize(new
|
|
{
|
|
trip = new
|
|
{
|
|
units = "kilometers",
|
|
summary = new { length = 1.0, time = 600.0 },
|
|
legs = new[] { new { shape = tienePuntos ? formaAlternativa : formaConflictiva } }
|
|
}
|
|
});
|
|
return new HttpResponseMessage(System.Net.HttpStatusCode.OK)
|
|
{
|
|
Content = new StringContent(contenido, System.Text.Encoding.UTF8, "application/json")
|
|
};
|
|
}
|
|
}
|