PostGIS ST_Intersection de polígonos pode retornar linhas

9

Ao recortar os polígonos de uma tabela com polígonos em outra, ST_Intersection pode retornar um conjunto de resultados que podem ser manipulados com ST_Dump. As várias geometrias retornadas não são necessariamente ST_Polygon, mas também ST_LineString (provavelmente também um ponto). Então, ao executar uma consulta

INSERT INTO c (geom)
  (SELECT (ST_Dump(ST_Intersection(a.geom,b.geom))).geom
  FROM a INNER JOIN b ON ST_Intersects(a.geom, b.geom));

ao tentar preencher a tabela "c" com polígonos cortados, ela falha com ERRO: O tipo de geometria (LineString) não corresponde ao tipo de coluna (polígono)

Eu fiz outra instrução SELECT aninhada para que apenas as geometrias de polígonos aparecessem, como:

INSERT INTO c (geom)
  (SELECT geom FROM
    (SELECT (ST_Dump(ST_Intersection(a.geom,b.geom))).geom
    FROM a INNER JOIN b ON ST_Intersects(a.geom, b.geom))) AS cl
    WHERE ST_GeometryType(cl.geom)='ST_Polygon');

Como isso é um pouco complicado, pergunto-me se existe uma solução mais elegante para eliminar geometrias inválidas?

Robert Špendl
fonte
BTW, eu coloquei <! - language: lang-sql -> antes do código ser bloqueado, mas ainda não há marcações. Alguma dica para um noob?
Robert Špendl
Para formatar o código, basta colá-lo, selecione-o e use o botão Amostra de código {}acima da janela de edição de perguntas.
PolyGeo
Hum, eu fiz isso, o código está marcado como "código", mas as palavras-chave ainda não estão coloridas.
Robert Špendl
Normalmente, não tento fingir o código SQL aqui, mas acabei de encontrar uma publicação meta que parece útil em meta.stackexchange.com/questions/90521/… - você deixou uma "linha em branco entre o comentário e o código"?
PolyGeo
Uma interseção de duas geometrias pode produzir qualquer tipo de geometria, portanto, acho que sua solução atual é a mais direta.
Mike T

Respostas:

9

Esse pode ser um bom local para usar uma função da linguagem SQL. Aqui está uma rápida que deve funcionar para esta situação:

CREATE OR REPLACE FUNCTION PolygonalIntersection(a geometry, b geometry)
RETURNS geometry AS $$
SELECT ST_Collect(geom)
FROM 
(SELECT (ST_Dump(ST_Intersection(a, b))).geom 
UNION ALL
-- union in an empty polygon so we get an 
-- empty geometry instead of NULL if there
-- is are no polygons in the intersection
SELECT ST_GeomFromText('POLYGON EMPTY')) SQ
WHERE ST_GeometryType(geom) = 'ST_Polygon';
$$ LANGUAGE SQL;

Isso manterá os componentes poligonais de uma interseção, mas jogue fora todo o resto. Ele sempre retorna um MultiPolygon, mesmo se você tiver um ou nenhum componente.

WITH 
      square   as (SELECT ST_GeomFromText('POLYGON ((0 0, 0  1,  1  1,  1  0, 0 0))') AS geom),
biggersquare   as (SELECT ST_GeomFromText('POLYGON ((0 0, 0 10, 10 10, 10  0, 0 0))') AS geom),
adjacentsquare as (SELECT ST_GeomFromText('POLYGON ((0 0, 1  0,  1 -1, -1 -1, 0 0))') AS geom)   

SELECT ST_AsText(PolygonalIntersection(square.geom, biggersquare.geom))
  FROM square, biggersquare;
--"MULTIPOLYGON(((0 0,0 1,1 1,1 0,0 0)))"

SELECT ST_AsText(PolygonalIntersection(square.geom, adjacentsquare.geom))
  FROM square, adjacentsquare;
--"MULTIPOLYGON(EMPTY)"
dbaston
fonte
3

Resposta muito boa de @dbaston. No entanto, retornar uma geometria vazia em vez de nula pode causar problemas porque a geometria vazia retornada não possui srid. St_Intersection também pode retornar MultiPolygon. Esta função atualizada foi realmente útil para mim:

CREATE OR REPLACE FUNCTION PolygonalIntersection(a geometry, b geometry)
RETURNS geometry AS $$
SELECT ST_Collect(geom)
FROM 
(SELECT (ST_Dump(ST_Intersection(a, b))).geom 
) SQ
WHERE ST_GeometryType(geom) = 'ST_Polygon' OR ST_GeometryType(geom) = 'ST_MultiPolygon';
$$ LANGUAGE SQL;
thoomasbro
fonte