Replacement for SDO_GEOM.RELATE – ST_Relate based on JTS

Even old dogs like me forget things and have to learn them again. You know, I am sure that I knew that SDO_GEOM.RELATE was a Spatial licensed feature and not available in Locator, but I clean forgot until a situation with a customer required me to look at an alternative.

The reason is because this customer is trying to reduce their Oracle license costs (as part of a future migration to SQL Server) by dropping their Enterprise license and going back to Standard Edition. As part of this, an audit of Spatial feature usage showed that a limited number of functions were being used eg SDO_UNION and one or two SDO_LRS functions but what I had forgotten about was the use of SDO_GEOM.RELATE.

Now my SC4O wrapper over the Java Topology Suite 1.12 has been a great success but did not include a replacement for RELATE. Dipping in to the JTS toolkit I have found, and now exposed, the relevant components to be able to release a “replacement” for SDO_GEOM.RELATE.

I put “replacement” in inverted commas as it does not replace SDO_GEOM.RELATE exactly as it is implemented. To do so would require another 3 or 4 days work or testing but I can see little value in doing so. If anyone is interested in what this work might entail please contact me.

Anyway, here is the SC4O function wrapper:

  /**
     * ST_Relate
     * Implements a license free version of sdo_geom.RELATE.
     * @note Supports JTS named topological relationships and not Oracle specific keywords like OVERLAPBDYDISJOINT
     * @param p_geom1     : sdo_geometry : geometry which will be compared to second
     * @param p_mask      : varchar2     : Mask containing DETERMINE, ANYINTERACT or a list of comma separated topological relationships
     * @param p_geom2     : sdo_geometry : geometry which will be compared to first.
     * @param p_precision : number of decimal places of precision of a geometry
     * @return String     : Result of processing
     * @throws SQLException
     * @history Simon Greener, November 2011, Original coding.
     */
   FUNCTION ST_Relate(p_geom1     IN mdsys.sdo_geometry,
                      p_mask      IN varchar2,
                      p_geom2     IN mdsys.sdo_geometry,
                      p_precision IN NUMBER)
     RETURN varchar2 Deterministic;

Here is an example test SQL statement.

 SELECT a.id1, a.geom1.sdo_gtype,
        a.id2, a.geom2.sdo_gtype,
        sdo_geom.relate(geom1,'DETERMINE',  geom2,0.05) AS sdo_relate,
            SC4O.ST_Relate(geom1,'DETERMINE',  geom2,1   ) AS jtso_relate,
        sdo_geom.relate(geom1,'TOUCH',      geom2,0.05) AS sdo_touch,
            SC4O.ST_Relate(geom1,'TOUCHES',    geom2,1   ) AS jtso_touch,
        sdo_geom.relate(geom1,'ANYINTERACT',geom2,0.05) AS sdo_anyinteract,
            SC4O.ST_Relate(geom1,'ANYINTERACT',geom2,1   ) AS jtso_anyinteract,
        sdo_geom.relate(geom2,'DETERMINE',  geom1,0.05) AS sdo_relate_rev,
            SC4O.ST_Relate(geom2,'DETERMINE',  geom1,1   ) AS jtso_relate_rev
  FROM (SELECT 104 AS id1, sdo_geometry('LINESTRING(1700 200, 1700 900, 2200 900, 1700 1300, 2200 1300)',NULL) AS geom1,
               110 AS id2, sdo_geometry('POINT(1700 200)',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 104 AS id1, sdo_geometry('LINESTRING(1700 200, 1700 900, 2200 900, 1700 1300, 2200 1300)',NULL) AS geom1,
               109 AS id2, sdo_geometry('POINT(2200 900)',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 104 AS id1, sdo_geometry('LINESTRING(1700 200, 1700 900, 2200 900, 1700 1300, 2200 1300)',NULL) AS geom1,
               111 AS id2, sdo_geometry('LINESTRING(1300 400, 2200 1300)',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 101 AS id1, sdo_geometry('POLYGON((1300 600, 1700 600, 1700 1300, 1300 1300, 1300 600))',NULL) AS geom1,
               108 AS id2, sdo_geometry('POINT(1400 600)',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 101 AS id1, sdo_geometry('POLYGON((1300 600, 1700 600, 1700 1300, 1300 1300, 1300 600))',NULL) AS geom1,
               109 AS id2, sdo_geometry('POINT(2200 900)',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 101 AS id1, sdo_geometry('POLYGON((1300 600, 1700 600, 1700 1300, 1300 1300, 1300 600))',NULL) AS geom1,
               105 AS id2, sdo_geometry('POINT(1400 1100)',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 101 AS id1, sdo_geometry('POLYGON((1300 600, 1700 600, 1700 1300, 1300 1300, 1300 600))',NULL) AS geom1,
               106 AS id2, sdo_geometry('POINT(1300 1300)',NULL)  AS geom2
          FROM dual
        UNION ALL
        SELECT 101 AS id1, sdo_geometry('POLYGON((1300 600, 1700 600, 1700 1300, 1300 1300, 1300 600))',NULL) AS geom1,
               104 AS id1, sdo_geometry('LINESTRING(1700 200, 1700 900, 2200 900, 1700 1300, 2200 1300)',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 101 AS id1, sdo_geometry('POLYGON((1300 600, 1700 600, 1700 1300, 1300 1300, 1300 600))',NULL) AS geom1,
               112 AS id2, sdo_geometry('POLYGON((1200 300, 1600 300, 1600 800, 1200 800, 1200 300))',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 101 AS id1, sdo_geometry('POLYGON((1300 600, 1700 600, 1700 1300, 1300 1300, 1300 600))',NULL) AS geom1,
               102 AS id2, sdo_geometry('POLYGON((1400 1300, 1600 1300, 1600 1650, 1400 1650, 1400 1300))',NULL) AS geom2
          FROM dual
        UNION ALL
        SELECT 101 AS id1, sdo_geometry('POLYGON((1300 600, 1700 600, 1700 1300, 1300 1300, 1300 600))',NULL) AS geom1,
               103 AS id2, sdo_geometry('POLYGON((1600 1300, 1400 1300, 1400 1000, 1600 1000, 1600 1300))',NULL) AS geom2
          FROM dual
        ) a;

And the answer is as follows.

ST_Relate is downloadable as part of the C4O package.

I hope this is of interest to someone.