añadidos atajos ctrl + alt + f para guardar datos de fabrica

añadidos atajos ctrl + alt + z para abrir zonas de exclusion
añadido superficio de terreno en el punto z
This commit is contained in:
2026-09-08 14:29:35 +02:00
parent 235297fd3a
commit acbdd1501d
10 changed files with 211 additions and 12 deletions

View File

@@ -49,6 +49,8 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
try { await JS.InvokeVoidAsync("initBrilloMapa", _dotRef); } catch (Exception ex) { Console.WriteLine("initBrilloMapa: " + ex); } try { await JS.InvokeVoidAsync("initBrilloMapa", _dotRef); } catch (Exception ex) { Console.WriteLine("initBrilloMapa: " + ex); }
try { await JS.InvokeVoidAsync("initAltInspector", _dotRef); } catch (Exception ex) { Console.WriteLine("initAltInspector: " + ex); } try { await JS.InvokeVoidAsync("initAltInspector", _dotRef); } catch (Exception ex) { Console.WriteLine("initAltInspector: " + ex); }
try { await JS.InvokeVoidAsync("initZonasShortcut", _dotRef); } catch (Exception ex) { Console.WriteLine("initZonasShortcut: " + ex); }
try { await JS.InvokeVoidAsync("initFabricaShortcut", _dotRef); } catch (Exception ex) { Console.WriteLine("initFabricaShortcut: " + ex); }
try { await JS.InvokeVoidAsync("rt.setDotNetHelper", _dotRef); } catch (Exception ex) { Console.WriteLine("rt.setDotNetHelper: " + ex); } try { await JS.InvokeVoidAsync("rt.setDotNetHelper", _dotRef); } catch (Exception ex) { Console.WriteLine("rt.setDotNetHelper: " + ex); }
try { await JS.InvokeVoidAsync("rt.blockLeafletDoubleClick"); } catch (Exception ex) { Console.WriteLine("rt.blockLeafletDoubleClick: " + ex); } try { await JS.InvokeVoidAsync("rt.blockLeafletDoubleClick"); } catch (Exception ex) { Console.WriteLine("rt.blockLeafletDoubleClick: " + ex); }
try { await JS.InvokeVoidAsync("rt.initMobileMapControls"); } catch (Exception ex) { Console.WriteLine("rt.initMobileMapControls: " + ex); } try { await JS.InvokeVoidAsync("rt.initMobileMapControls"); } catch (Exception ex) { Console.WriteLine("rt.initMobileMapControls: " + ex); }

View File

@@ -1400,7 +1400,7 @@ public partial class PlanificadorRutas : ComponentBase
private bool _elevPopupMinimizado = false; private bool _elevPopupMinimizado = false;
private const string _versionApp = "(v-20260908a)"; private const string _versionApp = "(v-20260908b)";
private double? _paradaElevIdee; private double? _paradaElevIdee;

View File

@@ -1541,6 +1541,8 @@ public partial class PlanificadorRutas : ComponentBase
_inspectorElevLocal2 = null; _inspectorElevLocal2 = null;
_inspectorElevLocal1 = null; _inspectorElevLocal1 = null;
_inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>(); _inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
_inspectorSuperficieTexto = null;
_inspectorSuperficieCargando = true;
_popupInspectorVisible = true; _popupInspectorVisible = true;
ObtenerElevacionesLocalesComparativasPorLote(new[] { (lat, lon) }); ObtenerElevacionesLocalesComparativasPorLote(new[] { (lat, lon) });
@@ -1558,6 +1560,8 @@ public partial class PlanificadorRutas : ComponentBase
await ActualizarMarcadoresInspectorJsAsync(); await ActualizarMarcadoresInspectorJsAsync();
StateHasChanged(); StateHasChanged();
_ = CompletarSuperficiePuntoInspectorAsync(lat, lon, versionInspector);
if (_inspectorPuntoAnterior is { } puntoAnterior) if (_inspectorPuntoAnterior is { } puntoAnterior)
_ = CompletarDatosPuntoInspectorAnteriorAsync(puntoAnterior, versionInspector); _ = CompletarDatosPuntoInspectorAnteriorAsync(puntoAnterior, versionInspector);
@@ -1648,6 +1652,8 @@ public partial class PlanificadorRutas : ComponentBase
_popupInspectorVisible = false; _popupInspectorVisible = false;
_inspectorPuntoAnterior = null; _inspectorPuntoAnterior = null;
_inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>(); _inspectorLugaresCercanos = Array.Empty<LugarGeocodificadoCercano>();
_inspectorSuperficieCargando = false;
_inspectorSuperficieTexto = null;
try { await JS.InvokeVoidAsync("rt.clearInspectorMarkers"); } catch { } try { await JS.InvokeVoidAsync("rt.clearInspectorMarkers"); } catch { }
} }

View File

