wiki:UsersWikiExamplesPolygonOverlaps

Finding and Editing Polygon Overlaps

The function pgoverlap checks a polygon table for overlapping geometries, separating between sliver and non-sliver overlappings. It returns two tables - one table with non-overlapping geometries and adjusted sliver-overlapping polygons and one table with non-sliver overlappings which have to be handled manually afterwards.

This function is a test version. Any comments or ideas for improvement are welcome!

Preconditions for successful use of the function:

  • gid column of data type integer or serial, unique, with btree index!!
  • the_geom column of data type geometry and geometry type POLYGON, with gist index!!
  • column of data type integer (name is variable) for subsequent joining of attributes with btree index!!
  • geometries should be valid (otherwise number of exceptions rises)

Caution: Do not call the functions twice at a time!! - Intermediate tables have fixed names and are locked during function processing (otherwise their content would be overwritten).

Example:

CREATE TABLE pgoverlap_bsp_input (gid serial PRIMARY KEY, a_id integer, the_geom geometry);
INSERT INTO pgoverlap_bsp_input (a_id, the_geom) VALUES
(1,ST_GeomFromText('POLYGON((3565961 5361731,3570432 5359825,3574390 5362977,3577469 5360631,3582526 5360192,3591616 5351909,3592715 5343113,3591835 5337395,3586265 5339448,3579008 5336882,3572045 5340254,3567500 5338788,3564421 5342013,3562296 5353301,3566327 5356527,3565961 5361731))',31467)),
(2,ST_GeomFromText('POLYGON((3587846 5359101,3587353 5362736,3592036 5366371,3598813 5365755,3601708 5361997,3592528 5358855,3587846 5359101))',31467)),
(3,ST_GeomFromText('POLYGON((3600429 5353669,3606823 5355001,3612240 5361751,3622453 5355801,3637461 5355179,3635951 5340171,3617036 5337240,3605225 5340260,3600429 5353669))',31467)),
(4,ST_GeomFromText('POLYGON((3580359 5328360,3588529 5330935,3597943 5323120,3606024 5322587,3612951 5315217,3608155 5306691,3603804 5305093,3596166 5306159,3591105 5310244,3584799 5310688,3576540 5317437,3580359 5328360))',31467)),
(5,ST_GeomFromText('POLYGON((3568123 5343184,3577776 5350721,3586922 5344624,3584890 5336664,3593104 5330312,3586499 5322014,3572187 5325232,3568123 5343184))',31467)),
(6,ST_GeomFromText('POLYGON((3594628 5349620,3601741 5358935,3617153 5357580,3621387 5348519,3617153 5339712,3610040 5334293,3616137 5322945,3607923 5311598,3600302 5314392,3596068 5319727,3600725 5329974,3596406 5339882,3594628 5349620))',31467)),
(7,ST_GeomFromText('POLYGON((3622319 5325401,3625029 5327433,3630194 5329466,3635191 5326502,3638747 5317610,3631719 5311598,3622150 5316594,3622319 5325401))',31467));

SELECT _pgoverlap('public','pgoverlap_bsp_input','a_id','pgoverlap_bsp_output','pgoverlap_bsp_todo');

Function:

CREATE OR REPLACE FUNCTION _pgoverlap(schema_a varchar, table_a varchar, a_id varchar, table_result1 varchar, table_result2 varchar)
  RETURNS void AS
$BODY$

/*
$Id: pgoverlap_wiki.sql 2011-03-09 15:19Z Birgit Laggner $

pgoverlap - checks table for overlapping geometries separating between sliver and non-sliver overlappings, returns table with non-overlapping geometries and adjusted sliver-overlapping polygons as well as table with non-sliver overlappings (for later treatment)

schema_a:      database schema where table is located
table_a:       table name
a_id:          id column name in table
table_result1: name of 1st result table (non-overlapping polygons)
table_result2: name of 2nd result table (overlapping polygons)

Preconditions for successful use of the function:
- gid column of data type integer or serial, unique, with btree index!!
- the_geom column of data type geometry and geometry type POLYGON, with gist index!!
- column of data type integer (name is variable) for subsequent joining of attributes, with btree index!!
- geometries should be valid (otherwise number of exceptions rises)

Caution: Do not call the function twice at a time!! - Intermediate tables have fixed names and are locked during function processing (otherwise their content would be overwritten).

Copyright (C) 2011 Johann Heinrich von Thünen-Institute (vTI) - Federal Research Institute for Rural Areas, Forestry and Fisheries, Institute of Rural Studies, Braunschweig, Germany (http://www.vti.bund.de)
Version 0.1
contact: birgit dot laggner at vti dot bund dot de

This is free software; you can redistribute and/or modify it under
the terms of the GNU General Public Licence. This software is without any warrenty and you use it at your own risk.
*/

