Eu tenho 2 conjuntos de dados que consistem em dados de parcelas cadastrais - aproximadamente 125.000 linhas cada. A coluna de geometria é polígonos WKB que representam os limites das parcelas; todos os dados são geometricamente válidos (os polígonos estão fechados etc.).
Alguns dados recentes chegaram em uma projeção diferente dos dados de base usados para um trabalho de comparação - então eu reprojetei o mais recente (a base era 4326; a outra era o WGA94 que foi trazido para o PostGIS como 900914 ... eu o reprojetei para 4326) .
A primeira etapa da análise foi encontrar e armazenar parcelas não correspondentes; parte disso é identificar e armazenar parcelas com geometria idêntica.
Então, eu executei uma consulta muito padrão (o bloco de código abaixo abstrai os detalhes do esquema, etc.):
create table matchdata as
select a.*
from gg2014 a, gg2013 b
where ST_Equals(a.g1,b.g1)
ZERO results.
"Estranho ..." pensei. "Talvez tenha havido pequenas mudanças de vértices causadas pela reprojeção: isso seria irritante e realmente não deveria acontecer".
Felizmente, existem dados aspaciais abundantes (5 colunas identificadoras) que me permitem estabelecer parcelas que devem ser espacialmente idênticas: aquelas com o mesmo identificador, cuja data de alteração na tabela de 2014 era anterior à data de alteração máxima nos dados de 2013. Isso totalizou 120.086 linhas distintas.
Armazenei os identificadores e geometrias em uma tabela separada ( match_id
) e executei a seguinte consulta:
select apid,
bpid,
ST_Area(ag::geometry) as aa,
ST_Area(bg::geometry) as ab,
ST_Area(ST_Intersection(ag,bg)::geometry)/ST_Area(ag::geometry) as inta,
ST_Area(ST_Intersection(ag,bg)::geometry)/ST_Area(ag::geometry) as intb
from match_id
order by inta
Os 16 primeiros valores inta
e intb
foram idênticos a zero, os 456 seguintes foram 0,99999999-ish (min 0,99999999999994, máx 0,999999999999999) e as linhas 473 em diante foram 1 - até a linha 120050, quando a área da interseção era maior que a geometria (a maior valor para inta
e intb
era 1.00000000000029, mas ainda assim).
Então, eis o meu enigma: se duas geometrias se cruzam espacialmente entre 99,999999999994% e 100,00000000002929% de suas respectivas áreas, eu gostaria que "ST_Equals" dissesse "Sim ... eu darei a você essa. Fechar o suficiente".
Afinal, é equivalente a ficar em 1 parte em 16 trilhões ... ou seja, como se a dívida nacional dos EUA estivesse abaixo de 93 centavos.
No contexto da circunferência da Terra (a ~ 40.000 km), é como estar em 0,0000000025 km, no máximo (desde que resultem em uma diferença de área tão pequena, qualquer mudança de vértice deve ser ainda menor).
De acordo com o TFD (que eu recomendei), a tolerância ST_Intersects()
é de 0,00001m (1mm), de modo que as alterações implícitas nos vértices (que eu confesso que não verifiquei: quero ST_Dump()
e faço) pareceriam menores do que a tolerância. (Eu percebo isso ST_Intersects !== ST_Intersection()
, mas é a única tolerância mencionada).
Não fui capaz de descobrir a tolerância correspondente à comparação de vértices realizada por ST_Equals()
... mas parece realmente estranho que pelo menos 120.000 das minhas linhas devam passar por qualquer avaliação sensata da identidade espacial, mas não o fazem.
(Nota: eu também fiz o mesmo exercício usando ::geography
- com resultados que tinham mais variabilidade, mas ainda mais que 110.000 entradas com um bom '1' limpo).
Existe uma maneira de diminuir a tolerância de ST_Equals, que não exija escavação nos interstícios do código? Eu não estou interessado em fazer isso.
Se não, existe algum argumento que alguém esteja ciente?
Nota: seria bom se o 'kludge' não estivesse fazendo uma comparação bilateral como
where ST_within(g1, ST_Buffer(g2, 0.0000001))
and ST_within(g2, ST_Buffer(g1, 0.0000001))
- I've done that: sure, it works... but it's a gigantic documentation PITA).
Posso resolver isso, mas escrever as 20 páginas para documentar a solução alternativa - que só será exibida novamente se tivermos dados duvidosos - é uma PITA que eu preferiria não fazer, já que é provável que seja uma ocorrência única .
(Versões: Postgresql 9.3.5; PostGIS 2.1.3)
ST_Equals
retorna apenas true
quando as geometrias são iguais - tipo de geometria, número de vértices, SRID e valores de vértice (em todas as dimensões, na mesma ordem). Se houver alguma variação, a comparação será interrompida e false
retornada.
ST_Equals()
ignora a direcionalidade. Entendi que isso significa que, para um polígono 2-D fechado, não faz diferença se os pontos são enumerados no sentido horário versus anti-horário. ST_OrderingEquals()
é o teste mais rígido. Dito isso, depois de inspecionar os vértices (usando ST_Dump()
e calculando deltas para todos os vértices), fica claro que a incrível resposta de @John Barça está no dinheiro. ST_equals()
é contra-indicado, mesmo para dados idênticos conhecidos ex-ante , se uma geometria for reprojetada - a menos que a comparação seja feita com ST_SnapToGrid ().
(100*(ST_Area(ST_Intersection(a.g1, b.g1))/ST_Area(a.g1)))::int as int_pca
e (100*(ST_Area(ST_Intersection(a.g1, b.g1))/ST_Area(b.g1)))::int as int_pcb
(verifique se você JOIN
inclui ST_Intersects(a.g1,b.g1)
). Teste se (int_pca, int_pcb)=(100,100)
(ou algum outro conjunto de pontos de corte). Kludgy, mas fará 2,6 milhões de encomendas em ~ 30 min (desde que g1 esteja indexado no GIST).