Search code examples
postgresqlleafletgeospatialpostgis

Postgis - ST_within didn't do what I want. How to find a point in a hollow area?


See the screen print.

I ran a spatial query in Postgis to return the electoral constituency (area) that a point on the map lies in. The query uses a ST_within function where the point is within a polygon.

As you can see from the print, the point is not actually 'in' the polygon area of York Outer although technically you might say it's 'within' it, or at least Postgis thinks so. The point would actually lie in York Central.

I'm sure Postgis actually returns both but since I only fetch the first record from the cursor, this is what I see.

A point can only be in one electoral constituency at a time and this query has returned the wrong one or rather I asked the wrong question of the database.

Which function should I be using to ensure I always return the correct area for a point where it's possible the area may have a hollow interior or be a strange shape?

ST_within allows a non overlapping point

Thanks

Phil


Solution

  • This should work as you described it. Maybe something is wrong with the data? Could you provide a small repro, with polygon / point data?

    Also, a somewhat common reason for such problems is not valid GIS data. You can check the polygon shape with PostGIS's ST_IsValid function. If the data is not valid, different tools might interpret it in different ways, and how GIS data is drawn might not match what PostGIS thinks this data represents, causing more confusion.

    Here is a simple repro showing it works as you expect it to work, with point inside the outer polygon's hole only st_within the inner polygon, not the outer one:

    select st_astext(point), name 
    from 
      (select 
           'outer' as name, 
           st_geomfromtext('polygon((0 0, 30 0, 30 30, 0 30, 0 0), (10 10, 20 10, 20 20, 10 20, 10 10))') g
      union all 
      select 
          'inner' as name, 
           st_geomfromtext('polygon((10 10, 20 10, 20 20, 10 20, 10 10))') g
      ) shapes
    cross join
      (select st_geomfromtext('point(15 15)') point
       union all
       select st_geomfromtext('point(5 5)') point
      ) points
    where st_within(point, g)
    

    My results are

    1   POINT(5 5)     outer
    2   POINT(15 15)   inner