845 lines
23 KiB
C#
845 lines
23 KiB
C#
using NetTopologySuite.Geometries;
|
|
using OperationControl.Mapas.Models;
|
|
using OperationControl.Mapas.Services;
|
|
using System.Globalization;
|
|
using static AgroBase.Models.Enums;
|
|
|
|
namespace OperationControl.Mapas.Creation
|
|
{
|
|
/// <summary>
|
|
/// Backend puro da criação de mapas por levantamento GNSS.
|
|
///
|
|
/// Não conhece WPF, Mapsui, Dock, Rover ou GpsService.
|
|
/// A futura camada Controller entrega snapshots GNSS válidos
|
|
/// e pede operações a este serviço.
|
|
/// </summary>
|
|
public sealed class MapaCriacaoService
|
|
{
|
|
private const double DistanciaMinimaPontoManualMetros = 0.02;
|
|
|
|
private readonly MapaValidationService _validationService;
|
|
|
|
private readonly GeometryFactory _geometryFactory =
|
|
new(
|
|
new PrecisionModel(),
|
|
4326
|
|
);
|
|
|
|
public MapaCriacaoService(
|
|
MapaValidationService? validationService = null)
|
|
{
|
|
_validationService =
|
|
validationService
|
|
?? new MapaValidationService();
|
|
}
|
|
|
|
|
|
public void Iniciar(
|
|
MapaEstadoCriacao estado,
|
|
string? nome,
|
|
TipoMapaOperacao tipo,
|
|
double distanciaMinimaAutomaticaMetros = 0.50)
|
|
{
|
|
ValidarEstado(estado);
|
|
|
|
if (estado.EmCriacao)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Já existe uma sessão de criação de mapa ativa."
|
|
);
|
|
}
|
|
|
|
if (
|
|
!double.IsFinite(distanciaMinimaAutomaticaMetros) ||
|
|
distanciaMinimaAutomaticaMetros <= 0
|
|
)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(distanciaMinimaAutomaticaMetros),
|
|
"A distância mínima automática deve ser maior que zero."
|
|
);
|
|
}
|
|
|
|
estado.Limpar();
|
|
|
|
estado.EmCriacao = true;
|
|
estado.Nome = NormalizarNome(nome);
|
|
estado.Tipo = tipo;
|
|
estado.DistanciaMinimaCapturaAutomaticaMetros =
|
|
distanciaMinimaAutomaticaMetros;
|
|
estado.ModoCaptura = MapaModoCapturaCriacao.Manual;
|
|
estado.GravacaoAutomaticaAtiva = false;
|
|
|
|
estado.RuasMutaveis.Add(
|
|
new MapaRuaCriacao
|
|
{
|
|
Numero = 1
|
|
}
|
|
);
|
|
|
|
estado.IndiceRuaAtual = 0;
|
|
}
|
|
|
|
|
|
public void Cancelar(
|
|
MapaEstadoCriacao estado)
|
|
{
|
|
ValidarEstado(estado);
|
|
estado.Limpar();
|
|
}
|
|
|
|
|
|
public void DefinirNome(
|
|
MapaEstadoCriacao estado,
|
|
string? nome)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
estado.Nome = NormalizarNome(nome);
|
|
}
|
|
|
|
|
|
public bool DefinirModoCaptura(
|
|
MapaEstadoCriacao estado,
|
|
MapaModoCapturaCriacao modo,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
{
|
|
motivo =
|
|
"Finalize a gravação automática antes de trocar o modo.";
|
|
return false;
|
|
}
|
|
|
|
estado.ModoCaptura = modo;
|
|
return true;
|
|
}
|
|
|
|
|
|
public bool IniciarGravacaoAutomatica(
|
|
MapaEstadoCriacao estado,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
|
|
if (
|
|
estado.ModoCaptura !=
|
|
MapaModoCapturaCriacao.Automatico
|
|
)
|
|
{
|
|
motivo =
|
|
"A captura atual não está no modo automático.";
|
|
return false;
|
|
}
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
return true;
|
|
|
|
if (estado.RuaAtual == null)
|
|
{
|
|
motivo =
|
|
"Não existe rua atual para receber pontos.";
|
|
return false;
|
|
}
|
|
|
|
estado.GravacaoAutomaticaAtiva = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
public bool PararGravacaoAutomatica(
|
|
MapaEstadoCriacao estado,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
estado.GravacaoAutomaticaAtiva = false;
|
|
return true;
|
|
}
|
|
|
|
|
|
public bool TentarAdicionarPontoManual(
|
|
MapaEstadoCriacao estado,
|
|
MapaPontoCriacao ponto,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
|
|
if (
|
|
estado.ModoCaptura !=
|
|
MapaModoCapturaCriacao.Manual
|
|
)
|
|
{
|
|
motivo =
|
|
"A captura atual está no modo automático.";
|
|
return false;
|
|
}
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
{
|
|
motivo =
|
|
"Finalize a gravação automática antes de registrar um ponto manual.";
|
|
return false;
|
|
}
|
|
|
|
return TentarAdicionarPonto(
|
|
estado,
|
|
ponto,
|
|
DistanciaMinimaPontoManualMetros,
|
|
out motivo
|
|
);
|
|
}
|
|
|
|
|
|
public bool TentarAdicionarPontoAutomatico(
|
|
MapaEstadoCriacao estado,
|
|
MapaPontoCriacao ponto,
|
|
bool forcarPrimeiroPonto,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
|
|
if (
|
|
estado.ModoCaptura !=
|
|
MapaModoCapturaCriacao.Automatico ||
|
|
!estado.GravacaoAutomaticaAtiva
|
|
)
|
|
{
|
|
motivo =
|
|
"A gravação automática não está ativa.";
|
|
return false;
|
|
}
|
|
|
|
MapaRuaCriacao? rua = estado.RuaAtual;
|
|
|
|
if (rua == null)
|
|
{
|
|
motivo =
|
|
"Não existe rua atual para receber pontos.";
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
forcarPrimeiroPonto &&
|
|
rua.QuantidadePontos == 0
|
|
)
|
|
{
|
|
return TentarAdicionarPonto(
|
|
estado,
|
|
ponto,
|
|
distanciaMinimaMetros: 0,
|
|
out motivo
|
|
);
|
|
}
|
|
|
|
return TentarAdicionarPonto(
|
|
estado,
|
|
ponto,
|
|
estado.DistanciaMinimaCapturaAutomaticaMetros,
|
|
out motivo
|
|
);
|
|
}
|
|
|
|
|
|
public bool TentarRemoverUltimoPonto(
|
|
MapaEstadoCriacao estado,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
{
|
|
motivo =
|
|
"Finalize a gravação automática antes de remover pontos.";
|
|
return false;
|
|
}
|
|
|
|
MapaRuaCriacao? rua = estado.RuaAtual;
|
|
|
|
if (
|
|
rua == null ||
|
|
rua.QuantidadePontos <= 0
|
|
)
|
|
{
|
|
motivo =
|
|
"A rua atual não possui pontos.";
|
|
return false;
|
|
}
|
|
|
|
rua.PontosMutaveis.RemoveAt(
|
|
rua.QuantidadePontos - 1
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public bool TentarCriarNovaRua(
|
|
MapaEstadoCriacao estado,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
{
|
|
motivo =
|
|
"Finalize a gravação automática antes de criar outra rua.";
|
|
return false;
|
|
}
|
|
|
|
MapaRuaCriacao? atual = estado.RuaAtual;
|
|
|
|
if (
|
|
atual == null ||
|
|
atual.QuantidadePontos < 2
|
|
)
|
|
{
|
|
int numero =
|
|
atual?.Numero
|
|
?? (estado.IndiceRuaAtual + 1);
|
|
|
|
motivo =
|
|
$"Registre pelo menos 2 pontos na rua {numero} antes de criar uma nova rua.";
|
|
return false;
|
|
}
|
|
|
|
estado.RuasMutaveis.Add(
|
|
new MapaRuaCriacao
|
|
{
|
|
Numero =
|
|
estado.RuasMutaveis.Count + 1
|
|
}
|
|
);
|
|
|
|
estado.IndiceRuaAtual =
|
|
estado.RuasMutaveis.Count - 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public bool TentarSelecionarRua(
|
|
MapaEstadoCriacao estado,
|
|
int indiceRua,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
{
|
|
motivo =
|
|
"Finalize a gravação automática antes de trocar de rua.";
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
indiceRua < 0 ||
|
|
indiceRua >= estado.RuasMutaveis.Count
|
|
)
|
|
{
|
|
motivo =
|
|
"A rua informada não existe.";
|
|
return false;
|
|
}
|
|
|
|
estado.IndiceRuaAtual = indiceRua;
|
|
return true;
|
|
}
|
|
|
|
|
|
public bool TentarExcluirRua(
|
|
MapaEstadoCriacao estado,
|
|
int indiceRua,
|
|
out string motivo)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
motivo = string.Empty;
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
{
|
|
motivo =
|
|
"Finalize a gravação automática antes de excluir uma rua.";
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
indiceRua < 0 ||
|
|
indiceRua >= estado.RuasMutaveis.Count
|
|
)
|
|
{
|
|
motivo =
|
|
"A rua informada não existe.";
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* A sessão mantém pelo menos uma rua.
|
|
* Se existe apenas uma, excluir significa limpar a Rua 1.
|
|
*/
|
|
if (estado.RuasMutaveis.Count == 1)
|
|
{
|
|
estado.RuasMutaveis[0]
|
|
.PontosMutaveis
|
|
.Clear();
|
|
|
|
estado.IndiceRuaAtual = 0;
|
|
return true;
|
|
}
|
|
|
|
estado.RuasMutaveis.RemoveAt(indiceRua);
|
|
RenumerarRuas(estado);
|
|
|
|
if (
|
|
estado.IndiceRuaAtual >=
|
|
estado.RuasMutaveis.Count
|
|
)
|
|
{
|
|
estado.IndiceRuaAtual =
|
|
estado.RuasMutaveis.Count - 1;
|
|
}
|
|
else if (indiceRua < estado.IndiceRuaAtual)
|
|
{
|
|
estado.IndiceRuaAtual--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public void LimparPontos(
|
|
MapaEstadoCriacao estado)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Finalize a gravação automática antes de limpar o levantamento."
|
|
);
|
|
}
|
|
|
|
estado.RuasMutaveis.Clear();
|
|
estado.RuasMutaveis.Add(
|
|
new MapaRuaCriacao
|
|
{
|
|
Numero = 1
|
|
}
|
|
);
|
|
estado.IndiceRuaAtual = 0;
|
|
}
|
|
|
|
|
|
public MapaResumoCriacao CriarResumo(
|
|
MapaEstadoCriacao estado)
|
|
{
|
|
ValidarEstado(estado);
|
|
|
|
MapaRuaCriacao? atual = estado.RuaAtual;
|
|
|
|
double comprimentoAtual =
|
|
atual == null
|
|
? 0
|
|
: CalcularComprimentoMetros(
|
|
atual.Pontos
|
|
);
|
|
|
|
double comprimentoTotal =
|
|
estado.Ruas.Sum(
|
|
rua =>
|
|
CalcularComprimentoMetros(
|
|
rua.Pontos
|
|
)
|
|
);
|
|
|
|
|
|
var ruasResumo =
|
|
estado.Ruas
|
|
.Select(
|
|
(rua, indice) =>
|
|
new MapaRuaResumoCriacao
|
|
{
|
|
Numero = rua.Numero,
|
|
QuantidadePontos = rua.QuantidadePontos,
|
|
ComprimentoMetros =
|
|
CalcularComprimentoMetros(rua.Pontos),
|
|
Valida = rua.Valida,
|
|
Atual = indice == estado.IndiceRuaAtual
|
|
}
|
|
)
|
|
.ToList();
|
|
|
|
return new MapaResumoCriacao
|
|
{
|
|
EmCriacao = estado.EmCriacao,
|
|
Nome = estado.Nome,
|
|
RuaAtual = atual?.Numero ?? 0,
|
|
QuantidadeRuas = estado.QuantidadeRuas,
|
|
QuantidadeRuasValidas = estado.QuantidadeRuasValidas,
|
|
PontosRuaAtual = atual?.QuantidadePontos ?? 0,
|
|
PontosTotal = estado.QuantidadePontosTotal,
|
|
ComprimentoRuaAtualMetros = comprimentoAtual,
|
|
ComprimentoTotalMetros = comprimentoTotal,
|
|
ModoCaptura = estado.ModoCaptura,
|
|
GravacaoAutomaticaAtiva = estado.GravacaoAutomaticaAtiva,
|
|
DistanciaMinimaAutomaticaMetros =
|
|
estado.DistanciaMinimaCapturaAutomaticaMetros,
|
|
PodeNovaRua = estado.PodeCriarNovaRua,
|
|
PodeSalvar =
|
|
estado.PodeSalvar &&
|
|
!string.IsNullOrWhiteSpace(estado.Nome),
|
|
Ruas = ruasResumo
|
|
};
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Converte o levantamento transitório para o domínio canônico.
|
|
/// IDs finais são sempre strings numéricas sequenciais.
|
|
/// </summary>
|
|
public MapaDocumento CriarDocumento(
|
|
MapaEstadoCriacao estado)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
|
|
if (estado.GravacaoAutomaticaAtiva)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Finalize a gravação automática antes de gerar o mapa."
|
|
);
|
|
}
|
|
|
|
string nome = NormalizarNome(estado.Nome);
|
|
|
|
if (string.IsNullOrWhiteSpace(nome))
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Informe um nome para o novo mapa."
|
|
);
|
|
}
|
|
|
|
if (estado.Ruas.Count == 0)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"O levantamento não possui ruas."
|
|
);
|
|
}
|
|
|
|
for (int i = 0; i < estado.Ruas.Count; i++)
|
|
{
|
|
MapaRuaCriacao rua = estado.Ruas[i];
|
|
|
|
if (
|
|
rua == null ||
|
|
rua.QuantidadePontos < 2
|
|
)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"A rua {i + 1} precisa possuir pelo menos 2 pontos."
|
|
);
|
|
}
|
|
}
|
|
|
|
var documento =
|
|
new MapaDocumento
|
|
{
|
|
Nome = nome,
|
|
CaminhoOrigem = null,
|
|
FormatoOrigem = MapaFormatoOrigem.Desconhecido,
|
|
Tipo = estado.Tipo,
|
|
Modificado = true
|
|
};
|
|
|
|
for (int i = 0; i < estado.Ruas.Count; i++)
|
|
{
|
|
MapaRuaCriacao ruaCriacao = estado.Ruas[i];
|
|
|
|
string id =
|
|
(i + 1).ToString(
|
|
CultureInfo.InvariantCulture
|
|
);
|
|
|
|
Coordinate[] coordenadas =
|
|
ruaCriacao.Pontos
|
|
.Select(
|
|
ponto =>
|
|
new Coordinate(
|
|
ponto.Longitude,
|
|
ponto.Latitude
|
|
)
|
|
)
|
|
.ToArray();
|
|
|
|
LineString geometria =
|
|
_geometryFactory.CreateLineString(
|
|
coordenadas
|
|
);
|
|
|
|
documento.Ruas.Add(
|
|
new MapaRua
|
|
{
|
|
Id = id,
|
|
IdOriginal = null,
|
|
IdGeradoAutomaticamente = true,
|
|
Nome = nome,
|
|
Geometria = geometria,
|
|
ComprimentoMetros =
|
|
CalcularComprimentoMetros(
|
|
ruaCriacao.Pontos
|
|
),
|
|
Dist1 = 0,
|
|
Dist2 = 0,
|
|
IndiceFeatureOrigem = i
|
|
}
|
|
);
|
|
}
|
|
|
|
MapaResultadoValidacao validacao =
|
|
_validationService.Validar(
|
|
documento
|
|
);
|
|
|
|
if (!validacao.Valido)
|
|
{
|
|
string erros =
|
|
string.Join(
|
|
Environment.NewLine,
|
|
validacao.Erros
|
|
);
|
|
|
|
throw new InvalidOperationException(
|
|
"O mapa criado não passou pela validação canônica:" +
|
|
Environment.NewLine +
|
|
erros
|
|
);
|
|
}
|
|
|
|
return documento;
|
|
}
|
|
|
|
|
|
public static double CalcularComprimentoMetros(
|
|
IReadOnlyList<MapaPontoCriacao> pontos)
|
|
{
|
|
if (
|
|
pontos == null ||
|
|
pontos.Count < 2
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
double total = 0;
|
|
|
|
for (int i = 1; i < pontos.Count; i++)
|
|
{
|
|
total += CalcularDistanciaMetros(
|
|
pontos[i - 1],
|
|
pontos[i]
|
|
);
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
|
|
public static double CalcularDistanciaMetros(
|
|
MapaPontoCriacao a,
|
|
MapaPontoCriacao b)
|
|
{
|
|
if (
|
|
a == null ||
|
|
b == null
|
|
)
|
|
{
|
|
return double.PositiveInfinity;
|
|
}
|
|
|
|
const double raioTerraMetros = 6378137.0;
|
|
|
|
double lat1 = GrausParaRadianos(a.Latitude);
|
|
double lat2 = GrausParaRadianos(b.Latitude);
|
|
double dLat = GrausParaRadianos(b.Latitude - a.Latitude);
|
|
double dLon = GrausParaRadianos(b.Longitude - a.Longitude);
|
|
|
|
double h =
|
|
Math.Sin(dLat / 2.0) * Math.Sin(dLat / 2.0) +
|
|
Math.Cos(lat1) *
|
|
Math.Cos(lat2) *
|
|
Math.Sin(dLon / 2.0) * Math.Sin(dLon / 2.0);
|
|
|
|
h = Math.Clamp(h, 0.0, 1.0);
|
|
|
|
return
|
|
2.0 *
|
|
raioTerraMetros *
|
|
Math.Atan2(
|
|
Math.Sqrt(h),
|
|
Math.Sqrt(1.0 - h)
|
|
);
|
|
}
|
|
|
|
|
|
private static bool TentarAdicionarPonto(
|
|
MapaEstadoCriacao estado,
|
|
MapaPontoCriacao ponto,
|
|
double distanciaMinimaMetros,
|
|
out string motivo)
|
|
{
|
|
motivo = string.Empty;
|
|
|
|
if (ponto == null)
|
|
{
|
|
motivo =
|
|
"O ponto informado é nulo.";
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
!CoordenadaValida(
|
|
ponto.Latitude,
|
|
ponto.Longitude
|
|
)
|
|
)
|
|
{
|
|
motivo =
|
|
"O ponto informado possui coordenada inválida.";
|
|
return false;
|
|
}
|
|
|
|
MapaRuaCriacao? rua = estado.RuaAtual;
|
|
|
|
if (rua == null)
|
|
{
|
|
motivo =
|
|
"Não existe rua atual para receber pontos.";
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
rua.QuantidadePontos > 0 &&
|
|
distanciaMinimaMetros > 0
|
|
)
|
|
{
|
|
MapaPontoCriacao ultimo =
|
|
rua.Pontos[
|
|
rua.QuantidadePontos - 1
|
|
];
|
|
|
|
double distancia =
|
|
CalcularDistanciaMetros(
|
|
ultimo,
|
|
ponto
|
|
);
|
|
|
|
if (
|
|
!double.IsFinite(distancia) ||
|
|
distancia < distanciaMinimaMetros
|
|
)
|
|
{
|
|
motivo =
|
|
"Ponto ignorado: distância do último ponto = " +
|
|
$"{distancia:F3} m; mínimo = " +
|
|
$"{distanciaMinimaMetros:F3} m.";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* O estado guarda um snapshot próprio.
|
|
* Nenhuma referência externa fica viva aqui dentro.
|
|
*/
|
|
rua.PontosMutaveis.Add(
|
|
ponto.Clonar()
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
private static bool CoordenadaValida(
|
|
double latitude,
|
|
double longitude)
|
|
{
|
|
return
|
|
double.IsFinite(latitude) &&
|
|
double.IsFinite(longitude) &&
|
|
latitude >= -90.0 &&
|
|
latitude <= 90.0 &&
|
|
longitude >= -180.0 &&
|
|
longitude <= 180.0 &&
|
|
(
|
|
Math.Abs(latitude) > 1e-12 ||
|
|
Math.Abs(longitude) > 1e-12
|
|
);
|
|
}
|
|
|
|
|
|
private static void RenumerarRuas(
|
|
MapaEstadoCriacao estado)
|
|
{
|
|
for (
|
|
int i = 0;
|
|
i < estado.RuasMutaveis.Count;
|
|
i++
|
|
)
|
|
{
|
|
estado.RuasMutaveis[i].Numero = i + 1;
|
|
}
|
|
}
|
|
|
|
|
|
private static string NormalizarNome(
|
|
string? nome)
|
|
{
|
|
return
|
|
string.IsNullOrWhiteSpace(nome)
|
|
? string.Empty
|
|
: nome.Trim();
|
|
}
|
|
|
|
|
|
private static double GrausParaRadianos(
|
|
double graus)
|
|
{
|
|
return
|
|
graus *
|
|
Math.PI /
|
|
180.0;
|
|
}
|
|
|
|
|
|
private static void ValidarEstado(
|
|
MapaEstadoCriacao estado)
|
|
{
|
|
if (estado == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(estado)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
private static void ValidarEstadoAtivo(
|
|
MapaEstadoCriacao estado)
|
|
{
|
|
ValidarEstado(estado);
|
|
|
|
if (!estado.EmCriacao)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Não existe uma sessão de criação de mapa ativa."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|