using NetTopologySuite.Geometries; using OperationControl.Controls; using OperationControl.Mapas.Editing; using OperationControl.Mapas.Interaction; namespace OperationControl.Mapas { public sealed partial class MapaOperacaoController { /* * ========================================================= * CONFIGURAÇÃO DE INTERAÇÃO * ========================================================= */ /// /// Define explicitamente como o MapViewControl deve /// interpretar a interação do operador. /// /// Se a View estiver em uma sessão de edição ativa, /// o modo Edicao tem prioridade até a sessão terminar. /// public void DefinirModoInteracao( MapViewControl? mapaControl, MapaModoInteracao modo) { if ( mapaControl?.Mapa?.Map == null ) { return; } RegistrarMapa( mapaControl ); MapaModoInteracao modoEfetivo = _estadoEdicao.EmEdicao && _mapasEmEdicao.Contains( mapaControl ) ? MapaModoInteracao.Edicao : modo; AplicarModoInteracao( mapaControl, modoEfetivo ); } private void AplicarModoInteracao( MapViewControl mapaControl, MapaModoInteracao modo) { if ( mapaControl.ModoInteracao == modo ) { return; } var anterior = mapaControl.ModoInteracao; mapaControl.DefinirModoInteracao( modo ); Log( "[MAPA][INTERACAO] " + $"Anterior={anterior} | " + $"Atual={modo}" ); } /* * ========================================================= * RUA * ========================================================= */ private void OnMapaNovoRuaClicada(string ruaId) { try { /* * ============================================= * MODO EDIÇÃO * ============================================= * * A seleção operacional fica completamente * intocada. */ if (_estadoEdicao.EmEdicao) { /* * Uma rua só responde ao clique direto * enquanto a ferramenta Selecionar * estiver ativa. */ if ( _ferramentaEdicaoAtual != MapaFerramentaEdicao.Selecionar ) { return; } AlternarRuaEdicao( ruaId ); Log( "[MAPA][EDICAO][CLIQUE] " + $"Rua={ruaId} | " + $"Selecionadas=" + $"{QuantidadeRuasSelecionadasEdicao}" ); return; } /* * ============================================= * MODO NORMAL * ============================================= */ AlternarRuaOperacao( ruaId ); string ruas = string.Join( " -> ", ObterRuasPercorrer() ); Log( "[MAPA][CLIQUE] " + $"Rua={ruaId} | " + $"Percurso=[{ruas}]" ); } catch (Exception ex) { Log( "[MAPA][CLIQUE][ERRO] " + ex.Message ); } } /* * ========================================================= * RETORNO * ========================================================= */ private void OnMapaNovoPontoRetornoClicado( double latitude, double longitude) { try { AlternarPontoRetorno( latitude, longitude ); double distancia = ObterDistanciaRotaRetornoMetros(); Log( "[MAPA][RETORNO][CLIQUE] " + $"Pontos={QuantidadePontosRetorno} | " + $"Distancia={distancia:F2} m" ); } catch (Exception ex) { Log( "[MAPA][RETORNO][CLIQUE][ERRO] " + ex.Message ); } } /* * ========================================================= * ENTIDADE * ========================================================= */ private void OnMapaNovaEntidadeClicada( string entidadeId) { try { var entidade = ObterEntidade( entidadeId ); if (entidade == null) return; Log( "[MAPA][ENTIDADE][CLIQUE] " + $"Id={entidade.Id} | " + $"Tipo={entidade.Tipo}" ); /* * Fim da responsabilidade do mapa. * * Dock decidirá o que significa * selecionar Base ou Rover. */ EntidadeClicada? .Invoke( entidade.Id ); } catch (Exception ex) { Log( "[MAPA][ENTIDADE][CLIQUE][ERRO] " + ex.Message ); } } /* * ========================================================= * VIEWPORT * ========================================================= */ private void OnMapaNovoViewportAlterado( MapViewControl mapaControl) { /* * Somente entidades dependem da resolução * atual para manter tamanho visual adequado. */ RenderizarEntidadesDinamicasNoMapa( mapaControl ); mapaControl .Mapa .Refresh(); } private void OnMapaEditorFerramentaSelecionada(MapViewControl mapaControl, MapaFerramentaEdicao ferramenta) { if ( !_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains( mapaControl ) ) { return; } DefinirFerramentaEdicao( ferramenta ); } private void OnMapaEditorLimparSelecaoSolicitado(MapViewControl mapaControl) { if ( !_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains( mapaControl ) ) { return; } LimparSelecaoEdicao(); } private void OnMapaEditorRetanguloSelecionado(MapViewControl mapaControl, MapaRetanguloSelecao retangulo) { if ( !_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains( mapaControl ) || _ferramentaEdicaoAtual != MapaFerramentaEdicao .SelecionarRetangulo ) { return; } try { var encontrados = _edicaoService .SelecionarPorRetangulo( _estadoEdicao, retangulo .MinLatitude, retangulo .MinLongitude, retangulo .MaxLatitude, retangulo .MaxLongitude, substituirSelecao: true ); foreach ( var mapa in _mapasEmEdicao .ToArray()) { if ( _rendersMapa.TryGetValue( mapa, out var render ) ) { _edicaoRenderService .AplicarSelecao( render, _estadoEdicao ); mapa.Mapa.Refresh(); } } SincronizarCockpitEdicao(); EstadoEdicaoAlterado? .Invoke(); Log( "[MAPA][EDICAO][RETANGULO] " + $"Encontradas={encontrados.Count} | " + $"Selecionadas=" + $"{QuantidadeRuasSelecionadasEdicao}" ); } catch (Exception ex) { Log( "[MAPA][EDICAO][RETANGULO][ERRO] " + ex.Message ); } } private void OnMapaEditorPoligonoRecorteDefinido(MapViewControl mapaControl, MapaPoligonoRecorte recorte) { bool ferramentaPoligono = _ferramentaEdicaoAtual == MapaFerramentaEdicao.RecorteLivre || _ferramentaEdicaoAtual == MapaFerramentaEdicao.RecortePorPontos; if ( !_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains( mapaControl ) || !ferramentaPoligono ) { return; } if ( recorte == null || recorte.QuantidadePontos < 3 ) { return; } try { /* * Fronteira UI → domínio. * * DTO: * Latitude / Longitude * * NTS: * X = longitude * Y = latitude */ Coordinate[] pontos = recorte .Pontos .Select( p => new Coordinate( p.Longitude, p.Latitude ) ) .ToArray(); /* * V1: * o próprio polígono define o escopo. * * Depois adicionaremos a opção * "Somente ruas selecionadas". */ if (_estadoRecorte.SomenteSelecionadas && !_estadoEdicao.PossuiSelecao) { Log( "[MAPA][EDICAO][RECORTE][IGNORADO] " + "Escopo=SomenteSelecionadas | " + "Nenhuma rua selecionada." ); SincronizarCockpitEdicao(); return; } MapaResultadoRecorte resultado = _edicaoService .RecortarPorPoligono( _estadoEdicao, pontos, somenteSelecionadas: _estadoRecorte.SomenteSelecionadas ); /* * Mesmo sem alteração podemos ter * ruas ambíguas importantes para log. */ if (!resultado.HouveAlteracao) { string tipoRecorte = _ferramentaEdicaoAtual == MapaFerramentaEdicao.RecorteLivre ? "RECORTE-LIVRE" : "RECORTE-PONTOS"; Log( $"[MAPA][EDICAO][{tipoRecorte}] " + $"Escopo={_estadoRecorte.Escopo} | " + resultado ); return; } /* * É alteração estrutural/geometria. * * Não basta atualizar estilo. */ RenderizarDocumentoEdicaoAtual(); SincronizarCockpitEdicao(); EstadoEdicaoAlterado? .Invoke(); Log( "[MAPA][EDICAO][RECORTE-LIVRE] " + resultado ); } catch (Exception ex) { /* * O MapaEdicaoService já restaura * o snapshot transacional em caso * de erro. */ Log( "[MAPA][EDICAO][RECORTE-LIVRE][ERRO] " + ex.Message ); } } private void OnMapaEditorDesfazerSolicitado(MapViewControl mapaControl) { if ( !_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains( mapaControl ) ) { return; } DesfazerEdicao(); } private void OnMapaEditorRefazerSolicitado(MapViewControl mapaControl) { if ( !_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains( mapaControl ) ) { return; } RefazerEdicao(); } private void OnMapaEditorExcluirSelecionadasSolicitado(MapViewControl mapaControl) { if ( !_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains( mapaControl ) || !_estadoEdicao.PossuiSelecao ) { return; } RemoverRuasSelecionadasEdicao(); } private void OnMapaEditorAplicarEdicaoSolicitado(MapViewControl mapaControl) { if (!_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains(mapaControl)) { return; } AplicarEdicao(); } private void OnMapaEditorCancelarEdicaoSolicitado(MapViewControl mapaControl) { if (!_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains(mapaControl)) { return; } CancelarEdicao(mapaControl); } private void OnMapaEditorEscopoRecorteAlterado(MapViewControl mapaControl, MapaEscopoRecorte escopo) { if (!_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains(mapaControl)) return; _estadoRecorte.Escopo = escopo; SincronizarCockpitEdicao(); Log( "[MAPA][EDICAO][RECORTE][ESCOPO] " + $"Atual={escopo}" ); } private void OnMapaEditorBordaGnssAcaoSolicitada(MapViewControl mapaControl, MapaAcaoBordaGnss acao) { if ( !_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains(mapaControl) || _ferramentaEdicaoAtual != MapaFerramentaEdicao.RecorteBordaGnss ) { return; } switch (acao) { case MapaAcaoBordaGnss .RegistrarPonto: RegistrarPontoBordaGnssManual(); break; case MapaAcaoBordaGnss .RemoverUltimo: RemoverUltimoPontoBordaGnss(); break; case MapaAcaoBordaGnss .Finalizar: FinalizarCapturaBordaGnss(); break; case MapaAcaoBordaGnss .Continuar: ContinuarCapturaBordaGnss(); break; case MapaAcaoBordaGnss .Descartar: DescartarCapturaBordaGnss(); break; case MapaAcaoBordaGnss.Aplicar: AplicarBordaGnss(); break; case MapaAcaoBordaGnss.DefinirModoManual: DefinirModoCapturaBordaGnss(MapaModoCapturaGnss.Manual); break; case MapaAcaoBordaGnss.DefinirModoAutomatico: DefinirModoCapturaBordaGnss(MapaModoCapturaGnss.Automatico); break; case MapaAcaoBordaGnss.IniciarGravacaoAutomatica: IniciarGravacaoBordaGnssAutomatica(); break; } } private void OnMapaEditorRestaurarFonteSolicitado(MapViewControl mapaControl) { if (!_estadoEdicao.EmEdicao || !_mapasEmEdicao.Contains(mapaControl)) { return; } RestaurarMapaFonteEdicao(); } } }