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>
/// 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>
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);
}
/// <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)
{
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()

View File

@@ -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))

View File

@@ -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;

View File

@@ -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

View File

@@ -1270,9 +1270,123 @@ public partial class PlanificadorRutas : ComponentBase
var listaOrdenada = ordenadas.ToList();
_opcionesRuta.Clear();
_opcionesRuta.AddRange(listaOrdenada);
FiltrarAlternativasBusRedundantes();
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()
{
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();

View File

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

View File

@@ -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 @@
</p>
}
@if (dRecta > 0 || dCorregida > 0 || dEquilibradaAndando > 0)
@if (dRecta > 0 || dCorregida > 0)
{
<div class="muted" style="font-size:0.75rem;margin-top:4px;">
@if (dRecta > 0)
@@ -945,10 +944,6 @@
{
<div>d.Corregida a pie: @FormatearDistanciaDetalleRuta(dCorregida)</div>
}
@if (dEquilibradaAndando > 0)
{
<div>d.Equilib. A: @FormatearDistanciaDetalleRuta(dEquilibradaAndando)</div>
}
@if (!string.IsNullOrWhiteSpace(scoreEquilibradoSeleccionado))
{
<div>Score equilibrado A: @scoreEquilibradoSeleccionado</div>
@@ -1067,12 +1062,18 @@
{
var item = _historialLocal[i];
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"
class="history-item-main history-item-main--numbered"
@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>
</button>
@@ -1129,12 +1130,18 @@
{
var item = _historialCompartido[i];
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"
class="history-item-main history-item-main--numbered"
@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>
</button>
@@ -1150,7 +1157,17 @@
}
</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" : "")">
@_mensajeHistorial
@@ -1499,9 +1516,9 @@
<div class="config-dual-labeled">
<label class="config-dual-labeled__field">
<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"
title="Factor del modo equilibrado"
title="Divisor de la distancia en bus en el modo equilibrado"
@bind="FactorEquilibradoAndarBusTexto" @bind:event="oninput"
@onblur="OnBlurFactorEquilibradoAndarBus" />
</label>