I am trying to use ST_Difference to create a set of polygons (processing.trimmedparcelsnew) that do not contain any of the area covered by another set of polygons (test.single_geometry_1) using PostGis 2.1 (and Postgres SQL 9.3). Here is my query:
CREATE TABLE processing.trimmedparcelsnew AS
SELECT
orig.id, ST_Difference(orig.geom, cont.geom) AS difference
FROM
test.single_geometry_1 cont,
test.multi_geometry_1 orig;
But the resulting polygons have not been trimmed, instead they seem to have been split where they intersect with the other layer. I have tried just running the select without putting the result in a table and everything else I can think of, but I can't seem to get this function to work.
I have attached a picture of the result
After comments, I have tried adding a WHERE clause. I want the parcels that have no intersections, and the intersecting areas of the other parcels removed (the layer test.single_geometry represents contamination I want removed from my parcels). I tried an intersect but of course I actually want the non intersections so I am now trying a disjoint. I have also tried adding the orig to my table but the documentation for ST_Difference (http://postgis.net/docs/ST_Difference.html) does say it returns the exact geometry I need (a geometry that represents that part of geometry A that does not intersect with geometry B), so I am confused as to why I would want the original polygon in my table instead. Anyway, here is my modified code:
CREATE TABLE processing.trimmedparcelsnew AS
SELECT
orig.id, ST_Difference(orig.geom, cont.geom) AS difference, orig.geom AS geom
FROM
test.single_geometry_1 cont,
test.multi_geometry_1 orig
WHERE ST_Disjoint(orig.geom, cont.geom);
Following from dbaston's answer I have now tried:
CREATE TABLE processing.parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom)
FROM test.single_geometry_1 b
WHERE ST_Intersects(a.geom, b.geom)
AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;
The result of this is just a copy of test.multi_geometry_1. Though now the splitting is no longer occurring.
I tried the earlier version, but again just get a copy of test.multi_geometry_1:
CREATE TABLE processing.parcels_trimmed_no_coalesce AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom)
FROM test.single_geometry_1 b
WHERE ST_Intersects(a.geom, b.geom)
AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;
I am starting to wonder if there is something else I am doing wrong? The proceeding statement is:
DROP TABLE IF EXISTS processing.parcels_trimmed_no_coalesce;
And I am running the queries from the PostgreSQL SQL query window and Openjump.
The statement I use to see the table is:
SELECT * FROM processing.parcels_trimmed_no_coalesce;
In the interest of simplification I have now reduced this query down to just:
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom)
FROM test.geometriestocutagainst b
WHERE ST_Intersects(a.geom, b.geom)
AND a.id != b.id)), a.geom)
FROM test.geometriestocut a;
This still results in just the original polygons (test.geometriestocut) when the desired result is the original trimmed against test.geometriestocutagainst.
Answer
A self-join allows you to operate on the relationship between pairs of two features. But I don't think you're interested in pairs: for each feature, you want to operate on the relationship between that feature and all other features in your dataset. You can accomplish this with a subquery expression:
CREATE TABLE parcels_trimmed AS
SELECT id, ST_Difference(geom, (SELECT ST_Union(b.geom)
FROM parcels b
WHERE ST_Intersects(a.geom, b.geom)
AND a.id != b.id))
FROM parcels a;
You might see something odd in the results, though. Parcels that don't have any overlaps are being dropped entirely! That's because the ST_Union
aggregate on an empty recordset is going to be NULL
, and ST_Difference(geom, NULL)
is NULL
. To smooth this over, you need to wrap your ST_Difference
call in a COALESCE
:
CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom)
FROM parcels b
WHERE ST_Intersects(a.geom, b.geom)
AND a.id != b.id)), a.geom)
FROM parcels a;
This means that if the result of ST_Difference
is NULL
, the coalesced expression will evaluate to the original geometry.
The above query will remove overlapping areas from your domain entirely. If you instead want to pick a winner, you could do a.id < b.id
, or some other criterion, instead of a.id != b.id
.
No comments:
Post a Comment