cambio foco elemento compartido

poner km en los tramos de bus
line 16 oscurecida
flechas de sentido en las paradas
This commit is contained in:
2026-07-28 08:17:04 +02:00
parent f2e0b59faa
commit 8a75c583ca
8 changed files with 231 additions and 87 deletions

View File

@@ -465,13 +465,70 @@ public partial class PlanificadorRutas : ComponentBase
/// </summary> /// </summary>
private static List<object> BuildStopsDesdeParadas(IEnumerable<Parada> paradas) private static List<object> BuildStopsDesdeParadas(IEnumerable<Parada> paradas)
{ {
var stops = new List<object>(); var paradasOrdenadas = paradas.ToList();
foreach (var parada in paradas) var stops = new List<object>(paradasOrdenadas.Count);
stops.Add(new { lat = parada.Latitud, lon = parada.Longitud, code = parada.Codigo }); for (var indice = 0; indice < paradasOrdenadas.Count; indice++)
{
var parada = paradasOrdenadas[indice];
stops.Add(new
{
lat = parada.Latitud,
lon = parada.Longitud,
code = parada.Codigo,
bearing = CalcularRumboParada(paradasOrdenadas, indice)
});
}
return stops; return stops;
} }
/// <summary>
/// Calcula el sentido de marcha hacia la siguiente parada; en la terminal conserva el rumbo de llegada.
/// </summary>
private static double? CalcularRumboParada(IReadOnlyList<Parada> paradas, int indice)
{
var actual = paradas[indice];
for (var siguiente = indice + 1; siguiente < paradas.Count; siguiente++)
{
var rumbo = CalcularRumboGrados(actual, paradas[siguiente]);
if (rumbo is not null)
return rumbo;
}
for (var anterior = indice - 1; anterior >= 0; anterior--)
{
var rumbo = CalcularRumboGrados(paradas[anterior], actual);
if (rumbo is not null)
return rumbo;
}
return null;
}
private static double? CalcularRumboGrados(Parada origen, Parada destino)
{
if (!double.IsFinite(origen.Latitud) ||
!double.IsFinite(origen.Longitud) ||
!double.IsFinite(destino.Latitud) ||
!double.IsFinite(destino.Longitud))
{
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 y = Math.Sin(diferenciaLongitud) * Math.Cos(latitudDestino);
var x = Math.Cos(latitudOrigen) * Math.Sin(latitudDestino) -
Math.Sin(latitudOrigen) * Math.Cos(latitudDestino) * Math.Cos(diferenciaLongitud);
if (Math.Abs(x) < 1e-12 && Math.Abs(y) < 1e-12)
return null;
var rumbo = Math.Atan2(y, x) * 180.0 / Math.PI;
return (rumbo + 360.0) % 360.0;
}
/// <summary> /// <summary>
/// Construye las paradas visibles recorridas por una variante completa. /// Construye las paradas visibles recorridas por una variante completa.
/// </summary> /// </summary>

View File

@@ -29,6 +29,7 @@ public partial class PlanificadorRutas
private bool _mostrarHistorialRutas = false; private bool _mostrarHistorialRutas = false;
private bool _refrescandoHistorialCompartido = false; private bool _refrescandoHistorialCompartido = false;
private bool _omitirRegistroHistorialEnSiguienteCalculo = false; private bool _omitirRegistroHistorialEnSiguienteCalculo = false;
private bool _aplicandoRegistroHistorial = false;
private string? _historialCompartidoPendienteEliminarId; private string? _historialCompartidoPendienteEliminarId;
private ConfiguracionLocalPlanificador? _configuracionTemporalPreviaHistorial; private ConfiguracionLocalPlanificador? _configuracionTemporalPreviaHistorial;
private Task<bool>? _tareaCargaHistorialLocal; private Task<bool>? _tareaCargaHistorialLocal;
@@ -181,7 +182,8 @@ public partial class PlanificadorRutas
{ {
_historialLocal.Clear(); _historialLocal.Clear();
_historialLocal.AddRange(items); _historialLocal.AddRange(items);
_historialLocalSeleccionadoId = _historialLocal.FirstOrDefault()?.Id ?? string.Empty; if (!_historialLocal.Any(x => x.Id == _historialLocalSeleccionadoId))
_historialLocalSeleccionadoId = string.Empty;
_historialLocalCargado = true; _historialLocalCargado = true;
} }
@@ -232,7 +234,7 @@ public partial class PlanificadorRutas
} }
if (!_historialCompartido.Any(x => x.Id == _historialCompartidoSeleccionadoId)) if (!_historialCompartido.Any(x => x.Id == _historialCompartidoSeleccionadoId))
_historialCompartidoSeleccionadoId = _historialCompartido[0].Id; _historialCompartidoSeleccionadoId = string.Empty;
} }
catch (Exception) catch (Exception)
{ {
@@ -332,13 +334,12 @@ public partial class PlanificadorRutas
while (_historialLocal.Count > MaxItemsHistorialLocal) while (_historialLocal.Count > MaxItemsHistorialLocal)
_historialLocal.RemoveAt(_historialLocal.Count - 1); _historialLocal.RemoveAt(_historialLocal.Count - 1);
_historialLocalSeleccionadoId = existente.Id;
await PersistirHistorialLocalAsync(); await PersistirHistorialLocalAsync();
} }
private async Task CargarHistorialLocalSeleccionadoAsync() private async Task CargarHistorialLocalAsync(string id)
{ {
var item = _historialLocal.FirstOrDefault(x => x.Id == _historialLocalSeleccionadoId); var item = _historialLocal.FirstOrDefault(x => x.Id == id);
if (item is null) if (item is null)
{ {
MostrarMensajeHistorial("No encuentro esa combinacion en tu historial personal.", esError: true); MostrarMensajeHistorial("No encuentro esa combinacion en tu historial personal.", esError: true);
@@ -346,18 +347,13 @@ public partial class PlanificadorRutas
} }
await AplicarRegistroHistorialAsync(item); await AplicarRegistroHistorialAsync(item);
SeleccionarHistorialLocal(id);
MostrarMensajeHistorial("Combinacion personal cargada."); MostrarMensajeHistorial("Combinacion personal cargada.");
} }
private async Task CargarHistorialLocalAsync(string id) private async Task CargarHistorialCompartidoAsync(string id)
{ {
_historialLocalSeleccionadoId = id; var item = _historialCompartido.FirstOrDefault(x => x.Id == id);
await CargarHistorialLocalSeleccionadoAsync();
}
private async Task CargarHistorialCompartidoSeleccionadoAsync()
{
var item = _historialCompartido.FirstOrDefault(x => x.Id == _historialCompartidoSeleccionadoId);
if (item is null) if (item is null)
{ {
MostrarMensajeHistorial("No encuentro esa combinacion en el historial compartido.", esError: true); MostrarMensajeHistorial("No encuentro esa combinacion en el historial compartido.", esError: true);
@@ -365,13 +361,26 @@ public partial class PlanificadorRutas
} }
await AplicarRegistroHistorialAsync(item); await AplicarRegistroHistorialAsync(item);
SeleccionarHistorialCompartido(id);
MostrarMensajeHistorial("Combinación compartida cargada."); MostrarMensajeHistorial("Combinación compartida cargada.");
} }
private async Task CargarHistorialCompartidoAsync(string id) private void SeleccionarHistorialLocal(string id)
{ {
_historialLocalSeleccionadoId = id;
_historialCompartidoSeleccionadoId = string.Empty;
}
private void SeleccionarHistorialCompartido(string id)
{
_historialLocalSeleccionadoId = string.Empty;
_historialCompartidoSeleccionadoId = id; _historialCompartidoSeleccionadoId = id;
await CargarHistorialCompartidoSeleccionadoAsync(); }
private void LimpiarSeleccionHistorial()
{
_historialLocalSeleccionadoId = string.Empty;
_historialCompartidoSeleccionadoId = string.Empty;
} }
private async Task CompartirEscenarioActualAsync() private async Task CompartirEscenarioActualAsync()
@@ -387,9 +396,8 @@ public partial class PlanificadorRutas
try try
{ {
var guardado = await HistorialRutasCompartidoService.GuardarAsync(actual); await HistorialRutasCompartidoService.GuardarAsync(actual);
await CargarHistorialCompartidoAsync(); await CargarHistorialCompartidoAsync();
_historialCompartidoSeleccionadoId = guardado.Id;
MostrarMensajeHistorial("Combinacion enviada al historial compartido."); MostrarMensajeHistorial("Combinacion enviada al historial compartido.");
} }
catch (Exception) catch (Exception)
@@ -400,7 +408,6 @@ public partial class PlanificadorRutas
private async Task CompartirHistorialLocalAsync(string id) private async Task CompartirHistorialLocalAsync(string id)
{ {
_historialLocalSeleccionadoId = id;
var item = _historialLocal.FirstOrDefault(x => x.Id == id); var item = _historialLocal.FirstOrDefault(x => x.Id == id);
if (item is null) if (item is null)
{ {
@@ -410,9 +417,8 @@ public partial class PlanificadorRutas
try try
{ {
var guardado = await HistorialRutasCompartidoService.GuardarAsync(item); await HistorialRutasCompartidoService.GuardarAsync(item);
await CargarHistorialCompartidoAsync(); await CargarHistorialCompartidoAsync();
_historialCompartidoSeleccionadoId = guardado.Id;
MostrarMensajeHistorial("Combinacion enviada al historial compartido."); MostrarMensajeHistorial("Combinacion enviada al historial compartido.");
} }
catch (Exception) catch (Exception)
@@ -433,7 +439,7 @@ public partial class PlanificadorRutas
_historialLocal.Remove(item); _historialLocal.Remove(item);
if (_historialLocalSeleccionadoId == id) if (_historialLocalSeleccionadoId == id)
_historialLocalSeleccionadoId = _historialLocal.FirstOrDefault()?.Id ?? string.Empty; _historialLocalSeleccionadoId = string.Empty;
await PersistirHistorialLocalAsync(); await PersistirHistorialLocalAsync();
MostrarMensajeHistorial("Combinacion eliminada del historial personal."); MostrarMensajeHistorial("Combinacion eliminada del historial personal.");
@@ -477,7 +483,7 @@ public partial class PlanificadorRutas
await CargarHistorialCompartidoAsync(); await CargarHistorialCompartidoAsync();
if (_historialCompartidoSeleccionadoId == id) if (_historialCompartidoSeleccionadoId == id)
_historialCompartidoSeleccionadoId = _historialCompartido.FirstOrDefault()?.Id ?? string.Empty; _historialCompartidoSeleccionadoId = string.Empty;
MostrarMensajeHistorial("Combinacion eliminada del historial compartido."); MostrarMensajeHistorial("Combinacion eliminada del historial compartido.");
} }
@@ -489,61 +495,69 @@ public partial class PlanificadorRutas
private async Task AplicarRegistroHistorialAsync(RegistroHistorialRuta item) private async Task AplicarRegistroHistorialAsync(RegistroHistorialRuta item)
{ {
CapturarConfiguracionTemporalAntesDeHistorial(); _aplicandoRegistroHistorial = true;
try
_omitirRegistroHistorialEnSiguienteCalculo = true;
_textoBusquedaOrigen = item.OrigenTexto;
_textoBusquedaDestino = item.DestinoTexto;
_coordenadasOrigen = new[] { item.OrigenLatitud, item.OrigenLongitud };
_coordenadasDestino = new[] { item.DestinoLatitud, item.DestinoLongitud };
_usarHorariosTeoricos = item.UsarHorariosTeoricos;
_usarHoraSimulada = item.UsarHoraSimulada;
if (DateOnly.TryParse(item.FechaSimulada, out var fecha))
_fechaSimulada = fecha;
if (TimeOnly.TryParse(item.HoraSimulada, out var hora))
_horaSimulada = hora;
_criterioPrioritarioRuta = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta);
SincronizarPreferenciaDesdeCriterioRuta();
if (item.FactorEquilibradoAndarBus is { } factorEquilibrado && double.IsFinite(factorEquilibrado))
{ {
_factorEquilibradoAndarBus = Math.Max(0, factorEquilibrado); CapturarConfiguracionTemporalAntesDeHistorial();
SincronizarTextosPenalizacionPie();
if (_configuracionTemporalPreviaHistorial is not null) _omitirRegistroHistorialEnSiguienteCalculo = true;
_configuracionTemporalPreviaHistorial.FactorEquilibradoAndarBus = _factorEquilibradoAndarBus; _textoBusquedaOrigen = item.OrigenTexto;
_textoBusquedaDestino = item.DestinoTexto;
_coordenadasOrigen = new[] { item.OrigenLatitud, item.OrigenLongitud };
_coordenadasDestino = new[] { item.DestinoLatitud, item.DestinoLongitud };
_usarHorariosTeoricos = item.UsarHorariosTeoricos;
_usarHoraSimulada = item.UsarHoraSimulada;
if (DateOnly.TryParse(item.FechaSimulada, out var fecha))
_fechaSimulada = fecha;
if (TimeOnly.TryParse(item.HoraSimulada, out var hora))
_horaSimulada = hora;
_criterioPrioritarioRuta = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta);
SincronizarPreferenciaDesdeCriterioRuta();
if (item.FactorEquilibradoAndarBus is { } factorEquilibrado && double.IsFinite(factorEquilibrado))
{
_factorEquilibradoAndarBus = Math.Max(0, factorEquilibrado);
SincronizarTextosPenalizacionPie();
if (_configuracionTemporalPreviaHistorial is not null)
_configuracionTemporalPreviaHistorial.FactorEquilibradoAndarBus = _factorEquilibradoAndarBus;
}
_activarRangoLineas = item.ActivarFiltroLineas;
_lineasPermitidas.Clear();
foreach (var linea in NormalizarLineasHistorial(item.LineasPermitidas))
_lineasPermitidas.Add(linea);
_busFilteredCache.Clear();
SincronizarSegundosPorParadaConHorarios();
StopClock();
if (_usarHoraSimulada)
await RefrescarHoraUIAsync();
else
StartClock();
await PersistirConfiguracionAsync();
await CerrarTodasLasPreviewsAsync();
await CerrarPopupsAsync();
if (_mapa is not null)
{
try { await _mapa.Geometric.DisplayPolylinesFromArray.deleteConnectors(); } catch { }
await ClearRoutesJsAsync();
}
await SincronizarMarcadoresOrigenDestinoJsAsync();
await CalcularRutaCompletaAsync();
} }
finally
_activarRangoLineas = item.ActivarFiltroLineas;
_lineasPermitidas.Clear();
foreach (var linea in NormalizarLineasHistorial(item.LineasPermitidas))
_lineasPermitidas.Add(linea);
_busFilteredCache.Clear();
SincronizarSegundosPorParadaConHorarios();
StopClock();
if (_usarHoraSimulada)
await RefrescarHoraUIAsync();
else
StartClock();
await PersistirConfiguracionAsync();
await CerrarTodasLasPreviewsAsync();
await CerrarPopupsAsync();
if (_mapa is not null)
{ {
try { await _mapa.Geometric.DisplayPolylinesFromArray.deleteConnectors(); } catch { } _aplicandoRegistroHistorial = false;
await ClearRoutesJsAsync();
} }
await SincronizarMarcadoresOrigenDestinoJsAsync();
await CalcularRutaCompletaAsync();
} }
private void CapturarConfiguracionTemporalAntesDeHistorial() private void CapturarConfiguracionTemporalAntesDeHistorial()

