Search code examples
sqloracle-databaseoracle-spatial

Oracle Spatial Geometry covered by the most


I have a table which contains a number of geometries. I am attempting to extract the one which is most covered by another geometry.

This is best explained with pictures and code.

Currently I am doing this simple spatial query to get any rows that spatially interact with a passed in WKT Geometry

SELECT ID, NAME FROM MY_TABLE WHERE 
sdo_anyinteract(geom, 
sdo_geometry('POLYGON((400969 95600,402385 95957,402446 95579,400905 95353,400969 95600))',27700)) = 'TRUE';

Works great, returns a bunch of rows that interact in any way with my passed in geometry.

What I preferably want though is to find which one is covered most by my passed in geometry. Consider this image.

Polygon selection example

The coloured blocks represent 'MY_TABLE'. The black polygon over the top represents my passed in geometry I am searching with. The result I want returned from this is Polygon 2, as this is the one that is most covered by my polygon. Is this possible? Is there something I can use to pull the cover percentage in and order by that or a way of doing it that simply returns just that one result?

--EDIT--

Just to supplement the accepted answer (which you should go down and give an upvote as it is the entire basis for this) this is what I ended up with.

SELECT name, MI_PRINX, 
SDO_GEOM.SDO_AREA(
  SDO_GEOM.SDO_INTERSECTION(
    GEOM, 
    sdo_geometry('POLYGON((400969.48717156524 95600.59583240788,402385.9445972018 95957.22742049221,402446.64806962677 95579.91508788493,400905.95874489535 95353.03765349534,400969.48717156524 95600.59583240788))',27700)
    ,0.005
  )
,0.005) AS intersect_area 
FROM LIFE_HEATHLAND WHERE sdo_anyinteract(geom, sdo_geometry('POLYGON((400969.48717156524 95600.59583240788,402385.9445972018 95957.22742049221,402446.64806962677 95579.91508788493,400905.95874489535 95353.03765349534,400969.48717156524 95600.59583240788))',27700)) = 'TRUE'
ORDER BY INTERSECT_AREA DESC;

This returns me all the results that intersect my query polygon with a new column called INTERSECT_AREA, which provides the area. I can then sort this and pick up the highest number.


Solution

  • Just compute the intersection between each of the returned geometries and your query window (using SDO_GEOM.SDO_INTERSECTION()), compute the area of each such intersection (using SDO_GEOM.SDO_AREA()) and return the row with the largest area (order the results in descending order of the computed area and only retain the first row).

    For example, the following computes how much space Yellowstone National Park occupies in each state it covers. The results are ordered by area (descending).

    SELECT s.state,
           sdo_geom.sdo_area (
             sdo_geom.sdo_intersection (
               s.geom, p.geom, 0.5),
             0.5, 'unit=sq_km') area
      FROM us_states s, us_parks p
     WHERE SDO_ANYINTERACT (s.geom, p.geom) = 'TRUE'
       AND p.name = 'Yellowstone NP'
     ORDER by area desc;
    

    Which returns:

    STATE                                AREA
    ------------------------------ ----------
    Wyoming                        8100.64988
    Montana                        640.277886
    Idaho                          154.657145
    
    3 rows selected.
    

    To only retain the row with the largest intersection do:

    SELECT * FROM (
      SELECT s.state,
             sdo_geom.sdo_area (
               sdo_geom.sdo_intersection (
                 s.geom, p.geom, 0.5),
               0.5, 'unit=sq_km') area
        FROM us_states s, us_parks p
       WHERE SDO_ANYINTERACT (s.geom, p.geom) = 'TRUE'
         AND p.name = 'Yellowstone NP'
       ORDER by area desc
    )
    WHERE rownum = 1;     
    

    giving:

    STATE                                AREA
    ------------------------------ ----------
    Wyoming                        8100.64988
    
    1 row selected.
    

    The following variant also returns the percentage of the park's surface in each intersecting state:

    WITH p AS (
      SELECT s.state,
             sdo_geom.sdo_area (
               sdo_geom.sdo_intersection (
                 s.geom, p.geom, 0.5),
               0.5, 'unit=sq_km') area
        FROM us_states s, us_parks p
       WHERE SDO_ANYINTERACT (s.geom, p.geom) = 'TRUE'
         AND p.name = 'Yellowstone NP'
    )
    SELECT state, area,
           RATIO_TO_REPORT(area) OVER () * 100 AS pct
    FROM p
    ORDER BY pct DESC;
    

    If you want to return the geometry of the intersections, just include that into your result set.