Geometry Snapping using JTS in Oracle

In my exploration of the possibilities of using Java Topology Suite’s (JTS) robust algorithms with Oracle SDO_GEOMETRY I have moved to expose three methods within JTS’s GeometrySnapper (com.vividsolutions.jts.operation.overlay.snap) class:

  • snap – Snaps two geometries to each other with both being able to move.
  • snapTo – Snaps the vertices of the source geometry to the vertices of the given snap geometry.
  • snapToSelf – Snaps the vertices of the source geometry to itself.

The following examples not not exhaust the possibilities in the implementation. I leave it up to you to install try each function on your own data. Note: I do not test geodetic data though the functions to snap geometries through use of sdo_cs.transform are in the package.

All the functions are accessed by the Spatial Companion For Oracle (SC4O) PLSQL package.

1. Snap Two Lines to Each Other

This example might be of use with GPS track logs.

Before Image

 WITH q_geoms AS (
    SELECT MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(0.2,0.4,9.8,10.5,19.7,-0.2,30.2,9.6)) AS geom1,
            MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(0,0,10,10,20,0,30,10)) AS snapGeom
      FROM dual
 )
 SELECT SC4O.ST_Snap(geom1,snapgeom,1.0,3) AS SnappedLines
   FROM q_geoms;
 
 SNAPPEDLINES
 -----------------------------------------------------------------------------------------------------------------------------
 MDSYS.SDO_GEOMETRY(2004,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,9,2,1),MDSYS.SDO_ORDINATE_ARRAY(0,0,10,10,20,0,30,10,0,0,10,10,20,0,30,10))

After Image

2. Snap One Line to Another

In the first image above, the red line will be snapped to the blue line.

 -- Snap first line to second
 WITH q_geoms AS (
    SELECT MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(0.2,0.4,9.8,10.5,19.7,-0.2,30.2,9.6)) AS line1,
            MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(0,0,10,10,20,0,30,10)) AS snapGeom
      FROM dual
 )
 SELECT SC4O.ST_SnapTo(line1,snapgeom,1.0,3) AS SnappedLine1
   FROM q_geoms;

 SNAPPEDLINE1
 -------------------------------------------------------------------------
 MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(0,0,10,10,20,0,30,10))

After Image

3. Snap a Point To a Polygon

Before Image

 -- Point to Area
 WITH q_geoms AS (
 SELECT MDSYS.SDO_GEOMETRY(2001,NULL,MDSYS.SDO_POINT_TYPE(-7.091,1.347,NULL),NULL,NULL) AS point,
         MDSYS.SDO_GEOMETRY(2003,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),MDSYS.SDO_ORDINATE_ARRAY(-8.369,14.803,-8.191,8.673,-8.072,0.400,5.737,0.400,5.142,14.922,-8.369,14.803)) AS snapGeom
   FROM dual
 )
 SELECT SC4O.ST_SnapTo(point,snapgeom,2.0,3) AS snappedPoint
   FROM q_geoms;

 SNAPPEDPOINT
 ----------------------------------
 MDSYS.SDO_GEOMETRY(2001,NULL,MDSYS.SDO_POINT_TYPE(-8.072,0.4,NULL),NULL,NULL)

After Image

4. Snap a Line To a Polygon

Before Image

 -- Snap a line to an area
 WITH q_geoms AS (
 SELECT MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(-8.339,-1.553,-8.682,8.496,-8.476,16.728)) AS line,
         MDSYS.SDO_GEOMETRY(2003,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),MDSYS.SDO_ORDINATE_ARRAY(-8.369,14.803,-8.191,8.673,-8.072,0.400,5.737,0.400,5.142,14.922,-8.369,14.803)) AS snapGeom
   FROM dual
 )
 SELECT SC4O.ST_SnapTo(line,snapgeom,0.75,3) AS snappedLine
   FROM q_geoms;

 SNAPPEDLINE
 --------------------------------------------------------------------------------
 MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(-8.339,-1.553,-8.072,0.4,-8.191,8.673,-8.369,14.803,-8.476,16.728))

After Image

In this image both the before and after lines are shown.

5. Snap One Polygon To Another

Before Image

The gaps between the three “adjoining” vertices are as shown in the following images.

Top Middle Bottom
!http://www.spatialdbadvisor.com/images/141.png! !http://www.spatialdbadvisor.com/images/142.png! !http://www.spatialdbadvisor.com/images/140.png!

Knowing these gaps we can set a suitable snapping tolerance to bring snap the left polygon to the right one.

 -- Snap one area to another
 WITH q_geoms AS (
 SELECT MDSYS.SDO_GEOMETRY(2003,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),MDSYS.SDO_ORDINATE_ARRAY(-24.089,0.348,-8.339,0.553,-8.682,8.496,-8.476,14.728,-24.020,14.522,-24.089,0.348)) AS poly,
         MDSYS.SDO_GEOMETRY(2003,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),MDSYS.SDO_ORDINATE_ARRAY(-8.369,14.803,-8.191,8.673,-8.072,0.400,5.737,0.400,5.142,14.922,-8.369,14.803)) AS snapPoly
   FROM dual
 )
 SELECT SC4O.ST_SnapTo(poly,snapPoly,0.75,3) AS snappedPoly
   FROM q_geoms;

 SNAPPEDPOLY
 --------------------------------------------------------------------------------------------------
 MDSYS.SDO_GEOMETRY(2003,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),MDSYS.SDO_ORDINATE_ARRAY(-24.089,0.348,-8.072,0.4,-8.191,8.673,-8.369,14.803,-24.02,14.522,-24.089,0.348))

The resultant polygon after snapping is shown below.

After Image

