agrobot_base/AgroBase/OperationControl/Mapas/Rendering/MapaRastroRenderService.cs

287 lines
7.0 KiB
C#

using Mapsui;
using Mapsui.Layers;
using Mapsui.Nts;
using Mapsui.Projections;
using Mapsui.Styles;
using NetTopologySuite.Geometries;
using OperationControl.Mapas.Models;
using Color = Mapsui.Styles.Color;
using Pen = Mapsui.Styles.Pen;
namespace OperationControl.Mapas.Rendering
{
public sealed class MapaRastroRenderService
{
public const string NomeCamada =
"MapaRastroNovo";
/*
* Mesma ideia da paleta do legado:
* cada RoverId recebe uma cor estável.
*
* A cor é responsabilidade de render,
* portanto não precisa entrar no domínio.
*/
private static readonly Color[] Paleta =
{
new Color(0, 191, 255, 255), // DeepSkyBlue
new Color(50, 205, 50, 255), // LimeGreen
new Color(255, 215, 0, 255), // Gold
new Color(255, 165, 0, 255), // Orange
new Color(147, 112, 219, 255), // MediumPurple
new Color(0, 255, 255, 255), // Cyan
new Color(255, 0, 255, 255), // Magenta
new Color(0, 255, 127, 255), // SpringGreen
new Color(255, 127, 80, 255), // Coral
new Color(255, 255, 0, 255) // Yellow
};
public MemoryLayer CriarCamada(
MapaEstadoMovimento estado)
{
if (estado == null)
{
throw new ArgumentNullException(
nameof(estado)
);
}
return new MemoryLayer
{
Name = NomeCamada,
Features = CriarFeatures(
estado
),
Style = null
};
}
public void AtualizarCamada(
MemoryLayer camada,
MapaEstadoMovimento estado)
{
if (camada == null)
{
throw new ArgumentNullException(
nameof(camada)
);
}
if (estado == null)
{
throw new ArgumentNullException(
nameof(estado)
);
}
camada.Features =
CriarFeatures(
estado
);
}
private static List<IFeature>
CriarFeatures(
MapaEstadoMovimento estado)
{
var features =
new List<IFeature>();
foreach (
var movimento
in estado.Rovers)
{
Color corRover =
ObterCorRover(
movimento.RoverId
);
foreach (
var trecho
in movimento.Trechos)
{
if (
trecho == null ||
trecho.Pontos.Count < 2
)
{
continue;
}
var coordenadas =
new List<Coordinate>();
foreach (
var ponto
in trecho.Pontos)
{
var mercator =
SphericalMercator
.FromLonLat(
ponto.Longitude,
ponto.Latitude
);
coordenadas.Add(
new Coordinate(
mercator.x,
mercator.y
)
);
}
if (
coordenadas.Count < 2
)
{
continue;
}
var feature =
new GeometryFeature
{
Geometry =
new LineString(
coordenadas
.ToArray()
)
};
feature["RoverId"] =
movimento.RoverId;
feature["Tipo"] =
"Rastro";
feature["Pulverizando"] =
trecho.Pulverizando
? "1"
: "0";
feature.Styles.Add(
CriarEstilo(
corRover,
trecho.Pulverizando
)
);
features.Add(
feature
);
}
}
return features;
}
private static VectorStyle CriarEstilo(
Color cor,
bool pulverizando)
{
/*
* Mantemos a semântica visual do legado:
*
* normal:
* mais transparente e fino
*
* pulverizando:
* opaco e mais grosso
*/
Color corLinha =
pulverizando
? new Color(
cor.R,
cor.G,
cor.B,
255
)
: new Color(
cor.R,
cor.G,
cor.B,
180
);
return new VectorStyle
{
Line =
new Pen(
corLinha,
pulverizando
? 4
: 2
)
};
}
private static Color ObterCorRover(
string roverId)
{
if (
string.IsNullOrWhiteSpace(
roverId
)
)
{
return Paleta[0];
}
/*
* Hash determinístico.
*
* Não usamos string.GetHashCode()
* porque ele não precisa ser estável
* entre execuções do processo.
*/
uint hash =
2166136261;
foreach (
char caractere
in roverId
.Trim()
.ToUpperInvariant())
{
unchecked
{
hash ^= caractere;
hash *= 16777619;
}
}
int indice =
(int)(
hash %
(uint)Paleta.Length
);
return Paleta[
indice
];
}
}
}