@@ -20,7 +20,6 @@ public partial class PlanificadorRutas
_zonasExclusionCaminata.Find(zona => zona.Id == _zonaExclusionSeleccionada); _zonasExclusionCaminata.Find(zona => zona.Id == _zonaExclusionSeleccionada);
private bool _modoDibujoExclusionCaminata => private bool _modoDibujoExclusionCaminata =>
_mostrarHerramientaZonas && ZonaExclusionSeleccionada is { Cerrada: false }; _mostrarHerramientaZonas && ZonaExclusionSeleccionada is { Cerrada: false };
private int NumeroZonasExclusion => _zonasExclusionCaminata.Count(zona => zona.Cerrada);
private List<List<double[]>>? ObtenerPoligonosExclusionCaminata() private List<List<double[]>>? ObtenerPoligonosExclusionCaminata()
{ {
@@ -37,6 +36,17 @@ public partial class PlanificadorRutas
await ActualizarEditorExclusionCaminataAsync(); await ActualizarEditorExclusionCaminataAsync();
} }
[JSInvokable]
public async Task AlternarHerramientaZonas()
{
if (_mostrarHerramientaZonas)
await CerrarHerramientaZonasAsync();
else
await AbrirHerramientaZonasAsync();
await InvokeAsync(StateHasChanged);
}
private async Task CerrarHerramientaZonasAsync() private async Task CerrarHerramientaZonasAsync()
{ {
_zonasExclusionCaminata.RemoveAll(zona => !zona.Cerrada); _zonasExclusionCaminata.RemoveAll(zona => !zona.Cerrada);

View File

@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using Microsoft.JSInterop; using Microsoft.JSInterop;
using RutasDBUS.Modelos.Planificacion; using RutasDBUS.Modelos.Planificacion;
@@ -7,6 +8,96 @@ namespace RutasDBUS.Components.Pages;
public partial class PlanificadorRutas public partial class PlanificadorRutas
{ {
private int _versionConsultaSuperficieSegmento; private int _versionConsultaSuperficieSegmento;
private bool _inspectorSuperficieCargando;
private string? _inspectorSuperficieTexto;
private readonly ConcurrentDictionary<string, Task<InformacionSueloTramo?>> _superficiePuntosInspectorCache = new();
/// <summary>
/// Consulta la superficie del camino más cercano a un punto Z. Valhalla necesita
/// una traza, por eso se construye una pequeña línea centrada en el punto.
/// </summary>
private async Task CompletarSuperficiePuntoInspectorAsync(
double latitud,
double longitud,
int versionInspector)
{
try
{
var informacion = await ObtenerSuperficiePuntoInspectorCacheadaAsync(latitud, longitud);
if (versionInspector != _versionInspector
|| !_popupInspectorVisible
|| Math.Abs(_inspectorLat - latitud) > 0.00000001
|| Math.Abs(_inspectorLon - longitud) > 0.00000001)
{
return;
}
_inspectorSuperficieTexto = FormatearSuperficiePuntoInspector(informacion);
_inspectorSuperficieCargando = false;
await InvokeAsync(StateHasChanged);
}
catch
{
if (versionInspector != _versionInspector
|| !_popupInspectorVisible
|| Math.Abs(_inspectorLat - latitud) > 0.00000001
|| Math.Abs(_inspectorLon - longitud) > 0.00000001)
{
return;
}
_inspectorSuperficieTexto = "No disponible";
_inspectorSuperficieCargando = false;
await InvokeAsync(StateHasChanged);
}
}
private Task<InformacionSueloTramo?> ObtenerSuperficiePuntoInspectorCacheadaAsync(
double latitud,
double longitud)
{
var clave = $"{latitud:0.000000}|{longitud:0.000000}";
return _superficiePuntosInspectorCache.GetOrAdd(
clave,
_ => ClienteValhalla.ObtenerInformacionSueloAsync(
CrearTrazaSondaSuperficie(latitud, longitud)));
}
private static double[,] CrearTrazaSondaSuperficie(double latitud, double longitud)
{
const double semilongitudMetros = 12.0;
const double metrosPorGrado = 111_320.0;
var deltaLatitud = semilongitudMetros / metrosPorGrado;
return new double[,]
{
{ latitud - deltaLatitud, longitud },
{ latitud, longitud },
{ latitud + deltaLatitud, longitud }
};
}
private static string FormatearSuperficiePuntoInspector(InformacionSueloTramo? informacion)
{
if (informacion is null || !informacion.Disponible || informacion.Segmentos.Count == 0)
return "No disponible";
var superficies = informacion.Segmentos
.Select(segmento =>
{
var uso = string.IsNullOrWhiteSpace(segmento.Uso)
? ""
: $" ({segmento.Uso})";
return $"{segmento.Superficie}{uso}";
})
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
return superficies.Count == 0
? "No disponible"
: string.Join(" · ", superficies);
}
/// <summary> /// <summary>
/// Consulta la superficie únicamente del segmento de elevación que se acaba de seleccionar. /// Consulta la superficie únicamente del segmento de elevación que se acaba de seleccionar.

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using RutasDBUS.Modelos.Historial; using RutasDBUS.Modelos.Historial;
using RutasDBUS.Servicios.Configuracion; using RutasDBUS.Servicios.Configuracion;
@@ -34,4 +35,18 @@ public partial class PlanificadorRutas
AplicarConfiguracionLocal(predeterminada); AplicarConfiguracionLocal(predeterminada);
_modoConfiguracionHistorial = ModoConfiguracionHistorial.Actual; _modoConfiguracionHistorial = ModoConfiguracionHistorial.Actual;
} }
[JSInvokable]
public async Task GuardarConfiguracionFabricaDesdeAtajoAsync()
{
if (!_configuracionLocalCargada)
return;
var actual = CrearConfiguracionLocal();
if (!await ValoresFabrica.GuardarAsync(actual))
return;
_configuracionFabrica = actual.Clonar();
await InvokeAsync(StateHasChanged);
}
} }

