Splitting a polygon using one or more linestrings

One of the geoprocessing functions that seems to be poorly implemented is the ability to clip a polygon using one or more linestrings such that the result is a number of polygons.

Here is an example of someone asking for help on the Oracle Technology Network’s Spatial forum:

I have a task where i have to draw a 30 meter buffer around roads feature class and wherever it comes across intersection there that buffer should be splitted according with intersection, for example if it is T inetersect, then polygon should be splitted in three piece, or if it has four exit points, then it should be splitted in four piece, please anyone can guide me how can i achieve this task?

Here is an example “T” intersection:

 SELECT 'Line'     AS ltype, sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(5,0,5,5)) AS geom FROM dual UNION ALL
 SELECT 'IntLine1' AS ltype, sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(0,5,5,5)) AS geom FROM dual UNION ALL
 SELECT 'IntLine2' AS ltype, sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(5,5,10,5)) AS geom FROM dual;
 -- Results
 --
 LTYPE    GEOM
 -------- ------------------------------------------------------------------------------------------------------
 Line     MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(5,0,5,5))
 IntLine1 MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(0,5,5,5))
 IntLine2 MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(5,5,10,5))

These look like this:

The polygon will be created by buffering “Line” by 3 meters. In order to correctly cut the buffered polygon as the questioner requires, we need to extend “Line” at its start by more than 3 meters. The buffering will be done by my Spatial Companion 4 Oracle (SC4O) ST_Buffer rather than Oracle’s SDO_GEOM.SDO_BUFFER but the latter could be used as easily. The extension of “Line” at its start is done by my GEOM.EXTEND function (see free pl/sql packages).

 SELECT 'LineExtend' AS ltype, codesys.GEOM.extend(sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(5,0,5,5)),3,0.005,'START') AS geom FROM dual UNION ALL
 SELECT 'PolyBuff'   AS ltype, codesys.sc4o.st_buffer(sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(5,0,5,5)),3,2) AS buff FROM dual;
 -- Results
 --
 LTYPE      GEOM
 ---------- ------------------------------------------------------------------------------------------------------
 LineExtend MDSYS.SDO_GEOMETRY(2002,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(5,-3,5,5))
 PolyBuff   MDSYS.SDO_GEOMETRY(2003,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),MDSYS.SDO_ORDINATE_ARRAY(2,5,2,0,2.06,-0.59,2.23,-1.15,2.51,-1.67,2.88,-2.12,3.33,-2.49,3.85,-2.77,4.41,-2.94,5,-3,5.59,-2.94,6.15,-2.77,6.67,-2.49,7.12,-2.12,7.49,-1.67,7.77,-1.15,7.94,-0.59,8,0,8,5,7.94,5.59,7.77,6.15,7.49,6.67,7.12,7.12,6.67,7.49,6.15,7.77,5.59,7.94,5,8,4.41,7.94,3.85,7.77,3.33,7.49,2.88,7.12,2.51,6.67,2.23,6.15,2.06,5.59,2,5))

The result of the extend (red dash lines) and the buffer can be seen here:

We now have the components for splitting a (buffered) polygon with a set of lines.

One might think that if we simply collect the linestrings in to a single multilinestring and use it to clip the buffered polygon using SDO_GEOM.SDO_INTERSECTION we will have the right result. But this will not happen because SDO_INTERSECTION:

Returns a geometry object that is the topological intersection (AND operation) of two geometry objects.

In other words it will clip the lines and return them as they are the only common elements of the two objects. (In the image below note the lines are wholly within the buffer.

Similarly, SDO_XOR, SDO_DIFFERENCE etc all do not work.

So, how do we do this?

My approach is as follows (includes steps already done):

1. Buffer “Line” by 3 meters;
2. Extend “Line” at start by 3 meters;
3. Intersect (clip) all three lines with buffered polygon;
4. Convert buffered polygon into a linestring (outer boundary);
5. Pump collected set of linestring into the Java Topology Suite’s (JTS) PolygonBuilder routine exposed via my SC4O.ST_PolygonBuilder function. This function will build a set of polygons from the input lines.

Here is the SQL:

  SELECT codesys.sc4o.ST_PolygonBuilder(
            codesys.T_GeometrySet(
               codesys.sc4o.st_intersection(c.mLines,
                                            codesys.sc4o.st_buffer(sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(5,0,5,5)),3,2),
                                           3)),3) AS RESULT
   FROM (SELECT sdo_aggr_concat_lines(b.geom) AS mLines
           FROM (SELECT 'LineExtend' AS ltype, codesys.GEOM.extend(sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(5,0,5,5)),3,0.005,'START') AS geom FROM dual UNION ALL
                 SELECT 'IntLine1'   AS ltype, sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(0,5,5,5)) AS geom FROM dual UNION ALL
                 SELECT 'IntLine2'   AS ltype, sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(5,5,10,5)) AS geom FROM dual UNION ALL
                 SELECT 'buffer'     AS ltype, sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),a.buff.sdo_ordinates) AS geom
                  FROM (SELECT codesys.sc4o.st_buffer(sdo_geometry(2002,NULL,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(5,0,5,5)),3,2) AS buff
                          FROM dual ) a
                ) b
         ) c;
 -- Result
 --
 RESULT
 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 MDSYS.SDO_GEOMETRY(2007,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1,39,1003,1,63,1003,1),MDSYS.SDO_ORDINATE_ARRAY(7.12,7.12,6.67,7.49,6.15,7.77,5.59,7.94,5,8,4.41,7.94,3.85,7.77,3.33,7.49,2.88,7.12,2.51,6.67,2.23,6.15,2.06,5.59,2,5,5,5,8,5,7.94,5.59,7.77,6.15,7.49,6.67,7.12,7.12,7.77,-1.15,7.94,-0.59,8,0,8,5,5,5,5,-3,5.59,-2.94,6.15,-2.77,6.67,-2.49,7.12,-2.12,7.49,-1.67,7.77,-1.15,2,0,2.06,-0.59,2.23,-1.15,2.51,-1.67,2.88,-2.12,3.33,-2.49,3.85,-2.77,4.41,-2.94,5,-3,5,5,2,5,2,0))

This looks like this:

Which very much looks like the correct result.

I hope this helps someone.