- Integrado el esfuerzo extra por pendiente en la distancia corregida a pie.
- Aplicada la distancia corregida al cálculo de d.Equilib. A, score y ordenación de alternativas. - Conservadas las distancias reales en las tarjetas y descripciones de las rutas. - Añadido el recálculo del ranking al modificar el umbral o los coeficientes de pendientes. - Simplificado el cálculo de elevaciones para utilizar únicamente la fuente G (IDEE). - Añadida la suma de esfuerzo extra al detalle de puntos de elevación. - Reorganizada la cabecera del modal de elevación y compactado su resumen. - Eliminado el factor x12 de la representación visual del score. - Mejorado el aviso de pendientes positivas significativas. - Corregidos los tooltips del punto Z para mostrarse por encima de los marcadores. - Agrupados el interruptor de visualización de pendientes y su botón de configuración. - Personalizadas las barras de desplazamiento de modales y paneles. - Priorizado C:\ProgramData\RutasDBUS para compartir historial, modelos externos y modelo activo entre distintas publicaciones IIS.
This commit is contained in:
@@ -255,7 +255,6 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
|
|||||||
|
|
||||||
private bool _popupInspectorVisible = false;
|
private bool _popupInspectorVisible = false;
|
||||||
private double _inspectorLat, _inspectorLon;
|
private double _inspectorLat, _inspectorLon;
|
||||||
private double? _inspectorElev;
|
|
||||||
private double? _inspectorElevIdee;
|
private double? _inspectorElevIdee;
|
||||||
private IReadOnlyList<LugarGeocodificadoCercano> _inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
|
private IReadOnlyList<LugarGeocodificadoCercano> _inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
|
||||||
private int _versionInspector;
|
private int _versionInspector;
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
{
|
{
|
||||||
SincronizarTextosPenalizacionPie();
|
SincronizarTextosPenalizacionPie();
|
||||||
await PersistirConfiguracionAsync();
|
await PersistirConfiguracionAsync();
|
||||||
|
await RecalcularRankingTrasCambioEsfuerzoPendienteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1209,12 +1210,16 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
|
|
||||||
var parametros = CrearParametrosPenalizacionActuales();
|
var parametros = CrearParametrosPenalizacionActuales();
|
||||||
|
|
||||||
if (alternativa.EsSoloAPie)
|
var distanciaCorregidaBase = alternativa.EsSoloAPie
|
||||||
return UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaSoloAPie, parametros);
|
? UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaSoloAPie, parametros)
|
||||||
|
: UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieInicioAprox, parametros)
|
||||||
|
+ UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieTransbordosAprox, parametros)
|
||||||
|
+ UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieFinAprox, parametros);
|
||||||
|
var esfuerzoExtra = double.IsFinite(alternativa.EsfuerzoExtraPendienteMetros)
|
||||||
|
? Math.Max(0, alternativa.EsfuerzoExtraPendienteMetros)
|
||||||
|
: 0;
|
||||||
|
|
||||||
return UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieInicioAprox, parametros)
|
return distanciaCorregidaBase + esfuerzoExtra;
|
||||||
+ UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieTransbordosAprox, parametros)
|
|
||||||
+ UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(alternativa.DistanciaPieFinAprox, parametros);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double CalcularCosteAndarEquilibradoSegundos(AlternativaRuta? alternativa)
|
private double CalcularCosteAndarEquilibradoSegundos(AlternativaRuta? alternativa)
|
||||||
@@ -1237,9 +1242,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
if (alternativa is null)
|
if (alternativa is null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var distanciaAndando = alternativa.EsSoloAPie
|
var distanciaAndando = CalcularDistanciaCorregidaAlternativa(alternativa);
|
||||||
? alternativa.DistanciaSoloAPie
|
|
||||||
: alternativa.DistanciaPieTotalAprox;
|
|
||||||
var factor = double.IsFinite(_factorEquilibradoAndarBus)
|
var factor = double.IsFinite(_factorEquilibradoAndarBus)
|
||||||
? Math.Max(0, _factorEquilibradoAndarBus)
|
? Math.Max(0, _factorEquilibradoAndarBus)
|
||||||
: 12.0;
|
: 12.0;
|
||||||
@@ -1269,7 +1272,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
if (!double.IsFinite(score))
|
if (!double.IsFinite(score))
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
return $"Score {FormatearDistanciaTarjeta(score)} (x{_factorEquilibradoAndarBus.ToString("0.##", CultureInfo.InvariantCulture)})";
|
return $"Score {FormatearDistanciaTarjeta(score)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RecalcularRutaEquilibradaTrasCambioFactorAsync()
|
private async Task RecalcularRutaEquilibradaTrasCambioFactorAsync()
|
||||||
|
|||||||
@@ -1204,7 +1204,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
private bool _elevPopupMinimizado = false;
|
private bool _elevPopupMinimizado = false;
|
||||||
|
|
||||||
|
|
||||||
private const string _versionApp = "(v-20260720c)";
|
private const string _versionApp = "(v-20260721a)";
|
||||||
|
|
||||||
|
|
||||||
private double? _paradaElevG;
|
private double? _paradaElevG;
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Actualiza las alturas (Z) de origen y destino para el resumen del detalle de ruta.
|
/// Actualiza las alturas (Z) de origen y destino para el resumen del detalle de ruta.
|
||||||
/// Prioriza IDEE y usa OpenElevation como respaldo.
|
/// Usa la fuente G (IDEE).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task ActualizarAlturasResumenRutaAsync(CancellationToken ct)
|
private async Task ActualizarAlturasResumenRutaAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
@@ -137,20 +137,6 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
? gDst
|
? gDst
|
||||||
: (double?)null;
|
: (double?)null;
|
||||||
|
|
||||||
var faltanOpen = new List<(double lat, double lon)>(2);
|
|
||||||
if (zOrigen is null && !_elevCache.ContainsKey(ElevKey(origen.Item1, origen.Item2)))
|
|
||||||
faltanOpen.Add(origen);
|
|
||||||
if (zDestino is null && !_elevCache.ContainsKey(ElevKey(destino.Item1, destino.Item2)))
|
|
||||||
faltanOpen.Add(destino);
|
|
||||||
|
|
||||||
if (faltanOpen.Count > 0)
|
|
||||||
await ObtenerElevacionesPorLoteAsync(faltanOpen, CancellationToken.None);
|
|
||||||
|
|
||||||
if (zOrigen is null && _elevCache.TryGetValue(ElevKey(origen.Item1, origen.Item2), out var oOri))
|
|
||||||
zOrigen = oOri;
|
|
||||||
if (zDestino is null && _elevCache.TryGetValue(ElevKey(destino.Item1, destino.Item2), out var oDst))
|
|
||||||
zDestino = oDst;
|
|
||||||
|
|
||||||
_altitudOrigenResumenRuta = zOrigen;
|
_altitudOrigenResumenRuta = zOrigen;
|
||||||
_altitudDestinoResumenRuta = zDestino;
|
_altitudDestinoResumenRuta = zDestino;
|
||||||
}
|
}
|
||||||
@@ -306,26 +292,11 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<string, double> _elevCache =
|
|
||||||
new(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
// Ajustes (rendimiento)
|
// Ajustes (rendimiento)
|
||||||
/* private const int ELEV_MAX_PUNTOS_POR_TRAMO = 24; */
|
/* private const int ELEV_MAX_PUNTOS_POR_TRAMO = 24; */
|
||||||
private int _elevMaxPuntosPorTramo = 999; // configurable
|
private int _elevMaxPuntosPorTramo = 999; // configurable
|
||||||
private const int ELEV_OPEN_TIMEOUT_MS = 1500;
|
|
||||||
// muestreo por tramo
|
// muestreo por tramo
|
||||||
private const int ELEV_BATCH_MAX_PUNTOS = 90; // por request (prudente)
|
private const int ELEV_BATCH_MAX_PUNTOS = 90; // por request (prudente)
|
||||||
private const int ELEV_DECIMALES_CACHE = 6; // redondeo coord para cache
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gestiona clave.
|
|
||||||
/// </summary>
|
|
||||||
private static string ElevKey(double lat, double lon)
|
|
||||||
{
|
|
||||||
double rLat = Math.Round(lat, ELEV_DECIMALES_CACHE);
|
|
||||||
double rLon = Math.Round(lon, ELEV_DECIMALES_CACHE);
|
|
||||||
return $"{rLat.ToString("0.000000", CultureInfo.InvariantCulture)},{rLon.ToString("0.000000", CultureInfo.InvariantCulture)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<(double lat, double lon)> SampleLine(double[,] line, int maxPoints)
|
private static List<(double lat, double lon)> SampleLine(double[,] line, int maxPoints)
|
||||||
{
|
{
|
||||||
@@ -385,43 +356,92 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
await ObtenerElevacionesIdeePorLoteAsync(missesIdee.Skip(i).Take(ELEV_BATCH_MAX_PUNTOS).ToList(), ct);
|
await ObtenerElevacionesIdeePorLoteAsync(missesIdee.Skip(i).Take(ELEV_BATCH_MAX_PUNTOS).ToList(), ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- OpenElevation: best-effort y con timeout corto ---
|
return ConstruirMuestrasElevacionDesdeCache(line);
|
||||||
var missesOpen = new List<(double lat, double lon)>();
|
}
|
||||||
foreach (var (lat, lon) in sampled)
|
|
||||||
{
|
|
||||||
var k = ElevKey(lat, lon);
|
|
||||||
if (!_elevCache.ContainsKey(k))
|
|
||||||
missesOpen.Add((Math.Round(lat, ELEV_DECIMALES_CACHE), Math.Round(lon, ELEV_DECIMALES_CACHE)));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < missesOpen.Count; i += ELEV_BATCH_MAX_PUNTOS)
|
private List<MuestraElevacion> ConstruirMuestrasElevacionDesdeCache(double[,] line)
|
||||||
{
|
{
|
||||||
ct.ThrowIfCancellationRequested();
|
var sampled = SampleLine(line, _elevMaxPuntosPorTramo);
|
||||||
await ObtenerElevacionesPorLoteAsync(missesOpen.Skip(i).Take(ELEV_BATCH_MAX_PUNTOS).ToList(), ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- montar resultado (UNA sola vez) ---
|
|
||||||
var res = new List<MuestraElevacion>(sampled.Count);
|
var res = new List<MuestraElevacion>(sampled.Count);
|
||||||
for (int i = 0; i < sampled.Count; i++)
|
for (int i = 0; i < sampled.Count; i++)
|
||||||
{
|
{
|
||||||
ct.ThrowIfCancellationRequested();
|
|
||||||
var (lat, lon) = sampled[i];
|
var (lat, lon) = sampled[i];
|
||||||
|
|
||||||
var elevOpen = _elevCache.TryGetValue(ElevKey(lat, lon), out var e1) ? e1 : (double?)null;
|
|
||||||
var elevIdee = _elevIdeeCache.TryGetValue(ClaveElevacionIdee(lat, lon), out var e2) ? e2 : (double?)null;
|
var elevIdee = _elevIdeeCache.TryGetValue(ClaveElevacionIdee(lat, lon), out var e2) ? e2 : (double?)null;
|
||||||
|
|
||||||
res.Add(new MuestraElevacion
|
res.Add(new MuestraElevacion
|
||||||
{
|
{
|
||||||
Indice = i,
|
Indice = i,
|
||||||
Latitud = lat,
|
Latitud = lat,
|
||||||
Longitud = lon,
|
Longitud = lon,
|
||||||
Elevacion = elevOpen,
|
|
||||||
ElevacionIdee = elevIdee
|
ElevacionIdee = elevIdee
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task CalcularEsfuerzoExtraPendienteAlternativasAsync(
|
||||||
|
IReadOnlyList<AlternativaRuta> alternativas,
|
||||||
|
CancellationToken ct)
|
||||||
|
{
|
||||||
|
var lineasPorAlternativa = alternativas
|
||||||
|
.Select(alternativa => new
|
||||||
|
{
|
||||||
|
Alternativa = alternativa,
|
||||||
|
Lineas = (alternativa.EsSoloAPie
|
||||||
|
? alternativa.LineaSoloAPie is { } lineaSolo
|
||||||
|
? new[] { lineaSolo }
|
||||||
|
: Enumerable.Empty<double[,]>()
|
||||||
|
: alternativa.LineasPieCalculoElevacion)
|
||||||
|
.Where(linea => linea.GetLength(0) >= 2)
|
||||||
|
.ToList()
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var puntosPendientes = new Dictionary<string, (double lat, double lon)>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var item in lineasPorAlternativa)
|
||||||
|
{
|
||||||
|
foreach (var linea in item.Lineas)
|
||||||
|
{
|
||||||
|
foreach (var (lat, lon) in SampleLine(linea, _elevMaxPuntosPorTramo))
|
||||||
|
{
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
|
var clave = ClaveElevacionIdee(lat, lon);
|
||||||
|
if (!_elevIdeeCache.ContainsKey(clave))
|
||||||
|
{
|
||||||
|
puntosPendientes.TryAdd(
|
||||||
|
clave,
|
||||||
|
(Math.Round(lat, ELEV_IDEE_DECIMALES_CACHE), Math.Round(lon, ELEV_IDEE_DECIMALES_CACHE)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var puntos = puntosPendientes.Values.ToList();
|
||||||
|
for (var i = 0; i < puntos.Count; i += ELEV_BATCH_MAX_PUNTOS)
|
||||||
|
{
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
|
await ObtenerElevacionesIdeePorLoteAsync(
|
||||||
|
puntos.Skip(i).Take(ELEV_BATCH_MAX_PUNTOS).ToList(),
|
||||||
|
ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in lineasPorAlternativa)
|
||||||
|
{
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
|
var esfuerzoExtra = 0.0;
|
||||||
|
foreach (var linea in item.Lineas)
|
||||||
|
{
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
|
var muestras = ConstruirMuestrasElevacionDesdeCache(linea);
|
||||||
|
esfuerzoExtra += CalcularResumenEsfuerzoElevacion(muestras).EsfuerzoExtraMetros;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Alternativa.EsfuerzoExtraPendienteMetros = double.IsFinite(esfuerzoExtra)
|
||||||
|
? Math.Max(0, esfuerzoExtra)
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gestiona tramo bus es basura.
|
/// Gestiona tramo bus es basura.
|
||||||
@@ -539,64 +559,6 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private sealed class RespuestaOpenElevation
|
|
||||||
{
|
|
||||||
public List<ResultadoOpenElevation>? results { get; set; }
|
|
||||||
}
|
|
||||||
private sealed class ResultadoOpenElevation
|
|
||||||
{
|
|
||||||
public double latitude { get; set; }
|
|
||||||
public double longitude { get; set; }
|
|
||||||
public double elevation { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Recupera elevations el lote.
|
|
||||||
/// </summary>
|
|
||||||
private async Task ObtenerElevacionesPorLoteAsync(List<(double lat, double lon)> points, CancellationToken ct)
|
|
||||||
{
|
|
||||||
if (points.Count == 0) return;
|
|
||||||
|
|
||||||
// locations=lat,lon|lat,lon|...
|
|
||||||
var parts = new List<string>(points.Count);
|
|
||||||
foreach (var (lat, lon) in points)
|
|
||||||
{
|
|
||||||
ct.ThrowIfCancellationRequested();
|
|
||||||
parts.Add($"{lat.ToString(CultureInfo.InvariantCulture)},{lon.ToString(CultureInfo.InvariantCulture)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
string url = "https://openelevation.tecnosis.net/api/v1/lookup?locations=" + string.Join("|", parts);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var req = new HttpRequestMessage(HttpMethod.Get, url);
|
|
||||||
using var ctsTimeout = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
|
||||||
ctsTimeout.CancelAfter(ELEV_OPEN_TIMEOUT_MS);
|
|
||||||
|
|
||||||
using var resp = await Http.SendAsync(req, ctsTimeout.Token);
|
|
||||||
if (!resp.IsSuccessStatusCode) return;
|
|
||||||
|
|
||||||
var json = await resp.Content.ReadAsStringAsync(ctsTimeout.Token);
|
|
||||||
|
|
||||||
var data = JsonSerializer.Deserialize<RespuestaOpenElevation>(json, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data?.results is null || data.results.Count == 0) return;
|
|
||||||
|
|
||||||
foreach (var r in data.results)
|
|
||||||
{
|
|
||||||
var k = ElevKey(r.latitude, r.longitude);
|
|
||||||
_elevCache[k] = r.elevation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// silencioso: elevación es informativa
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gestiona geometria completa variante.
|
/// Gestiona geometria completa variante.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1271,7 +1233,6 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
var versionInspector = ++_versionInspector;
|
var versionInspector = ++_versionInspector;
|
||||||
_inspectorLat = lat;
|
_inspectorLat = lat;
|
||||||
_inspectorLon = lon;
|
_inspectorLon = lon;
|
||||||
_inspectorElev = null;
|
|
||||||
_inspectorElevIdee = null;
|
_inspectorElevIdee = null;
|
||||||
_inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
|
_inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
|
||||||
_popupInspectorVisible = true;
|
_popupInspectorVisible = true;
|
||||||
@@ -1286,29 +1247,9 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
maxResultados: 3,
|
maxResultados: 3,
|
||||||
cancellationToken: CancellationToken.None);
|
cancellationToken: CancellationToken.None);
|
||||||
|
|
||||||
double? elevacionOpen = null;
|
|
||||||
double? elevacionIdee = null;
|
double? elevacionIdee = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// -------------------------
|
|
||||||
// OpenElevation (cache primero)
|
|
||||||
// -------------------------
|
|
||||||
var kOpen = ElevKey(lat, lon);
|
|
||||||
if (_elevCache.TryGetValue(kOpen, out var cachedOpen))
|
|
||||||
{
|
|
||||||
elevacionOpen = cachedOpen;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await ObtenerElevacionesPorLoteAsync(
|
|
||||||
new List<(double lat, double lon)> { (lat, lon) },
|
|
||||||
CancellationToken.None);
|
|
||||||
|
|
||||||
elevacionOpen = _elevCache.TryGetValue(ElevKey(lat, lon), out var elevOpen)
|
|
||||||
? elevOpen
|
|
||||||
: (double?)null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// IDEE (cache primero)
|
// IDEE (cache primero)
|
||||||
// -------------------------
|
// -------------------------
|
||||||
@@ -1351,7 +1292,6 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
if (versionInspector != _versionInspector)
|
if (versionInspector != _versionInspector)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_inspectorElev = elevacionOpen;
|
|
||||||
_inspectorElevIdee = elevacionIdee;
|
_inspectorElevIdee = elevacionIdee;
|
||||||
await ActualizarMarcadoresInspectorJsAsync();
|
await ActualizarMarcadoresInspectorJsAsync();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
@@ -1701,6 +1641,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
|
|
||||||
_opcionesRuta.Clear();
|
_opcionesRuta.Clear();
|
||||||
_opcionesRuta.AddRange(filtradas);
|
_opcionesRuta.AddRange(filtradas);
|
||||||
|
await CalcularEsfuerzoExtraPendienteAlternativasAsync(_opcionesRuta, ct);
|
||||||
OrdenarOpcionesRutaSegunCriterio();
|
OrdenarOpcionesRutaSegunCriterio();
|
||||||
|
|
||||||
if (!resultado.Ok || _opcionesRuta.Count == 0)
|
if (!resultado.Ok || _opcionesRuta.Count == 0)
|
||||||
|
|||||||
@@ -18,6 +18,19 @@ namespace RutasDBUS.Components.Pages;
|
|||||||
|
|
||||||
public partial class PlanificadorRutas : ComponentBase
|
public partial class PlanificadorRutas : ComponentBase
|
||||||
{
|
{
|
||||||
|
private readonly record struct ResumenEsfuerzoElevacion(
|
||||||
|
double DistanciaTotalMetros,
|
||||||
|
double DistanciaPendientePositivaMetros,
|
||||||
|
double AlturaPendientePositivaMetros,
|
||||||
|
double EsfuerzoPositivo,
|
||||||
|
double EsfuerzoTotal,
|
||||||
|
bool HayEsfuerzoPositivo,
|
||||||
|
bool HayEsfuerzo)
|
||||||
|
{
|
||||||
|
public double EsfuerzoExtraMetros =>
|
||||||
|
Math.Max(0, EsfuerzoPositivo - DistanciaPendientePositivaMetros);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asegura que elevacion tramo este disponible.
|
/// Asegura que elevacion tramo este disponible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -40,20 +53,19 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
{
|
{
|
||||||
var ctNone = CancellationToken.None;
|
var ctNone = CancellationToken.None;
|
||||||
|
|
||||||
// 1) Samples bajo demanda, priorizando G y dejando O como best-effort
|
// Samples bajo demanda usando la fuente G.
|
||||||
var samples = await ConstruirMuestrasElevacionAsync(tramo.Linea, ctNone);
|
var samples = await ConstruirMuestrasElevacionAsync(tramo.Linea, ctNone);
|
||||||
tramo.MuestrasElevacion = samples;
|
tramo.MuestrasElevacion = samples;
|
||||||
|
|
||||||
// 2) Resumen O solo con lo que ya haya llegado, sin bloquear más la UI
|
var muestrasIdee = samples
|
||||||
var muestrasOpen = samples
|
.Where(x => x.ElevacionIdee is not null)
|
||||||
.Where(x => x.Elevacion is not null)
|
.Select(x => x.ElevacionIdee!.Value)
|
||||||
.Select(x => x.Elevacion!.Value)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
tramo.ElevInicio = samples.FirstOrDefault()?.Elevacion;
|
tramo.ElevInicio = samples.FirstOrDefault()?.ElevacionIdee;
|
||||||
tramo.ElevFin = samples.LastOrDefault()?.Elevacion;
|
tramo.ElevFin = samples.LastOrDefault()?.ElevacionIdee;
|
||||||
tramo.ElevMin = muestrasOpen.Count > 0 ? muestrasOpen.Min() : null;
|
tramo.ElevMin = muestrasIdee.Count > 0 ? muestrasIdee.Min() : null;
|
||||||
tramo.ElevMax = muestrasOpen.Count > 0 ? muestrasOpen.Max() : null;
|
tramo.ElevMax = muestrasIdee.Count > 0 ? muestrasIdee.Max() : null;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -95,12 +107,54 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
|
|
||||||
await PersistirConfiguracionAsync();
|
await PersistirConfiguracionAsync();
|
||||||
|
|
||||||
if (_mostrarElevacionesRuta)
|
if (esSubida)
|
||||||
|
{
|
||||||
|
await RecalcularRankingTrasCambioEsfuerzoPendienteAsync();
|
||||||
|
}
|
||||||
|
else if (_mostrarElevacionesRuta)
|
||||||
await RepintarRutaSeleccionadaPorElevacionesAsync("Aplicando pendientes...");
|
await RepintarRutaSeleccionadaPorElevacionesAsync("Aplicando pendientes...");
|
||||||
else
|
else
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RecalcularRankingTrasCambioEsfuerzoPendienteAsync()
|
||||||
|
{
|
||||||
|
if (_opcionesRuta.Count == 0)
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var opcionSeleccionadaAnterior = _indiceAlternativaSeleccionada >= 0 &&
|
||||||
|
_indiceAlternativaSeleccionada < _opcionesRuta.Count
|
||||||
|
? _opcionesRuta[_indiceAlternativaSeleccionada]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
await CalcularEsfuerzoExtraPendienteAlternativasAsync(_opcionesRuta, CancellationToken.None);
|
||||||
|
OrdenarOpcionesRutaSegunCriterio();
|
||||||
|
|
||||||
|
var nuevoIndice = CriterioPrioritarioTiempoActivo && opcionSeleccionadaAnterior is not null
|
||||||
|
? _opcionesRuta.IndexOf(opcionSeleccionadaAnterior)
|
||||||
|
: BuscarIndicePreferidoPorCriterio(_criterioPrioritarioRuta);
|
||||||
|
if (nuevoIndice < 0 || nuevoIndice >= _opcionesRuta.Count)
|
||||||
|
nuevoIndice = 0;
|
||||||
|
|
||||||
|
var cambioSeleccion = _indiceAlternativaSeleccionada != nuevoIndice ||
|
||||||
|
!ReferenceEquals(opcionSeleccionadaAnterior, _opcionesRuta[nuevoIndice]);
|
||||||
|
_indiceAlternativaSeleccionada = nuevoIndice;
|
||||||
|
|
||||||
|
if (_mostrarElevacionesRuta || cambioSeleccion)
|
||||||
|
{
|
||||||
|
await RepintarRutaSeleccionadaPorElevacionesAsync(_mostrarElevacionesRuta
|
||||||
|
? "Aplicando pendientes..."
|
||||||
|
: "Representando ruta seleccionada...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task CambiarUmbralPisosPendienteRutaAsync(ChangeEventArgs e)
|
private async Task CambiarUmbralPisosPendienteRutaAsync(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_umbralPisosPendienteRuta = ParseUmbralPisosPendienteRuta(
|
_umbralPisosPendienteRuta = ParseUmbralPisosPendienteRuta(
|
||||||
@@ -153,7 +207,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
|
|
||||||
private static double NormalizarUmbralPendienteRuta(double valor)
|
private static double NormalizarUmbralPendienteRuta(double valor)
|
||||||
=> double.IsFinite(valor)
|
=> double.IsFinite(valor)
|
||||||
? Math.Clamp(Math.Abs(valor), 0.0, 50.0)
|
? Math.Clamp(Math.Abs(valor), 0.0, 100.0)
|
||||||
: UmbralPendienteSubidaRutaPorDefecto;
|
: UmbralPendienteSubidaRutaPorDefecto;
|
||||||
|
|
||||||
private static double ParseUmbralPisosPendienteRuta(object? valor, double actual)
|
private static double ParseUmbralPisosPendienteRuta(object? valor, double actual)
|
||||||
@@ -211,7 +265,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
{
|
{
|
||||||
if (!_mostrarElevacionesRuta || linea.GetLength(0) < 2)
|
if (!_mostrarElevacionesRuta || linea.GetLength(0) < 2)
|
||||||
return (null, 0.0, 0.0);
|
return (null, 0.0, 0.0);
|
||||||
|
|
||||||
var muestras = await ConstruirMuestrasElevacionAsync(linea, ct);
|
var muestras = await ConstruirMuestrasElevacionAsync(linea, ct);
|
||||||
if (muestras.Count < 2)
|
if (muestras.Count < 2)
|
||||||
return (null, 0.0, 0.0);
|
return (null, 0.0, 0.0);
|
||||||
@@ -274,39 +328,68 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
|
|
||||||
private (double DistanciaMetros, double AlturaMetros)
|
private (double DistanciaMetros, double AlturaMetros)
|
||||||
CalcularTotalesPendientePositivaSignificativa(IReadOnlyList<MuestraElevacion>? muestras)
|
CalcularTotalesPendientePositivaSignificativa(IReadOnlyList<MuestraElevacion>? muestras)
|
||||||
|
{
|
||||||
|
var resumen = CalcularResumenEsfuerzoElevacion(muestras);
|
||||||
|
return (resumen.DistanciaPendientePositivaMetros, resumen.AlturaPendientePositivaMetros);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResumenEsfuerzoElevacion CalcularResumenEsfuerzoElevacion(
|
||||||
|
IReadOnlyList<MuestraElevacion>? muestras)
|
||||||
{
|
{
|
||||||
if (muestras is null || muestras.Count < 2)
|
if (muestras is null || muestras.Count < 2)
|
||||||
return (0.0, 0.0);
|
return default;
|
||||||
|
|
||||||
var distanciaTotal = 0.0;
|
var distanciaTotal = 0.0;
|
||||||
var alturaTotal = 0.0;
|
var distanciaPendientePositiva = 0.0;
|
||||||
|
var alturaPendientePositiva = 0.0;
|
||||||
|
var esfuerzoPositivo = 0.0;
|
||||||
|
var esfuerzoTotal = 0.0;
|
||||||
|
var hayEsfuerzoPositivo = false;
|
||||||
|
var hayEsfuerzo = false;
|
||||||
var umbralSubida = NormalizarUmbralPendienteRuta(_umbralPendienteSubidaRuta);
|
var umbralSubida = NormalizarUmbralPendienteRuta(_umbralPendienteSubidaRuta);
|
||||||
|
|
||||||
for (var i = 1; i < muestras.Count; i++)
|
for (var i = 1; i < muestras.Count; i++)
|
||||||
{
|
{
|
||||||
var anterior = muestras[i - 1];
|
var anterior = muestras[i - 1];
|
||||||
var actual = muestras[i];
|
var actual = muestras[i];
|
||||||
if (anterior.ElevacionIdee is null || actual.ElevacionIdee is null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var distancia = CalcularDistanciaMetros(
|
var distancia = CalcularDistanciaMetros(
|
||||||
anterior.Latitud,
|
anterior.Latitud,
|
||||||
anterior.Longitud,
|
anterior.Longitud,
|
||||||
actual.Latitud,
|
actual.Latitud,
|
||||||
actual.Longitud);
|
actual.Longitud);
|
||||||
if (distancia <= 0)
|
if (!double.IsFinite(distancia) || distancia <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
distanciaTotal += distancia;
|
||||||
|
if (anterior.ElevacionIdee is null || actual.ElevacionIdee is null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var diferenciaAltura = actual.ElevacionIdee.Value - anterior.ElevacionIdee.Value;
|
var diferenciaAltura = actual.ElevacionIdee.Value - anterior.ElevacionIdee.Value;
|
||||||
var pendiente = diferenciaAltura / distancia * 100.0;
|
var pendiente = diferenciaAltura / distancia * 100.0;
|
||||||
if (pendiente <= umbralSubida)
|
var esfuerzo = CalcularEsfuerzoElevacion(distancia, pendiente);
|
||||||
|
if (esfuerzo is null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
distanciaTotal += distancia;
|
esfuerzoTotal += esfuerzo.Value;
|
||||||
alturaTotal += Math.Max(0, diferenciaAltura);
|
hayEsfuerzo = true;
|
||||||
|
|
||||||
|
if (pendiente < umbralSubida)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
distanciaPendientePositiva += distancia;
|
||||||
|
alturaPendientePositiva += Math.Max(0, diferenciaAltura);
|
||||||
|
esfuerzoPositivo += esfuerzo.Value;
|
||||||
|
hayEsfuerzoPositivo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (distanciaTotal, alturaTotal);
|
return new ResumenEsfuerzoElevacion(
|
||||||
|
distanciaTotal,
|
||||||
|
distanciaPendientePositiva,
|
||||||
|
alturaPendientePositiva,
|
||||||
|
esfuerzoPositivo,
|
||||||
|
esfuerzoTotal,
|
||||||
|
hayEsfuerzoPositivo,
|
||||||
|
hayEsfuerzo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatearDistanciaResumenElevacion(double distanciaMetros)
|
private static string FormatearDistanciaResumenElevacion(double distanciaMetros)
|
||||||
@@ -327,7 +410,7 @@ public partial class PlanificadorRutas : ComponentBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static double? ElevacionPreferidaMapa(MuestraElevacion muestra)
|
private static double? ElevacionPreferidaMapa(MuestraElevacion muestra)
|
||||||
=> muestra.ElevacionIdee ?? muestra.Elevacion;
|
=> muestra.ElevacionIdee;
|
||||||
|
|
||||||
private (string Categoria, string Color) ClasificarPendienteMapa(double? pendientePorcentaje)
|
private (string Categoria, string Color) ClasificarPendienteMapa(double? pendientePorcentaje)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -346,13 +346,6 @@
|
|||||||
<div class="popup-parada-body">
|
<div class="popup-parada-body">
|
||||||
<div><b>Latitud:</b> @_inspectorLat.ToString("0.000000")</div>
|
<div><b>Latitud:</b> @_inspectorLat.ToString("0.000000")</div>
|
||||||
<div><b>Longitud:</b> @_inspectorLon.ToString("0.000000")</div>
|
<div><b>Longitud:</b> @_inspectorLon.ToString("0.000000")</div>
|
||||||
<div>
|
|
||||||
<b>Altitud (O):</b>
|
|
||||||
@(_inspectorElev is null
|
|
||||||
? "-"
|
|
||||||
: _inspectorElev.Value.ToString("0.00", CultureInfo.InvariantCulture) + " m")
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<b>Altitud (G):</b>
|
<b>Altitud (G):</b>
|
||||||
@(_inspectorElevIdee is null
|
@(_inspectorElevIdee is null
|
||||||
@@ -404,7 +397,11 @@
|
|||||||
|
|
||||||
<div class="popup-parada-header popup-elev-header draggable-popup-handle">
|
<div class="popup-parada-header popup-elev-header draggable-popup-handle">
|
||||||
<span class="popup-elev-title">
|
<span class="popup-elev-title">
|
||||||
Puntos de elevación · @_tramoElevDetalle.Tipo
|
<span class="popup-elev-title-main">Puntos de elevación</span>
|
||||||
|
<span class="popup-elev-title-threshold">
|
||||||
|
Umbral de subida (Esf.):
|
||||||
|
@NormalizarUmbralPendienteRuta(_umbralPendienteSubidaRuta).ToString("0.#", CultureInfo.InvariantCulture) %
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="popup-elev-actions">
|
<div class="popup-elev-actions">
|
||||||
@@ -438,11 +435,6 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="muted" style="font-size:0.72rem; margin:0 0 .35rem;">
|
|
||||||
<b>Umbral de subida (Esf.):</b>
|
|
||||||
@NormalizarUmbralPendienteRuta(_umbralPendienteSubidaRuta).ToString("0.#", CultureInfo.InvariantCulture) %
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="popup-elev-table" style="
|
<table class="popup-elev-table" style="
|
||||||
width:100%;
|
width:100%;
|
||||||
font-size:0.8rem;
|
font-size:0.8rem;
|
||||||
@@ -542,13 +534,6 @@
|
|||||||
{
|
{
|
||||||
var s = _tramoElevDetalle.MuestrasElevacion ?? new List<MuestraElevacion>();
|
var s = _tramoElevDetalle.MuestrasElevacion ?? new List<MuestraElevacion>();
|
||||||
|
|
||||||
// --- resumen altitudes ---
|
|
||||||
double? OIni = s.Count > 0 ? s[0].Elevacion : null;
|
|
||||||
double? OFin = s.Count > 0 ? s[^1].Elevacion : null;
|
|
||||||
double? OMin = s.Where(x => x.Elevacion is not null).Select(x => x.Elevacion!.Value).DefaultIfEmpty().Min();
|
|
||||||
double? OMax = s.Where(x => x.Elevacion is not null).Select(x => x.Elevacion!.Value).DefaultIfEmpty().Max();
|
|
||||||
if (!s.Any(x => x.Elevacion is not null)) { OMin = null; OMax = null; }
|
|
||||||
|
|
||||||
double? GIni = s.Count > 0 ? s[0].ElevacionIdee : null;
|
double? GIni = s.Count > 0 ? s[0].ElevacionIdee : null;
|
||||||
double? GFin = s.Count > 0 ? s[^1].ElevacionIdee : null;
|
double? GFin = s.Count > 0 ? s[^1].ElevacionIdee : null;
|
||||||
double? GMin = s.Where(x => x.ElevacionIdee is not null).Select(x => x.ElevacionIdee!.Value).DefaultIfEmpty().Min();
|
double? GMin = s.Where(x => x.ElevacionIdee is not null).Select(x => x.ElevacionIdee!.Value).DefaultIfEmpty().Min();
|
||||||
@@ -556,61 +541,27 @@
|
|||||||
if (!s.Any(x => x.ElevacionIdee is not null)) { GMin = null; GMax = null; }
|
if (!s.Any(x => x.ElevacionIdee is not null)) { GMin = null; GMax = null; }
|
||||||
|
|
||||||
string F(double? v) => v is null ? "-" : v.Value.ToString("0.00", CultureInfo.InvariantCulture) + " m";
|
string F(double? v) => v is null ? "-" : v.Value.ToString("0.00", CultureInfo.InvariantCulture) + " m";
|
||||||
|
var resumenEsfuerzo = CalcularResumenEsfuerzoElevacion(s);
|
||||||
// --- suma distancias entre puntos ---
|
|
||||||
double totalDist = 0;
|
|
||||||
double totalEsfuerzoPositivo = 0;
|
|
||||||
double totalEsfuerzo = 0;
|
|
||||||
var totalesPendientePositiva = CalcularTotalesPendientePositivaSignificativa(s);
|
|
||||||
double totalDistPendPositiva = totalesPendientePositiva.DistanciaMetros;
|
|
||||||
double totalAlturaPendPositiva = totalesPendientePositiva.AlturaMetros;
|
|
||||||
var hayEsfuerzoPositivo = false;
|
|
||||||
var hayEsfuerzo = false;
|
|
||||||
var umbralPendientePositiva = NormalizarUmbralPendienteRuta(_umbralPendienteSubidaRuta);
|
|
||||||
for (int i = 1; i < s.Count; i++)
|
|
||||||
{
|
|
||||||
var anterior = s[i - 1];
|
|
||||||
var actual = s[i];
|
|
||||||
var distancia = CalcularDistanciaMetros(anterior.Latitud, anterior.Longitud, actual.Latitud, actual.Longitud);
|
|
||||||
totalDist += distancia;
|
|
||||||
|
|
||||||
double? pendiente = null;
|
|
||||||
if (distancia > 0 && anterior.ElevacionIdee is not null && actual.ElevacionIdee is not null)
|
|
||||||
{
|
|
||||||
pendiente = (actual.ElevacionIdee.Value - anterior.ElevacionIdee.Value) / distancia * 100.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var esfuerzo = CalcularEsfuerzoElevacion(distancia, pendiente);
|
|
||||||
if (esfuerzo is not null)
|
|
||||||
{
|
|
||||||
totalEsfuerzo += esfuerzo.Value;
|
|
||||||
hayEsfuerzo = true;
|
|
||||||
|
|
||||||
if (pendiente > umbralPendientePositiva)
|
|
||||||
{
|
|
||||||
totalEsfuerzoPositivo += esfuerzo.Value;
|
|
||||||
hayEsfuerzoPositivo = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="muted" style="font-size:0.78rem; margin-top:.5rem;">
|
<div class="muted" style="font-size:0.78rem; margin-top:.5rem;">
|
||||||
<div><b>O:</b> (inicio → fin) = @F(OIni) → @F(OFin) · (mínimo/máximo) = @F(OMin) / @F(OMax)</div>
|
<div><b>G:</b> Ini/Fin: @F(GIni) / @F(GFin) · Min/Max: @F(GMin) / @F(GMax)</div>
|
||||||
<div><b>G:</b> (inicio → fin) = @F(GIni) → @F(GFin) · (mínimo/máximo) = @F(GMin) / @F(GMax)</div>
|
|
||||||
<div style="margin-top:.25rem;">
|
<div style="margin-top:.25rem;">
|
||||||
<b>Suma de distancias entre puntos:</b> @FormatearDistanciaResumenElevacion(totalDist)
|
<b>Suma de distancias entre puntos:</b> @FormatearDistanciaResumenElevacion(resumenEsfuerzo.DistanciaTotalMetros)
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<b>Suma de distancias con pendiente positiva:</b> @FormatearDistanciaResumenElevacion(totalDistPendPositiva)
|
<b>Suma de distancias con pendiente positiva:</b> @FormatearDistanciaResumenElevacion(resumenEsfuerzo.DistanciaPendientePositivaMetros)
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<b>Suma de alturas con pendiente positiva:</b> @(totalAlturaPendPositiva > 0
|
<b>Suma de alturas con pendiente positiva:</b> @(resumenEsfuerzo.AlturaPendientePositivaMetros > 0
|
||||||
? totalAlturaPendPositiva.ToString("0.00", CultureInfo.InvariantCulture) + " m (" + (totalAlturaPendPositiva / MetrosPorPiso).ToString("0.0", CultureInfo.InvariantCulture) + " pisos)"
|
? resumenEsfuerzo.AlturaPendientePositivaMetros.ToString("0.00", CultureInfo.InvariantCulture) + " m (" + (resumenEsfuerzo.AlturaPendientePositivaMetros / MetrosPorPiso).ToString("0.0", CultureInfo.InvariantCulture) + " pisos)"
|
||||||
: "-")
|
: "-")
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<b>Suma de esfuerzos positivos:</b> @(hayEsfuerzoPositivo ? totalEsfuerzoPositivo.ToString("0.00", CultureInfo.InvariantCulture) : "-")
|
<b>Suma de esfuerzos positivos:</b> @(resumenEsfuerzo.HayEsfuerzoPositivo ? resumenEsfuerzo.EsfuerzoPositivo.ToString("0.00", CultureInfo.InvariantCulture) : "-")
|
||||||
<span style="margin-left:.5rem;"><b>Total:</b> @(hayEsfuerzo ? totalEsfuerzo.ToString("0.00", CultureInfo.InvariantCulture) : "-")</span>
|
<span style="margin-left:.5rem;"><b>Total:</b> @(resumenEsfuerzo.HayEsfuerzo ? resumenEsfuerzo.EsfuerzoTotal.ToString("0.00", CultureInfo.InvariantCulture) : "-")</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<b>Suma de esfuerzo extra:</b> @(resumenEsfuerzo.HayEsfuerzo ? resumenEsfuerzo.EsfuerzoExtraMetros.ToString("0.00", CultureInfo.InvariantCulture) : "-")
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -815,11 +766,11 @@
|
|||||||
@if (MostrarAvisoPendientesPositivasSignificativas)
|
@if (MostrarAvisoPendientesPositivasSignificativas)
|
||||||
{
|
{
|
||||||
<div class="route-slope-warning" role="status">
|
<div class="route-slope-warning" role="status">
|
||||||
<div>Solución con pendientes positivas significativas.</div>
|
<div>Pendientes positivas significativas.</div>
|
||||||
<div style="margin-top:.2rem; padding-left:.55rem;">
|
<div style="margin-top:.2rem; padding-left:.55rem;">
|
||||||
<div>- Distancia con pendiente: @FormatearDistanciaResumenElevacion(_distanciaPendientePositivaSignificativaRutaMetros)</div>
|
<div>- Distancia con subidas: @FormatearDistanciaResumenElevacion(_distanciaPendientePositivaSignificativaRutaMetros)</div>
|
||||||
<div>
|
<div>
|
||||||
- Altura subida: @_alturaPendientePositivaSignificativaRutaMetros.ToString("0.00", CultureInfo.InvariantCulture) m
|
- Subidas acumuladas: @_alturaPendientePositivaSignificativaRutaMetros.ToString("0.00", CultureInfo.InvariantCulture) m
|
||||||
(@((_alturaPendientePositivaSignificativaRutaMetros / MetrosPorPiso).ToString("0.0", CultureInfo.InvariantCulture)) pisos)
|
(@((_alturaPendientePositivaSignificativaRutaMetros / MetrosPorPiso).ToString("0.0", CultureInfo.InvariantCulture)) pisos)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ public sealed class AlternativaRuta
|
|||||||
public double DistanciaPieTotalAprox =>
|
public double DistanciaPieTotalAprox =>
|
||||||
DistanciaPieInicioAprox + DistanciaPieTransbordosAprox + DistanciaPieFinAprox;
|
DistanciaPieInicioAprox + DistanciaPieTransbordosAprox + DistanciaPieFinAprox;
|
||||||
|
|
||||||
|
public List<double[,]> LineasPieCalculoElevacion { get; set; } = new();
|
||||||
|
public double EsfuerzoExtraPendienteMetros { get; set; }
|
||||||
|
|
||||||
public double TiempoPieInicio { get; set; }
|
public double TiempoPieInicio { get; set; }
|
||||||
public double TiempoPieFin { get; set; }
|
public double TiempoPieFin { get; set; }
|
||||||
public double TiempoEsperaInicialSegundos { get; set; }
|
public double TiempoEsperaInicialSegundos { get; set; }
|
||||||
|
|||||||
@@ -5,6 +5,5 @@ public sealed class MuestraElevacion
|
|||||||
public int Indice { get; set; }
|
public int Indice { get; set; }
|
||||||
public double Latitud { get; set; }
|
public double Latitud { get; set; }
|
||||||
public double Longitud { get; set; }
|
public double Longitud { get; set; }
|
||||||
public double? Elevacion { get; set; }
|
|
||||||
public double? ElevacionIdee { get; set; }
|
public double? ElevacionIdee { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,12 +239,12 @@ public sealed class ServicioHistorialRutasCompartido : IHistorialRutasCompartido
|
|||||||
|
|
||||||
private static IEnumerable<string> EnumerarCarpetasCandidatas(IWebHostEnvironment env)
|
private static IEnumerable<string> EnumerarCarpetasCandidatas(IWebHostEnvironment env)
|
||||||
{
|
{
|
||||||
yield return Path.Combine(env.ContentRootPath, "Data");
|
|
||||||
|
|
||||||
var commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
var commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
||||||
if (!string.IsNullOrWhiteSpace(commonAppData))
|
if (!string.IsNullOrWhiteSpace(commonAppData))
|
||||||
yield return Path.Combine(commonAppData, "RutasDBUS");
|
yield return Path.Combine(commonAppData, "RutasDBUS");
|
||||||
|
|
||||||
|
yield return Path.Combine(env.ContentRootPath, "Data");
|
||||||
|
|
||||||
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
if (!string.IsNullOrWhiteSpace(localAppData))
|
if (!string.IsNullOrWhiteSpace(localAppData))
|
||||||
yield return Path.Combine(localAppData, "RutasDBUS");
|
yield return Path.Combine(localAppData, "RutasDBUS");
|
||||||
|
|||||||
@@ -271,8 +271,8 @@ public sealed class ServicioArchivoImportacionDbusMonitorizado : IServicioArchiv
|
|||||||
|
|
||||||
var candidatas = new[]
|
var candidatas = new[]
|
||||||
{
|
{
|
||||||
Path.Combine(_entorno.ContentRootPath, "App_Data"),
|
|
||||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "RutasDBUS"),
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "RutasDBUS"),
|
||||||
|
Path.Combine(_entorno.ContentRootPath, "App_Data"),
|
||||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "RutasDBUS"),
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "RutasDBUS"),
|
||||||
Path.Combine(Path.GetTempPath(), "RutasDBUS")
|
Path.Combine(Path.GetTempPath(), "RutasDBUS")
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -816,7 +816,10 @@ public sealed class BuscadorRutasTemporalesDbus
|
|||||||
ParametrosPlanificadorRuta parametros)
|
ParametrosPlanificadorRuta parametros)
|
||||||
{
|
{
|
||||||
var distanciaBus = Math.Max(0, resultado.RutaBus.DistanciaTotalBus);
|
var distanciaBus = Math.Max(0, resultado.RutaBus.DistanciaTotalBus);
|
||||||
var distanciaAndando = Math.Max(0, resultado.DistanciaPieTotal);
|
var distanciaAndando =
|
||||||
|
UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieInicio, parametros)
|
||||||
|
+ UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieTransbordos, parametros)
|
||||||
|
+ UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieFin, parametros);
|
||||||
var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus)
|
var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus)
|
||||||
? Math.Max(0, parametros.FactorEquilibradoAndarBus)
|
? Math.Max(0, parametros.FactorEquilibradoAndarBus)
|
||||||
: 12.0;
|
: 12.0;
|
||||||
|
|||||||
@@ -1516,7 +1516,11 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
if (distanciaBus <= 0 && resultado.RutaBus.Tramos.Count > 0)
|
if (distanciaBus <= 0 && resultado.RutaBus.Tramos.Count > 0)
|
||||||
distanciaBus = CalcularDistanciaBusCandidata(resultado.RutaBus.Tramos);
|
distanciaBus = CalcularDistanciaBusCandidata(resultado.RutaBus.Tramos);
|
||||||
|
|
||||||
var distanciaAndando = Math.Max(0, resultado.DistanciaPieTotal);
|
var distanciaAndando = parametros is null
|
||||||
|
? Math.Max(0, resultado.DistanciaPieTotal)
|
||||||
|
: UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieInicio, parametros)
|
||||||
|
+ UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieTransbordos, parametros)
|
||||||
|
+ UtilidadesTransitoDbus.CalcularDistanciaPieCorregidaMetros(resultado.DistanciaPieFin, parametros);
|
||||||
var factor = parametros is not null && double.IsFinite(parametros.FactorEquilibradoAndarBus)
|
var factor = parametros is not null && double.IsFinite(parametros.FactorEquilibradoAndarBus)
|
||||||
? Math.Max(0, parametros.FactorEquilibradoAndarBus)
|
? Math.Max(0, parametros.FactorEquilibradoAndarBus)
|
||||||
: 12.0;
|
: 12.0;
|
||||||
@@ -2028,6 +2032,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
Etiqueta = "Solo a pie (v1)",
|
Etiqueta = "Solo a pie (v1)",
|
||||||
ProveedorRuta = "v1",
|
ProveedorRuta = "v1",
|
||||||
LineaSoloAPie = osrm.linea,
|
LineaSoloAPie = osrm.linea,
|
||||||
|
LineasPieCalculoElevacion = new List<double[,]> { osrm.linea },
|
||||||
DistanciaSoloAPie = osrm.distancia,
|
DistanciaSoloAPie = osrm.distancia,
|
||||||
DuracionSoloAPie = duracion,
|
DuracionSoloAPie = duracion,
|
||||||
DuracionTotalAproxSegundos = duracion,
|
DuracionTotalAproxSegundos = duracion,
|
||||||
@@ -2046,6 +2051,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
Etiqueta = "Solo a pie (v2)",
|
Etiqueta = "Solo a pie (v2)",
|
||||||
ProveedorRuta = "v2",
|
ProveedorRuta = "v2",
|
||||||
LineaSoloAPie = valhalla.linea,
|
LineaSoloAPie = valhalla.linea,
|
||||||
|
LineasPieCalculoElevacion = new List<double[,]> { valhalla.linea },
|
||||||
DistanciaSoloAPie = valhalla.distancia,
|
DistanciaSoloAPie = valhalla.distancia,
|
||||||
DuracionSoloAPie = duracion,
|
DuracionSoloAPie = duracion,
|
||||||
DuracionTotalAproxSegundos = duracion,
|
DuracionTotalAproxSegundos = duracion,
|
||||||
@@ -2056,6 +2062,35 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
return alternativas;
|
return alternativas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AgregarLineaPieCalculoElevacion(
|
||||||
|
AlternativaRuta alternativa,
|
||||||
|
double[,]? linea,
|
||||||
|
double latitudOrigen,
|
||||||
|
double longitudOrigen,
|
||||||
|
double latitudDestino,
|
||||||
|
double longitudDestino)
|
||||||
|
{
|
||||||
|
if (linea is not null && linea.GetLength(0) >= 2)
|
||||||
|
{
|
||||||
|
alternativa.LineasPieCalculoElevacion.Add(linea);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var distanciaDirecta = UtilidadesTransitoDbus.CalcularDistanciaMetros(
|
||||||
|
latitudOrigen,
|
||||||
|
longitudOrigen,
|
||||||
|
latitudDestino,
|
||||||
|
longitudDestino);
|
||||||
|
if (!double.IsFinite(distanciaDirecta) || distanciaDirecta <= 0.5)
|
||||||
|
return;
|
||||||
|
|
||||||
|
alternativa.LineasPieCalculoElevacion.Add(new double[,]
|
||||||
|
{
|
||||||
|
{ latitudOrigen, longitudOrigen },
|
||||||
|
{ latitudDestino, longitudDestino }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recalcula una alternativa de bus usando caminatas exactas y horarios reales.
|
/// Recalcula una alternativa de bus usando caminatas exactas y horarios reales.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2069,6 +2104,9 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
if (alternativa.AlternativaBus is null || alternativa.AlternativaBus.Tramos.Count == 0)
|
if (alternativa.AlternativaBus is null || alternativa.AlternativaBus.Tramos.Count == 0)
|
||||||
return double.PositiveInfinity;
|
return double.PositiveInfinity;
|
||||||
|
|
||||||
|
alternativa.LineasPieCalculoElevacion.Clear();
|
||||||
|
alternativa.EsfuerzoExtraPendienteMetros = 0;
|
||||||
|
|
||||||
var tramosBus = alternativa.AlternativaBus.Tramos;
|
var tramosBus = alternativa.AlternativaBus.Tramos;
|
||||||
var instanteSalida = UtilidadesTransitoDbus.ResolverInstanteSalida(parametros);
|
var instanteSalida = UtilidadesTransitoDbus.ResolverInstanteSalida(parametros);
|
||||||
|
|
||||||
@@ -2107,6 +2145,13 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
var cursor = instanteSalida;
|
var cursor = instanteSalida;
|
||||||
|
|
||||||
var acceso = await WalkOsrmCached(origenLatLon[0], origenLatLon[1], paradaInicioBus.Latitud, paradaInicioBus.Longitud);
|
var acceso = await WalkOsrmCached(origenLatLon[0], origenLatLon[1], paradaInicioBus.Latitud, paradaInicioBus.Longitud);
|
||||||
|
AgregarLineaPieCalculoElevacion(
|
||||||
|
alternativa,
|
||||||
|
acceso.linea,
|
||||||
|
origenLatLon[0],
|
||||||
|
origenLatLon[1],
|
||||||
|
paradaInicioBus.Latitud,
|
||||||
|
paradaInicioBus.Longitud);
|
||||||
alternativa.DistanciaPieInicioAprox = acceso.distancia > 0
|
alternativa.DistanciaPieInicioAprox = acceso.distancia > 0
|
||||||
? acceso.distancia
|
? acceso.distancia
|
||||||
: UtilidadesTransitoDbus.CalcularDistanciaMetros(origenLatLon[0], origenLatLon[1], paradaInicioBus.Latitud, paradaInicioBus.Longitud);
|
: UtilidadesTransitoDbus.CalcularDistanciaMetros(origenLatLon[0], origenLatLon[1], paradaInicioBus.Latitud, paradaInicioBus.Longitud);
|
||||||
@@ -2161,6 +2206,14 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
puntoInicioTramo![0],
|
puntoInicioTramo![0],
|
||||||
puntoInicioTramo[1]);
|
puntoInicioTramo[1]);
|
||||||
|
|
||||||
|
AgregarLineaPieCalculoElevacion(
|
||||||
|
alternativa,
|
||||||
|
transferencia.linea,
|
||||||
|
puntoUltimaLlegada[0],
|
||||||
|
puntoUltimaLlegada[1],
|
||||||
|
puntoInicioTramo[0],
|
||||||
|
puntoInicioTramo[1]);
|
||||||
|
|
||||||
var distanciaTransferencia = transferencia.distancia > 0
|
var distanciaTransferencia = transferencia.distancia > 0
|
||||||
? transferencia.distancia
|
? transferencia.distancia
|
||||||
: distanciaUnion;
|
: distanciaUnion;
|
||||||
@@ -2282,6 +2335,13 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
alternativa.DestinoParada = paradaFinBus;
|
alternativa.DestinoParada = paradaFinBus;
|
||||||
|
|
||||||
var salidaFinal = await WalkOsrmCached(puntoUltimaLlegada[0], puntoUltimaLlegada[1], destinoLatLon[0], destinoLatLon[1]);
|
var salidaFinal = await WalkOsrmCached(puntoUltimaLlegada[0], puntoUltimaLlegada[1], destinoLatLon[0], destinoLatLon[1]);
|
||||||
|
AgregarLineaPieCalculoElevacion(
|
||||||
|
alternativa,
|
||||||
|
salidaFinal.linea,
|
||||||
|
puntoUltimaLlegada[0],
|
||||||
|
puntoUltimaLlegada[1],
|
||||||
|
destinoLatLon[0],
|
||||||
|
destinoLatLon[1]);
|
||||||
alternativa.DistanciaPieFinAprox = salidaFinal.distancia > 0
|
alternativa.DistanciaPieFinAprox = salidaFinal.distancia > 0
|
||||||
? salidaFinal.distancia
|
? salidaFinal.distancia
|
||||||
: UtilidadesTransitoDbus.CalcularDistanciaMetros(puntoUltimaLlegada[0], puntoUltimaLlegada[1], destinoLatLon[0], destinoLatLon[1]);
|
: UtilidadesTransitoDbus.CalcularDistanciaMetros(puntoUltimaLlegada[0], puntoUltimaLlegada[1], destinoLatLon[0], destinoLatLon[1]);
|
||||||
@@ -3070,7 +3130,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
|||||||
return double.PositiveInfinity;
|
return double.PositiveInfinity;
|
||||||
|
|
||||||
var distanciaBus = Math.Max(0, alternativa.AlternativaBus.DistanciaTotalBus);
|
var distanciaBus = Math.Max(0, alternativa.AlternativaBus.DistanciaTotalBus);
|
||||||
var distanciaAndando = Math.Max(0, alternativa.DistanciaPieTotalAprox);
|
var distanciaAndando = CalcularDistanciaAndandoCorregidaMetros(alternativa, parametros);
|
||||||
var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus)
|
var factor = double.IsFinite(parametros.FactorEquilibradoAndarBus)
|
||||||
? Math.Max(0, parametros.FactorEquilibradoAndarBus)
|
? Math.Max(0, parametros.FactorEquilibradoAndarBus)
|
||||||
: 12.0;
|
: 12.0;
|
||||||
|
|||||||
@@ -873,12 +873,21 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.route-visual-options {
|
.route-visual-options {
|
||||||
display: flex;
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-self: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
gap: .35rem;
|
width: fit-content;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: .25rem;
|
||||||
margin: -.15rem 0 .4rem;
|
margin: -.15rem 0 .4rem;
|
||||||
|
padding: 2px 3px 2px 5px;
|
||||||
|
border: 1px solid rgba(148, 163, 184, .28);
|
||||||
|
border-radius: 7px;
|
||||||
|
background: rgba(2, 6, 23, .3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-elevation-switch {
|
.route-elevation-switch {
|
||||||
@@ -892,6 +901,7 @@ html, body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-elevation-switch input {
|
.route-elevation-switch input {
|
||||||
@@ -943,10 +953,11 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.route-elevation-settings {
|
.route-elevation-settings {
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-left: auto;
|
margin-left: .05rem;
|
||||||
|
padding-left: .3rem;
|
||||||
|
border-left: 1px solid rgba(148, 163, 184, .35);
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -982,8 +993,8 @@ html, body {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
top: calc(100% + 6px);
|
top: calc(100% + 6px);
|
||||||
right: 0;
|
right: auto;
|
||||||
left: auto;
|
left: 0;
|
||||||
width: 238px;
|
width: 238px;
|
||||||
max-width: calc(100vw - 24px);
|
max-width: calc(100vw - 24px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -1541,6 +1552,114 @@ html, body {
|
|||||||
padding: 1rem 1rem .9rem;
|
padding: 1rem 1rem .9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scrollbars compactas para paneles y modales de la aplicacion. */
|
||||||
|
:where(
|
||||||
|
.route-panel,
|
||||||
|
.route-panel *,
|
||||||
|
.history-panel,
|
||||||
|
.history-panel *,
|
||||||
|
.config-panel,
|
||||||
|
.config-panel *,
|
||||||
|
.custom-modal-card,
|
||||||
|
.custom-modal-card *,
|
||||||
|
.popup-stack-left,
|
||||||
|
.popup-stack-left *,
|
||||||
|
.popup-parada,
|
||||||
|
.popup-parada *
|
||||||
|
) {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(100, 116, 139, .78) rgba(2, 6, 23, .18);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(
|
||||||
|
.route-panel,
|
||||||
|
.route-panel *,
|
||||||
|
.history-panel,
|
||||||
|
.history-panel *,
|
||||||
|
.config-panel,
|
||||||
|
.config-panel *,
|
||||||
|
.custom-modal-card,
|
||||||
|
.custom-modal-card *,
|
||||||
|
.popup-stack-left,
|
||||||
|
.popup-stack-left *,
|
||||||
|
.popup-parada,
|
||||||
|
.popup-parada *
|
||||||
|
)::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(
|
||||||
|
.route-panel,
|
||||||
|
.route-panel *,
|
||||||
|
.history-panel,
|
||||||
|
.history-panel *,
|
||||||
|
.config-panel,
|
||||||
|
.config-panel *,
|
||||||
|
.custom-modal-card,
|
||||||
|
.custom-modal-card *,
|
||||||
|
.popup-stack-left,
|
||||||
|
.popup-stack-left *,
|
||||||
|
.popup-parada,
|
||||||
|
.popup-parada *
|
||||||
|
)::-webkit-scrollbar-track {
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(2, 6, 23, .18);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(
|
||||||
|
.route-panel,
|
||||||
|
.route-panel *,
|
||||||
|
.history-panel,
|
||||||
|
.history-panel *,
|
||||||
|
.config-panel,
|
||||||
|
.config-panel *,
|
||||||
|
.custom-modal-card,
|
||||||
|
.custom-modal-card *,
|
||||||
|
.popup-stack-left,
|
||||||
|
.popup-stack-left *,
|
||||||
|
.popup-parada,
|
||||||
|
.popup-parada *
|
||||||
|
)::-webkit-scrollbar-thumb {
|
||||||
|
border: 1px solid rgba(15, 23, 42, .88);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(100, 116, 139, .78);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(
|
||||||
|
.route-panel,
|
||||||
|
.route-panel *,
|
||||||
|
.history-panel,
|
||||||
|
.history-panel *,
|
||||||
|
.config-panel,
|
||||||
|
.config-panel *,
|
||||||
|
.custom-modal-card,
|
||||||
|
.custom-modal-card *,
|
||||||
|
.popup-stack-left,
|
||||||
|
.popup-stack-left *,
|
||||||
|
.popup-parada,
|
||||||
|
.popup-parada *
|
||||||
|
)::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(56, 189, 248, .78);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(
|
||||||
|
.route-panel,
|
||||||
|
.route-panel *,
|
||||||
|
.history-panel,
|
||||||
|
.history-panel *,
|
||||||
|
.config-panel,
|
||||||
|
.config-panel *,
|
||||||
|
.custom-modal-card,
|
||||||
|
.custom-modal-card *,
|
||||||
|
.popup-stack-left,
|
||||||
|
.popup-stack-left *,
|
||||||
|
.popup-parada,
|
||||||
|
.popup-parada *
|
||||||
|
)::-webkit-scrollbar-corner {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.custom-modal-title {
|
.custom-modal-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -2279,11 +2398,29 @@ html, body {
|
|||||||
.popup-elev-title {
|
.popup-elev-title {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: .45rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-elev-title-main {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: .95rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-elev-title-threshold {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: .68rem;
|
||||||
|
font-weight: 400;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
.popup-elev-actions {
|
.popup-elev-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
@@ -2389,7 +2526,7 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.popup-elev-topright .popup-elev-title {
|
.popup-elev-topright .popup-elev-title {
|
||||||
max-width: 150px;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-elev-topright .popup-parada-body {
|
.popup-elev-topright .popup-parada-body {
|
||||||
@@ -2415,6 +2552,12 @@ html, body {
|
|||||||
.popup-elev-table thead th:first-child {
|
.popup-elev-table thead th:first-child {
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-elev-g-summary {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: .74rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== SLIDER (range) limpio para el panel ===== */
|
/* ===== SLIDER (range) limpio para el panel ===== */
|
||||||
.config-range {
|
.config-range {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -2989,9 +3132,10 @@ html, body {
|
|||||||
|
|
||||||
.route-visual-options {
|
.route-visual-options {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
gap: 6px 10px;
|
gap: 4px;
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
|
padding: 2px 3px 2px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-elevation-switch {
|
.route-elevation-switch {
|
||||||
@@ -3005,8 +3149,8 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.route-elevation-settings-popover {
|
.route-elevation-settings-popover {
|
||||||
right: 0;
|
right: auto;
|
||||||
left: auto;
|
left: 0;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
max-width: calc(100vw - 20px);
|
max-width: calc(100vw - 20px);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1150,6 +1150,11 @@ window.rt.ensureInspectorLayer = function () {
|
|||||||
pane.style.zIndex = 790;
|
pane.style.zIndex = 790;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inspectorTooltipPane = map.getPane("rtInspectorTooltipPane")
|
||||||
|
|| map.createPane("rtInspectorTooltipPane");
|
||||||
|
inspectorTooltipPane.style.zIndex = 10000;
|
||||||
|
inspectorTooltipPane.style.pointerEvents = "none";
|
||||||
|
|
||||||
if (!window.rt._inspectorLayer) {
|
if (!window.rt._inspectorLayer) {
|
||||||
window.rt._inspectorLayer = window.L.layerGroup().addTo(map);
|
window.rt._inspectorLayer = window.L.layerGroup().addTo(map);
|
||||||
}
|
}
|
||||||
@@ -1177,7 +1182,9 @@ window.rt.setInspectorMarkers = function (lat, lon, places) {
|
|||||||
bubblingMouseEvents: false
|
bubblingMouseEvents: false
|
||||||
});
|
});
|
||||||
puntoZ.bindTooltip("Punto Z", {
|
puntoZ.bindTooltip("Punto Z", {
|
||||||
|
pane: "rtInspectorTooltipPane",
|
||||||
direction: "top",
|
direction: "top",
|
||||||
|
offset: [0, -16],
|
||||||
opacity: 0.95,
|
opacity: 0.95,
|
||||||
className: "rt-inspector-tooltip"
|
className: "rt-inspector-tooltip"
|
||||||
});
|
});
|
||||||
@@ -1247,8 +1254,9 @@ window.rt.setInspectorMarkers = function (lat, lon, places) {
|
|||||||
const tooltip = document.createElement("span");
|
const tooltip = document.createElement("span");
|
||||||
tooltip.textContent = texto;
|
tooltip.textContent = texto;
|
||||||
marker.bindTooltip(tooltip, {
|
marker.bindTooltip(tooltip, {
|
||||||
|
pane: "rtInspectorTooltipPane",
|
||||||
direction: "top",
|
direction: "top",
|
||||||
offset: [offsetX, -12],
|
offset: [offsetX, -24],
|
||||||
opacity: 0.95,
|
opacity: 0.95,
|
||||||
className: "rt-inspector-tooltip"
|
className: "rt-inspector-tooltip"
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user