View File

@@ -228,15 +228,6 @@
</div> </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> </div>
@* STACK DE POPUPS (izquierda) *@ @* STACK DE POPUPS (izquierda) *@
@if (_paradaClicada is not null || _itinerarioClicado is not null || _popupInspectorVisible) @if (_paradaClicada is not null || _itinerarioClicado is not null || _popupInspectorVisible)
@@ -455,6 +446,17 @@
</span> </span>
} }
</div> </div>
<div class="inspector-surface-row">
<b>Superficie:</b>
@if (_inspectorSuperficieCargando)
{
<span class="muted">Consultando...</span>
}
else
{
<span>@(_inspectorSuperficieTexto ?? "No disponible")</span>
}
</div>
@if (_inspectorLugaresCercanos.Count > 0) @if (_inspectorLugaresCercanos.Count > 0)
{ {

View File

@@ -37,6 +37,42 @@ public sealed class ServicioValoresFabrica
finally { _bloqueo.Release(); } finally { _bloqueo.Release(); }
} }
public async Task<bool> GuardarAsync(ConfiguracionHistorialRuta config)
{
if (!EsValida(config))
return false;
await _bloqueo.WaitAsync();
var temporal = _ruta + ".tmp";
try
{
var directorio = Path.GetDirectoryName(_ruta);
if (!string.IsNullOrWhiteSpace(directorio))
Directory.CreateDirectory(directorio);
await using (var archivo = File.Create(temporal))
{
await JsonSerializer.SerializeAsync(
archivo,
config,
new JsonSerializerOptions { WriteIndented = true });
}
File.Move(temporal, _ruta, overwrite: true);
return true;
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or JsonException)
{
_logger.LogWarning("No se pudieron guardar los valores de fabrica: {Tipo}", ex.GetType().Name);
try { if (File.Exists(temporal)) File.Delete(temporal); } catch { }
return false;
}
finally
{
_bloqueo.Release();
}
}
private static bool EsValida(ConfiguracionHistorialRuta config) private static bool EsValida(ConfiguracionHistorialRuta config)
{ {
static bool Positivo(double? valor) => valor is { } v && double.IsFinite(v) && v > 0; static bool Positivo(double? valor) => valor is { } v && double.IsFinite(v) && v > 0;

View File

@@ -1363,6 +1363,10 @@ html, body {
color: #9ca3af; color: #9ca3af;
} }
.inspector-surface-row {
overflow-wrap: anywhere;
}
/* LISTA DE TRAMOS TIPO “DIRECCIONES” CON NÚMEROS */ /* LISTA DE TRAMOS TIPO “DIRECCIONES” CON NÚMEROS */
.lista-tramos-ruta { .lista-tramos-ruta {

View File

@@ -14,7 +14,10 @@ window.initAltInspector = function (dotNetHelper) {
}; };
document.addEventListener("keydown", function (e) { document.addEventListener("keydown", function (e) {
if (e.key === "Z" || e.key === "z") setInspectorActive(true); if (!e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey &&
(e.key === "Z" || e.key === "z")) {
setInspectorActive(true);
}
}, true); }, true);
document.addEventListener("keyup", function (e) { document.addEventListener("keyup", function (e) {
@@ -26,6 +29,36 @@ window.initAltInspector = function (dotNetHelper) {
}); });
}; };
window.initZonasShortcut = function (dotNetHelper) {
if (window.rt && window.rt._zonasShortcutInicializado) return;
window.rt = window.rt || {};
window.rt._zonasShortcutInicializado = true;
document.addEventListener("keydown", function (e) {
if (e.repeat || e.code !== "KeyZ" || !e.ctrlKey || !e.altKey || e.shiftKey) return;
e.preventDefault();
e.stopImmediatePropagation();
dotNetHelper.invokeMethodAsync("AlternarHerramientaZonas").catch(function () { });
}, true);
};
window.initFabricaShortcut = function (dotNetHelper) {
if (window.rt && window.rt._fabricaShortcutInicializado) return;
window.rt = window.rt || {};
window.rt._fabricaShortcutInicializado = true;
document.addEventListener("keydown", function (e) {
if (e.repeat || e.code !== "KeyF" || !e.ctrlKey || !e.altKey || e.shiftKey || e.metaKey) return;
e.preventDefault();
e.stopImmediatePropagation();
dotNetHelper.invokeMethodAsync("GuardarConfiguracionFabricaDesdeAtajoAsync").catch(function () { });
}, true);
};