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 regresatrue
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 yfalse
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 (usandoST_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 queJOIN
incluyeST_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).Respuestas:
Supongo que las transformaciones de coordenadas han introducido pequeños errores de redondeo (vea un ejemplo a continuación). Como no hay forma de establecer la tolerancia en ST_Equals, esto provoca que ST_Equals devuelva falso para algunas geometrías que solo difieren en el enésimo lugar decimal, ya que las geometrías tienen que ser idénticas en todos los aspectos; consulte la definición de matriz de intersección en libgeos . Puedes comprobar esto con un ejemplo realmente extremo,
que devuelve falso .
Si usa ST_SnapToGrid , puede imponer una precisión dada, por ejemplo, a diez decimales,
ahora vuelve verdadero .
Si tuvieras que correr,
estableciendo una tolerancia adecuada, sospecho que sus problemas desaparecerían.
Aquí hay un enlace a una discusión del desarrollador de Postgis sobre la tolerancia que sugiere que es menos que trivial de implementar.
Hice un par de conversiones entre British National Grid (EPSG: 27700) y lat / lon para ilustrar el punto sobre la precisión del redondeo, tomando un punto en algún lugar de Londres,
devoluciones
POINT(-0.19680497282746 51.5949871603888)
y revertir esto,
devoluciones
POINT(525000.000880007 189999.999516211)
que está desactivado en menos de un milímetro, pero más que suficiente para hacer que ST_Equals devuelva falso.
fuente
¿Ejecutó la verificación ST_IsValid en sus geometrías? Si no son válidos, todas las apuestas están canceladas. ST_Intersects y la otra familia de funciones de relación espacial GEOS a menudo devolverán falso porque el área no está bien definida desde el punto de vista de la matriz de intersección. La razón por la que probablemente funcione ST_Buffer es porque está convirtiendo sus geometrías inválidas en válidas. ST_Buffer (..., tinybit) es lo que se conoce como la herramienta del "pobre intento de hacer que mi geometría sea válida".
fuente
ST_isValid(g1)
- eso se mencionó (oblicuamente) "[l] columna de geometría son polígonos WKB que representan límites de parcela; todos los datos son geométricamente válidos (los polígonos están cerrados, etc.) ".Mi respuesta llega un poco tarde, pero quizás ayude a alguien que tiene el mismo problema. Según mi experiencia, cuando dos geometrías son iguales pero ST_Equals devuelve Falso, dos cosas pueden ayudar:
ST_Equals(st_astext(a.geom), st_astext(b.geom))
lugar deST_Equals(a.geom , b.geom)
El primero ya se menciona en la documentación . El segundo parece irracional pero funciona. No lo sé, pero supongo que tiene que ver con el formato binario de la geometría postGIS predeterminada.
fuente