using Mapsui; using Mapsui.Layers; using Mapsui.Nts; using Mapsui.Projections; using Mapsui.Providers; using Mapsui.Styles; using NetTopologySuite.Geometries; using OperationControl.Mapas.Editing; using Color = Mapsui.Styles.Color; using Pen = Mapsui.Styles.Pen; namespace OperationControl.Mapas.Rendering { public sealed class MapaEdicaoRenderService { public const string NomeCamada = "MapaEdicaoNovo"; private static readonly VectorStyle EstiloNormal = new() { Line = new Pen( new Color( 46, 204, 113, 210 ), 2 ) }; /* * Cor própria da edição. * * Não confundir com o amarelo usado * na seleção operacional da missão. */ private static readonly VectorStyle EstiloSelecionado = new() { Line = new Pen( new Color( 255, 64, 190, 255 ), 5 ) }; public MapaRenderResult CriarCamada( MapaEstadoEdicao estado) { if (estado == null) { throw new ArgumentNullException( nameof(estado) ); } if ( !estado.EmEdicao || estado.DocumentoTrabalho == null ) { throw new InvalidOperationException( "Não existe uma sessão de edição ativa." ); } var featuresPorRuaId = new Dictionary< string, GeometryFeature>( StringComparer.OrdinalIgnoreCase ); var features = new List(); foreach ( var rua in estado.DocumentoTrabalho.Ruas) { if ( rua == null || rua.Geometria == null || rua.Geometria.IsEmpty || rua.Geometria.NumPoints < 2 ) { continue; } /* * Documento canônico: * * EPSG:4326 * X = longitude * Y = latitude * * Mapsui: * * desenhamos diretamente em * WebMercator EPSG:3857. */ var coordenadas = rua.Geometria .Coordinates .Select( coordenada => { var mercator = SphericalMercator .FromLonLat( coordenada.X, coordenada.Y ); return new Coordinate( mercator.x, mercator.y ); } ) .ToArray(); if (coordenadas.Length < 2) continue; var feature = new GeometryFeature { Geometry = new LineString( coordenadas ) }; /* * Mantemos os mesmos atributos * esperados pelo hit-test atual. */ feature["RuaId"] = rua.Id; feature["Id"] = rua.Id; feature["Tipo"] = "RuaEdicao"; bool selecionada = EstaSelecionada( estado, rua.Id ); feature .Styles .Add( selecionada ? EstiloSelecionado : EstiloNormal ); features.Add( feature ); featuresPorRuaId[ rua.Id ] = feature; } /* * MapaRenderResult trabalha com Layer, * portanto seguimos exatamente esse * contrato. */ var provider = new MemoryProvider( features ); var camada = new Layer { Name = NomeCamada, DataSource = provider, /* * Cada feature possui seu * próprio estilo. */ Style = null }; /* * IMPORTANTE: * * MapaRenderResult NÃO possui * construtor vazio. * * Layer e dicionário entram pelo * construtor obrigatório. */ return new MapaRenderResult( camada, featuresPorRuaId ); } public void AtualizarEstiloRua( MapaRenderResult render, MapaEstadoEdicao estado, string ruaId) { if ( render == null || estado == null || string.IsNullOrWhiteSpace( ruaId ) ) { return; } /* * Aproveitamos a API que já existe * no próprio MapaRenderResult. */ var feature = render.ObterFeature( ruaId ); if (feature == null) return; bool selecionada = EstaSelecionada( estado, ruaId ); feature .Styles .Clear(); feature .Styles .Add( selecionada ? EstiloSelecionado : EstiloNormal ); } public void AplicarSelecao( MapaRenderResult render, MapaEstadoEdicao estado) { if ( render == null || estado == null ) { return; } foreach ( var item in render.FeaturesPorRuaId) { bool selecionada = EstaSelecionada( estado, item.Key ); item.Value .Styles .Clear(); item.Value .Styles .Add( selecionada ? EstiloSelecionado : EstiloNormal ); } } private static bool EstaSelecionada( MapaEstadoEdicao estado, string ruaId) { if ( string.IsNullOrWhiteSpace( ruaId ) ) { return false; } return estado .RuasSelecionadas .Any( id => string.Equals( id, ruaId, StringComparison .OrdinalIgnoreCase ) ); } } }