View File

@@ -1204,7 +1204,7 @@ public partial class PlanificadorRutas : ComponentBase
private bool _elevPopupMinimizado = false; private bool _elevPopupMinimizado = false;
private const string _versionApp = "(v-20260721a)"; private const string _versionApp = "(v-20260727a)";
private double? _paradaElevG; private double? _paradaElevG;

View File

@@ -1552,6 +1552,9 @@ public partial class PlanificadorRutas : ComponentBase
if (_mapa is null || _coordenadasOrigen is null || _coordenadasDestino is null) if (_mapa is null || _coordenadasOrigen is null || _coordenadasDestino is null)
return; return;
if (!_aplicandoRegistroHistorial)
LimpiarSeleccionHistorial();
var cronometroCalculo = System.Diagnostics.Stopwatch.StartNew(); var cronometroCalculo = System.Diagnostics.Stopwatch.StartNew();
_panelRutasMovilContraido = _mostrarBuscadorMapa; _panelRutasMovilContraido = _mostrarBuscadorMapa;

View File

@@ -859,6 +859,13 @@ public partial class PlanificadorRutas : ComponentBase
DateTime? depT = null; DateTime? depT = null;
DateTime? arrT = null; DateTime? arrT = null;
double distanciaTramo = 0;
for (int p = 1; p < lineaGeom.GetLength(0); p++)
{
distanciaTramo += CalcularDistanciaMetros(
lineaGeom[p - 1, 0], lineaGeom[p - 1, 1],
lineaGeom[p, 0], lineaGeom[p, 1]);
}
if (_usarHorariosTeoricos && _horariosOk) if (_usarHorariosTeoricos && _horariosOk)
{ {
@@ -957,21 +964,13 @@ public partial class PlanificadorRutas : ComponentBase
Descripcion = CapitalizarPalabrasClave( Descripcion = CapitalizarPalabrasClave(
$"{HHmm(depT.Value)} -> {HHmm(arrT.Value)} - Linea {codLineaItinSentidoMostrado} ({infoVariante.Linea.Nombre})." $"{HHmm(depT.Value)} -> {HHmm(arrT.Value)} - Linea {codLineaItinSentidoMostrado} ({infoVariante.Linea.Nombre})."
), ),
DistanciaMetros = 0, DistanciaMetros = distanciaTramo,
DuracionSegundos = durBus, DuracionSegundos = durBus,
VarianteBus = infoVariante VarianteBus = infoVariante
}); });
} }
else else
{ {
double distanciaTramo = 0;
for (int p = 1; p < lineaGeom.GetLength(0); p++)
{
distanciaTramo += CalcularDistanciaMetros(
lineaGeom[p - 1, 0], lineaGeom[p - 1, 1],
lineaGeom[p, 0], lineaGeom[p, 1]);
}
int paradasTramo = ContarParadasRecorridas(tramo); int paradasTramo = ContarParadasRecorridas(tramo);
double extraParadasSeg = paradasTramo * Math.Max(0, _segundosPorParadaBus); double extraParadasSeg = paradasTramo * Math.Max(0, _segundosPorParadaBus);
double duracionBusSegundos = EstimarDuracionBusSegundos(distanciaTramo) + extraParadasSeg; double duracionBusSegundos = EstimarDuracionBusSegundos(distanciaTramo) + extraParadasSeg;

View File

@@ -8,6 +8,7 @@ namespace RutasDBUS.Servicios.RedBus;
public class RedBusServicio : IRedBusServicio public class RedBusServicio : IRedBusServicio
{ {
private const string ColorVisualLinea16Argb = "FF5AAED6";
private const string ColorVisualLinea40Argb = "FF9A7B00"; private const string ColorVisualLinea40Argb = "FF9A7B00";
private readonly OpcionesRedBus _opciones; private readonly OpcionesRedBus _opciones;
private readonly ILogger<RedBusServicio> _logger; private readonly ILogger<RedBusServicio> _logger;
@@ -143,7 +144,9 @@ public class RedBusServicio : IRedBusServicio
private static void AjustarColorVisualLinea(LineaBus linea) private static void AjustarColorVisualLinea(LineaBus linea)
{ {
if (string.Equals(linea.Codigo, "40", StringComparison.OrdinalIgnoreCase)) if (string.Equals(linea.Codigo, "16", StringComparison.OrdinalIgnoreCase))
linea.ColorArgb = ColorVisualLinea16Argb;
else if (string.Equals(linea.Codigo, "40", StringComparison.OrdinalIgnoreCase))
linea.ColorArgb = ColorVisualLinea40Argb; linea.ColorArgb = ColorVisualLinea40Argb;
} }

View File

@@ -1755,6 +1755,47 @@ html, body {
border: none; border: none;
} }
.rt-stop-direction-icon {
border: 0;
background: transparent;
pointer-events: none;
}
.rt-stop-direction-arrow {
position: absolute;
top: 50%;
left: 50%;
width: 10px;
height: 12px;
transform: translate(-50%, -50%) rotate(var(--stop-bearing, 0deg));
transform-origin: 50% 50%;
filter: drop-shadow(0 0 1px #0f172a) drop-shadow(0 1px 0 #0f172a);
}
.rt-stop-direction-arrow::before {
content: "";
position: absolute;
top: 0;
left: 50%;
width: 0;
height: 0;
transform: translateX(-50%);
border-right: 3px solid transparent;
border-bottom: 6px solid #f8fafc;
border-left: 3px solid transparent;
}
.rt-stop-direction-arrow::after {
content: "";
position: absolute;
top: 5px;
left: 4px;
width: 2px;
height: 6px;
border-radius: 1px;
background: #f8fafc;
}
.rt-arrow { .rt-arrow {
position: relative; position: relative;
transform-origin: 50% 50%; transform-origin: 50% 50%;

View File

@@ -894,7 +894,32 @@ window.rt.ensureItStopsLayer = function () {
return true; return true;
}; };
// stops: [{lat, lon}] window.rt.addStopDirectionMarker = function (group, stop, pane, radius) {
if (!group || !stop || !window.L) return;
const bearing = Number(stop.bearing);
const lat = Number(stop.lat);
const lon = Number(stop.lon);
if (!Number.isFinite(bearing) || !Number.isFinite(lat) || !Number.isFinite(lon)) return;
const size = Math.max(14, Number(radius || 7) * 2);
const icon = window.L.divIcon({
className: "rt-stop-direction-icon",
html: `<span class="rt-stop-direction-arrow" style="--stop-bearing:${bearing}deg"></span>`,
iconSize: [size, size],
iconAnchor: [size / 2, size / 2]
});
window.L.marker([lat, lon], {
pane,
icon,
interactive: false,
keyboard: false,
zIndexOffset: 1000
}).addTo(group);
};
// stops: [{lat, lon, code, bearing}]
window.rt.toggleItStops = function (id, stops, options) { window.rt.toggleItStops = function (id, stops, options) {
if (!window.rt.ensureItStopsLayer()) return; if (!window.rt.ensureItStopsLayer()) return;
@@ -944,6 +969,7 @@ window.rt.toggleItStops = function (id, stops, options) {
} }
m.addTo(group); m.addTo(group);
window.rt.addStopDirectionMarker(group, s, "rtStopsPane", radius);
} }
} }
@@ -1021,6 +1047,7 @@ window.rt.drawRouteStops = function (id, stops, options) {
} }
m.addTo(group); m.addTo(group);
window.rt.addStopDirectionMarker(group, s, "rtRouteStopsPane", radius);
} }
group.addTo(window.rt._routeStopsLayer); group.addTo(window.rt._routeStopsLayer);