1179 lines
27 KiB
C#
1179 lines
27 KiB
C#
using NetTopologySuite.Geometries;
|
|
using OperationControl.Mapas.Models;
|
|
using OperationControl.Mapas.Services;
|
|
|
|
namespace OperationControl.Mapas.Editing
|
|
{
|
|
public sealed class MapaEdicaoService
|
|
{
|
|
private const int LimiteHistorico = 50;
|
|
private readonly MapaDocumentoCloneService _cloneService = new();
|
|
private readonly MapaRecorteService _recorteService = new();
|
|
|
|
|
|
public void Iniciar(MapaEstadoEdicao estado, MapaDocumento documento)
|
|
{
|
|
if (estado == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(estado)
|
|
);
|
|
}
|
|
|
|
if (documento == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(documento)
|
|
);
|
|
}
|
|
|
|
estado.DocumentoOriginal = _cloneService.Clonar(documento);
|
|
estado.DocumentoTrabalho = _cloneService.Clonar(documento);
|
|
estado.RuasSelecionadasMutaveis.Clear();
|
|
estado.DocumentoTrabalho.Modificado = false;
|
|
estado.HistoricoDesfazerMutavel.Clear();
|
|
estado.HistoricoRefazerMutavel.Clear();
|
|
}
|
|
|
|
|
|
public void Cancelar(MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstado(estado);
|
|
estado.DocumentoOriginal = null;
|
|
estado.DocumentoTrabalho = null;
|
|
estado.RuasSelecionadasMutaveis.Clear();
|
|
estado.HistoricoDesfazerMutavel.Clear();
|
|
estado.HistoricoRefazerMutavel.Clear();
|
|
}
|
|
|
|
|
|
public bool AlternarRua(MapaEstadoEdicao estado, string ruaId)
|
|
{
|
|
var id =
|
|
ObterIdCanonico(
|
|
estado,
|
|
ruaId
|
|
);
|
|
|
|
|
|
if (
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Contains(
|
|
id
|
|
)
|
|
)
|
|
{
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Remove(
|
|
id
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Add(
|
|
id
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public void SelecionarRua(
|
|
MapaEstadoEdicao estado,
|
|
string ruaId)
|
|
{
|
|
string id =
|
|
ObterIdCanonico(
|
|
estado,
|
|
ruaId
|
|
);
|
|
|
|
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Add(
|
|
id
|
|
);
|
|
}
|
|
|
|
|
|
public void RemoverRuaSelecao(
|
|
MapaEstadoEdicao estado,
|
|
string ruaId)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
if (
|
|
string.IsNullOrWhiteSpace(
|
|
ruaId
|
|
)
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Remove(
|
|
ruaId.Trim()
|
|
);
|
|
}
|
|
|
|
|
|
public void LimparSelecao(
|
|
MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Clear();
|
|
}
|
|
|
|
|
|
/*
|
|
* Operação destrutiva de exclusão completa
|
|
* de ruas selecionadas.
|
|
*
|
|
* A alteração é registrada no histórico
|
|
* como uma única transação de Undo/Redo.
|
|
*/
|
|
public int RemoverRuasSelecionadas(MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
if (
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Count == 0
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
var documento =
|
|
estado.DocumentoTrabalho!;
|
|
|
|
|
|
/*
|
|
* Descobrimos primeiro se realmente haverá
|
|
* alguma mutação.
|
|
*
|
|
* Não queremos criar Ctrl+Z vazio.
|
|
*/
|
|
bool possuiRuaRemovivel =
|
|
documento
|
|
.Ruas
|
|
.Any(
|
|
rua =>
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Contains(
|
|
rua.Id
|
|
)
|
|
);
|
|
|
|
|
|
if (!possuiRuaRemovivel)
|
|
return 0;
|
|
|
|
|
|
/*
|
|
* Estado imediatamente ANTES da operação.
|
|
*
|
|
* A seleção também é preservada.
|
|
*/
|
|
var antes =
|
|
CriarSnapshot(
|
|
estado
|
|
);
|
|
|
|
|
|
int quantidadeAntes =
|
|
documento.Ruas.Count;
|
|
|
|
|
|
documento.Ruas.RemoveAll(
|
|
rua =>
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Contains(
|
|
rua.Id
|
|
)
|
|
);
|
|
|
|
|
|
int removidas =
|
|
quantidadeAntes -
|
|
documento.Ruas.Count;
|
|
|
|
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Clear();
|
|
|
|
|
|
if (removidas <= 0)
|
|
return 0;
|
|
|
|
|
|
documento.Modificado =
|
|
true;
|
|
|
|
|
|
/*
|
|
* Estado imediatamente DEPOIS.
|
|
*/
|
|
var depois =
|
|
CriarSnapshot(
|
|
estado
|
|
);
|
|
|
|
|
|
RegistrarAlteracao(
|
|
estado,
|
|
removidas == 1
|
|
? "Excluir 1 rua"
|
|
: $"Excluir {removidas} ruas",
|
|
antes,
|
|
depois
|
|
);
|
|
|
|
|
|
return removidas;
|
|
}
|
|
|
|
|
|
public MapaResultadoRecorte RecortarPorPoligono(MapaEstadoEdicao estado, IEnumerable<Coordinate> pontosPoligono, bool somenteSelecionadas = false)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
if (
|
|
somenteSelecionadas &&
|
|
!estado.PossuiSelecao
|
|
)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Nenhuma rua está selecionada " +
|
|
"para receber o recorte."
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Snapshot ANTES da operação inteira.
|
|
*
|
|
* Se 80 ruas forem cortadas,
|
|
* ainda será apenas um Ctrl+Z.
|
|
*/
|
|
var antes =
|
|
CriarSnapshot(
|
|
estado
|
|
);
|
|
|
|
|
|
try
|
|
{
|
|
IReadOnlyCollection<string>?
|
|
escopo =
|
|
somenteSelecionadas
|
|
? estado
|
|
.RuasSelecionadas
|
|
.ToArray()
|
|
: null;
|
|
|
|
|
|
var resultado =
|
|
_recorteService
|
|
.RecortarPorPoligono(
|
|
estado
|
|
.DocumentoTrabalho!,
|
|
pontosPoligono,
|
|
escopo
|
|
);
|
|
|
|
|
|
if (
|
|
!resultado.HouveAlteracao
|
|
)
|
|
{
|
|
return resultado;
|
|
}
|
|
|
|
|
|
/*
|
|
* Uma rua completamente removida
|
|
* não pode continuar selecionada.
|
|
*/
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.ExceptWith(
|
|
resultado
|
|
.RuasRemovidasIds
|
|
);
|
|
|
|
|
|
var depois =
|
|
CriarSnapshot(
|
|
estado
|
|
);
|
|
|
|
|
|
RegistrarAlteracao(
|
|
estado,
|
|
|
|
$"Recorte por polígono " +
|
|
$"({resultado.RuasAlteradas + resultado.RuasRemovidas} ruas)",
|
|
|
|
antes,
|
|
depois
|
|
);
|
|
|
|
|
|
return resultado;
|
|
}
|
|
catch
|
|
{
|
|
/*
|
|
* Segurança transacional.
|
|
*
|
|
* Se algo inesperado acontecer depois
|
|
* de alguma alteração, o DocumentoTrabalho
|
|
* volta integralmente ao snapshot anterior.
|
|
*/
|
|
RestaurarSnapshot(
|
|
estado,
|
|
antes
|
|
);
|
|
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
public MapaResultadoRecorte ApararPorLinha(MapaEstadoEdicao estado, IEnumerable<Coordinate> pontosLinha, bool somenteSelecionadas = false)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
if (
|
|
somenteSelecionadas &&
|
|
!estado.PossuiSelecao
|
|
)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Nenhuma rua está selecionada " +
|
|
"para receber o recorte."
|
|
);
|
|
}
|
|
|
|
|
|
var antes =
|
|
CriarSnapshot(
|
|
estado
|
|
);
|
|
|
|
|
|
try
|
|
{
|
|
IReadOnlyCollection<string>?
|
|
escopo =
|
|
somenteSelecionadas
|
|
? estado
|
|
.RuasSelecionadas
|
|
.ToArray()
|
|
: null;
|
|
|
|
|
|
var resultado =
|
|
_recorteService
|
|
.ApararPorLinha(
|
|
estado
|
|
.DocumentoTrabalho!,
|
|
pontosLinha,
|
|
escopo
|
|
);
|
|
|
|
|
|
if (
|
|
!resultado.HouveAlteracao
|
|
)
|
|
{
|
|
return resultado;
|
|
}
|
|
|
|
|
|
var depois =
|
|
CriarSnapshot(
|
|
estado
|
|
);
|
|
|
|
|
|
RegistrarAlteracao(
|
|
estado,
|
|
|
|
$"Aparar por borda " +
|
|
$"({resultado.RuasAlteradas} ruas)",
|
|
|
|
antes,
|
|
depois
|
|
);
|
|
|
|
|
|
return resultado;
|
|
}
|
|
catch
|
|
{
|
|
RestaurarSnapshot(
|
|
estado,
|
|
antes
|
|
);
|
|
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
public bool RestaurarDocumentoFonte(MapaEstadoEdicao estado, MapaDocumento documentoFonte)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
if (documentoFonte == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(documentoFonte)
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Não criamos histórico vazio.
|
|
*
|
|
* Se o documento de trabalho já corresponde
|
|
* exatamente à fonte, não há nada para fazer.
|
|
*/
|
|
if (
|
|
DocumentosEquivalentes(
|
|
estado.DocumentoTrabalho!,
|
|
documentoFonte
|
|
)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Tudo que existia imediatamente antes
|
|
* da restauração pode voltar com Ctrl+Z.
|
|
*/
|
|
MapaEdicaoSnapshot antes =
|
|
CriarSnapshot(
|
|
estado
|
|
);
|
|
|
|
|
|
/*
|
|
* Nunca entregamos DocumentoFonte diretamente.
|
|
*
|
|
* O editor recebe outra cópia profunda.
|
|
*/
|
|
estado.DocumentoTrabalho =
|
|
_cloneService
|
|
.Clonar(
|
|
documentoFonte
|
|
);
|
|
|
|
|
|
/*
|
|
* Embora geometricamente seja o documento-fonte,
|
|
* dentro desta sessão isso representa uma alteração
|
|
* em relação ao documento oficial atual.
|
|
*
|
|
* Portanto Apply precisa ficar habilitado.
|
|
*/
|
|
estado.DocumentoTrabalho.Modificado =
|
|
true;
|
|
|
|
|
|
/*
|
|
* A seleção de edição pertence ao documento
|
|
* anterior.
|
|
*
|
|
* Limpamos para evitar uma seleção cirúrgica
|
|
* antiga sendo aplicada acidentalmente depois
|
|
* da restauração.
|
|
*
|
|
* A seleção OPERACIONAL do Workspace não passa
|
|
* por aqui.
|
|
*/
|
|
estado
|
|
.RuasSelecionadasMutaveis
|
|
.Clear();
|
|
|
|
|
|
MapaEdicaoSnapshot depois =
|
|
CriarSnapshot(
|
|
estado
|
|
);
|
|
|
|
|
|
RegistrarAlteracao(
|
|
estado,
|
|
"Restaurar mapa original",
|
|
antes,
|
|
depois
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Devolve uma cópia.
|
|
*
|
|
* Nunca entregamos diretamente
|
|
* DocumentoTrabalho para outra camada
|
|
* começar a mutá-lo escondido.
|
|
*/
|
|
public MapaDocumento ObterResultado(MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
return _cloneService.Clonar(estado.DocumentoTrabalho!);
|
|
}
|
|
|
|
|
|
private static string ObterIdCanonico(
|
|
MapaEstadoEdicao estado,
|
|
string ruaId)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
if (
|
|
string.IsNullOrWhiteSpace(
|
|
ruaId
|
|
)
|
|
)
|
|
{
|
|
throw new ArgumentException(
|
|
"ID da rua inválido.",
|
|
nameof(ruaId)
|
|
);
|
|
}
|
|
|
|
|
|
var correspondencias =
|
|
estado
|
|
.DocumentoTrabalho!
|
|
.Ruas
|
|
.Where(
|
|
x =>
|
|
string.Equals(
|
|
x.Id,
|
|
ruaId.Trim(),
|
|
StringComparison
|
|
.OrdinalIgnoreCase
|
|
)
|
|
)
|
|
.ToList();
|
|
|
|
|
|
if (correspondencias.Count == 0)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Rua '{ruaId}' não pertence " +
|
|
"ao documento em edição."
|
|
);
|
|
}
|
|
|
|
|
|
if (correspondencias.Count > 1)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"O ID '{ruaId}' está ambíguo " +
|
|
"no documento em edição."
|
|
);
|
|
}
|
|
|
|
|
|
return correspondencias[0].Id;
|
|
}
|
|
|
|
|
|
private MapaEdicaoSnapshot CriarSnapshot(MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
|
|
return new MapaEdicaoSnapshot(
|
|
_cloneService.Clonar(estado.DocumentoTrabalho!),
|
|
estado.RuasSelecionadasMutaveis
|
|
);
|
|
}
|
|
|
|
|
|
private void RestaurarSnapshot(MapaEstadoEdicao estado, MapaEdicaoSnapshot snapshot)
|
|
{
|
|
ValidarEstadoAtivo(estado);
|
|
|
|
if (snapshot == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(snapshot)
|
|
);
|
|
}
|
|
|
|
|
|
estado.DocumentoTrabalho = _cloneService.Clonar(snapshot.Documento);
|
|
estado.RuasSelecionadasMutaveis.Clear();
|
|
|
|
var idsValidos =
|
|
estado
|
|
.DocumentoTrabalho
|
|
.Ruas
|
|
.Select(x => x.Id)
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
foreach (string ruaId in snapshot.RuasSelecionadas)
|
|
{
|
|
if (idsValidos.Contains(ruaId))
|
|
{
|
|
estado.RuasSelecionadasMutaveis.Add(ruaId);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private static void RegistrarAlteracao(MapaEstadoEdicao estado, string descricao, MapaEdicaoSnapshot antes, MapaEdicaoSnapshot depois)
|
|
{
|
|
var item =
|
|
new MapaEdicaoHistoricoItem(
|
|
descricao,
|
|
antes,
|
|
depois
|
|
);
|
|
|
|
|
|
estado
|
|
.HistoricoDesfazerMutavel
|
|
.Add(
|
|
item
|
|
);
|
|
|
|
|
|
/*
|
|
* Uma edição nova depois de um Undo
|
|
* abre uma nova linha temporal.
|
|
*
|
|
* O Redo anterior deixa de fazer sentido.
|
|
*/
|
|
estado
|
|
.HistoricoRefazerMutavel
|
|
.Clear();
|
|
|
|
|
|
while (
|
|
estado
|
|
.HistoricoDesfazerMutavel
|
|
.Count >
|
|
LimiteHistorico
|
|
)
|
|
{
|
|
estado
|
|
.HistoricoDesfazerMutavel
|
|
.RemoveAt(
|
|
0
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
public bool PodeDesfazer(MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstado(
|
|
estado
|
|
);
|
|
|
|
|
|
return
|
|
estado.EmEdicao &&
|
|
estado.PodeDesfazer;
|
|
}
|
|
|
|
|
|
public bool PodeRefazer(MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstado(
|
|
estado
|
|
);
|
|
|
|
|
|
return
|
|
estado.EmEdicao &&
|
|
estado.PodeRefazer;
|
|
}
|
|
|
|
|
|
public bool Desfazer(MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
if (!estado.PodeDesfazer)
|
|
return false;
|
|
|
|
|
|
int indice =
|
|
estado
|
|
.HistoricoDesfazerMutavel
|
|
.Count -
|
|
1;
|
|
|
|
|
|
var item =
|
|
estado
|
|
.HistoricoDesfazerMutavel[
|
|
indice
|
|
];
|
|
|
|
|
|
estado
|
|
.HistoricoDesfazerMutavel
|
|
.RemoveAt(
|
|
indice
|
|
);
|
|
|
|
|
|
RestaurarSnapshot(
|
|
estado,
|
|
item.Antes
|
|
);
|
|
|
|
|
|
estado
|
|
.HistoricoRefazerMutavel
|
|
.Add(
|
|
item
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public bool Refazer(MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstadoAtivo(
|
|
estado
|
|
);
|
|
|
|
|
|
if (!estado.PodeRefazer)
|
|
return false;
|
|
|
|
|
|
int indice =
|
|
estado
|
|
.HistoricoRefazerMutavel
|
|
.Count -
|
|
1;
|
|
|
|
|
|
var item =
|
|
estado
|
|
.HistoricoRefazerMutavel[
|
|
indice
|
|
];
|
|
|
|
|
|
estado
|
|
.HistoricoRefazerMutavel
|
|
.RemoveAt(
|
|
indice
|
|
);
|
|
|
|
|
|
RestaurarSnapshot(
|
|
estado,
|
|
item.Depois
|
|
);
|
|
|
|
|
|
estado
|
|
.HistoricoDesfazerMutavel
|
|
.Add(
|
|
item
|
|
);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
private static void ValidarEstado(
|
|
MapaEstadoEdicao estado)
|
|
{
|
|
if (estado == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(estado)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
private static void ValidarEstadoAtivo(
|
|
MapaEstadoEdicao estado)
|
|
{
|
|
ValidarEstado(
|
|
estado
|
|
);
|
|
|
|
|
|
if (
|
|
estado.DocumentoOriginal == null ||
|
|
estado.DocumentoTrabalho == null
|
|
)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Não existe uma sessão de edição ativa."
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
public IReadOnlyList<string> SelecionarPorRetangulo(
|
|
MapaEstadoEdicao estado,
|
|
double minLatitude,
|
|
double minLongitude,
|
|
double maxLatitude,
|
|
double maxLongitude,
|
|
bool substituirSelecao = true)
|
|
{
|
|
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."
|
|
);
|
|
}
|
|
|
|
|
|
double minLat =
|
|
Math.Min(
|
|
minLatitude,
|
|
maxLatitude
|
|
);
|
|
|
|
|
|
double maxLat =
|
|
Math.Max(
|
|
minLatitude,
|
|
maxLatitude
|
|
);
|
|
|
|
|
|
double minLon =
|
|
Math.Min(
|
|
minLongitude,
|
|
maxLongitude
|
|
);
|
|
|
|
|
|
double maxLon =
|
|
Math.Max(
|
|
minLongitude,
|
|
maxLongitude
|
|
);
|
|
|
|
|
|
if (
|
|
minLat < -90 ||
|
|
maxLat > 90 ||
|
|
minLon < -180 ||
|
|
maxLon > 180
|
|
)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
"Retângulo WGS84 inválido."
|
|
);
|
|
}
|
|
|
|
|
|
var envelope =
|
|
new Envelope(
|
|
minLon,
|
|
maxLon,
|
|
minLat,
|
|
maxLat
|
|
);
|
|
|
|
|
|
/*
|
|
* Documento canônico é WGS84.
|
|
*/
|
|
var geometryFactory =
|
|
new GeometryFactory(
|
|
new PrecisionModel(),
|
|
4326
|
|
);
|
|
|
|
|
|
Geometry area =
|
|
geometryFactory
|
|
.ToGeometry(
|
|
envelope
|
|
);
|
|
|
|
|
|
var encontrados =
|
|
new List<string>();
|
|
|
|
|
|
foreach (
|
|
var rua
|
|
in estado
|
|
.DocumentoTrabalho
|
|
.Ruas)
|
|
{
|
|
if (
|
|
rua == null ||
|
|
rua.Geometria == null ||
|
|
rua.Geometria.IsEmpty
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
/*
|
|
* Filtro barato primeiro.
|
|
*/
|
|
if (
|
|
!rua.Geometria
|
|
.EnvelopeInternal
|
|
.Intersects(
|
|
envelope
|
|
)
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
/*
|
|
* Teste real.
|
|
*
|
|
* Não basta o bounding box da rua
|
|
* tocar o retângulo.
|
|
*/
|
|
if (
|
|
!rua.Geometria
|
|
.Intersects(
|
|
area
|
|
)
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
encontrados.Add(
|
|
rua.Id
|
|
);
|
|
}
|
|
|
|
|
|
if (substituirSelecao)
|
|
{
|
|
LimparSelecao(estado);
|
|
}
|
|
|
|
|
|
foreach (string ruaId in encontrados)
|
|
{
|
|
SelecionarRua(estado, ruaId);
|
|
}
|
|
|
|
|
|
return encontrados;
|
|
}
|
|
|
|
|
|
private static bool DocumentosEquivalentes(MapaDocumento atual, MapaDocumento fonte)
|
|
{
|
|
if (
|
|
atual == null ||
|
|
fonte == null
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Modificado NÃO entra nessa comparação.
|
|
*
|
|
* Queremos saber se o conteúdo canônico
|
|
* é equivalente, não o estado da sessão.
|
|
*/
|
|
if (
|
|
!string.Equals(
|
|
atual.Nome,
|
|
fonte.Nome,
|
|
StringComparison.Ordinal
|
|
) ||
|
|
!string.Equals(
|
|
atual.CaminhoOrigem,
|
|
fonte.CaminhoOrigem,
|
|
StringComparison.Ordinal
|
|
) ||
|
|
atual.FormatoOrigem !=
|
|
fonte.FormatoOrigem ||
|
|
atual.Tipo !=
|
|
fonte.Tipo
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
if (
|
|
atual.Ruas.Count !=
|
|
fonte.Ruas.Count
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
for (
|
|
int i = 0;
|
|
i < atual.Ruas.Count;
|
|
i++)
|
|
{
|
|
MapaRua a =
|
|
atual.Ruas[i];
|
|
|
|
|
|
MapaRua b =
|
|
fonte.Ruas[i];
|
|
|
|
|
|
if (
|
|
!string.Equals(
|
|
a.Id,
|
|
b.Id,
|
|
StringComparison.Ordinal
|
|
) ||
|
|
!string.Equals(
|
|
a.IdOriginal,
|
|
b.IdOriginal,
|
|
StringComparison.Ordinal
|
|
) ||
|
|
a.IdGeradoAutomaticamente !=
|
|
b.IdGeradoAutomaticamente ||
|
|
!string.Equals(
|
|
a.Nome,
|
|
b.Nome,
|
|
StringComparison.Ordinal
|
|
) ||
|
|
Math.Abs(
|
|
a.ComprimentoMetros -
|
|
b.ComprimentoMetros
|
|
) > 0.000001 ||
|
|
Math.Abs(
|
|
a.Dist1 -
|
|
b.Dist1
|
|
) > 0.000001 ||
|
|
Math.Abs(
|
|
a.Dist2 -
|
|
b.Dist2
|
|
) > 0.000001 ||
|
|
a.IndiceFeatureOrigem !=
|
|
b.IndiceFeatureOrigem
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
bool geometriaANula =
|
|
a.Geometria == null;
|
|
|
|
|
|
bool geometriaBNula =
|
|
b.Geometria == null;
|
|
|
|
|
|
if (
|
|
geometriaANula !=
|
|
geometriaBNula
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
if (
|
|
!geometriaANula &&
|
|
!a.Geometria.EqualsExact(
|
|
b.Geometria
|
|
)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
}
|
|
} |