Añadido horario simualdo con advertencia a lostaxibus
añadido desseleccion de segmentos y iluminado del mismo añadido limites nuevos en lugares y ordenes alfabeticos quitado acalarion b5m defectos de L5 y maximos al 80% formula cambiada a 5.2 y division por distancia de bus dsitancia con z anteriores
This commit is contained in:
@@ -49,7 +49,8 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
// CONFIG
|
// CONFIG
|
||||||
private const double FactorPenalizacionDistanciaPiePorDefecto = 1.35;
|
private const double FactorPenalizacionDistanciaPiePorDefecto = 1.35;
|
||||||
private const double ParametroPenalizacionDistanciaPieMetrosPorDefecto = 200.0;
|
private const double ParametroPenalizacionDistanciaPieMetrosPorDefecto = 200.0;
|
||||||
private const double CoeficienteEsfuerzoAlturaPorDefecto = 8.2;
|
private const double CoeficienteEsfuerzoAlturaPorDefecto = 5.2;
|
||||||
|
private const double PendienteMaximaEsfuerzoPorcentaje = 80.0;
|
||||||
private const double FactorEquilibradoAndarBusPorDefecto = 6.0;
|
private const double FactorEquilibradoAndarBusPorDefecto = 6.0;
|
||||||
private const double UmbralPendienteSubidaRutaPorDefecto = 2.0;
|
private const double UmbralPendienteSubidaRutaPorDefecto = 2.0;
|
||||||
private const double UmbralPendienteBajadaRutaPorDefecto = 5.0;
|
private const double UmbralPendienteBajadaRutaPorDefecto = 5.0;
|
||||||
@@ -64,7 +65,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private double _factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
private double _factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
||||||
private string _factorPenalizacionDistanciaPieTextoEdicion = "1.35";
|
private string _factorPenalizacionDistanciaPieTextoEdicion = "1.35";
|
||||||
private string _parametroPenalizacionDistanciaPieTextoEdicion = "200";
|
private string _parametroPenalizacionDistanciaPieTextoEdicion = "200";
|
||||||
private string _coeficienteEsfuerzoAlturaTextoEdicion = "8.20";
|
private string _coeficienteEsfuerzoAlturaTextoEdicion = "5.20";
|
||||||
private string _pesoTransbordoRankingTextoEdicion = "4,0";
|
private string _pesoTransbordoRankingTextoEdicion = "4,0";
|
||||||
private string _factorEquilibradoAndarBusTextoEdicion = "6";
|
private string _factorEquilibradoAndarBusTextoEdicion = "6";
|
||||||
private double _velocidadBusKmH = 16.0;
|
private double _velocidadBusKmH = 16.0;
|
||||||
@@ -234,7 +235,10 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
if (pendientePorcentaje.Value < umbralSubida)
|
if (pendientePorcentaje.Value < umbralSubida)
|
||||||
return distanciaMetros.Value;
|
return distanciaMetros.Value;
|
||||||
|
|
||||||
var pendiente = pendientePorcentaje.Value / 100.0;
|
var pendienteLimitadaPorcentaje = Math.Min(
|
||||||
|
pendientePorcentaje.Value,
|
||||||
|
PendienteMaximaEsfuerzoPorcentaje);
|
||||||
|
var pendiente = pendienteLimitadaPorcentaje / 100.0;
|
||||||
var factorConPotencia = Math.Pow(1.0 + pendiente, coeficiente);
|
var factorConPotencia = Math.Pow(1.0 + pendiente, coeficiente);
|
||||||
var esfuerzo = distanciaMetros.Value * factorConPotencia;
|
var esfuerzo = distanciaMetros.Value * factorConPotencia;
|
||||||
return double.IsFinite(esfuerzo)
|
return double.IsFinite(esfuerzo)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public partial class PlanificadorRutas
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(120, ct);
|
await Task.Delay(120, ct);
|
||||||
var sugerencias = await CrearSugerenciasBusquedaAsync(texto, 20, ct);
|
var sugerencias = await CrearSugerenciasBusquedaAsync(texto, 50, ct);
|
||||||
|
|
||||||
if (ct.IsCancellationRequested || version != _versionSugerenciasBusqueda)
|
if (ct.IsCancellationRequested || version != _versionSugerenciasBusqueda)
|
||||||
return;
|
return;
|
||||||
@@ -160,7 +160,9 @@ public partial class PlanificadorRutas
|
|||||||
.Concat(lugares.Select(CrearSugerenciaLugar))
|
.Concat(lugares.Select(CrearSugerenciaLugar))
|
||||||
.GroupBy(sugerencia => NormalizarClaveSugerencia(sugerencia), StringComparer.Ordinal)
|
.GroupBy(sugerencia => NormalizarClaveSugerencia(sugerencia), StringComparer.Ordinal)
|
||||||
.Select(grupo => grupo.First())
|
.Select(grupo => grupo.First())
|
||||||
.Take(Math.Clamp(maxResultados, 1, 20))
|
.OrderBy(sugerencia => sugerencia.Titulo, StringComparer.CurrentCultureIgnoreCase)
|
||||||
|
.ThenBy(sugerencia => sugerencia.Subtitulo, StringComparer.CurrentCultureIgnoreCase)
|
||||||
|
.Take(Math.Clamp(maxResultados, 1, 50))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,8 +249,8 @@ public partial class PlanificadorRutas
|
|||||||
TextoEntrada = lugar.Nombre,
|
TextoEntrada = lugar.Nombre,
|
||||||
Titulo = lugar.Nombre,
|
Titulo = lugar.Nombre,
|
||||||
Subtitulo = string.IsNullOrWhiteSpace(lugar.Subtitulo)
|
Subtitulo = string.IsNullOrWhiteSpace(lugar.Subtitulo)
|
||||||
? $"{lugar.Tipo} · {lugar.Fuente}"
|
? lugar.Tipo
|
||||||
: $"{lugar.Subtitulo} · {lugar.Fuente}",
|
: lugar.Subtitulo,
|
||||||
Tipo = lugar.Tipo,
|
Tipo = lugar.Tipo,
|
||||||
Fuente = lugar.Fuente,
|
Fuente = lugar.Fuente,
|
||||||
Latitud = lugar.Latitud,
|
Latitud = lugar.Latitud,
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ public partial class PlanificadorRutas
|
|||||||
private TipoReferenciaHistorial _tipoReferenciaHistorial;
|
private TipoReferenciaHistorial _tipoReferenciaHistorial;
|
||||||
private string _historialReferenciaId = string.Empty;
|
private string _historialReferenciaId = string.Empty;
|
||||||
private bool _rutaActualDerivadaDeHistorial;
|
private bool _rutaActualDerivadaDeHistorial;
|
||||||
|
private readonly List<PasoDerivacionHistorial> _pasosDerivacionHistorial = new();
|
||||||
|
private RegistroHistorialRuta? _escenarioBaseDerivacionHistorial;
|
||||||
|
private bool _mostrarInformacionDerivacionHistorial;
|
||||||
|
private int _indicePasoDerivacionSeleccionado = -1;
|
||||||
private string _mensajeHistorial = string.Empty;
|
private string _mensajeHistorial = string.Empty;
|
||||||
private bool _mensajeHistorialEsError = false;
|
private bool _mensajeHistorialEsError = false;
|
||||||
private bool _mostrarHistorialRutas = false;
|
private bool _mostrarHistorialRutas = false;
|
||||||
@@ -53,6 +57,17 @@ public partial class PlanificadorRutas
|
|||||||
Compartido
|
Compartido
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed record PasoDerivacionHistorial(
|
||||||
|
int Numero,
|
||||||
|
DateTime FechaUtc,
|
||||||
|
RegistroHistorialRuta Escenario);
|
||||||
|
|
||||||
|
private sealed record CambioDerivacionHistorial(
|
||||||
|
string Campo,
|
||||||
|
string ValorAnterior,
|
||||||
|
string ValorActual,
|
||||||
|
string Detalle);
|
||||||
|
|
||||||
private sealed class ResultadoStorageNavegador
|
private sealed class ResultadoStorageNavegador
|
||||||
{
|
{
|
||||||
[JsonPropertyName("ok")]
|
[JsonPropertyName("ok")]
|
||||||
@@ -113,6 +128,7 @@ public partial class PlanificadorRutas
|
|||||||
{
|
{
|
||||||
_versionAperturaHistorial++;
|
_versionAperturaHistorial++;
|
||||||
_mostrarHistorialRutas = false;
|
_mostrarHistorialRutas = false;
|
||||||
|
_mostrarInformacionDerivacionHistorial = false;
|
||||||
_historialLocalCargando = false;
|
_historialLocalCargando = false;
|
||||||
StopRefrescoHistorialCompartido();
|
StopRefrescoHistorialCompartido();
|
||||||
await RestaurarConfiguracionTemporalHistorialAsync();
|
await RestaurarConfiguracionTemporalHistorialAsync();
|
||||||
@@ -834,13 +850,27 @@ public partial class PlanificadorRutas
|
|||||||
private void ConfirmarDerivacionHistorial()
|
private void ConfirmarDerivacionHistorial()
|
||||||
{
|
{
|
||||||
_rutaActualDerivadaDeHistorial = ExisteReferenciaHistorialActual();
|
_rutaActualDerivadaDeHistorial = ExisteReferenciaHistorialActual();
|
||||||
|
if (_rutaActualDerivadaDeHistorial &&
|
||||||
|
_tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido)
|
||||||
|
{
|
||||||
|
RegistrarPasoDerivacionHistorialActual();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EstablecerReferenciaHistorial(TipoReferenciaHistorial tipo, string id)
|
private void EstablecerReferenciaHistorial(TipoReferenciaHistorial tipo, string id)
|
||||||
{
|
{
|
||||||
|
ReiniciarSeguimientoDerivacionHistorial();
|
||||||
_tipoReferenciaHistorial = tipo;
|
_tipoReferenciaHistorial = tipo;
|
||||||
_historialReferenciaId = id;
|
_historialReferenciaId = id;
|
||||||
_rutaActualDerivadaDeHistorial = false;
|
_rutaActualDerivadaDeHistorial = false;
|
||||||
|
|
||||||
|
if (tipo == TipoReferenciaHistorial.Compartido)
|
||||||
|
{
|
||||||
|
var compartida = _historialCompartido.FirstOrDefault(item =>
|
||||||
|
string.Equals(item.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (compartida is not null)
|
||||||
|
_escenarioBaseDerivacionHistorial = ClonarEscenarioHistorial(compartida);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LimpiarReferenciaHistorial()
|
private void LimpiarReferenciaHistorial()
|
||||||
@@ -848,6 +878,7 @@ public partial class PlanificadorRutas
|
|||||||
_tipoReferenciaHistorial = TipoReferenciaHistorial.Ninguna;
|
_tipoReferenciaHistorial = TipoReferenciaHistorial.Ninguna;
|
||||||
_historialReferenciaId = string.Empty;
|
_historialReferenciaId = string.Empty;
|
||||||
_rutaActualDerivadaDeHistorial = false;
|
_rutaActualDerivadaDeHistorial = false;
|
||||||
|
ReiniciarSeguimientoDerivacionHistorial();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ExisteReferenciaHistorialActual()
|
private bool ExisteReferenciaHistorialActual()
|
||||||
@@ -884,11 +915,265 @@ public partial class PlanificadorRutas
|
|||||||
_ => (string.Empty, -1)
|
_ => (string.Empty, -1)
|
||||||
};
|
};
|
||||||
|
|
||||||
return indice >= 0
|
if (indice < 0)
|
||||||
? $"Ruta actual derivada del Historial {nombre} n.º {indice + 1}."
|
return string.Empty;
|
||||||
|
|
||||||
|
var numeroDerivacion = _tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido &&
|
||||||
|
_pasosDerivacionHistorial.Count > 0
|
||||||
|
? $" · Derivación n.º {_pasosDerivacionHistorial.Count}"
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
|
return $"Ruta actual derivada del Historial {nombre} n.º {indice + 1}{numeroDerivacion}.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool PuedeMostrarInformacionDerivacionHistorial =>
|
||||||
|
_rutaActualDerivadaDeHistorial &&
|
||||||
|
_tipoReferenciaHistorial == TipoReferenciaHistorial.Compartido &&
|
||||||
|
_escenarioBaseDerivacionHistorial is not null &&
|
||||||
|
_pasosDerivacionHistorial.Count > 0;
|
||||||
|
|
||||||
|
private int IndiceHistorialCompartidoReferencia =>
|
||||||
|
_historialCompartido.FindIndex(item => string.Equals(
|
||||||
|
item.Id,
|
||||||
|
_historialReferenciaId,
|
||||||
|
StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
private PasoDerivacionHistorial? PasoDerivacionSeleccionado =>
|
||||||
|
_indicePasoDerivacionSeleccionado >= 0 &&
|
||||||
|
_indicePasoDerivacionSeleccionado < _pasosDerivacionHistorial.Count
|
||||||
|
? _pasosDerivacionHistorial[_indicePasoDerivacionSeleccionado]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
private void AbrirInformacionDerivacionHistorial()
|
||||||
|
{
|
||||||
|
if (!PuedeMostrarInformacionDerivacionHistorial)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_indicePasoDerivacionSeleccionado = _pasosDerivacionHistorial.Count - 1;
|
||||||
|
_mostrarInformacionDerivacionHistorial = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CerrarInformacionDerivacionHistorial()
|
||||||
|
{
|
||||||
|
_mostrarInformacionDerivacionHistorial = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SeleccionarPasoDerivacionHistorial(int indice)
|
||||||
|
{
|
||||||
|
if (indice < 0 || indice >= _pasosDerivacionHistorial.Count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_indicePasoDerivacionSeleccionado = indice;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReiniciarSeguimientoDerivacionHistorial()
|
||||||
|
{
|
||||||
|
_pasosDerivacionHistorial.Clear();
|
||||||
|
_escenarioBaseDerivacionHistorial = null;
|
||||||
|
_indicePasoDerivacionSeleccionado = -1;
|
||||||
|
_mostrarInformacionDerivacionHistorial = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegistrarPasoDerivacionHistorialActual()
|
||||||
|
{
|
||||||
|
_escenarioBaseDerivacionHistorial ??= _historialCompartido
|
||||||
|
.FirstOrDefault(item => string.Equals(
|
||||||
|
item.Id,
|
||||||
|
_historialReferenciaId,
|
||||||
|
StringComparison.OrdinalIgnoreCase)) is { } compartida
|
||||||
|
? ClonarEscenarioHistorial(compartida)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (_escenarioBaseDerivacionHistorial is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var actual = ConstruirRegistroHistorialActual();
|
||||||
|
if (actual is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var anterior = _pasosDerivacionHistorial.Count > 0
|
||||||
|
? _pasosDerivacionHistorial[^1].Escenario
|
||||||
|
: _escenarioBaseDerivacionHistorial;
|
||||||
|
if (string.Equals(
|
||||||
|
CalcularClaveEscenario(anterior),
|
||||||
|
CalcularClaveEscenario(actual),
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pasosDerivacionHistorial.Add(new PasoDerivacionHistorial(
|
||||||
|
_pasosDerivacionHistorial.Count + 1,
|
||||||
|
DateTime.UtcNow,
|
||||||
|
ClonarEscenarioHistorial(actual)));
|
||||||
|
_indicePasoDerivacionSeleccionado = _pasosDerivacionHistorial.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RegistroHistorialRuta? ObtenerEscenarioAnteriorDerivacion(
|
||||||
|
PasoDerivacionHistorial paso)
|
||||||
|
{
|
||||||
|
var indice = paso.Numero - 1;
|
||||||
|
if (indice <= 0)
|
||||||
|
return _escenarioBaseDerivacionHistorial;
|
||||||
|
|
||||||
|
return indice - 1 < _pasosDerivacionHistorial.Count
|
||||||
|
? _pasosDerivacionHistorial[indice - 1].Escenario
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<CambioDerivacionHistorial> ObtenerCambiosDerivacion(
|
||||||
|
RegistroHistorialRuta? anterior,
|
||||||
|
RegistroHistorialRuta actual)
|
||||||
|
{
|
||||||
|
if (anterior is null)
|
||||||
|
return Array.Empty<CambioDerivacionHistorial>();
|
||||||
|
|
||||||
|
var cambios = new List<CambioDerivacionHistorial>();
|
||||||
|
AgregarCambioPuntoDerivacion(
|
||||||
|
cambios,
|
||||||
|
"Origen",
|
||||||
|
anterior.OrigenTexto,
|
||||||
|
anterior.OrigenLatitud,
|
||||||
|
anterior.OrigenLongitud,
|
||||||
|
actual.OrigenTexto,
|
||||||
|
actual.OrigenLatitud,
|
||||||
|
actual.OrigenLongitud);
|
||||||
|
AgregarCambioPuntoDerivacion(
|
||||||
|
cambios,
|
||||||
|
"Destino",
|
||||||
|
anterior.DestinoTexto,
|
||||||
|
anterior.DestinoLatitud,
|
||||||
|
anterior.DestinoLongitud,
|
||||||
|
actual.DestinoTexto,
|
||||||
|
actual.DestinoLatitud,
|
||||||
|
actual.DestinoLongitud);
|
||||||
|
|
||||||
|
AgregarCambioTextoDerivacion(
|
||||||
|
cambios,
|
||||||
|
"Fecha y hora",
|
||||||
|
FormatearFechaHoraComparacionDerivacion(anterior),
|
||||||
|
FormatearFechaHoraComparacionDerivacion(actual));
|
||||||
|
AgregarCambioTextoDerivacion(
|
||||||
|
cambios,
|
||||||
|
"Modo",
|
||||||
|
FormatearModoHistorial(anterior),
|
||||||
|
FormatearModoHistorial(actual));
|
||||||
|
AgregarCambioTextoDerivacion(
|
||||||
|
cambios,
|
||||||
|
"Factor Equilib. A",
|
||||||
|
FormatearFactorDerivacion(anterior),
|
||||||
|
FormatearFactorDerivacion(actual));
|
||||||
|
AgregarCambioTextoDerivacion(
|
||||||
|
cambios,
|
||||||
|
"Horarios",
|
||||||
|
anterior.UsarHorariosTeoricos ? "Activados" : "Desactivados",
|
||||||
|
actual.UsarHorariosTeoricos ? "Activados" : "Desactivados");
|
||||||
|
AgregarCambioTextoDerivacion(
|
||||||
|
cambios,
|
||||||
|
"Líneas utilizables",
|
||||||
|
FormatearLineasDerivacion(anterior),
|
||||||
|
FormatearLineasDerivacion(actual));
|
||||||
|
|
||||||
|
return cambios;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AgregarCambioPuntoDerivacion(
|
||||||
|
ICollection<CambioDerivacionHistorial> cambios,
|
||||||
|
string campo,
|
||||||
|
string textoAnterior,
|
||||||
|
double latitudAnterior,
|
||||||
|
double longitudAnterior,
|
||||||
|
string textoActual,
|
||||||
|
double latitudActual,
|
||||||
|
double longitudActual)
|
||||||
|
{
|
||||||
|
var distancia = CalcularDistanciaMetros(
|
||||||
|
latitudAnterior,
|
||||||
|
longitudAnterior,
|
||||||
|
latitudActual,
|
||||||
|
longitudActual);
|
||||||
|
if (!double.IsFinite(distancia) || distancia < 0.5)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cambios.Add(new CambioDerivacionHistorial(
|
||||||
|
campo,
|
||||||
|
FormatearPuntoDerivacion(textoAnterior, latitudAnterior, longitudAnterior),
|
||||||
|
FormatearPuntoDerivacion(textoActual, latitudActual, longitudActual),
|
||||||
|
$"Desplazado {FormatearDistanciaDerivacion(distancia)}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AgregarCambioTextoDerivacion(
|
||||||
|
ICollection<CambioDerivacionHistorial> cambios,
|
||||||
|
string campo,
|
||||||
|
string? valorAnterior,
|
||||||
|
string? valorActual)
|
||||||
|
{
|
||||||
|
var anterior = string.IsNullOrWhiteSpace(valorAnterior) ? "-" : valorAnterior.Trim();
|
||||||
|
var actual = string.IsNullOrWhiteSpace(valorActual) ? "-" : valorActual.Trim();
|
||||||
|
if (string.Equals(anterior, actual, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cambios.Add(new CambioDerivacionHistorial(campo, anterior, actual, string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatearPuntoDerivacion(
|
||||||
|
string texto,
|
||||||
|
double latitud,
|
||||||
|
double longitud)
|
||||||
|
{
|
||||||
|
var nombre = string.IsNullOrWhiteSpace(texto) ? "Punto del mapa" : texto.Trim();
|
||||||
|
return $"{nombre} · {latitud.ToString("0.000000", CultureInfo.InvariantCulture)}, " +
|
||||||
|
longitud.ToString("0.000000", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatearDistanciaDerivacion(double distanciaMetros)
|
||||||
|
=> distanciaMetros >= 1000
|
||||||
|
? $"{(distanciaMetros / 1000.0).ToString("0.000", CultureInfo.InvariantCulture)} km"
|
||||||
|
: $"{Math.Round(distanciaMetros).ToString("0", CultureInfo.InvariantCulture)} m";
|
||||||
|
|
||||||
|
private static string FormatearFechaHoraComparacionDerivacion(RegistroHistorialRuta item)
|
||||||
|
=> item.UsarHoraSimulada
|
||||||
|
? FormatearHoraReferenciaHistorial(item)
|
||||||
|
: "Fecha y hora actuales";
|
||||||
|
|
||||||
|
private static string FormatearFactorDerivacion(RegistroHistorialRuta item)
|
||||||
|
=> item.FactorEquilibradoAndarBus is { } factor && double.IsFinite(factor) && factor > 0
|
||||||
|
? factor.ToString("0.##", CultureInfo.InvariantCulture)
|
||||||
|
: "-";
|
||||||
|
|
||||||
|
private static string FormatearLineasDerivacion(RegistroHistorialRuta item)
|
||||||
|
{
|
||||||
|
if (!item.ActivarFiltroLineas)
|
||||||
|
return "Todas";
|
||||||
|
|
||||||
|
var lineas = NormalizarLineasHistorial(item.LineasPermitidas);
|
||||||
|
return lineas.Count == 0
|
||||||
|
? "Ninguna"
|
||||||
|
: string.Join(", ", lineas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RegistroHistorialRuta ClonarEscenarioHistorial(RegistroHistorialRuta item)
|
||||||
|
=> new()
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
OrigenTexto = item.OrigenTexto,
|
||||||
|
DestinoTexto = item.DestinoTexto,
|
||||||
|
OrigenLatitud = item.OrigenLatitud,
|
||||||
|
OrigenLongitud = item.OrigenLongitud,
|
||||||
|
DestinoLatitud = item.DestinoLatitud,
|
||||||
|
DestinoLongitud = item.DestinoLongitud,
|
||||||
|
UsarHorariosTeoricos = item.UsarHorariosTeoricos,
|
||||||
|
UsarHoraSimulada = item.UsarHoraSimulada,
|
||||||
|
FechaSimulada = item.FechaSimulada,
|
||||||
|
HoraSimulada = item.HoraSimulada,
|
||||||
|
CriterioPrioritarioRuta = item.CriterioPrioritarioRuta,
|
||||||
|
PreferenciaEtiquetaRuta = item.PreferenciaEtiquetaRuta,
|
||||||
|
FactorEquilibradoAndarBus = item.FactorEquilibradoAndarBus,
|
||||||
|
ActivarFiltroLineas = item.ActivarFiltroLineas,
|
||||||
|
LineasPermitidas = item.LineasPermitidas.ToList(),
|
||||||
|
FechaCreacionUtc = item.FechaCreacionUtc,
|
||||||
|
FechaActualizacionUtc = item.FechaActualizacionUtc
|
||||||
|
};
|
||||||
|
|
||||||
private async Task CompartirEscenarioActualAsync()
|
private async Task CompartirEscenarioActualAsync()
|
||||||
{
|
{
|
||||||
var actual = ConstruirRegistroHistorialActual()
|
var actual = ConstruirRegistroHistorialActual()
|
||||||
|
|||||||
35
RutasDBUS/Components/Pages/PlanificadorRutas.InspectorZ.cs
Normal file
35
RutasDBUS/Components/Pages/PlanificadorRutas.InspectorZ.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace RutasDBUS.Components.Pages;
|
||||||
|
|
||||||
|
public partial class PlanificadorRutas
|
||||||
|
{
|
||||||
|
private PuntoInspectorAnterior? _inspectorPuntoAnterior;
|
||||||
|
|
||||||
|
private sealed record PuntoInspectorAnterior(
|
||||||
|
double Latitud,
|
||||||
|
double Longitud,
|
||||||
|
double? ElevacionIdee,
|
||||||
|
double? ElevacionLocal5,
|
||||||
|
double? ElevacionLocal2,
|
||||||
|
double? ElevacionLocal1);
|
||||||
|
|
||||||
|
private double? ObtenerElevacionPuntoInspectorAnterior(PuntoInspectorAnterior punto)
|
||||||
|
=> UsarElevacionLocal1ParaCalculo
|
||||||
|
? punto.ElevacionLocal1
|
||||||
|
: UsarElevacionLocal2ParaCalculo
|
||||||
|
? punto.ElevacionLocal2
|
||||||
|
: UsarElevacionLocal5ParaCalculo
|
||||||
|
? punto.ElevacionLocal5
|
||||||
|
: punto.ElevacionIdee;
|
||||||
|
|
||||||
|
private static string FormatearDistanciaPuntoInspector(double distanciaMetros)
|
||||||
|
=> distanciaMetros < 1000
|
||||||
|
? distanciaMetros.ToString("0.0", CultureInfo.InvariantCulture) + " m"
|
||||||
|
: (distanciaMetros / 1000.0).ToString("0.000", CultureInfo.InvariantCulture) + " km";
|
||||||
|
|
||||||
|
private static string FormatearAlturaPuntoInspector(double? altura)
|
||||||
|
=> altura is { } valor
|
||||||
|
? valor.ToString("0.00", CultureInfo.InvariantCulture) + " m"
|
||||||
|
: "-";
|
||||||
|
}
|
||||||
@@ -1202,7 +1202,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private bool _elevPopupMinimizado = false;
|
private bool _elevPopupMinimizado = false;
|
||||||
|
|
||||||
|
|
||||||
private const string _versionApp = "(v-20260825b)";
|
private const string _versionApp = "(v-20260827b)";
|
||||||
|
|
||||||
|
|
||||||
private double? _paradaElevIdee;
|
private double? _paradaElevIdee;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ public partial class PlanificadorRutas
|
|||||||
private const string ClaveConfiguracionLocal = "rutasdbus.planificador.config.v1";
|
private const string ClaveConfiguracionLocal = "rutasdbus.planificador.config.v1";
|
||||||
private const int VersionPersistenciaFactorEquilibrado = 2;
|
private const int VersionPersistenciaFactorEquilibrado = 2;
|
||||||
private const int VersionPersistenciaPendientes = 2;
|
private const int VersionPersistenciaPendientes = 2;
|
||||||
|
private const int VersionPersistenciaCoeficienteEsfuerzo = 1;
|
||||||
|
private const int VersionPersistenciaFuenteElevacion = 1;
|
||||||
private bool _configuracionLocalCargada = false;
|
private bool _configuracionLocalCargada = false;
|
||||||
private bool _mostrarConfirmacionRestablecer = false;
|
private bool _mostrarConfirmacionRestablecer = false;
|
||||||
|
|
||||||
@@ -38,11 +40,13 @@ public partial class PlanificadorRutas
|
|||||||
public double? FactorPenalizacionDistanciaPie { get; set; }
|
public double? FactorPenalizacionDistanciaPie { 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 double? PesoTransbordoRankingMinutos { get; set; }
|
public double? PesoTransbordoRankingMinutos { get; set; }
|
||||||
public double? FactorEquilibradoAndarBus { get; set; }
|
public double? FactorEquilibradoAndarBus { get; set; }
|
||||||
public int? VersionFactorEquilibrado { get; set; }
|
public int? VersionFactorEquilibrado { get; set; }
|
||||||
public bool? MostrarElevacionesRuta { get; set; }
|
public bool? MostrarElevacionesRuta { get; set; }
|
||||||
public string? FuenteElevacionCalculo { get; set; }
|
public string? FuenteElevacionCalculo { get; set; }
|
||||||
|
public int? VersionFuenteElevacion { get; set; }
|
||||||
public int? VersionPendientes { get; set; }
|
public int? VersionPendientes { get; set; }
|
||||||
public double? UmbralPendienteSubidaRuta { get; set; }
|
public double? UmbralPendienteSubidaRuta { get; set; }
|
||||||
public double? UmbralPendienteBajadaRuta { get; set; }
|
public double? UmbralPendienteBajadaRuta { get; set; }
|
||||||
@@ -154,11 +158,13 @@ public partial class PlanificadorRutas
|
|||||||
FactorPenalizacionDistanciaPie = _factorPenalizacionDistanciaPie,
|
FactorPenalizacionDistanciaPie = _factorPenalizacionDistanciaPie,
|
||||||
ParametroPenalizacionDistanciaPieMetros = _parametroPenalizacionDistanciaPieMetros,
|
ParametroPenalizacionDistanciaPieMetros = _parametroPenalizacionDistanciaPieMetros,
|
||||||
CoeficienteEsfuerzoAltura = _coeficienteEsfuerzoAltura,
|
CoeficienteEsfuerzoAltura = _coeficienteEsfuerzoAltura,
|
||||||
|
VersionCoeficienteEsfuerzo = VersionPersistenciaCoeficienteEsfuerzo,
|
||||||
PesoTransbordoRankingMinutos = _pesoTransbordoRankingMinutos,
|
PesoTransbordoRankingMinutos = _pesoTransbordoRankingMinutos,
|
||||||
FactorEquilibradoAndarBus = _factorEquilibradoAndarBus,
|
FactorEquilibradoAndarBus = _factorEquilibradoAndarBus,
|
||||||
VersionFactorEquilibrado = VersionPersistenciaFactorEquilibrado,
|
VersionFactorEquilibrado = VersionPersistenciaFactorEquilibrado,
|
||||||
MostrarElevacionesRuta = _mostrarElevacionesRutaConfiguracion,
|
MostrarElevacionesRuta = _mostrarElevacionesRutaConfiguracion,
|
||||||
FuenteElevacionCalculo = _fuenteElevacionCalculo,
|
FuenteElevacionCalculo = _fuenteElevacionCalculo,
|
||||||
|
VersionFuenteElevacion = VersionPersistenciaFuenteElevacion,
|
||||||
VersionPendientes = VersionPersistenciaPendientes,
|
VersionPendientes = VersionPersistenciaPendientes,
|
||||||
UmbralPendienteSubidaRuta = _umbralPendienteSubidaRuta,
|
UmbralPendienteSubidaRuta = _umbralPendienteSubidaRuta,
|
||||||
UmbralPendienteBajadaRuta = _umbralPendienteBajadaRuta,
|
UmbralPendienteBajadaRuta = _umbralPendienteBajadaRuta,
|
||||||
@@ -218,11 +224,15 @@ public partial class PlanificadorRutas
|
|||||||
Math.Abs(parametro - 100.0) < 0.0001
|
Math.Abs(parametro - 100.0) < 0.0001
|
||||||
? ParametroPenalizacionDistanciaPieMetrosPorDefecto
|
? ParametroPenalizacionDistanciaPieMetrosPorDefecto
|
||||||
: parametro;
|
: parametro;
|
||||||
|
var migrarCoeficienteEsfuerzo =
|
||||||
|
(config.VersionCoeficienteEsfuerzo ?? 0) < VersionPersistenciaCoeficienteEsfuerzo;
|
||||||
if (config.CoeficienteEsfuerzoAltura is { } coeficienteAltura && double.IsFinite(coeficienteAltura))
|
if (config.CoeficienteEsfuerzoAltura is { } coeficienteAltura && double.IsFinite(coeficienteAltura))
|
||||||
_coeficienteEsfuerzoAltura =
|
_coeficienteEsfuerzoAltura =
|
||||||
Math.Abs(coeficienteAltura - 1.6) < 0.0001 ||
|
migrarCoeficienteEsfuerzo &&
|
||||||
Math.Abs(coeficienteAltura - 5.2) < 0.0001 ||
|
(Math.Abs(coeficienteAltura - 1.6) < 0.0001 ||
|
||||||
Math.Abs(coeficienteAltura - 9.2) < 0.0001
|
Math.Abs(coeficienteAltura - 5.2) < 0.0001 ||
|
||||||
|
Math.Abs(coeficienteAltura - 8.2) < 0.0001 ||
|
||||||
|
Math.Abs(coeficienteAltura - 9.2) < 0.0001)
|
||||||
? CoeficienteEsfuerzoAlturaPorDefecto
|
? CoeficienteEsfuerzoAlturaPorDefecto
|
||||||
: coeficienteAltura;
|
: coeficienteAltura;
|
||||||
if (config.PesoTransbordoRankingMinutos is { } pesoTransbordo && double.IsFinite(pesoTransbordo))
|
if (config.PesoTransbordoRankingMinutos is { } pesoTransbordo && double.IsFinite(pesoTransbordo))
|
||||||
@@ -239,11 +249,14 @@ public partial class PlanificadorRutas
|
|||||||
? true
|
? true
|
||||||
: config.MostrarElevacionesRuta ?? true;
|
: config.MostrarElevacionesRuta ?? true;
|
||||||
_mostrarElevacionesRutaConfiguracion = _mostrarElevacionesRuta;
|
_mostrarElevacionesRutaConfiguracion = _mostrarElevacionesRuta;
|
||||||
_fuenteElevacionCalculo = migrarValoresPendientes &&
|
var migrarFuenteElevacion =
|
||||||
(string.IsNullOrWhiteSpace(config.FuenteElevacionCalculo) ||
|
(config.VersionFuenteElevacion ?? 0) < VersionPersistenciaFuenteElevacion;
|
||||||
string.Equals(config.FuenteElevacionCalculo, FuenteElevacionLocal5, StringComparison.OrdinalIgnoreCase) ||
|
_fuenteElevacionCalculo = migrarFuenteElevacion &&
|
||||||
string.Equals(config.FuenteElevacionCalculo, "local", StringComparison.OrdinalIgnoreCase))
|
string.Equals(
|
||||||
? FuenteElevacionLocal1
|
config.FuenteElevacionCalculo,
|
||||||
|
FuenteElevacionLocal1,
|
||||||
|
StringComparison.OrdinalIgnoreCase)
|
||||||
|
? FuenteElevacionLocal5
|
||||||
: NormalizarFuenteElevacion(config.FuenteElevacionCalculo);
|
: NormalizarFuenteElevacion(config.FuenteElevacionCalculo);
|
||||||
if (config.UmbralPendienteSubidaRuta is { } umbralSubida && double.IsFinite(umbralSubida))
|
if (config.UmbralPendienteSubidaRuta is { } umbralSubida && double.IsFinite(umbralSubida))
|
||||||
_umbralPendienteSubidaRuta = Math.Abs(umbralSubida - 3.0) < 0.0001
|
_umbralPendienteSubidaRuta = Math.Abs(umbralSubida - 3.0) < 0.0001
|
||||||
@@ -376,7 +389,7 @@ public partial class PlanificadorRutas
|
|||||||
_pesoTransbordoRankingMinutos = 4.0;
|
_pesoTransbordoRankingMinutos = 4.0;
|
||||||
_factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
_factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
||||||
_mostrarElevacionesRutaConfiguracion = true;
|
_mostrarElevacionesRutaConfiguracion = true;
|
||||||
_fuenteElevacionCalculo = FuenteElevacionLocal1;
|
_fuenteElevacionCalculo = FuenteElevacionLocal5;
|
||||||
_umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
|
_umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
|
||||||
_umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto;
|
_umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto;
|
||||||
_umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto;
|
_umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto;
|
||||||
@@ -429,7 +442,7 @@ public partial class PlanificadorRutas
|
|||||||
_pesoTransbordoRankingMinutos = 4.0;
|
_pesoTransbordoRankingMinutos = 4.0;
|
||||||
_factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
_factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
||||||
_mostrarElevacionesRutaConfiguracion = true;
|
_mostrarElevacionesRutaConfiguracion = true;
|
||||||
_fuenteElevacionCalculo = FuenteElevacionLocal1;
|
_fuenteElevacionCalculo = FuenteElevacionLocal5;
|
||||||
_umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
|
_umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
|
||||||
_umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto;
|
_umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto;
|
||||||
_umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto;
|
_umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto;
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private const string FuenteElevacionLocal1 = "local1";
|
private const string FuenteElevacionLocal1 = "local1";
|
||||||
private const string FuenteElevacionIdee = "idee";
|
private const string FuenteElevacionIdee = "idee";
|
||||||
private const int TiempoMaximoIdeeSegundos = 10;
|
private const int TiempoMaximoIdeeSegundos = 10;
|
||||||
private string _fuenteElevacionCalculo = FuenteElevacionLocal1;
|
private string _fuenteElevacionCalculo = FuenteElevacionLocal5;
|
||||||
|
|
||||||
// IDEE: en el correo indican que 6 decimales es suficiente
|
// IDEE: en el correo indican que 6 decimales es suficiente
|
||||||
private const int ELEV_IDEE_DECIMALES_CACHE = 6;
|
private const int ELEV_IDEE_DECIMALES_CACHE = 6;
|
||||||
@@ -217,7 +217,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
return FuenteElevacionLocal5;
|
return FuenteElevacionLocal5;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FuenteElevacionLocal1;
|
return FuenteElevacionLocal5;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ElevacionLocal5Disponible
|
private bool ElevacionLocal5Disponible
|
||||||
@@ -1519,6 +1519,16 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task MostrarInspectorAsync(double lat, double lon)
|
private async Task MostrarInspectorAsync(double lat, double lon)
|
||||||
{
|
{
|
||||||
|
_inspectorPuntoAnterior = _popupInspectorVisible
|
||||||
|
? new PuntoInspectorAnterior(
|
||||||
|
_inspectorLat,
|
||||||
|
_inspectorLon,
|
||||||
|
_inspectorElevIdee,
|
||||||
|
_inspectorElevLocal5,
|
||||||
|
_inspectorElevLocal2,
|
||||||
|
_inspectorElevLocal1)
|
||||||
|
: null;
|
||||||
|
|
||||||
var versionInspector = ++_versionInspector;
|
var versionInspector = ++_versionInspector;
|
||||||
_inspectorLat = lat;
|
_inspectorLat = lat;
|
||||||
_inspectorLon = lon;
|
_inspectorLon = lon;
|
||||||
@@ -1629,6 +1639,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
{
|
{
|
||||||
_versionInspector++;
|
_versionInspector++;
|
||||||
_popupInspectorVisible = false;
|
_popupInspectorVisible = false;
|
||||||
|
_inspectorPuntoAnterior = null;
|
||||||
_inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
|
_inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
|
||||||
|
|
||||||
try { await JS.InvokeVoidAsync("rt.clearInspectorMarkers"); } catch { }
|
try { await JS.InvokeVoidAsync("rt.clearInspectorMarkers"); } catch { }
|
||||||
|
|||||||
@@ -218,6 +218,26 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
string ItinerarioClave);
|
string ItinerarioClave);
|
||||||
|
|
||||||
private List<ProximaSalidaVm> _nextDeps = new();
|
private List<ProximaSalidaVm> _nextDeps = new();
|
||||||
|
|
||||||
|
private bool HorariosTaxibusSimuladosVisibles
|
||||||
|
=> _nextDeps.Any(salida => TaxibusDbus.EsExpedicionSimulada(salida.ExpId));
|
||||||
|
|
||||||
|
private static string ObtenerAvisoTaxibus(AlternativaRuta alternativa)
|
||||||
|
{
|
||||||
|
var lineasTaxibus = alternativa.AlternativaBus?.Tramos
|
||||||
|
.Select(tramo => tramo.Variante.Linea)
|
||||||
|
.Where(linea => TaxibusDbus.EsLinea(linea.Codigo))
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (lineasTaxibus is not { Count: > 0 })
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return lineasTaxibus.Any(linea => linea.HorarioSimulado)
|
||||||
|
? TaxibusDbus.MensajeHorarioSimulado
|
||||||
|
: TaxibusDbus.MensajeReserva;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gestiona toggle hora simulada.
|
/// Gestiona toggle hora simulada.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -299,6 +299,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (HorariosTaxibusSimuladosVisibles)
|
||||||
|
{
|
||||||
|
<div class="taxibus-notice taxibus-notice--schedule" role="status">
|
||||||
|
<strong>Taxibús:</strong> @TaxibusDbus.MensajeHorarioSimulado
|
||||||
|
</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 style="max-height: 220px; overflow-y: auto; border: 1px solid rgba(255,255,255,.12); border-radius: 6px; padding: .35rem .5rem; scroll-behavior: smooth;">
|
||||||
<ul style="margin:0; padding-left:1.1rem;">
|
<ul style="margin:0; padding-left:1.1rem;">
|
||||||
@foreach (var x in NextDepsOrdenados)
|
@foreach (var x in NextDepsOrdenados)
|
||||||
@@ -401,6 +408,29 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (_inspectorPuntoAnterior is { } puntoAnterior)
|
||||||
|
{
|
||||||
|
var distanciaPuntoAnterior = CalcularDistanciaMetros(
|
||||||
|
puntoAnterior.Latitud,
|
||||||
|
puntoAnterior.Longitud,
|
||||||
|
_inspectorLat,
|
||||||
|
_inspectorLon);
|
||||||
|
var elevacionPuntoAnterior = ObtenerElevacionPuntoInspectorAnterior(puntoAnterior);
|
||||||
|
|
||||||
|
<div class="inspector-previous-point">
|
||||||
|
<div class="inspector-previous-point__distance">
|
||||||
|
<b>Distancia al punto Z anterior:</b>
|
||||||
|
@FormatearDistanciaPuntoInspector(distanciaPuntoAnterior)
|
||||||
|
</div>
|
||||||
|
<div class="inspector-previous-point__details">
|
||||||
|
<span>Punto anterior</span>
|
||||||
|
<span>Lat: @puntoAnterior.Latitud.ToString("0.000000", CultureInfo.InvariantCulture)</span>
|
||||||
|
<span>Lon: @puntoAnterior.Longitud.ToString("0.000000", CultureInfo.InvariantCulture)</span>
|
||||||
|
<span>@EtiquetaFuenteElevacionCalculo: @FormatearAlturaPuntoInspector(elevacionPuntoAnterior)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@if (_inspectorLugaresCercanos.Count > 0)
|
@if (_inspectorLugaresCercanos.Count > 0)
|
||||||
{
|
{
|
||||||
<div class="inspector-address-list">
|
<div class="inspector-address-list">
|
||||||
@@ -741,6 +771,7 @@
|
|||||||
@if (_opcionesRuta.Count > 0 && _indiceAlternativaSeleccionada >= 0 && _indiceAlternativaSeleccionada < _opcionesRuta.Count)
|
@if (_opcionesRuta.Count > 0 && _indiceAlternativaSeleccionada >= 0 && _indiceAlternativaSeleccionada < _opcionesRuta.Count)
|
||||||
{
|
{
|
||||||
var alt = _opcionesRuta[_indiceAlternativaSeleccionada];
|
var alt = _opcionesRuta[_indiceAlternativaSeleccionada];
|
||||||
|
var avisoTaxibus = ObtenerAvisoTaxibus(alt);
|
||||||
|
|
||||||
@if (MostrarAlertaPendientesPositivasSignificativas)
|
@if (MostrarAlertaPendientesPositivasSignificativas)
|
||||||
{
|
{
|
||||||
@@ -753,6 +784,13 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(avisoTaxibus))
|
||||||
|
{
|
||||||
|
<div class="taxibus-notice" role="status">
|
||||||
|
<strong>Taxibús:</strong> @avisoTaxibus
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@* ========= RESUMEN BONITO ARRIBA (único) ========= *@
|
@* ========= RESUMEN BONITO ARRIBA (único) ========= *@
|
||||||
<p class="route-main-text">
|
<p class="route-main-text">
|
||||||
@if (alt.EsSoloAPie)
|
@if (alt.EsSoloAPie)
|
||||||
@@ -1162,7 +1200,15 @@
|
|||||||
{
|
{
|
||||||
<div class="history-derived-note">
|
<div class="history-derived-note">
|
||||||
<span class="history-derived-marker" aria-hidden="true">*</span>
|
<span class="history-derived-marker" aria-hidden="true">*</span>
|
||||||
<span>@textoReferenciaDerivada</span>
|
<span class="history-derived-note__text">@textoReferenciaDerivada</span>
|
||||||
|
@if (PuedeMostrarInformacionDerivacionHistorial)
|
||||||
|
{
|
||||||
|
<button type="button"
|
||||||
|
class="history-derived-info-btn"
|
||||||
|
@onclick="AbrirInformacionDerivacionHistorial">
|
||||||
|
Info
|
||||||
|
</button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1198,6 +1244,126 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (_mostrarInformacionDerivacionHistorial &&
|
||||||
|
_escenarioBaseDerivacionHistorial is not null &&
|
||||||
|
PasoDerivacionSeleccionado is { } pasoDerivacion)
|
||||||
|
{
|
||||||
|
var cambiosRespectoOriginal = ObtenerCambiosDerivacion(
|
||||||
|
_escenarioBaseDerivacionHistorial,
|
||||||
|
pasoDerivacion.Escenario);
|
||||||
|
var escenarioAnterior = ObtenerEscenarioAnteriorDerivacion(pasoDerivacion);
|
||||||
|
var cambiosRespectoAnterior = ObtenerCambiosDerivacion(
|
||||||
|
escenarioAnterior,
|
||||||
|
pasoDerivacion.Escenario);
|
||||||
|
var textoComparacionAnterior = pasoDerivacion.Numero <= 1
|
||||||
|
? "Respecto al Historial Compartido original"
|
||||||
|
: $"Respecto a la Derivación n.º {pasoDerivacion.Numero - 1}";
|
||||||
|
|
||||||
|
<div class="custom-modal-backdrop history-derivation-backdrop"
|
||||||
|
@onclick="CerrarInformacionDerivacionHistorial">
|
||||||
|
<div class="custom-modal-card history-derivation-modal"
|
||||||
|
@onclick:stopPropagation="true">
|
||||||
|
<div class="history-derivation-modal__header">
|
||||||
|
<div>
|
||||||
|
<div class="custom-modal-title">Cambios de la derivación</div>
|
||||||
|
<div class="history-derivation-modal__source">
|
||||||
|
Historial Compartido n.º @(IndiceHistorialCompartidoReferencia + 1)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
class="config-close-btn"
|
||||||
|
title="Cerrar"
|
||||||
|
aria-label="Cerrar"
|
||||||
|
@onclick="CerrarInformacionDerivacionHistorial">
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="history-derivation-steps" aria-label="Derivaciones registradas">
|
||||||
|
@foreach (var paso in _pasosDerivacionHistorial)
|
||||||
|
{
|
||||||
|
var indicePaso = paso.Numero - 1;
|
||||||
|
<button type="button"
|
||||||
|
class="history-derivation-step @(indicePaso == _indicePasoDerivacionSeleccionado ? "history-derivation-step--selected" : "")"
|
||||||
|
@onclick="() => SeleccionarPasoDerivacionHistorial(indicePaso)">
|
||||||
|
Der. @paso.Numero
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="history-derivation-selected">
|
||||||
|
<strong>Derivación n.º @pasoDerivacion.Numero</strong>
|
||||||
|
<span>@pasoDerivacion.FechaUtc.ToLocalTime().ToString("dd/MM/yyyy HH:mm:ss")</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="history-derivation-comparison">
|
||||||
|
<h6>Respecto al Historial Compartido original</h6>
|
||||||
|
@if (cambiosRespectoOriginal.Count == 0)
|
||||||
|
{
|
||||||
|
<div class="history-derivation-no-changes">Sin cambios registrados.</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@foreach (var cambio in cambiosRespectoOriginal)
|
||||||
|
{
|
||||||
|
<div class="history-derivation-change">
|
||||||
|
<div class="history-derivation-change__heading">
|
||||||
|
<strong>@cambio.Campo</strong>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(cambio.Detalle))
|
||||||
|
{
|
||||||
|
<span>@cambio.Detalle</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="history-derivation-change__value">
|
||||||
|
<span>Antes</span>
|
||||||
|
<div>@cambio.ValorAnterior</div>
|
||||||
|
</div>
|
||||||
|
<div class="history-derivation-change__value">
|
||||||
|
<span>Ahora</span>
|
||||||
|
<div>@cambio.ValorActual</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
@if (pasoDerivacion.Numero > 1)
|
||||||
|
{
|
||||||
|
<section class="history-derivation-comparison">
|
||||||
|
<h6>@textoComparacionAnterior</h6>
|
||||||
|
@if (cambiosRespectoAnterior.Count == 0)
|
||||||
|
{
|
||||||
|
<div class="history-derivation-no-changes">Sin cambios respecto a la anterior.</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@foreach (var cambio in cambiosRespectoAnterior)
|
||||||
|
{
|
||||||
|
<div class="history-derivation-change">
|
||||||
|
<div class="history-derivation-change__heading">
|
||||||
|
<strong>@cambio.Campo</strong>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(cambio.Detalle))
|
||||||
|
{
|
||||||
|
<span>@cambio.Detalle</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="history-derivation-change__value">
|
||||||
|
<span>Antes</span>
|
||||||
|
<div>@cambio.ValorAnterior</div>
|
||||||
|
</div>
|
||||||
|
<div class="history-derivation-change__value">
|
||||||
|
<span>Ahora</span>
|
||||||
|
<div>@cambio.ValorActual</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- CONFIG -->
|
<!-- CONFIG -->
|
||||||
@* <button class="config-toggle-btn" @onclick="ToggleConfigPanel">⚙</button>
|
@* <button class="config-toggle-btn" @onclick="ToggleConfigPanel">⚙</button>
|
||||||
*@
|
*@
|
||||||
@@ -1309,7 +1475,7 @@
|
|||||||
<div class="line-filter">
|
<div class="line-filter">
|
||||||
<label class="line-filter__toggle">
|
<label class="line-filter__toggle">
|
||||||
<input type="checkbox" @bind="_activarRangoLineas" @bind:after="PersistirConfiguracionAsync" />
|
<input type="checkbox" @bind="_activarRangoLineas" @bind:after="PersistirConfiguracionAsync" />
|
||||||
<span>Aplicar lineas utilizables:</span>
|
<span>Utiliza las siguientes líneas:</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@if (_activarRangoLineas)
|
@if (_activarRangoLineas)
|
||||||
@@ -1651,15 +1817,15 @@
|
|||||||
disabled="@_pintandoElevacionesRuta"
|
disabled="@_pintandoElevacionesRuta"
|
||||||
@bind="_fuenteElevacionCalculo"
|
@bind="_fuenteElevacionCalculo"
|
||||||
@bind:after="OnFuenteElevacionChangedAsync">
|
@bind:after="OnFuenteElevacionChangedAsync">
|
||||||
<option value="@FuenteElevacionIdee">Altura (G)</option>
|
<option value="@FuenteElevacionIdee">G</option>
|
||||||
<option value="@FuenteElevacionLocal5" disabled="@(!ElevacionLocal5Disponible)">
|
<option value="@FuenteElevacionLocal5" disabled="@(!ElevacionLocal5Disponible)">
|
||||||
Altura (L5)@(!ElevacionLocal5Disponible ? " - no disponible" : string.Empty)
|
L5@(!ElevacionLocal5Disponible ? " - no disponible" : string.Empty)
|
||||||
</option>
|
</option>
|
||||||
<option value="@FuenteElevacionLocal2" disabled="@(!ElevacionLocal2Disponible)">
|
<option value="@FuenteElevacionLocal2" disabled="@(!ElevacionLocal2Disponible)">
|
||||||
Altura (L2)@(!ElevacionLocal2Disponible ? " - no disponible" : string.Empty)
|
L2@(!ElevacionLocal2Disponible ? " - no disponible" : string.Empty)
|
||||||
</option>
|
</option>
|
||||||
<option value="@FuenteElevacionLocal1" disabled="@(!ElevacionLocal1Disponible)">
|
<option value="@FuenteElevacionLocal1" disabled="@(!ElevacionLocal1Disponible)">
|
||||||
Altura (L1)@(!ElevacionLocal1Disponible ? " - no disponible" : string.Empty)
|
L1@(!ElevacionLocal1Disponible ? " - no disponible" : string.Empty)
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ public sealed class ArchivoImportacionDbus
|
|||||||
public IReadOnlyList<FilaTiempoRealCsv> TiemposReales { get; init; } = Array.Empty<FilaTiempoRealCsv>();
|
public IReadOnlyList<FilaTiempoRealCsv> TiemposReales { get; init; } = Array.Empty<FilaTiempoRealCsv>();
|
||||||
public IReadOnlyList<FilaTransbordoCsv> Transbordos { get; init; } = Array.Empty<FilaTransbordoCsv>();
|
public IReadOnlyList<FilaTransbordoCsv> Transbordos { get; init; } = Array.Empty<FilaTransbordoCsv>();
|
||||||
public IReadOnlyList<FilaVehiculoCsv> Vehiculos { get; init; } = Array.Empty<FilaVehiculoCsv>();
|
public IReadOnlyList<FilaVehiculoCsv> Vehiculos { get; init; } = Array.Empty<FilaVehiculoCsv>();
|
||||||
|
public IReadOnlySet<string> RutasConHorarioSimulado { get; init; } =
|
||||||
|
new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, FilaParadaCsv> ParadasPorId { get; init; } = new Dictionary<int, FilaParadaCsv>();
|
public IReadOnlyDictionary<int, FilaParadaCsv> ParadasPorId { get; init; } = new Dictionary<int, FilaParadaCsv>();
|
||||||
public IReadOnlyDictionary<string, FilaRutaCsv> RutasPorId { get; init; } =
|
public IReadOnlyDictionary<string, FilaRutaCsv> RutasPorId { get; init; } =
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ public class LineaBus
|
|||||||
/// <summary>Color de fondo en formato ARGB (ej. FF0000FF).</summary>
|
/// <summary>Color de fondo en formato ARGB (ej. FF0000FF).</summary>
|
||||||
public string ColorArgb { get; set; } = "FF0000FF";
|
public string ColorArgb { get; set; } = "FF0000FF";
|
||||||
|
|
||||||
|
/// <summary>Indica si el horario activo se ha completado con una frecuencia provisional.</summary>
|
||||||
|
public bool HorarioSimulado { get; set; }
|
||||||
|
|
||||||
/// <summary>Código del itinerario por defecto.</summary>
|
/// <summary>Código del itinerario por defecto.</summary>
|
||||||
public int CodigoItinerarioPorDefecto { get; set; }
|
public int CodigoItinerarioPorDefecto { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -43,9 +43,10 @@ public sealed class GeocodificadorLocalServicio : IGeocodificadorLocalServicio
|
|||||||
if (tokens.Length == 0)
|
if (tokens.Length == 0)
|
||||||
return Array.Empty<LugarGeocodificado>();
|
return Array.Empty<LugarGeocodificado>();
|
||||||
|
|
||||||
maxResultados = Math.Clamp(maxResultados, 1, 30);
|
maxResultados = Math.Clamp(maxResultados, 1, 50);
|
||||||
|
|
||||||
return indice
|
return indice
|
||||||
|
.Where(entrada => EsDonostia(entrada.Municipio))
|
||||||
.Select(entrada => new
|
.Select(entrada => new
|
||||||
{
|
{
|
||||||
Entrada = entrada,
|
Entrada = entrada,
|
||||||
|
|||||||
@@ -534,17 +534,24 @@ namespace RutasDBUS.Servicios.Horarios
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool CoincideLineaBusqueda(FilaExpedicion exp, string idRutaPedido, string lineaPedida)
|
private bool CoincideLineaBusqueda(FilaExpedicion exp, string idRutaPedido, string lineaPedida)
|
||||||
{
|
{
|
||||||
|
return CoincideLineaExpedicion(exp, idRutaPedido) ||
|
||||||
|
CoincideLineaExpedicion(exp, lineaPedida);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CoincideLineaExpedicion(FilaExpedicion exp, string? lineaPedida)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(lineaPedida) ||
|
||||||
|
string.Equals(lineaPedida, "?", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var idRutaExp = NormalizarLinea(exp.IdRuta);
|
var idRutaExp = NormalizarLinea(exp.IdRuta);
|
||||||
var codigoVisibleExp = NormalizarLinea(ResolverCodigoLineaVisible(exp.IdRuta));
|
var codigoVisibleExp = NormalizarLinea(ResolverCodigoLineaVisible(exp.IdRuta));
|
||||||
|
var lineaNormalizada = NormalizarLinea(lineaPedida);
|
||||||
|
|
||||||
return (!string.IsNullOrWhiteSpace(idRutaPedido) &&
|
return string.Equals(idRutaExp, lineaNormalizada, StringComparison.OrdinalIgnoreCase) ||
|
||||||
!string.Equals(idRutaPedido, "?", StringComparison.Ordinal) &&
|
string.Equals(codigoVisibleExp, lineaNormalizada, StringComparison.OrdinalIgnoreCase);
|
||||||
(string.Equals(idRutaExp, idRutaPedido, StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
string.Equals(codigoVisibleExp, idRutaPedido, StringComparison.OrdinalIgnoreCase))) ||
|
|
||||||
(!string.IsNullOrWhiteSpace(lineaPedida) &&
|
|
||||||
!string.Equals(lineaPedida, "?", StringComparison.Ordinal) &&
|
|
||||||
(string.Equals(idRutaExp, lineaPedida, StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
string.Equals(codigoVisibleExp, lineaPedida, StringComparison.OrdinalIgnoreCase)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -763,8 +770,7 @@ namespace RutasDBUS.Servicios.Horarios
|
|||||||
if (!activeCals.Contains(exp.IdCal))
|
if (!activeCals.Contains(exp.IdCal))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var lineaExp = NormalizarLinea(exp.IdRuta);
|
if (!CoincideLineaExpedicion(exp, lineaPedida))
|
||||||
if (!string.Equals(lineaExp, lineaPedida, StringComparison.OrdinalIgnoreCase))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var sentidoExp = DireccionToSentido(exp.Direccion);
|
var sentidoExp = DireccionToSentido(exp.Direccion);
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ public sealed class ServicioArchivoImportacionDbus
|
|||||||
var tiemposReales = await ReadTiemposRealesAsync(FindEntry(zip, "tiempo_real.csv"), ct);
|
var tiemposReales = await ReadTiemposRealesAsync(FindEntry(zip, "tiempo_real.csv"), ct);
|
||||||
var transbordos = await ReadTransbordosAsync(FindEntry(zip, "transbordos.csv"), ct);
|
var transbordos = await ReadTransbordosAsync(FindEntry(zip, "transbordos.csv"), ct);
|
||||||
var vehiculos = await ReadVehiculosAsync(FindEntry(zip, "vehiculos.csv"), ct);
|
var vehiculos = await ReadVehiculosAsync(FindEntry(zip, "vehiculos.csv"), ct);
|
||||||
|
var rutasConHorarioSimulado = SimuladorHorariosTaxibus.AplicarSiFaltanHorarios(
|
||||||
|
calendarios,
|
||||||
|
expediciones,
|
||||||
|
rutas,
|
||||||
|
tiemposParada);
|
||||||
|
|
||||||
return new ArchivoImportacionDbus
|
return new ArchivoImportacionDbus
|
||||||
{
|
{
|
||||||
@@ -126,6 +131,7 @@ public sealed class ServicioArchivoImportacionDbus
|
|||||||
TiemposReales = tiemposReales,
|
TiemposReales = tiemposReales,
|
||||||
Transbordos = transbordos,
|
Transbordos = transbordos,
|
||||||
Vehiculos = vehiculos,
|
Vehiculos = vehiculos,
|
||||||
|
RutasConHorarioSimulado = rutasConHorarioSimulado,
|
||||||
ParadasPorId = paradas.ToDictionary(x => x.Id),
|
ParadasPorId = paradas.ToDictionary(x => x.Id),
|
||||||
RutasPorId = rutas.ToDictionary(x => x.Id, StringComparer.OrdinalIgnoreCase),
|
RutasPorId = rutas.ToDictionary(x => x.Id, StringComparer.OrdinalIgnoreCase),
|
||||||
ExpedicionesPorId = expediciones.ToDictionary(x => x.Id, StringComparer.OrdinalIgnoreCase),
|
ExpedicionesPorId = expediciones.ToDictionary(x => x.Id, StringComparer.OrdinalIgnoreCase),
|
||||||
|
|||||||
200
RutasDBUS/Servicios/Importacion/SimuladorHorariosTaxibus.cs
Normal file
200
RutasDBUS/Servicios/Importacion/SimuladorHorariosTaxibus.cs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
using RutasDBUS.Modelos.Importacion;
|
||||||
|
|
||||||
|
namespace RutasDBUS.Servicios.Importacion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Completa las plantillas sin horario de TB6 y TB7 con una frecuencia provisional.
|
||||||
|
/// </summary>
|
||||||
|
internal static class SimuladorHorariosTaxibus
|
||||||
|
{
|
||||||
|
private const string IdCalendario = "TAXIBUS_SIM_TODOS_LOS_DIAS";
|
||||||
|
private const int FrecuenciaMinutos = 30;
|
||||||
|
private const double VelocidadMetrosSegundo = 16_000.0 / 3_600.0;
|
||||||
|
private const int SegundosMinimosEntreParadas = 30;
|
||||||
|
|
||||||
|
public static IReadOnlySet<string> AplicarSiFaltanHorarios(
|
||||||
|
List<FilaCalendarioCsv> calendarios,
|
||||||
|
List<FilaExpedicionCsv> expediciones,
|
||||||
|
IReadOnlyList<FilaRutaCsv> rutas,
|
||||||
|
List<FilaTiempoParadaCsv> tiemposParada)
|
||||||
|
{
|
||||||
|
var rutasSimuladas = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var tiemposOriginalesPorExpedicion = tiemposParada
|
||||||
|
.GroupBy(tiempo => tiempo.IdExp, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToDictionary(
|
||||||
|
grupo => grupo.Key,
|
||||||
|
grupo => grupo.OrderBy(tiempo => tiempo.SecPar).ToList(),
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var ruta in rutas.Where(EsRutaTaxibus))
|
||||||
|
{
|
||||||
|
var expedicionesRuta = expediciones
|
||||||
|
.Where(expedicion => string.Equals(
|
||||||
|
expedicion.IdRuta,
|
||||||
|
ruta.Id,
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
.OrderBy(expedicion => expedicion.Id, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (expedicionesRuta.Count == 0 ||
|
||||||
|
TieneHorarioOficial(expedicionesRuta, tiemposOriginalesPorExpedicion))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var plantillas = expedicionesRuta
|
||||||
|
.Select(expedicion => new PlantillaTaxibus(
|
||||||
|
expedicion,
|
||||||
|
tiemposOriginalesPorExpedicion.TryGetValue(expedicion.Id, out var tiempos)
|
||||||
|
? tiempos
|
||||||
|
: new List<FilaTiempoParadaCsv>()))
|
||||||
|
.Where(plantilla => plantilla.Tiempos.Count >= 2)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (plantillas.Count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var idsPlantilla = expedicionesRuta
|
||||||
|
.Select(expedicion => expedicion.Id)
|
||||||
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
expediciones.RemoveAll(expedicion => idsPlantilla.Contains(expedicion.Id));
|
||||||
|
tiemposParada.RemoveAll(tiempo => idsPlantilla.Contains(tiempo.IdExp));
|
||||||
|
|
||||||
|
for (var indicePlantilla = 0; indicePlantilla < plantillas.Count; indicePlantilla++)
|
||||||
|
{
|
||||||
|
CrearExpedicionesSimuladas(
|
||||||
|
ruta,
|
||||||
|
plantillas[indicePlantilla],
|
||||||
|
indicePlantilla,
|
||||||
|
expediciones,
|
||||||
|
tiemposParada);
|
||||||
|
}
|
||||||
|
|
||||||
|
rutasSimuladas.Add(ruta.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rutasSimuladas.Count > 0)
|
||||||
|
{
|
||||||
|
calendarios.RemoveAll(calendario =>
|
||||||
|
calendario.Id.Equals(IdCalendario, StringComparison.OrdinalIgnoreCase));
|
||||||
|
calendarios.Add(new FilaCalendarioCsv(
|
||||||
|
IdCalendario,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
new DateOnly(2000, 1, 1),
|
||||||
|
new DateOnly(2099, 12, 31)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rutasSimuladas;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool EsRutaTaxibus(FilaRutaCsv ruta)
|
||||||
|
=> TaxibusDbus.EsLinea(ruta.Abreviatura) || TaxibusDbus.EsLinea(ruta.Id);
|
||||||
|
|
||||||
|
private static bool TieneHorarioOficial(
|
||||||
|
IReadOnlyList<FilaExpedicionCsv> expediciones,
|
||||||
|
IReadOnlyDictionary<string, List<FilaTiempoParadaCsv>> tiemposPorExpedicion)
|
||||||
|
{
|
||||||
|
foreach (var expedicion in expediciones)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(expedicion.IdCal) &&
|
||||||
|
!expedicion.IdCal.Equals("0000", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expedicion.HSalida is { } salida && salida != TimeOnly.MinValue)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!tiemposPorExpedicion.TryGetValue(expedicion.Id, out var tiempos))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tiempos.Any(tiempo =>
|
||||||
|
EsHoraReal(tiempo.HoraLlegada) || EsHoraReal(tiempo.HoraSalida)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool EsHoraReal(TimeOnly? hora)
|
||||||
|
=> hora is { } valor && valor != TimeOnly.MinValue;
|
||||||
|
|
||||||
|
private static void CrearExpedicionesSimuladas(
|
||||||
|
FilaRutaCsv ruta,
|
||||||
|
PlantillaTaxibus plantilla,
|
||||||
|
int indicePlantilla,
|
||||||
|
ICollection<FilaExpedicionCsv> expediciones,
|
||||||
|
ICollection<FilaTiempoParadaCsv> tiemposParada)
|
||||||
|
{
|
||||||
|
var cantidadSalidas = 24 * 60 / FrecuenciaMinutos;
|
||||||
|
for (var indiceSalida = 0; indiceSalida < cantidadSalidas; indiceSalida++)
|
||||||
|
{
|
||||||
|
var segundosInicio = indiceSalida * FrecuenciaMinutos * 60;
|
||||||
|
var horaInicio = HoraDesdeSegundos(segundosInicio);
|
||||||
|
var idExpedicion =
|
||||||
|
$"{TaxibusDbus.PrefijoExpedicionSimulada}{ruta.Id}_{indicePlantilla:00}_{indiceSalida:00}";
|
||||||
|
|
||||||
|
expediciones.Add(new FilaExpedicionCsv(
|
||||||
|
idExpedicion,
|
||||||
|
plantilla.Expedicion.IdRuta,
|
||||||
|
plantilla.Expedicion.DirDestino,
|
||||||
|
plantilla.Expedicion.Direccion,
|
||||||
|
plantilla.Expedicion.CambioExp,
|
||||||
|
plantilla.Expedicion.IdPatronExp,
|
||||||
|
horaInicio,
|
||||||
|
IdCalendario));
|
||||||
|
|
||||||
|
var segundosPaso = segundosInicio;
|
||||||
|
for (var indiceParada = 0; indiceParada < plantilla.Tiempos.Count; indiceParada++)
|
||||||
|
{
|
||||||
|
var tiempoPlantilla = plantilla.Tiempos[indiceParada];
|
||||||
|
var horaPaso = HoraDesdeSegundos(segundosPaso);
|
||||||
|
|
||||||
|
tiemposParada.Add(new FilaTiempoParadaCsv(
|
||||||
|
idExpedicion,
|
||||||
|
horaPaso,
|
||||||
|
horaPaso,
|
||||||
|
tiempoPlantilla.DistanciaSiguienteParada,
|
||||||
|
tiempoPlantilla.IdTiempoPar,
|
||||||
|
tiempoPlantilla.SecPar,
|
||||||
|
tiempoPlantilla.TipoSubidaPar,
|
||||||
|
tiempoPlantilla.TipoBajadaPar,
|
||||||
|
tiempoPlantilla.NuevaDirDestinoPar));
|
||||||
|
|
||||||
|
if (indiceParada < plantilla.Tiempos.Count - 1)
|
||||||
|
segundosPaso += CalcularSegundosEntreParadas(tiempoPlantilla.DistanciaSiguienteParada);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalcularSegundosEntreParadas(double distanciaMetros)
|
||||||
|
{
|
||||||
|
if (!double.IsFinite(distanciaMetros) || distanciaMetros <= 0)
|
||||||
|
return SegundosMinimosEntreParadas;
|
||||||
|
|
||||||
|
return Math.Max(
|
||||||
|
SegundosMinimosEntreParadas,
|
||||||
|
(int)Math.Round(
|
||||||
|
distanciaMetros / VelocidadMetrosSegundo,
|
||||||
|
MidpointRounding.AwayFromZero));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TimeOnly HoraDesdeSegundos(int segundos)
|
||||||
|
{
|
||||||
|
var segundosDia = ((segundos % (24 * 3600)) + (24 * 3600)) % (24 * 3600);
|
||||||
|
return TimeOnly.FromTimeSpan(TimeSpan.FromSeconds(segundosDia));
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed record PlantillaTaxibus(
|
||||||
|
FilaExpedicionCsv Expedicion,
|
||||||
|
IReadOnlyList<FilaTiempoParadaCsv> Tiempos);
|
||||||
|
}
|
||||||
@@ -3363,8 +3363,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
.Where(x => x.DistanciaPieTotalAprox <= limiteAhorroClaro)
|
.Where(x => x.DistanciaPieTotalAprox <= limiteAhorroClaro)
|
||||||
.OrderBy(x => x.DistanciaPieTotalAprox)
|
.OrderBy(x => x.DistanciaPieTotalAprox)
|
||||||
.ThenBy(x => DuracionVisibleOrdenacion(x))
|
.ThenBy(x => DuracionVisibleOrdenacion(x))
|
||||||
.ThenBy(x => x.PuntuacionCoste)
|
.ThenBy(x => x.PuntuacionCoste));
|
||||||
.Take(8));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ public sealed class CreadorRedTransporteDbus
|
|||||||
: CodigoSinDecimal(row.Id),
|
: CodigoSinDecimal(row.Id),
|
||||||
Nombre = !string.IsNullOrWhiteSpace(row.Nombre) ? row.Nombre : (row.Descripcion ?? ""),
|
Nombre = !string.IsNullOrWhiteSpace(row.Nombre) ? row.Nombre : (row.Descripcion ?? ""),
|
||||||
ColorArgb = NormalizarColorArgb(row.Color),
|
ColorArgb = NormalizarColorArgb(row.Color),
|
||||||
|
HorarioSimulado = archive.RutasConHorarioSimulado.Contains(row.Id ?? ""),
|
||||||
CodigoItinerarioPorDefecto = 0
|
CodigoItinerarioPorDefecto = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
32
RutasDBUS/Servicios/TaxibusDbus.cs
Normal file
32
RutasDBUS/Servicios/TaxibusDbus.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
namespace RutasDBUS.Servicios;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identificacion y textos comunes de las lineas Taxibus.
|
||||||
|
/// </summary>
|
||||||
|
public static class TaxibusDbus
|
||||||
|
{
|
||||||
|
public const string PrefijoExpedicionSimulada = "TAXIBUS_SIM_";
|
||||||
|
public const string MensajeReserva = "Esta l\u00ednea requiere solicitud de reserva.";
|
||||||
|
public const string MensajeHorarioSimulado =
|
||||||
|
"Horario provisional simulado cada 30 min. Esta l\u00ednea requiere solicitud de reserva.";
|
||||||
|
|
||||||
|
public static bool EsLinea(string? codigo)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(codigo))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var normalizado = codigo.Trim();
|
||||||
|
var separador = normalizado.IndexOf('.');
|
||||||
|
if (separador > 0)
|
||||||
|
normalizado = normalizado[..separador];
|
||||||
|
|
||||||
|
return normalizado.Equals("TB6", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
normalizado.Equals("TB7", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
normalizado.Equals("6", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
normalizado.Equals("7", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool EsExpedicionSimulada(string? idExpedicion)
|
||||||
|
=> !string.IsNullOrWhiteSpace(idExpedicion) &&
|
||||||
|
idExpedicion.StartsWith(PrefijoExpedicionSimulada, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
@@ -53,6 +53,33 @@
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-previous-point {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
margin: 7px 0 6px;
|
||||||
|
padding: 7px 0 2px;
|
||||||
|
border-top: 1px solid rgba(148, 163, 184, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-previous-point__distance {
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-previous-point__details {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2px 8px;
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: .68rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-previous-point__details > span:first-child {
|
||||||
|
flex-basis: 100%;
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.rt-inspector-place-icon {
|
.rt-inspector-place-icon {
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
@@ -373,6 +400,19 @@ html, body {
|
|||||||
cursor: default !important;
|
cursor: default !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Leaflet enfoca cada tramo SVG con una caja rectangular al pulsarlo. */
|
||||||
|
.leaflet-interactive.rt-elev-segment:focus {
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rt-elev-segment-halo {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rt-elev-segment-halo--outer {
|
||||||
|
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .75));
|
||||||
|
}
|
||||||
|
|
||||||
/* Evitar cursor de mano en puntos, polilíneas, polígonos de Leaflet */
|
/* Evitar cursor de mano en puntos, polilíneas, polígonos de Leaflet */
|
||||||
.leaflet-marker-icon,
|
.leaflet-marker-icon,
|
||||||
.leaflet-marker-shadow,
|
.leaflet-marker-shadow,
|
||||||
@@ -946,6 +986,27 @@ html, body {
|
|||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.taxibus-notice {
|
||||||
|
margin: 0 0 .45rem;
|
||||||
|
padding: .38rem .5rem;
|
||||||
|
border-left: 3px solid #38bdf8;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(14, 116, 144, .2);
|
||||||
|
color: #e0f2fe;
|
||||||
|
font-size: .72rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxibus-notice strong {
|
||||||
|
color: #7dd3fc;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taxibus-notice--schedule {
|
||||||
|
margin: .3rem 0 .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Lista de alternativas de ruta */
|
/* Lista de alternativas de ruta */
|
||||||
|
|
||||||
.route-alt-list {
|
.route-alt-list {
|
||||||
@@ -1243,13 +1304,185 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.history-derived-note {
|
.history-derived-note {
|
||||||
display: flex;
|
display: grid;
|
||||||
align-items: baseline;
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
gap: .35rem;
|
gap: .35rem;
|
||||||
|
width: 100%;
|
||||||
font-size: .76rem;
|
font-size: .76rem;
|
||||||
color: #fcd34d;
|
color: #fcd34d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.history-derived-note__text {
|
||||||
|
min-width: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derived-info-btn {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
min-height: 26px;
|
||||||
|
padding: .2rem .5rem;
|
||||||
|
border: 1px solid rgba(251, 191, 36, .5);
|
||||||
|
border-radius: 5px;
|
||||||
|
background: rgba(120, 53, 15, .24);
|
||||||
|
color: #fde68a;
|
||||||
|
font-size: .7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derived-info-btn:hover,
|
||||||
|
.history-derived-info-btn:focus-visible {
|
||||||
|
border-color: rgba(251, 191, 36, .86);
|
||||||
|
background: rgba(146, 64, 14, .38);
|
||||||
|
color: #fef3c7;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-backdrop {
|
||||||
|
z-index: 5700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-modal-card.history-derivation-modal {
|
||||||
|
width: min(620px, calc(100vw - 2rem));
|
||||||
|
max-height: min(760px, calc(100dvh - 2rem));
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-modal__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: .75rem;
|
||||||
|
margin-bottom: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-modal__header .custom-modal-title {
|
||||||
|
margin-bottom: .15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-modal__source {
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-steps {
|
||||||
|
display: flex;
|
||||||
|
gap: .35rem;
|
||||||
|
padding: .15rem 0 .55rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-step {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
min-height: 30px;
|
||||||
|
padding: .25rem .55rem;
|
||||||
|
border: 1px solid rgba(148, 163, 184, .32);
|
||||||
|
border-radius: 5px;
|
||||||
|
background: rgba(30, 41, 59, .52);
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-size: .72rem;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-step:hover,
|
||||||
|
.history-derivation-step:focus-visible {
|
||||||
|
border-color: rgba(56, 189, 248, .7);
|
||||||
|
color: #e0f2fe;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-step--selected {
|
||||||
|
border-color: rgba(251, 191, 36, .8);
|
||||||
|
background: rgba(120, 53, 15, .3);
|
||||||
|
color: #fef3c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-selected {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: .75rem;
|
||||||
|
padding: .5rem 0;
|
||||||
|
border-top: 1px solid rgba(148, 163, 184, .2);
|
||||||
|
border-bottom: 1px solid rgba(148, 163, 184, .2);
|
||||||
|
color: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-selected span {
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: .72rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-comparison {
|
||||||
|
padding-top: .7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-comparison + .history-derivation-comparison {
|
||||||
|
margin-top: .25rem;
|
||||||
|
border-top: 1px solid rgba(148, 163, 184, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-comparison h6 {
|
||||||
|
margin: 0 0 .35rem;
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-size: .78rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-change {
|
||||||
|
padding: .48rem 0;
|
||||||
|
border-bottom: 1px solid rgba(148, 163, 184, .14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-change:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-change__heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: .75rem;
|
||||||
|
margin-bottom: .28rem;
|
||||||
|
color: #e2e8f0;
|
||||||
|
font-size: .76rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-change__heading span {
|
||||||
|
color: #fbbf24;
|
||||||
|
font-size: .7rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-change__value {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 54px minmax(0, 1fr);
|
||||||
|
gap: .4rem;
|
||||||
|
padding: .08rem 0;
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-size: .72rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-change__value > span {
|
||||||
|
color: #64748b;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-change__value > div {
|
||||||
|
min-width: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-derivation-no-changes {
|
||||||
|
padding: .35rem 0;
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: .74rem;
|
||||||
|
}
|
||||||
|
|
||||||
.history-item-action,
|
.history-item-action,
|
||||||
.history-item-delete {
|
.history-item-delete {
|
||||||
flex: 0 0 32px;
|
flex: 0 0 32px;
|
||||||
|
|||||||
@@ -100,8 +100,137 @@ window.rt.ensureRouteLayer = function () {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.rt.clearElevationSegmentSelection = function () {
|
||||||
|
const selected = window.rt._selectedElevationSegment;
|
||||||
|
if (!selected) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (selected.line && selected.originalStyle) {
|
||||||
|
selected.line.setStyle(selected.originalStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected.group && Array.isArray(selected.endpoints)) {
|
||||||
|
selected.endpoints.forEach(function (endpoint) {
|
||||||
|
selected.group.removeLayer(endpoint);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected.group && Array.isArray(selected.halos)) {
|
||||||
|
selected.halos.forEach(function (halo) {
|
||||||
|
selected.group.removeLayer(halo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
|
||||||
|
window.rt._selectedElevationSegment = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.rt.ensureElevationSegmentSelectionDismiss = function () {
|
||||||
|
if (window.rt._elevationSelectionDismissHandler) return;
|
||||||
|
|
||||||
|
window.rt._elevationSelectionDismissHandler = function (event) {
|
||||||
|
const selected = window.rt._selectedElevationSegment;
|
||||||
|
if (!selected) return;
|
||||||
|
|
||||||
|
const selectedPath = selected.line && selected.line._path;
|
||||||
|
const target = event && event.target;
|
||||||
|
if (selectedPath && target &&
|
||||||
|
(target === selectedPath ||
|
||||||
|
(typeof selectedPath.contains === "function" && selectedPath.contains(target)))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.rt.clearElevationSegmentSelection();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
"click",
|
||||||
|
window.rt._elevationSelectionDismissHandler,
|
||||||
|
true);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.rt.ensureElevationSegmentSelectionDismiss();
|
||||||
|
|
||||||
|
window.rt.selectElevationSegment = function (
|
||||||
|
routeId,
|
||||||
|
line,
|
||||||
|
coords,
|
||||||
|
group,
|
||||||
|
originalStyle,
|
||||||
|
baseWeight) {
|
||||||
|
window.rt.clearElevationSegmentSelection();
|
||||||
|
|
||||||
|
if (!line || !Array.isArray(coords) || coords.length < 2 || !group) return;
|
||||||
|
|
||||||
|
const selectionColor = originalStyle && originalStyle.color
|
||||||
|
? originalStyle.color
|
||||||
|
: "#dc2626";
|
||||||
|
const selectedWeight = Math.max(6, Number(baseWeight || 4) + 2);
|
||||||
|
const outerHalo = window.L.polyline(coords, {
|
||||||
|
color: "#111827",
|
||||||
|
weight: selectedWeight + 7,
|
||||||
|
opacity: 0.94,
|
||||||
|
dashArray: null,
|
||||||
|
lineCap: "round",
|
||||||
|
interactive: false,
|
||||||
|
className: "rt-elev-segment-halo rt-elev-segment-halo--outer"
|
||||||
|
});
|
||||||
|
const innerHalo = window.L.polyline(coords, {
|
||||||
|
color: "#f8fafc",
|
||||||
|
weight: selectedWeight + 4,
|
||||||
|
opacity: 0.96,
|
||||||
|
dashArray: null,
|
||||||
|
lineCap: "round",
|
||||||
|
interactive: false,
|
||||||
|
className: "rt-elev-segment-halo rt-elev-segment-halo--inner"
|
||||||
|
});
|
||||||
|
outerHalo.addTo(group);
|
||||||
|
innerHalo.addTo(group);
|
||||||
|
|
||||||
|
line.setStyle({
|
||||||
|
color: selectionColor,
|
||||||
|
weight: selectedWeight,
|
||||||
|
opacity: 1,
|
||||||
|
dashArray: null,
|
||||||
|
lineCap: "round"
|
||||||
|
});
|
||||||
|
|
||||||
|
const endpointOptions = {
|
||||||
|
radius: 4,
|
||||||
|
color: "#ffffff",
|
||||||
|
weight: 2,
|
||||||
|
opacity: 1,
|
||||||
|
fillColor: selectionColor,
|
||||||
|
fillOpacity: 1,
|
||||||
|
interactive: false,
|
||||||
|
className: "rt-elev-segment-endpoint"
|
||||||
|
};
|
||||||
|
const endpoints = [
|
||||||
|
window.L.circleMarker(coords[0], endpointOptions),
|
||||||
|
window.L.circleMarker(coords[coords.length - 1], endpointOptions)
|
||||||
|
];
|
||||||
|
|
||||||
|
endpoints.forEach(function (endpoint) {
|
||||||
|
endpoint.addTo(group);
|
||||||
|
});
|
||||||
|
try { line.bringToFront(); } catch { }
|
||||||
|
endpoints.forEach(function (endpoint) {
|
||||||
|
try { endpoint.bringToFront(); } catch { }
|
||||||
|
});
|
||||||
|
|
||||||
|
window.rt._selectedElevationSegment = {
|
||||||
|
routeId,
|
||||||
|
line,
|
||||||
|
group,
|
||||||
|
halos: [outerHalo, innerHalo],
|
||||||
|
endpoints,
|
||||||
|
originalStyle
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
window.rt.clearRoutes = function () {
|
window.rt.clearRoutes = function () {
|
||||||
if (!window.rt.ensureRouteLayer()) return;
|
if (!window.rt.ensureRouteLayer()) return;
|
||||||
|
window.rt.clearElevationSegmentSelection();
|
||||||
window.rt._routeLayer.clearLayers();
|
window.rt._routeLayer.clearLayers();
|
||||||
window.rt._routeLines = {};
|
window.rt._routeLines = {};
|
||||||
window.rt._routeCoords = {};
|
window.rt._routeCoords = {};
|
||||||
@@ -114,6 +243,11 @@ window.rt.clearRoutes = function () {
|
|||||||
window.rt.drawRoute = function (id, coords, options) {
|
window.rt.drawRoute = function (id, coords, options) {
|
||||||
if (!window.rt.ensureRouteLayer()) return;
|
if (!window.rt.ensureRouteLayer()) return;
|
||||||
|
|
||||||
|
if (window.rt._selectedElevationSegment &&
|
||||||
|
window.rt._selectedElevationSegment.routeId === id) {
|
||||||
|
window.rt.clearElevationSegmentSelection();
|
||||||
|
}
|
||||||
|
|
||||||
if (window.rt._routeLines[id]) {
|
if (window.rt._routeLines[id]) {
|
||||||
window.rt._routeLayer.removeLayer(window.rt._routeLines[id]);
|
window.rt._routeLayer.removeLayer(window.rt._routeLines[id]);
|
||||||
delete window.rt._routeLines[id];
|
delete window.rt._routeLines[id];
|
||||||
@@ -241,11 +375,19 @@ window.rt.drawRoute = function (id, coords, options) {
|
|||||||
weight,
|
weight,
|
||||||
opacity,
|
opacity,
|
||||||
lineCap: lineCap || "round",
|
lineCap: lineCap || "round",
|
||||||
|
className: "rt-elev-segment",
|
||||||
bubblingMouseEvents: false
|
bubblingMouseEvents: false
|
||||||
};
|
};
|
||||||
if (dashArray) segmentOpts.dashArray = dashArray;
|
if (dashArray) segmentOpts.dashArray = dashArray;
|
||||||
|
|
||||||
const segmentLine = buildLine(segmentCoords, segmentOpts);
|
const segmentLine = buildLine(segmentCoords, segmentOpts);
|
||||||
|
const originalSegmentStyle = {
|
||||||
|
color: segmentColor,
|
||||||
|
weight,
|
||||||
|
opacity,
|
||||||
|
lineCap: lineCap || "round",
|
||||||
|
dashArray: dashArray || null
|
||||||
|
};
|
||||||
const label = elevationTooltip(segment, 0.5);
|
const label = elevationTooltip(segment, 0.5);
|
||||||
|
|
||||||
if (label) {
|
if (label) {
|
||||||
@@ -262,6 +404,21 @@ window.rt.drawRoute = function (id, coords, options) {
|
|||||||
segmentLine.on("mouseover", updateTooltipAtCursor);
|
segmentLine.on("mouseover", updateTooltipAtCursor);
|
||||||
segmentLine.on("mousemove", updateTooltipAtCursor);
|
segmentLine.on("mousemove", updateTooltipAtCursor);
|
||||||
segmentLine.on("click", function (e) {
|
segmentLine.on("click", function (e) {
|
||||||
|
window.rt.selectElevationSegment(
|
||||||
|
id,
|
||||||
|
segmentLine,
|
||||||
|
segmentCoords,
|
||||||
|
group,
|
||||||
|
originalSegmentStyle,
|
||||||
|
weight);
|
||||||
|
window.requestAnimationFrame(function () {
|
||||||
|
try {
|
||||||
|
if (segmentLine._path && typeof segmentLine._path.blur === "function") {
|
||||||
|
segmentLine._path.blur();
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
});
|
||||||
|
|
||||||
if (!window.rt.isMobileLayout || !window.rt.isMobileLayout()) return;
|
if (!window.rt.isMobileLayout || !window.rt.isMobileLayout()) return;
|
||||||
updateTooltipAtCursor(e);
|
updateTooltipAtCursor(e);
|
||||||
try { segmentLine.openTooltip(e && e.latlng); } catch { }
|
try { segmentLine.openTooltip(e && e.latlng); } catch { }
|
||||||
|
|||||||
Reference in New Issue
Block a user