SpatialDB Advisor
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.

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