ST_Intersecta con LINESTRING degenerado

9

Tengo, en PostGIS, un montón de objetos LINESTRING Z (que representan polos), y quiero encontrar cuáles están dentro de un área (representados por un POLÍGONO). Para el propósito de este ejercicio, podemos asumir con seguridad que un poste es bastante vertical, por lo que no se cruza con el límite del área.

El problema es que a veces el poste es exactamente vertical.

Esta consulta, la que me gustaría hacer, no tiene éxito:

SELECT ST_Intersects(ST_GeomFromText('LINESTRING Z (544483.525 6849134.28 104.1098,544483.525 6849134.28 114.6)',28356),
ST_GeomFromText('POLYGON((543907.636214323 6848710.84802846,543909.787417164 6849286.92923919,544869.040437688 6849283.30837091,544866.842236582 6848707.22673193,543907.636214323 6848710.84802846))',28356));

Explorando algunas variantes, esto tiene éxito:

SELECT ST_Intersects(ST_GeomFromText('POINT (544483.525 6849134.28)',28356),
ST_GeomFromText('POLYGON((543907.636214323 6848710.84802846,543909.787417164 6849286.92923919,544869.040437688 6849283.30837091,544866.842236582 6848707.22673193,543907.636214323 6848710.84802846))',28356));

Esto no tiene éxito:

SELECT ST_Intersects(ST_GeomFromText('LINESTRING (544483.525 6849134.28,544483.525 6849134.28)',28356),
ST_GeomFromText('POLYGON((543907.636214323 6848710.84802846,543909.787417164 6849286.92923919,544869.040437688 6849283.30837091,544866.842236582 6848707.22673193,543907.636214323 6848710.84802846))',28356));

Si realiza una consulta 3DIntersects, tiene éxito:

SELECT ST_3DIntersects(ST_GeomFromText('LINESTRING Z (544483.525 6849134.28 104.1098,544483.525 6849134.28 114.6)',28356),
ST_GeomFromText('POLYGON((543907.636214323 6848710.84802846,543909.787417164 6849286.92923919,544869.040437688 6849283.30837091,544866.842236582 6848707.22673193,543907.636214323 6848710.84802846))',28356));

sin embargo, genera un AVISO de que:

One or both of the geometries is missing z-value. The unknown z-value will be regarded as "any value"

Eso funciona, más o menos, pero llena mis registros con ruido y prefiero no apagar las advertencias.

Mi lectura del estándar de acceso a funciones simples OpenGIS (ver parte 1, sección 4.14) es que, aunque LINESTRING es degenerado, aún debe interpretarse como topológicamente cerrado, por lo que deben cruzarse.

¿Es esto correcto?

EDITAR

Después de experimentar un poco, puedo responder parte de mi propia pregunta. La razón por la cual las consultas anteriores devuelven falso es esto porque se cree que LINESTRING Z no es válido:

SELECT ST_IsValidReason(ST_GeomFromText('LINESTRING Z (544483.525 6849134.28 104.1098,544483.525 6849134.28 114.6)',28356));
                           st_isvalidreason                           
----------------------------------------------------------------------
 Too few points in geometry component[544483.525 6849134.28 104.1098]
(1 row)

Esta es una manera educada de decir que OGC no es compatible con 3D y, como tal, GDAL / PostGIS solo lo admite.

Puedo aceptar eso, aunque todavía no he encontrado nada en la especificación OGC que indique que el LINESTRING correspondiente no es válido.

Así que supongo que mi pregunta realmente es: ¿existe una forma oficialmente bendecida de encontrar la huella de una pieza de geometría 3D que siempre devuelve una geometría 2D válida?

Seudónimo
fuente

Respuestas:

6

Una solución realmente simple en la primera consulta es usar ST_MakeValid para crear una geometría "válida" que devuelva verdadero:

SELECT ST_Intersects(ST_MakeValid(A), B), ST_AsText(ST_MakeValid(A)) AS ST_MakeValid
FROM (
  SELECT ST_GeomFromText('LINESTRING Z (544483.525 6849134.28 104.1098,544483.525 6849134.28 114.6)',28356) AS A,
         ST_GeomFromText('POLYGON((543907.636214323 6848710.84802846,543909.787417164 6849286.92923919,544869.040437688 6849283.30837091,544866.842236582 6848707.22673193,543907.636214323 6848710.84802846))',28356) AS B
) AS d;

-[ RECORD 1 ]-+-----------------------------------------
st_intersects | t
st_makevalid  | POINT Z (544483.525 6849134.28 104.1098)

La consulta original falló porque el objeto LineString no era válido, como lo descubrieron ST_IsValid o ST_IsValidReason . Esto se debe a que ST_Intersects es un operador 2D, por lo que la geometría fue forzada a dos dimensiones LINESTRING(544483.525 6849134.28,544483.525 6849134.28), lo que no es válido.

ST_MakeValid elimina la coordenada repetida (en el espacio 2D) y la cambia a un tipo de punto ya que la geometría resultante solo tiene una coordenada.

Puede ver que la coordenada Z todavía está parcialmente allí, pero se ignora. Si esto te molesta, también puedes usar ST_Force2D para usar solo geometrías 2D para cosas como ST_Intersects:

SELECT ST_Intersects(ST_MakeValid(ST_Force2D(A)), B),
  ST_AsText(ST_Force2D(A)) AS ST_Force2D,
  ST_AsText(ST_MakeValid(ST_Force2D(A))) AS ST_MakeValid
FROM (
  SELECT ST_GeomFromText('LINESTRING Z (544483.525 6849134.28 104.1098,544483.525 6849134.28 114.6)',28356) AS A,
         ST_GeomFromText('POLYGON((543907.636214323 6848710.84802846,543909.787417164 6849286.92923919,544869.040437688 6849283.30837091,544866.842236582 6848707.22673193,543907.636214323 6848710.84802846))',28356) AS B
) AS d;

-[ RECORD 1 ]-+--------------------------------------------------------
st_intersects | t
st_force2d    | LINESTRING(544483.525 6849134.28,544483.525 6849134.28)
st_makevalid  | POINT(544483.525 6849134.28)
Mike T
fuente
¡Gracias! Esta fue una buena explicación de lo que está sucediendo, y la solución también es buena.
Seudónimo