DECLARE

i integer;
sql_1 text;


BEGIN

--find all overlappings with geometrytype Polygon:

EXECUTE
'DROP TABLE IF EXISTS '||schema_a||'.pgoverlap_tmp1;';

EXECUTE
'CREATE TABLE '||schema_a||'.pgoverlap_tmp1 (
gid serial,
a_'||a_id||' integer,
b_'||a_id||' integer,
a_geom geometry,
b_geom geometry,
i_geom geometry,
area numeric,
perimeter2 numeric,
perimeter2_uc numeric,
cmp numeric,
sliver character varying(1)
 );';

RAISE NOTICE 'Find all overlappings with geometrytype Polygon. ';

EXECUTE
'INSERT INTO '||schema_a||'.pgoverlap_tmp1 (
a_'||a_id||', b_'||a_id||', a_geom, b_geom, i_geom)
SELECT DISTINCT
sel.a_'||a_id||',
sel.b_'||a_id||',
sel.a_geom,
sel.b_geom,
sel.i_geom
FROM (
SELECT
a.'||a_id||' AS a_'||a_id||',
b.'||a_id||' AS b_'||a_id||',
a.the_geom AS a_geom,
b.the_geom AS b_geom,
(ST_Dump(ST_Intersection(a.the_geom,b.the_geom))).geom AS i_geom
FROM
'||schema_a||'.'||table_a||' a
LEFT JOIN
'||schema_a||'.'||table_a||' b
ON a.the_geom && b.the_geom
WHERE
a.gid < b.gid AND
ST_Intersects(a.the_geom, b.the_geom)) AS sel
WHERE
ST_GeometryType(sel.i_geom) NOT IN (
''ST_Point'',
''ST_LineString'',
''ST_MultiPoint'',
''ST_MultiLineString'',
''ST_Line'');';

EXECUTE
'CREATE INDEX pgoverlap_tmp1_gid_btree
ON '||schema_a||'.pgoverlap_tmp1
USING btree(gid);';

EXECUTE
'CREATE INDEX pgoverlap_tmp1_a_'||a_id||'_btree
ON '||schema_a||'.pgoverlap_tmp1
USING btree(a_'||a_id||');';

EXECUTE
'CREATE INDEX pgoverlap_tmp1_b_'||a_id||'_btree
ON '||schema_a||'.pgoverlap_tmp1
USING btree(b_'||a_id||');';


--Check for sliver polygons

RAISE NOTICE 'Check for sliver polygons. ';

--1. Calculation of area and perimeter:

EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp1 
SET area=ST_Area(i_geom),
    perimeter2=ST_Perimeter(i_geom)^2;';

--2. Calculation of circle perimeter with area equal to area of the polygon:
EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp1 
SET perimeter2_uc=4*pi()*area;';


--3. Calculation of Compactness Index (cmp):

EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp1 
SET cmp=perimeter2_uc/perimeter2;';

--4. Decision: sliver polygon, true or false?:

EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp1 SET sliver=''t'' WHERE area < 10;';

EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp1 SET sliver=''t'' WHERE cmp < 0.13;';

EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp1 SET sliver=''f'' WHERE sliver IS NULL;';
/*
EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp1 SET sliver=''f'' WHERE area > 150;';
*/

--Create table for sliver polygons:

EXECUTE
'DROP TABLE IF EXISTS '||schema_a||'.pgoverlap_tmp2;';

EXECUTE
'CREATE TABLE '||schema_a||'.pgoverlap_tmp2 (
gid serial,
a_'||a_id||' integer,
a_geom geometry,
i_geom_union geometry,
the_geom geometry
);';

RAISE NOTICE 'Processing sliver polygons. ';

EXECUTE
'INSERT INTO '||schema_a||'.pgoverlap_tmp2
(a_'||a_id||', a_geom, i_geom_union)
SELECT a_'||a_id||', a_geom, CASE WHEN ST_Union(i_geom) IS NULL THEN ST_Collect(i_geom) ELSE ST_Union(i_geom) END
FROM '||schema_a||'.pgoverlap_tmp1
WHERE sliver=''t''
GROUP BY a_'||a_id||', a_geom;';

i:=0;

sql_1:='SELECT gid FROM '||schema_a||'.pgoverlap_tmp2 ORDER BY gid;';

FOR i IN EXECUTE sql_1 LOOP
BEGIN

EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp2 SET the_geom=ST_Difference(a_geom, i_geom_union) WHERE gid='||i||';';

