- Separado el control de poligonos excluyentes
- Añadido interpreacion de tipologia de suelo en segmentos andando - Persistencia modo andando - Transbordo penalizado a 6 - Añadidos varias zonas de prohibicion de poligonos
This commit is contained in:
@@ -506,13 +506,16 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
|
||||
|
||||
private async Task<(double[,]? line, double dist, double dur)> WalkValhallaCached(double lat1, double lon1, double lat2, double lon2)
|
||||
{
|
||||
var poligonos = ObtenerPoligonosExclusionCaminata();
|
||||
var key = WalkKey("valhalla", lat1, lon1, lat2, lon2);
|
||||
if (poligonos is not null) key += "|" + JsonSerializer.Serialize(poligonos);
|
||||
if (_walkCache.TryGetValue(key, out var v))
|
||||
return v;
|
||||
|
||||
try
|
||||
{
|
||||
var (line, dist, dur) = await ClienteValhalla.ObtenerRutaAsync(lat1, lon1, lat2, lon2, costing: "pedestrian");
|
||||
var (line, dist, dur) = await ClienteValhalla.ObtenerRutaAsync(lat1, lon1, lat2, lon2,
|
||||
costing: "pedestrian", excludePolygons: poligonos);
|
||||
|
||||
// ✅ Solo cachear si es un resultado "bueno"
|
||||
if (line is not null && dist > 0)
|
||||
|
||||
@@ -68,12 +68,12 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
private double _factorPenalizacionDistanciaPie = FactorPenalizacionDistanciaPiePorDefecto;
|
||||
private double _parametroPenalizacionDistanciaPieMetros = ParametroPenalizacionDistanciaPieMetrosPorDefecto;
|
||||
private double _coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto;
|
||||
private double _pesoTransbordoRankingMinutos = 4.0;
|
||||
private double _pesoTransbordoRankingMinutos = 6.0;
|
||||
private double _factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
||||
private string _factorPenalizacionDistanciaPieTextoEdicion = "1.8";
|
||||
private string _parametroPenalizacionDistanciaPieTextoEdicion = "200";
|
||||
private string _coeficienteEsfuerzoAlturaTextoEdicion = "5.20";
|
||||
private string _pesoTransbordoRankingTextoEdicion = "4,0";
|
||||
private string _pesoTransbordoRankingTextoEdicion = "6,0";
|
||||
private string _factorEquilibradoAndarBusTextoEdicion = "6";
|
||||
private double _velocidadBusKmH = 16.0;
|
||||
private int _minutosTransbordo = 5;
|
||||
|
||||
@@ -1522,6 +1522,8 @@ public partial class PlanificadorRutas
|
||||
_coordenadasOrigen = new[] { item.OrigenLatitud, item.OrigenLongitud };
|
||||
_coordenadasDestino = new[] { item.DestinoLatitud, item.DestinoLongitud };
|
||||
|
||||
if (_modoConfiguracionHistorial == ModoConfiguracionHistorial.Predeterminada)
|
||||
_configuracionFabrica = await ValoresFabrica.CargarAsync();
|
||||
var configuracionHistorial = _modoConfiguracionHistorial switch
|
||||
{
|
||||
ModoConfiguracionHistorial.Guardada => CrearConfiguracionGuardadaHistorial(item),
|
||||
|
||||
@@ -1400,7 +1400,7 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
private bool _elevPopupMinimizado = false;
|
||||
|
||||
|
||||
private const string _versionApp = "(v-20260907a)";
|
||||
private const string _versionApp = "(v-20260908a)";
|
||||
|
||||
|
||||
private double? _paradaElevIdee;
|
||||
|
||||
@@ -23,6 +23,8 @@ public partial class PlanificadorRutas
|
||||
/// </summary>
|
||||
private async Task CargarConfiguracionLocalAsync()
|
||||
{
|
||||
_configuracionFabrica = await ValoresFabrica.CargarAsync();
|
||||
AplicarConfiguracionLocal(CrearConfiguracionPredeterminadaHistorial());
|
||||
try
|
||||
{
|
||||
var json = await JS.InvokeAsync<string?>("rt.loadLocalValue", ClaveConfiguracionLocal);
|
||||
@@ -146,7 +148,7 @@ public partial class PlanificadorRutas
|
||||
PreferenciaEtiquetaRuta = _preferenciaEtiquetaRuta
|
||||
};
|
||||
|
||||
private static ConfiguracionHistorialRuta CrearConfiguracionPredeterminadaHistorial()
|
||||
private static ConfiguracionHistorialRuta CrearConfiguracionBaseHistorial()
|
||||
{
|
||||
var ahora = DateTime.Now;
|
||||
return new ConfiguracionHistorialRuta
|
||||
@@ -174,7 +176,7 @@ public partial class PlanificadorRutas
|
||||
ParametroPenalizacionDistanciaPieMetros = ParametroPenalizacionDistanciaPieMetrosPorDefecto,
|
||||
CoeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto,
|
||||
VersionCoeficienteEsfuerzo = VersionPersistenciaCoeficienteEsfuerzo,
|
||||
PesoTransbordoRankingMinutos = 4.0,
|
||||
PesoTransbordoRankingMinutos = 6.0,
|
||||
FactorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto,
|
||||
VersionFactorEquilibrado = VersionPersistenciaFactorEquilibrado,
|
||||
MostrarElevacionesRuta = true,
|
||||
@@ -267,7 +269,7 @@ public partial class PlanificadorRutas
|
||||
? CoeficienteEsfuerzoAlturaPorDefecto
|
||||
: coeficienteAltura;
|
||||
if (config.PesoTransbordoRankingMinutos is { } pesoTransbordo && double.IsFinite(pesoTransbordo))
|
||||
_pesoTransbordoRankingMinutos = Math.Abs(pesoTransbordo - 8.0) < 0.0001 ? 4.0 : Math.Max(0, pesoTransbordo);
|
||||
_pesoTransbordoRankingMinutos = Math.Max(0, pesoTransbordo);
|
||||
if (config.FactorEquilibradoAndarBus is { } factorEquilibrado &&
|
||||
double.IsFinite(factorEquilibrado) &&
|
||||
factorEquilibrado > 0)
|
||||
@@ -442,48 +444,8 @@ public partial class PlanificadorRutas
|
||||
if (!confirmado)
|
||||
return;
|
||||
|
||||
_mostrarParadas = true;
|
||||
_usarHorariosTeoricos = true;
|
||||
_segundosPorParadaBusManual = 30;
|
||||
_segundosPorParadaBus = 30;
|
||||
|
||||
_activarRangoItinerarios = true;
|
||||
_itinerarioMin = 1;
|
||||
_itinerarioMax = 99;
|
||||
_filtroSentidoItinerario = FiltroSentidoItinerario.Ambos;
|
||||
|
||||
_activarRangoTransbordos = true;
|
||||
_minTransbordosPermitidos = 0;
|
||||
_maxTransbordosPermitidos = 3;
|
||||
|
||||
_factorPenalizacionDistanciaPie = FactorPenalizacionDistanciaPiePorDefecto;
|
||||
_parametroPenalizacionDistanciaPieMetros = ParametroPenalizacionDistanciaPieMetrosPorDefecto;
|
||||
_coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto;
|
||||
_pesoTransbordoRankingMinutos = 4.0;
|
||||
_factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
||||
_mostrarElevacionesRutaConfiguracion = true;
|
||||
_fuenteElevacionCalculo = FuenteElevacionLocal5;
|
||||
_umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
|
||||
_umbralPendienteSubidaMediaRuta = UmbralPendienteSubidaMediaRutaPorDefecto;
|
||||
_umbralPendienteSubidaFuerteRuta = UmbralPendienteSubidaFuerteRutaPorDefecto;
|
||||
_colorPendienteBajadaRuta = ColorPendienteBajadaRutaPorDefecto;
|
||||
_colorPendienteLlanoRuta = ColorPendienteLlanoRutaPorDefecto;
|
||||
_colorPendienteSubidaSuaveRuta = ColorPendienteSubidaSuaveRutaPorDefecto;
|
||||
_colorPendienteSubidaMediaRuta = ColorPendienteSubidaMediaRutaPorDefecto;
|
||||
_colorPendienteSubidaFuerteRuta = ColorPendienteSubidaFuerteRutaPorDefecto;
|
||||
_umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto;
|
||||
_umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto;
|
||||
_velocidadPieKmH = 3.6;
|
||||
_velocidadBusKmH = 16.0;
|
||||
_minutosTransbordo = 5;
|
||||
_margenTransbordoSegundos = 120;
|
||||
_elevMaxPuntosPorTramo = 999;
|
||||
_radioCandidatasMetros = 1200;
|
||||
_walkProvider = ProveedorCaminata.Valhalla;
|
||||
_nivelBrilloMapa = 0;
|
||||
_usarColorRealLineaEnItinerarios = false;
|
||||
_usarOsrmParaRanking = false;
|
||||
_modoConfiguracionHistorial = ModoConfiguracionHistorial.Actual;
|
||||
_configuracionFabrica = await ValoresFabrica.CargarAsync();
|
||||
AplicarValoresFabricaConservandoSeleccion();
|
||||
|
||||
if (_usarHoraSimulada)
|
||||
StopClock();
|
||||
@@ -504,48 +466,8 @@ public partial class PlanificadorRutas
|
||||
{
|
||||
_mostrarConfirmacionRestablecer = false;
|
||||
|
||||
_mostrarParadas = true;
|
||||
_usarHorariosTeoricos = true;
|
||||
_segundosPorParadaBusManual = 30;
|
||||
_segundosPorParadaBus = 30;
|
||||
|
||||
_activarRangoItinerarios = true;
|
||||
_itinerarioMin = 1;
|
||||
_itinerarioMax = 99;
|
||||
_filtroSentidoItinerario = FiltroSentidoItinerario.Ambos;
|
||||
|
||||
_activarRangoTransbordos = true;
|
||||
_minTransbordosPermitidos = 0;
|
||||
_maxTransbordosPermitidos = 3;
|
||||
|
||||
_factorPenalizacionDistanciaPie = FactorPenalizacionDistanciaPiePorDefecto;
|
||||
_parametroPenalizacionDistanciaPieMetros = ParametroPenalizacionDistanciaPieMetrosPorDefecto;
|
||||
_coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto;
|
||||
_pesoTransbordoRankingMinutos = 4.0;
|
||||
_factorEquilibradoAndarBus = FactorEquilibradoAndarBusPorDefecto;
|
||||
_mostrarElevacionesRutaConfiguracion = true;
|
||||
_fuenteElevacionCalculo = FuenteElevacionLocal5;
|
||||
_umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
|
||||
_umbralPendienteSubidaMediaRuta = UmbralPendienteSubidaMediaRutaPorDefecto;
|
||||
_umbralPendienteSubidaFuerteRuta = UmbralPendienteSubidaFuerteRutaPorDefecto;
|
||||
_colorPendienteBajadaRuta = ColorPendienteBajadaRutaPorDefecto;
|
||||
_colorPendienteLlanoRuta = ColorPendienteLlanoRutaPorDefecto;
|
||||
_colorPendienteSubidaSuaveRuta = ColorPendienteSubidaSuaveRutaPorDefecto;
|
||||
_colorPendienteSubidaMediaRuta = ColorPendienteSubidaMediaRutaPorDefecto;
|
||||
_colorPendienteSubidaFuerteRuta = ColorPendienteSubidaFuerteRutaPorDefecto;
|
||||
_umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto;
|
||||
_umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto;
|
||||
_velocidadPieKmH = 3.6;
|
||||
_velocidadBusKmH = 16.0;
|
||||
_minutosTransbordo = 5;
|
||||
_margenTransbordoSegundos = 120;
|
||||
_elevMaxPuntosPorTramo = 999;
|
||||
_radioCandidatasMetros = 1200;
|
||||
_walkProvider = ProveedorCaminata.Valhalla;
|
||||
_nivelBrilloMapa = 0;
|
||||
_usarColorRealLineaEnItinerarios = false;
|
||||
_usarOsrmParaRanking = false;
|
||||
_modoConfiguracionHistorial = ModoConfiguracionHistorial.Actual;
|
||||
_configuracionFabrica = await ValoresFabrica.CargarAsync();
|
||||
AplicarValoresFabricaConservandoSeleccion();
|
||||
|
||||
if (_usarHoraSimulada)
|
||||
StopClock();
|
||||
|
||||
@@ -1146,6 +1146,7 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
if (indice < 0 || indice >= _opcionesRuta.Count) return;
|
||||
|
||||
_indiceAlternativaSeleccionada = indice;
|
||||
_proveedorSoloAPieRecalculo = _opcionesRuta[indice].EsSoloAPie ? _opcionesRuta[indice].ProveedorRuta : null;
|
||||
await CerrarPopupsAsync();
|
||||
|
||||
ResetEstadoRuta(limpiarOpciones: false, limpiarMensaje: false);
|
||||
@@ -1755,6 +1756,7 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
[JSInvokable]
|
||||
public async Task OnMobileMapLongPress(double lat, double lon)
|
||||
{
|
||||
if (_modoDibujoExclusionCaminata) return;
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
if (_inspectorActivo)
|
||||
@@ -1874,15 +1876,27 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
/// <summary>
|
||||
/// Calcula ruta completa.
|
||||
/// </summary>
|
||||
private string? _proveedorSoloAPieRecalculo;
|
||||
|
||||
private string? ObtenerProveedorSoloAPieParaRecalculo(bool conservarSeleccion)
|
||||
{
|
||||
if (!conservarSeleccion)
|
||||
_proveedorSoloAPieRecalculo = null;
|
||||
else if (_indiceAlternativaSeleccionada >= 0 && _indiceAlternativaSeleccionada < _opcionesRuta.Count)
|
||||
{
|
||||
var seleccionada = _opcionesRuta[_indiceAlternativaSeleccionada];
|
||||
_proveedorSoloAPieRecalculo = seleccionada.EsSoloAPie ? seleccionada.ProveedorRuta : null;
|
||||
}
|
||||
// Durante un calculo las opciones se vacian; otro cambio de punto conserva la preferencia.
|
||||
return _proveedorSoloAPieRecalculo;
|
||||
}
|
||||
|
||||
private async Task CalcularRutaCompletaAsync(bool conservarSeleccionSoloAPie = false)
|
||||
{
|
||||
if (_mapa is null || _coordenadasOrigen is null || _coordenadasDestino is null)
|
||||
return;
|
||||
|
||||
var restaurarSeleccionSoloAPie = conservarSeleccionSoloAPie &&
|
||||
_indiceAlternativaSeleccionada >= 0 &&
|
||||
_indiceAlternativaSeleccionada < _opcionesRuta.Count &&
|
||||
_opcionesRuta[_indiceAlternativaSeleccionada].EsSoloAPie;
|
||||
var proveedorSoloAPiePreferido = ObtenerProveedorSoloAPieParaRecalculo(conservarSeleccionSoloAPie);
|
||||
|
||||
_mostrarElevacionesRuta = _mostrarElevacionesRutaConfiguracion;
|
||||
|
||||
@@ -2002,8 +2016,9 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
return;
|
||||
}
|
||||
|
||||
var idxPreferida = restaurarSeleccionSoloAPie
|
||||
? _opcionesRuta.FindIndex(opcion => opcion.EsSoloAPie)
|
||||
var idxPreferida = proveedorSoloAPiePreferido is not null
|
||||
? _opcionesRuta.FindIndex(opcion => opcion.EsSoloAPie &&
|
||||
string.Equals(opcion.ProveedorRuta, proveedorSoloAPiePreferido, StringComparison.OrdinalIgnoreCase))
|
||||
: BuscarIndicePreferidoPorCriterio(_criterioPrioritarioRuta);
|
||||
if (idxPreferida < 0 || idxPreferida >= _opcionesRuta.Count)
|
||||
{
|
||||
|
||||
@@ -1312,7 +1312,7 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
}
|
||||
await CerrarPopupsAsync();
|
||||
|
||||
await CalcularRutaCompletaAsync();
|
||||
await CalcularRutaCompletaAsync(conservarSeleccionSoloAPie: true);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
@@ -1321,6 +1321,9 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
/// </summary>
|
||||
private async Task ReiniciarSeleccion()
|
||||
{
|
||||
_proveedorSoloAPieRecalculo = null;
|
||||
await LimpiarZonasExclusionAsync();
|
||||
await CerrarHerramientaZonasAsync();
|
||||
await CerrarTodasLasPreviewsAsync();
|
||||
|
||||
await CerrarPopupsAsync();
|
||||
|
||||
@@ -4,112 +4,159 @@ namespace RutasDBUS.Components.Pages;
|
||||
|
||||
public partial class PlanificadorRutas
|
||||
{
|
||||
private bool _modoDibujoExclusionCaminata;
|
||||
private bool _poligonoExclusionCaminataCerrado;
|
||||
private readonly List<double[]> _verticesExclusionCaminata = new();
|
||||
private sealed class ZonaExclusionCaminata
|
||||
{
|
||||
public string Id { get; } = Guid.NewGuid().ToString("N");
|
||||
public List<double[]> Vertices { get; } = new();
|
||||
public bool Cerrada { get; set; }
|
||||
}
|
||||
|
||||
private bool PuedeProbarExclusionCaminata =>
|
||||
_walkProvider == ProveedorCaminata.Valhalla &&
|
||||
_hayRutaValida &&
|
||||
_opcionesRuta.Count > 0 &&
|
||||
_indiceAlternativaSeleccionada >= 0 &&
|
||||
_indiceAlternativaSeleccionada < _opcionesRuta.Count &&
|
||||
(_opcionesRuta[_indiceAlternativaSeleccionada].EsSoloAPie ||
|
||||
_tramosRuta.Any(tramo => tramo.Tipo.StartsWith("A pie", StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
private bool PoligonoExclusionCaminataValido =>
|
||||
_verticesExclusionCaminata.Count >= 3 && _poligonoExclusionCaminataCerrado;
|
||||
private readonly List<ZonaExclusionCaminata> _zonasExclusionCaminata = new();
|
||||
private string? _zonaExclusionSeleccionada;
|
||||
private bool _mostrarHerramientaZonas;
|
||||
private bool _recalculandoZonas;
|
||||
private string? _errorZonas;
|
||||
private ZonaExclusionCaminata? ZonaExclusionSeleccionada =>
|
||||
_zonasExclusionCaminata.Find(zona => zona.Id == _zonaExclusionSeleccionada);
|
||||
private bool _modoDibujoExclusionCaminata =>
|
||||
_mostrarHerramientaZonas && ZonaExclusionSeleccionada is { Cerrada: false };
|
||||
private int NumeroZonasExclusion => _zonasExclusionCaminata.Count(zona => zona.Cerrada);
|
||||
|
||||
private List<List<double[]>>? ObtenerPoligonosExclusionCaminata()
|
||||
{
|
||||
if (!PoligonoExclusionCaminataValido)
|
||||
return null;
|
||||
var poligonos = _zonasExclusionCaminata.Where(zona => zona.Cerrada)
|
||||
.Select(zona => zona.Vertices.Select(p => new[] { p[1], p[0] })
|
||||
.Append(new[] { zona.Vertices[0][1], zona.Vertices[0][0] }).ToList()).ToList();
|
||||
return poligonos.Count == 0 ? null : poligonos;
|
||||
}
|
||||
|
||||
return new List<List<double[]>>
|
||||
{
|
||||
_verticesExclusionCaminata
|
||||
.Select(punto => new[] { punto[1], punto[0] })
|
||||
.Append(new[] { _verticesExclusionCaminata[0][1], _verticesExclusionCaminata[0][0] })
|
||||
.ToList()
|
||||
};
|
||||
private async Task AbrirHerramientaZonasAsync()
|
||||
{
|
||||
_mostrarConfigAvanzada = false;
|
||||
_mostrarHerramientaZonas = true;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
}
|
||||
|
||||
private async Task CerrarHerramientaZonasAsync()
|
||||
{
|
||||
_zonasExclusionCaminata.RemoveAll(zona => !zona.Cerrada);
|
||||
_zonaExclusionSeleccionada = null;
|
||||
_mostrarHerramientaZonas = false;
|
||||
_errorZonas = null;
|
||||
await JS.InvokeVoidAsync("rt.clearExclusionPolygonEditor");
|
||||
}
|
||||
|
||||
private async Task IniciarDibujoExclusionCaminataAsync()
|
||||
{
|
||||
if (!PuedeProbarExclusionCaminata)
|
||||
return;
|
||||
|
||||
_modoDibujoExclusionCaminata = true;
|
||||
_poligonoExclusionCaminataCerrado = false;
|
||||
_verticesExclusionCaminata.Clear();
|
||||
await JS.InvokeVoidAsync("rt.beginExclusionPolygonEditor");
|
||||
StateHasChanged();
|
||||
if (_recalculandoZonas || _modoDibujoExclusionCaminata || _zonasExclusionCaminata.Count >= 10) return;
|
||||
var zona = new ZonaExclusionCaminata();
|
||||
_zonasExclusionCaminata.Add(zona);
|
||||
_zonaExclusionSeleccionada = zona.Id;
|
||||
_errorZonas = null;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
}
|
||||
|
||||
private async Task CancelarDibujoExclusionCaminataAsync()
|
||||
private async Task SeleccionarZonaExclusionAsync(string id)
|
||||
{
|
||||
_modoDibujoExclusionCaminata = false;
|
||||
_poligonoExclusionCaminataCerrado = false;
|
||||
_verticesExclusionCaminata.Clear();
|
||||
if (_recalculandoZonas) return;
|
||||
_zonasExclusionCaminata.RemoveAll(zona => !zona.Cerrada);
|
||||
_zonaExclusionSeleccionada = id;
|
||||
_errorZonas = null;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
}
|
||||
|
||||
private async Task EliminarZonaExclusionAsync(string id)
|
||||
{
|
||||
if (_recalculandoZonas) return;
|
||||
_zonasExclusionCaminata.RemoveAll(zona => zona.Id == id);
|
||||
if (_zonaExclusionSeleccionada == id) _zonaExclusionSeleccionada = null;
|
||||
_errorZonas = null;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
}
|
||||
|
||||
private async Task LimpiarZonasExclusionAsync()
|
||||
{
|
||||
_zonasExclusionCaminata.Clear();
|
||||
_zonaExclusionSeleccionada = null;
|
||||
_errorZonas = null;
|
||||
await JS.InvokeVoidAsync("rt.clearExclusionPolygonEditor");
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task DeshacerVerticeExclusionCaminataAsync()
|
||||
{
|
||||
if (_verticesExclusionCaminata.Count == 0 || _poligonoExclusionCaminataCerrado)
|
||||
return;
|
||||
|
||||
_verticesExclusionCaminata.RemoveAt(_verticesExclusionCaminata.Count - 1);
|
||||
if (ZonaExclusionSeleccionada is not { Cerrada: false } zona || zona.Vertices.Count == 0) return;
|
||||
zona.Vertices.RemoveAt(zona.Vertices.Count - 1);
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
}
|
||||
|
||||
private async Task CerrarPoligonoExclusionCaminataAsync()
|
||||
{
|
||||
if (_verticesExclusionCaminata.Count < 3)
|
||||
if (ZonaExclusionSeleccionada is not { Cerrada: false } zona || zona.Vertices.Count < 3) return;
|
||||
if (!await JS.InvokeAsync<bool>("rt.isExclusionPolygonValid", zona.Vertices))
|
||||
{
|
||||
_errorZonas = "Revisa la zona: los lados no pueden cruzarse ni quedar alineados.";
|
||||
return;
|
||||
|
||||
_poligonoExclusionCaminataCerrado = true;
|
||||
await JS.InvokeVoidAsync("rt.closeExclusionPolygonEditor");
|
||||
StateHasChanged();
|
||||
}
|
||||
zona.Cerrada = true;
|
||||
_errorZonas = null;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
}
|
||||
|
||||
private async Task CalcularRutaEvitandoPoligonoCaminataAsync()
|
||||
{
|
||||
if (!PoligonoExclusionCaminataValido)
|
||||
return;
|
||||
|
||||
_modoDibujoExclusionCaminata = false;
|
||||
await JS.InvokeVoidAsync("rt.finishExclusionPolygonEditor");
|
||||
_omitirRegistroHistorialEnSiguienteCalculo = true;
|
||||
await CalcularRutaCompletaAsync();
|
||||
if (_recalculandoZonas || _modoDibujoExclusionCaminata || _coordenadasOrigen is null || _coordenadasDestino is null) return;
|
||||
foreach (var zona in _zonasExclusionCaminata)
|
||||
{
|
||||
if (!await JS.InvokeAsync<bool>("rt.isExclusionPolygonValid", zona.Vertices))
|
||||
{
|
||||
_errorZonas = "Una zona tiene lados cruzados o no tiene superficie. Ajusta sus puntos.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
_recalculandoZonas = true;
|
||||
_errorZonas = null;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
try
|
||||
{
|
||||
_omitirRegistroHistorialEnSiguienteCalculo = true;
|
||||
await CalcularRutaCompletaAsync(conservarSeleccionSoloAPie: true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_recalculandoZonas = false;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public async Task OnExclusionPolygonMapClick(double latitud, double longitud)
|
||||
{
|
||||
if (!_modoDibujoExclusionCaminata || _poligonoExclusionCaminataCerrado)
|
||||
return;
|
||||
|
||||
_verticesExclusionCaminata.Add(new[] { latitud, longitud });
|
||||
if (!_modoDibujoExclusionCaminata || _recalculandoZonas || !CoordenadaExclusionValida(latitud, longitud)) return;
|
||||
var zona = ZonaExclusionSeleccionada!;
|
||||
if (zona.Vertices.Count >= 50) return;
|
||||
zona.Vertices.Add(new[] { latitud, longitud });
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public Task OnExclusionPolygonVertexDragged(int indice, double latitud, double longitud)
|
||||
public Task OnExclusionPolygonVertexDragged(string id, int indice, double latitud, double longitud)
|
||||
{
|
||||
if (indice >= 0 && indice < _verticesExclusionCaminata.Count)
|
||||
{
|
||||
_verticesExclusionCaminata[indice][0] = latitud;
|
||||
_verticesExclusionCaminata[indice][1] = longitud;
|
||||
}
|
||||
|
||||
var zona = _zonasExclusionCaminata.Find(zona => zona.Id == id);
|
||||
if (_mostrarHerramientaZonas && !_recalculandoZonas && zona is not null &&
|
||||
indice >= 0 && indice < zona.Vertices.Count && CoordenadaExclusionValida(latitud, longitud))
|
||||
zona.Vertices[indice] = new[] { latitud, longitud };
|
||||
return InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private static bool CoordenadaExclusionValida(double latitud, double longitud) =>
|
||||
double.IsFinite(latitud) && double.IsFinite(longitud) && Math.Abs(latitud) <= 90 && Math.Abs(longitud) <= 180;
|
||||
|
||||
private async Task ActualizarEditorExclusionCaminataAsync()
|
||||
{
|
||||
await JS.InvokeVoidAsync("rt.updateExclusionPolygonEditor", _verticesExclusionCaminata);
|
||||
StateHasChanged();
|
||||
if (!_mostrarHerramientaZonas) return;
|
||||
await JS.InvokeVoidAsync("rt.updateExclusionPolygonEditor",
|
||||
_zonasExclusionCaminata.Select(zona => new { id = zona.Id, points = zona.Vertices, closed = zona.Cerrada }),
|
||||
_zonaExclusionSeleccionada, _recalculandoZonas);
|
||||
}
|
||||
}
|
||||
|
||||
86
RutasDBUS/Components/Pages/PlanificadorRutas.Superficie.cs
Normal file
86
RutasDBUS/Components/Pages/PlanificadorRutas.Superficie.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.JSInterop;
|
||||
using RutasDBUS.Modelos.Planificacion;
|
||||
|
||||
namespace RutasDBUS.Components.Pages;
|
||||
|
||||
public partial class PlanificadorRutas
|
||||
{
|
||||
private int _versionConsultaSuperficieSegmento;
|
||||
|
||||
/// <summary>
|
||||
/// Consulta la superficie únicamente del segmento de elevación que se acaba de seleccionar.
|
||||
/// </summary>
|
||||
[JSInvokable]
|
||||
public async Task OnElevationSegmentSelected(
|
||||
string routeId,
|
||||
int segmentIndex,
|
||||
double[][] coordenadas)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(routeId)
|
||||
|| segmentIndex < 0
|
||||
|| coordenadas is null
|
||||
|| coordenadas.Length < 2)
|
||||
return;
|
||||
|
||||
var linea = new double[coordenadas.Length, 2];
|
||||
for (var i = 0; i < coordenadas.Length; i++)
|
||||
{
|
||||
if (coordenadas[i] is null || coordenadas[i].Length < 2)
|
||||
return;
|
||||
|
||||
linea[i, 0] = coordenadas[i][0];
|
||||
linea[i, 1] = coordenadas[i][1];
|
||||
if (!double.IsFinite(linea[i, 0]) || !double.IsFinite(linea[i, 1]))
|
||||
return;
|
||||
}
|
||||
|
||||
var version = ++_versionConsultaSuperficieSegmento;
|
||||
string superficie;
|
||||
|
||||
try
|
||||
{
|
||||
var informacion = await ClienteValhalla.ObtenerInformacionSueloAsync(linea);
|
||||
superficie = FormatearSuperficieSegmento(informacion);
|
||||
}
|
||||
catch
|
||||
{
|
||||
superficie = "No disponible";
|
||||
}
|
||||
|
||||
if (version != _versionConsultaSuperficieSegmento)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await JS.InvokeVoidAsync(
|
||||
"rt.setElevationSegmentSurface",
|
||||
routeId,
|
||||
segmentIndex,
|
||||
superficie);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// El mapa puede haber sido redibujado mientras respondía la consulta.
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatearSuperficieSegmento(InformacionSueloTramo? informacion)
|
||||
{
|
||||
if (informacion is null || !informacion.Disponible || informacion.Segmentos.Count == 0)
|
||||
return "No disponible";
|
||||
|
||||
return string.Join(
|
||||
"<br>",
|
||||
informacion.Segmentos.Select(segmento =>
|
||||
{
|
||||
var uso = string.IsNullOrWhiteSpace(segmento.Uso)
|
||||
? ""
|
||||
: $" ({segmento.Uso})";
|
||||
var distancia = segmento.DistanciaMetros < 1000
|
||||
? segmento.DistanciaMetros.ToString("0.##", CultureInfo.InvariantCulture) + " m"
|
||||
: (segmento.DistanciaMetros / 1000.0).ToString("0.###", CultureInfo.InvariantCulture) + " km";
|
||||
return $"{segmento.Superficie}{uso}, {distancia}";
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using RutasDBUS.Modelos.Historial;
|
||||
using RutasDBUS.Servicios.Configuracion;
|
||||
|
||||
namespace RutasDBUS.Components.Pages;
|
||||
|
||||
public partial class PlanificadorRutas
|
||||
{
|
||||
[Inject] private ServicioValoresFabrica ValoresFabrica { get; set; } = default!;
|
||||
private ConfiguracionHistorialRuta? _configuracionFabrica;
|
||||
|
||||
private ConfiguracionHistorialRuta CrearConfiguracionPredeterminadaHistorial()
|
||||
{
|
||||
var config = _configuracionFabrica?.Clonar() ?? CrearConfiguracionBaseHistorial();
|
||||
var ahora = DateTime.Now;
|
||||
config.FechaSimulada = ahora.ToString("yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
|
||||
config.HoraSimulada = ahora.ToString("HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
|
||||
return config;
|
||||
}
|
||||
|
||||
private void AplicarValoresFabricaConservandoSeleccion()
|
||||
{
|
||||
var actual = CrearConfiguracionLocal();
|
||||
var predeterminada = CrearConfiguracionPredeterminadaHistorial();
|
||||
predeterminada.ActivarRangoLineas = actual.ActivarRangoLineas;
|
||||
predeterminada.LineaMin = actual.LineaMin;
|
||||
predeterminada.LineaMax = actual.LineaMax;
|
||||
predeterminada.LineasPermitidas = actual.LineasPermitidas;
|
||||
predeterminada.UsarHoraSimulada = actual.UsarHoraSimulada;
|
||||
predeterminada.FechaSimulada = actual.FechaSimulada;
|
||||
predeterminada.HoraSimulada = actual.HoraSimulada;
|
||||
predeterminada.CriterioPrioritarioRuta = actual.CriterioPrioritarioRuta;
|
||||
predeterminada.PreferenciaEtiquetaRuta = actual.PreferenciaEtiquetaRuta;
|
||||
AplicarConfiguracionLocal(predeterminada);
|
||||
_modoConfiguracionHistorial = ModoConfiguracionHistorial.Actual;
|
||||
}
|
||||
}
|
||||
@@ -227,6 +227,16 @@
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
<button type="button"
|
||||
class="map-zones-toggle @(_mostrarHerramientaZonas ? "is-active" : "")"
|
||||
title="Zonas a evitar: @NumeroZonasExclusion"
|
||||
aria-label="Abrir zonas a evitar"
|
||||
aria-pressed="@_mostrarHerramientaZonas"
|
||||
@onclick="AbrirHerramientaZonasAsync">
|
||||
<span aria-hidden="true">⬡</span>
|
||||
<span>Zonas</span>
|
||||
</button>
|
||||
</div>
|
||||
@* STACK DE POPUPS (izquierda) *@
|
||||
@if (_paradaClicada is not null || _itinerarioClicado is not null || _popupInspectorVisible)
|
||||
@@ -847,53 +857,6 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (PuedeProbarExclusionCaminata)
|
||||
{
|
||||
<div class="route-exclusion-tool" role="region" aria-label="Evitar zona andando">
|
||||
<div class="route-exclusion-tool__header">
|
||||
<strong>Probar evitando una zona andando</strong>
|
||||
<span class="muted">v2</span>
|
||||
</div>
|
||||
|
||||
@if (!_modoDibujoExclusionCaminata && !PoligonoExclusionCaminataValido)
|
||||
{
|
||||
<button type="button" class="btn-ghost route-exclusion-tool__button"
|
||||
title="Dibujar una zona para evitarla en la siguiente búsqueda"
|
||||
@onclick="IniciarDibujoExclusionCaminataAsync">
|
||||
Dibujar zona a evitar
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="route-exclusion-tool__status">
|
||||
@if (_poligonoExclusionCaminataCerrado)
|
||||
{
|
||||
<span>Zona cerrada con @_verticesExclusionCaminata.Count vértices. Puedes arrastrarlos.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Pulsa en el mapa para añadir vértices: @_verticesExclusionCaminata.Count.</span>
|
||||
}
|
||||
</div>
|
||||
<div class="route-exclusion-tool__actions">
|
||||
@if (!_poligonoExclusionCaminataCerrado)
|
||||
{
|
||||
<button type="button" class="btn-ghost" @onclick="DeshacerVerticeExclusionCaminataAsync"
|
||||
disabled="@(_verticesExclusionCaminata.Count == 0)">Deshacer punto</button>
|
||||
<button type="button" class="btn-ghost route-exclusion-tool__close" @onclick="CerrarPoligonoExclusionCaminataAsync"
|
||||
disabled="@(_verticesExclusionCaminata.Count < 3)">Cerrar zona</button>
|
||||
}
|
||||
@if (PoligonoExclusionCaminataValido)
|
||||
{
|
||||
<button type="button" class="btn-primary route-exclusion-tool__calculate" @onclick="CalcularRutaEvitandoPoligonoCaminataAsync">
|
||||
Calcular alternativa
|
||||
</button>
|
||||
}
|
||||
<button type="button" class="btn-ghost" @onclick="CancelarDibujoExclusionCaminataAsync">Cancelar</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@* ========= RESUMEN BONITO ARRIBA (único) ========= *@
|
||||
<p class="route-main-text">
|
||||
@@ -1503,6 +1466,53 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (_mostrarHerramientaZonas)
|
||||
{
|
||||
<section class="zones-panel draggable-popup" data-drag-key="zonas-exclusion" aria-label="Zonas a evitar">
|
||||
<div class="config-panel-header draggable-popup-handle">
|
||||
<h5>Zonas a evitar <small>v2</small></h5>
|
||||
<button type="button" class="config-close-btn" title="Cerrar herramienta" aria-label="Cerrar herramienta" @onclick="CerrarHerramientaZonasAsync">×</button>
|
||||
</div>
|
||||
<div class="zones-panel__list">
|
||||
@for (var i = 0; i < _zonasExclusionCaminata.Count; i++)
|
||||
{
|
||||
var zona = _zonasExclusionCaminata[i];
|
||||
<div class="zones-panel__item" @key="zona.Id">
|
||||
<button type="button" class="btn-ghost @(zona.Id == _zonaExclusionSeleccionada ? "zones-panel__selected" : "")"
|
||||
disabled="@(_recalculandoZonas || !zona.Cerrada)" @onclick="() => SeleccionarZonaExclusionAsync(zona.Id)">
|
||||
Zona @(i + 1) · @zona.Vertices.Count puntos @(!zona.Cerrada ? "(abierta)" : "")
|
||||
</button>
|
||||
<button type="button" class="btn-ghost zones-panel__icon" title="Eliminar zona" aria-label="Eliminar zona"
|
||||
disabled="@_recalculandoZonas" @onclick="() => EliminarZonaExclusionAsync(zona.Id)">×</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="zones-panel__actions">
|
||||
@if (_modoDibujoExclusionCaminata)
|
||||
{
|
||||
<button type="button" class="btn-ghost" title="Deshacer último punto" aria-label="Deshacer último punto"
|
||||
disabled="@(ZonaExclusionSeleccionada!.Vertices.Count == 0)" @onclick="DeshacerVerticeExclusionCaminataAsync">↶</button>
|
||||
<button type="button" class="btn-ghost" disabled="@(ZonaExclusionSeleccionada!.Vertices.Count < 3)"
|
||||
@onclick="CerrarPoligonoExclusionCaminataAsync">Cerrar zona</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn-ghost" disabled="@(_recalculandoZonas || _zonasExclusionCaminata.Count >= 10)"
|
||||
@onclick="IniciarDibujoExclusionCaminataAsync">Añadir zona</button>
|
||||
}
|
||||
<button type="button" class="btn-ghost" disabled="@(_recalculandoZonas || _zonasExclusionCaminata.Count == 0)"
|
||||
@onclick="LimpiarZonasExclusionAsync">Borrar todas</button>
|
||||
<button type="button" class="btn-ghost zones-panel__primary"
|
||||
disabled="@(_recalculandoZonas || _modoDibujoExclusionCaminata || _coordenadasOrigen is null || _coordenadasDestino is null)"
|
||||
@onclick="CalcularRutaEvitandoPoligonoCaminataAsync">@(_recalculandoZonas ? "Calculando..." : "Recalcular")</button>
|
||||
</div>
|
||||
@if (!string.IsNullOrWhiteSpace(_errorZonas))
|
||||
{
|
||||
<p class="zones-panel__error" role="alert">@_errorZonas</p>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
|
||||
<!-- CONFIG -->
|
||||
@* <button class="config-toggle-btn" @onclick="ToggleConfigPanel">⚙</button>
|
||||
*@
|
||||
|
||||
Reference in New Issue
Block a user