Go to content Go to navigation and search

Current Oracle Spatial Blogs

Search

RSS / Atom

Email me

textpattern

Split Sdo_Geometry Linestring at a known point · Jan 29, 05:06 pm by Simon Greener

I recently received an email from someone asking me:

Just wondering if you could shed some light on a problem I have. In oracle using PLSQL I need to cut a polyline at two know (sic) points (not necessarly vertices and the line may be not be straight) and copy the cut line into another table. I would prefer not to go to linear referencing to do this. I cannot seem to find a function that will cut polylines. I can find the concatinate (sic) functions but this does not help much.

I’m new to oracal (sic) spatial but have a very extensive GIS background mostly ESRI and was suprised that there was not a built in function to split a line.

Any help you could offer would be greatly appreciated and really help me out.

It is true that there there is no function in Oracle Locator/Spatial that can split a linestring at a known point and return the “halfs” as separate linestrings. The closest LRS function in LRS is:

SDO_LRS.SPLIT_GEOM_SEGMENT(
     geom_segment IN SDO_GEOMETRY,
     split_measure IN NUMBER,
     segment_1 OUT SDO_GEOMETRY,
     segment_2 OUT SDO_GEOMETRY);

But note that it does not take a point sdo_geometry but, suprise, surprise, a measure! (Also it is a procedure and not a function.)

My correspondant happened to be lucky as I had just been playing around with some of the vector functions in my GEOM PL/SQL package to find the nearest vector (segment of a linestring) closest to other objects eg:

codesys@XE> select rownum as id,
  2         b.startcoord.x as x1,
  3         b.startcoord.y as y1,
  4         b.endcoord.x as x2,
  5         b.endcoord.y as y2,
  6         sdo_geom.sdo_distance(mdsys.sdo_geometry(2002,NULL,NULL,
  7                                        mdsys.sdo_elem_info_array(1,2,1),
  8                                        mdsys.sdo_ordinate_array(b.startcoord.x,b.startcoord.y,b.endcoord.x,b.endcoord.y)),
  9                               mdsys.sdo_geometry(2001,null,
 10                                        mdsys.sdo_point_type(380326.792,5167489.29,NULL),NULL,NULL),0.005) as linedist
 11    from projline2d a,
 12         table(codesys.geom.getpipedvector2d(a.geom)) b
 13* where linetype = 'VERTEX'
codesys@XE> /
        ID         X1         Y1         X2         Y2   LINEDIST
---------- ---------- ---------- ---------- ---------- ----------
         1 380326.792 5167089.29 380326.792 5167889.29          0
         2 380326.792 5167889.29 380826.792 5167889.29    399.996
         3 380826.792 5167889.29 380126.792 5167489.29 99.2243147

I realised that this was the basis of what my correspondant wanted so I created a procedure called Split and have integrated it into my geom PL/SQL package. The procedure’s type signature is as follows:

procedure Split( p_line      in mdsys.sdo_geometry,
                 p_point     in mdsys.sdo_geometry,
                 p_tolerance in number,
                 p_out_line1 out mdsys.sdo_geometry,
                 p_out_line2 out mdsys.sdo_geometry )

This procedure can be called as follows:

codesys@XE> declare
  2    v_line mdsys.sdo_geometry := mdsys.sdo_geometry(2006,NULL,NULL, mdsys.sdo_elem_info_array(1,2,1,5,2,1), mdsys.sdo_ordinate_array(0,0,5,5,5,5,10
,10));
  3    v_point mdsys.sdo_geometry := mdsys.sdo_geometry(2001,null,mdsys.sdo_point_type(2.5,2.5,NULL),NULL,NULL);
  4    v_oline1 mdsys.sdo_geometry;
  5    v_oline2 mdsys.sdo_geometry;
  6    cursor c_vectors(p_geometry in mdsys.sdo_geometry) Is
  7    select rownum as id,
  8           b.x, b.y
  9      from table(sdo_util.getvertices(p_geometry)) b;
 10    procedure printgeom(p_text in varchar2,p_geom in mdsys.sdo_geometry)
 11    Is
 12      v_geom   mdsys.sdo_geometry;
 13    Begin
 14      dbms_output.put_line(p_text || ': Number of Elements ' || sdo_util.GetNumElem(p_geom));
 15      for v_element in 1..sdo_util.GetNumElem(p_geom) loop
 16        v_geom := sdo_util.Extract(p_geom,v_element);   -- Extract element with all sub-elements
 17        dbms_output.put_line('Element ' || v_element || ' Vertices');
 18        for rec in c_vectors(v_geom) loop
 19          dbms_output.put_line(rec.id||','||rec.x||','||rec.y);
 20        end loop;
 21      end loop;
 22    End PrintGeom;
 23  begin
 24    codesys.geom.split(v_line, v_point,0.005,v_oline1,v_oline2);
 25    PrintGeom('Outline1',v_oline1);
 26    PrintGeom('Outline2',v_oline2);
 27  end;
 28  /
Outline1: Number of Elements 1
Element 1 Vertices
1,0,0
2,2.5,2.5
Outline2: Number of Elements 2
Element 1 Vertices
1,2.5,2.5
2,5,5
Element 2 Vertices
1,5,5
2,10,10
PL/SQL procedure successfully completed.

Now the Split procedure will handle single (2002) and multi-part (2006) linestring geometries. It will also handle situations where the point:

Function not Procedure

For those who want a function instead of a procedure so that the function can be used in SQL, I have added a Split function that returns a set of sdo_geometries as follows.

  Function Split( p_line      in mdsys.sdo_geometry,
                  p_point     in mdsys.sdo_geometry,
                  p_tolerance in number )
    Return codesys.GeometrySetType pipelined;

This function can be used with a table function in a SQL SELECT statement as follows:


codesys@XE> select b.* 2 from table( codesys.geom.split( mdsys.sdo_geometry(2006,NULL,NULL, mdsys.sdo_elem_info_array(1,2,1,5,2,1), mdsys.sdo_ordinate_array(0,0,5,5,5,5,10,10)), 3 mdsys.sdo_geometry(2001,null,mdsys.sdo_point_type(2.5,2.5,NULL),NULL,NULL), 4 0.005) ) b 5 /

GEOMETRY(SDO_GTYPE, SDO_SRID, SDO_POINT(X, Y, Z), SDO_ELEM_INFO, SDO_ORDINATES)
-----------------------------------------------------------------------------------------------------------------------------------
SDO_GEOMETRY(2002, NULL, NULL, SDO_ELEM_INFO_ARRAY(1, 2, 1), SDO_ORDINATE_ARRAY(0, 0, 2.5, 2.5))
SDO_GEOMETRY(2006, NULL, NULL, SDO_ELEM_INFO_ARRAY(1, 2, 1, 5, 2, 1), SDO_ORDINATE_ARRAY(2.5, 2.5, 5, 5, 5, 5, 10,10))

Two caveats:

I will describe the algorithm I have created in another posting: I need to prepare some images that show how it works as these will be simpler than laborious text!

Finally, if there are any errors in the function please let me know and I will endeavour to correct them.

I hope the function is useful.

  Textile Help

<<Tip #7: Forcing Sdo_Geometry to contain only points, lines or areas >>Oracle Spatial Centroid Shootout