EXCEPTION WHEN internal_error THEN

EXECUTE
'UPDATE '||schema_a||'.pgoverlap_tmp2 SET the_geom=ST_Difference(ST_Buffer(a_geom,0.0), ST_Buffer(i_geom_union,0.0)) WHERE gid='||i||';';

END;
END LOOP;

EXECUTE
'CREATE INDEX pgoverlap_tmp2_a_'||a_id||'_btree
ON '||schema_a||'.pgoverlap_tmp2
USING btree(a_'||a_id||');';


--Create table with IDs of sliver polygons:

RAISE NOTICE 'Creating table with IDs of sliver polygons. ';

EXECUTE
'DROP TABLE IF EXISTS '||schema_a||'.pgoverlap_tmp3;';

EXECUTE
'CREATE TABLE '||schema_a||'.pgoverlap_tmp3 (
'||a_id||' integer);';

EXECUTE
'INSERT INTO '||schema_a||'.pgoverlap_tmp3 ('||a_id||')
SELECT DISTINCT a_'||a_id||'
FROM '||schema_a||'.pgoverlap_tmp2;';

EXECUTE
'INSERT INTO '||schema_a||'.pgoverlap_tmp3 ('||a_id||')
SELECT DISTINCT a.b_'||a_id||'
FROM
'||schema_a||'.pgoverlap_tmp1 a
LEFT JOIN
'||schema_a||'.pgoverlap_tmp2 b
ON a.b_'||a_id||'=b.a_'||a_id||'
WHERE b.a_'||a_id||' IS NULL;';


--Insert non-sliver polygons into separate table:

