Search code examples
oraclespatialoracle-spatialgrahams-scan

polygon based on perimeter points - oracle spatial


I've a table with points. I would like to create polygon based on this points and then return only these points which are on perimeter(exclude points which are inside a polygon) . Is there any easy way to do that on database level using SDO GEOMETRY? I find sth like a GrahamScan, but I can't find anything like that for Spatial


Solution

  • I can't think of any easy answer with the built-in packages that Oracle supplies. You might have to consider writing some fairly horrible code to do this if you want to solve the problem in Oracle.

    A cheesy solution might be to break this into a few steps:

    1. Brute force all possible lines between every set of two points (select the cartesian product of the points, construct these into lines):
    2. Find all lines where there is no intersection with any other line (the outer-most perimeter) and get the point values.
    3. Join the point values back to your original table to retrieve any data (if necessary).

    This will likely not perform or scale well, but it should work if your dataset isn't very big.

    Here's a quick illustration (untested SQL)...

    Inputs:

    SELECT sdo_geometry(2001, NULL, sdo_point_type(tbl.x, tbl.y,  NULL), NULL, NULL)
    FROM my_table tbl
    

    Inputs to algorithm

    Cartesian product of coordinates converted to lines:

    WITH point_cartesian AS (
      SELECT 
        tbl.x x1
      , tbl.y y1
      , tbl2.x x2
      , tbl2.y y2
      FROM my_table tbl
      CROSS JOIN my_table tbl2
      WHERE tbl.x != tbl2.x 
      OR tbl.y != tbl2.y
    )
    SELECT sdo_geometry(
      2002
    , NULL
    , NULL
    , sdo_elem_info_array(1, 2, 1)
    , sdo_ordinate_array(x1, y1, x2, y2)
    )
    FROM point_cartesian
    

    Cartesian product of coordinates converted to lines

    Identify desired lines:

    WITH point_cartesian AS (
      SELECT 
        tbl.x x1
      , tbl.y y1
      , tbl2.x x2
      , tbl2.y y2
      FROM my_table tbl
      CROSS JOIN my_table tbl2
      WHERE tbl.x != tbl2.x 
      OR tbl.y != tbl2.y
    )
    , lines AS (
      SELECT sdo_geometry(
        2002
      , NULL
      , NULL
      , sdo_elem_info_array(1, 2, 1)
      , sdo_ordinate_array(x1, y1, x2, y2)
      ) geom
      , ROWNUM line_id
      FROM point_cartesian
    )
    SELECT *
    FROM lines l1
    WHERE NOT EXISTS (
      SELECT 1
      FROM lines l2
      WHERE l2.line_id != l1.line_id
      AND sdo_geom.sdo_intersect (l1.geom, l2.geom, 0.05) IS NOT NULL
    )
    

    Identify lines

    Please note: the SQL is untested, but hopefully you get the idea.