diff --git a/AgroBase/AgroBase/Models/GeneralJoystick.cs b/AgroBase/AgroBase/Models/GeneralJoystick.cs index 84f6eddef..b7f7d47ad 100644 --- a/AgroBase/AgroBase/Models/GeneralJoystick.cs +++ b/AgroBase/AgroBase/Models/GeneralJoystick.cs @@ -863,6 +863,9 @@ namespace AgroBase switch (Comando.Dispositivo) { case T_Code.Mov: + if (_Controle.TipoMovimento == TipoMovimentoDirecional.RotacionarNoEixo) + return; + _Controle.PercentualVelocidadeSP = Comando.ValorAplicar; _Controle.EmFreio = Comando.EstadoAplicar; op.AtualizarDadosControle(ForcarComando, new List() { Comando.Dispositivo }); diff --git a/AgroBase/AgroBase/Models/Modules/MovimentacaoUnificadoModel.cs b/AgroBase/AgroBase/Models/Modules/MovimentacaoUnificadoModel.cs index e601e4e48..f94c2fb85 100644 --- a/AgroBase/AgroBase/Models/Modules/MovimentacaoUnificadoModel.cs +++ b/AgroBase/AgroBase/Models/Modules/MovimentacaoUnificadoModel.cs @@ -564,11 +564,9 @@ namespace AgroBase.Models.Modules modulo.MovMotor.EnviarComandoControle(Direcao.Parado); } - if (DirecaoAtual == Direcao.Parado) - { - await Task.Delay(1000, cts.Token); - } - + bool parado() => op?.Sensoriamento?.Movimentacao?.RPMMotorMedio <= 5; + await FuncoesGlobais.AguardarCondicaoAsync(parado, 2500, 200); + switch (op.Controle.TipoMovimento) { case TipoMovimentoDirecional.RotacionarNoEixo: @@ -580,13 +578,10 @@ namespace AgroBase.Models.Modules } DateTime Agora = DateTime.Now; - bool Sucesso = Modulos.All(x => x.DirMotor.PosicaoTipoMovimentoConcluido(DirecaoAtual)); - while (Agora.AddMilliseconds(5000) > DateTime.Now && !Sucesso) - { - await Task.Delay(100, cts.Token); - Sucesso = Modulos.All(x => x.DirMotor.PosicaoTipoMovimentoConcluido(DirecaoAtual)); - } - + bool atingiuSp() => Modulos.All(x => x.DirMotor.PosicaoTipoMovimentoConcluido(DirecaoAtual)); + await FuncoesGlobais.AguardarCondicaoAsync(atingiuSp, 5000, 200); + bool Sucesso = atingiuSp(); + if (Sucesso && DirecaoAtual != Direcao.Parado) { op.Controle.PercentualVelocidadeSP = 20; diff --git a/AgroBase/AgroBase/Services/MapasService.cs b/AgroBase/AgroBase/Services/MapasService.cs index cafb17cfe..6fbd4a14d 100644 --- a/AgroBase/AgroBase/Services/MapasService.cs +++ b/AgroBase/AgroBase/Services/MapasService.cs @@ -144,7 +144,22 @@ namespace AgroBase.Services { ValidarArquivosShapefile(caminhoShp); - Func transformar = CriarTransformacaoParaWgs84(caminhoShp); + var featuresShp = Shapefile + .ReadAllFeatures(caminhoShp) + .ToList(); + + if (featuresShp.Count == 0) + { + throw new InvalidOperationException( + "O Shapefile não possui features válidas." + ); + } + + Func transformar = + CriarTransformacaoParaWgs84( + caminhoShp, + featuresShp + ); var dados = new MapaFeatureCollectionModel { @@ -154,18 +169,22 @@ namespace AgroBase.Services int indiceRua = 1; - foreach (var featureShp in Shapefile.ReadAllFeatures(caminhoShp)) + foreach (var featureShp in featuresShp) { - if (featureShp?.Geometry == null || featureShp.Geometry.IsEmpty) + if (featureShp?.Geometry == null || + featureShp.Geometry.IsEmpty) { continue; } - IEnumerable linhas = ExtrairLineStrings(featureShp.Geometry); + IEnumerable linhas = + ExtrairLineStrings(featureShp.Geometry); foreach (LineString linha in linhas) { - if (linha == null || linha.IsEmpty || linha.NumPoints < 2) + if (linha == null || + linha.IsEmpty || + linha.NumPoints < 2) { continue; } @@ -174,37 +193,49 @@ namespace AgroBase.Services foreach (Coordinate coordenada in linha.Coordinates) { - double[] lonLat = transformar(coordenada.X, coordenada.Y); + double[] lonLat = + transformar( + coordenada.X, + coordenada.Y + ); double longitude = lonLat[0]; double latitude = lonLat[1]; - if (double.IsNaN(longitude) || + if ( + double.IsNaN(longitude) || double.IsInfinity(longitude) || double.IsNaN(latitude) || double.IsInfinity(latitude) || longitude < -180 || longitude > 180 || latitude < -90 || - latitude > 90) + latitude > 90 + ) { throw new InvalidOperationException( "O Shapefile produziu uma coordenada " + - $"inválida: {longitude}, {latitude}." + $"inválida após transformação: " + + $"{longitude}, {latitude}." ); } - coordenadas.Add(new List - { - longitude, - latitude - }); + coordenadas.Add( + new List + { + longitude, + latitude + } + ); } if (coordenadas.Count < 2) continue; - string id = indiceRua.ToString(CultureInfo.InvariantCulture); + string id = + indiceRua.ToString( + CultureInfo.InvariantCulture + ); var properties = new MapaFeaturePropertiesModel @@ -314,62 +345,224 @@ namespace AgroBase.Services ". São aceitas somente linhas." ); } - private static Func CriarTransformacaoParaWgs84(string caminhoShp) + private static Func CriarTransformacaoParaWgs84(string caminhoShp, IEnumerable features) { - string caminhoPrj = Path.ChangeExtension(caminhoShp, ".prj"); + string caminhoPrj = + Path.ChangeExtension(caminhoShp, ".prj"); + bool coordenadasParecemGeograficas = + CoordenadasParecemLongitudeLatitude(features); + + /* + * ========================================================== + * 1. SHAPEFILE SEM .PRJ + * ========================================================== + * + * Se as próprias coordenadas já estão dentro dos limites + * longitude/latitude, aceitamos como coordenadas geográficas. + */ if (!File.Exists(caminhoPrj)) { + if (coordenadasParecemGeograficas) + { + Variaveis.MostrarLog( + "[MapasService] Shapefile sem arquivo .prj. " + + "As coordenadas já parecem estar em " + + "longitude/latitude. Nenhuma reprojeção será aplicada." + ); + + return (x, y) => new[] { x, y }; + } + + throw new InvalidOperationException( + "O Shapefile não possui arquivo .prj e suas " + + "coordenadas não parecem estar em longitude/latitude. " + + "Não é possível determinar com segurança o sistema " + + "de coordenadas do mapa." + ); + } + + string wkt = + File.ReadAllText( + caminhoPrj, + Encoding.UTF8 + ); + + /* + * ========================================================== + * 2. .PRJ VAZIO + * ========================================================== + */ + if (string.IsNullOrWhiteSpace(wkt)) + { + if (coordenadasParecemGeograficas) + { + Variaveis.MostrarLog( + "[MapasService] Arquivo .prj vazio. " + + "As coordenadas parecem longitude/latitude; " + + "o mapa será carregado sem reprojeção." + ); + + return (x, y) => new[] { x, y }; + } + + throw new InvalidOperationException( + "O arquivo .prj está vazio e as coordenadas " + + "do Shapefile não parecem longitude/latitude." + ); + } + + /* + * ========================================================== + * 3. TENTA INTERPRETAR O .PRJ NORMALMENTE + * ========================================================== + */ + try + { + var fabricaSistemas = + new CoordinateSystemFactory(); + + var sistemaOrigem = + fabricaSistemas.CreateFromWkt(wkt); + + if (sistemaOrigem == null) + { + throw new InvalidOperationException( + "O sistema de coordenadas do .prj não pôde ser criado." + ); + } + + var sistemaDestino = + GeographicCoordinateSystem.WGS84; + + var fabricaTransformacao = + new CoordinateTransformationFactory(); + + var transformacao = + fabricaTransformacao + .CreateFromCoordinateSystems( + sistemaOrigem, + sistemaDestino + ); + + if (transformacao?.MathTransform == null) + { + throw new InvalidOperationException( + "Não foi possível criar a transformação para WGS84." + ); + } + /* - * Sem PRJ, só será permitido se as coordenadas - * já parecerem longitude/latitude. + * O PRJ é válido. + * Usa a transformação normalmente. */ return (x, y) => { - if (x < -180 || x > 180 || y < -90 || y > 90) - { - throw new InvalidOperationException( - "O Shapefile não possui arquivo .prj e " + - "as coordenadas não estão em longitude/latitude." + double[] resultado = + transformacao.MathTransform.Transform( + new[] { x, y } ); - } - return new[] { x, y }; + return resultado; }; } - - string wkt = File.ReadAllText(caminhoPrj, Encoding.UTF8); - - if (string.IsNullOrWhiteSpace(wkt)) - { - throw new InvalidOperationException("O arquivo .prj está vazio."); - } - - try - { - var fabricaSistemas = new CoordinateSystemFactory(); - - var sistemaOrigem = fabricaSistemas.CreateFromWkt(wkt); - - var sistemaDestino = GeographicCoordinateSystem.WGS84; - - var fabricaTransformacao = new CoordinateTransformationFactory(); - - var transformacao = fabricaTransformacao.CreateFromCoordinateSystems(sistemaOrigem, sistemaDestino); - - return (x, y) => transformacao.MathTransform.Transform( - new[] { x, y } - ); - } catch (Exception ex) { + /* + * ====================================================== + * 4. .PRJ EXISTE, MAS ESTÁ QUEBRADO + * ====================================================== + * + * Se as coordenadas já são compatíveis com longitude / + * latitude, podemos ignorar o PRJ defeituoso. + * + * Esse é exatamente o caso do mapa atual. + */ + if (coordenadasParecemGeograficas) + { + Variaveis.MostrarLog( + "[MapasService] AVISO: o arquivo .prj existe, " + + "mas não pôde ser interpretado. " + + "Como as coordenadas do Shapefile já parecem estar " + + "em longitude/latitude, o mapa será carregado " + + "sem reprojeção. Erro do PRJ: " + + ex.Message + ); + + return (x, y) => new[] { x, y }; + } + + /* + * Temos um PRJ quebrado e coordenadas aparentemente + * projetadas (UTM, por exemplo). + * + * Aqui NÃO devemos adivinhar. + */ throw new InvalidOperationException( - "Não foi possível interpretar ou reprojetar " + - "o sistema de coordenadas definido no arquivo .prj.", + "O arquivo .prj do Shapefile não pôde ser interpretado " + + "e as coordenadas do mapa não parecem estar em " + + "longitude/latitude. Não é possível determinar com " + + "segurança a projeção utilizada.", ex ); } } + private static bool CoordenadasParecemLongitudeLatitude(IEnumerable features) + { + if (features == null) + return false; + + int quantidade = 0; + + foreach (IFeature feature in features) + { + if (feature?.Geometry == null || + feature.Geometry.IsEmpty) + { + continue; + } + + foreach (Coordinate coordenada in feature.Geometry.Coordinates) + { + if (coordenada == null) + continue; + + double x = coordenada.X; + double y = coordenada.Y; + + if ( + double.IsNaN(x) || + double.IsInfinity(x) || + double.IsNaN(y) || + double.IsInfinity(y) + ) + { + return false; + } + + /* + * Limites físicos de longitude e latitude. + */ + if ( + x < -180.0 || + x > 180.0 || + y < -90.0 || + y > 90.0 + ) + { + return false; + } + + quantidade++; + } + } + + /* + * Só consideramos válido se realmente encontramos + * coordenadas para analisar. + */ + return quantidade > 0; + } private static void ValidarArquivosShapefile(string caminhoShp) { string caminhoDbf = Path.ChangeExtension(caminhoShp, ".dbf");