Tengo 2 conjuntos de datos que consisten en datos de parcelas catastrales, aproximadamente 125,000 filas cada uno. La columna de geometría es polígonos WKB que representan límites de parcela; Todos los datos son geométricamente válidos (los polígonos están cerrados, etc.).
Algunos datos recientes llegaron en una proyección diferente a los datos base que se utilizan para un trabajo de comparación, así que reproyecté el más nuevo (la base era 4326; el otro era WGA94 que se introdujo en PostGIS como 900914 ... Lo reproyecté a 4326) .
La primera etapa del análisis fue encontrar y almacenar parcelas no coincidentes; parte de eso es identificar y almacenar parcelas con geometría idéntica.
Entonces ejecuté una consulta muy estándar (el bloque de código a continuación extrae los detalles del esquema, etc.):
create table matchdata as
select a.*
from gg2014 a, gg2013 b
where ST_Equals(a.g1,b.g1)
CERO resultados.
"Extraño ..." pensé. "Tal vez ha habido pequeños cambios en los vértices causados por la reproyección: eso sería molesto y realmente no debería suceder".
Afortunadamente, hay abundantes datos espaciales (5 columnas de identificador) que me permiten establecer parcelas que deberían ser espacialmente idénticas: aquellas con el mismo identificador, cuya fecha de cambio en la tabla de 2014 fue anterior a la fecha de cambio máxima en los datos de 2013. Eso ascendió a 120.086 filas distintas.
Almacené los identificadores y las geometrías en una tabla separada ( match_id
), y ejecuté la siguiente 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
Los primeros 16 valores para inta
y intb
fueron idénticamente cero, los siguientes 456 fueron 0.99999999-ish (mínimo 0.99999999999994, máximo 0.999999999999999), y las filas 473 en adelante fueron 1 - hasta la fila 120050, cuando el área de la intersección fue mayor que cualquiera de las geometrías (la mayor valor para inta
y intb
fue 1.00000000000029, pero aún así).
Así que aquí está mi enigma: si dos geometrías se cruzan espacialmente entre 99.999999999994% y 100.000000000029% de sus áreas respectivas, me gustaría que "ST_Equals" diga "Sí ... te daré esa. Lo suficientemente cerca".
Después de todo, es equivalente a estar fuera por aproximadamente 1 parte en 16 billones ... es decir, como si la deuda nacional de los EE. UU. Hubiera saldado en menos de 93 centavos.
En el contexto de la circunferencia de la Tierra (a ~ 40,000 km), es como estar fuera de 0.0000000025km, arriba (ya que para dar como resultado una diferencia de área tan pequeña, cualquier cambio de vértice debe ser aún más pequeño).
De acuerdo con TFD (que tengo R'd), la tolerancia no ST_Intersects()
es 0,00001 m (1 mm), por lo que los cambios implícitos en los vértices (que confieso que no he verificado: lo haré ST_Dump()
y lo haré) parecerían ser más pequeños que la tolerancia (Me doy cuenta de eso ST_Intersects !== ST_Intersection()
, pero es la única tolerancia mencionada).
No he podido encontrar la tolerancia correspondiente para la comparación de vértices realizada por ST_Equals()
... pero parece realmente extraño que al menos 120,000 de mis filas deban pasar cualquier evaluación sensata de identidad espacial, pero no lo hacen.
(Nota: también hice el mismo ejercicio usando ::geography
, con resultados que tenían más variabilidad, pero aún más de 110,000 entradas con un bonito '1' limpio).
¿Hay alguna manera de aflojar la tolerancia de ST_Equals, que no requiera profundizar en los intersticios del código? No estoy interesado en hacer eso.
Si no es así, ¿hay algún error que alguien conozca?
Nota: sería bueno si el 'kludge' no estuviera haciendo una comparación 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).
Puedo evitar esto, pero escribir las 20 páginas para documentar la solución, que solo volverá a aparecer si obtenemos datos dudosos, es un PITA que preferiría no tener que hacer dado que es probable que sea una sola vez .
(Versiones: Postgresql 9.3.5; PostGIS 2.1.3)
ST_Equals
solo regresa true
cuando las geometrías son iguales : tipo de geometría, número de vértices, SRID y valores de vértice (en todas las dimensiones, en el mismo orden). Si hay alguna variación, la comparación se detiene y false
se devuelve.
ST_Equals()
ignora la direccionalidad. Supuse que eso significaba que para un polígono cerrado en 2-D, no hay diferencia si los puntos se enumeran en sentido horario frente a sentido antihorario. ST_OrderingEquals()
Es la prueba más estricta. Dicho esto, después de inspeccionar los vértices (usando ST_Dump()
y calculando deltas para cada vértice) está claro que la increíble respuesta de @John Barça está en el dinero. ST_equals()
está contraindicado, incluso para datos ex ante idénticos conocidos, si se reproyecta una geometría, a menos que la comparación se realice con ST_SnapToGrid ().
(100*(ST_Area(ST_Intersection(a.g1, b.g1))/ST_Area(a.g1)))::int as int_pca
y (100*(ST_Area(ST_Intersection(a.g1, b.g1))/ST_Area(b.g1)))::int as int_pcb
(asegúrese de que JOIN
incluye ST_Intersects(a.g1,b.g1)
). Pruebe si (int_pca, int_pcb)=(100,100)
(o algún otro conjunto de puntos de corte). Kludgy, pero hará 2.6 millones de paquetes en ~ 30min (siempre que g1 esté indexado GIST).