From 6145fb635a4241ccc7861703ef90c4cbfae7d568 Mon Sep 17 00:00:00 2001 From: Pedro Date: Wed, 29 Jul 2026 14:45:59 +0200 Subject: [PATCH] - andar equilibraod separado del calculo - comienzo de penalizacoin en 200m - centrado de origen --- .../Pages/PlanificadorRutas.Estado.cs | 243 +++++++++++++----- .../Pages/PlanificadorRutas.Historial.cs | 44 +++- .../Pages/PlanificadorRutas.Itinerarios.cs | 2 +- .../Pages/PlanificadorRutas.Persistencia.cs | 9 +- .../Pages/PlanificadorRutas.Renderizado.cs | 4 +- .../Modelos/Planificacion/AlternativaRuta.cs | 1 + .../Modelos/Planificacion/ParadaCandidata.cs | 2 +- .../BuscadorRutasTemporalesDbus.cs | 64 ++++- .../PlanificadorRutasServicio.cs | 107 ++++++-- .../Planificacion/UtilidadesTransitoDbus.cs | 18 +- RutasDBUS/wwwroot/app.js | 11 +- 11 files changed, 404 insertions(+), 101 deletions(-) diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs index 987889a..b31ecd1 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs @@ -48,6 +48,7 @@ public partial class PlanificadorRutas : ComponentBase // CONFIG private const double FactorPenalizacionDistanciaPiePorDefecto = 1.35; + private const double ParametroPenalizacionDistanciaPieMetrosPorDefecto = 200.0; private const double CoeficienteEsfuerzoAlturaPorDefecto = 8.2; private const double UmbralPendienteSubidaRutaPorDefecto = 2.0; private const double UmbralPendienteBajadaRutaPorDefecto = 5.0; @@ -56,12 +57,12 @@ public partial class PlanificadorRutas : ComponentBase private double _velocidadPieKmH = 3.6; private double _factorPenalizacionDistanciaPie = FactorPenalizacionDistanciaPiePorDefecto; - private double _parametroPenalizacionDistanciaPieMetros = 100.0; + private double _parametroPenalizacionDistanciaPieMetros = ParametroPenalizacionDistanciaPieMetrosPorDefecto; private double _coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto; private double _pesoTransbordoRankingMinutos = 4.0; private double _factorEquilibradoAndarBus = 12.0; private string _factorPenalizacionDistanciaPieTextoEdicion = "1.35"; - private string _parametroPenalizacionDistanciaPieTextoEdicion = "100"; + private string _parametroPenalizacionDistanciaPieTextoEdicion = "200"; private string _coeficienteEsfuerzoAlturaTextoEdicion = "8.20"; private string _pesoTransbordoRankingTextoEdicion = "4,0"; private string _factorEquilibradoAndarBusTextoEdicion = "12"; @@ -461,21 +462,47 @@ public partial class PlanificadorRutas : ComponentBase /// - /// Construye paradas for variante. + /// Construye el payload JS de paradas de una variante. /// - private static List BuildStopsDesdeParadas(IEnumerable paradas) + private List BuildStopsForVariante(InfoVariante v) { - var paradasOrdenadas = paradas.ToList(); - var stops = new List(paradasOrdenadas.Count); - for (var indice = 0; indice < paradasOrdenadas.Count; indice++) + if (v?.CodigosParadas is null || v.CodigosParadas.Count == 0) + return new List(); + + return BuildStopsParaRangoVariante(v, 0, v.CodigosParadas.Count - 1); + } + + /// + /// Construye el payload JS de paradas recorridas por un tramo concreto. + /// + private List BuildStopsForTramoBus(TramoBus tramo) + { + if (tramo?.Variante?.CodigosParadas is null || tramo.Variante.CodigosParadas.Count == 0) + return new List(); + + var ultimoIndice = tramo.Variante.CodigosParadas.Count - 1; + var inicio = Math.Clamp(Math.Min(tramo.IndiceInicio, tramo.IndiceFin), 0, ultimoIndice); + var fin = Math.Clamp(Math.Max(tramo.IndiceInicio, tramo.IndiceFin), 0, ultimoIndice); + return BuildStopsParaRangoVariante(tramo.Variante, inicio, fin); + } + + private List BuildStopsParaRangoVariante(InfoVariante variante, int inicio, int fin) + { + var stops = new List(); + var codigosVistos = new HashSet(StringComparer.OrdinalIgnoreCase); + + for (var indice = inicio; indice <= fin; indice++) { - var parada = paradasOrdenadas[indice]; + var codigo = variante.CodigosParadas[indice]; + if (!codigosVistos.Add(codigo) || !_paradaByCode.TryGetValue(codigo, out var parada)) + continue; + stops.Add(new { lat = parada.Latitud, lon = parada.Longitud, code = parada.Codigo, - bearing = CalcularRumboParada(paradasOrdenadas, indice) + bearing = CalcularRumboParada(variante, indice) }); } @@ -483,21 +510,121 @@ public partial class PlanificadorRutas : ComponentBase } /// - /// Calcula el sentido de marcha hacia la siguiente parada; en la terminal conserva el rumbo de llegada. + /// Obtiene la tangente local de la geometria real al salir de la parada. + /// En la terminal usa la tangente de llegada del ultimo trayecto. /// - private static double? CalcularRumboParada(IReadOnlyList paradas, int indice) + private double? CalcularRumboParada(InfoVariante variante, int indiceParada) { - var actual = paradas[indice]; - for (var siguiente = indice + 1; siguiente < paradas.Count; siguiente++) + var idsTrayectos = variante.Variante?.IdsTrayectos; + if (idsTrayectos is not null) { - var rumbo = CalcularRumboGrados(actual, paradas[siguiente]); + if (indiceParada >= 0 && + indiceParada < idsTrayectos.Count && + RedBusServicio.Trayectos.TryGetValue(idsTrayectos[indiceParada], out var trayectoSalida)) + { + var rumboSalida = CalcularRumboGeometriaTrayecto(trayectoSalida.Posiciones, desdeInicio: true); + if (rumboSalida is not null) + return rumboSalida; + } + + var indiceTrayectoLlegada = indiceParada - 1; + if (indiceTrayectoLlegada >= 0 && + indiceTrayectoLlegada < idsTrayectos.Count && + RedBusServicio.Trayectos.TryGetValue(idsTrayectos[indiceTrayectoLlegada], out var trayectoLlegada)) + { + var rumboLlegada = CalcularRumboGeometriaTrayecto(trayectoLlegada.Posiciones, desdeInicio: false); + if (rumboLlegada is not null) + return rumboLlegada; + } + } + + return CalcularRumboParadaPorCoordenadas(variante, indiceParada); + } + + private static double? CalcularRumboGeometriaTrayecto( + IReadOnlyList posiciones, + bool desdeInicio) + { + var puntos = posiciones + .Where(punto => + punto is { Length: >= 2 } && + double.IsFinite(punto[0]) && + double.IsFinite(punto[1])) + .ToList(); + + if (puntos.Count < 2) + return null; + + const double distanciaMinimaRumboMetros = 3.0; + double? rumboCorto = null; + + if (desdeInicio) + { + var origen = puntos[0]; + for (var indice = 1; indice < puntos.Count; indice++) + { + var destino = puntos[indice]; + var rumbo = CalcularRumboGrados(origen[0], origen[1], destino[0], destino[1]); + if (rumbo is null) + continue; + + rumboCorto ??= rumbo; + if (CalcularDistanciaMetros(origen[0], origen[1], destino[0], destino[1]) >= distanciaMinimaRumboMetros) + return rumbo; + } + } + else + { + var destino = puntos[^1]; + for (var indice = puntos.Count - 2; indice >= 0; indice--) + { + var origen = puntos[indice]; + var rumbo = CalcularRumboGrados(origen[0], origen[1], destino[0], destino[1]); + if (rumbo is null) + continue; + + rumboCorto ??= rumbo; + if (CalcularDistanciaMetros(origen[0], origen[1], destino[0], destino[1]) >= distanciaMinimaRumboMetros) + return rumbo; + } + } + + return rumboCorto; + } + + private double? CalcularRumboParadaPorCoordenadas(InfoVariante variante, int indice) + { + if (indice < 0 || + indice >= variante.CodigosParadas.Count || + !_paradaByCode.TryGetValue(variante.CodigosParadas[indice], out var actual)) + { + return null; + } + + for (var siguiente = indice + 1; siguiente < variante.CodigosParadas.Count; siguiente++) + { + if (!_paradaByCode.TryGetValue(variante.CodigosParadas[siguiente], out var paradaSiguiente)) + continue; + + var rumbo = CalcularRumboGrados( + actual.Latitud, + actual.Longitud, + paradaSiguiente.Latitud, + paradaSiguiente.Longitud); if (rumbo is not null) return rumbo; } for (var anterior = indice - 1; anterior >= 0; anterior--) { - var rumbo = CalcularRumboGrados(paradas[anterior], actual); + if (!_paradaByCode.TryGetValue(variante.CodigosParadas[anterior], out var paradaAnterior)) + continue; + + var rumbo = CalcularRumboGrados( + paradaAnterior.Latitud, + paradaAnterior.Longitud, + actual.Latitud, + actual.Longitud); if (rumbo is not null) return rumbo; } @@ -505,19 +632,23 @@ public partial class PlanificadorRutas : ComponentBase return null; } - private static double? CalcularRumboGrados(Parada origen, Parada destino) + private static double? CalcularRumboGrados( + double latitudOrigenGrados, + double longitudOrigenGrados, + double latitudDestinoGrados, + double longitudDestinoGrados) { - if (!double.IsFinite(origen.Latitud) || - !double.IsFinite(origen.Longitud) || - !double.IsFinite(destino.Latitud) || - !double.IsFinite(destino.Longitud)) + if (!double.IsFinite(latitudOrigenGrados) || + !double.IsFinite(longitudOrigenGrados) || + !double.IsFinite(latitudDestinoGrados) || + !double.IsFinite(longitudDestinoGrados)) { return null; } - var latitudOrigen = origen.Latitud * Math.PI / 180.0; - var latitudDestino = destino.Latitud * Math.PI / 180.0; - var diferenciaLongitud = (destino.Longitud - origen.Longitud) * Math.PI / 180.0; + var latitudOrigen = latitudOrigenGrados * Math.PI / 180.0; + var latitudDestino = latitudDestinoGrados * Math.PI / 180.0; + var diferenciaLongitud = (longitudDestinoGrados - longitudOrigenGrados) * Math.PI / 180.0; var y = Math.Sin(diferenciaLongitud) * Math.Cos(latitudDestino); var x = Math.Cos(latitudOrigen) * Math.Sin(latitudDestino) - Math.Sin(latitudOrigen) * Math.Cos(latitudDestino) * Math.Cos(diferenciaLongitud); @@ -529,33 +660,6 @@ public partial class PlanificadorRutas : ComponentBase return (rumbo + 360.0) % 360.0; } - /// - /// Construye las paradas visibles recorridas por una variante completa. - /// - private List BuildParadasForVariante(InfoVariante v) - { - var stops = new List(v?.CodigosParadas?.Count ?? 0); - if (v?.CodigosParadas is null) return stops; - - var codigosVistos = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var cod in v.CodigosParadas) - { - if (!codigosVistos.Add(cod)) - continue; - - if (_paradaByCode.TryGetValue(cod, out var parada)) - stops.Add(parada); - } - - return stops; - } - - /// - /// Construye el payload JS de paradas de una variante. - /// - private List BuildStopsForVariante(InfoVariante v) - => BuildStopsDesdeParadas(BuildParadasForVariante(v)); - /// /// Construye las paradas visibles recorridas por un tramo concreto de bus. /// @@ -1061,14 +1165,16 @@ public partial class PlanificadorRutas : ComponentBase /// /// Ajusta rutas js. /// - private async Task FitRoutesJsAsync(IReadOnlyList? coordenadas = null) + private async Task FitRoutesJsAsync( + IReadOnlyList? coordenadas = null, + double[]? centroPreferido = null) { try { - if (coordenadas is null) + if (coordenadas is null && centroPreferido is null) await JS.InvokeVoidAsync("rt.fitRoutes"); else - await JS.InvokeVoidAsync("rt.fitRoutes", coordenadas); + await JS.InvokeVoidAsync("rt.fitRoutes", coordenadas, centroPreferido); } catch { } } @@ -1267,11 +1373,19 @@ public partial class PlanificadorRutas : ComponentBase var parametros = CrearParametrosPenalizacionActuales(); - var distanciaCorregidaBase = alternativa.EsSoloAPie - ? UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaSoloAPie, parametros) - : UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieInicioAprox, parametros) - + UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieTransbordosAprox, parametros) - + UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieFinAprox, parametros); + IEnumerable distanciasPiePorTramo = alternativa.EsSoloAPie + ? new[] { alternativa.DistanciaSoloAPie } + : alternativa.DistanciasPiePorTramoAprox.Count > 0 + ? alternativa.DistanciasPiePorTramoAprox + : new[] + { + alternativa.DistanciaPieInicioAprox, + alternativa.DistanciaPieTransbordosAprox, + alternativa.DistanciaPieFinAprox + }; + var distanciaCorregidaBase = UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaTotalMetros( + distanciasPiePorTramo, + parametros); var esfuerzoExtra = double.IsFinite(alternativa.EsfuerzoExtraPendienteMetros) ? Math.Max(0, alternativa.EsfuerzoExtraPendienteMetros) : 0; @@ -1391,12 +1505,19 @@ public partial class PlanificadorRutas : ComponentBase alternativa.DistanciaSoloAPie); } + var distanciasPiePorTramo = alternativa.DistanciasPiePorTramoAprox.Count > 0 + ? alternativa.DistanciasPiePorTramoAprox.ToArray() + : new[] + { + alternativa.DistanciaPieInicioAprox, + alternativa.DistanciaPieTransbordosAprox, + alternativa.DistanciaPieFinAprox + }; + return UtilidadesTransitoDbus.CalcularCosteConPenalizacionPieSegundos( duracionReal, parametros, - alternativa.DistanciaPieInicioAprox, - alternativa.DistanciaPieTransbordosAprox, - alternativa.DistanciaPieFinAprox); + distanciasPiePorTramo); } /// diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs index 80e97ae..d0123a5 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs @@ -432,16 +432,29 @@ public partial class PlanificadorRutas if (!await AsegurarHistorialLocalCargadoAsync()) return; - var item = _historialLocal.FirstOrDefault(x => x.Id == id); - if (item is null) + var indiceEliminado = _historialLocal.FindIndex(x => x.Id == id); + if (indiceEliminado < 0) return; - _historialLocal.Remove(item); - - if (_historialLocalSeleccionadoId == id) - _historialLocalSeleccionadoId = string.Empty; + var estabaSeleccionado = _historialLocalSeleccionadoId == id; + _historialLocal.RemoveAt(indiceEliminado); await PersistirHistorialLocalAsync(); + + if (estabaSeleccionado) + { + if (_historialLocal.Count == 0) + { + _historialLocalSeleccionadoId = string.Empty; + } + else + { + var siguiente = _historialLocal[Math.Min(indiceEliminado, _historialLocal.Count - 1)]; + await AplicarRegistroHistorialAsync(siguiente); + SeleccionarHistorialLocal(siguiente.Id); + } + } + MostrarMensajeHistorial("Combinacion eliminada del historial personal."); } @@ -473,6 +486,8 @@ public partial class PlanificadorRutas { try { + var indiceEliminado = _historialCompartido.FindIndex(x => x.Id == id); + var estabaSeleccionado = _historialCompartidoSeleccionadoId == id; var ok = await HistorialRutasCompartidoService.EliminarAsync(id); if (!ok) { @@ -481,9 +496,22 @@ public partial class PlanificadorRutas } await CargarHistorialCompartidoAsync(); + _historialCompartido.RemoveAll(x => x.Id == id); - if (_historialCompartidoSeleccionadoId == id) - _historialCompartidoSeleccionadoId = string.Empty; + if (estabaSeleccionado) + { + if (_historialCompartido.Count == 0) + { + _historialCompartidoSeleccionadoId = string.Empty; + } + else + { + var indiceSiguiente = Math.Clamp(indiceEliminado, 0, _historialCompartido.Count - 1); + var siguiente = _historialCompartido[indiceSiguiente]; + await AplicarRegistroHistorialAsync(siguiente); + SeleccionarHistorialCompartido(siguiente.Id); + } + } MostrarMensajeHistorial("Combinacion eliminada del historial compartido."); } diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs index 37b504c..c4adad0 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs @@ -1204,7 +1204,7 @@ public partial class PlanificadorRutas : ComponentBase private bool _elevPopupMinimizado = false; - private const string _versionApp = "(v-20260727a)"; + private const string _versionApp = "(v-20260728a)"; private double? _paradaElevG; diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs index d44cb6a..13f79b4 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs @@ -192,7 +192,10 @@ public partial class PlanificadorRutas ? FactorPenalizacionDistanciaPiePorDefecto : factor; if (config.ParametroPenalizacionDistanciaPieMetros is { } parametro && double.IsFinite(parametro)) - _parametroPenalizacionDistanciaPieMetros = parametro; + _parametroPenalizacionDistanciaPieMetros = + Math.Abs(parametro - 100.0) < 0.0001 + ? ParametroPenalizacionDistanciaPieMetrosPorDefecto + : parametro; if (config.CoeficienteEsfuerzoAltura is { } coeficienteAltura && double.IsFinite(coeficienteAltura)) _coeficienteEsfuerzoAltura = Math.Abs(coeficienteAltura - 1.6) < 0.0001 || @@ -315,7 +318,7 @@ public partial class PlanificadorRutas _maxTransbordosPermitidos = 3; _factorPenalizacionDistanciaPie = FactorPenalizacionDistanciaPiePorDefecto; - _parametroPenalizacionDistanciaPieMetros = 100.0; + _parametroPenalizacionDistanciaPieMetros = ParametroPenalizacionDistanciaPieMetrosPorDefecto; _coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto; _pesoTransbordoRankingMinutos = 4.0; _factorEquilibradoAndarBus = 12.0; @@ -365,7 +368,7 @@ public partial class PlanificadorRutas _maxTransbordosPermitidos = 3; _factorPenalizacionDistanciaPie = FactorPenalizacionDistanciaPiePorDefecto; - _parametroPenalizacionDistanciaPieMetros = 100.0; + _parametroPenalizacionDistanciaPieMetros = ParametroPenalizacionDistanciaPieMetrosPorDefecto; _coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto; _pesoTransbordoRankingMinutos = 4.0; _factorEquilibradoAndarBus = 12.0; diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs index be8d796..a5949df 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs @@ -463,7 +463,7 @@ public partial class PlanificadorRutas : ComponentBase await CerrarPopupsAsync(); ThrowIfPintadoObsoleto(versionPintado); - await FitRoutesJsAsync(); + await FitRoutesJsAsync(centroPreferido: _coordenadasOrigen); ThrowIfPintadoObsoleto(versionPintado); StateHasChanged(); } @@ -505,7 +505,7 @@ public partial class PlanificadorRutas : ComponentBase if (paradasTramo.Count == 0) continue; - var stops = BuildStopsDesdeParadas(paradasTramo); + var stops = BuildStopsForTramoBus(tramo); var id = $"route_stops_{indice}_{tramo.Variante.Linea.Codigo}_{tramo.Variante.CodigoItinerario}_{NormalizarSentidoCodigo(tramo.Variante.Sentido?.Codigo)}"; var color = ColorCssDesdeVariante(tramo.Variante); diff --git a/RutasDBUS/Modelos/Planificacion/AlternativaRuta.cs b/RutasDBUS/Modelos/Planificacion/AlternativaRuta.cs index 4c0a400..61d0268 100644 --- a/RutasDBUS/Modelos/Planificacion/AlternativaRuta.cs +++ b/RutasDBUS/Modelos/Planificacion/AlternativaRuta.cs @@ -20,6 +20,7 @@ public sealed class AlternativaRuta public double DistanciaPieFinAprox { get; set; } public double DistanciaPieTotalAprox => DistanciaPieInicioAprox + DistanciaPieTransbordosAprox + DistanciaPieFinAprox; + public List DistanciasPiePorTramoAprox { get; set; } = new(); public List LineasPieCalculoElevacion { get; set; } = new(); public double EsfuerzoExtraPendienteMetros { get; set; } diff --git a/RutasDBUS/Modelos/Planificacion/ParadaCandidata.cs b/RutasDBUS/Modelos/Planificacion/ParadaCandidata.cs index bf90651..9c77554 100644 --- a/RutasDBUS/Modelos/Planificacion/ParadaCandidata.cs +++ b/RutasDBUS/Modelos/Planificacion/ParadaCandidata.cs @@ -24,7 +24,7 @@ public sealed class ParametrosPlanificadorRuta public int MargenTransbordoSegundos { get; set; } = 120; public int SegundosPorParadaBus { get; set; } = 30; public double FactorPenalizacionDistanciaPie { get; set; } = 1.35; - public double ParametroPenalizacionDistanciaPieMetros { get; set; } = 100.0; + public double ParametroPenalizacionDistanciaPieMetros { get; set; } = 200.0; public double PesoTransbordoRankingMinutos { get; set; } = 4.0; public double FactorEquilibradoAndarBus { get; set; } = 12.0; diff --git a/RutasDBUS/Servicios/Planificacion/BuscadorRutasTemporalesDbus.cs b/RutasDBUS/Servicios/Planificacion/BuscadorRutasTemporalesDbus.cs index 5efe45a..568de3d 100644 --- a/RutasDBUS/Servicios/Planificacion/BuscadorRutasTemporalesDbus.cs +++ b/RutasDBUS/Servicios/Planificacion/BuscadorRutasTemporalesDbus.cs @@ -714,13 +714,16 @@ public sealed class BuscadorRutasTemporalesDbus }; var duracionEstimada = Math.Max(0, (estadoDestino.Llegada - instanteSalida).TotalSeconds) + destino.TiempoAPie; + var distanciasPieTransbordos = ObtenerDistanciasPieTransbordos(estadoDestino); + var distanciasPiePorTramo = ConstruirDistanciasPiePorTramo( + origen.DistanciaAPie, + distanciasPieTransbordos, + destino.DistanciaAPie); var puntuacionCoste = UtilidadesTransitoDbus.CalcularCosteConPenalizacionesRankingSegundos( duracionEstimada, parametros, numeroTransbordos, - origen.DistanciaAPie, - estadoDestino.DistanciaPieTransbordos, - destino.DistanciaAPie); + distanciasPiePorTramo.ToArray()); var resultado = new ResultadoBusquedaRutaBusDbus { @@ -731,6 +734,7 @@ public sealed class BuscadorRutasTemporalesDbus DistanciaPieFin = destino.DistanciaAPie, TiempoPieFin = destino.TiempoAPie, DistanciaPieTransbordos = estadoDestino.DistanciaPieTransbordos, + DistanciasPieTransbordosPorTramo = distanciasPieTransbordos, TiempoEsperaInicialSegundos = estadoDestino.TiempoEsperaInicialSegundos, TiempoEsperaTransbordosSegundos = estadoDestino.TiempoEsperaTransbordosSegundos, DuracionEstimadaSegundos = duracionEstimada, @@ -816,10 +820,12 @@ public sealed class BuscadorRutasTemporalesDbus ParametrosPlanificadorRuta parametros) { var distanciaBus = Math.Max(0, resultado.RutaBus.DistanciaTotalBus); - var distanciaAndando = - UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieInicio, parametros) - + UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieTransbordos, parametros) - + UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieFin, parametros); + var distanciaAndando = UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaTotalMetros( + ConstruirDistanciasPiePorTramo( + resultado.DistanciaPieInicio, + resultado.DistanciasPieTransbordosPorTramo, + resultado.DistanciaPieFin), + parametros); var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) ? Math.Max(0, parametros.FactorEquilibradoAndarBus) : 12.0; @@ -830,6 +836,45 @@ public sealed class BuscadorRutasTemporalesDbus : double.PositiveInfinity; } + private static List ObtenerDistanciasPieTransbordos(EstadoBusquedaParadaDbus estadoDestino) + { + var distancias = new List(); + var cursor = estadoDestino; + while (cursor is not null) + { + if (cursor.Movimiento is MovimientoPieTransbordoDbus movimiento && + double.IsFinite(movimiento.DistanciaMetros) && + movimiento.DistanciaMetros > 0) + { + distancias.Add(movimiento.DistanciaMetros); + } + + cursor = cursor.EstadoAnterior; + } + + distancias.Reverse(); + return distancias; + } + + private static List ConstruirDistanciasPiePorTramo( + double distanciaInicio, + IEnumerable distanciasTransbordos, + double distanciaFin) + { + var distancias = new List(); + AgregarDistancia(distanciaInicio); + foreach (var distancia in distanciasTransbordos) + AgregarDistancia(distancia); + AgregarDistancia(distanciaFin); + return distancias; + + void AgregarDistancia(double distancia) + { + if (double.IsFinite(distancia) && distancia > 0) + distancias.Add(distancia); + } + } + private static double CalcularDistanciaBusEstado(EstadoBusquedaParadaDbus estadoDestino) { var distancia = 0.0; @@ -1345,6 +1390,11 @@ public sealed class ResultadoBusquedaRutaBusDbus /// public double DistanciaPieTransbordos { get; init; } + /// + /// Distancias peatonales de cada transbordo, sin agregarlas antes de penalizarlas. + /// + public IReadOnlyList DistanciasPieTransbordosPorTramo { get; init; } = Array.Empty(); + /// /// Espera inicial acumulada. /// diff --git a/RutasDBUS/Servicios/Planificacion/PlanificadorRutasServicio.cs b/RutasDBUS/Servicios/Planificacion/PlanificadorRutasServicio.cs index 69f2e93..2544c46 100644 --- a/RutasDBUS/Servicios/Planificacion/PlanificadorRutasServicio.cs +++ b/RutasDBUS/Servicios/Planificacion/PlanificadorRutasServicio.cs @@ -295,6 +295,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas DistanciaPieInicioAprox = candidataBus.DistanciaPieInicio, DistanciaPieFinAprox = candidataBus.DistanciaPieFin, DistanciaPieTransbordosAprox = candidataBus.DistanciaPieTransbordos, + DistanciasPiePorTramoAprox = ConstruirDistanciasPiePorTramo(candidataBus), TiempoPieInicio = candidataBus.TiempoPieInicio, TiempoPieFin = candidataBus.TiempoPieFin, TiempoEsperaInicialSegundos = candidataBus.TiempoEsperaInicialSegundos, @@ -992,6 +993,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas DistanciaPieFin = destino.DistanciaAPie, TiempoPieFin = destino.TiempoAPie, DistanciaPieTransbordos = transferencia.DistanciaMetros, + DistanciasPieTransbordosPorTramo = new[] { transferencia.DistanciaMetros }, TiempoEsperaInicialSegundos = esperaInicial, TiempoEsperaTransbordosSegundos = esperaTotalTransbordos, DuracionEstimadaSegundos = duracionTotal, @@ -1343,6 +1345,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas DistanciaPieFin = baseRuta.DistanciaPieFin, TiempoPieFin = baseRuta.TiempoPieFin, DistanciaPieTransbordos = baseRuta.DistanciaPieTransbordos, + DistanciasPieTransbordosPorTramo = baseRuta.DistanciasPieTransbordosPorTramo.ToArray(), TiempoEsperaInicialSegundos = esperaInicial, TiempoEsperaTransbordosSegundos = baseRuta.TiempoEsperaTransbordosSegundos, DuracionEstimadaSegundos = duracionTotal, @@ -1350,9 +1353,10 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas duracionTotal, parametros, numeroTransbordos, - origen.DistanciaAPie, - baseRuta.DistanciaPieTransbordos, - baseRuta.DistanciaPieFin), + ConstruirDistanciasPiePorTramo( + origen.DistanciaAPie, + ObtenerDistanciasPieTransbordosPorTramo(baseRuta), + baseRuta.DistanciaPieFin).ToArray()), RutaBus = ruta }; @@ -1518,9 +1522,9 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas var distanciaAndando = parametros is null ? Math.Max(0, resultado.DistanciaPieTotal) - : UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieInicio, parametros) - + UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieTransbordos, parametros) - + UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieFin, parametros); + : UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaTotalMetros( + ConstruirDistanciasPiePorTramo(resultado), + parametros); var factor = parametros is not null && double.IsFinite(parametros.FactorEquilibradoAndarBus) ? Math.Max(0, parametros.FactorEquilibradoAndarBus) : 12.0; @@ -1531,6 +1535,42 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas : double.PositiveInfinity; } + private static List ConstruirDistanciasPiePorTramo(ResultadoBusquedaRutaBusDbus resultado) + => ConstruirDistanciasPiePorTramo( + resultado.DistanciaPieInicio, + ObtenerDistanciasPieTransbordosPorTramo(resultado), + resultado.DistanciaPieFin); + + private static IReadOnlyList ObtenerDistanciasPieTransbordosPorTramo( + ResultadoBusquedaRutaBusDbus resultado) + { + if (resultado.DistanciasPieTransbordosPorTramo.Count > 0) + return resultado.DistanciasPieTransbordosPorTramo; + + return double.IsFinite(resultado.DistanciaPieTransbordos) && resultado.DistanciaPieTransbordos > 0 + ? new[] { resultado.DistanciaPieTransbordos } + : Array.Empty(); + } + + private static List ConstruirDistanciasPiePorTramo( + double distanciaInicio, + IEnumerable distanciasTransbordos, + double distanciaFin) + { + var distancias = new List(); + AgregarDistancia(distanciaInicio); + foreach (var distancia in distanciasTransbordos) + AgregarDistancia(distancia); + AgregarDistancia(distanciaFin); + return distancias; + + void AgregarDistancia(double distancia) + { + if (double.IsFinite(distancia) && distancia > 0) + distancias.Add(distancia); + } + } + private static bool DebeBuscarTransbordosRobustos( IReadOnlyList candidatasBus, IReadOnlyList candidatasOrigen, @@ -2033,6 +2073,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas ProveedorRuta = "v1", LineaSoloAPie = osrm.linea, LineasPieCalculoElevacion = new List { osrm.linea }, + DistanciasPiePorTramoAprox = new List { osrm.distancia }, DistanciaSoloAPie = osrm.distancia, DuracionSoloAPie = duracion, DuracionTotalAproxSegundos = duracion, @@ -2052,6 +2093,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas ProveedorRuta = "v2", LineaSoloAPie = valhalla.linea, LineasPieCalculoElevacion = new List { valhalla.linea }, + DistanciasPiePorTramoAprox = new List { valhalla.distancia }, DistanciaSoloAPie = valhalla.distancia, DuracionSoloAPie = duracion, DuracionTotalAproxSegundos = duracion, @@ -2091,6 +2133,23 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas }); } + private static void AgregarDistanciaPiePorTramo(AlternativaRuta alternativa, double distanciaMetros) + { + if (double.IsFinite(distanciaMetros) && distanciaMetros > 0) + alternativa.DistanciasPiePorTramoAprox.Add(distanciaMetros); + } + + private static void AcumularDistanciaPieEnPrimerTramo(AlternativaRuta alternativa, double distanciaMetros) + { + if (!double.IsFinite(distanciaMetros) || distanciaMetros <= 0) + return; + + if (alternativa.DistanciasPiePorTramoAprox.Count == 0) + alternativa.DistanciasPiePorTramoAprox.Add(distanciaMetros); + else + alternativa.DistanciasPiePorTramoAprox[0] += distanciaMetros; + } + /// /// Recalcula una alternativa de bus usando caminatas exactas y horarios reales. /// @@ -2105,6 +2164,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas return double.PositiveInfinity; alternativa.LineasPieCalculoElevacion.Clear(); + alternativa.DistanciasPiePorTramoAprox.Clear(); alternativa.EsfuerzoExtraPendienteMetros = 0; var tramosBus = alternativa.AlternativaBus.Tramos; @@ -2155,6 +2215,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas alternativa.DistanciaPieInicioAprox = acceso.distancia > 0 ? acceso.distancia : UtilidadesTransitoDbus.CalcularDistanciaMetros(origenLatLon[0], origenLatLon[1], paradaInicioBus.Latitud, paradaInicioBus.Longitud); + AgregarDistanciaPiePorTramo(alternativa, alternativa.DistanciaPieInicioAprox); alternativa.TiempoPieInicio = UtilidadesTransitoDbus.EstimarDuracionPieSegundos(alternativa.DistanciaPieInicioAprox, parametros); total += alternativa.TiempoPieInicio; cursor = UtilidadesTransitoDbus.AddSecondsSafe(cursor, alternativa.TiempoPieInicio); @@ -2229,10 +2290,12 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas { alternativa.DistanciaPieInicioAprox += distanciaTransferencia; alternativa.TiempoPieInicio += UtilidadesTransitoDbus.EstimarDuracionPieSegundos(distanciaTransferencia, parametros); + AcumularDistanciaPieEnPrimerTramo(alternativa, distanciaTransferencia); } else { alternativa.DistanciaPieTransbordosAprox += distanciaTransferencia; + AgregarDistanciaPiePorTramo(alternativa, distanciaTransferencia); } var duracionTransferencia = UtilidadesTransitoDbus.EstimarDuracionPieSegundos(distanciaTransferencia, parametros); @@ -2345,6 +2408,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas alternativa.DistanciaPieFinAprox = salidaFinal.distancia > 0 ? salidaFinal.distancia : UtilidadesTransitoDbus.CalcularDistanciaMetros(puntoUltimaLlegada[0], puntoUltimaLlegada[1], destinoLatLon[0], destinoLatLon[1]); + AgregarDistanciaPiePorTramo(alternativa, alternativa.DistanciaPieFinAprox); alternativa.TiempoPieFin = UtilidadesTransitoDbus.EstimarDuracionPieSegundos(alternativa.DistanciaPieFinAprox, parametros); total += alternativa.TiempoPieFin; @@ -2462,6 +2526,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas DistanciaPieFin = candidataBase.DistanciaPieFin, TiempoPieFin = candidataBase.TiempoPieFin, DistanciaPieTransbordos = candidataBase.DistanciaPieTransbordos, + DistanciasPieTransbordosPorTramo = candidataBase.DistanciasPieTransbordosPorTramo.ToArray(), TiempoEsperaInicialSegundos = esperaInicial, TiempoEsperaTransbordosSegundos = candidataBase.TiempoEsperaTransbordosSegundos, DuracionEstimadaSegundos = duracionEstimada, @@ -2469,9 +2534,10 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas duracionEstimada, parametros, rutaDirecta.NumeroTransbordos, - origenDirecto.DistanciaAPie, - candidataBase.DistanciaPieTransbordos, - candidataBase.DistanciaPieFin), + ConstruirDistanciasPiePorTramo( + origenDirecto.DistanciaAPie, + ObtenerDistanciasPieTransbordosPorTramo(candidataBase), + candidataBase.DistanciaPieFin).ToArray()), RutaBus = rutaDirecta }; @@ -3156,9 +3222,22 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas private static double CalcularDistanciaAndandoCorregidaMetros( AlternativaRuta alternativa, ParametrosPlanificadorRuta parametros) - => UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieInicioAprox, parametros) - + UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieTransbordosAprox, parametros) - + UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieFinAprox, parametros); + => UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaTotalMetros( + ObtenerDistanciasPiePorTramo(alternativa), + parametros); + + private static IReadOnlyList ObtenerDistanciasPiePorTramo(AlternativaRuta alternativa) + { + if (alternativa.DistanciasPiePorTramoAprox.Count > 0) + return alternativa.DistanciasPiePorTramoAprox; + + return ConstruirDistanciasPiePorTramo( + alternativa.DistanciaPieInicioAprox, + alternativa.DistanciaPieTransbordosAprox > 0 + ? new[] { alternativa.DistanciaPieTransbordosAprox } + : Array.Empty(), + alternativa.DistanciaPieFinAprox); + } private static double DuracionVisibleOrdenacion(AlternativaRuta alternativa) => double.IsFinite(alternativa.DuracionTotalAproxSegundos) @@ -3355,9 +3434,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas alternativa.DuracionTotalAproxSegundos, parametros, alternativa.NumeroTransbordosReales, - alternativa.DistanciaPieInicioAprox, - alternativa.DistanciaPieTransbordosAprox, - alternativa.DistanciaPieFinAprox); + ObtenerDistanciasPiePorTramo(alternativa).ToArray()); } /// diff --git a/RutasDBUS/Servicios/Planificacion/UtilidadesTransitoDbus.cs b/RutasDBUS/Servicios/Planificacion/UtilidadesTransitoDbus.cs index d0a3f82..f0065fe 100644 --- a/RutasDBUS/Servicios/Planificacion/UtilidadesTransitoDbus.cs +++ b/RutasDBUS/Servicios/Planificacion/UtilidadesTransitoDbus.cs @@ -316,7 +316,7 @@ internal static class UtilidadesTransitoDbus : 1.35; var parametroBaseMetros = double.IsFinite(parametros.ParametroPenalizacionDistanciaPieMetros) ? Math.Abs(parametros.ParametroPenalizacionDistanciaPieMetros) - : 100.0; + : 200.0; if (parametroBaseMetros <= 0) parametroBaseMetros = 1.0; @@ -325,6 +325,22 @@ internal static class UtilidadesTransitoDbus return Math.Max(distanciaMetros, corregida); } + /// + /// Aplica la correccion a cada tramo peatonal por separado y suma los resultados. + /// + public static double CalcularDistanciaPieCorregidaTotalMetros( + IEnumerable distanciasPieMetros, + ParametrosPlanificadorRuta parametros) + { + var total = 0.0; + foreach (var distancia in distanciasPieMetros) + total += CalcularDistanciaPieCorregidaMetros(distancia, parametros); + + return double.IsFinite(total) && total >= 0 + ? total + : double.PositiveInfinity; + } + /// /// Calcula un coste interno que penaliza de forma progresiva la caminata sin cambiar el tiempo real. /// diff --git a/RutasDBUS/wwwroot/app.js b/RutasDBUS/wwwroot/app.js index 94a79f9..8496900 100644 --- a/RutasDBUS/wwwroot/app.js +++ b/RutasDBUS/wwwroot/app.js @@ -322,7 +322,7 @@ window.rt.drawRoute = function (id, coords, options) { } }; -window.rt.fitRoutes = function (coords) { +window.rt.fitRoutes = function (coords, preferredCenter) { const map = window.__rtLeafletMap; if (!map || !window.L) return false; @@ -364,7 +364,14 @@ window.rt.fitRoutes = function (coords) { const geometryBounds = window.L.latLngBounds(points); if (!geometryBounds.isValid()) return false; - map.panTo(geometryBounds.getCenter(), { + const center = Array.isArray(preferredCenter) + && preferredCenter.length >= 2 + && Number.isFinite(Number(preferredCenter[0])) + && Number.isFinite(Number(preferredCenter[1])) + ? window.L.latLng(Number(preferredCenter[0]), Number(preferredCenter[1])) + : geometryBounds.getCenter(); + + map.panTo(center, { animate: true, duration: 0.35 });