agrobot_base/AgroBase/AgroBase/Services/MapasService.cs

1594 lines
57 KiB
C#

using AgroBase.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NetTopologySuite.Features;
using NetTopologySuite.Geometries;
using NetTopologySuite.IO.Esri;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;
namespace AgroBase.Services
{
public static class MapasVariaveisModel
{
public static string NomeArquivoHtml = "mapa_interativo_webview2.html";
public static string NomePastaLeaflet = "folium";
public static string NomeLeafletJs = "leaflet.js";
public static string NomeLeafletCss = "leaflet.css";
public static int IDMarcadorBase = 1;
public static int IDMarcadorRover = 2;
}
public class MapasService
{
private readonly string _caminhoSaida;
public string NomeArquivos { get; private set; }
public string CaminhoArquivo { get; private set; }
public string CaminhoHtml { get; private set; }
public MapaFeatureCollectionModel DadosMapa
{
get;
private set;
}
public bool Carregado
{
get;
private set;
}
public MapasService()
{
_caminhoSaida = Path.Combine(Variaveis.CaminhoSistema, PythonService.CaminhoGeral, PythonService.CaminhoLeitura);
Directory.CreateDirectory(_caminhoSaida);
CaminhoHtml = Path.Combine(_caminhoSaida, MapasVariaveisModel.NomeArquivoHtml);
}
public bool SelecionarECarregarMapa(IWin32Window owner = null)
{
using (OpenFileDialog dialog = new OpenFileDialog())
{
dialog.DefaultExt = ".json";
dialog.Filter =
"Arquivos de mapa (*.json;*.geojson;*.shp)|*.json;*.geojson;*.shp|" +
"GeoJSON (*.json;*.geojson)|*.json;*.geojson|" +
"Shapefile (*.shp)|*.shp";
dialog.FilterIndex = 1;
dialog.Title = "Selecionar arquivo de mapa";
DialogResult resultado =
owner != null
? dialog.ShowDialog(owner)
: dialog.ShowDialog();
if (resultado != DialogResult.OK)
return false;
return CarregarMapa(dialog.FileName);
}
}
public bool CarregarMapa(string caminhoArquivo)
{
Carregado = false;
DadosMapa = null;
if (string.IsNullOrWhiteSpace(caminhoArquivo))
return false;
if (!File.Exists(caminhoArquivo))
{
throw new FileNotFoundException("O arquivo de mapa não foi encontrado.", caminhoArquivo);
}
string extensao = Path.GetExtension(caminhoArquivo).ToLowerInvariant();
MapaFeatureCollectionModel dados;
switch (extensao)
{
case ".json":
case ".geojson":
dados = CarregarGeoJson(caminhoArquivo);
break;
case ".shp":
dados = CarregarShapefile(caminhoArquivo);
break;
default:
throw new InvalidOperationException(
$"Formato de mapa não suportado: {extensao}."
);
}
ValidarMapa(dados);
NormalizarMapa(dados);
CaminhoArquivo = caminhoArquivo;
NomeArquivos = Path.GetFileNameWithoutExtension(caminhoArquivo);
DadosMapa = dados;
CriarArquivoHtml();
Carregado = true;
return true;
}
private static MapaFeatureCollectionModel CarregarGeoJson(string caminhoArquivo)
{
string json = File.ReadAllText(caminhoArquivo, Encoding.UTF8);
var dados = JsonConvert.DeserializeObject<MapaFeatureCollectionModel>(json);
if (dados == null)
{
throw new InvalidOperationException("Não foi possível interpretar o JSON do mapa.");
}
return dados;
}
private static MapaFeatureCollectionModel CarregarShapefile(string caminhoShp)
{
ValidarArquivosShapefile(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
{
type = "FeatureCollection",
features = new List<MapaFeatureModel>()
};
int indiceRua = 1;
foreach (var featureShp in featuresShp)
{
if (featureShp?.Geometry == null ||
featureShp.Geometry.IsEmpty)
{
continue;
}
IEnumerable<LineString> linhas =
ExtrairLineStrings(featureShp.Geometry);
foreach (LineString linha in linhas)
{
if (linha == null ||
linha.IsEmpty ||
linha.NumPoints < 2)
{
continue;
}
var coordenadas = new List<List<double>>();
foreach (Coordinate coordenada in linha.Coordinates)
{
double[] lonLat =
transformar(
coordenada.X,
coordenada.Y
);
double longitude = lonLat[0];
double latitude = lonLat[1];
if (
double.IsNaN(longitude) ||
double.IsInfinity(longitude) ||
double.IsNaN(latitude) ||
double.IsInfinity(latitude) ||
longitude < -180 ||
longitude > 180 ||
latitude < -90 ||
latitude > 90
)
{
throw new InvalidOperationException(
"O Shapefile produziu uma coordenada " +
$"inválida após transformação: " +
$"{longitude}, {latitude}."
);
}
coordenadas.Add(
new List<double>
{
longitude,
latitude
}
);
}
if (coordenadas.Count < 2)
continue;
string id =
indiceRua.ToString(
CultureInfo.InvariantCulture
);
var properties =
new MapaFeaturePropertiesModel
{
Id = id,
Name =
ObterAtributoTexto(
featureShp.Attributes,
"Name",
"NAME",
"Nome",
"NOME"
) ?? $"Rua {id}",
Dist1 =
ObterAtributoDouble(
featureShp.Attributes,
"Dist1",
"DIST1"
),
Dist2 =
ObterAtributoDouble(
featureShp.Attributes,
"Dist2",
"DIST2"
),
Length =
ObterAtributoDouble(
featureShp.Attributes,
"Length",
"LENGTH",
"Comprimento",
"COMPRIMENT"
)
};
dados.features.Add(
new MapaFeatureModel
{
type = "Feature",
geometry =
new MapaFeatureGeometryModel
{
id = id,
type = "LineString",
coordinates = coordenadas
},
properties = properties
}
);
indiceRua++;
}
}
if (dados.features.Count == 0)
{
throw new InvalidOperationException(
"O Shapefile não possui linhas válidas."
);
}
return dados;
}
private static IEnumerable<LineString> ExtrairLineStrings(Geometry geometria)
{
if (geometria is LineString linha)
{
yield return linha;
yield break;
}
if (geometria is MultiLineString variasLinhas)
{
for (int i = 0; i < variasLinhas.NumGeometries; i++)
{
if (variasLinhas.GetGeometryN(i) is LineString parte)
{
yield return parte;
}
}
yield break;
}
if (geometria is GeometryCollection colecao)
{
for (int i = 0; i < colecao.NumGeometries; i++)
{
foreach (LineString parte in ExtrairLineStrings(colecao.GetGeometryN(i)))
{
yield return parte;
}
}
yield break;
}
throw new InvalidOperationException(
"O Shapefile contém uma geometria não suportada: " +
geometria.GeometryType +
". São aceitas somente linhas."
);
}
private static Func<double, double, double[]> CriarTransformacaoParaWgs84(string caminhoShp, IEnumerable<IFeature> features)
{
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."
);
}
/*
* O PRJ é válido.
* Usa a transformação normalmente.
*/
return (x, y) =>
{
double[] resultado =
transformacao.MathTransform.Transform(
new[] { x, y }
);
return resultado;
};
}
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(
"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<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)
{
string caminhoDbf = Path.ChangeExtension(caminhoShp, ".dbf");
if (!File.Exists(caminhoDbf))
{
throw new FileNotFoundException(
"O arquivo .dbf correspondente ao Shapefile " +
"não foi encontrado.",
caminhoDbf
);
}
/*
* O .prj é tratado no método de transformação:
* sem ele, somente coordenadas que já estejam dentro
* dos limites de longitude/latitude serão aceitas.
*/
}
private static object ObterAtributo(IAttributesTable atributos, params string[] nomes)
{
if (atributos == null)
return null;
string[] nomesExistentes = atributos.GetNames();
foreach (string nomeDesejado in nomes)
{
string nomeEncontrado =
nomesExistentes.FirstOrDefault(
x => string.Equals(
x,
nomeDesejado,
StringComparison.OrdinalIgnoreCase
)
);
if (!string.IsNullOrWhiteSpace(nomeEncontrado))
{
object valor = atributos[nomeEncontrado];
if (valor != null && valor != DBNull.Value)
{
return valor;
}
}
}
return null;
}
private static string ObterAtributoTexto(IAttributesTable atributos, params string[] nomes)
{
return ObterAtributo(atributos, nomes)?.ToString();
}
private static double ObterAtributoDouble(IAttributesTable atributos, params string[] nomes)
{
object valor = ObterAtributo(atributos, nomes);
if (valor == null)
return 0;
if (valor is IConvertible)
{
try
{
return Convert.ToDouble(valor, CultureInfo.InvariantCulture);
}
catch
{
// Tenta interpretar como texto abaixo.
}
}
string texto = valor.ToString();
if (double.TryParse(texto, NumberStyles.Any, CultureInfo.InvariantCulture, out double resultado))
{
return resultado;
}
if (double.TryParse(texto, NumberStyles.Any, CultureInfo.GetCultureInfo("pt-BR"), out resultado))
{
return resultado;
}
return 0;
}
public void DefinirMapa(MapaFeatureCollectionModel dados, string nomeMapa = "Mapa")
{
Carregado = false;
DadosMapa = null;
ValidarMapa(dados);
NormalizarMapa(dados);
DadosMapa = dados;
NomeArquivos = nomeMapa;
CaminhoArquivo = null;
CriarArquivoHtml();
Carregado = true;
}
public string ObterGeoJson()
{
if (DadosMapa == null)
return null;
return JsonConvert.SerializeObject(DadosMapa, Formatting.None);
}
public void Limpar()
{
NomeArquivos = null;
CaminhoArquivo = null;
DadosMapa = null;
Carregado = false;
}
private static void ValidarMapa(MapaFeatureCollectionModel dados)
{
if (dados == null)
{
throw new InvalidOperationException(
"O conteúdo do mapa é inválido."
);
}
if (dados.features == null)
{
throw new InvalidOperationException(
"O mapa não possui uma lista de features."
);
}
if (dados.features.Count == 0)
{
throw new InvalidOperationException(
"O mapa não possui ruas."
);
}
for (int i = 0; i < dados.features.Count; i++)
{
MapaFeatureModel feature = dados.features[i];
if (feature == null || feature.geometry == null)
{
throw new InvalidOperationException(
"A rua " +
(i + 1) +
" possui geometria inválida."
);
}
if (!string.Equals(feature.geometry.type, "LineString", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
"A rua " +
(i + 1) +
" não é do tipo LineString."
);
}
if (feature.geometry.coordinates == null || feature.geometry.coordinates.Count < 2)
{
throw new InvalidOperationException(
"A rua " +
(i + 1) +
" não possui pontos suficientes."
);
}
}
}
private static void NormalizarMapa(MapaFeatureCollectionModel dados)
{
dados.type = string.IsNullOrWhiteSpace(dados.type)
? "FeatureCollection"
: dados.type;
var idsUtilizados = new HashSet<string>(StringComparer.Ordinal);
for (int i = 0; i < dados.features.Count; i++)
{
MapaFeatureModel feature = dados.features[i];
feature.type = string.IsNullOrWhiteSpace(feature.type)
? "Feature"
: feature.type;
if (feature.properties == null)
feature.properties = new MapaFeaturePropertiesModel();
string id = feature.properties.Id?.Trim();
// Mapa sem ID explícito:
// gera um ID numérico sequencial.
if (string.IsNullOrWhiteSpace(id))
{
id = (i + 1).ToString(
CultureInfo.InvariantCulture
);
}
if (!idsUtilizados.Add(id))
{
throw new InvalidOperationException(
$"O mapa possui ID de rua duplicado: '{id}'."
);
}
// Uma única identidade em todo o sistema.
feature.properties.Id = id;
feature.geometry.id = id;
}
}
public void CriarArquivoHtml()
{
string html = ObterHtmlMapa();
File.WriteAllText(CaminhoHtml, html, new UTF8Encoding(false));
}
private static string ObterHtmlMapa()
{
return @"<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<meta
name='viewport'
content='width=device-width,
initial-scale=1.0,
maximum-scale=1.0,
user-scalable=no' />
<link
rel='stylesheet'
href='folium/leaflet.css' />
<script src='folium/leaflet.js'></script>
<style>
html,
body,
#mapa {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
background: #121d16;
}
.leaflet-container {
background: #121d16;
font-family: Segoe UI, Arial, sans-serif;
}
.leaflet-control-zoom a {
background: #1b2a20;
color: #d7e5da;
border-color: #314538;
}
.leaflet-control-attribution {
display: none;
}
.rua-tooltip {
background: #17251c;
color: #d7e5da;
border: 1px solid #42664e;
box-shadow: none;
font-size: 11px;
font-weight: bold;
}
</style>
</head>
<body>
<div id='mapa'></div>
<script>
const COR_NORMAL = '#5b9bd5';
const COR_SELECIONADA = '#26c65a';
let mapa = null;
let camadaRuas = null;
let ruasSelecionadas = [];
let marcadorRover = null;
let marcadorBase = null;
let ultimaPosicaoRover = null;
let camadaTrajetoriaPlanejada = null;
let camadaRastroRover = null;
let camadaRastroBase = null;
let camadaTrajetoGps = null;
let pontosTrajetoGps = [];
let pontosRastroRover = [];
let pontosRastroBase = [];
let ultimaPosicaoBase = null;
let selecaoRuasHabilitada = false;
function inicializarMapa() {
if (mapa !== null) {
return;
}
mapa = L.map('mapa', {
zoomControl: true,
attributionControl: false,
preferCanvas: true
});
mapa.setView([0, 0], 18);
}
function definirSelecaoRuasHabilitada(habilitada) {
selecaoRuasHabilitada = habilitada === true;
if (!camadaRuas) {
return;
}
camadaRuas.eachLayer(function(layer) {
if (selecaoRuasHabilitada) {
layer.options.interactive = true;
if (layer._path) {
layer._path.style.cursor = 'pointer';
}
}
else {
layer.options.interactive = false;
if (layer._path) {
layer._path.style.cursor = 'default';
}
}
});
}
function obterIdRua(feature, indice) {
if (
feature &&
feature.properties &&
feature.properties.Id !== undefined &&
feature.properties.Id !== null
) {
return String(feature.properties.Id);
}
if (
feature &&
feature.geometry &&
feature.geometry.id !== undefined &&
feature.geometry.id !== null
) {
return String(feature.geometry.id);
}
if (
feature &&
feature.id !== undefined &&
feature.id !== null
) {
return String(feature.id);
}
return String(indice + 1);
}
function ruaSelecionada(id) {
return ruasSelecionadas.indexOf(String(id)) >= 0;
}
function obterEstiloRua(id) {
const selecionada = ruaSelecionada(id);
return {
color: selecionada
? COR_SELECIONADA
: COR_NORMAL,
weight: selecionada
? 8
: 6,
opacity: selecaoRuasHabilitada
? 1
: 0.8,
lineCap: 'round',
lineJoin: 'round'
};
}
function atualizarEstilos() {
if (!camadaRuas) {
return;
}
camadaRuas.eachLayer(function(layer) {
const id = layer.__ruaId;
if (id === undefined) {
return;
}
layer.setStyle(
obterEstiloRua(id)
);
});
}
function alternarRua(id) {
id = String(id);
const indice =
ruasSelecionadas.indexOf(id);
if (indice >= 0) {
ruasSelecionadas.splice(
indice,
1
);
}
else {
ruasSelecionadas.push(id);
}
atualizarEstilos();
enviarSelecaoParaCSharp();
}
function enviarSelecaoParaCSharp() {
if (
window.chrome &&
window.chrome.webview
) {
window.chrome.webview.postMessage({
tipo: 'selecaoRuas',
ruas: ruasSelecionadas
});
}
}
function carregarMapa(
dadosMapa,
selecaoInicial,
permitirSelecao
) {
inicializarMapa();
selecaoRuasHabilitada = permitirSelecao === true;
if (typeof dadosMapa === 'string') {
dadosMapa =
JSON.parse(dadosMapa);
}
ruasSelecionadas =
Array.isArray(selecaoInicial)
? selecaoInicial.map(String)
: [];
if (camadaRuas !== null) {
mapa.removeLayer(camadaRuas);
camadaRuas = null;
}
let indiceFeature = 0;
camadaRuas = L.geoJSON(
dadosMapa,
{
style: function(feature) {
const id =
obterIdRua(
feature,
indiceFeature
);
indiceFeature++;
return obterEstiloRua(id);
},
onEachFeature:
function(feature, layer) {
const indice =
camadaRuas
? camadaRuas
.getLayers()
.length
: 0;
const id =
obterIdRua(
feature,
indice
);
layer.__ruaId =
String(id);
let titulo =
'Rua ' +
(
feature.properties &&
feature.properties.Id
? feature.properties.Id
: id
);
if (
feature.properties &&
feature.properties.Length
) {
titulo +=
' · ' +
Number(
feature.properties.Length
).toFixed(1) +
' m';
}
layer.bindTooltip(
titulo,
{
sticky: true,
className:
'rua-tooltip'
}
);
layer.on(
'click',
function() {
if (!selecaoRuasHabilitada) {
return;
}
alternarRua(id);
}
);
}
}
);
camadaRuas.addTo(mapa);
atualizarEstilos();
const limites =
camadaRuas.getBounds();
if (limites.isValid()) {
mapa.fitBounds(
limites,
{
padding: [18, 18],
maxZoom: 21
}
);
}
setTimeout(function() {
mapa.invalidateSize();
}, 50);
enviarMensagemMapaCarregado();
}
function definirSelecaoRuas(ids) {
ruasSelecionadas =
Array.isArray(ids)
? ids.map(String)
: [];
atualizarEstilos();
}
function definirTrajetoriaPlanejada(pontos) {
inicializarMapa();
if (camadaTrajetoriaPlanejada !== null) {
mapa.removeLayer(camadaTrajetoriaPlanejada);
camadaTrajetoriaPlanejada = null;
}
if (!Array.isArray(pontos) || pontos.length < 2) {
return;
}
const coordenadas = pontos
.map(function(ponto) {
return [
Number(ponto.latitude),
Number(ponto.longitude)
];
})
.filter(function(ponto) {
return (
Number.isFinite(ponto[0]) &&
Number.isFinite(ponto[1])
);
});
if (coordenadas.length < 2) {
return;
}
camadaTrajetoriaPlanejada = L.polyline(
coordenadas,
{
color: '#f5c542',
weight: 5,
opacity: 0.95,
dashArray: '10, 7',
lineCap: 'round',
lineJoin: 'round',
interactive: false
}
).addTo(mapa);
}
function obterSelecaoRuas() {
return ruasSelecionadas.slice();
}
function limparMapa() {
ruasSelecionadas = [];
if (
mapa !== null &&
camadaRuas !== null
) {
mapa.removeLayer(camadaRuas);
}
camadaRuas = null;
}
function redimensionarMapa() {
if (mapa !== null) {
mapa.invalidateSize();
}
}
function enviarMensagemMapaCarregado() {
if (
window.chrome &&
window.chrome.webview
) {
window.chrome.webview.postMessage({
tipo: 'mapaCarregado'
});
}
}
function criarIconeRover(orientacao) {
return L.divIcon({
className: '',
iconSize: [30, 30],
iconAnchor: [15, 15],
html:
'<div style=""' +
'width:30px;' +
'height:30px;' +
'display:flex;' +
'align-items:center;' +
'justify-content:center;' +
'transform:rotate(' + orientacao + 'deg);' +
'transform-origin:center;' +
'font-size:25px;' +
'color:#26c65a;' +
'text-shadow:0 0 3px #000;' +
'"">' +
'▲' +
'</div>'
});
}
function criarIconeBase() {
return L.divIcon({
className: '',
iconSize: [24, 24],
iconAnchor: [12, 12],
html:
'<div style=""' +
'width:18px;' +
'height:18px;' +
'border-radius:50%;' +
'background:#e34b4b;' +
'border:3px solid white;' +
'box-shadow:0 0 4px #000;' +
'"">' +
'</div>'
});
}
function inicializarModoGps() {
inicializarMapa();
pontosTrajetoGps = [];
ultimaPosicaoRover = null;
if (camadaRuas !== null) {
mapa.removeLayer(camadaRuas);
camadaRuas = null;
}
if (camadaTrajetoGps !== null) {
mapa.removeLayer(camadaTrajetoGps);
}
camadaTrajetoGps = L.polyline(
[],
{
color: '#ef5350',
weight: 4,
opacity: 0.9,
lineCap: 'round',
lineJoin: 'round',
interactive: false
}
).addTo(mapa);
if (marcadorRover !== null) {
mapa.removeLayer(marcadorRover);
marcadorRover = null;
}
if (marcadorBase !== null) {
mapa.removeLayer(marcadorBase);
marcadorBase = null;
}
}
function coordenadasDiferentes(a, b) {
if (!a || !b) {
return true;
}
const tolerancia = 0.000001;
return (
Math.abs(a[0] - b[0]) > tolerancia ||
Math.abs(a[1] - b[1]) > tolerancia
);
}
function garantirCamadaRastroRover() {
if (camadaRastroRover !== null) {
return;
}
camadaRastroRover = L.polyline(
[],
{
color: '#ef5350',
weight: 4,
opacity: 0.9,
interactive: false
}
).addTo(mapa);
}
function garantirCamadaRastroBase() {
if (camadaRastroBase !== null) {
return;
}
camadaRastroBase = L.polyline(
[],
{
color: '#b36cff',
weight: 3,
opacity: 0.85,
interactive: false
}
).addTo(mapa);
}
function atualizarPosicaoGps(
id,
latitude,
longitude,
orientacao,
foco
) {
inicializarMapa();
const posicao = [
Number(latitude),
Number(longitude)
];
if (id === " + MapasVariaveisModel.IDMarcadorBase + @") {
garantirCamadaRastroBase();
if (coordenadasDiferentes(ultimaPosicaoBase, posicao)) {
pontosRastroBase.push([posicao[0], posicao[1]]);
camadaRastroBase.setLatLngs(pontosRastroBase);
ultimaPosicaoBase = [posicao[0], posicao[1]];
}
if (marcadorBase === null) {
marcadorBase = L.marker(
posicao,
{
icon: criarIconeBase()
}
).addTo(mapa);
}
else {
marcadorBase.setLatLng(posicao);
}
}
else {
garantirCamadaRastroRover();
if (coordenadasDiferentes(ultimaPosicaoRover, posicao)) {
pontosRastroRover.push([posicao[0], posicao[1]]);
camadaRastroRover.setLatLngs(pontosRastroRover);
ultimaPosicaoRover =[posicao[0], posicao[1]];
}
if (marcadorRover === null) {
marcadorRover = L.marker(
posicao,
{
icon:
criarIconeRover(
Number(orientacao)
)
}
).addTo(mapa);
}
else {
marcadorRover.setLatLng(posicao);
marcadorRover.setIcon(
criarIconeRover(
Number(orientacao)
)
);
}
if (
coordenadasDiferentes(
ultimaPosicaoRover,
posicao
)
) {
pontosTrajetoGps.push(posicao);
camadaTrajetoGps.setLatLngs(
pontosTrajetoGps
);
ultimaPosicaoRover =
posicao;
}
}
if (foco) {
mapa.setView(
posicao,
mapa.getZoom() < 18
? 18
: mapa.getZoom()
);
}
}
function limparRastroRover() {
pontosRastroRover = [];
ultimaPosicaoRover = null;
if (camadaRastroRover !== null) {
camadaRastroRover.setLatLngs([]);
}
}
function limparRastroBase() {
pontosRastroBase = [];
ultimaPosicaoBase = null;
if (camadaRastroBase !== null) {
camadaRastroBase.setLatLngs([]);
}
}
function limparTodosOsRastros() {
limparRastroRover();
limparRastroBase();
}
function limparTrajetoriaPlanejada() {
if (
mapa !== null &&
camadaTrajetoriaPlanejada !== null
) {
mapa.removeLayer(
camadaTrajetoriaPlanejada
);
}
camadaTrajetoriaPlanejada = null;
}
inicializarMapa();
</script>
</body>
</html>";
}
public static void CriarArquivoMapa(List<List<GPSModel>> trajetoria, int id = 1517, string name = "Projeto", double dist1 = 0.0, double dist2 = 0.0)
{
MapaFeatureCollectionModel featureCollection = ConverterPontosParaMapa(trajetoria, id, name, dist1, dist2);
string json = JsonConvert.SerializeObject(featureCollection, Formatting.None);
if (!Directory.Exists(Variaveis.CaminhoMapasConvertidos))
{
Directory.CreateDirectory(Variaveis.CaminhoMapasConvertidos);
}
string caminho = Path.Combine(Variaveis.CaminhoMapasConvertidos, name + ".json");
File.WriteAllText(caminho, json, new UTF8Encoding(false));
}
public static MapaFeatureCollectionModel ConverterPontosParaMapa(List<List<GPSModel>> trajetoria, int id = 1517, string name = "Projeto", double dist1 = 0.0, double dist2 = 0.0)
{
MapaFeatureCollectionModel featureCollection =
new MapaFeatureCollectionModel
{
type = "FeatureCollection",
features = new List<MapaFeatureModel>()
};
if (trajetoria == null)
return featureCollection;
for (int i = 0; i < trajetoria.Count; i++)
{
List<GPSModel> rua = trajetoria[i];
if (rua == null || rua.Count == 0)
continue;
double comprimento = GPSUtils.DistanciaDoTrecho(rua);
MapaFeatureModel feature =
new MapaFeatureModel
{
type = "Feature",
properties =
new MapaFeaturePropertiesModel
{
Id = (i + 1).ToString(),
Name = name,
Length = comprimento,
Dist1 = dist1,
Dist2 = dist2
},
geometry =
new MapaFeatureGeometryModel
{
id = i.ToString(),
type = "LineString",
coordinates =
new List<List<double>>()
}
};
foreach (GPSModel ponto in rua)
{
if (ponto == null)
continue;
feature.geometry.coordinates.Add(
new List<double>
{
ponto.Longitude,
ponto.Latitude
}
);
}
/*
* Uma LineString precisa de pelo menos dois pontos.
*/
if (feature.geometry.coordinates.Count >= 2)
{
featureCollection.features.Add(feature);
}
}
/*
* Normaliza os IDs internos depois de ignorar ruas inválidas.
*/
for (int i = 0; i < featureCollection.features.Count; i++)
{
featureCollection.features[i].geometry.id = i.ToString();
}
return featureCollection;
}
}
}