I think you will agree that this stuff is pretty cool! Well done JTS and Martin Davis! Now, coupled with the JTS PolygonBuilder functionality, and the geometry editing functions in my GEOM package, we have the basis of some pretty useful editing of geometries. Those functions are:

 FUNCTION Move( p_geometry    IN MDSYS.SDO_GEOMETRY,
                 p_tolerance   IN NUMBER,
                 p_deltaX      IN NUMBER,
                 p_deltaY      IN NUMBER,
                 p_deltaZ      IN NUMBER             := NULL,
                 p_mbr         IN &&defaultSchema..MBR := NULL,
                 p_filter_geom IN MDSYS.SDO_GEOMETRY := NULL,
                 p_filter_mask IN VARCHAR2           := 'INSIDE'
               )
     RETURN MDSYS.SDO_GEOMETRY Deterministic;
 
   FUNCTION Scale( p_geometry    IN MDSYS.SDO_GEOMETRY,
                    p_diminfo     IN MDSYS.SDO_DIM_ARRAY,
                    p_XFactor     IN NUMBER,
                    p_YFactor     IN NUMBER,
                    p_ZFactor     IN NUMBER  := NULL
                 )
     RETURN MDSYS.SDO_GEOMETRY Deterministic;  
 
   FUNCTION Rotate ( p_geometry IN MDSYS.SDO_Geometry,
                      p_dimarray IN MDSYS.SDO_Dim_Array,
                      p_X        IN NUMBER,
                      p_Y        IN NUMBER,
                      p_rotation IN NUMBER := 0)
     RETURN MDSYS.SDO_Geometry deterministic;

   FUNCTION Affine(p_geom IN mdsys.sdo_geometry,
                    p_a NUMBER,
                    p_b NUMBER,
                    p_c NUMBER,
                    p_d NUMBER,
                    p_e NUMBER,
                    p_f NUMBER,
                    p_g NUMBER,
                    p_h NUMBER,
                    p_i NUMBER,
                    p_xoff NUMBER,
                    p_yoff NUMBER,
                    p_zoff NUMBER)
     RETURN mdsys.sdo_geometry deterministic;

   FUNCTION Densify( p_geometry  IN MDSYS.SDO_Geometry,
                      p_tolerance IN NUMBER,
                      p_distance  IN NUMBER )
            RETURN MDSYS.SDO_Geometry deterministic;
 
   PROCEDURE Split( p_line      IN mdsys.sdo_geometry,
                     p_point     IN mdsys.sdo_geometry,
                     p_tolerance IN NUMBER,
                     p_out_line1 OUT nocopy mdsys.sdo_geometry,
                     p_out_line2 OUT nocopy mdsys.sdo_geometry );

   FUNCTION removeDuplicateCoordinates (p_geometry IN MDSYS.SDO_Geometry,
                                         p_diminfo  IN MDSYS.SDO_Dim_Array)
     RETURN MDSYS.SDO_Geometry deterministic;

   FUNCTION Roundordinates( P_Geometry        IN Mdsys.Sdo_Geometry,
                             P_X_Round_Factor  IN NUMBER,
                             P_Y_Round_Factor  IN NUMBER := NULL,
                             P_Z_Round_Factor  IN NUMBER := NULL,
                             p_m_round_factor  IN NUMBER := NULL)
     RETURN Mdsys.Sdo_Geometry Deterministic;

   FUNCTION Parallel(p_geometry   IN mdsys.sdo_geometry,
                      p_distance   IN NUMBER,
                      p_tolerance  IN NUMBER,
                      p_curved     IN NUMBER := 0)
     RETURN mdsys.sdo_geometry DETERMINISTIC;

   FUNCTION SDO_AddPoint(p_geometry   IN MDSYS.SDO_Geometry,
                          p_point      IN MDSYS.Vertex_Type,
                          p_position   IN NUMBER )
     RETURN MDSYS.SDO_Geometry Deterministic;

   FUNCTION SDO_RemovePoint(p_geometry  IN MDSYS.SDO_Geometry,
                             p_position  IN NUMBER)
     RETURN MDSYS.SDO_Geometry Deterministic;

   FUNCTION SDO_SetPoint(p_geometry   IN MDSYS.SDO_Geometry,
                          p_point      IN MDSYS.Vertex_Type,
                          p_position   IN NUMBER )
     RETURN MDSYS.SDO_Geometry Deterministic;

   FUNCTION SDO_VertexUpdate(p_geometry  IN MDSYS.SDO_Geometry,
                              p_old_point IN MDSYS.Vertex_Type,
                              p_new_point IN MDSYS.Vertex_Type)
     RETURN MDSYS.SDO_Geometry Deterministic;

   FUNCTION fix_ordinates(p_geometry IN mdsys.sdo_geometry,
                           p_x_formula IN varchar2,
                           p_y_formula IN varchar2,
                           p_z_formula IN varchar2 := NULL,
                           p_w_formula IN varchar2 := NULL)
     RETURN mdsys.SDO_GEOMETRY Deterministic;

   FUNCTION Extend( p_geom      IN mdsys.sdo_geometry,
                     p_extension IN NUMBER,
                     p_tolerance IN NUMBER,
                     p_end       IN varchar2 DEFAULT 'START' )
     RETURN mdsys.sdo_geometry deterministic;

   FUNCTION SwapOrdinates( p_geom IN mdsys.sdo_geometry,
                            p_pair IN varchar2 DEFAULT 'XY' /* Can be XY, XZ, XM, YZ, YM, ZM */ )
     RETURN mdsys.sdo_geometry deterministic;

I hope this article is of interest to someone out there.

Leave a Reply

Your email address will not be published. Required fields are marked *