3

I have managed to get the intersection between a circle around a point and objects in a geometry using this PostGIS query:

SELECT ST_Intersection(sea_level.geom, ST_Buffer(ST_Transform(ST_SetSRID(ST_MakePoint(13.01667,55.71667), 4326), 3006), 10000))
FROM sea_level
LIMIT 20000;

This works fine if I set the limit high enough to get "valuable" objects, but most of the returned rows are just "empty" objects (as GeoJSON):

{"type":"Polygon","crs":{"type":"name","properties":{"name":"EPSG:3006"}},"coordinates":[]}

If I set the limit too low, I will only get these "empty" rows, and none of the valuable rows with the actual intersecting objects. What are these "empty" rows and why are they returned?

8
  • 1
    Order in the result set is random without an ORDER BY (except with a Primary Key column in the SELECT), and the result set in your query includes all rows insea level without a filter expression (e.g. WHERE, JOIN), so also those that don't have a spatial overlap (which are NULL then). Hint: do not use a buffer for proximity filter - use ST_DWithin instead.
    – geozelot
    Commented Dec 11, 2021 at 22:33
  • Thanks. I tried to use ST_DWithin first: ``` SELECT geom FROM sea_level WHERE ST_DWithin(sea_level.geom,ST_Transform(ST_SetSRID(ST_MakePoint(13.01667,55.71667), 4326), 3006), 64) LIMIT 10; ``` returns one single row contains a huge geometry covering the whole dataset. If I change the distance to below 64, it returns no rows (not even any null rows). So, why does it not return any null rows, and why only one row with the whole geometry of the dataset, while the ST_Intersects query returns what I want, i.e. multiple rows with intersecting objects, but also more than I want, the null rows?
    – hajons
    Commented Dec 12, 2021 at 14:38
  • How many features you have in the table "sea_level"? Without testing it looks like your query is returning one line per each feature in the sea_level, with empty geometry if there is no intersection.
    – user30184
    Commented Dec 12, 2021 at 14:48
  • There are 1356 features in sea_level
    – hajons
    Commented Dec 12, 2021 at 16:35
  • ... and yes, you are correct, all 1356 features are returned, with empty geometry when no intersection. What is the right way to just get the intersecting features?
    – hajons
    Commented Dec 12, 2021 at 19:07

2 Answers 2

5

Because my comment about proximity searches seems to have caused confusion: the correct way to implement a high performance proximity filter to help with this specific scenario would be:

SELECT ST_Intersection(sl.geom, ST_Buffer(poi, 10000)) AS geom
FROM   sea_level AS sl
CROSS JOIN LATERAL
       ST_Transform(ST_SetSRID(ST_MakePoint(13.01667, 55.71667), 4326), 3006) AS poi
WHERE  ST_DWithin(sl.geom, poi, 10000)
;

Here

  • ST_DWithin quickly filters for all sl.geom that would not fall inside the ST_Buffer, and thus
    • reduces the actual (and costly) ST_Intersection operation, on (less but still costly) ST_Buffer'ed geometries, to only those sl.geom that are relevant
    • in effect does not include NULL values for non-intersecting features
  • the CROSS JOIN LATERAL moves the point-of-interest creation (poi) to a cache-able function value expression, mainly to avoid repeated calls

Without the need to intersect an ST_Buffer, the filter would e.g. use a call to ST_Intersects. In general, for where it's applicable, and functions interchangeable, the benefit of using ST_DWithin is its algorithmic complexity, which may (dependent on geometry types) be lesser than that of a spatial overlap check except for the worst-case.


If you see any significant performance boost is questionable: the planner may neglect any indexes completely for a table that small, and a few thousand spatial overlap calculations on simple point buffers are fairly fast these days. But add two orders of magnitude to the row count, or more complex geometries, and the difference in execution times will be minutes to seconds.

2
  • One minor point - the buffer polygon is not a full circle, so it's possible that there will be some geometries which satisfy the ST_DWithin distance, but still do not intersect the buffer. To avoid this it might be better to use ST_Intersects(ST_Buffer...)) as the condition.
    – dr_jts
    Commented Dec 16, 2021 at 17:50
  • @dr_jts and that's a good catch. Yet, buffers for proximity searches are just so bad. And after all, can a buffer ever be the circle it promises to be...?
    – geozelot
    Commented Dec 16, 2021 at 20:33
3

To avoid returning empty intersection results, run the intersection only on geometries which actually intersect by using a ST_Intersects condition:

SELECT ST_Intersection(sea_level.geom, poi.geom)
FROM sea_level JOIN
  (SELECT ST_Buffer(ST_Transform(ST_SetSRID(
              ST_MakePoint(13.01667,55.71667), 4326), 3006), 10000) AS geom) poi
ON ST_Intersects(sea_level.geom, poi.geom)
LIMIT 20000;

This also improves performance, because:

  • ST_Intersects takes advantage of a spatial index on sea_level
  • the ST_Intersects calculation is faster than ST_Intersection, so it's better to use it first
1
  • Exact with respect to the buffered geometry, for a small tradeoff in performance. Can't have it all ,)
    – geozelot
    Commented Dec 16, 2021 at 21:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.