From 49c5d23ace97624e344e9064072f1e45a2b36e58 Mon Sep 17 00:00:00 2001 From: Pedro Date: Wed, 26 Aug 2026 08:09:43 +0200 Subject: [PATCH] =?UTF-8?q?Restricciones=20de=20seleccion=20de=20paradas.?= =?UTF-8?q?=20se=C3=B1al=20de=20dereivacion=20de=20historial.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit importante: cambiod e formula, desaparece distancia equilibrada y el factor pasa a dividir a la distancia en bus --- .../Pages/PlanificadorRutas.Estado.cs | 34 ++--- .../Pages/PlanificadorRutas.Historial.cs | 119 ++++++++++++++++- .../Pages/PlanificadorRutas.Itinerarios.cs | 2 +- .../Pages/PlanificadorRutas.Persistencia.cs | 6 +- .../Pages/PlanificadorRutas.Planificacion.cs | 120 +++++++++++++++++- .../Pages/PlanificadorRutas.Renderizado.cs | 2 + .../Components/Pages/PlanificadorRutas.razor | 43 +++++-- .../Modelos/Planificacion/ParadaCandidata.cs | 1 + .../ServicioHistorialRutasCompartido.cs | 6 +- .../BuscadorRutasTemporalesDbus.cs | 7 +- .../PlanificadorRutasServicio.cs | 22 ++-- RutasDBUS/wwwroot/app.css | 18 +++ 12 files changed, 317 insertions(+), 63 deletions(-) diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs index 9bf6988..c98ce10 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs @@ -151,7 +151,7 @@ public partial class PlanificadorRutas : ComponentBase } /// - /// Expone el factor que convierte metros andando a metros equivalentes de bus en el modo equilibrado. + /// Expone el factor que reduce el peso de la distancia en bus en el modo equilibrado. /// private string FactorEquilibradoAndarBusTexto { @@ -164,8 +164,8 @@ public partial class PlanificadorRutas : ComponentBase if (!double.TryParse(texto, NumberStyles.Float, CultureInfo.InvariantCulture, out var valor)) return; - if (double.IsFinite(valor)) - _factorEquilibradoAndarBus = Math.Max(0, valor); + if (double.IsFinite(valor) && valor > 0) + _factorEquilibradoAndarBus = valor; } } @@ -1420,25 +1420,6 @@ public partial class PlanificadorRutas : ComponentBase return EstimarDuracionPieSegundos(distanciaCorregida); } - /// - /// Calcula la aportacion de todos los tramos andando a la distancia equilibrada. - /// - private double CalcularDistanciaEquilibradaAndandoAlternativa(AlternativaRuta? alternativa) - { - if (alternativa is null) - return 0; - - var distanciaAndando = CalcularDistanciaCorregidaAlternativa(alternativa); - var factor = double.IsFinite(_factorEquilibradoAndarBus) - ? Math.Max(0, _factorEquilibradoAndarBus) - : FactorEquilibradoAndarBusPorDefecto; - var distanciaEquilibrada = Math.Max(0, distanciaAndando) * factor; - - return double.IsFinite(distanciaEquilibrada) - ? distanciaEquilibrada - : 0; - } - private double CalcularScoreEquilibradoFactor(AlternativaRuta? alternativa) { if (alternativa is null) @@ -1453,11 +1434,14 @@ public partial class PlanificadorRutas : ComponentBase distanciaBus = Math.Max(0, alternativa.AlternativaBus.DistanciaTotalBus); } - var distanciaEquilibradaAndando = CalcularDistanciaEquilibradaAndandoAlternativa(alternativa); + var distanciaAndando = CalcularDistanciaCorregidaAlternativa(alternativa); + var factor = double.IsFinite(_factorEquilibradoAndarBus) && _factorEquilibradoAndarBus > 0 + ? _factorEquilibradoAndarBus + : FactorEquilibradoAndarBusPorDefecto; var penalizacionTransbordos = UtilidadesTransitoDbus.CalcularPenalizacionTransbordosRankingMetros( alternativa.NumeroTransbordosReales, CrearParametrosPenalizacionActuales()); - var score = distanciaBus + distanciaEquilibradaAndando + penalizacionTransbordos; + var score = distanciaAndando + (distanciaBus / factor) + penalizacionTransbordos; return double.IsFinite(score) && score >= 0 ? score : double.PositiveInfinity; @@ -1469,7 +1453,7 @@ public partial class PlanificadorRutas : ComponentBase if (!double.IsFinite(score)) return string.Empty; - return (score / 1000.0).ToString("0.000", CultureInfo.InvariantCulture); + return score.ToString("0", CultureInfo.InvariantCulture); } private async Task RecalcularRutaEquilibradaTrasCambioFactorAsync() diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs index e368a96..fc18127 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs @@ -27,6 +27,9 @@ public partial class PlanificadorRutas private CancellationTokenSource? _historialCompartidoCts; private string _historialLocalSeleccionadoId = string.Empty; private string _historialCompartidoSeleccionadoId = string.Empty; + private TipoReferenciaHistorial _tipoReferenciaHistorial; + private string _historialReferenciaId = string.Empty; + private bool _rutaActualDerivadaDeHistorial; private string _mensajeHistorial = string.Empty; private bool _mensajeHistorialEsError = false; private bool _mostrarHistorialRutas = false; @@ -43,6 +46,13 @@ public partial class PlanificadorRutas private string _resumenErrorHistorialLocal = string.Empty; private string _detalleErrorHistorialLocal = string.Empty; + private enum TipoReferenciaHistorial + { + Ninguna, + Personal, + Compartido + } + private sealed class ResultadoStorageNavegador { [JsonPropertyName("ok")] @@ -400,6 +410,11 @@ public partial class PlanificadorRutas _historialLocal.AddRange(items); if (!_historialLocal.Any(x => x.Id == _historialLocalSeleccionadoId)) _historialLocalSeleccionadoId = string.Empty; + if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Personal && + !_historialLocal.Any(x => x.Id == _historialReferenciaId)) + { + LimpiarReferenciaHistorial(); + } _historialLocalCargado = true; } @@ -632,11 +647,18 @@ public partial class PlanificadorRutas if (_historialCompartido.Count == 0) { _historialCompartidoSeleccionadoId = string.Empty; + if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido) + LimpiarReferenciaHistorial(); return; } if (!_historialCompartido.Any(x => x.Id == _historialCompartidoSeleccionadoId)) _historialCompartidoSeleccionadoId = string.Empty; + if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido && + !_historialCompartido.Any(x => x.Id == _historialReferenciaId)) + { + LimpiarReferenciaHistorial(); + } } catch (Exception) { @@ -783,12 +805,14 @@ public partial class PlanificadorRutas { _historialLocalSeleccionadoId = id; _historialCompartidoSeleccionadoId = string.Empty; + EstablecerReferenciaHistorial(TipoReferenciaHistorial.Personal, id); } private void SeleccionarHistorialCompartido(string id) { _historialLocalSeleccionadoId = string.Empty; _historialCompartidoSeleccionadoId = id; + EstablecerReferenciaHistorial(TipoReferenciaHistorial.Compartido, id); } private void LimpiarSeleccionHistorial() @@ -797,6 +821,74 @@ public partial class PlanificadorRutas _historialCompartidoSeleccionadoId = string.Empty; } + private bool PrepararDerivacionHistorialParaRecalculo() + { + var procedeDeHistorial = _tipoReferenciaHistorial != TipoReferenciaHistorial.Ninguna && + !string.IsNullOrWhiteSpace(_historialReferenciaId); + + _rutaActualDerivadaDeHistorial = false; + LimpiarSeleccionHistorial(); + return procedeDeHistorial; + } + + private void ConfirmarDerivacionHistorial() + { + _rutaActualDerivadaDeHistorial = ExisteReferenciaHistorialActual(); + } + + private void EstablecerReferenciaHistorial(TipoReferenciaHistorial tipo, string id) + { + _tipoReferenciaHistorial = tipo; + _historialReferenciaId = id; + _rutaActualDerivadaDeHistorial = false; + } + + private void LimpiarReferenciaHistorial() + { + _tipoReferenciaHistorial = TipoReferenciaHistorial.Ninguna; + _historialReferenciaId = string.Empty; + _rutaActualDerivadaDeHistorial = false; + } + + private bool ExisteReferenciaHistorialActual() + => _tipoReferenciaHistorial switch + { + TipoReferenciaHistorial.Personal => + _historialLocal.Any(x => x.Id == _historialReferenciaId), + TipoReferenciaHistorial.Compartido => + _historialCompartido.Any(x => x.Id == _historialReferenciaId), + _ => false + }; + + private bool EsReferenciaDerivadaHistorialPersonal(string id) + => _rutaActualDerivadaDeHistorial && + _tipoReferenciaHistorial == TipoReferenciaHistorial.Personal && + string.Equals(_historialReferenciaId, id, StringComparison.OrdinalIgnoreCase); + + private bool EsReferenciaDerivadaHistorialCompartido(string id) + => _rutaActualDerivadaDeHistorial && + _tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido && + string.Equals(_historialReferenciaId, id, StringComparison.OrdinalIgnoreCase); + + private string TextoReferenciaHistorialDerivado() + { + if (!_rutaActualDerivadaDeHistorial) + return string.Empty; + + var (nombre, indice) = _tipoReferenciaHistorial switch + { + TipoReferenciaHistorial.Personal => + ("Personal", _historialLocal.FindIndex(x => x.Id == _historialReferenciaId)), + TipoReferenciaHistorial.Compartido => + ("Compartido", _historialCompartido.FindIndex(x => x.Id == _historialReferenciaId)), + _ => (string.Empty, -1) + }; + + return indice >= 0 + ? $"Ruta actual derivada del Historial {nombre} n.º {indice + 1}." + : string.Empty; + } + private async Task CompartirEscenarioActualAsync() { var actual = ConstruirRegistroHistorialActual() @@ -854,6 +946,11 @@ public partial class PlanificadorRutas var estabaSeleccionado = _historialLocalSeleccionadoId == id; _historialLocal.RemoveAt(indiceEliminado); + if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Personal && + string.Equals(_historialReferenciaId, id, StringComparison.OrdinalIgnoreCase)) + { + LimpiarReferenciaHistorial(); + } await PersistirHistorialLocalAsync(); @@ -924,6 +1021,11 @@ public partial class PlanificadorRutas await CargarHistorialCompartidoAsync(); _historialCompartido.RemoveAll(x => x.Id == id); + if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido && + string.Equals(_historialReferenciaId, id, StringComparison.OrdinalIgnoreCase)) + { + LimpiarReferenciaHistorial(); + } if (estabaSeleccionado) { @@ -973,9 +1075,11 @@ public partial class PlanificadorRutas _criterioPrioritarioRuta = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta); SincronizarPreferenciaDesdeCriterioRuta(); - if (item.FactorEquilibradoAndarBus is { } factorEquilibrado && double.IsFinite(factorEquilibrado)) + if (item.FactorEquilibradoAndarBus is { } factorEquilibrado && + double.IsFinite(factorEquilibrado) && + factorEquilibrado > 0) { - _factorEquilibradoAndarBus = Math.Max(0, factorEquilibrado); + _factorEquilibradoAndarBus = factorEquilibrado; SincronizarTextosPenalizacionPie(); if (_configuracionTemporalPreviaHistorial is not null) @@ -1063,8 +1167,9 @@ public partial class PlanificadorRutas HoraSimulada = TimeOnly.FromDateTime(ahora).ToString("HH:mm:ss", CultureInfo.InvariantCulture), CriterioPrioritarioRuta = _criterioPrioritarioRuta, PreferenciaEtiquetaRuta = _preferenciaEtiquetaRuta, - FactorEquilibradoAndarBus = double.IsFinite(_factorEquilibradoAndarBus) - ? Math.Max(0, _factorEquilibradoAndarBus) + FactorEquilibradoAndarBus = double.IsFinite(_factorEquilibradoAndarBus) && + _factorEquilibradoAndarBus > 0 + ? _factorEquilibradoAndarBus : FactorEquilibradoAndarBusPorDefecto, ActivarFiltroLineas = _activarRangoLineas, LineasPermitidas = ObtenerLineasPermitidasSeleccionadas().ToList(), @@ -1194,8 +1299,10 @@ public partial class PlanificadorRutas var fecha = item.UsarHoraSimulada ? item.FechaSimulada?.Trim() ?? string.Empty : string.Empty; var hora = item.UsarHoraSimulada ? item.HoraSimulada?.Trim() ?? string.Empty : string.Empty; var criterio = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta); - var factorEquilibrado = item.FactorEquilibradoAndarBus is { } factor && double.IsFinite(factor) - ? Math.Max(0, factor).ToString("0.######", CultureInfo.InvariantCulture) + var factorEquilibrado = item.FactorEquilibradoAndarBus is { } factor && + double.IsFinite(factor) && + factor > 0 + ? factor.ToString("0.######", CultureInfo.InvariantCulture) : string.Empty; var lineas = item.ActivarFiltroLineas ? string.Join(",", NormalizarLineasHistorial(item.LineasPermitidas)) diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs index b160338..d726c85 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs @@ -1202,7 +1202,7 @@ public partial class PlanificadorRutas : ComponentBase private bool _elevPopupMinimizado = false; - private const string _versionApp = "(v-20260807b)"; + private const string _versionApp = "(v-20260825b)"; private double? _paradaElevIdee; diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs index 1bffe95..b65f8fc 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs @@ -227,11 +227,13 @@ public partial class PlanificadorRutas : coeficienteAltura; if (config.PesoTransbordoRankingMinutos is { } pesoTransbordo && double.IsFinite(pesoTransbordo)) _pesoTransbordoRankingMinutos = Math.Abs(pesoTransbordo - 8.0) < 0.0001 ? 4.0 : Math.Max(0, pesoTransbordo); - if (config.FactorEquilibradoAndarBus is { } factorEquilibrado && double.IsFinite(factorEquilibrado)) + if (config.FactorEquilibradoAndarBus is { } factorEquilibrado && + double.IsFinite(factorEquilibrado) && + factorEquilibrado > 0) _factorEquilibradoAndarBus = (config.VersionFactorEquilibrado ?? 0) < VersionPersistenciaFactorEquilibrado && Math.Abs(factorEquilibrado - 12.0) < 0.0001 ? FactorEquilibradoAndarBusPorDefecto - : Math.Max(0, factorEquilibrado); + : factorEquilibrado; var migrarValoresPendientes = (config.VersionPendientes ?? 0) < VersionPersistenciaPendientes; _mostrarElevacionesRuta = migrarValoresPendientes ? true diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Planificacion.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Planificacion.cs index 93c03fd..f14e24d 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Planificacion.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Planificacion.cs @@ -1270,9 +1270,123 @@ public partial class PlanificadorRutas : ComponentBase var listaOrdenada = ordenadas.ToList(); _opcionesRuta.Clear(); _opcionesRuta.AddRange(listaOrdenada); + FiltrarAlternativasBusRedundantes(); RenumerarEtiquetasGenericasOpcionesRuta(); } + /// + /// Conserva la mejor alternativa de cada direccion de acceso a una misma linea + /// y descarta rutas en bus que no ahorran ninguna distancia andando. + /// + private void FiltrarAlternativasBusRedundantes() + { + if (_opcionesRuta.Count <= 1) + return; + + var distanciaSoloPie = _opcionesRuta + .Where(opcion => opcion.EsSoloAPie && + double.IsFinite(opcion.DistanciaSoloAPie) && + opcion.DistanciaSoloAPie > 0) + .Select(opcion => opcion.DistanciaSoloAPie) + .DefaultIfEmpty(double.PositiveInfinity) + .Min(); + + var conservadas = new List(_opcionesRuta.Count); + foreach (var candidata in _opcionesRuta) + { + if (candidata.EsSoloAPie) + { + conservadas.Add(candidata); + continue; + } + + if (double.IsFinite(distanciaSoloPie) && + double.IsFinite(candidata.DistanciaPieTotalAprox) && + candidata.DistanciaPieTotalAprox >= distanciaSoloPie) + { + continue; + } + + var repiteDireccionAcceso = conservadas.Any(referencia => + !referencia.EsSoloAPie && + EsMismaLineaInicial(referencia, candidata) && + CaminanEnLaMismaDireccionDesdeOrigen(referencia, candidata)); + + if (!repiteDireccionAcceso) + conservadas.Add(candidata); + } + + if (conservadas.Count == _opcionesRuta.Count) + return; + + _opcionesRuta.Clear(); + _opcionesRuta.AddRange(conservadas); + } + + private static bool EsMismaLineaInicial( + AlternativaRuta referencia, + AlternativaRuta candidata) + { + var tramoReferencia = referencia.AlternativaBus?.Tramos.FirstOrDefault(); + var tramoCandidata = candidata.AlternativaBus?.Tramos.FirstOrDefault(); + if (tramoReferencia is null || tramoCandidata is null) + return false; + + return string.Equals( + UtilidadesTransitoDbus.LineaCodigoStr(tramoReferencia.Variante), + UtilidadesTransitoDbus.LineaCodigoStr(tramoCandidata.Variante), + StringComparison.OrdinalIgnoreCase) && + tramoReferencia.Variante.CodigoItinerario == tramoCandidata.Variante.CodigoItinerario; + } + + private bool CaminanEnLaMismaDireccionDesdeOrigen( + AlternativaRuta referencia, + AlternativaRuta candidata) + { + if (_coordenadasOrigen is null || + _coordenadasOrigen.Length < 2 || + referencia.OrigenParada is null || + candidata.OrigenParada is null) + { + return false; + } + + var latitudOrigen = _coordenadasOrigen[0]; + var longitudOrigen = _coordenadasOrigen[1]; + if (!double.IsFinite(latitudOrigen) || !double.IsFinite(longitudOrigen)) + return false; + + var metrosPorGradoLatitud = 111_320d; + var metrosPorGradoLongitud = + metrosPorGradoLatitud * Math.Cos(latitudOrigen * Math.PI / 180d); + + var referenciaX = + (referencia.OrigenParada.Longitud - longitudOrigen) * metrosPorGradoLongitud; + var referenciaY = + (referencia.OrigenParada.Latitud - latitudOrigen) * metrosPorGradoLatitud; + var candidataX = + (candidata.OrigenParada.Longitud - longitudOrigen) * metrosPorGradoLongitud; + var candidataY = + (candidata.OrigenParada.Latitud - latitudOrigen) * metrosPorGradoLatitud; + + var moduloReferencia = Math.Sqrt( + referenciaX * referenciaX + referenciaY * referenciaY); + var moduloCandidata = Math.Sqrt( + candidataX * candidataX + candidataY * candidataY); + if (!double.IsFinite(moduloReferencia) || + !double.IsFinite(moduloCandidata) || + moduloReferencia < 1d || + moduloCandidata < 1d) + { + return false; + } + + var cosenoDireccion = + (referenciaX * candidataX + referenciaY * candidataY) / + (moduloReferencia * moduloCandidata); + return double.IsFinite(cosenoDireccion) && cosenoDireccion >= 0d; + } + private void RenumerarEtiquetasGenericasOpcionesRuta() { for (var i = 0; i < _opcionesRuta.Count; i++) @@ -1744,8 +1858,8 @@ public partial class PlanificadorRutas : ComponentBase _mostrarElevacionesRuta = _mostrarElevacionesRutaConfiguracion; - if (!_aplicandoRegistroHistorial) - LimpiarSeleccionHistorial(); + var confirmarDerivacionHistorial = !_aplicandoRegistroHistorial && + PrepararDerivacionHistorialParaRecalculo(); var cronometroCalculo = System.Diagnostics.Stopwatch.StartNew(); _panelRutasMovilContraido = _mostrarBuscadorMapa; @@ -1872,6 +1986,8 @@ public partial class PlanificadorRutas : ComponentBase { await RegistrarEscenarioActualEnHistorialLocalAsync(); } + if (confirmarDerivacionHistorial) + ConfirmarDerivacionHistorial(); await ActualizarResumenTecnicoRutaAsync(CancellationToken.None); _tiempoUltimoCalculoRuta = cronometroCalculo.Elapsed; StateHasChanged(); diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs index 1bbbaba..c10148b 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs @@ -1151,6 +1151,8 @@ public partial class PlanificadorRutas : ComponentBase _coordenadasOrigen = null; _coordenadasDestino = null; + LimpiarSeleccionHistorial(); + LimpiarReferenciaHistorial(); _textoBusquedaOrigen = string.Empty; _textoBusquedaDestino = string.Empty; _ubicacionOrigenSeleccionada = null; diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.razor b/RutasDBUS/Components/Pages/PlanificadorRutas.razor index 5329467..112042d 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.razor +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.razor @@ -913,7 +913,6 @@ : _duracionTotalSegundos; var dRecta = DistanciaLineaRectaOrigenDestino(); var dCorregida = CalcularDistanciaCorregidaAlternativa(altSeleccionada); - var dEquilibradaAndando = CalcularDistanciaEquilibradaAndandoAlternativa(altSeleccionada); var xyzOrigen = FormatearResumenXYZOrigen(); var xyzDestino = FormatearResumenXYZDestino(); var callejeroOrigen = FormatearResumenCallejeroOrigen(); @@ -934,7 +933,7 @@

} - @if (dRecta > 0 || dCorregida > 0 || dEquilibradaAndando > 0) + @if (dRecta > 0 || dCorregida > 0) {
@if (dRecta > 0) @@ -945,10 +944,6 @@ {
d.Corregida a pie: @FormatearDistanciaDetalleRuta(dCorregida)
} - @if (dEquilibradaAndando > 0) - { -
d.Equilib. A: @FormatearDistanciaDetalleRuta(dEquilibradaAndando)
- } @if (!string.IsNullOrWhiteSpace(scoreEquilibradoSeleccionado)) {
Score equilibrado A: @scoreEquilibradoSeleccionado
@@ -1067,12 +1062,18 @@ { var item = _historialLocal[i]; var numeroPersonal = i + 1; + var esReferenciaDerivada = EsReferenciaDerivadaHistorialPersonal(item.Id); -
+
@@ -1129,12 +1130,18 @@ { var item = _historialCompartido[i]; var numeroCompartido = i + 1; + var esReferenciaDerivada = EsReferenciaDerivadaHistorialCompartido(item.Id); -
+
@@ -1150,7 +1157,17 @@ }
- @if (!string.IsNullOrWhiteSpace(_mensajeHistorial)) + @{ var textoReferenciaDerivada = TextoReferenciaHistorialDerivado(); } + @if (!string.IsNullOrWhiteSpace(textoReferenciaDerivada)) + { +
+ + @textoReferenciaDerivada +
+ } + + @if (!string.IsNullOrWhiteSpace(_mensajeHistorial) && + (string.IsNullOrWhiteSpace(textoReferenciaDerivada) || _mensajeHistorialEsError)) {
@_mensajeHistorial @@ -1499,9 +1516,9 @@
diff --git a/RutasDBUS/Modelos/Planificacion/ParadaCandidata.cs b/RutasDBUS/Modelos/Planificacion/ParadaCandidata.cs index ccc6eb0..fde8307 100644 --- a/RutasDBUS/Modelos/Planificacion/ParadaCandidata.cs +++ b/RutasDBUS/Modelos/Planificacion/ParadaCandidata.cs @@ -34,6 +34,7 @@ public sealed class ParametrosPlanificadorRuta public double FactorPenalizacionDistanciaPie { get; set; } = 1.35; public double ParametroPenalizacionDistanciaPieMetros { get; set; } = 200.0; public double PesoTransbordoRankingMinutos { get; set; } = 4.0; + // Divisor aplicado a la distancia en bus dentro del score equilibrado. public double FactorEquilibradoAndarBus { get; set; } = 6.0; // selección candidatas diff --git a/RutasDBUS/Servicios/Historial/ServicioHistorialRutasCompartido.cs b/RutasDBUS/Servicios/Historial/ServicioHistorialRutasCompartido.cs index d50ecfb..7762edb 100644 --- a/RutasDBUS/Servicios/Historial/ServicioHistorialRutasCompartido.cs +++ b/RutasDBUS/Servicios/Historial/ServicioHistorialRutasCompartido.cs @@ -315,8 +315,10 @@ public sealed class ServicioHistorialRutasCompartido : IHistorialRutasCompartido var criterio = NormalizarTextoClave(item.CriterioPrioritarioRuta); if (string.IsNullOrWhiteSpace(criterio)) criterio = NormalizarTextoClave(item.PreferenciaEtiquetaRuta); - var factorEquilibrado = item.FactorEquilibradoAndarBus is { } factor && double.IsFinite(factor) - ? Math.Max(0, factor).ToString("0.######", CultureInfo.InvariantCulture) + var factorEquilibrado = item.FactorEquilibradoAndarBus is { } factor && + double.IsFinite(factor) && + factor > 0 + ? factor.ToString("0.######", CultureInfo.InvariantCulture) : string.Empty; var lineas = item.ActivarFiltroLineas ? string.Join(",", NormalizarLineas(item.LineasPermitidas)) diff --git a/RutasDBUS/Servicios/Planificacion/BuscadorRutasTemporalesDbus.cs b/RutasDBUS/Servicios/Planificacion/BuscadorRutasTemporalesDbus.cs index 3be96fa..a265d8b 100644 --- a/RutasDBUS/Servicios/Planificacion/BuscadorRutasTemporalesDbus.cs +++ b/RutasDBUS/Servicios/Planificacion/BuscadorRutasTemporalesDbus.cs @@ -826,14 +826,15 @@ public sealed class BuscadorRutasTemporalesDbus resultado.DistanciasPieTransbordosPorTramo, resultado.DistanciaPieFin), parametros); - var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) - ? Math.Max(0, parametros.FactorEquilibradoAndarBus) + var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) && + parametros.FactorEquilibradoAndarBus > 0 + ? parametros.FactorEquilibradoAndarBus : 6.0; var penalizacionTransbordos = UtilidadesTransitoDbus.CalcularPenalizacionTransbordosRankingMetros( UtilidadesTransitoDbus.ContarTransbordosReales(resultado.RutaBus.Tramos), parametros); - var score = distanciaBus + (distanciaAndando * factor) + penalizacionTransbordos; + var score = distanciaAndando + (distanciaBus / factor) + penalizacionTransbordos; return double.IsFinite(score) && score >= 0 ? score : double.PositiveInfinity; diff --git a/RutasDBUS/Servicios/Planificacion/PlanificadorRutasServicio.cs b/RutasDBUS/Servicios/Planificacion/PlanificadorRutasServicio.cs index 46e57af..fbe6257 100644 --- a/RutasDBUS/Servicios/Planificacion/PlanificadorRutasServicio.cs +++ b/RutasDBUS/Servicios/Planificacion/PlanificadorRutasServicio.cs @@ -1543,8 +1543,10 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas : UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaTotalMetros( ConstruirDistanciasPiePorTramo(resultado), parametros); - var factor = parametros is not null && double.IsFinite(parametros.FactorEquilibradoAndarBus) - ? Math.Max(0, parametros.FactorEquilibradoAndarBus) + var factor = parametros is not null && + double.IsFinite(parametros.FactorEquilibradoAndarBus) && + parametros.FactorEquilibradoAndarBus > 0 + ? parametros.FactorEquilibradoAndarBus : 6.0; var penalizacionTransbordos = parametros is null ? 0 @@ -1552,7 +1554,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas UtilidadesTransitoDbus.ContarTransbordosReales(resultado.RutaBus.Tramos), parametros); - var score = distanciaBus + (distanciaAndando * factor) + penalizacionTransbordos; + var score = distanciaAndando + (distanciaBus / factor) + penalizacionTransbordos; return double.IsFinite(score) && score >= 0 ? score : double.PositiveInfinity; @@ -3244,14 +3246,15 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas var distanciaBus = Math.Max(0, alternativa.AlternativaBus.DistanciaTotalBus); var distanciaAndando = CalcularDistanciaAndandoCorregidaMetros(alternativa, parametros); - var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) - ? Math.Max(0, parametros.FactorEquilibradoAndarBus) + var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) && + parametros.FactorEquilibradoAndarBus > 0 + ? parametros.FactorEquilibradoAndarBus : 6.0; var penalizacionTransbordos = UtilidadesTransitoDbus.CalcularPenalizacionTransbordosRankingMetros( alternativa.NumeroTransbordosReales, parametros); - var score = distanciaBus + (distanciaAndando * factor) + penalizacionTransbordos; + var score = distanciaAndando + (distanciaBus / factor) + penalizacionTransbordos; return double.IsFinite(score) && score >= 0 ? score : double.PositiveInfinity; @@ -3508,8 +3511,8 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas } /// - /// Firma orientada a UI: agrupa alternativas con la misma secuencia de lineas y sentidos - /// aunque cambie la parada exacta. + /// Firma orientada a UI: agrupa solo alternativas con el mismo recorrido visible. + /// Las paradas forman parte de la identidad porque cambian el acceso a pie y sus pendientes. /// private static string CrearFirmaFamiliaRutaBus(RutaBusAlternativa alternativa) { @@ -3518,7 +3521,8 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas { var linea = UtilidadesTransitoDbus.LineaCodigoStr(tramo.Variante); var sentido = UtilidadesTransitoDbus.NormalizarSentidoCodigo(tramo.Variante.Sentido?.Codigo); - var parte = $"{linea}.{tramo.Variante.CodigoItinerario}-{sentido}"; + var parte = $"{linea}.{tramo.Variante.CodigoItinerario}-{sentido}:" + + $"{tramo.CodigoParadaInicio}>{tramo.CodigoParadaFin}"; if (partes.Count == 0 || !string.Equals(partes[^1], parte, StringComparison.OrdinalIgnoreCase)) partes.Add(parte); } diff --git a/RutasDBUS/wwwroot/app.css b/RutasDBUS/wwwroot/app.css index 6f1cda5..1f8ac76 100644 --- a/RutasDBUS/wwwroot/app.css +++ b/RutasDBUS/wwwroot/app.css @@ -1232,6 +1232,24 @@ html, body { background: rgba(21,128,61,.18); } +.history-item--derived-source .history-item-main { + border-color: rgba(245,158,11,.72); + background: rgba(120,53,15,.17); +} + +.history-derived-marker { + color: #fbbf24; + font-weight: 800; +} + +.history-derived-note { + display: flex; + align-items: baseline; + gap: .35rem; + font-size: .76rem; + color: #fcd34d; +} + .history-item-action, .history-item-delete { flex: 0 0 32px;