- Horario tb6 cambiado
- Colores de subida/bajada cambiados - On defecto en configuracion actual (texto cambiado) - Coeficiente cambiado a 1.8 - Direccion del bus añadida en el detalle de tramos - Tooltip del buscador - Visualizador de lineas añadido en el listado de las paradas - Palabra RESERVA en mayusculas - Añadido editor de colores simple
This commit is contained in:
@@ -185,6 +185,16 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
|
|||||||
{
|
{
|
||||||
var it = GetSelectedIt();
|
var it = GetSelectedIt();
|
||||||
if (it is null) return;
|
if (it is null) return;
|
||||||
|
|
||||||
|
await QuitarItItemAsync(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Quita del mapa todos los sentidos incluidos en un elemento de itinerario.
|
||||||
|
/// </summary>
|
||||||
|
private async Task QuitarItItemAsync(ElementoItinerario it)
|
||||||
|
{
|
||||||
|
if (it is null) return;
|
||||||
|
|
||||||
// claves a borrar:
|
// claves a borrar:
|
||||||
// - si seleccionas IV => borra I y V
|
// - si seleccionas IV => borra I y V
|
||||||
@@ -204,6 +214,8 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QuitarSalidasParadaActivasPorClaves(claves);
|
||||||
|
|
||||||
foreach (var clave in claves)
|
foreach (var clave in claves)
|
||||||
{
|
{
|
||||||
@@ -243,6 +255,7 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
|
|||||||
|
|
||||||
_itPreviewActivos.Clear();
|
_itPreviewActivos.Clear();
|
||||||
_itPreviewIdsPorClave.Clear();
|
_itPreviewIdsPorClave.Clear();
|
||||||
|
_salidasParadaItinerariosActivas.Clear();
|
||||||
LimpiarParadasItinerarioVisibles();
|
LimpiarParadasItinerarioVisibles();
|
||||||
|
|
||||||
_previewLineasActivas.Clear(); // <- esto ya lo usas para los "full_..."
|
_previewLineasActivas.Clear(); // <- esto ya lo usas para los "full_..."
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private int _nivelBrilloMapa = 0;
|
private int _nivelBrilloMapa = 0;
|
||||||
|
|
||||||
// CONFIG
|
// CONFIG
|
||||||
private const double FactorPenalizacionDistanciaPiePorDefecto = 1.35;
|
private const double FactorPenalizacionDistanciaPiePorDefecto = 1.8;
|
||||||
private const double ParametroPenalizacionDistanciaPieMetrosPorDefecto = 200.0;
|
private const double ParametroPenalizacionDistanciaPieMetrosPorDefecto = 200.0;
|
||||||
private const double CoeficienteEsfuerzoAlturaPorDefecto = 5.2;
|
private const double CoeficienteEsfuerzoAlturaPorDefecto = 5.2;
|
||||||
private const double PendienteMaximaEsfuerzoPorcentaje = 80.0;
|
private const double PendienteMaximaEsfuerzoPorcentaje = 80.0;
|
||||||
@@ -55,8 +55,8 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private const double UmbralPendienteSubidaRutaPorDefecto = 2.0;
|
private const double UmbralPendienteSubidaRutaPorDefecto = 2.0;
|
||||||
private const double UmbralPendienteSubidaMediaRutaPorDefecto = 5.0;
|
private const double UmbralPendienteSubidaMediaRutaPorDefecto = 5.0;
|
||||||
private const double UmbralPendienteSubidaFuerteRutaPorDefecto = 10.0;
|
private const double UmbralPendienteSubidaFuerteRutaPorDefecto = 10.0;
|
||||||
private const string ColorPendienteSubidaSuaveRutaPorDefecto = "#d946ef";
|
private const string ColorPendienteSubidaSuaveRutaPorDefecto = "#ec4899";
|
||||||
private const string ColorPendienteSubidaMediaRutaPorDefecto = "#4c1d95";
|
private const string ColorPendienteSubidaMediaRutaPorDefecto = "#2563eb";
|
||||||
private const string ColorPendienteSubidaFuerteRutaPorDefecto = "#000000";
|
private const string ColorPendienteSubidaFuerteRutaPorDefecto = "#000000";
|
||||||
private const double UmbralPendienteBajadaRutaPorDefecto = 5.0;
|
private const double UmbralPendienteBajadaRutaPorDefecto = 5.0;
|
||||||
private const double UmbralAlturaAlertaPendienteRutaMetrosPorDefecto = 6.0;
|
private const double UmbralAlturaAlertaPendienteRutaMetrosPorDefecto = 6.0;
|
||||||
@@ -68,7 +68,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private double _coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto;
|
private double _coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto;
|
||||||
private double _pesoTransbordoRankingMinutos = 4.0;
|
private double _pesoTransbordoRankingMinutos = 4.0;
|
||||||
private double _factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
private double _factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
||||||
private string _factorPenalizacionDistanciaPieTextoEdicion = "1.35";
|
private string _factorPenalizacionDistanciaPieTextoEdicion = "1.8";
|
||||||
private string _parametroPenalizacionDistanciaPieTextoEdicion = "200";
|
private string _parametroPenalizacionDistanciaPieTextoEdicion = "200";
|
||||||
private string _coeficienteEsfuerzoAlturaTextoEdicion = "5.20";
|
private string _coeficienteEsfuerzoAlturaTextoEdicion = "5.20";
|
||||||
private string _pesoTransbordoRankingTextoEdicion = "4,0";
|
private string _pesoTransbordoRankingTextoEdicion = "4,0";
|
||||||
@@ -1163,7 +1163,10 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
string? dashArray = null,
|
string? dashArray = null,
|
||||||
bool arrowEnd = false,
|
bool arrowEnd = false,
|
||||||
string? lineCap = null,
|
string? lineCap = null,
|
||||||
IReadOnlyList<object>? elevationSegments = null)
|
IReadOnlyList<object>? elevationSegments = null,
|
||||||
|
string? haloColor = null,
|
||||||
|
int? haloWeight = null,
|
||||||
|
double? haloOpacity = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -1171,7 +1174,19 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
"rt.drawRoute",
|
"rt.drawRoute",
|
||||||
id,
|
id,
|
||||||
LineaToLatLng(linea),
|
LineaToLatLng(linea),
|
||||||
new { color, weight, opacity, dashArray, arrowEnd, lineCap, elevationSegments }
|
new
|
||||||
|
{
|
||||||
|
color,
|
||||||
|
weight,
|
||||||
|
opacity,
|
||||||
|
dashArray,
|
||||||
|
arrowEnd,
|
||||||
|
lineCap,
|
||||||
|
elevationSegments,
|
||||||
|
haloColor,
|
||||||
|
haloWeight,
|
||||||
|
haloOpacity
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ public partial class PlanificadorRutas
|
|||||||
public string Fuente { get; init; } = "";
|
public string Fuente { get; init; } = "";
|
||||||
public double Latitud { get; init; }
|
public double Latitud { get; init; }
|
||||||
public double Longitud { get; init; }
|
public double Longitud { get; init; }
|
||||||
|
public string TextoCompleto => string.IsNullOrWhiteSpace(Subtitulo)
|
||||||
|
? Titulo
|
||||||
|
: $"{Titulo} - {Subtitulo}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<SugerenciaBusquedaMapa> _sugerenciasBusquedaOrigen = new();
|
private readonly List<SugerenciaBusquedaMapa> _sugerenciasBusquedaOrigen = new();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public partial class PlanificadorRutas
|
|||||||
private bool _refrescandoHistorialCompartido = false;
|
private bool _refrescandoHistorialCompartido = false;
|
||||||
private bool _omitirRegistroHistorialEnSiguienteCalculo = false;
|
private bool _omitirRegistroHistorialEnSiguienteCalculo = false;
|
||||||
private bool _aplicandoRegistroHistorial = false;
|
private bool _aplicandoRegistroHistorial = false;
|
||||||
private bool _usarParametrosActualesHistorial = false;
|
private bool _usarParametrosActualesHistorial = true;
|
||||||
private bool _cambiandoParametrosHistorial = false;
|
private bool _cambiandoParametrosHistorial = false;
|
||||||
private string? _historialCompartidoPendienteEliminarId;
|
private string? _historialCompartidoPendienteEliminarId;
|
||||||
private ConfiguracionLocalPlanificador? _configuracionTemporalPreviaHistorial;
|
private ConfiguracionLocalPlanificador? _configuracionTemporalPreviaHistorial;
|
||||||
@@ -1488,7 +1488,7 @@ public partial class PlanificadorRutas
|
|||||||
var configuracionPrevia = _configuracionTemporalPreviaHistorial;
|
var configuracionPrevia = _configuracionTemporalPreviaHistorial;
|
||||||
_configuracionTemporalPreviaHistorial = null;
|
_configuracionTemporalPreviaHistorial = null;
|
||||||
_configuracionParametrosActualesHistorial = null;
|
_configuracionParametrosActualesHistorial = null;
|
||||||
_usarParametrosActualesHistorial = false;
|
_usarParametrosActualesHistorial = true;
|
||||||
|
|
||||||
if (configuracionPrevia is null)
|
if (configuracionPrevia is null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -293,6 +293,113 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Alterna desde el popup de parada el itinerario asociado a una salida.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<string, string> _salidasParadaItinerariosActivas =
|
||||||
|
new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private async Task ToggleItinerarioSalidaParadaAsync(ProximaSalidaVm salida)
|
||||||
|
{
|
||||||
|
var itinerario = ObtenerItinerarioSalidaParada(salida);
|
||||||
|
if (itinerario is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var salidaId = CrearIdSalidaParada(salida);
|
||||||
|
var claveItinerario = ItClave(itinerario.Variantes[0]);
|
||||||
|
var esSalidaActiva = _salidasParadaItinerariosActivas.TryGetValue(salidaId, out var claveActiva) &&
|
||||||
|
string.Equals(claveActiva, claveItinerario, StringComparison.OrdinalIgnoreCase);
|
||||||
|
var estaPintado = EstaItinerarioPintado(itinerario);
|
||||||
|
|
||||||
|
if (esSalidaActiva && estaPintado)
|
||||||
|
{
|
||||||
|
await QuitarItItemAsync(itinerario);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!estaPintado)
|
||||||
|
{
|
||||||
|
await PintarItItemAsync(itinerario);
|
||||||
|
await FitRoutesJsAsync(ConstruirCoordenadasItinerario(itinerario));
|
||||||
|
}
|
||||||
|
|
||||||
|
QuitarSalidasParadaActivasPorClaves([claveItinerario]);
|
||||||
|
_salidasParadaItinerariosActivas[salidaId] = claveItinerario;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_itItemsFiltrados.Any(x => x.Id.Equals(itinerario.Id, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
_itSeleccionadoId = itinerario.Id;
|
||||||
|
_selectItKey++;
|
||||||
|
}
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool EstaItinerarioSalidaParadaPintado(ProximaSalidaVm salida)
|
||||||
|
{
|
||||||
|
var itinerario = ObtenerItinerarioSalidaParada(salida);
|
||||||
|
return itinerario is not null &&
|
||||||
|
_salidasParadaItinerariosActivas.TryGetValue(CrearIdSalidaParada(salida), out var claveActiva) &&
|
||||||
|
itinerario.Variantes.Any(variante =>
|
||||||
|
string.Equals(claveActiva, ItClave(variante), StringComparison.OrdinalIgnoreCase)) &&
|
||||||
|
EstaItinerarioPintado(itinerario);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QuitarSalidasParadaActivasPorClaves(IEnumerable<string> claves)
|
||||||
|
{
|
||||||
|
var clavesAEliminar = new HashSet<string>(claves, StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var salidaId in _salidasParadaItinerariosActivas
|
||||||
|
.Where(x => clavesAEliminar.Contains(x.Value))
|
||||||
|
.Select(x => x.Key)
|
||||||
|
.ToList())
|
||||||
|
{
|
||||||
|
_salidasParadaItinerariosActivas.Remove(salidaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CrearIdSalidaParada(ProximaSalidaVm salida)
|
||||||
|
=> string.Join('|',
|
||||||
|
salida.ExpId,
|
||||||
|
salida.RutaId,
|
||||||
|
salida.Hora,
|
||||||
|
salida.ItinerarioClave);
|
||||||
|
|
||||||
|
private bool EstaItinerarioPintado(ElementoItinerario itinerario)
|
||||||
|
=> itinerario.Variantes.Any(variante =>
|
||||||
|
_itPreviewIdsPorClave.TryGetValue(ItClave(variante), out var ids) && ids.Count > 0);
|
||||||
|
|
||||||
|
private ElementoItinerario? ObtenerItinerarioSalidaParada(ProximaSalidaVm salida)
|
||||||
|
{
|
||||||
|
var claves = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
NormalizarClaveItinerarioPopup(salida.ItinerarioClave),
|
||||||
|
NormalizarClaveItinerarioPopup(salida.LineaTxt)
|
||||||
|
};
|
||||||
|
claves.RemoveWhere(string.IsNullOrWhiteSpace);
|
||||||
|
|
||||||
|
return _itItems.FirstOrDefault(item =>
|
||||||
|
item.Variantes.Count == 1 &&
|
||||||
|
claves.Contains(NormalizarClaveItinerarioPopup(ItClave(item.Variantes[0]))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizarClaveItinerarioPopup(string? texto)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(texto))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var clave = texto.Trim();
|
||||||
|
var separadorNombre = clave.IndexOf(" - ", StringComparison.Ordinal);
|
||||||
|
if (separadorNombre >= 0)
|
||||||
|
clave = clave[..separadorNombre];
|
||||||
|
|
||||||
|
const string prefijoLinea = "Línea ";
|
||||||
|
if (clave.StartsWith(prefijoLinea, StringComparison.OrdinalIgnoreCase))
|
||||||
|
clave = clave[prefijoLinea.Length..];
|
||||||
|
|
||||||
|
return clave.Replace(" ", "", StringComparison.Ordinal).ToUpperInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reúne la geometría de todos los sentidos incluidos en un elemento de itinerario.
|
/// Reúne la geometría de todos los sentidos incluidos en un elemento de itinerario.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -345,7 +452,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private async Task MostrarPopupParadaAsync(Parada paradaDetectada)
|
private async Task MostrarPopupParadaAsync(Parada paradaDetectada)
|
||||||
{
|
{
|
||||||
var myToken = Interlocked.Increment(ref _paradaPopupToken);
|
var myToken = Interlocked.Increment(ref _paradaPopupToken);
|
||||||
|
|
||||||
_paradaClicada = null;
|
_paradaClicada = null;
|
||||||
_paradaElevIdee = null;
|
_paradaElevIdee = null;
|
||||||
_paradaElevLocal5 = null;
|
_paradaElevLocal5 = null;
|
||||||
@@ -1202,7 +1309,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private bool _elevPopupMinimizado = false;
|
private bool _elevPopupMinimizado = false;
|
||||||
|
|
||||||
|
|
||||||
private const string _versionApp = "(v-20260831a)";
|
private const string _versionApp = "(v-20260901a)";
|
||||||
|
|
||||||
|
|
||||||
private double? _paradaElevIdee;
|
private double? _paradaElevIdee;
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ namespace RutasDBUS.Components.Pages;
|
|||||||
public partial class PlanificadorRutas
|
public partial class PlanificadorRutas
|
||||||
{
|
{
|
||||||
private const string ClaveConfiguracionLocal = "rutasdbus.planificador.config.v1";
|
private const string ClaveConfiguracionLocal = "rutasdbus.planificador.config.v1";
|
||||||
|
private const int VersionPersistenciaFactorPenalizacionPie = 1;
|
||||||
private const int VersionPersistenciaFactorEquilibrado = 2;
|
private const int VersionPersistenciaFactorEquilibrado = 2;
|
||||||
private const int VersionPersistenciaPendientes = 2;
|
private const int VersionPersistenciaPendientes = 2;
|
||||||
|
private const int VersionPersistenciaColoresPendientes = 1;
|
||||||
private const int VersionPersistenciaCoeficienteEsfuerzo = 1;
|
private const int VersionPersistenciaCoeficienteEsfuerzo = 1;
|
||||||
private const int VersionPersistenciaFuenteElevacion = 1;
|
private const int VersionPersistenciaFuenteElevacion = 1;
|
||||||
private bool _configuracionLocalCargada = false;
|
private bool _configuracionLocalCargada = false;
|
||||||
@@ -38,6 +40,7 @@ public partial class PlanificadorRutas
|
|||||||
public string? FechaSimulada { get; set; }
|
public string? FechaSimulada { get; set; }
|
||||||
public string? HoraSimulada { get; set; }
|
public string? HoraSimulada { get; set; }
|
||||||
public double? FactorPenalizacionDistanciaPie { get; set; }
|
public double? FactorPenalizacionDistanciaPie { get; set; }
|
||||||
|
public int? VersionFactorPenalizacionPie { get; set; }
|
||||||
public double? ParametroPenalizacionDistanciaPieMetros { get; set; }
|
public double? ParametroPenalizacionDistanciaPieMetros { get; set; }
|
||||||
public double? CoeficienteEsfuerzoAltura { get; set; }
|
public double? CoeficienteEsfuerzoAltura { get; set; }
|
||||||
public int? VersionCoeficienteEsfuerzo { get; set; }
|
public int? VersionCoeficienteEsfuerzo { get; set; }
|
||||||
@@ -48,6 +51,7 @@ public partial class PlanificadorRutas
|
|||||||
public string? FuenteElevacionCalculo { get; set; }
|
public string? FuenteElevacionCalculo { get; set; }
|
||||||
public int? VersionFuenteElevacion { get; set; }
|
public int? VersionFuenteElevacion { get; set; }
|
||||||
public int? VersionPendientes { get; set; }
|
public int? VersionPendientes { get; set; }
|
||||||
|
public int? VersionColoresPendientes { get; set; }
|
||||||
public double? UmbralPendienteSubidaRuta { get; set; }
|
public double? UmbralPendienteSubidaRuta { get; set; }
|
||||||
public double? UmbralPendienteSubidaMediaRuta { get; set; }
|
public double? UmbralPendienteSubidaMediaRuta { get; set; }
|
||||||
public double? UmbralPendienteSubidaFuerteRuta { get; set; }
|
public double? UmbralPendienteSubidaFuerteRuta { get; set; }
|
||||||
@@ -161,6 +165,7 @@ public partial class PlanificadorRutas
|
|||||||
FechaSimulada = _fechaSimulada.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
|
FechaSimulada = _fechaSimulada.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
|
||||||
HoraSimulada = _horaSimulada.ToString("HH:mm:ss", CultureInfo.InvariantCulture),
|
HoraSimulada = _horaSimulada.ToString("HH:mm:ss", CultureInfo.InvariantCulture),
|
||||||
FactorPenalizacionDistanciaPie = _factorPenalizacionDistanciaPie,
|
FactorPenalizacionDistanciaPie = _factorPenalizacionDistanciaPie,
|
||||||
|
VersionFactorPenalizacionPie = VersionPersistenciaFactorPenalizacionPie,
|
||||||
ParametroPenalizacionDistanciaPieMetros = _parametroPenalizacionDistanciaPieMetros,
|
ParametroPenalizacionDistanciaPieMetros = _parametroPenalizacionDistanciaPieMetros,
|
||||||
CoeficienteEsfuerzoAltura = _coeficienteEsfuerzoAltura,
|
CoeficienteEsfuerzoAltura = _coeficienteEsfuerzoAltura,
|
||||||
VersionCoeficienteEsfuerzo = VersionPersistenciaCoeficienteEsfuerzo,
|
VersionCoeficienteEsfuerzo = VersionPersistenciaCoeficienteEsfuerzo,
|
||||||
@@ -171,6 +176,7 @@ public partial class PlanificadorRutas
|
|||||||
FuenteElevacionCalculo = _fuenteElevacionCalculo,
|
FuenteElevacionCalculo = _fuenteElevacionCalculo,
|
||||||
VersionFuenteElevacion = VersionPersistenciaFuenteElevacion,
|
VersionFuenteElevacion = VersionPersistenciaFuenteElevacion,
|
||||||
VersionPendientes = VersionPersistenciaPendientes,
|
VersionPendientes = VersionPersistenciaPendientes,
|
||||||
|
VersionColoresPendientes = VersionPersistenciaColoresPendientes,
|
||||||
UmbralPendienteSubidaRuta = _umbralPendienteSubidaRuta,
|
UmbralPendienteSubidaRuta = _umbralPendienteSubidaRuta,
|
||||||
UmbralPendienteSubidaMediaRuta = _umbralPendienteSubidaMediaRuta,
|
UmbralPendienteSubidaMediaRuta = _umbralPendienteSubidaMediaRuta,
|
||||||
UmbralPendienteSubidaFuerteRuta = _umbralPendienteSubidaFuerteRuta,
|
UmbralPendienteSubidaFuerteRuta = _umbralPendienteSubidaFuerteRuta,
|
||||||
@@ -224,9 +230,14 @@ public partial class PlanificadorRutas
|
|||||||
if (TimeOnly.TryParse(config.HoraSimulada, out var horaSimulada))
|
if (TimeOnly.TryParse(config.HoraSimulada, out var horaSimulada))
|
||||||
_horaSimulada = horaSimulada;
|
_horaSimulada = horaSimulada;
|
||||||
|
|
||||||
|
var migrarFactorPenalizacionPie =
|
||||||
|
(config.VersionFactorPenalizacionPie ?? 0) < VersionPersistenciaFactorPenalizacionPie;
|
||||||
if (config.FactorPenalizacionDistanciaPie is { } factor && double.IsFinite(factor))
|
if (config.FactorPenalizacionDistanciaPie is { } factor && double.IsFinite(factor))
|
||||||
_factorPenalizacionDistanciaPie =
|
_factorPenalizacionDistanciaPie =
|
||||||
Math.Abs(factor - 1.3) < 0.0001 || Math.Abs(factor - 1.6) < 0.0001
|
migrarFactorPenalizacionPie &&
|
||||||
|
(Math.Abs(factor - 1.3) < 0.0001 ||
|
||||||
|
Math.Abs(factor - 1.35) < 0.0001 ||
|
||||||
|
Math.Abs(factor - 1.6) < 0.0001)
|
||||||
? FactorPenalizacionDistanciaPiePorDefecto
|
? FactorPenalizacionDistanciaPiePorDefecto
|
||||||
: factor;
|
: factor;
|
||||||
if (config.ParametroPenalizacionDistanciaPieMetros is { } parametro && double.IsFinite(parametro))
|
if (config.ParametroPenalizacionDistanciaPieMetros is { } parametro && double.IsFinite(parametro))
|
||||||
@@ -282,15 +293,23 @@ public partial class PlanificadorRutas
|
|||||||
double.IsFinite(umbralSubidaFuerte)
|
double.IsFinite(umbralSubidaFuerte)
|
||||||
? NormalizarUmbralPendienteRuta(umbralSubidaFuerte)
|
? NormalizarUmbralPendienteRuta(umbralSubidaFuerte)
|
||||||
: UmbralPendienteSubidaFuerteRutaPorDefecto;
|
: UmbralPendienteSubidaFuerteRutaPorDefecto;
|
||||||
_colorPendienteSubidaSuaveRuta = NormalizarColorHexRuta(
|
var migrarColoresPendientes =
|
||||||
config.ColorPendienteSubidaSuaveRuta,
|
(config.VersionColoresPendientes ?? 0) < VersionPersistenciaColoresPendientes;
|
||||||
ColorPendienteSubidaSuaveRutaPorDefecto);
|
_colorPendienteSubidaSuaveRuta = migrarColoresPendientes
|
||||||
_colorPendienteSubidaMediaRuta = NormalizarColorHexRuta(
|
? ColorPendienteSubidaSuaveRutaPorDefecto
|
||||||
config.ColorPendienteSubidaMediaRuta,
|
: NormalizarColorHexRuta(
|
||||||
ColorPendienteSubidaMediaRutaPorDefecto);
|
config.ColorPendienteSubidaSuaveRuta,
|
||||||
_colorPendienteSubidaFuerteRuta = NormalizarColorHexRuta(
|
ColorPendienteSubidaSuaveRutaPorDefecto);
|
||||||
config.ColorPendienteSubidaFuerteRuta,
|
_colorPendienteSubidaMediaRuta = migrarColoresPendientes
|
||||||
ColorPendienteSubidaFuerteRutaPorDefecto);
|
? ColorPendienteSubidaMediaRutaPorDefecto
|
||||||
|
: NormalizarColorHexRuta(
|
||||||
|
config.ColorPendienteSubidaMediaRuta,
|
||||||
|
ColorPendienteSubidaMediaRutaPorDefecto);
|
||||||
|
_colorPendienteSubidaFuerteRuta = migrarColoresPendientes
|
||||||
|
? ColorPendienteSubidaFuerteRutaPorDefecto
|
||||||
|
: NormalizarColorHexRuta(
|
||||||
|
config.ColorPendienteSubidaFuerteRuta,
|
||||||
|
ColorPendienteSubidaFuerteRutaPorDefecto);
|
||||||
NormalizarOrdenUmbralesColorSubidaRuta();
|
NormalizarOrdenUmbralesColorSubidaRuta();
|
||||||
if (config.UmbralPendienteBajadaRuta is { } umbralBajada && double.IsFinite(umbralBajada))
|
if (config.UmbralPendienteBajadaRuta is { } umbralBajada && double.IsFinite(umbralBajada))
|
||||||
_umbralPendienteBajadaRuta =
|
_umbralPendienteBajadaRuta =
|
||||||
|
|||||||
@@ -141,22 +141,36 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
Fuerte
|
Fuerte
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CambiarColorPendienteSubidaRutaAsync(
|
private NivelColorSubidaRuta? _selectorColorPendienteRuta;
|
||||||
ChangeEventArgs e,
|
|
||||||
|
private void AbrirConfiguracionPendientesRuta()
|
||||||
|
{
|
||||||
|
_selectorColorPendienteRuta = null;
|
||||||
|
_mostrarConfiguracionPendientesRuta = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CerrarConfiguracionPendientesRuta()
|
||||||
|
{
|
||||||
|
_selectorColorPendienteRuta = null;
|
||||||
|
_mostrarConfiguracionPendientesRuta = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AlternarSelectorColorPendienteRuta(NivelColorSubidaRuta nivel)
|
||||||
|
=> _selectorColorPendienteRuta = _selectorColorPendienteRuta == nivel ? null : nivel;
|
||||||
|
|
||||||
|
private string ObtenerColorPendienteSubidaRuta(NivelColorSubidaRuta nivel)
|
||||||
|
=> nivel switch
|
||||||
|
{
|
||||||
|
NivelColorSubidaRuta.Media => _colorPendienteSubidaMediaRuta,
|
||||||
|
NivelColorSubidaRuta.Fuerte => _colorPendienteSubidaFuerteRuta,
|
||||||
|
_ => _colorPendienteSubidaSuaveRuta
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task AplicarColorPendienteSubidaRutaAsync(
|
||||||
|
string color,
|
||||||
NivelColorSubidaRuta nivel)
|
NivelColorSubidaRuta nivel)
|
||||||
{
|
{
|
||||||
var (actual, porDefecto) = nivel switch
|
var nuevo = NormalizarColorHexRuta(color, ObtenerColorPendienteSubidaRuta(nivel));
|
||||||
{
|
|
||||||
NivelColorSubidaRuta.Media =>
|
|
||||||
(_colorPendienteSubidaMediaRuta, ColorPendienteSubidaMediaRutaPorDefecto),
|
|
||||||
NivelColorSubidaRuta.Fuerte =>
|
|
||||||
(_colorPendienteSubidaFuerteRuta, ColorPendienteSubidaFuerteRutaPorDefecto),
|
|
||||||
_ =>
|
|
||||||
(_colorPendienteSubidaSuaveRuta, ColorPendienteSubidaSuaveRutaPorDefecto)
|
|
||||||
};
|
|
||||||
var nuevo = NormalizarColorHexRuta(
|
|
||||||
Convert.ToString(e.Value, CultureInfo.InvariantCulture),
|
|
||||||
NormalizarColorHexRuta(actual, porDefecto));
|
|
||||||
|
|
||||||
switch (nivel)
|
switch (nivel)
|
||||||
{
|
{
|
||||||
@@ -171,6 +185,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_selectorColorPendienteRuta = null;
|
||||||
await PersistirConfiguracionAsync();
|
await PersistirConfiguracionAsync();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@@ -355,7 +370,10 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
dashArray: DashArrayPiePunteado,
|
dashArray: DashArrayPiePunteado,
|
||||||
arrowEnd: arrowEnd,
|
arrowEnd: arrowEnd,
|
||||||
lineCap: "round",
|
lineCap: "round",
|
||||||
elevationSegments: resultadoElevacion.Segmentos);
|
elevationSegments: resultadoElevacion.Segmentos,
|
||||||
|
haloColor: "#ffffff",
|
||||||
|
haloWeight: weight + 3,
|
||||||
|
haloOpacity: 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(
|
private async Task<(
|
||||||
@@ -520,6 +538,15 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
_distanciaPendientePositivaSignificativaRutaMetros = 0.0;
|
_distanciaPendientePositivaSignificativaRutaMetros = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ObtenerNombreDestinoItinerario(InfoVariante variante)
|
||||||
|
{
|
||||||
|
if (variante.CodigosParadas.Count == 0)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var ultimaParada = ResolverParadaFisica(variante.CodigosParadas[^1]);
|
||||||
|
return ultimaParada.Nombre?.Trim() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private double? ElevacionPreferidaMapa(MuestraElevacion muestra)
|
private double? ElevacionPreferidaMapa(MuestraElevacion muestra)
|
||||||
=> ObtenerElevacionFuenteCalculo(muestra);
|
=> ObtenerElevacionFuenteCalculo(muestra);
|
||||||
|
|
||||||
@@ -561,7 +588,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
ColorPendienteSubidaSuaveRutaPorDefecto));
|
ColorPendienteSubidaSuaveRutaPorDefecto));
|
||||||
|
|
||||||
if (pendientePorcentaje.Value <= -umbralBajada)
|
if (pendientePorcentaje.Value <= -umbralBajada)
|
||||||
return ("bajada", "#2563eb");
|
return ("bajada", "#16a34a");
|
||||||
|
|
||||||
return ("llano", "#dc2626");
|
return ("llano", "#dc2626");
|
||||||
}
|
}
|
||||||
@@ -829,6 +856,10 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
var tramo = tramosBus[i];
|
var tramo = tramosBus[i];
|
||||||
var esTransbordoReal = i > 0 && UtilidadesTransitoDbus.EsTransbordoReal(tramosBus[i - 1], tramo);
|
var esTransbordoReal = i > 0 && UtilidadesTransitoDbus.EsTransbordoReal(tramosBus[i - 1], tramo);
|
||||||
var infoVariante = tramo.Variante;
|
var infoVariante = tramo.Variante;
|
||||||
|
var nombreDestinoItinerario = ObtenerNombreDestinoItinerario(infoVariante);
|
||||||
|
var textoDestinoItinerario = string.IsNullOrWhiteSpace(nombreDestinoItinerario)
|
||||||
|
? string.Empty
|
||||||
|
: $" → {nombreDestinoItinerario}";
|
||||||
var lineaGeom = lineasBus[i];
|
var lineaGeom = lineasBus[i];
|
||||||
|
|
||||||
string codParadaIni = infoVariante.CodigosParadas[tramo.IndiceInicio];
|
string codParadaIni = infoVariante.CodigosParadas[tramo.IndiceInicio];
|
||||||
@@ -1100,7 +1131,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
{
|
{
|
||||||
Tipo = "Bus",
|
Tipo = "Bus",
|
||||||
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}){textoDestinoItinerario}."
|
||||||
),
|
),
|
||||||
DistanciaMetros = distanciaTramo,
|
DistanciaMetros = distanciaTramo,
|
||||||
DuracionSegundos = durBus,
|
DuracionSegundos = durBus,
|
||||||
@@ -1120,7 +1151,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
{
|
{
|
||||||
Tipo = "Bus",
|
Tipo = "Bus",
|
||||||
Descripcion = CapitalizarPalabrasClave(
|
Descripcion = CapitalizarPalabrasClave(
|
||||||
$"Linea {codLineaItinSentidoMostrado} ({infoVariante.Linea.Nombre})."
|
$"Linea {codLineaItinSentidoMostrado} ({infoVariante.Linea.Nombre}){textoDestinoItinerario}."
|
||||||
),
|
),
|
||||||
DistanciaMetros = distanciaTramo,
|
DistanciaMetros = distanciaTramo,
|
||||||
DuracionSegundos = duracionBusSegundos,
|
DuracionSegundos = duracionBusSegundos,
|
||||||
|
|||||||
@@ -222,6 +222,12 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private bool HorariosTaxibusSimuladosVisibles
|
private bool HorariosTaxibusSimuladosVisibles
|
||||||
=> _nextDeps.Any(salida => TaxibusDbus.EsExpedicionSimulada(salida.ExpId));
|
=> _nextDeps.Any(salida => TaxibusDbus.EsExpedicionSimulada(salida.ExpId));
|
||||||
|
|
||||||
|
private string MensajeHorarioTaxibusSimuladoVisible
|
||||||
|
=> TaxibusDbus.ObtenerMensajeHorarioSimulado(
|
||||||
|
_nextDeps
|
||||||
|
.Where(salida => TaxibusDbus.EsExpedicionSimulada(salida.ExpId))
|
||||||
|
.SelectMany(salida => new[] { salida.RutaId, salida.LineaTxt }));
|
||||||
|
|
||||||
private static string ObtenerAvisoTaxibus(AlternativaRuta alternativa)
|
private static string ObtenerAvisoTaxibus(AlternativaRuta alternativa)
|
||||||
{
|
{
|
||||||
var lineasTaxibus = alternativa.AlternativaBus?.Tramos
|
var lineasTaxibus = alternativa.AlternativaBus?.Tramos
|
||||||
@@ -233,8 +239,13 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
if (lineasTaxibus is not { Count: > 0 })
|
if (lineasTaxibus is not { Count: > 0 })
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
return lineasTaxibus.Any(linea => linea.HorarioSimulado)
|
var lineasSimuladas = lineasTaxibus
|
||||||
? TaxibusDbus.MensajeHorarioSimulado
|
.Where(linea => linea.HorarioSimulado)
|
||||||
|
.Select(linea => linea.Codigo)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return lineasSimuladas.Count > 0
|
||||||
|
? TaxibusDbus.ObtenerMensajeHorarioSimulado(lineasSimuladas)
|
||||||
: TaxibusDbus.MensajeReserva;
|
: TaxibusDbus.MensajeReserva;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,7 @@
|
|||||||
{
|
{
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="gm-suggestion"
|
class="gm-suggestion"
|
||||||
|
title="@sugerencia.TextoCompleto"
|
||||||
@onclick="() => SeleccionarSugerenciaBusquedaAsync(CampoBusquedaMapa.Origen, sugerencia)">
|
@onclick="() => SeleccionarSugerenciaBusquedaAsync(CampoBusquedaMapa.Origen, sugerencia)">
|
||||||
<span class="gm-suggestion__type">@sugerencia.Tipo</span>
|
<span class="gm-suggestion__type">@sugerencia.Tipo</span>
|
||||||
<span class="gm-suggestion__text">
|
<span class="gm-suggestion__text">
|
||||||
@@ -196,6 +197,7 @@
|
|||||||
{
|
{
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="gm-suggestion"
|
class="gm-suggestion"
|
||||||
|
title="@sugerencia.TextoCompleto"
|
||||||
@onclick="() => SeleccionarSugerenciaBusquedaAsync(CampoBusquedaMapa.Destino, sugerencia)">
|
@onclick="() => SeleccionarSugerenciaBusquedaAsync(CampoBusquedaMapa.Destino, sugerencia)">
|
||||||
<span class="gm-suggestion__type">@sugerencia.Tipo</span>
|
<span class="gm-suggestion__type">@sugerencia.Tipo</span>
|
||||||
<span class="gm-suggestion__text">
|
<span class="gm-suggestion__text">
|
||||||
@@ -302,17 +304,28 @@
|
|||||||
@if (HorariosTaxibusSimuladosVisibles)
|
@if (HorariosTaxibusSimuladosVisibles)
|
||||||
{
|
{
|
||||||
<div class="taxibus-notice taxibus-notice--schedule" role="status">
|
<div class="taxibus-notice taxibus-notice--schedule" role="status">
|
||||||
<strong>Taxibús:</strong> @TaxibusDbus.MensajeHorarioSimulado
|
<strong>Taxibús:</strong> @MensajeHorarioTaxibusSimuladoVisible
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div style="max-height: 220px; overflow-y: auto; border: 1px solid rgba(255,255,255,.12); border-radius: 6px; padding: .35rem .5rem; scroll-behavior: smooth;">
|
<div class="stop-schedule-list">
|
||||||
<ul style="margin:0; padding-left:1.1rem;">
|
<ul>
|
||||||
@foreach (var x in NextDepsOrdenados)
|
@foreach (var x in NextDepsOrdenados)
|
||||||
{
|
{
|
||||||
|
var itinerarioPintado = EstaItinerarioSalidaParadaPintado(x);
|
||||||
<li id="@x.AnchorId"
|
<li id="@x.AnchorId"
|
||||||
style="margin:.15rem 0; @(x.EsProximo ? "background: rgba(255,255,255,.12); border-radius:6px; padding:2px 6px;" : "")">
|
class="stop-schedule-row @(x.EsProximo ? "stop-schedule-row--next" : "")">
|
||||||
<b>@x.Hora</b> - @x.LineaTxt
|
<span class="stop-schedule-row__text">
|
||||||
|
<b>@x.Hora</b> - @x.LineaTxt
|
||||||
|
</span>
|
||||||
|
<button type="button"
|
||||||
|
class="stop-itinerary-toggle @(itinerarioPintado ? "is-active" : "")"
|
||||||
|
title="@(itinerarioPintado ? "Ocultar itinerario" : "Ver itinerario")"
|
||||||
|
aria-label="@(itinerarioPintado ? "Ocultar itinerario" : "Ver itinerario")"
|
||||||
|
aria-pressed="@itinerarioPintado"
|
||||||
|
@onclick="() => ToggleItinerarioSalidaParadaAsync(x)">
|
||||||
|
<span aria-hidden="true">👁</span>
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1085,7 +1098,7 @@
|
|||||||
<div class="history-panel-body">
|
<div class="history-panel-body">
|
||||||
<label class="history-parameters-toggle @(_usarParametrosActualesHistorial ? "history-parameters-toggle--active" : "")"
|
<label class="history-parameters-toggle @(_usarParametrosActualesHistorial ? "history-parameters-toggle--active" : "")"
|
||||||
title="Recalcular las rutas del historial con la configuración actual">
|
title="Recalcular las rutas del historial con la configuración actual">
|
||||||
<span class="history-parameters-toggle__label">Usar parámetros actuales</span>
|
<span class="history-parameters-toggle__label">Usar configuración actual</span>
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
checked="@_usarParametrosActualesHistorial"
|
checked="@_usarParametrosActualesHistorial"
|
||||||
disabled="@(_aplicandoRegistroHistorial || _cambiandoParametrosHistorial)"
|
disabled="@(_aplicandoRegistroHistorial || _cambiandoParametrosHistorial)"
|
||||||
@@ -1769,7 +1782,7 @@
|
|||||||
title="Configurar Pendientes"
|
title="Configurar Pendientes"
|
||||||
aria-label="Configurar Pendientes"
|
aria-label="Configurar Pendientes"
|
||||||
disabled="@_pintandoElevacionesRuta"
|
disabled="@_pintandoElevacionesRuta"
|
||||||
@onclick="() => _mostrarConfiguracionPendientesRuta = true">
|
@onclick="AbrirConfiguracionPendientesRuta">
|
||||||
<span aria-hidden="true">⚙</span>
|
<span aria-hidden="true">⚙</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1808,7 +1821,7 @@
|
|||||||
@if (_mostrarConfiguracionPendientesRuta)
|
@if (_mostrarConfiguracionPendientesRuta)
|
||||||
{
|
{
|
||||||
<div class="custom-modal-backdrop pendientes-config-modal-backdrop"
|
<div class="custom-modal-backdrop pendientes-config-modal-backdrop"
|
||||||
@onclick="() => _mostrarConfiguracionPendientesRuta = false">
|
@onclick="CerrarConfiguracionPendientesRuta">
|
||||||
<div class="custom-modal-card pendientes-config-modal" @onclick:stopPropagation="true">
|
<div class="custom-modal-card pendientes-config-modal" @onclick:stopPropagation="true">
|
||||||
<div class="pendientes-config-modal__header">
|
<div class="pendientes-config-modal__header">
|
||||||
<div class="custom-modal-title">Configurar Pendientes</div>
|
<div class="custom-modal-title">Configurar Pendientes</div>
|
||||||
@@ -1816,7 +1829,7 @@
|
|||||||
class="config-close-btn pendientes-config-modal__close"
|
class="config-close-btn pendientes-config-modal__close"
|
||||||
title="Cerrar"
|
title="Cerrar"
|
||||||
aria-label="Cerrar"
|
aria-label="Cerrar"
|
||||||
@onclick="() => _mostrarConfiguracionPendientesRuta = false">
|
@onclick="CerrarConfiguracionPendientesRuta">
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1844,14 +1857,21 @@
|
|||||||
|
|
||||||
<div class="pendientes-config-modal__field">
|
<div class="pendientes-config-modal__field">
|
||||||
<span class="pendientes-config-modal__threshold-label">
|
<span class="pendientes-config-modal__threshold-label">
|
||||||
<input type="color"
|
<button type="button"
|
||||||
class="pendientes-config-modal__swatch"
|
class="pendientes-config-modal__swatch"
|
||||||
value="@_colorPendienteSubidaSuaveRuta"
|
style="background-color: @_colorPendienteSubidaSuaveRuta"
|
||||||
title="Elegir color de subida"
|
title="Cambiar color de subida"
|
||||||
aria-label="Elegir color de subida"
|
aria-label="Cambiar color de subida"
|
||||||
disabled="@_pintandoElevacionesRuta"
|
disabled="@_pintandoElevacionesRuta"
|
||||||
@onchange="@(e => CambiarColorPendienteSubidaRutaAsync(e, NivelColorSubidaRuta.Suave))" />
|
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorSubidaRuta.Suave)">
|
||||||
|
</button>
|
||||||
<span>Umbral Subida (%)</span>
|
<span>Umbral Subida (%)</span>
|
||||||
|
@if (_selectorColorPendienteRuta == NivelColorSubidaRuta.Suave)
|
||||||
|
{
|
||||||
|
<SelectorColorSimple Color="@_colorPendienteSubidaSuaveRuta"
|
||||||
|
Disabled="@_pintandoElevacionesRuta"
|
||||||
|
ColorChanged="@(color => AplicarColorPendienteSubidaRutaAsync(color, NivelColorSubidaRuta.Suave))" />
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
class="config-input"
|
class="config-input"
|
||||||
@@ -1864,14 +1884,21 @@
|
|||||||
|
|
||||||
<div class="pendientes-config-modal__field">
|
<div class="pendientes-config-modal__field">
|
||||||
<span class="pendientes-config-modal__threshold-label">
|
<span class="pendientes-config-modal__threshold-label">
|
||||||
<input type="color"
|
<button type="button"
|
||||||
class="pendientes-config-modal__swatch"
|
class="pendientes-config-modal__swatch"
|
||||||
value="@_colorPendienteSubidaMediaRuta"
|
style="background-color: @_colorPendienteSubidaMediaRuta"
|
||||||
title="Elegir color de subida media"
|
title="Cambiar color de subida media"
|
||||||
aria-label="Elegir color de subida media"
|
aria-label="Cambiar color de subida media"
|
||||||
disabled="@_pintandoElevacionesRuta"
|
disabled="@_pintandoElevacionesRuta"
|
||||||
@onchange="@(e => CambiarColorPendienteSubidaRutaAsync(e, NivelColorSubidaRuta.Media))" />
|
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorSubidaRuta.Media)">
|
||||||
|
</button>
|
||||||
<span>Subida Media (%)</span>
|
<span>Subida Media (%)</span>
|
||||||
|
@if (_selectorColorPendienteRuta == NivelColorSubidaRuta.Media)
|
||||||
|
{
|
||||||
|
<SelectorColorSimple Color="@_colorPendienteSubidaMediaRuta"
|
||||||
|
Disabled="@_pintandoElevacionesRuta"
|
||||||
|
ColorChanged="@(color => AplicarColorPendienteSubidaRutaAsync(color, NivelColorSubidaRuta.Media))" />
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
class="config-input"
|
class="config-input"
|
||||||
@@ -1884,14 +1911,21 @@
|
|||||||
|
|
||||||
<div class="pendientes-config-modal__field">
|
<div class="pendientes-config-modal__field">
|
||||||
<span class="pendientes-config-modal__threshold-label">
|
<span class="pendientes-config-modal__threshold-label">
|
||||||
<input type="color"
|
<button type="button"
|
||||||
class="pendientes-config-modal__swatch"
|
class="pendientes-config-modal__swatch"
|
||||||
value="@_colorPendienteSubidaFuerteRuta"
|
style="background-color: @_colorPendienteSubidaFuerteRuta"
|
||||||
title="Elegir color de subida fuerte"
|
title="Cambiar color de subida fuerte"
|
||||||
aria-label="Elegir color de subida fuerte"
|
aria-label="Cambiar color de subida fuerte"
|
||||||
disabled="@_pintandoElevacionesRuta"
|
disabled="@_pintandoElevacionesRuta"
|
||||||
@onchange="@(e => CambiarColorPendienteSubidaRutaAsync(e, NivelColorSubidaRuta.Fuerte))" />
|
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorSubidaRuta.Fuerte)">
|
||||||
|
</button>
|
||||||
<span>Subida Fuerte (%)</span>
|
<span>Subida Fuerte (%)</span>
|
||||||
|
@if (_selectorColorPendienteRuta == NivelColorSubidaRuta.Fuerte)
|
||||||
|
{
|
||||||
|
<SelectorColorSimple Color="@_colorPendienteSubidaFuerteRuta"
|
||||||
|
Disabled="@_pintandoElevacionesRuta"
|
||||||
|
ColorChanged="@(color => AplicarColorPendienteSubidaRutaAsync(color, NivelColorSubidaRuta.Fuerte))" />
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
class="config-input"
|
class="config-input"
|
||||||
|
|||||||
58
RutasDBUS/Components/SelectorColorSimple.razor
Normal file
58
RutasDBUS/Components/SelectorColorSimple.razor
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<div class="pendientes-color-picker" @onclick:stopPropagation="true">
|
||||||
|
<div class="pendientes-color-picker__title">Colores básicos</div>
|
||||||
|
|
||||||
|
<div class="pendientes-color-picker__presets">
|
||||||
|
@foreach (var color in ColoresPredefinidos)
|
||||||
|
{
|
||||||
|
var seleccionado = string.Equals(Color, color.Hex, StringComparison.OrdinalIgnoreCase);
|
||||||
|
<button type="button"
|
||||||
|
class="pendientes-color-picker__preset @(seleccionado ? "is-selected" : null)"
|
||||||
|
style="background-color: @color.Hex"
|
||||||
|
title="@color.Nombre"
|
||||||
|
aria-label="Usar color @color.Nombre"
|
||||||
|
disabled="@Disabled"
|
||||||
|
@onclick="() => SeleccionarColorAsync(color.Hex)">
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="pendientes-color-picker__advanced">
|
||||||
|
<span>Color exacto</span>
|
||||||
|
<input type="color"
|
||||||
|
value="@Color"
|
||||||
|
aria-label="Elegir color exacto"
|
||||||
|
disabled="@Disabled"
|
||||||
|
@onchange="CambiarColorExactoAsync" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private static readonly (string Nombre, string Hex)[] ColoresPredefinidos =
|
||||||
|
[
|
||||||
|
("Verde", "#16a34a"),
|
||||||
|
("Amarillo", "#eab308"),
|
||||||
|
("Naranja", "#f97316"),
|
||||||
|
("Rojo", "#dc2626"),
|
||||||
|
("Rosa", "#ec4899"),
|
||||||
|
("Violeta", "#7c3aed"),
|
||||||
|
("Azul", "#2563eb"),
|
||||||
|
("Cian", "#0891b2"),
|
||||||
|
("Gris", "#64748b"),
|
||||||
|
("Negro", "#000000")
|
||||||
|
];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Color { get; set; } = "#000000";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<string> ColorChanged { get; set; }
|
||||||
|
|
||||||
|
private Task SeleccionarColorAsync(string color)
|
||||||
|
=> ColorChanged.InvokeAsync(color);
|
||||||
|
|
||||||
|
private Task CambiarColorExactoAsync(ChangeEventArgs e)
|
||||||
|
=> ColorChanged.InvokeAsync(Convert.ToString(e.Value) ?? Color);
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ public sealed class ParametrosPlanificadorRuta
|
|||||||
public int MinutosPorTransbordo { get; set; } = 5;
|
public int MinutosPorTransbordo { get; set; } = 5;
|
||||||
public int MargenTransbordoSegundos { get; set; } = 120;
|
public int MargenTransbordoSegundos { get; set; } = 120;
|
||||||
public int SegundosPorParadaBus { get; set; } = 30;
|
public int SegundosPorParadaBus { get; set; } = 30;
|
||||||
public double FactorPenalizacionDistanciaPie { get; set; } = 1.35;
|
public double FactorPenalizacionDistanciaPie { get; set; } = 1.8;
|
||||||
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.
|
// Divisor aplicado a la distancia en bus dentro del score equilibrado.
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ namespace RutasDBUS.Servicios.Importacion;
|
|||||||
internal static class SimuladorHorariosTaxibus
|
internal static class SimuladorHorariosTaxibus
|
||||||
{
|
{
|
||||||
private const string IdCalendario = "TAXIBUS_SIM_TODOS_LOS_DIAS";
|
private const string IdCalendario = "TAXIBUS_SIM_TODOS_LOS_DIAS";
|
||||||
private const int FrecuenciaMinutos = 30;
|
private const int FrecuenciaMinutosTb6 = 10;
|
||||||
|
private const int FrecuenciaMinutosRestoTaxibus = 30;
|
||||||
private const int MinutoPrimeraSalida = 7 * 60;
|
private const int MinutoPrimeraSalida = 7 * 60;
|
||||||
private const int MinutoFinServicioExclusivo = 24 * 60;
|
private const int MinutoFinServicioExclusivoTb6 = 22 * 60;
|
||||||
|
private const int MinutoFinServicioExclusivoRestoTaxibus = 24 * 60;
|
||||||
private const double VelocidadMetrosSegundo = 16_000.0 / 3_600.0;
|
private const double VelocidadMetrosSegundo = 16_000.0 / 3_600.0;
|
||||||
private const int SegundosMinimosEntreParadas = 30;
|
private const int SegundosMinimosEntreParadas = 30;
|
||||||
|
|
||||||
@@ -65,10 +67,14 @@ internal static class SimuladorHorariosTaxibus
|
|||||||
|
|
||||||
for (var indicePlantilla = 0; indicePlantilla < plantillas.Count; indicePlantilla++)
|
for (var indicePlantilla = 0; indicePlantilla < plantillas.Count; indicePlantilla++)
|
||||||
{
|
{
|
||||||
|
var esTb6 = TaxibusDbus.EsLineaTb6(ruta.Abreviatura) ||
|
||||||
|
TaxibusDbus.EsLineaTb6(ruta.Id);
|
||||||
CrearExpedicionesSimuladas(
|
CrearExpedicionesSimuladas(
|
||||||
ruta,
|
ruta,
|
||||||
plantillas[indicePlantilla],
|
plantillas[indicePlantilla],
|
||||||
indicePlantilla,
|
indicePlantilla,
|
||||||
|
esTb6 ? FrecuenciaMinutosTb6 : FrecuenciaMinutosRestoTaxibus,
|
||||||
|
esTb6 ? MinutoFinServicioExclusivoTb6 : MinutoFinServicioExclusivoRestoTaxibus,
|
||||||
expediciones,
|
expediciones,
|
||||||
tiemposParada);
|
tiemposParada);
|
||||||
}
|
}
|
||||||
@@ -134,13 +140,15 @@ internal static class SimuladorHorariosTaxibus
|
|||||||
FilaRutaCsv ruta,
|
FilaRutaCsv ruta,
|
||||||
PlantillaTaxibus plantilla,
|
PlantillaTaxibus plantilla,
|
||||||
int indicePlantilla,
|
int indicePlantilla,
|
||||||
|
int frecuenciaMinutos,
|
||||||
|
int minutoFinServicioExclusivo,
|
||||||
ICollection<FilaExpedicionCsv> expediciones,
|
ICollection<FilaExpedicionCsv> expediciones,
|
||||||
ICollection<FilaTiempoParadaCsv> tiemposParada)
|
ICollection<FilaTiempoParadaCsv> tiemposParada)
|
||||||
{
|
{
|
||||||
var indiceSalida = 0;
|
var indiceSalida = 0;
|
||||||
for (var minutoInicio = MinutoPrimeraSalida;
|
for (var minutoInicio = MinutoPrimeraSalida;
|
||||||
minutoInicio < MinutoFinServicioExclusivo;
|
minutoInicio < minutoFinServicioExclusivo;
|
||||||
minutoInicio += FrecuenciaMinutos, indiceSalida++)
|
minutoInicio += frecuenciaMinutos, indiceSalida++)
|
||||||
{
|
{
|
||||||
var segundosInicio = minutoInicio * 60;
|
var segundosInicio = minutoInicio * 60;
|
||||||
var horaInicio = HoraDesdeSegundos(segundosInicio);
|
var horaInicio = HoraDesdeSegundos(segundosInicio);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public sealed class BuscadorRutasTemporalesDbus
|
|||||||
{
|
{
|
||||||
private const int MaxEstadosPorParada = 4;
|
private const int MaxEstadosPorParada = 4;
|
||||||
private const int MaxContinuidadesCircularesExtra = 2;
|
private const int MaxContinuidadesCircularesExtra = 2;
|
||||||
|
private const int MaxBusesSinHorarios = 4;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Busca alternativas de bus entre grupos de paradas candidatas.
|
/// Busca alternativas de bus entre grupos de paradas candidatas.
|
||||||
@@ -58,7 +59,14 @@ public sealed class BuscadorRutasTemporalesDbus
|
|||||||
rondas.Add(rondaInicial);
|
rondas.Add(rondaInicial);
|
||||||
var marcadas = new HashSet<string>(rondaInicial.Values.Select(estado => estado.CodigoParada), StringComparer.OrdinalIgnoreCase);
|
var marcadas = new HashSet<string>(rondaInicial.Values.Select(estado => estado.CodigoParada), StringComparer.OrdinalIgnoreCase);
|
||||||
var maximoBuses = ResolverMaximoBuses(parametros);
|
var maximoBuses = ResolverMaximoBuses(parametros);
|
||||||
var codigosParadaRelevantes = CrearFiltroParadasRelevantes(indice, candidatasDestino, parametros);
|
var codigosParadaRelevantes = parametros.UsarHorariosReales
|
||||||
|
? CrearFiltroParadasRelevantes(indice, candidatasDestino, parametros)
|
||||||
|
: CrearFiltroParadasRelevantesSinHorarios(
|
||||||
|
indice,
|
||||||
|
candidatasDestino,
|
||||||
|
parametros,
|
||||||
|
maximoBuses,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
for (var ronda = 1; ronda <= maximoBuses && marcadas.Count > 0; ronda++)
|
for (var ronda = 1; ronda <= maximoBuses && marcadas.Count > 0; ronda++)
|
||||||
{
|
{
|
||||||
@@ -137,7 +145,15 @@ public sealed class BuscadorRutasTemporalesDbus
|
|||||||
/// Calcula el maximo de buses a considerar.
|
/// Calcula el maximo de buses a considerar.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static int ResolverMaximoBuses(ParametrosPlanificadorRuta parametros)
|
private static int ResolverMaximoBuses(ParametrosPlanificadorRuta parametros)
|
||||||
=> Math.Max(2, Math.Max(0, parametros.MaxTransbordosPermitidos) + 1 + MaxContinuidadesCircularesExtra);
|
{
|
||||||
|
var maximoConfigurado = Math.Max(
|
||||||
|
2,
|
||||||
|
Math.Max(0, parametros.MaxTransbordosPermitidos) + 1 + MaxContinuidadesCircularesExtra);
|
||||||
|
|
||||||
|
return parametros.UsarHorariosReales
|
||||||
|
? maximoConfigurado
|
||||||
|
: Math.Min(maximoConfigurado, MaxBusesSinHorarios);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recorre un patron en modo horario real.
|
/// Recorre un patron en modo horario real.
|
||||||
@@ -547,6 +563,64 @@ public sealed class BuscadorRutasTemporalesDbus
|
|||||||
return relevantes;
|
return relevantes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Conserva, en modo aproximado, solo paradas desde las que aun se puede alcanzar el destino.
|
||||||
|
/// La expansion inversa respeta el maximo acotado de tramos de bus.
|
||||||
|
/// </summary>
|
||||||
|
private static HashSet<string> CrearFiltroParadasRelevantesSinHorarios(
|
||||||
|
IndiceTransitoDbus indice,
|
||||||
|
IReadOnlyList<ParadaCandidata> candidatasDestino,
|
||||||
|
ParametrosPlanificadorRuta parametros,
|
||||||
|
int maximoBuses,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var alcanzables = new HashSet<string>(
|
||||||
|
candidatasDestino.Select(x => x.Parada.Codigo),
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
for (var ronda = 0; ronda < maximoBuses; ronda++)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var objetivosBus = new HashSet<string>(alcanzables, StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var transferenciaOrigen in indice.TransferenciasPorParada)
|
||||||
|
{
|
||||||
|
if (transferenciaOrigen.Value.Any(transferencia =>
|
||||||
|
alcanzables.Contains(transferencia.CodigoParadaDestino)))
|
||||||
|
{
|
||||||
|
objetivosBus.Add(transferenciaOrigen.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alcanzables.UnionWith(objetivosBus);
|
||||||
|
var nuevas = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var patron in indice.Patrones)
|
||||||
|
{
|
||||||
|
if (!PatronPermitido(patron, parametros))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var conduceAlDestino = false;
|
||||||
|
for (var indiceParada = patron.CodigosParada.Count - 1; indiceParada >= 0; indiceParada--)
|
||||||
|
{
|
||||||
|
var codigoParada = patron.CodigosParada[indiceParada];
|
||||||
|
if (objetivosBus.Contains(codigoParada))
|
||||||
|
conduceAlDestino = true;
|
||||||
|
|
||||||
|
if (conduceAlDestino)
|
||||||
|
nuevas.Add(codigoParada);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nuevas.IsSubsetOf(alcanzables))
|
||||||
|
break;
|
||||||
|
|
||||||
|
alcanzables.UnionWith(nuevas);
|
||||||
|
}
|
||||||
|
|
||||||
|
return alcanzables;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ParadaEsRelevante(string codigoParada, IReadOnlySet<string>? codigosParadaRelevantes)
|
private static bool ParadaEsRelevante(string codigoParada, IReadOnlySet<string>? codigosParadaRelevantes)
|
||||||
=> codigosParadaRelevantes is null || codigosParadaRelevantes.Contains(codigoParada);
|
=> codigosParadaRelevantes is null || codigosParadaRelevantes.Contains(codigoParada);
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
private const double FactorAhorroClaroAndandoFinal = 0.75;
|
private const double FactorAhorroClaroAndandoFinal = 0.75;
|
||||||
private const double UmbralAccesoCercanoRefinamientoInicioMetros = 120.0;
|
private const double UmbralAccesoCercanoRefinamientoInicioMetros = 120.0;
|
||||||
private const double MaxIncrementoAccesoCercanoRefinamientoInicioMetros = 60.0;
|
private const double MaxIncrementoAccesoCercanoRefinamientoInicioMetros = 60.0;
|
||||||
|
private const int MaxCandidatasRefinamientoSinHorarios = 20;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inicializa una nueva instancia del planificador.
|
/// Inicializa una nueva instancia del planificador.
|
||||||
@@ -287,6 +288,13 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
MaxContinuidadesCircularesConsecutivas))
|
MaxContinuidadesCircularesConsecutivas))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
if (!parametros.UsarHorariosReales)
|
||||||
|
{
|
||||||
|
candidatasBus = SeleccionarCandidatasRefinamientoSinHorarios(
|
||||||
|
candidatasBus,
|
||||||
|
parametros);
|
||||||
|
}
|
||||||
|
|
||||||
var alternativasBus = new List<AlternativaRuta>();
|
var alternativasBus = new List<AlternativaRuta>();
|
||||||
foreach (var candidataBus in candidatasBus)
|
foreach (var candidataBus in candidatasBus)
|
||||||
{
|
{
|
||||||
@@ -1530,6 +1538,121 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Limita el refinamiento exacto del modo sin horarios conservando rutas variadas.
|
||||||
|
/// </summary>
|
||||||
|
private List<ResultadoBusquedaRutaBusDbus> SeleccionarCandidatasRefinamientoSinHorarios(
|
||||||
|
IEnumerable<ResultadoBusquedaRutaBusDbus> candidatas,
|
||||||
|
ParametrosPlanificadorRuta parametros)
|
||||||
|
{
|
||||||
|
var mejoresPorFirma = new Dictionary<string, ResultadoBusquedaRutaBusDbus>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var candidata in candidatas)
|
||||||
|
{
|
||||||
|
var firma = UtilidadesTransitoDbus.FirmaRutaBus(candidata.RutaBus);
|
||||||
|
if (!mejoresPorFirma.TryGetValue(firma, out var actual) ||
|
||||||
|
ResultadoBusquedaEsMejor(candidata, actual))
|
||||||
|
{
|
||||||
|
mejoresPorFirma[firma] = candidata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lista = mejoresPorFirma.Values.ToList();
|
||||||
|
if (lista.Count <= MaxCandidatasRefinamientoSinHorarios)
|
||||||
|
return OrdenarCandidatasRefinamiento(lista, parametros);
|
||||||
|
|
||||||
|
var seleccionadas = new List<ResultadoBusquedaRutaBusDbus>(MaxCandidatasRefinamientoSinHorarios);
|
||||||
|
var firmasSeleccionadas = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
Agregar(lista
|
||||||
|
.GroupBy(x => x.ParadaOrigen.Codigo, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(grupo => grupo
|
||||||
|
.OrderBy(x => CalcularScoreBusquedaEquilibrado(x, parametros))
|
||||||
|
.ThenBy(x => x.PuntuacionCoste)
|
||||||
|
.ThenBy(x => x.DistanciaPieTotal)
|
||||||
|
.First())
|
||||||
|
.OrderBy(x => CalcularScoreBusquedaEquilibrado(x, parametros))
|
||||||
|
.Take(6));
|
||||||
|
|
||||||
|
Agregar(lista
|
||||||
|
.OrderBy(x => CalcularScoreBusquedaEquilibrado(x, parametros))
|
||||||
|
.ThenBy(x => x.RutaBus.NumeroTransbordos)
|
||||||
|
.ThenBy(x => x.PuntuacionCoste)
|
||||||
|
.Take(5));
|
||||||
|
|
||||||
|
Agregar(lista
|
||||||
|
.OrderBy(x => x.PuntuacionCoste)
|
||||||
|
.ThenBy(x => x.DuracionEstimadaSegundos)
|
||||||
|
.ThenBy(x => x.DistanciaPieTotal)
|
||||||
|
.Take(3));
|
||||||
|
|
||||||
|
Agregar(lista
|
||||||
|
.OrderBy(x => x.DistanciaPieTotal)
|
||||||
|
.ThenBy(x => CalcularScoreBusquedaEquilibrado(x, parametros))
|
||||||
|
.ThenBy(x => x.DuracionEstimadaSegundos)
|
||||||
|
.Take(3));
|
||||||
|
|
||||||
|
Agregar(lista
|
||||||
|
.Where(x => UtilidadesTransitoDbus.ContarTransbordosReales(x.RutaBus.Tramos) == 0)
|
||||||
|
.OrderBy(x => CalcularScoreBusquedaEquilibrado(x, parametros))
|
||||||
|
.ThenBy(x => x.DuracionEstimadaSegundos)
|
||||||
|
.Take(2));
|
||||||
|
|
||||||
|
Agregar(lista
|
||||||
|
.OrderBy(x => x.DuracionEstimadaSegundos)
|
||||||
|
.ThenBy(x => x.PuntuacionCoste)
|
||||||
|
.Take(2));
|
||||||
|
|
||||||
|
Agregar(lista
|
||||||
|
.GroupBy(CrearClavePrimeraLineaCandidata, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(grupo => grupo
|
||||||
|
.OrderBy(x => CalcularScoreBusquedaEquilibrado(x, parametros))
|
||||||
|
.ThenBy(x => x.PuntuacionCoste)
|
||||||
|
.First())
|
||||||
|
.OrderBy(x => CalcularScoreBusquedaEquilibrado(x, parametros)));
|
||||||
|
|
||||||
|
Agregar(lista
|
||||||
|
.OrderBy(x => CalcularScoreBusquedaEquilibrado(x, parametros))
|
||||||
|
.ThenBy(x => x.PuntuacionCoste));
|
||||||
|
|
||||||
|
return OrdenarCandidatasRefinamiento(seleccionadas, parametros);
|
||||||
|
|
||||||
|
void Agregar(IEnumerable<ResultadoBusquedaRutaBusDbus> origen)
|
||||||
|
{
|
||||||
|
foreach (var candidata in origen)
|
||||||
|
{
|
||||||
|
if (seleccionadas.Count >= MaxCandidatasRefinamientoSinHorarios)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var firma = UtilidadesTransitoDbus.FirmaRutaBus(candidata.RutaBus);
|
||||||
|
if (firmasSeleccionadas.Add(firma))
|
||||||
|
seleccionadas.Add(candidata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResultadoBusquedaRutaBusDbus> OrdenarCandidatasRefinamiento(
|
||||||
|
IEnumerable<ResultadoBusquedaRutaBusDbus> candidatas,
|
||||||
|
ParametrosPlanificadorRuta parametros)
|
||||||
|
=> candidatas
|
||||||
|
.OrderBy(x => CalcularScoreBusquedaEquilibrado(x, parametros))
|
||||||
|
.ThenBy(x => x.PuntuacionCoste)
|
||||||
|
.ThenBy(x => x.DuracionEstimadaSegundos)
|
||||||
|
.ThenBy(x => x.DistanciaPieTotal)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
private static string CrearClavePrimeraLineaCandidata(ResultadoBusquedaRutaBusDbus candidata)
|
||||||
|
{
|
||||||
|
var primerTramo = candidata.RutaBus.Tramos.FirstOrDefault();
|
||||||
|
if (primerTramo is null)
|
||||||
|
return candidata.ParadaOrigen.Codigo;
|
||||||
|
|
||||||
|
return string.Join('|',
|
||||||
|
candidata.ParadaOrigen.Codigo,
|
||||||
|
UtilidadesTransitoDbus.LineaCodigoStr(primerTramo.Variante),
|
||||||
|
primerTramo.Variante.CodigoItinerario,
|
||||||
|
UtilidadesTransitoDbus.NormalizarSentidoCodigo(primerTramo.Variante.Sentido?.Codigo));
|
||||||
|
}
|
||||||
|
|
||||||
private double CalcularScoreBusquedaEquilibrado(
|
private double CalcularScoreBusquedaEquilibrado(
|
||||||
ResultadoBusquedaRutaBusDbus resultado,
|
ResultadoBusquedaRutaBusDbus resultado,
|
||||||
ParametrosPlanificadorRuta? parametros)
|
ParametrosPlanificadorRuta? parametros)
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ internal static class UtilidadesTransitoDbus
|
|||||||
|
|
||||||
var factor = double.IsFinite(parametros.FactorPenalizacionDistanciaPie)
|
var factor = double.IsFinite(parametros.FactorPenalizacionDistanciaPie)
|
||||||
? parametros.FactorPenalizacionDistanciaPie
|
? parametros.FactorPenalizacionDistanciaPie
|
||||||
: 1.35;
|
: 1.8;
|
||||||
var parametroBaseMetros = double.IsFinite(parametros.ParametroPenalizacionDistanciaPieMetros)
|
var parametroBaseMetros = double.IsFinite(parametros.ParametroPenalizacionDistanciaPieMetros)
|
||||||
? Math.Abs(parametros.ParametroPenalizacionDistanciaPieMetros)
|
? Math.Abs(parametros.ParametroPenalizacionDistanciaPieMetros)
|
||||||
: 200.0;
|
: 200.0;
|
||||||
|
|||||||
@@ -6,24 +6,56 @@ namespace RutasDBUS.Servicios;
|
|||||||
public static class TaxibusDbus
|
public static class TaxibusDbus
|
||||||
{
|
{
|
||||||
public const string PrefijoExpedicionSimulada = "TAXIBUS_SIM_";
|
public const string PrefijoExpedicionSimulada = "TAXIBUS_SIM_";
|
||||||
public const string MensajeReserva = "Esta l\u00ednea requiere solicitud de reserva.";
|
public const string MensajeReserva = "Esta l\u00ednea requiere solicitud de RESERVA.";
|
||||||
|
public const string MensajeHorarioSimuladoTb6 =
|
||||||
|
"Horario provisional simulado cada 10 min, de 07:00 a 22:00. Esta l\u00ednea requiere solicitud de RESERVA.";
|
||||||
public const string MensajeHorarioSimulado =
|
public const string MensajeHorarioSimulado =
|
||||||
"Horario provisional simulado cada 30 min, de 07:00 a 00:00. Esta l\u00ednea requiere solicitud de reserva.";
|
"Horario provisional simulado cada 30 min, de 07:00 a 00:00. Esta l\u00ednea requiere solicitud de RESERVA.";
|
||||||
|
|
||||||
public static bool EsLinea(string? codigo)
|
public static bool EsLinea(string? codigo)
|
||||||
|
=> EsLineaTb6(codigo) || EsLineaTb7(codigo);
|
||||||
|
|
||||||
|
public static bool EsLineaTb6(string? codigo)
|
||||||
|
{
|
||||||
|
var normalizado = NormalizarCodigoLinea(codigo);
|
||||||
|
return normalizado.Equals("TB6", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
normalizado.Equals("6", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ObtenerMensajeHorarioSimulado(IEnumerable<string?> codigosLinea)
|
||||||
|
{
|
||||||
|
var codigos = codigosLinea.Where(codigo => !string.IsNullOrWhiteSpace(codigo)).ToList();
|
||||||
|
var mensajes = new List<string>(2);
|
||||||
|
|
||||||
|
if (codigos.Any(EsLineaTb6))
|
||||||
|
mensajes.Add(MensajeHorarioSimuladoTb6);
|
||||||
|
|
||||||
|
if (codigos.Any(codigo => EsLineaTb7(codigo)))
|
||||||
|
mensajes.Add(MensajeHorarioSimulado);
|
||||||
|
|
||||||
|
return mensajes.Count > 0
|
||||||
|
? string.Join(" ", mensajes)
|
||||||
|
: MensajeHorarioSimulado;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool EsLineaTb7(string? codigo)
|
||||||
|
{
|
||||||
|
var normalizado = NormalizarCodigoLinea(codigo);
|
||||||
|
return normalizado.Equals("TB7", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
normalizado.Equals("7", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizarCodigoLinea(string? codigo)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(codigo))
|
if (string.IsNullOrWhiteSpace(codigo))
|
||||||
return false;
|
return string.Empty;
|
||||||
|
|
||||||
var normalizado = codigo.Trim();
|
var normalizado = codigo.Trim();
|
||||||
var separador = normalizado.IndexOf('.');
|
var separador = normalizado.IndexOf('.');
|
||||||
if (separador > 0)
|
if (separador > 0)
|
||||||
normalizado = normalizado[..separador];
|
normalizado = normalizado[..separador];
|
||||||
|
|
||||||
return normalizado.Equals("TB6", StringComparison.OrdinalIgnoreCase) ||
|
return normalizado;
|
||||||
normalizado.Equals("TB7", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
normalizado.Equals("6", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
normalizado.Equals("7", StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool EsExpedicionSimulada(string? idExpedicion)
|
public static bool EsExpedicionSimulada(string? idExpedicion)
|
||||||
|
|||||||
@@ -791,6 +791,69 @@ html, body {
|
|||||||
margin-bottom: .15rem;
|
margin-bottom: .15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stop-schedule-list {
|
||||||
|
max-height: 220px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid rgba(255,255,255,.12);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: .3rem;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-schedule-list ul {
|
||||||
|
display: grid;
|
||||||
|
gap: 2px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-schedule-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) 26px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
min-height: 28px;
|
||||||
|
padding: 2px 3px 2px 6px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-schedule-row--next {
|
||||||
|
background: rgba(255,255,255,.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-schedule-row__text {
|
||||||
|
min-width: 0;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-itinerary-toggle {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0;
|
||||||
|
border: 1px solid rgba(148,163,184,.45);
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #cbd5e1;
|
||||||
|
background: rgba(15,23,42,.62);
|
||||||
|
font-size: .82rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-itinerary-toggle:hover {
|
||||||
|
border-color: #38bdf8;
|
||||||
|
color: #ffffff;
|
||||||
|
background: rgba(14,116,144,.34);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-itinerary-toggle.is-active {
|
||||||
|
border-color: #38bdf8;
|
||||||
|
color: #ffffff;
|
||||||
|
background: #0369a1;
|
||||||
|
box-shadow: 0 0 0 1px rgba(125,211,252,.24);
|
||||||
|
}
|
||||||
|
|
||||||
.popup-parada-cerrar {
|
.popup-parada-cerrar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: .3rem;
|
top: .3rem;
|
||||||
@@ -1799,6 +1862,7 @@ html, body {
|
|||||||
.history-panel *,
|
.history-panel *,
|
||||||
.config-panel,
|
.config-panel,
|
||||||
.config-panel *,
|
.config-panel *,
|
||||||
|
.gm-suggestions,
|
||||||
.custom-modal-card,
|
.custom-modal-card,
|
||||||
.custom-modal-card *,
|
.custom-modal-card *,
|
||||||
.popup-stack-left,
|
.popup-stack-left,
|
||||||
@@ -1817,6 +1881,7 @@ html, body {
|
|||||||
.history-panel *,
|
.history-panel *,
|
||||||
.config-panel,
|
.config-panel,
|
||||||
.config-panel *,
|
.config-panel *,
|
||||||
|
.gm-suggestions,
|
||||||
.custom-modal-card,
|
.custom-modal-card,
|
||||||
.custom-modal-card *,
|
.custom-modal-card *,
|
||||||
.popup-stack-left,
|
.popup-stack-left,
|
||||||
@@ -1835,6 +1900,7 @@ html, body {
|
|||||||
.history-panel *,
|
.history-panel *,
|
||||||
.config-panel,
|
.config-panel,
|
||||||
.config-panel *,
|
.config-panel *,
|
||||||
|
.gm-suggestions,
|
||||||
.custom-modal-card,
|
.custom-modal-card,
|
||||||
.custom-modal-card *,
|
.custom-modal-card *,
|
||||||
.popup-stack-left,
|
.popup-stack-left,
|
||||||
@@ -1853,6 +1919,7 @@ html, body {
|
|||||||
.history-panel *,
|
.history-panel *,
|
||||||
.config-panel,
|
.config-panel,
|
||||||
.config-panel *,
|
.config-panel *,
|
||||||
|
.gm-suggestions,
|
||||||
.custom-modal-card,
|
.custom-modal-card,
|
||||||
.custom-modal-card *,
|
.custom-modal-card *,
|
||||||
.popup-stack-left,
|
.popup-stack-left,
|
||||||
@@ -1872,6 +1939,7 @@ html, body {
|
|||||||
.history-panel *,
|
.history-panel *,
|
||||||
.config-panel,
|
.config-panel,
|
||||||
.config-panel *,
|
.config-panel *,
|
||||||
|
.gm-suggestions,
|
||||||
.custom-modal-card,
|
.custom-modal-card,
|
||||||
.custom-modal-card *,
|
.custom-modal-card *,
|
||||||
.popup-stack-left,
|
.popup-stack-left,
|
||||||
@@ -2280,6 +2348,7 @@ html, body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .38rem;
|
gap: .38rem;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pendientes-config-modal__swatch {
|
.pendientes-config-modal__swatch {
|
||||||
@@ -2289,22 +2358,9 @@ html, body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
border: 1px solid rgba(226, 232, 240, .7);
|
border: 1px solid rgba(226, 232, 240, .7);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background: transparent;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
appearance: none;
|
||||||
}
|
|
||||||
|
|
||||||
.pendientes-config-modal__swatch::-webkit-color-swatch-wrapper {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pendientes-config-modal__swatch::-webkit-color-swatch {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pendientes-config-modal__swatch::-moz-color-swatch {
|
|
||||||
border: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pendientes-config-modal__swatch:hover,
|
.pendientes-config-modal__swatch:hover,
|
||||||
@@ -2319,6 +2375,77 @@ html, body {
|
|||||||
opacity: .55;
|
opacity: .55;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pendientes-color-picker {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 30;
|
||||||
|
top: calc(100% + .45rem);
|
||||||
|
left: 0;
|
||||||
|
width: 188px;
|
||||||
|
padding: .6rem;
|
||||||
|
border: 1px solid rgba(148, 163, 184, .42);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #0f172a;
|
||||||
|
box-shadow: 0 14px 34px rgba(2, 6, 23, .72);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pendientes-color-picker__title {
|
||||||
|
margin-bottom: .48rem;
|
||||||
|
color: #e2e8f0;
|
||||||
|
font-size: .72rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pendientes-color-picker__presets {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: .45rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pendientes-color-picker__preset {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0;
|
||||||
|
justify-self: center;
|
||||||
|
border: 1px solid rgba(248, 250, 252, .55);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pendientes-color-picker__preset:hover,
|
||||||
|
.pendientes-color-picker__preset:focus-visible {
|
||||||
|
outline: 2px solid #7dd3fc;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pendientes-color-picker__preset.is-selected {
|
||||||
|
border-color: #f8fafc;
|
||||||
|
box-shadow: 0 0 0 2px #38bdf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pendientes-color-picker__advanced {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: .65rem;
|
||||||
|
margin-top: .58rem;
|
||||||
|
padding-top: .52rem;
|
||||||
|
border-top: 1px solid rgba(148, 163, 184, .24);
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-size: .72rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pendientes-color-picker__advanced input[type="color"] {
|
||||||
|
width: 34px;
|
||||||
|
height: 25px;
|
||||||
|
padding: 1px;
|
||||||
|
border: 1px solid rgba(226, 232, 240, .55);
|
||||||
|
border-radius: 3px;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.config-row-map-opacity {
|
.config-row-map-opacity {
|
||||||
margin-top: .7rem !important;
|
margin-top: .7rem !important;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|||||||
@@ -278,6 +278,11 @@ window.rt.drawRoute = function (id, coords, options) {
|
|||||||
const dashArray = options && options.dashArray;
|
const dashArray = options && options.dashArray;
|
||||||
const lineCap = options && options.lineCap;
|
const lineCap = options && options.lineCap;
|
||||||
const arrowEnd = !!(options && options.arrowEnd);
|
const arrowEnd = !!(options && options.arrowEnd);
|
||||||
|
const haloColor = options && options.haloColor;
|
||||||
|
const haloWeight = Number(options && options.haloWeight);
|
||||||
|
const haloOpacity = Number(options && options.haloOpacity);
|
||||||
|
const hasHalo = typeof haloColor === "string" && haloColor.length > 0 &&
|
||||||
|
Number.isFinite(haloWeight) && haloWeight > weight;
|
||||||
const elevationSegments = Array.isArray(options && options.elevationSegments)
|
const elevationSegments = Array.isArray(options && options.elevationSegments)
|
||||||
? options.elevationSegments
|
? options.elevationSegments
|
||||||
: [];
|
: [];
|
||||||
@@ -380,6 +385,19 @@ window.rt.drawRoute = function (id, coords, options) {
|
|||||||
};
|
};
|
||||||
if (dashArray) segmentOpts.dashArray = dashArray;
|
if (dashArray) segmentOpts.dashArray = dashArray;
|
||||||
|
|
||||||
|
if (hasHalo) {
|
||||||
|
const haloOpts = {
|
||||||
|
color: haloColor,
|
||||||
|
weight: haloWeight,
|
||||||
|
opacity: Number.isFinite(haloOpacity) ? haloOpacity : opacity,
|
||||||
|
lineCap: lineCap || "round",
|
||||||
|
interactive: false,
|
||||||
|
bubblingMouseEvents: false
|
||||||
|
};
|
||||||
|
if (dashArray) haloOpts.dashArray = dashArray;
|
||||||
|
window.L.polyline(segmentCoords, haloOpts).addTo(group);
|
||||||
|
}
|
||||||
|
|
||||||
const segmentLine = buildLine(segmentCoords, segmentOpts);
|
const segmentLine = buildLine(segmentCoords, segmentOpts);
|
||||||
const originalSegmentStyle = {
|
const originalSegmentStyle = {
|
||||||
color: segmentColor,
|
color: segmentColor,
|
||||||
@@ -441,8 +459,27 @@ window.rt.drawRoute = function (id, coords, options) {
|
|||||||
if (lineCap) lineOpts.lineCap = lineCap;
|
if (lineCap) lineOpts.lineCap = lineCap;
|
||||||
|
|
||||||
const line = buildLine(coords, lineOpts);
|
const line = buildLine(coords, lineOpts);
|
||||||
line.addTo(window.rt._routeLayer);
|
if (hasHalo) {
|
||||||
window.rt._routeLines[id] = line;
|
const group = window.L.layerGroup();
|
||||||
|
const haloOpts = {
|
||||||
|
color: haloColor,
|
||||||
|
weight: haloWeight,
|
||||||
|
opacity: Number.isFinite(haloOpacity) ? haloOpacity : opacity,
|
||||||
|
lineCap: lineCap || "round",
|
||||||
|
interactive: false,
|
||||||
|
bubblingMouseEvents: false
|
||||||
|
};
|
||||||
|
if (dashArray) haloOpts.dashArray = dashArray;
|
||||||
|
|
||||||
|
window.L.polyline(coords, haloOpts).addTo(group);
|
||||||
|
line.addTo(group);
|
||||||
|
group.addTo(window.rt._routeLayer);
|
||||||
|
window.rt._routeLines[id] = group;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
line.addTo(window.rt._routeLayer);
|
||||||
|
window.rt._routeLines[id] = line;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arrowEnd && coords && coords.length >= 2) {
|
if (arrowEnd && coords && coords.length >= 2) {
|
||||||
|
|||||||
Reference in New Issue
Block a user