BEGIN
EXECUTE
'SELECT DropGeometryTable('''||schema_a||''','''||table_result2||''');';
EXCEPTION WHEN undefined_table THEN 
END;

EXECUTE
'CREATE TABLE '||schema_a||'.'||table_result2||' (
 gid serial PRIMARY KEY, 
 a_'||a_id||' integer,
 b_'||a_id||' integer
 );';

EXECUTE
'SELECT AddGeometryColumn('''||schema_a||''', '''||table_result2||''',''a_geom'',(SELECT DISTINCT ST_SRID(the_geom) FROM '||schema_a||'.'||table_a||'),''POLYGON'',2);';

EXECUTE
'SELECT AddGeometryColumn('''||schema_a||''', '''||table_result2||''',''b_geom'',(SELECT DISTINCT ST_SRID(the_geom) FROM '||schema_a||'.'||table_a||'),''POLYGON'',2);';

EXECUTE
'SELECT AddGeometryColumn('''||schema_a||''', '''||table_result2||''',''i_geom'',(SELECT DISTINCT ST_SRID(the_geom) FROM '||schema_a||'.'||table_a||'),''POLYGON'',2);';

RAISE NOTICE 'Inserting non-sliver polygons into separate table. ';

EXECUTE
'INSERT INTO '||schema_a||'.'||table_result2||' (
 a_'||a_id||',
 b_'||a_id||',
 a_geom,
 b_geom,
 i_geom)
SELECT 
 a_'||a_id||',
 b_'||a_id||',
 a_geom,
 b_geom,
 i_geom
FROM '||schema_a||'.pgoverlap_tmp1
WHERE sliver=''f'';';


--create table with IDs of non-sliver polygons:

RAISE NOTICE 'creating table with IDs of non-sliver polygons. ';

EXECUTE
'DROP TABLE IF EXISTS '||schema_a||'.pgoverlap_tmp4;';

EXECUTE
'CREATE TABLE '||schema_a||'.pgoverlap_tmp4 (
'||a_id||' integer);';

EXECUTE
'INSERT INTO '||schema_a||'.pgoverlap_tmp4 ('||a_id||')
SELECT DISTINCT a_'||a_id||'
FROM '||schema_a||'.'||table_result2||';';

EXECUTE
'INSERT INTO '||schema_a||'.pgoverlap_tmp4 ('||a_id||')
SELECT DISTINCT a.b_'||a_id||'
FROM
'||schema_a||'.'||table_result2||' a
LEFT JOIN
'||schema_a||'.'||table_result2||' b
ON a.b_'||a_id||'=b.a_'||a_id||'
WHERE b.a_'||a_id||' IS NULL;';


--Insert geometries without overlappings into new table:

EXECUTE
'DROP TABLE IF EXISTS '||schema_a||'.pgoverlap_tmp5;';

EXECUTE
'CREATE TABLE '||schema_a||'.pgoverlap_tmp5 (
 gid serial PRIMARY KEY, 
 '||a_id||' integer,
 the_geom geometry
 );';

EXECUTE
'INSERT INTO '||schema_a||'.pgoverlap_tmp5
 ('||a_id||', the_geom)
SELECT 
 a.'||a_id||',
 a.the_geom
FROM
'||schema_a||'.'||table_a||' a
LEFT JOIN
'||schema_a||'.pgoverlap_tmp3 b
ON a.'||a_id||'=b.'||a_id||'
LEFT JOIN
'||schema_a||'.pgoverlap_tmp4 c
ON a.'||a_id||'=c.'||a_id||'
WHERE
b.'||a_id||' IS NULL AND
c.'||a_id||' IS NULL;';


--Create and merge result table:

BEGIN
EXECUTE
'SELECT DropGeometryTable('''||schema_a||''','''||table_result1||''');';
EXCEPTION WHEN undefined_table THEN 
END;

EXECUTE
'CREATE TABLE '||schema_a||'.'||table_result1||' (
gid serial PRIMARY KEY,
'||a_id||' integer
);';
EXECUTE
'SELECT AddGeometryColumn('''||schema_a||''','''||table_result1||''',''the_geom'',(SELECT DISTINCT ST_SRID(the_geom) FROM '||schema_a||'.'||table_a||'),''POLYGON'',2);';
-- 11May12 PostGIS 2.0 rejects this DROP CONSTRAINT as an error  -dbb
EXECUTE
'ALTER TABLE '||schema_a||'.'||table_result1||' DROP CONSTRAINT enforce_geotype_the_geom;';


--Insert Difference (sliver overlappings) into result table:

EXECUTE
'INSERT INTO '||schema_a||'.'||table_result1||' (
'||a_id||',
the_geom
)
SELECT
a_'||a_id||',
(ST_Dump(the_geom)).geom
FROM
'||schema_a||'.pgoverlap_tmp2
WHERE ST_IsEmpty(the_geom)=''f'';';


--Insert unchanged polygons (sliver overlappings) into result table:

EXECUTE
'INSERT INTO '||schema_a||'.'||table_result1||' (
'||a_id||',
the_geom
)
SELECT DISTINCT
a.b_'||a_id||',
a.b_geom
FROM
'||schema_a||'.pgoverlap_tmp1 a
LEFT JOIN
'||schema_a||'.pgoverlap_tmp2 b
ON a.b_'||a_id||'=b.a_'||a_id||'
WHERE 
a.sliver=''t'' AND
b.a_'||a_id||' IS NULL;';


--Insert non-overlapping polygons into result table:

EXECUTE
'INSERT INTO '||schema_a||'.'||table_result1||' (
'||a_id||',
the_geom
)
SELECT
'||a_id||',
the_geom
FROM '||schema_a||'.pgoverlap_tmp5;';


--Update geometries already processed as sliver overlappings which also have non-sliver overlappings:

EXECUTE
'UPDATE '||schema_a||'.'||table_result2||' t SET a_geom=a.geom FROM (SELECT '||a_id||', CASE WHEN ST_Union(the_geom) IS NULL THEN ST_Collect(the_geom) ELSE ST_Union(the_geom) END AS geom FROM '||schema_a||'.'||table_result1||' GROUP BY '||a_id||') a WHERE t.a_'||a_id||'=a.'||a_id||';';

EXECUTE
'UPDATE '||schema_a||'.'||table_result2||' t SET b_geom=a.geom FROM (SELECT '||a_id||', CASE WHEN ST_Union(the_geom) IS NULL THEN ST_Collect(the_geom) ELSE ST_Union(the_geom) END AS geom FROM '||schema_a||'.'||table_result1||' GROUP BY '||a_id||') a WHERE t.b_'||a_id||'=a.'||a_id||';';

EXECUTE
'DELETE FROM '||schema_a||'.'||table_result1||' WHERE '||a_id||' IN (SELECT '||a_id||' FROM '||schema_a||'.pgoverlap_tmp4);';

--Remove tmp tables:

EXECUTE
'DROP TABLE '||schema_a||'.pgoverlap_tmp1;';
EXECUTE
'DROP TABLE '||schema_a||'.pgoverlap_tmp2;';
EXECUTE
'DROP TABLE '||schema_a||'.pgoverlap_tmp3;';
EXECUTE
'DROP TABLE '||schema_a||'.pgoverlap_tmp4;';
EXECUTE
'DROP TABLE '||schema_a||'.pgoverlap_tmp5;';


END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION public._pgoverlap(varchar, varchar, varchar, varchar, varchar) OWNER TO postgres;
Last modified 6 years ago Last modified on May 11, 2012 9:33:49 PM