Restricciones de seleccion de paradas.

señal de dereivacion de historial.

importante: cambiod e formula, desaparece distancia equilibrada y el factor pasa a dividir a la distancia en bus
This commit is contained in:
2026-08-26 08:09:43 +02:00
parent e51f2b70e2
commit 49c5d23ace
12 changed files with 317 additions and 63 deletions

View File

@@ -151,7 +151,7 @@ public partial class PlanificadorRutas : ComponentBase
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
private string FactorEquilibradoAndarBusTexto private string FactorEquilibradoAndarBusTexto
{ {
@@ -164,8 +164,8 @@ public partial class PlanificadorRutas : ComponentBase
if (!double.TryParse(texto, NumberStyles.Float, CultureInfo.InvariantCulture, out var valor)) if (!double.TryParse(texto, NumberStyles.Float, CultureInfo.InvariantCulture, out var valor))
return; return;
if (double.IsFinite(valor)) if (double.IsFinite(valor) && valor > 0)
_factorEquilibradoAndarBus = Math.Max(0, valor); _factorEquilibradoAndarBus = valor;
} }
} }
@@ -1420,25 +1420,6 @@ public partial class PlanificadorRutas : ComponentBase
return EstimarDuracionPieSegundos(distanciaCorregida); return EstimarDuracionPieSegundos(distanciaCorregida);
} }
/// <summary>
/// Calcula la aportacion de todos los tramos andando a la distancia equilibrada.
/// </summary>
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) private double CalcularScoreEquilibradoFactor(AlternativaRuta? alternativa)
{ {
if (alternativa is null) if (alternativa is null)
@@ -1453,11 +1434,14 @@ public partial class PlanificadorRutas : ComponentBase
distanciaBus = Math.Max(0, alternativa.AlternativaBus.DistanciaTotalBus); 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( var penalizacionTransbordos = UtilidadesTransitoDbus.CalcularPenalizacionTransbordosRankingMetros(
alternativa.NumeroTransbordosReales, alternativa.NumeroTransbordosReales,
CrearParametrosPenalizacionActuales()); CrearParametrosPenalizacionActuales());
var score = distanciaBus + distanciaEquilibradaAndando + penalizacionTransbordos; var score = distanciaAndando + (distanciaBus / factor) + penalizacionTransbordos;
return double.IsFinite(score) && score >= 0 return double.IsFinite(score) && score >= 0
? score ? score
: double.PositiveInfinity; : double.PositiveInfinity;
@@ -1469,7 +1453,7 @@ public partial class PlanificadorRutas : ComponentBase
if (!double.IsFinite(score)) if (!double.IsFinite(score))
return string.Empty; return string.Empty;
return (score / 1000.0).ToString("0.000", CultureInfo.InvariantCulture); return score.ToString("0", CultureInfo.InvariantCulture);
} }
private async Task RecalcularRutaEquilibradaTrasCambioFactorAsync() private async Task RecalcularRutaEquilibradaTrasCambioFactorAsync()

View File

