cambaido ctrl por los shift
mover ventana de zonas de exclusion puntos en zonas de exclusion vertices mas pequeños en las zonas de exclusion check y persistencia de las zonas d eexclusion* distancia andando visual de un punto z al anterior primer lector del lidar para simualciones de terreno quitado el traductor de el selector de superficie de valhalla
This commit is contained in:
@@ -56,6 +56,7 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
|
||||
try { await JS.InvokeVoidAsync("rt.initMobileMapControls"); } catch (Exception ex) { Console.WriteLine("rt.initMobileMapControls: " + ex); }
|
||||
try { await JS.InvokeVoidAsync("rt.initDraggablePopups"); } catch (Exception ex) { Console.WriteLine("rt.initDraggablePopups: " + ex); }
|
||||
await CargarConfiguracionLocalAsync();
|
||||
await CargarZonasAsync();
|
||||
await CargarModoConfiguracionHistorialAsync();
|
||||
await AsegurarHistorialLocalCargadoAsync();
|
||||
await CargarHistorialCompartidoAsync();
|
||||
@@ -509,15 +510,17 @@ 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 puntos = ObtenerPuntosExclusionCaminata();
|
||||
var key = WalkKey("valhalla", lat1, lon1, lat2, lon2);
|
||||
if (poligonos is not null) key += "|" + JsonSerializer.Serialize(poligonos);
|
||||
if (puntos is not null) key += "|puntos|" + JsonSerializer.Serialize(puntos);
|
||||
if (_walkCache.TryGetValue(key, out var v))
|
||||
return v;
|
||||
|
||||
try
|
||||
{
|
||||
var (line, dist, dur) = await ClienteValhalla.ObtenerRutaAsync(lat1, lon1, lat2, lon2,
|
||||
costing: "pedestrian", excludePolygons: poligonos);
|
||||
costing: "pedestrian", excludePolygons: poligonos, excludeLocations: puntos);
|
||||
|
||||
// ✅ Solo cachear si es un resultado "bueno"
|
||||
if (line is not null && dist > 0)
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace RutasDBUS.Components.Pages;
|
||||
|
||||
public partial class PlanificadorRutas
|
||||
{
|
||||
private PuntoInspectorAnterior? _inspectorPuntoAnterior;
|
||||
private double[,]? _caminoInspectorAnterior;
|
||||
private bool _caminoInspectorVisible;
|
||||
|
||||
private async Task OcultarCaminoInspectorAsync()
|
||||
{
|
||||
_caminoInspectorAnterior = null;
|
||||
_caminoInspectorVisible = false;
|
||||
try { await JS.InvokeVoidAsync("rt.setInspectorWalk", Array.Empty<double[]>(), false); } catch { }
|
||||
}
|
||||
|
||||
private async Task AlternarCaminoInspectorAsync()
|
||||
{
|
||||
if (_caminoInspectorAnterior is null) return;
|
||||
_caminoInspectorVisible = !_caminoInspectorVisible;
|
||||
var puntos = Enumerable.Range(0, _caminoInspectorAnterior.GetLength(0))
|
||||
.Select(i => new[] { _caminoInspectorAnterior[i, 0], _caminoInspectorAnterior[i, 1] }).ToArray();
|
||||
await JS.InvokeVoidAsync("rt.setInspectorWalk", puntos, _caminoInspectorVisible);
|
||||
}
|
||||
|
||||
private sealed record PuntoInspectorAnterior(
|
||||
double Latitud,
|
||||
@@ -81,7 +100,7 @@ public partial class PlanificadorRutas
|
||||
try
|
||||
{
|
||||
var tareaDireccion = ResolverDireccionPuntoInspectorAnteriorAsync(punto);
|
||||
var tareaCaminata = WalkCached(
|
||||
var tareaCaminata = ClienteValhalla.ObtenerRutaAsync(
|
||||
punto.Latitud,
|
||||
punto.Longitud,
|
||||
_inspectorLat,
|
||||
@@ -96,11 +115,12 @@ public partial class PlanificadorRutas
|
||||
}
|
||||
|
||||
var caminata = await tareaCaminata;
|
||||
_caminoInspectorAnterior = caminata.Linea;
|
||||
_inspectorPuntoAnterior = puntoVigente with
|
||||
{
|
||||
Direccion = await tareaDireccion,
|
||||
DistanciaAndandoMetros = caminata.line is not null && caminata.dist > 0
|
||||
? caminata.dist
|
||||
DistanciaAndandoMetros = caminata.Linea is not null && double.IsFinite(caminata.DistanciaMetros) && caminata.DistanciaMetros >= 0
|
||||
? caminata.DistanciaMetros
|
||||
: null,
|
||||
CalculandoDatosAdicionales = false
|
||||
};
|
||||
|
||||
@@ -1400,7 +1400,7 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
private bool _elevPopupMinimizado = false;
|
||||
|
||||
|
||||
private const string _versionApp = "(v-20260908b)";
|
||||
private const string _versionApp = "(v-20260909a)";
|
||||
|
||||
|
||||
private double? _paradaElevIdee;
|
||||
|
||||
@@ -1534,6 +1534,8 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
: null;
|
||||
|
||||
var versionInspector = ++_versionInspector;
|
||||
await OcultarCaminoInspectorAsync();
|
||||
if (versionInspector != _versionInspector) return;
|
||||
_inspectorLat = lat;
|
||||
_inspectorLon = lon;
|
||||
_inspectorElevIdee = null;
|
||||
@@ -1649,6 +1651,7 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
private async Task CerrarInspectorAsync()
|
||||
{
|
||||
_versionInspector++;
|
||||
await OcultarCaminoInspectorAsync();
|
||||
_popupInspectorVisible = false;
|
||||
_inspectorPuntoAnterior = null;
|
||||
_inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
|
||||
@@ -1949,6 +1952,7 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
FactorEquilibradoAndarBus = _factorEquilibradoAndarBus,
|
||||
UsarValhallaParaCaminatas = _walkProvider == ProveedorCaminata.Valhalla,
|
||||
PoligonosExclusionCaminata = ObtenerPoligonosExclusionCaminata(),
|
||||
PuntosExclusionCaminata = ObtenerPuntosExclusionCaminata(),
|
||||
VelocidadBusKmH = _velocidadBusKmH,
|
||||
MinutosPorTransbordo = _minutosTransbordo,
|
||||
MargenTransbordoSegundos = Math.Clamp(_margenTransbordoSegundos, 0, 1800),
|
||||
|
||||
@@ -1322,7 +1322,7 @@ public partial class PlanificadorRutas : ComponentBase
|
||||
private async Task ReiniciarSeleccion()
|
||||
{
|
||||
_proveedorSoloAPieRecalculo = null;
|
||||
await LimpiarZonasExclusionAsync();
|
||||
await CerrarHerramientaZonasAsync();
|
||||
await CerrarHerramientaZonasAsync();
|
||||
await CerrarTodasLasPreviewsAsync();
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.JSInterop;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace RutasDBUS.Components.Pages;
|
||||
|
||||
@@ -6,9 +8,11 @@ public partial class PlanificadorRutas
|
||||
{
|
||||
private sealed class ZonaExclusionCaminata
|
||||
{
|
||||
public string Id { get; } = Guid.NewGuid().ToString("N");
|
||||
public List<double[]> Vertices { get; } = new();
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString("N");
|
||||
public List<double[]> Vertices { get; set; } = new();
|
||||
public bool Cerrada { get; set; }
|
||||
public bool Activa { get; set; } = true;
|
||||
public bool EsPunto { get; set; }
|
||||
}
|
||||
|
||||
private readonly List<ZonaExclusionCaminata> _zonasExclusionCaminata = new();
|
||||
@@ -16,6 +20,57 @@ public partial class PlanificadorRutas
|
||||
private bool _mostrarHerramientaZonas;
|
||||
private bool _recalculandoZonas;
|
||||
private string? _errorZonas;
|
||||
private const string ClaveZonasLocal = "rutasdbus.zonas.v1";
|
||||
private bool _zonasCargadas;
|
||||
private bool _nuevaExclusionEsPunto;
|
||||
|
||||
private async Task CargarZonasAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var resultado = await JS.InvokeAsync<JsonElement>("rt.loadLocalValueResult", ClaveZonasLocal);
|
||||
if (!resultado.GetProperty("ok").GetBoolean())
|
||||
throw new InvalidOperationException("Almacenamiento no disponible");
|
||||
var json = resultado.GetProperty("value").GetString();
|
||||
if (!string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
var zonas = JsonSerializer.Deserialize<List<ZonaExclusionCaminata>>(json) ?? new();
|
||||
foreach (var zona in zonas.Take(10))
|
||||
{
|
||||
if (zona is null || zona.Vertices is null || zona.Vertices.Count > 50 ||
|
||||
zona.Vertices.Any(p => p is null || p.Length != 2 || !CoordenadaExclusionValida(p[0], p[1])))
|
||||
continue;
|
||||
if (zona.EsPunto && zona.Vertices.Count > 1) continue;
|
||||
if (zona.Cerrada && (zona.EsPunto ? zona.Vertices.Count != 1 :
|
||||
!await JS.InvokeAsync<bool>("rt.isExclusionPolygonValid", zona.Vertices)))
|
||||
continue;
|
||||
zona.Id = Guid.NewGuid().ToString("N");
|
||||
_zonasExclusionCaminata.Add(zona);
|
||||
}
|
||||
}
|
||||
_zonasCargadas = true;
|
||||
}
|
||||
catch { _errorZonas = "No se pudieron cargar las zonas guardadas. Recarga antes de editarlas."; }
|
||||
}
|
||||
|
||||
private async Task GuardarZonasAsync()
|
||||
{
|
||||
if (!_zonasCargadas) return;
|
||||
try
|
||||
{
|
||||
var resultado = await JS.InvokeAsync<JsonElement>("rt.saveLocalValueResult", ClaveZonasLocal,
|
||||
JsonSerializer.Serialize(_zonasExclusionCaminata));
|
||||
if (!resultado.GetProperty("ok").GetBoolean())
|
||||
_errorZonas = "No se pudieron guardar las zonas en este navegador.";
|
||||
}
|
||||
catch { _errorZonas = "No se pudieron guardar las zonas en este navegador."; }
|
||||
}
|
||||
|
||||
private async Task CambiarZonaActivaAsync(ZonaExclusionCaminata zona, ChangeEventArgs args)
|
||||
{
|
||||
zona.Activa = args.Value is true;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
}
|
||||
private ZonaExclusionCaminata? ZonaExclusionSeleccionada =>
|
||||
_zonasExclusionCaminata.Find(zona => zona.Id == _zonaExclusionSeleccionada);
|
||||
private bool _modoDibujoExclusionCaminata =>
|
||||
@@ -23,12 +78,19 @@ public partial class PlanificadorRutas
|
||||
|
||||
private List<List<double[]>>? ObtenerPoligonosExclusionCaminata()
|
||||
{
|
||||
var poligonos = _zonasExclusionCaminata.Where(zona => zona.Cerrada)
|
||||
var poligonos = _zonasExclusionCaminata.Where(zona => zona.Cerrada && zona.Activa && !zona.EsPunto)
|
||||
.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;
|
||||
}
|
||||
|
||||
private List<double[]>? ObtenerPuntosExclusionCaminata()
|
||||
{
|
||||
var puntos = _zonasExclusionCaminata.Where(z => z.Cerrada && z.Activa && z.EsPunto)
|
||||
.Select(z => z.Vertices[0].ToArray()).ToList();
|
||||
return puntos.Count == 0 ? null : puntos;
|
||||
}
|
||||
|
||||
private async Task AbrirHerramientaZonasAsync()
|
||||
{
|
||||
_mostrarConfigAvanzada = false;
|
||||
@@ -49,7 +111,6 @@ public partial class PlanificadorRutas
|
||||
|
||||
private async Task CerrarHerramientaZonasAsync()
|
||||
{
|
||||
_zonasExclusionCaminata.RemoveAll(zona => !zona.Cerrada);
|
||||
_zonaExclusionSeleccionada = null;
|
||||
_mostrarHerramientaZonas = false;
|
||||
_errorZonas = null;
|
||||
@@ -58,8 +119,8 @@ public partial class PlanificadorRutas
|
||||
|
||||
private async Task IniciarDibujoExclusionCaminataAsync()
|
||||
{
|
||||
if (_recalculandoZonas || _modoDibujoExclusionCaminata || _zonasExclusionCaminata.Count >= 10) return;
|
||||
var zona = new ZonaExclusionCaminata();
|
||||
if (!_zonasCargadas || _recalculandoZonas || _modoDibujoExclusionCaminata || _zonasExclusionCaminata.Count >= 10) return;
|
||||
var zona = new ZonaExclusionCaminata { EsPunto = _nuevaExclusionEsPunto };
|
||||
_zonasExclusionCaminata.Add(zona);
|
||||
_zonaExclusionSeleccionada = zona.Id;
|
||||
_errorZonas = null;
|
||||
@@ -69,7 +130,6 @@ public partial class PlanificadorRutas
|
||||
private async Task SeleccionarZonaExclusionAsync(string id)
|
||||
{
|
||||
if (_recalculandoZonas) return;
|
||||
_zonasExclusionCaminata.RemoveAll(zona => !zona.Cerrada);
|
||||
_zonaExclusionSeleccionada = id;
|
||||
_errorZonas = null;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
@@ -89,6 +149,7 @@ public partial class PlanificadorRutas
|
||||
_zonasExclusionCaminata.Clear();
|
||||
_zonaExclusionSeleccionada = null;
|
||||
_errorZonas = null;
|
||||
await GuardarZonasAsync();
|
||||
await JS.InvokeVoidAsync("rt.clearExclusionPolygonEditor");
|
||||
}
|
||||
|
||||
@@ -115,7 +176,7 @@ public partial class PlanificadorRutas
|
||||
private async Task CalcularRutaEvitandoPoligonoCaminataAsync()
|
||||
{
|
||||
if (_recalculandoZonas || _modoDibujoExclusionCaminata || _coordenadasOrigen is null || _coordenadasDestino is null) return;
|
||||
foreach (var zona in _zonasExclusionCaminata)
|
||||
foreach (var zona in _zonasExclusionCaminata.Where(z => z.Cerrada && z.Activa && !z.EsPunto))
|
||||
{
|
||||
if (!await JS.InvokeAsync<bool>("rt.isExclusionPolygonValid", zona.Vertices))
|
||||
{
|
||||
@@ -145,18 +206,20 @@ public partial class PlanificadorRutas
|
||||
var zona = ZonaExclusionSeleccionada!;
|
||||
if (zona.Vertices.Count >= 50) return;
|
||||
zona.Vertices.Add(new[] { latitud, longitud });
|
||||
if (zona.EsPunto) zona.Cerrada = true;
|
||||
await ActualizarEditorExclusionCaminataAsync();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public Task OnExclusionPolygonVertexDragged(string id, int indice, double latitud, double longitud)
|
||||
public async Task OnExclusionPolygonVertexDragged(string id, int indice, double latitud, double 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);
|
||||
await GuardarZonasAsync();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private static bool CoordenadaExclusionValida(double latitud, double longitud) =>
|
||||
@@ -164,9 +227,10 @@ public partial class PlanificadorRutas
|
||||
|
||||
private async Task ActualizarEditorExclusionCaminataAsync()
|
||||
{
|
||||
await GuardarZonasAsync();
|
||||
if (!_mostrarHerramientaZonas) return;
|
||||
await JS.InvokeVoidAsync("rt.updateExclusionPolygonEditor",
|
||||
_zonasExclusionCaminata.Select(zona => new { id = zona.Id, points = zona.Vertices, closed = zona.Cerrada }),
|
||||
_zonasExclusionCaminata.Select(zona => new { id = zona.Id, points = zona.Vertices, closed = zona.Cerrada, active = zona.Activa, point = zona.EsPunto }),
|
||||
_zonaExclusionSeleccionada, _recalculandoZonas);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ public partial class PlanificadorRutas
|
||||
{
|
||||
[Inject] private ServicioValoresFabrica ValoresFabrica { get; set; } = default!;
|
||||
private ConfiguracionHistorialRuta? _configuracionFabrica;
|
||||
private ConfiguracionHistorialRuta? _fabricaPendiente;
|
||||
private readonly List<(string Campo, string Antes, string Despues)> _cambiosFabrica = new();
|
||||
private string? _mensajeFabrica;
|
||||
private bool _guardandoFabrica;
|
||||
|
||||
private ConfiguracionHistorialRuta CrearConfiguracionPredeterminadaHistorial()
|
||||
{
|
||||
@@ -39,14 +43,48 @@ public partial class PlanificadorRutas
|
||||
[JSInvokable]
|
||||
public async Task GuardarConfiguracionFabricaDesdeAtajoAsync()
|
||||
{
|
||||
if (!_configuracionLocalCargada)
|
||||
if (!_configuracionLocalCargada || _guardandoFabrica || _fabricaPendiente is not null)
|
||||
return;
|
||||
|
||||
var actual = CrearConfiguracionLocal();
|
||||
if (!await ValoresFabrica.GuardarAsync(actual))
|
||||
return;
|
||||
|
||||
_configuracionFabrica = actual.Clonar();
|
||||
var anterior = await ValoresFabrica.CargarAsync() ?? CrearConfiguracionBaseHistorial();
|
||||
_cambiosFabrica.Clear();
|
||||
foreach (var propiedad in typeof(ConfiguracionHistorialRuta).GetProperties())
|
||||
{
|
||||
if (propiedad.Name.StartsWith("Version", StringComparison.Ordinal)) continue;
|
||||
var antes = System.Text.Json.JsonSerializer.Serialize(propiedad.GetValue(anterior));
|
||||
var despues = System.Text.Json.JsonSerializer.Serialize(propiedad.GetValue(actual));
|
||||
if (antes == despues) continue;
|
||||
var nombre = System.Text.RegularExpressions.Regex.Replace(propiedad.Name, "([a-z])([A-Z])", "$1 $2");
|
||||
_cambiosFabrica.Add((nombre, antes.Trim('"'), despues.Trim('"')));
|
||||
}
|
||||
_mensajeFabrica = null;
|
||||
_fabricaPendiente = actual.Clonar();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void CancelarGuardadoFabrica()
|
||||
{
|
||||
if (_guardandoFabrica) return;
|
||||
_fabricaPendiente = null;
|
||||
_mensajeFabrica = null;
|
||||
}
|
||||
|
||||
private async Task ConfirmarGuardadoFabricaAsync()
|
||||
{
|
||||
if (_fabricaPendiente is null || _guardandoFabrica) return;
|
||||
_guardandoFabrica = true;
|
||||
try
|
||||
{
|
||||
if (await ValoresFabrica.GuardarAsync(_fabricaPendiente))
|
||||
{
|
||||
_configuracionFabrica = _fabricaPendiente.Clonar();
|
||||
_fabricaPendiente = null;
|
||||
_mensajeFabrica = "Valores predeterminados guardados.";
|
||||
}
|
||||
else
|
||||
_mensajeFabrica = "No se pudo guardar. Revisa los valores y los permisos de escritura del servidor.";
|
||||
}
|
||||
finally { _guardandoFabrica = false; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +447,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="inspector-surface-row">
|
||||
<b>Superficie:</b>
|
||||
<b>Superficie (V2):</b>
|
||||
@if (_inspectorSuperficieCargando)
|
||||
{
|
||||
<span class="muted">Consultando...</span>
|
||||
@@ -457,6 +457,10 @@
|
||||
<span>@(_inspectorSuperficieTexto ?? "No disponible")</span>
|
||||
}
|
||||
</div>
|
||||
<div class="inspector-surface-row">
|
||||
<b>Clasificación LiDAR:</b>
|
||||
<span class="muted">No disponible: falta la nube LiDAR clasificada local.</span>
|
||||
</div>
|
||||
|
||||
@if (_inspectorLugaresCercanos.Count > 0)
|
||||
{
|
||||
@@ -507,6 +511,15 @@
|
||||
else
|
||||
{
|
||||
<span>@(puntoAnterior.DistanciaAndandoMetros is { } distanciaAndando ? FormatearDistanciaPuntoInspector(distanciaAndando) : "-")</span>
|
||||
@if (_caminoInspectorAnterior is not null)
|
||||
{
|
||||
<button type="button" class="btn-ghost inspector-walk-eye"
|
||||
title="@(_caminoInspectorVisible ? "Ocultar camino" : "Ver camino")"
|
||||
aria-label="@(_caminoInspectorVisible ? "Ocultar camino" : "Ver camino")"
|
||||
aria-pressed="@_caminoInspectorVisible" @onclick="AlternarCaminoInspectorAsync">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7S2 12 2 12Z" /><circle cx="12" cy="12" r="3" /></svg>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
@@ -1480,9 +1493,12 @@
|
||||
{
|
||||
var zona = _zonasExclusionCaminata[i];
|
||||
<div class="zones-panel__item" @key="zona.Id">
|
||||
<input type="checkbox" checked="@zona.Activa" title="Activar zona"
|
||||
aria-label="Activar zona" disabled="@_recalculandoZonas"
|
||||
@onchange="e => CambiarZonaActivaAsync(zona, e)" />
|
||||
<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)" : "")
|
||||
disabled="@_recalculandoZonas" @onclick="() => SeleccionarZonaExclusionAsync(zona.Id)">
|
||||
@(zona.EsPunto ? "Punto" : "Zona") @(i + 1) @(!zona.EsPunto ? $"· {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>
|
||||
@@ -1494,13 +1510,17 @@
|
||||
{
|
||||
<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)"
|
||||
<button type="button" class="btn-ghost" disabled="@(ZonaExclusionSeleccionada!.Vertices.Count < 3 || ZonaExclusionSeleccionada.EsPunto)"
|
||||
@onclick="CerrarPoligonoExclusionCaminataAsync">Cerrar zona</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn-ghost" disabled="@(_recalculandoZonas || _zonasExclusionCaminata.Count >= 10)"
|
||||
@onclick="IniciarDibujoExclusionCaminataAsync">Añadir zona</button>
|
||||
@onclick="IniciarDibujoExclusionCaminataAsync">@(_nuevaExclusionEsPunto ? "Añadir punto" : "Añadir zona")</button>
|
||||
<select class="zones-panel__type" aria-label="Tipo de exclusión" @bind="_nuevaExclusionEsPunto" disabled="@_recalculandoZonas">
|
||||
<option value="false">Polígono</option>
|
||||
<option value="true">Punto</option>
|
||||
</select>
|
||||
}
|
||||
<button type="button" class="btn-ghost" disabled="@(_recalculandoZonas || _zonasExclusionCaminata.Count == 0)"
|
||||
@onclick="LimpiarZonasExclusionAsync">Borrar todas</button>
|
||||
@@ -2244,3 +2264,37 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (_fabricaPendiente is not null || _mensajeFabrica is not null)
|
||||
{
|
||||
<div class="custom-modal-backdrop" @onclick="CancelarGuardadoFabrica">
|
||||
<div class="custom-modal-card fabrica-confirmacion" @onclick:stopPropagation="true" role="dialog" aria-modal="true" aria-label="Guardar valores predeterminados">
|
||||
<div class="custom-modal-title">Valores predeterminados</div>
|
||||
@if (_fabricaPendiente is not null)
|
||||
{
|
||||
<p>¿Seguro que quieres guardar esta configuración y sustituir los valores predeterminados para todos los usuarios?</p>
|
||||
<div class="fabrica-cambios">
|
||||
@foreach (var cambio in _cambiosFabrica)
|
||||
{
|
||||
<div><b>@cambio.Campo:</b> @cambio.Antes → @cambio.Despues</div>
|
||||
}
|
||||
@if (_cambiosFabrica.Count == 0)
|
||||
{
|
||||
<div>Sin cambios respecto a los valores guardados.</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (_mensajeFabrica is not null)
|
||||
{
|
||||
<p role="status">@_mensajeFabrica</p>
|
||||
}
|
||||
<div class="custom-modal-actions">
|
||||
<button class="btn-ghost" disabled="@_guardandoFabrica" @onclick="CancelarGuardadoFabrica">@(_fabricaPendiente is null ? "Cerrar" : "Cancelar")</button>
|
||||
@if (_fabricaPendiente is not null)
|
||||
{
|
||||
<button class="btn-ghost" disabled="@_guardandoFabrica" @onclick="ConfirmarGuardadoFabricaAsync">@(_guardandoFabrica ? "Guardando..." : "Guardar y sustituir")</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public sealed class ParametrosPlanificadorRuta
|
||||
|
||||
// Zonas temporales que Valhalla debe excluir al calcular caminatas.
|
||||
public List<List<double[]>>? PoligonosExclusionCaminata { get; set; }
|
||||
public List<double[]>? PuntosExclusionCaminata { get; set; }
|
||||
|
||||
// selección candidatas
|
||||
public int RadioParadasCandidatasMetros { get; set; } = 1200;
|
||||
|
||||
@@ -34,7 +34,8 @@ public class ClienteValhalla : IClienteValhalla
|
||||
double latitudHasta,
|
||||
double longitudHasta,
|
||||
string costing = "pedestrian",
|
||||
List<List<double[]>>? excludePolygons = null)
|
||||
List<List<double[]>>? excludePolygons = null,
|
||||
List<double[]>? excludeLocations = null)
|
||||
{
|
||||
var cuerpo = new SolicitudValhalla
|
||||
{
|
||||
@@ -46,6 +47,8 @@ public class ClienteValhalla : IClienteValhalla
|
||||
PerfilCoste = string.IsNullOrWhiteSpace(costing) ? "pedestrian" : costing,
|
||||
FormatoGeometria = "polyline6",
|
||||
PoligonosExclusion = excludePolygons,
|
||||
PuntosExclusion = excludeLocations?.Select(p => new LocalizacionValhalla
|
||||
{ Latitud = p[0], Longitud = p[1] }).ToList(),
|
||||
OpcionesIndicaciones = new OpcionesIndicacionesValhalla
|
||||
{
|
||||
Idioma = "es-ES",
|
||||
@@ -208,8 +211,8 @@ public class ClienteValhalla : IClienteValhalla
|
||||
if (distanciaMetros <= 0)
|
||||
continue;
|
||||
|
||||
var superficie = EtiquetaSuperficie(arista.Superficie, arista.SinPavimentar);
|
||||
var uso = EtiquetaUso(arista.Uso);
|
||||
var superficie = arista.Superficie ?? "No disponible";
|
||||
var uso = arista.Uso;
|
||||
|
||||
if (segmentos.Count > 0 && EsMismaSuperficie(segmentos[^1], superficie, uso, arista.SinPavimentar))
|
||||
{
|
||||
@@ -324,6 +327,10 @@ public class ClienteValhalla : IClienteValhalla
|
||||
[JsonPropertyName("exclude_polygons")]
|
||||
public List<List<double[]>>? PoligonosExclusion { get; set; }
|
||||
|
||||
[JsonPropertyName("exclude_locations")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public List<LocalizacionValhalla>? PuntosExclusion { get; set; }
|
||||
|
||||
[JsonPropertyName("directions_options")]
|
||||
public OpcionesIndicacionesValhalla OpcionesIndicaciones { get; set; } = new();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ public interface IClienteValhalla
|
||||
double latitudHasta,
|
||||
double longitudHasta,
|
||||
string costing = "pedestrian",
|
||||
List<List<double[]>>? excludePolygons = null); // "pedestrian", "bicycle", etc.
|
||||
List<List<double[]>>? excludePolygons = null,
|
||||
List<double[]>? excludeLocations = null); // Coordenadas [lat, lon].
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene la superficie y el tipo de uso de los segmentos de una geometría peatonal.
|
||||
|
||||
@@ -2246,7 +2246,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
||||
origenLatLon[1],
|
||||
destinoLatLon[0],
|
||||
destinoLatLon[1],
|
||||
parametros.PoligonosExclusionCaminata);
|
||||
parametros.PoligonosExclusionCaminata, parametros.PuntosExclusionCaminata);
|
||||
if (valhalla.linea is not null && valhalla.distancia > 0)
|
||||
{
|
||||
var duracion = UtilidadesTransitoDbus.EstimarDuracionPieSegundos(valhalla.distancia, parametros);
|
||||
@@ -3835,7 +3835,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
||||
longitudOrigen,
|
||||
latitudDestino,
|
||||
longitudDestino,
|
||||
parametros.PoligonosExclusionCaminata)
|
||||
parametros.PoligonosExclusionCaminata, parametros.PuntosExclusionCaminata)
|
||||
: WalkOsrmCached(latitudOrigen, longitudOrigen, latitudDestino, longitudDestino);
|
||||
}
|
||||
|
||||
@@ -3880,20 +3880,23 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
||||
double longitudOrigen,
|
||||
double latitudDestino,
|
||||
double longitudDestino,
|
||||
List<List<double[]>>? excludePolygons = null)
|
||||
List<List<double[]>>? excludePolygons = null,
|
||||
List<double[]>? excludeLocations = null)
|
||||
{
|
||||
if (excludePolygons is { Count: > 0 })
|
||||
if (excludePolygons is { Count: > 0 } || excludeLocations is { Count: > 0 })
|
||||
{
|
||||
// Solo reutilizar una geometria conocida y alejada de todas las zonas.
|
||||
var claveNormal = BuildWalkKey("valhalla", latitudOrigen, longitudOrigen, latitudDestino, longitudDestino);
|
||||
if (_cacheCaminatas.TryGetValue(claveNormal, out var normal) && normal.linea is not null &&
|
||||
// Un punto se asocia a una arista, que puede extenderse mas alla del punto.
|
||||
if (excludeLocations is not { Count: > 0 } && excludePolygons is not null &&
|
||||
_cacheCaminatas.TryGetValue(claveNormal, out var normal) && normal.linea is not null &&
|
||||
!ExclusionPuedeAfectarTrayecto(normal.linea, excludePolygons))
|
||||
{
|
||||
return normal;
|
||||
}
|
||||
|
||||
var claveExclusion = BuildWalkKey("valhalla-exclusion", latitudOrigen, longitudOrigen, latitudDestino, longitudDestino)
|
||||
+ "|" + JsonSerializer.Serialize(excludePolygons);
|
||||
+ "|" + JsonSerializer.Serialize(excludePolygons) + "|" + JsonSerializer.Serialize(excludeLocations);
|
||||
if (_cacheCaminatasConExclusion.TryGetValue(claveExclusion, out var cacheadoConExclusion))
|
||||
return cacheadoConExclusion;
|
||||
|
||||
@@ -3905,7 +3908,7 @@ public sealed class PlanificadorRutasServicio : IPlanificadorRutas
|
||||
latitudDestino,
|
||||
longitudDestino,
|
||||
costing: "pedestrian",
|
||||
excludePolygons: excludePolygons);
|
||||
excludePolygons: excludePolygons, excludeLocations: excludeLocations);
|
||||
|
||||
var valor = (linea, distancia, duracion);
|
||||
if (linea is not null && distancia > 0)
|
||||
|
||||
@@ -971,7 +971,8 @@ html, body {
|
||||
.popup-elev-topright.draggable-popup--free,
|
||||
.route-panel-overlay.draggable-popup--free,
|
||||
.history-panel.draggable-popup--free,
|
||||
.config-panel.draggable-popup--free {
|
||||
.config-panel.draggable-popup--free,
|
||||
.zones-panel.draggable-popup--free {
|
||||
position: fixed !important;
|
||||
left: var(--drag-left) !important;
|
||||
top: var(--drag-top) !important;
|
||||
@@ -1137,6 +1138,7 @@ html, body {
|
||||
top: 90px;
|
||||
left: 16px;
|
||||
width: min(340px, calc(100vw - 32px));
|
||||
max-width: calc(100vw - 32px);
|
||||
max-height: calc(100dvh - 110px);
|
||||
overflow: auto;
|
||||
z-index: 1600;
|
||||
@@ -1151,12 +1153,21 @@ html, body {
|
||||
}
|
||||
|
||||
.zones-panel .config-panel-header h5 { font-size: .94rem; }
|
||||
@media (max-width: 700px) {
|
||||
.zones-panel.draggable-popup--free {
|
||||
left: 16px !important;
|
||||
top: 72px !important;
|
||||
max-height: calc(100dvh - 150px) !important;
|
||||
}
|
||||
}
|
||||
.zones-panel h5 small { font-size: .74rem; color: #94a3b8; }
|
||||
.zones-panel__list { display: grid; gap: .35rem; margin-block: .5rem; }
|
||||
.zones-panel__item { display: flex; gap: .35rem; min-width: 0; }
|
||||
.zones-panel__item > :first-child { flex: 1; text-align: left; }
|
||||
.zones-panel__item > button:first-of-type { flex: 1; text-align: left; }
|
||||
.zones-panel__item > input { flex: 0 0 auto; align-self: center; }
|
||||
.zones-panel__actions { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin-top: .5rem; }
|
||||
.zones-panel__actions .btn-ghost { white-space: nowrap; }
|
||||
.zones-panel__type { background: #161d2a; color: #e5e7eb; border: 1px solid #64748b; border-radius: 6px; min-height: 30px; max-width: 110px; font: inherit; }
|
||||
.zones-panel__icon { padding-inline: .6rem; }
|
||||
.btn-ghost.zones-panel__primary { background: #1d4ed8; color: #fff; }
|
||||
.btn-ghost.zones-panel__primary:disabled { background: #111827; color: #94a3b8; }
|
||||
@@ -1198,8 +1209,9 @@ html, body {
|
||||
}
|
||||
|
||||
.rt-exclusion-vertex {
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
width: 10px !important;
|
||||
height: 10px !important;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 50%;
|
||||
background: #a21caf;
|
||||
@@ -1367,6 +1379,13 @@ html, body {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.btn-ghost.inspector-walk-eye { display: inline-flex; padding: 2px 5px; min-height: 0; vertical-align: middle; margin-left: 5px; }
|
||||
.inspector-walk-eye[aria-pressed="true"] { color: #22d3ee; border-color: #22d3ee; }
|
||||
|
||||
.fabrica-confirmacion { width: min(560px, calc(100vw - 24px)); max-height: calc(100dvh - 32px); overflow-y: auto; }
|
||||
.fabrica-cambios { max-height: 45dvh; overflow-y: auto; font-size: .8rem; overflow-wrap: anywhere; }
|
||||
.fabrica-cambios > div { padding: 5px 0; border-bottom: 1px solid #374151; }
|
||||
|
||||
/* LISTA DE TRAMOS TIPO “DIRECCIONES” CON NÚMEROS */
|
||||
|
||||
.lista-tramos-ruta {
|
||||
|
||||
@@ -36,7 +36,7 @@ window.initZonasShortcut = function (dotNetHelper) {
|
||||
window.rt._zonasShortcutInicializado = true;
|
||||
|
||||
document.addEventListener("keydown", function (e) {
|
||||
if (e.repeat || e.code !== "KeyZ" || !e.ctrlKey || !e.altKey || e.shiftKey) return;
|
||||
if (e.repeat || e.code !== "KeyZ" || !e.ctrlKey || !e.shiftKey || e.altKey || e.metaKey) return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
@@ -51,10 +51,11 @@ window.initFabricaShortcut = function (dotNetHelper) {
|
||||
window.rt._fabricaShortcutInicializado = true;
|
||||
|
||||
document.addEventListener("keydown", function (e) {
|
||||
if (e.repeat || e.code !== "KeyF" || !e.ctrlKey || !e.altKey || e.shiftKey || e.metaKey) return;
|
||||
if (e.repeat || e.code !== "KeyF" || !e.ctrlKey || !e.shiftKey || e.altKey || e.metaKey) return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
document.activeElement?.blur();
|
||||
dotNetHelper.invokeMethodAsync("GuardarConfiguracionFabricaDesdeAtajoAsync").catch(function () { });
|
||||
}, true);
|
||||
};
|
||||
@@ -466,7 +467,8 @@ window.rt.drawRoute = function (id, coords, options) {
|
||||
"Altura: " + elevationText
|
||||
];
|
||||
if (segment && segment.surfaceText) {
|
||||
tooltipLines.push("Superficie: " + String(segment.surfaceText));
|
||||
tooltipLines.push("Superficie (V2): " + String(segment.surfaceText));
|
||||
tooltipLines.push("LiDAR: No disponible (falta la nube clasificada local)");
|
||||
}
|
||||
return tooltipLines.join("<br>");
|
||||
};
|
||||
@@ -766,21 +768,25 @@ window.rt.updateExclusionPolygonEditor = function (zones, selectedId, busy) {
|
||||
const selected = zone.id === selectedId;
|
||||
const points = zone.points.map(p => [p[0], p[1]]);
|
||||
const options = {
|
||||
color: selected ? "#e879f9" : "#f97316", weight: selected ? 3 : 2,
|
||||
fillOpacity: zone.closed ? 0.18 : 0.08, interactive: false
|
||||
color: zone.active === false ? "#9ca3af" : selected ? "#e879f9" : "#f97316", weight: selected ? 3 : 2,
|
||||
dashArray: zone.active === false ? "4 5" : null,
|
||||
fillOpacity: zone.active === false ? 0.03 : zone.closed ? 0.18 : 0.08, interactive: false
|
||||
};
|
||||
const polygon = (zone.closed ? L.polygon(points, options) : L.polyline(points, options)).addTo(layer);
|
||||
const polygon = zone.point
|
||||
? (points.length ? L.circleMarker(points[0], { ...options, radius: 6, fillOpacity: .5 }).addTo(layer) : null)
|
||||
: (zone.closed ? L.polygon(points, options) : L.polyline(points, options)).addTo(layer);
|
||||
if (!selected || busy) continue;
|
||||
points.forEach((point, index) => {
|
||||
const marker = L.marker(point, {
|
||||
draggable: true, autoPan: true, bubblingMouseEvents: false, zIndexOffset: 3000,
|
||||
icon: L.divIcon({ className: "rt-exclusion-vertex", html: "",
|
||||
iconSize: [18, 18], iconAnchor: [9, 9] })
|
||||
iconSize: [10, 10], iconAnchor: [5, 5] })
|
||||
}).addTo(layer);
|
||||
const update = () => {
|
||||
const pos = marker.getLatLng();
|
||||
points[index] = [pos.lat, pos.lng];
|
||||
polygon.setLatLngs(points);
|
||||
if (zone.point) polygon?.setLatLng(points[0]);
|
||||
else polygon.setLatLngs(points);
|
||||
return pos;
|
||||
};
|
||||
marker.on("drag", update);
|
||||
@@ -1761,6 +1767,17 @@ window.rt.clearInspectorMarkers = function () {
|
||||
}
|
||||
};
|
||||
|
||||
window.rt.setInspectorWalk = function (points, visible) {
|
||||
window.rt._inspectorWalkLayer?.remove();
|
||||
window.rt._inspectorWalkLayer = null;
|
||||
const map = window.__rtLeafletMap;
|
||||
if (!visible || !map || !window.L || points.length < 2) return;
|
||||
window.rt._inspectorWalkLayer = window.L.polyline(points, {
|
||||
color: "#0891b2", weight: 5, dashArray: "2 7", lineCap: "round", interactive: false
|
||||
}).addTo(map);
|
||||
if (!points.some(p => map.getBounds().contains(p))) map.panTo(points[0]);
|
||||
};
|
||||
|
||||
|
||||
window.rt = window.rt || {};
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ var fake = new FakeValhalla();
|
||||
var planner = new PlanificadorRutasServicio(null!, null!, fake, null!, null!, null!, null!);
|
||||
var walk = typeof(PlanificadorRutasServicio).GetMethod("WalkValhallaCached", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||||
async Task<(double[,]? linea, double distancia, double duracion)> Walk(List<List<double[]>>? zones = null) =>
|
||||
await (Task<(double[,]?, double, double)>)walk.Invoke(planner, new object?[] { 43.31, -1.99, 43.32, -1.98, zones })!;
|
||||
await (Task<(double[,]?, double, double)>)walk.Invoke(planner, new object?[] { 43.31, -1.99, 43.32, -1.98, zones, null })!;
|
||||
List<double[]> Ring(double lon, double lat, double size) =>
|
||||
new() { new[] { lon, lat }, new[] { lon + size, lat }, new[] { lon + size, lat + size },
|
||||
new[] { lon, lat + size }, new[] { lon, lat } };
|
||||
@@ -96,12 +96,17 @@ Console.WriteLine("Pruebas completadas.");
|
||||
|
||||
sealed class FakeValhalla : IClienteValhalla
|
||||
{
|
||||
public Task<InformacionSueloTramo?> ObtenerInformacionSueloAsync(
|
||||
double[,] linea, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult<InformacionSueloTramo?>(null);
|
||||
|
||||
public int Calls { get; private set; }
|
||||
public int Zones { get; private set; }
|
||||
public bool Fail { get; set; }
|
||||
public Task<(double[,]? Linea, double DistanciaMetros, double DuracionSegundos)> ObtenerRutaAsync(
|
||||
double latitudDesde, double longitudDesde, double latitudHasta, double longitudHasta,
|
||||
string costing = "pedestrian", List<List<double[]>>? excludePolygons = null)
|
||||
string costing = "pedestrian", List<List<double[]>>? excludePolygons = null,
|
||||
List<double[]>? excludeLocations = null)
|
||||
{
|
||||
Calls++;
|
||||
Zones = excludePolygons?.Count ?? 0;
|
||||
|
||||
@@ -14,7 +14,9 @@ const path = require("node:path");
|
||||
await page.goto("http://127.0.0.1:5199", { waitUntil: "domcontentloaded" });
|
||||
await page.waitForFunction(() => !!window.rt?._dotNetHelper && !!window.__rtLeafletMap);
|
||||
const initialErrors = errors.slice();
|
||||
await page.getByRole("button", { name: "Abrir zonas a evitar", exact: true }).click();
|
||||
await page.keyboard.press("Control+Shift+KeyZ");
|
||||
await page.locator(".zones-panel").waitFor();
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByRole("button", { name: "Añadir zona", exact: true }).click();
|
||||
for (const [x, y] of [[600, 420], [850, 430], [760, 640]]) {
|
||||
await page.mouse.click(x, y);
|
||||
@@ -24,6 +26,7 @@ const path = require("node:path");
|
||||
await page.getByRole("button", { name: "Añadir zona", exact: true }).waitFor();
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 1);
|
||||
assert.equal(await page.locator(".rt-exclusion-vertex").count(), 3);
|
||||
assert.equal(Math.round((await page.locator(".rt-exclusion-vertex").first().boundingBox()).width), 10);
|
||||
const polygon = page.locator('path[stroke="#e879f9"]');
|
||||
const previousShape = await polygon.getAttribute("d");
|
||||
const marker = await page.locator(".rt-exclusion-vertex").first().boundingBox();
|
||||
@@ -49,9 +52,32 @@ const path = require("node:path");
|
||||
await page.locator(".zones-panel").waitFor({ state: "detached" });
|
||||
assert.equal(await page.locator(".rt-exclusion-vertex").count(), 0);
|
||||
assert.equal(await page.locator('path[stroke="#e879f9"], path[stroke="#f97316"]').count(), 0);
|
||||
await page.getByRole("button", { name: "Abrir zonas a evitar", exact: true }).click();
|
||||
await page.keyboard.press("Control+Shift+KeyZ");
|
||||
await page.locator(".zones-panel").waitFor();
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 2, "Reabrir conserva zonas editables");
|
||||
await page.getByRole("checkbox", { name: "Activar zona", exact: true }).first().uncheck();
|
||||
await page.waitForFunction(() => JSON.parse(localStorage.getItem("rutasdbus.zonas.v1"))[0].Activa === false);
|
||||
await page.reload();
|
||||
await page.waitForFunction(() => !!window.rt?._dotNetHelper && !!window.__rtLeafletMap);
|
||||
await page.waitForTimeout(1000);
|
||||
await page.keyboard.press("Control+Shift+KeyZ");
|
||||
await page.locator(".zones-panel__item").first().waitFor();
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 2, "F5 conserva zonas");
|
||||
assert.equal(await page.getByRole("checkbox", { name: "Activar zona", exact: true }).first().isChecked(), false);
|
||||
const panelBefore = await page.locator(".zones-panel").boundingBox();
|
||||
const header = await page.locator(".zones-panel .draggable-popup-handle").boundingBox();
|
||||
await page.mouse.move(header.x + 40, header.y + 15);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(header.x + 190, header.y + 65, { steps: 10 });
|
||||
await page.mouse.up();
|
||||
const panelAfter = await page.locator(".zones-panel").boundingBox();
|
||||
assert.ok(Math.abs(panelAfter.x - panelBefore.x) > 50, "El modal se arrastra");
|
||||
await page.screenshot({ path: path.join(artifacts, "persistencia-arrastre.png") });
|
||||
await page.keyboard.press("Control+Shift+KeyF");
|
||||
await page.getByRole("dialog", { name: "Guardar valores predeterminados" }).waitFor();
|
||||
assert.ok(await page.getByRole("button", { name: "Guardar y sustituir", exact: true }).isVisible());
|
||||
await page.screenshot({ path: path.join(artifacts, "confirmacion-fabrica.png") });
|
||||
await page.getByRole("button", { name: "Cancelar", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Eliminar zona", exact: true }).first().click();
|
||||
await page.waitForFunction(() => document.querySelectorAll(".zones-panel__item").length === 1);
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 1);
|
||||
|
||||
Reference in New Issue
Block a user