1223 lines
30 KiB
C#
1223 lines
30 KiB
C#
using AgroBase.Models;
|
|
using Mapsui.Layers;
|
|
using NetTopologySuite.Geometries;
|
|
using OperationControl.Controls;
|
|
using OperationControl.Mapas.Editing;
|
|
using OperationControl.Mapas.Rendering;
|
|
using OperationControl.Mapas.Services;
|
|
using System.Diagnostics;
|
|
using static AgroBase.Models.Enums;
|
|
|
|
namespace OperationControl.Mapas
|
|
{
|
|
public sealed partial class MapaOperacaoController
|
|
{
|
|
private const double
|
|
IdadeMaximaPosicaoBordaGnssSegundos =
|
|
2.0;
|
|
|
|
|
|
private const double
|
|
DistanciaMinimaPontoManualMetros =
|
|
0.02;
|
|
|
|
|
|
private int _capturaAutomaticaUiAgendada;
|
|
|
|
|
|
private readonly object
|
|
_snapshotGnssLocalLock =
|
|
new();
|
|
|
|
|
|
private GPSModel?
|
|
_ultimaLeituraGnssLocal;
|
|
|
|
|
|
private readonly
|
|
MapaBordaGnssRenderService
|
|
_bordaGnssRenderService =
|
|
new();
|
|
|
|
|
|
private readonly Dictionary<
|
|
MapViewControl,
|
|
MemoryLayer>
|
|
_layersBordaGnss =
|
|
new();
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* SNAPSHOT DO GNSS LOCAL
|
|
* =========================================================
|
|
*/
|
|
|
|
private void AtualizarSnapshotGnssLocal(GPSModel dados)
|
|
{
|
|
if (dados == null)
|
|
return;
|
|
|
|
|
|
GPSModel copia = dados.Clone();
|
|
|
|
|
|
lock (_snapshotGnssLocalLock)
|
|
{
|
|
_ultimaLeituraGnssLocal = copia;
|
|
}
|
|
|
|
AgendarCapturaAutomaticaSeNecessario();
|
|
}
|
|
|
|
|
|
private GPSModel? ObterSnapshotGnssLocal()
|
|
{
|
|
lock (_snapshotGnssLocalLock)
|
|
{
|
|
return
|
|
_ultimaLeituraGnssLocal?
|
|
.Clone();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* RESUMO PARA O COCKPIT
|
|
* =========================================================
|
|
*/
|
|
|
|
private MapaResumoBordaGnss CriarResumoBordaGnss()
|
|
{
|
|
GPSModel? leitura =
|
|
ObterSnapshotGnssLocal();
|
|
|
|
|
|
bool leituraValida =
|
|
TentarValidarLeituraGnss(
|
|
leitura,
|
|
out double idadeSegundos,
|
|
out string motivo
|
|
);
|
|
|
|
|
|
bool qualidadeAlta =
|
|
leitura != null &&
|
|
(
|
|
leitura.QualidadeFix ==
|
|
TiposCorrecaoGPS.RTKFixo
|
|
||
|
|
leitura.QualidadeFix ==
|
|
TiposCorrecaoGPS.BaseFix
|
|
);
|
|
|
|
|
|
string status;
|
|
|
|
|
|
if (!leituraValida)
|
|
{
|
|
status =
|
|
motivo;
|
|
}
|
|
else
|
|
{
|
|
status =
|
|
$"{DescreverFix(leitura!.QualidadeFix)}" +
|
|
$" • {leitura.NumeroSatelites} sat" +
|
|
$" • {idadeSegundos:F1} s";
|
|
|
|
|
|
if (
|
|
double.IsFinite(
|
|
leitura.PrecisaoCm
|
|
)
|
|
)
|
|
{
|
|
status +=
|
|
$" • ~{leitura.PrecisaoCm:F1} cm";
|
|
}
|
|
}
|
|
|
|
|
|
return new MapaResumoBordaGnss
|
|
{
|
|
Modo = _estadoRecorte.ModoCapturaGnss,
|
|
|
|
QuantidadePontos = _estadoRecorte.QuantidadePontos,
|
|
|
|
ComprimentoMetros = CalcularComprimentoBordaGnss(),
|
|
|
|
CapturaAtiva = _estadoRecorte.CapturaAtiva,
|
|
|
|
Finalizada = _estadoRecorte.BordaGnssFinalizada,
|
|
|
|
LeituraGnssValida = leituraValida,
|
|
|
|
QualidadeAlta = qualidadeAlta,
|
|
|
|
StatusGnss = status,
|
|
|
|
GravacaoAutomaticaAtiva = _estadoRecorte.GravacaoAutomaticaAtiva,
|
|
|
|
DistanciaMinimaAutomaticaMetros = _estadoRecorte.DistanciaMinimaCapturaAutomaticaMetros,
|
|
};
|
|
}
|
|
|
|
|
|
private bool TentarValidarLeituraGnss(GPSModel? leitura, out double idadeSegundos, out string motivo)
|
|
{
|
|
idadeSegundos =
|
|
double.PositiveInfinity;
|
|
|
|
|
|
if (leitura == null)
|
|
{
|
|
motivo =
|
|
"GNSS sem leitura.";
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
if (!leitura.Inicializado)
|
|
{
|
|
motivo =
|
|
"GNSS ainda não inicializado.";
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
if (
|
|
!double.IsFinite(
|
|
leitura.Latitude
|
|
) ||
|
|
!double.IsFinite(
|
|
leitura.Longitude
|
|
) ||
|
|
leitura.Latitude < -90.0 ||
|
|
leitura.Latitude > 90.0 ||
|
|
leitura.Longitude < -180.0 ||
|
|
leitura.Longitude > 180.0 ||
|
|
(
|
|
Math.Abs(
|
|
leitura.Latitude
|
|
) < 1e-12 &&
|
|
Math.Abs(
|
|
leitura.Longitude
|
|
) < 1e-12
|
|
)
|
|
)
|
|
{
|
|
motivo =
|
|
"GNSS sem posição válida.";
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
double agoraMono =
|
|
Stopwatch.GetTimestamp() /
|
|
(double)
|
|
Stopwatch.Frequency;
|
|
|
|
|
|
double timestampPos =
|
|
leitura
|
|
.TimestampPos?
|
|
.valor
|
|
?? 0;
|
|
|
|
|
|
if (timestampPos > 0)
|
|
{
|
|
idadeSegundos =
|
|
Math.Max(
|
|
0,
|
|
agoraMono -
|
|
timestampPos
|
|
);
|
|
}
|
|
else if (
|
|
leitura.Momento !=
|
|
default
|
|
)
|
|
{
|
|
idadeSegundos =
|
|
Math.Max(
|
|
0,
|
|
(
|
|
DateTime.Now -
|
|
leitura.Momento
|
|
)
|
|
.TotalSeconds
|
|
);
|
|
}
|
|
|
|
|
|
if (
|
|
!double.IsFinite(
|
|
idadeSegundos
|
|
) ||
|
|
idadeSegundos >
|
|
IdadeMaximaPosicaoBordaGnssSegundos
|
|
)
|
|
{
|
|
motivo =
|
|
double.IsFinite(
|
|
idadeSegundos
|
|
)
|
|
? $"Posição GNSS desatualizada ({idadeSegundos:F1} s)."
|
|
: "Não foi possível determinar a idade da posição.";
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
motivo =
|
|
string.Empty;
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
private static string DescreverFix(TiposCorrecaoGPS fix)
|
|
{
|
|
return fix switch
|
|
{
|
|
TiposCorrecaoGPS.RTKFixo =>
|
|
"RTK FIX",
|
|
|
|
TiposCorrecaoGPS.RTKFlutuante =>
|
|
"RTK FLOAT",
|
|
|
|
TiposCorrecaoGPS.DGPS =>
|
|
"DGPS",
|
|
|
|
TiposCorrecaoGPS.BaseFix =>
|
|
"BASE FIX",
|
|
|
|
TiposCorrecaoGPS.Autonomo =>
|
|
"AUTÔNOMO",
|
|
|
|
TiposCorrecaoGPS.SemCorrecao =>
|
|
"SEM CORREÇÃO",
|
|
|
|
_ =>
|
|
fix.ToString()
|
|
};
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* CAPTURA
|
|
* =========================================================
|
|
*/
|
|
|
|
private void RegistrarPontoBordaGnssManual()
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_ferramentaEdicaoAtual !=
|
|
MapaFerramentaEdicao
|
|
.RecorteBordaGnss ||
|
|
!_estadoRecorte.CapturaAtiva
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
GPSModel? leitura =
|
|
ObterSnapshotGnssLocal();
|
|
|
|
|
|
if (
|
|
!TentarValidarLeituraGnss(
|
|
leitura,
|
|
out double idade,
|
|
out string motivo
|
|
)
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][PONTO][IGNORADO] " +
|
|
motivo
|
|
);
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
var ponto =
|
|
new Coordinate(
|
|
leitura!.Longitude,
|
|
leitura.Latitude
|
|
);
|
|
|
|
|
|
/*
|
|
* Manual respeita explicitamente
|
|
* a vontade do operador.
|
|
*
|
|
* Só bloqueamos duplicata praticamente
|
|
* idêntica.
|
|
*/
|
|
if (
|
|
_estadoRecorte.Pontos.Count > 0
|
|
)
|
|
{
|
|
Coordinate ultimo =
|
|
_estadoRecorte
|
|
.Pontos[
|
|
_estadoRecorte
|
|
.Pontos
|
|
.Count - 1
|
|
];
|
|
|
|
|
|
var linhaTeste =
|
|
new LineString(
|
|
new[]
|
|
{
|
|
new Coordinate(
|
|
ultimo.X,
|
|
ultimo.Y
|
|
),
|
|
|
|
new Coordinate(
|
|
ponto.X,
|
|
ponto.Y
|
|
)
|
|
}
|
|
);
|
|
|
|
|
|
double distancia =
|
|
MapaRecorteService
|
|
.CalcularComprimentoMetros(
|
|
linhaTeste
|
|
);
|
|
|
|
|
|
if (
|
|
distancia <
|
|
DistanciaMinimaPontoManualMetros
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][PONTO][IGNORADO] " +
|
|
$"Duplicata | Dist={distancia:F3} m"
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
_estadoRecorte
|
|
.AdicionarPontoBordaGnss(
|
|
ponto
|
|
);
|
|
|
|
|
|
AtualizarVisualBordaGnss();
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][PONTO] " +
|
|
$"N={_estadoRecorte.QuantidadePontos} | " +
|
|
$"Lat={leitura.Latitude:F9} | " +
|
|
$"Lon={leitura.Longitude:F9} | " +
|
|
$"Fix={leitura.QualidadeFix} | " +
|
|
$"Idade={idade:F2}s"
|
|
);
|
|
}
|
|
|
|
|
|
private void RemoverUltimoPontoBordaGnss()
|
|
{
|
|
if (
|
|
!_estadoRecorte
|
|
.RemoverUltimoPontoBordaGnss()
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
AtualizarVisualBordaGnss();
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS] " +
|
|
"Último ponto removido."
|
|
);
|
|
}
|
|
|
|
|
|
private void FinalizarCapturaBordaGnss()
|
|
{
|
|
if (
|
|
!_estadoRecorte
|
|
.FinalizarBordaGnss()
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
AtualizarVisualBordaGnss();
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS] " +
|
|
$"Captura finalizada | " +
|
|
$"Pontos={_estadoRecorte.QuantidadePontos} | " +
|
|
$"Comprimento={CalcularComprimentoBordaGnss():F2} m"
|
|
);
|
|
}
|
|
|
|
|
|
private void ContinuarCapturaBordaGnss()
|
|
{
|
|
if (
|
|
!_estadoRecorte
|
|
.ContinuarBordaGnss()
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS] " +
|
|
"Captura reaberta."
|
|
);
|
|
}
|
|
|
|
|
|
private void DescartarCapturaBordaGnss()
|
|
{
|
|
_estadoRecorte
|
|
.DescartarBordaGnss();
|
|
|
|
|
|
AtualizarVisualBordaGnss();
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS] " +
|
|
"Captura descartada."
|
|
);
|
|
}
|
|
|
|
|
|
private double CalcularComprimentoBordaGnss()
|
|
{
|
|
if (
|
|
_estadoRecorte
|
|
.Pontos
|
|
.Count < 2
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
var linha =
|
|
new LineString(
|
|
_estadoRecorte
|
|
.Pontos
|
|
.Select(
|
|
p =>
|
|
new Coordinate(
|
|
p.X,
|
|
p.Y
|
|
)
|
|
)
|
|
.ToArray()
|
|
);
|
|
|
|
|
|
return
|
|
MapaRecorteService
|
|
.CalcularComprimentoMetros(
|
|
linha
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* RENDER
|
|
* =========================================================
|
|
*/
|
|
|
|
private void AtualizarVisualBordaGnss()
|
|
{
|
|
foreach (
|
|
MapViewControl mapa
|
|
in _mapasEmEdicao
|
|
.ToArray())
|
|
{
|
|
RenderizarBordaGnssNoMapa(
|
|
mapa
|
|
);
|
|
|
|
|
|
mapa
|
|
.Mapa
|
|
.Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
private void AplicarBordaGnss()
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_ferramentaEdicaoAtual !=
|
|
MapaFerramentaEdicao
|
|
.RecorteBordaGnss
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* A captura precisa estar finalizada.
|
|
*
|
|
* Nada de cortar enquanto o operador
|
|
* ainda estiver levantando a borda.
|
|
*/
|
|
if (
|
|
!_estadoRecorte
|
|
.BordaGnssFinalizada ||
|
|
!_estadoRecorte
|
|
.PodeConcluirLinha
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][APLICAR][IGNORADO] " +
|
|
"A borda ainda não foi finalizada."
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Escopo cirúrgico:
|
|
*
|
|
* Somente selecionadas exige pelo menos
|
|
* uma rua selecionada.
|
|
*/
|
|
if (
|
|
_estadoRecorte
|
|
.SomenteSelecionadas &&
|
|
!_estadoEdicao
|
|
.PossuiSelecao
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][APLICAR][IGNORADO] " +
|
|
"Escopo=SomenteSelecionadas | " +
|
|
"Nenhuma rua selecionada."
|
|
);
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Copiamos a geometria.
|
|
*
|
|
* O service não recebe a coleção interna
|
|
* mutável do estado.
|
|
*/
|
|
Coordinate[] pontos =
|
|
_estadoRecorte
|
|
.Pontos
|
|
.Select(
|
|
p =>
|
|
new Coordinate(
|
|
p.X,
|
|
p.Y
|
|
)
|
|
)
|
|
.ToArray();
|
|
|
|
|
|
try
|
|
{
|
|
MapaResultadoRecorte resultado =
|
|
_edicaoService
|
|
.ApararPorLinha(
|
|
_estadoEdicao,
|
|
pontos,
|
|
somenteSelecionadas:
|
|
_estadoRecorte
|
|
.SomenteSelecionadas
|
|
);
|
|
|
|
|
|
/*
|
|
* Nenhuma alteração:
|
|
*
|
|
* NÃO descartamos a borda.
|
|
*
|
|
* Isso é muito importante em campo.
|
|
* O operador ainda pode olhar a linha,
|
|
* clicar em Continuar e corrigir/adicionar
|
|
* novos pontos.
|
|
*/
|
|
if (!resultado.HouveAlteracao)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][APLICAR] " +
|
|
$"Escopo={_estadoRecorte.Escopo} | " +
|
|
resultado
|
|
);
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Deu certo.
|
|
*
|
|
* A operação inteira já entrou no histórico
|
|
* como UMA única transação pelo
|
|
* MapaEdicaoService.
|
|
*
|
|
* Consumimos agora a borda utilizada e
|
|
* deixamos a ferramenta pronta para um
|
|
* novo levantamento.
|
|
*/
|
|
_estadoRecorte
|
|
.DescartarBordaGnss();
|
|
|
|
|
|
/*
|
|
* Houve alteração geométrica.
|
|
*
|
|
* Precisamos reconstruir o render
|
|
* do DocumentoTrabalho.
|
|
*/
|
|
RenderizarDocumentoEdicaoAtual();
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][APLICAR] " +
|
|
$"Escopo={_estadoRecorte.Escopo} | " +
|
|
resultado +
|
|
$" | Undo={PodeDesfazerEdicao} | " +
|
|
$"Redo={PodeRefazerEdicao}"
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
/*
|
|
* MapaEdicaoService é transacional.
|
|
*
|
|
* Em erro a geometria volta para o
|
|
* snapshot anterior.
|
|
*
|
|
* A borda GNSS permanece na tela para
|
|
* o operador não perder o levantamento.
|
|
*/
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][APLICAR][ERRO] " +
|
|
ex.Message
|
|
);
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
}
|
|
}
|
|
|
|
|
|
private void RenderizarBordaGnssNoMapa(MapViewControl mapaControl)
|
|
{
|
|
if (
|
|
mapaControl?.Mapa?.Map == null
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
bool exibir =
|
|
_estadoEdicao.EmEdicao &&
|
|
_mapasEmEdicao.Contains(
|
|
mapaControl
|
|
) &&
|
|
_ferramentaEdicaoAtual ==
|
|
MapaFerramentaEdicao
|
|
.RecorteBordaGnss &&
|
|
_estadoRecorte.Tipo ==
|
|
MapaTipoRecorte
|
|
.BordaGnss;
|
|
|
|
|
|
if (
|
|
!_layersBordaGnss
|
|
.TryGetValue(
|
|
mapaControl,
|
|
out MemoryLayer? camada
|
|
)
|
|
)
|
|
{
|
|
camada =
|
|
_bordaGnssRenderService
|
|
.CriarCamada(
|
|
_estadoRecorte
|
|
);
|
|
|
|
|
|
mapaControl
|
|
.Mapa
|
|
.Map
|
|
.Layers
|
|
.Add(
|
|
camada
|
|
);
|
|
|
|
|
|
_layersBordaGnss[
|
|
mapaControl
|
|
] =
|
|
camada;
|
|
}
|
|
|
|
|
|
if (exibir)
|
|
{
|
|
_bordaGnssRenderService
|
|
.AtualizarCamada(
|
|
camada,
|
|
_estadoRecorte
|
|
);
|
|
}
|
|
else
|
|
{
|
|
camada.Features =
|
|
Array.Empty<
|
|
Mapsui.IFeature>();
|
|
}
|
|
|
|
|
|
/*
|
|
* Preview fica sobre o mapa,
|
|
* mas Rover/Base continuam no topo.
|
|
*/
|
|
var layers =
|
|
mapaControl
|
|
.Mapa
|
|
.Map
|
|
.Layers;
|
|
|
|
|
|
if (
|
|
layers.Contains(
|
|
camada
|
|
)
|
|
)
|
|
{
|
|
layers.Remove(
|
|
camada
|
|
);
|
|
|
|
layers.Add(
|
|
camada
|
|
);
|
|
}
|
|
|
|
|
|
GarantirEntidadesNoTopo(
|
|
mapaControl
|
|
);
|
|
}
|
|
|
|
|
|
private void DefinirModoCapturaBordaGnss(MapaModoCapturaGnss modo)
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_ferramentaEdicaoAtual !=
|
|
MapaFerramentaEdicao
|
|
.RecorteBordaGnss
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (
|
|
!_estadoRecorte
|
|
.DefinirModoCapturaGnss(
|
|
modo
|
|
)
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][MODO] " +
|
|
$"Atual={modo}"
|
|
);
|
|
}
|
|
|
|
|
|
private void IniciarGravacaoBordaGnssAutomatica()
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_ferramentaEdicaoAtual !=
|
|
MapaFerramentaEdicao
|
|
.RecorteBordaGnss
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
GPSModel? leitura =
|
|
ObterSnapshotGnssLocal();
|
|
|
|
|
|
if (
|
|
!TentarValidarLeituraGnss(
|
|
leitura,
|
|
out double idade,
|
|
out string motivo
|
|
)
|
|
)
|
|
{
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][AUTO][INICIO][IGNORADO] " +
|
|
motivo
|
|
);
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if (
|
|
!_estadoRecorte
|
|
.IniciarGravacaoAutomatica()
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Registra imediatamente a posição
|
|
* onde o operador apertou INICIAR.
|
|
*/
|
|
ProcessarPontoAutomatico(
|
|
leitura!,
|
|
forcarPrimeiroPonto: true
|
|
);
|
|
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][AUTO] " +
|
|
"Gravação iniciada | " +
|
|
$"DistMin=" +
|
|
$"{_estadoRecorte.DistanciaMinimaCapturaAutomaticaMetros:F2} m | " +
|
|
$"Fix={leitura!.QualidadeFix} | " +
|
|
$"Idade={idade:F2}s"
|
|
);
|
|
}
|
|
|
|
|
|
private void ProcessarPontoAutomatico(GPSModel leitura, bool forcarPrimeiroPonto = false)
|
|
{
|
|
if (
|
|
leitura == null ||
|
|
!_estadoEdicao.EmEdicao ||
|
|
_ferramentaEdicaoAtual !=
|
|
MapaFerramentaEdicao
|
|
.RecorteBordaGnss ||
|
|
!_estadoRecorte
|
|
.GravacaoAutomaticaAtiva
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (
|
|
!TentarValidarLeituraGnss(
|
|
leitura,
|
|
out double idade,
|
|
out _
|
|
)
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
var ponto =
|
|
new Coordinate(
|
|
leitura.Longitude,
|
|
leitura.Latitude
|
|
);
|
|
|
|
|
|
bool adicionar =
|
|
_estadoRecorte
|
|
.QuantidadePontos == 0;
|
|
|
|
|
|
double distancia =
|
|
0;
|
|
|
|
|
|
if (
|
|
!adicionar &&
|
|
_estadoRecorte
|
|
.QuantidadePontos > 0
|
|
)
|
|
{
|
|
Coordinate ultimo =
|
|
_estadoRecorte
|
|
.Pontos[
|
|
_estadoRecorte
|
|
.QuantidadePontos - 1
|
|
];
|
|
|
|
|
|
var trecho =
|
|
new LineString(
|
|
new[]
|
|
{
|
|
new Coordinate(
|
|
ultimo.X,
|
|
ultimo.Y
|
|
),
|
|
|
|
new Coordinate(
|
|
ponto.X,
|
|
ponto.Y
|
|
)
|
|
}
|
|
);
|
|
|
|
|
|
distancia =
|
|
MapaRecorteService
|
|
.CalcularComprimentoMetros(
|
|
trecho
|
|
);
|
|
|
|
|
|
adicionar =
|
|
distancia >=
|
|
_estadoRecorte
|
|
.DistanciaMinimaCapturaAutomaticaMetros;
|
|
}
|
|
|
|
|
|
if (
|
|
forcarPrimeiroPonto &&
|
|
_estadoRecorte.QuantidadePontos == 0
|
|
)
|
|
{
|
|
adicionar =
|
|
true;
|
|
}
|
|
|
|
|
|
if (!adicionar)
|
|
return;
|
|
|
|
|
|
_estadoRecorte
|
|
.AdicionarPontoBordaGnss(
|
|
ponto
|
|
);
|
|
|
|
|
|
AtualizarVisualBordaGnss();
|
|
|
|
SincronizarCockpitEdicao();
|
|
|
|
|
|
EstadoEdicaoAlterado?
|
|
.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][EDICAO][BORDA-GNSS][AUTO][PONTO] " +
|
|
$"N={_estadoRecorte.QuantidadePontos} | " +
|
|
$"Dist={distancia:F2} m | " +
|
|
$"Lat={leitura.Latitude:F9} | " +
|
|
$"Lon={leitura.Longitude:F9} | " +
|
|
$"Fix={leitura.QualidadeFix} | " +
|
|
$"Idade={idade:F2}s"
|
|
);
|
|
}
|
|
|
|
|
|
private void AgendarCapturaAutomaticaSeNecessario()
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_ferramentaEdicaoAtual !=
|
|
MapaFerramentaEdicao
|
|
.RecorteBordaGnss ||
|
|
!_estadoRecorte
|
|
.GravacaoAutomaticaAtiva
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Evita colocar 300 delegates na fila
|
|
* caso o GNSS esteja publicando rápido.
|
|
*/
|
|
if (
|
|
System.Threading
|
|
.Interlocked
|
|
.Exchange(
|
|
ref _capturaAutomaticaUiAgendada,
|
|
1
|
|
) != 0
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
var dispatcher =
|
|
System.Windows
|
|
.Application
|
|
.Current?
|
|
.Dispatcher;
|
|
|
|
|
|
if (dispatcher == null)
|
|
{
|
|
System.Threading
|
|
.Interlocked
|
|
.Exchange(
|
|
ref _capturaAutomaticaUiAgendada,
|
|
0
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
dispatcher.BeginInvoke(
|
|
new Action(
|
|
() =>
|
|
{
|
|
try
|
|
{
|
|
if (
|
|
!_estadoEdicao.EmEdicao ||
|
|
_ferramentaEdicaoAtual !=
|
|
MapaFerramentaEdicao
|
|
.RecorteBordaGnss ||
|
|
!_estadoRecorte
|
|
.GravacaoAutomaticaAtiva
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
GPSModel? leitura =
|
|
ObterSnapshotGnssLocal();
|
|
|
|
|
|
if (leitura == null)
|
|
return;
|
|
|
|
|
|
ProcessarPontoAutomatico(
|
|
leitura
|
|
);
|
|
}
|
|
finally
|
|
{
|
|
System.Threading
|
|
.Interlocked
|
|
.Exchange(
|
|
ref _capturaAutomaticaUiAgendada,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
),
|
|
System.Windows.Threading
|
|
.DispatcherPriority.Background
|
|
);
|
|
}
|
|
|
|
|
|
}
|
|
} |