refinamento no movimento combinado; carregamento de mapa com .prj quebrado

This commit is contained in:
Diego Freitas 2026-09-02 15:35:07 -03:00
parent 3891aaf819
commit e07a842152
3 changed files with 255 additions and 64 deletions

View File

@ -863,6 +863,9 @@ namespace AgroBase
switch (Comando.Dispositivo) switch (Comando.Dispositivo)
{ {
case T_Code.Mov: case T_Code.Mov:
if (_Controle.TipoMovimento == TipoMovimentoDirecional.RotacionarNoEixo)
return;
_Controle.PercentualVelocidadeSP = Comando.ValorAplicar; _Controle.PercentualVelocidadeSP = Comando.ValorAplicar;
_Controle.EmFreio = Comando.EstadoAplicar; _Controle.EmFreio = Comando.EstadoAplicar;
op.AtualizarDadosControle(ForcarComando, new List<T_Code>() { Comando.Dispositivo }); op.AtualizarDadosControle(ForcarComando, new List<T_Code>() { Comando.Dispositivo });

View File

@ -564,11 +564,9 @@ namespace AgroBase.Models.Modules
modulo.MovMotor.EnviarComandoControle(Direcao.Parado); modulo.MovMotor.EnviarComandoControle(Direcao.Parado);
} }
if (DirecaoAtual == Direcao.Parado) bool parado() => op?.Sensoriamento?.Movimentacao?.RPMMotorMedio <= 5;
{ await FuncoesGlobais.AguardarCondicaoAsync(parado, 2500, 200);
await Task.Delay(1000, cts.Token);
}
switch (op.Controle.TipoMovimento) switch (op.Controle.TipoMovimento)
{ {
case TipoMovimentoDirecional.RotacionarNoEixo: case TipoMovimentoDirecional.RotacionarNoEixo:
@ -580,13 +578,10 @@ namespace AgroBase.Models.Modules
} }
DateTime Agora = DateTime.Now; DateTime Agora = DateTime.Now;
bool Sucesso = Modulos.All(x => x.DirMotor.PosicaoTipoMovimentoConcluido(DirecaoAtual)); bool atingiuSp() => Modulos.All(x => x.DirMotor.PosicaoTipoMovimentoConcluido(DirecaoAtual));
while (Agora.AddMilliseconds(5000) > DateTime.Now && !Sucesso) await FuncoesGlobais.AguardarCondicaoAsync(atingiuSp, 5000, 200);
{ bool Sucesso = atingiuSp();
await Task.Delay(100, cts.Token);
Sucesso = Modulos.All(x => x.DirMotor.PosicaoTipoMovimentoConcluido(DirecaoAtual));
}
if (Sucesso && DirecaoAtual != Direcao.Parado) if (Sucesso && DirecaoAtual != Direcao.Parado)
{ {
op.Controle.PercentualVelocidadeSP = 20; op.Controle.PercentualVelocidadeSP = 20;

View File

@ -144,7 +144,22 @@ namespace AgroBase.Services
{ {
ValidarArquivosShapefile(caminhoShp); ValidarArquivosShapefile(caminhoShp);
Func<double, double, double[]> 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<double, double, double[]> transformar =
CriarTransformacaoParaWgs84(
caminhoShp,
featuresShp
);
var dados = new MapaFeatureCollectionModel var dados = new MapaFeatureCollectionModel
{ {
@ -154,18 +169,22 @@ namespace AgroBase.Services
int indiceRua = 1; 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; continue;
} }
IEnumerable<LineString> linhas = ExtrairLineStrings(featureShp.Geometry); IEnumerable<LineString> linhas =
ExtrairLineStrings(featureShp.Geometry);
foreach (LineString linha in linhas) foreach (LineString linha in linhas)
{ {
if (linha == null || linha.IsEmpty || linha.NumPoints < 2) if (linha == null ||
linha.IsEmpty ||
linha.NumPoints < 2)
{ {
continue; continue;
} }
@ -174,37 +193,49 @@ namespace AgroBase.Services
foreach (Coordinate coordenada in linha.Coordinates) 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 longitude = lonLat[0];
double latitude = lonLat[1]; double latitude = lonLat[1];
if (double.IsNaN(longitude) || if (
double.IsNaN(longitude) ||
double.IsInfinity(longitude) || double.IsInfinity(longitude) ||
double.IsNaN(latitude) || double.IsNaN(latitude) ||
double.IsInfinity(latitude) || double.IsInfinity(latitude) ||
longitude < -180 || longitude < -180 ||
longitude > 180 || longitude > 180 ||
latitude < -90 || latitude < -90 ||
latitude > 90) latitude > 90
)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
"O Shapefile produziu uma coordenada " + "O Shapefile produziu uma coordenada " +
$"inválida: {longitude}, {latitude}." $"inválida após transformação: " +
$"{longitude}, {latitude}."
); );
} }
coordenadas.Add(new List<double> coordenadas.Add(
{ new List<double>
longitude, {
latitude longitude,
}); latitude
}
);
} }
if (coordenadas.Count < 2) if (coordenadas.Count < 2)
continue; continue;
string id = indiceRua.ToString(CultureInfo.InvariantCulture); string id =
indiceRua.ToString(
CultureInfo.InvariantCulture
);
var properties = var properties =
new MapaFeaturePropertiesModel new MapaFeaturePropertiesModel
@ -314,62 +345,224 @@ namespace AgroBase.Services
". São aceitas somente linhas." ". São aceitas somente linhas."
); );
} }
private static Func<double, double, double[]> CriarTransformacaoParaWgs84(string caminhoShp) private static Func<double, double, double[]> CriarTransformacaoParaWgs84(string caminhoShp, IEnumerable<IFeature> 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 (!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 * O PRJ é válido.
* já parecerem longitude/latitude. * Usa a transformação normalmente.
*/ */
return (x, y) => return (x, y) =>
{ {
if (x < -180 || x > 180 || y < -90 || y > 90) double[] resultado =
{ transformacao.MathTransform.Transform(
throw new InvalidOperationException( new[] { x, y }
"O Shapefile não possui arquivo .prj e " +
"as coordenadas não estão em longitude/latitude."
); );
}
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) 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( throw new InvalidOperationException(
"Não foi possível interpretar ou reprojetar " + "O arquivo .prj do Shapefile não pôde ser interpretado " +
"o sistema de coordenadas definido no arquivo .prj.", "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 ex
); );
} }
} }
private static bool CoordenadasParecemLongitudeLatitude(IEnumerable<IFeature> 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) private static void ValidarArquivosShapefile(string caminhoShp)
{ {
string caminhoDbf = Path.ChangeExtension(caminhoShp, ".dbf"); string caminhoDbf = Path.ChangeExtension(caminhoShp, ".dbf");