@@ -27,6 +27,9 @@ public partial class PlanificadorRutas
private CancellationTokenSource? _historialCompartidoCts; private CancellationTokenSource? _historialCompartidoCts;
private string _historialLocalSeleccionadoId = string.Empty; private string _historialLocalSeleccionadoId = string.Empty;
private string _historialCompartidoSeleccionadoId = string.Empty; private string _historialCompartidoSeleccionadoId = string.Empty;
private TipoReferenciaHistorial _tipoReferenciaHistorial;
private string _historialReferenciaId = string.Empty;
private bool _rutaActualDerivadaDeHistorial;
private string _mensajeHistorial = string.Empty; private string _mensajeHistorial = string.Empty;
private bool _mensajeHistorialEsError = false; private bool _mensajeHistorialEsError = false;
private bool _mostrarHistorialRutas = false; private bool _mostrarHistorialRutas = false;
@@ -43,6 +46,13 @@ public partial class PlanificadorRutas
private string _resumenErrorHistorialLocal = string.Empty; private string _resumenErrorHistorialLocal = string.Empty;
private string _detalleErrorHistorialLocal = string.Empty; private string _detalleErrorHistorialLocal = string.Empty;
private enum TipoReferenciaHistorial
{
Ninguna,
Personal,
Compartido
}
private sealed class ResultadoStorageNavegador private sealed class ResultadoStorageNavegador
{ {
[JsonPropertyName("ok")] [JsonPropertyName("ok")]
@@ -400,6 +410,11 @@ public partial class PlanificadorRutas
_historialLocal.AddRange(items); _historialLocal.AddRange(items);
if (!_historialLocal.Any(x => x.Id == _historialLocalSeleccionadoId)) if (!_historialLocal.Any(x => x.Id == _historialLocalSeleccionadoId))
_historialLocalSeleccionadoId = string.Empty; _historialLocalSeleccionadoId = string.Empty;
if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Personal &&
!_historialLocal.Any(x => x.Id == _historialReferenciaId))
{
LimpiarReferenciaHistorial();
}
_historialLocalCargado = true; _historialLocalCargado = true;
} }
@@ -632,11 +647,18 @@ public partial class PlanificadorRutas
if (_historialCompartido.Count == 0) if (_historialCompartido.Count == 0)
{ {
_historialCompartidoSeleccionadoId = string.Empty; _historialCompartidoSeleccionadoId = string.Empty;
if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido)
LimpiarReferenciaHistorial();
return; return;
} }
if (!_historialCompartido.Any(x => x.Id == _historialCompartidoSeleccionadoId)) if (!_historialCompartido.Any(x => x.Id == _historialCompartidoSeleccionadoId))
_historialCompartidoSeleccionadoId = string.Empty; _historialCompartidoSeleccionadoId = string.Empty;
if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido &&
!_historialCompartido.Any(x => x.Id == _historialReferenciaId))
{
LimpiarReferenciaHistorial();
}
} }
catch (Exception) catch (Exception)
{ {
@@ -783,12 +805,14 @@ public partial class PlanificadorRutas
{ {
_historialLocalSeleccionadoId = id; _historialLocalSeleccionadoId = id;
_historialCompartidoSeleccionadoId = string.Empty; _historialCompartidoSeleccionadoId = string.Empty;
EstablecerReferenciaHistorial(TipoReferenciaHistorial.Personal, id);
} }
private void SeleccionarHistorialCompartido(string id) private void SeleccionarHistorialCompartido(string id)
{ {
_historialLocalSeleccionadoId = string.Empty; _historialLocalSeleccionadoId = string.Empty;
_historialCompartidoSeleccionadoId = id; _historialCompartidoSeleccionadoId = id;
EstablecerReferenciaHistorial(TipoReferenciaHistorial.Compartido, id);
} }
private void LimpiarSeleccionHistorial() private void LimpiarSeleccionHistorial()
@@ -797,6 +821,74 @@ public partial class PlanificadorRutas
_historialCompartidoSeleccionadoId = string.Empty; _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() private async Task CompartirEscenarioActualAsync()
{ {
var actual = ConstruirRegistroHistorialActual() var actual = ConstruirRegistroHistorialActual()
@@ -854,6 +946,11 @@ public partial class PlanificadorRutas
var estabaSeleccionado = _historialLocalSeleccionadoId == id; var estabaSeleccionado = _historialLocalSeleccionadoId == id;
_historialLocal.RemoveAt(indiceEliminado); _historialLocal.RemoveAt(indiceEliminado);
if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Personal &&
string.Equals(_historialReferenciaId, id, StringComparison.OrdinalIgnoreCase))
{
LimpiarReferenciaHistorial();
}
await PersistirHistorialLocalAsync(); await PersistirHistorialLocalAsync();
@@ -924,6 +1021,11 @@ public partial class PlanificadorRutas
await CargarHistorialCompartidoAsync(); await CargarHistorialCompartidoAsync();
_historialCompartido.RemoveAll(x => x.Id == id); _historialCompartido.RemoveAll(x => x.Id == id);
if (_tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido &&
string.Equals(_historialReferenciaId, id, StringComparison.OrdinalIgnoreCase))
{
LimpiarReferenciaHistorial();
}
if (estabaSeleccionado) if (estabaSeleccionado)
{ {
@@ -973,9 +1075,11 @@ public partial class PlanificadorRutas
_criterioPrioritarioRuta = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta); _criterioPrioritarioRuta = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta);
SincronizarPreferenciaDesdeCriterioRuta(); 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(); SincronizarTextosPenalizacionPie();
if (_configuracionTemporalPreviaHistorial is not null) if (_configuracionTemporalPreviaHistorial is not null)
@@ -1063,8 +1167,9 @@ public partial class PlanificadorRutas
HoraSimulada = TimeOnly.FromDateTime(ahora).ToString("HH:mm:ss", CultureInfo.InvariantCulture), HoraSimulada = TimeOnly.FromDateTime(ahora).ToString("HH:mm:ss", CultureInfo.InvariantCulture),
CriterioPrioritarioRuta = _criterioPrioritarioRuta, CriterioPrioritarioRuta = _criterioPrioritarioRuta,
PreferenciaEtiquetaRuta = _preferenciaEtiquetaRuta, PreferenciaEtiquetaRuta = _preferenciaEtiquetaRuta,
FactorEquilibradoAndarBus = double.IsFinite(_factorEquilibradoAndarBus) FactorEquilibradoAndarBus = double.IsFinite(_factorEquilibradoAndarBus) &&
? Math.Max(0, _factorEquilibradoAndarBus) _factorEquilibradoAndarBus > 0
? _factorEquilibradoAndarBus
: FactorEquilibradoAndarBusPorDefecto, : FactorEquilibradoAndarBusPorDefecto,
ActivarFiltroLineas = _activarRangoLineas, ActivarFiltroLineas = _activarRangoLineas,
LineasPermitidas = ObtenerLineasPermitidasSeleccionadas().ToList(), LineasPermitidas = ObtenerLineasPermitidasSeleccionadas().ToList(),
@@ -1194,8 +1299,10 @@ public partial class PlanificadorRutas
var fecha = item.UsarHoraSimulada ? item.FechaSimulada?.Trim() ?? string.Empty : string.Empty; var fecha = item.UsarHoraSimulada ? item.FechaSimulada?.Trim() ?? string.Empty : string.Empty;
var hora = item.UsarHoraSimulada ? item.HoraSimulada?.Trim() ?? string.Empty : string.Empty; var hora = item.UsarHoraSimulada ? item.HoraSimulada?.Trim() ?? string.Empty : string.Empty;
var criterio = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta); var criterio = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta);
var factorEquilibrado = item.FactorEquilibradoAndarBus is { } factor && double.IsFinite(factor) var factorEquilibrado = item.FactorEquilibradoAndarBus is { } factor &&
? Math.Max(0, factor).ToString("0.######", CultureInfo.InvariantCulture) double.IsFinite(factor) &&
factor > 0
? factor.ToString("0.######", CultureInfo.InvariantCulture)
: string.Empty; : string.Empty;
var lineas = item.ActivarFiltroLineas var lineas = item.ActivarFiltroLineas
? string.Join(",", NormalizarLineasHistorial(item.LineasPermitidas)) ? string.Join(",", NormalizarLineasHistorial(item.LineasPermitidas))

View File

@@ -1202,7 +1202,7 @@ public partial class PlanificadorRutas : ComponentBase
private bool _elevPopupMinimizado = false; private bool _elevPopupMinimizado = false;
private const string _versionApp = "(v-20260807b)"; private const string _versionApp = "(v-20260825b)";
private double? _paradaElevIdee; private double? _paradaElevIdee;

View File

@@ -227,11 +227,13 @@ public partial class PlanificadorRutas
: coeficienteAltura; : coeficienteAltura;
if (config.PesoTransbordoRankingMinutos is { } pesoTransbordo && double.IsFinite(pesoTransbordo)) if (config.PesoTransbordoRankingMinutos is { } pesoTransbordo && double.IsFinite(pesoTransbordo))
_pesoTransbordoRankingMinutos = Math.Abs(pesoTransbordo - 8.0) < 0.0001 ? 4.0 : Math.Max(0, 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 && _factorEquilibradoAndarBus = (config.VersionFactorEquilibrado ?? 0) < VersionPersistenciaFactorEquilibrado &&
Math.Abs(factorEquilibrado - 12.0) < 0.0001 Math.Abs(factorEquilibrado - 12.0) < 0.0001
? FactorEquilibradoAndarBusPorDefecto ? FactorEquilibradoAndarBusPorDefecto
: Math.Max(0, factorEquilibrado); : factorEquilibrado;
var migrarValoresPendientes = (config.VersionPendientes ?? 0) < VersionPersistenciaPendientes; var migrarValoresPendientes = (config.VersionPendientes ?? 0) < VersionPersistenciaPendientes;
_mostrarElevacionesRuta = migrarValoresPendientes _mostrarElevacionesRuta = migrarValoresPendientes
? true ? true

View File

@@ -1270,9 +1270,123 @@ public partial class PlanificadorRutas : ComponentBase
var listaOrdenada = ordenadas.ToList(); var listaOrdenada = ordenadas.ToList();
_opcionesRuta.Clear(); _opcionesRuta.Clear();
_opcionesRuta.AddRange(listaOrdenada); _opcionesRuta.AddRange(listaOrdenada);
FiltrarAlternativasBusRedundantes();
RenumerarEtiquetasGenericasOpcionesRuta(); RenumerarEtiquetasGenericasOpcionesRuta();
} }
/// <summary>
/// Conserva la mejor alternativa de cada direccion de acceso a una misma linea
/// y descarta rutas en bus que no ahorran ninguna distancia andando.
/// </summary>
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<AlternativaRuta>(_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() private void RenumerarEtiquetasGenericasOpcionesRuta()
{ {
for (var i = 0; i < _opcionesRuta.Count; i++) for (var i = 0; i < _opcionesRuta.Count; i++)
@@ -1744,8 +1858,8 @@ public partial class PlanificadorRutas : ComponentBase
_mostrarElevacionesRuta = _mostrarElevacionesRutaConfiguracion; _mostrarElevacionesRuta = _mostrarElevacionesRutaConfiguracion;
if (!_aplicandoRegistroHistorial) var confirmarDerivacionHistorial = !_aplicandoRegistroHistorial &&
LimpiarSeleccionHistorial(); PrepararDerivacionHistorialParaRecalculo();
var cronometroCalculo = System.Diagnostics.Stopwatch.StartNew(); var cronometroCalculo = System.Diagnostics.Stopwatch.StartNew();
_panelRutasMovilContraido = _mostrarBuscadorMapa; _panelRutasMovilContraido = _mostrarBuscadorMapa;
@@ -1872,6 +1986,8 @@ public partial class PlanificadorRutas : ComponentBase
{ {
await RegistrarEscenarioActualEnHistorialLocalAsync(); await RegistrarEscenarioActualEnHistorialLocalAsync();
} }
if (confirmarDerivacionHistorial)
ConfirmarDerivacionHistorial();
await ActualizarResumenTecnicoRutaAsync(CancellationToken.None); await ActualizarResumenTecnicoRutaAsync(CancellationToken.None);
_tiempoUltimoCalculoRuta = cronometroCalculo.Elapsed; _tiempoUltimoCalculoRuta = cronometroCalculo.Elapsed;
StateHasChanged(); StateHasChanged();

View File

@@ -1151,6 +1151,8 @@ public partial class PlanificadorRutas : ComponentBase
_coordenadasOrigen = null; _coordenadasOrigen = null;
_coordenadasDestino = null; _coordenadasDestino = null;
LimpiarSeleccionHistorial();
LimpiarReferenciaHistorial();
_textoBusquedaOrigen = string.Empty; _textoBusquedaOrigen = string.Empty;
_textoBusquedaDestino = string.Empty; _textoBusquedaDestino = string.Empty;
_ubicacionOrigenSeleccionada = null; _ubicacionOrigenSeleccionada = null;

View File

@@ -913,7 +913,6 @@
: _duracionTotalSegundos; : _duracionTotalSegundos;
var dRecta = DistanciaLineaRectaOrigenDestino(); var dRecta = DistanciaLineaRectaOrigenDestino();
var dCorregida = CalcularDistanciaCorregidaAlternativa(altSeleccionada); var dCorregida = CalcularDistanciaCorregidaAlternativa(altSeleccionada);
var dEquilibradaAndando = CalcularDistanciaEquilibradaAndandoAlternativa(altSeleccionada);
var xyzOrigen = FormatearResumenXYZOrigen(); var xyzOrigen = FormatearResumenXYZOrigen();
var xyzDestino = FormatearResumenXYZDestino(); var xyzDestino = FormatearResumenXYZDestino();
var callejeroOrigen = FormatearResumenCallejeroOrigen(); var callejeroOrigen = FormatearResumenCallejeroOrigen();
@@ -934,7 +933,7 @@
</p> </p>
} }
@if (dRecta > 0 || dCorregida > 0 || dEquilibradaAndando > 0) @if (dRecta > 0 || dCorregida > 0)
{ {
<div class="muted" style="font-size:0.75rem;margin-top:4px;"> <div class="muted" style="font-size:0.75rem;margin-top:4px;">
@if (dRecta > 0) @if (dRecta > 0)
@@ -945,10 +944,6 @@
{ {
<div>d.Corregida a pie: @FormatearDistanciaDetalleRuta(dCorregida)</div> <div>d.Corregida a pie: @FormatearDistanciaDetalleRuta(dCorregida)</div>
} }
@if (dEquilibradaAndando > 0)
{
<div>d.Equilib. A: @FormatearDistanciaDetalleRuta(dEquilibradaAndando)</div>
}
@if (!string.IsNullOrWhiteSpace(scoreEquilibradoSeleccionado)) @if (!string.IsNullOrWhiteSpace(scoreEquilibradoSeleccionado))
{ {
<div>Score equilibrado A: @scoreEquilibradoSeleccionado</div> <div>Score equilibrado A: @scoreEquilibradoSeleccionado</div>
@@ -1067,12 +1062,18 @@
{ {
var item = _historialLocal[i]; var item = _historialLocal[i];
var numeroPersonal = i + 1; var numeroPersonal = i + 1;
var esReferenciaDerivada = EsReferenciaDerivadaHistorialPersonal(item.Id);
<div class="history-item @(item.Id == _historialLocalSeleccionadoId ? "history-item--selected" : "")"> <div class="history-item @(item.Id == _historialLocalSeleccionadoId ? "history-item--selected" : "") @(esReferenciaDerivada ? "history-item--derived-source" : "")">
<button type="button" <button type="button"
class="history-item-main history-item-main--numbered" class="history-item-main history-item-main--numbered"
@onclick="() => CargarHistorialLocalAsync(item.Id)"> @onclick="() => CargarHistorialLocalAsync(item.Id)">
<span class="history-item-number">@numeroPersonal.</span> <span class="history-item-number">
@numeroPersonal.@if (esReferenciaDerivada)
{
<span class="history-derived-marker" title="La ruta actual parte de este historial">*</span>
}
</span>
<span class="history-item-text">@TextoOpcionHistorial(item)</span> <span class="history-item-text">@TextoOpcionHistorial(item)</span>
</button> </button>
@@ -1129,12 +1130,18 @@
{ {
var item = _historialCompartido[i]; var item = _historialCompartido[i];
var numeroCompartido = i + 1; var numeroCompartido = i + 1;
var esReferenciaDerivada = EsReferenciaDerivadaHistorialCompartido(item.Id);
<div class="history-item @(item.Id == _historialCompartidoSeleccionadoId ? "history-item--selected" : "")"> <div class="history-item @(item.Id == _historialCompartidoSeleccionadoId ? "history-item--selected" : "") @(esReferenciaDerivada ? "history-item--derived-source" : "")">
<button type="button" <button type="button"
class="history-item-main history-item-main--numbered" class="history-item-main history-item-main--numbered"
@onclick="() => CargarHistorialCompartidoAsync(item.Id)"> @onclick="() => CargarHistorialCompartidoAsync(item.Id)">
<span class="history-item-number">@numeroCompartido.</span> <span class="history-item-number">
@numeroCompartido.@if (esReferenciaDerivada)
{
<span class="history-derived-marker" title="La ruta actual parte de este historial">*</span>
}
</span>
<span class="history-item-text">@TextoOpcionHistorial(item)</span> <span class="history-item-text">@TextoOpcionHistorial(item)</span>
</button> </button>
@@ -1150,7 +1157,17 @@
} }
</div> </div>
@if (!string.IsNullOrWhiteSpace(_mensajeHistorial)) @{ var textoReferenciaDerivada = TextoReferenciaHistorialDerivado(); }
@if (!string.IsNullOrWhiteSpace(textoReferenciaDerivada))
{
<div class="history-derived-note">
<span class="history-derived-marker" aria-hidden="true">*</span>
<span>@textoReferenciaDerivada</span>
</div>
}
@if (!string.IsNullOrWhiteSpace(_mensajeHistorial) &&
(string.IsNullOrWhiteSpace(textoReferenciaDerivada) || _mensajeHistorialEsError))
{ {
<div class="history-feedback @(_mensajeHistorialEsError ? "history-feedback--error" : "")"> <div class="history-feedback @(_mensajeHistorialEsError ? "history-feedback--error" : "")">
@_mensajeHistorial @_mensajeHistorial
@@ -1499,9 +1516,9 @@
<div class="config-dual-labeled"> <div class="config-dual-labeled">
<label class="config-dual-labeled__field"> <label class="config-dual-labeled__field">
<span>Equilib. A</span> <span>Equilib. A</span>
<input type="number" class="config-input" step="0.1" min="0" <input type="number" class="config-input" step="0.1" min="0.1"
placeholder="6" placeholder="6"
title="Factor del modo equilibrado" title="Divisor de la distancia en bus en el modo equilibrado"
@bind="FactorEquilibradoAndarBusTexto" @bind:event="oninput" @bind="FactorEquilibradoAndarBusTexto" @bind:event="oninput"
@onblur="OnBlurFactorEquilibradoAndarBus" /> @onblur="OnBlurFactorEquilibradoAndarBus" />
</label> </label>

View File

@@ -34,6 +34,7 @@ public sealed class ParametrosPlanificadorRuta
public double FactorPenalizacionDistanciaPie { get; set; } = 1.35; public double FactorPenalizacionDistanciaPie { get; set; } = 1.35;
public double ParametroPenalizacionDistanciaPieMetros { get; set; } = 200.0; public double ParametroPenalizacionDistanciaPieMetros { get; set; } = 200.0;
public double PesoTransbordoRankingMinutos { get; set; } = 4.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; public double FactorEquilibradoAndarBus { get; set; } = 6.0;
// selección candidatas // selección candidatas

View File

@@ -315,8 +315,10 @@ public sealed class ServicioHistorialRutasCompartido : IHistorialRutasCompartido
var criterio = NormalizarTextoClave(item.CriterioPrioritarioRuta); var criterio = NormalizarTextoClave(item.CriterioPrioritarioRuta);
if (string.IsNullOrWhiteSpace(criterio)) if (string.IsNullOrWhiteSpace(criterio))
criterio = NormalizarTextoClave(item.PreferenciaEtiquetaRuta); criterio = NormalizarTextoClave(item.PreferenciaEtiquetaRuta);
var factorEquilibrado = item.FactorEquilibradoAndarBus is { } factor && double.IsFinite(factor) var factorEquilibrado = item.FactorEquilibradoAndarBus is { } factor &&
? Math.Max(0, factor).ToString("0.######", CultureInfo.InvariantCulture) double.IsFinite(factor) &&
factor > 0
? factor.ToString("0.######", CultureInfo.InvariantCulture)
: string.Empty; : string.Empty;
var lineas = item.ActivarFiltroLineas var lineas = item.ActivarFiltroLineas
? string.Join(",", NormalizarLineas(item.LineasPermitidas)) ? string.Join(",", NormalizarLineas(item.LineasPermitidas))

View File

@@ -826,14 +826,15 @@ public sealed class BuscadorRutasTemporalesDbus
resultado.DistanciasPieTransbordosPorTramo, resultado.DistanciasPieTransbordosPorTramo,
resultado.DistanciaPieFin), resultado.DistanciaPieFin),
parametros); parametros);
var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) &&
? Math.Max(0, parametros.FactorEquilibradoAndarBus) parametros.FactorEquilibradoAndarBus > 0
? parametros.FactorEquilibradoAndarBus
: 6.0; : 6.0;
var penalizacionTransbordos = UtilidadesTransitoDbus.CalcularPenalizacionTransbordosRankingMetros( var penalizacionTransbordos = UtilidadesTransitoDbus.CalcularPenalizacionTransbordosRankingMetros(
UtilidadesTransitoDbus.ContarTransbordosReales(resultado.RutaBus.Tramos), UtilidadesTransitoDbus.ContarTransbordosReales(resultado.RutaBus.Tramos),
parametros); parametros);
var score = distanciaBus + (distanciaAndando * factor) + penalizacionTransbordos; var score = distanciaAndando + (distanciaBus / factor) + penalizacionTransbordos;
return double.IsFinite(score) && score >= 0 return double.IsFinite(score) && score >= 0
? score ? score
: double.PositiveInfinity; : double.PositiveInfinity;

View File

@@ -1543,8 +1543,10 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
: UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaTotalMetros( : UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaTotalMetros(
ConstruirDistanciasPiePorTramo(resultado), ConstruirDistanciasPiePorTramo(resultado),
parametros); parametros);
var factor = parametros is not null && double.IsFinite(parametros.FactorEquilibradoAndarBus) var factor = parametros is not null &&
? Math.Max(0, parametros.FactorEquilibradoAndarBus) double.IsFinite(parametros.FactorEquilibradoAndarBus) &&
parametros.FactorEquilibradoAndarBus > 0
? parametros.FactorEquilibradoAndarBus
: 6.0; : 6.0;
var penalizacionTransbordos = parametros is null var penalizacionTransbordos = parametros is null
? 0 ? 0
@@ -1552,7 +1554,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
UtilidadesTransitoDbus.ContarTransbordosReales(resultado.RutaBus.Tramos), UtilidadesTransitoDbus.ContarTransbordosReales(resultado.RutaBus.Tramos),
parametros); parametros);
var score = distanciaBus + (distanciaAndando * factor) + penalizacionTransbordos; var score = distanciaAndando + (distanciaBus / factor) + penalizacionTransbordos;
return double.IsFinite(score) && score >= 0 return double.IsFinite(score) && score >= 0
? score ? score
: double.PositiveInfinity; : double.PositiveInfinity;
@@ -3244,14 +3246,15 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
var distanciaBus = Math.Max(0, alternativa.AlternativaBus.DistanciaTotalBus); var distanciaBus = Math.Max(0, alternativa.AlternativaBus.DistanciaTotalBus);
var distanciaAndando = CalcularDistanciaAndandoCorregidaMetros(alternativa, parametros); var distanciaAndando = CalcularDistanciaAndandoCorregidaMetros(alternativa, parametros);
var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus) &&
? Math.Max(0, parametros.FactorEquilibradoAndarBus) parametros.FactorEquilibradoAndarBus > 0
? parametros.FactorEquilibradoAndarBus
: 6.0; : 6.0;
var penalizacionTransbordos = UtilidadesTransitoDbus.CalcularPenalizacionTransbordosRankingMetros( var penalizacionTransbordos = UtilidadesTransitoDbus.CalcularPenalizacionTransbordosRankingMetros(
alternativa.NumeroTransbordosReales, alternativa.NumeroTransbordosReales,
parametros); parametros);
var score = distanciaBus + (distanciaAndando * factor) + penalizacionTransbordos; var score = distanciaAndando + (distanciaBus / factor) + penalizacionTransbordos;
return double.IsFinite(score) && score >= 0 return double.IsFinite(score) && score >= 0
? score ? score
: double.PositiveInfinity; : double.PositiveInfinity;
@@ -3508,8 +3511,8 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
} }
/// <summary> /// <summary>
/// Firma orientada a UI: agrupa alternativas con la misma secuencia de lineas y sentidos /// Firma orientada a UI: agrupa solo alternativas con el mismo recorrido visible.
/// aunque cambie la parada exacta. /// Las paradas forman parte de la identidad porque cambian el acceso a pie y sus pendientes.
/// </summary> /// </summary>
private static string CrearFirmaFamiliaRutaBus(RutaBusAlternativa alternativa) private static string CrearFirmaFamiliaRutaBus(RutaBusAlternativa alternativa)
{ {
@@ -3518,7 +3521,8 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
{ {
var linea = UtilidadesTransitoDbus.LineaCodigoStr(tramo.Variante); var linea = UtilidadesTransitoDbus.LineaCodigoStr(tramo.Variante);
var sentido = UtilidadesTransitoDbus.NormalizarSentidoCodigo(tramo.Variante.Sentido?.Codigo); 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)) if (partes.Count == 0 || !string.Equals(partes[^1], parte, StringComparison.OrdinalIgnoreCase))
partes.Add(parte); partes.Add(parte);
} }

View File

@@ -1232,6 +1232,24 @@ html, body {
background: rgba(21,128,61,.18); 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-action,
.history-item-delete { .history-item-delete {
flex: 0 0 32px; flex: 0 0 32px;