agrobot_base/AgroBase/OperationControl/Mapas/Rendering/MapaEntidadeDinamicaRenderS...

606 lines
14 KiB
C#

using Mapsui;
using Mapsui.Layers;
using Mapsui.Nts;
using Mapsui.Styles;
using NetTopologySuite.Geometries;
using OperationControl.Mapas.Models;
using Brush = Mapsui.Styles.Brush;
using Font = Mapsui.Styles.Font;
using Pen = Mapsui.Styles.Pen;
namespace OperationControl.Mapas.Rendering
{
/// <summary>
/// Converte o estado dinâmico de Base/Rovers
/// em representação visual Mapsui.
///
/// Não armazena estado operacional.
/// </summary>
public sealed class MapaEntidadeDinamicaRenderService
{
public const string NomeCamada =
"MapaEntidadesDinamicasNovo";
public MemoryLayer CriarCamada(
MapaEstadoDinamico estado,
double resolution)
{
if (estado == null)
{
throw new ArgumentNullException(
nameof(estado)
);
}
return new MemoryLayer
{
Name = NomeCamada,
Features =
CriarFeatures(
estado,
resolution
)
};
}
public void AtualizarCamada(
MemoryLayer camada,
MapaEstadoDinamico estado,
double resolution)
{
if (camada == null)
{
throw new ArgumentNullException(
nameof(camada)
);
}
if (estado == null)
{
throw new ArgumentNullException(
nameof(estado)
);
}
camada.Features =
CriarFeatures(
estado,
resolution
);
}
private static IEnumerable<IFeature>
CriarFeatures(
MapaEstadoDinamico estado,
double resolution)
{
var features =
new List<IFeature>();
if (
!double.IsFinite(resolution) ||
resolution <= 0
)
{
resolution =
1.0;
}
foreach (
var entidade
in estado.Entidades)
{
if (!entidade.PossuiPosicao)
continue;
double latitude =
entidade.Latitude!.Value;
double longitude =
entidade.Longitude!.Value;
var mercator =
Mapsui.Projections
.SphericalMercator
.FromLonLat(
longitude,
latitude
);
var centro =
new MPoint(
mercator.x,
mercator.y
);
if (
entidade.Tipo ==
MapaTipoEntidadeDinamica.Base
)
{
CriarBase(
entidade,
centro,
resolution,
features
);
}
else
{
CriarRover(
entidade,
centro,
resolution,
features
);
}
/*
* Label e ponto central para futura
* identificação/hit-test.
*/
CriarCentroELabel(
entidade,
centro,
features
);
}
return features;
}
private static void CriarRover(
MapaEntidadeDinamica entidade,
MPoint centro,
double resolution,
List<IFeature> features)
{
/*
* Aproximadamente 24 px no zoom atual.
*/
double comprimento =
24.0 *
resolution;
double largura =
comprimento *
0.75;
double frente =
comprimento *
0.55;
double traseira =
comprimento *
0.45;
double meiaLargura =
largura *
0.5;
/*
* Convenção:
*
* 0° = Norte
* 90° = Leste
* 180° = Sul
* 270° = Oeste
*/
double heading =
entidade.Heading
?? 0.0;
double rad =
heading *
Math.PI /
180.0;
double ux =
Math.Sin(rad);
double uy =
Math.Cos(rad);
double px =
Math.Cos(rad);
double py =
-Math.Sin(rad);
var front =
new Coordinate(
centro.X +
ux * frente,
centro.Y +
uy * frente
);
var left =
new Coordinate(
centro.X -
ux * traseira +
px * meiaLargura,
centro.Y -
uy * traseira +
py * meiaLargura
);
var right =
new Coordinate(
centro.X -
ux * traseira -
px * meiaLargura,
centro.Y -
uy * traseira -
py * meiaLargura
);
var ring =
new LinearRing(
new[]
{
front,
left,
right,
front
}
);
var feature =
new GeometryFeature
{
Geometry =
new Polygon(
ring
)
};
feature["EntidadeId"] =
entidade.Id;
feature["Tipo"] =
"Rover";
var cor =
new Mapsui.Styles.Color(
45,
140,
220,
255
);
feature.Styles.Add(
new VectorStyle
{
Fill =
new Brush(
cor
),
Outline =
new Pen(
Mapsui.Styles.Color.White,
entidade.EmFoco
? 3
: 2
)
}
);
features.Add(
feature
);
/*
* Halo de foco.
*/
if (entidade.EmFoco)
{
var foco =
new PointFeature(
centro
);
foco["EntidadeId"] =
entidade.Id;
foco.Styles.Add(
new SymbolStyle
{
SymbolType =
SymbolType.Ellipse,
SymbolScale =
1.35,
Fill =
new Brush(
new Mapsui.Styles.Color(
cor.R,
cor.G,
cor.B,
40
)
),
Outline =
new Pen(
new Mapsui.Styles.Color(
cor.R,
cor.G,
cor.B,
180
),
2
)
}
);
features.Add(
foco
);
}
}
private static void CriarBase(
MapaEntidadeDinamica entidade,
MPoint centro,
double resolution,
List<IFeature> features)
{
double raio =
22.0 *
resolution;
var coords =
new List<Coordinate>();
/*
* Hexágono.
*/
for (
int i = 0;
i < 6;
i++)
{
double ang =
Math.PI / 6.0 +
i *
Math.PI / 3.0;
coords.Add(
new Coordinate(
centro.X +
Math.Cos(ang) *
raio,
centro.Y +
Math.Sin(ang) *
raio
)
);
}
coords.Add(
coords[0]
);
var feature =
new GeometryFeature
{
Geometry =
new Polygon(
new LinearRing(
coords.ToArray()
)
)
};
feature["EntidadeId"] =
entidade.Id;
feature["Tipo"] =
"Base";
var cor =
new Mapsui.Styles.Color(
220,
45,
45,
255
);
feature.Styles.Add(
new VectorStyle
{
Fill =
new Brush(
cor
),
Outline =
new Mapsui.Styles.Pen(
Mapsui.Styles.Color.White,
entidade.EmFoco
? 3
: 2
)
}
);
features.Add(
feature
);
/*
* Centro branco da base.
*/
var centroFeature =
new PointFeature(
centro
);
centroFeature["EntidadeId"] =
entidade.Id;
centroFeature.Styles.Add(
new SymbolStyle
{
SymbolType =
SymbolType.Ellipse,
SymbolScale =
0.45,
Fill =
new Brush(
Mapsui.Styles.Color.White
),
Outline =
null
}
);
features.Add(
centroFeature
);
}
private static void CriarCentroELabel(
MapaEntidadeDinamica entidade,
MPoint centro,
List<IFeature> features)
{
var ponto =
new PointFeature(
centro
);
/*
* Isso será muito útil no 7C:
*
* feature visual -> EntidadeId
*/
ponto["EntidadeId"] =
entidade.Id;
ponto["Tipo"] =
entidade.Tipo.ToString();
/*
* Pequeno ponto central.
*/
ponto.Styles.Add(
new SymbolStyle
{
SymbolScale =
0.10,
Fill =
new Brush(
Mapsui.Styles.Color.White
),
Outline =
null
}
);
ponto.Styles.Add(
new LabelStyle
{
Text =
entidade.Nome,
ForeColor =
Mapsui.Styles.Color.White,
BackColor =
new Brush(
new Mapsui.Styles.Color(
0,
0,
0,
150
)
),
Offset =
new Offset(
0,
-34
),
Font =
new Font
{
FontFamily =
"Segoe UI",
Size =
15
}
}
);
features.Add(
ponto
);
}
}
}