ST_Intersecta com LINESTRING degenerado

9

No PostGIS, tenho um monte de objetos LINESTRING Z (que representam pólos) e quero descobrir quais estão dentro de uma área (representada por um POLYGON). Para os fins deste exercício, podemos assumir com segurança que um poste é praticamente vertical, para que não cruze o limite da área.

O problema é que, às vezes, o poste é exatamente vertical.

Esta consulta, a que eu gostaria de fazer, não tem ê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 algumas variantes, é possível:

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));

Isso não tem ê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));

Se você fizer uma consulta 3DIntersects, ela terá ê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));

no entanto, gera um aviso de que:

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

Então isso funciona, mais ou menos, mas enche meus logs de ruído e eu prefiro não desativar os avisos.

Minha leitura do padrão de acesso ao recurso OpenGIS Simple (consulte a parte 1, seção 4.14) é que, embora o LINESTRING seja degenerado, ele ainda deve ser interpretado como topologicamente fechado, para que eles se cruzem.

Isso está correto?

EDITAR

Após algumas experiências, posso responder parte da minha própria pergunta. A razão pela qual as consultas acima retornam false é isso porque acredita-se que o LINESTRING Z não seja 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)

É uma maneira educada de dizer que o OGC não suporta 3D e, como tal, o GDAL / PostGIS apenas o suporta.

Posso aceitar isso, embora ainda não tenha encontrado nada na especificação OGC que indique que o LINESTRING correspondente não é válido.

Então, acho que minha pergunta é: existe uma maneira oficialmente abençoada de encontrar a pegada de um pedaço de geometria 3D que sempre retorna geometria 2D válida?

Pseudônimo
fonte

Respostas:

6

Uma correção realmente simples na primeira consulta é usar ST_MakeValid para criar uma geometria "válida" para retornar true:

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)

A consulta original falhou porque o objeto LineString não era válido, conforme descoberto por ST_IsValid ou ST_IsValidReason . Isso ocorre porque ST_Intersects é um operador 2D, portanto a geometria foi forçada a duas dimensões LINESTRING(544483.525 6849134.28,544483.525 6849134.28), o que é inválido.

ST_MakeValid remove a coordenada repetida (no espaço 2D) e a altera para um tipo de ponto, pois a geometria resultante tem apenas uma coordenada.

Você pode ver que a coordenada Z ainda está parcialmente lá, mas é ignorada. Se isso lhe incomoda, você também pode usar ST_Force2D para usar apenas geometrias 2D para coisas 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
fonte
Obrigado! Esta foi uma boa explicação do que está acontecendo, e a solução alternativa também é boa.
Pseudônimo