Top 5 Recent Articles
ARTICLES CATEGORIES
- Algorithms (22)
- All (399)
- Biography (1)
- Blog (44)
- Business Requirements (1)
- Commentary (1)
- Conversion (2)
- Customers (2)
- Data Models (1)
- Education (2)
- GeoRaptor (13)
- GPS (1)
- Image Processing (2)
- Import Export (8)
- Licensing (2)
- LiDAR (1)
- Linear Referencing (4)
- Manifold GIS (3)
- Mapping (1)
- MySQL Spatial (7)
- Networking and Routing (including Optimization) (5)
- Open Source (18)
- Oracle Spatial and Locator (194)
- Partitioning (1)
- PostGIS (36)
- Projections (1)
- Published Articles (1)
- qGIS (1)
- Recommendations (1)
- Services (1)
- Software Change Log (1)
- Source Code (37)
- Space Curves (9)
- Spatial Database Functions (109)
- Spatial DB comparison (1)
- Spatial XML Processing (11)
- SQL Server Spatial (92)
- Standards (3)
- Stored Procedure (17)
- Tessellation or Gridding (10)
- Tools (2)
- Topological Relationships (1)
- Training (2)
- Triangulation (2)
Implementing SDO_RemovePoint/ST_RemovePoint for Oracle
As I indicated in my article on SDO_SetPoint PostGIS has a ST_SetPoint function that can be used to ‘Replace(s) point N of linestring with given point’. (And that IBM’s spatial database products have the SE_VertexUpdate() functions.)
PostGIS also has the ST_RemovePoint function that “Removes point from a linestring”. The IBM Spatial Data Blade function is SE_VertexDelete (ST_Geometry, ST_Point) which “deletes a vertex from a geometry”.
Because there is no equivalent function to PostGIS’s ST_RemovePoint in Oracle, I thought I would implement one and add it to my free GEOM PL/SQL package.
Here are the relevant headers:
Here is the main body of the functions.
— Removes point (p_position) from a linestring. Offset is 1-based.
Function SDO_RemovePoint(p_geometry IN MDSYS.SDO_Geometry,
p_position IN Number)
Return MDSYS.SDO_Geometry
Is
v_elem_info MDSYS.SDO_Elem_Info_Array;
v_ordinates MDSYS.SDO_Ordinate_Array;
v_dims PLS_INTEGER;
v_gtype PLS_INTEGER;
v_coords PLS_INTEGER; /* Coordinate count after deletion */
v_position NUMBER := p_position;
v_end_position PLS_INTEGER;
NULL_GEOMETRY EXCEPTION;
NOT_LINESTRING_MULTIPOINT EXCEPTION;
IS_COMPOUND EXCEPTION;
INVALID_POSITION EXCEPTION;
Begin
If ( p_geometry is null ) Then
Raise NULL_GEOMETRY;
ElsIf ( Mod(p_geometry.sdo_gtype,10) not in (2,5,6) ) Then
RAISE NOT_LINESTRING_MULTIPOINT;
End If;
If isCompound(p_geometry.sdo_elem_info) Then
RAISE IS_COMPOUND;
End If;
v_dims := <acronym title="p_geometry.sdo_gtype/1000,0">TRUNC;
v_gtype := Mod(p_geometry.sdo_gtype,10);
v_ordinates := p_geometry.sdo_ordinates;
v_elem_info := p_geometry.sdo_elem_info;
/* Compute correct positions */
v_end_position := CASE WHEN v_ordinates is null
THEN 1
ELSE v_ordinates.COUNT / v_dims
END;
If ( v_position is NULL or v_position <= 0 ) Then
v_position := v_end_position;
End If;
— Can’t update a point that does not exist….
If Not ( v_position BETWEEN 1 AND v_end_position ) Then
RAISE INVALID_POSITION;
End If;
— First remove coordinate from ordinate array
SELECT i.ordinate
BULK COLLECT INTO v_ordinates
FROM (SELECT Ceil(rownum/v_dims) as coord,
rownum – (Ceil(rownum/v_dims) – 1) * v_dims as ord,
a.column_value as ordinate
FROM TABLE a
) i
WHERE i.coord <> v_position
ORDER BY coord,ord /* Probably not needed, but just in case */;
— Need coordinate count for next checks
v_coords := v_ordinates.COUNT / v_dims;
— If a single element of a linestring then check we have enough coords */
If ( v_coords = 1 And v_gtype = 2) Then
raise ZERO_DIVIDE;
End If;
— Now, modify sdo_elem_info if needed for multi objects
If ( v_gtype in (5,6) ) Then
SELECT case when ( f.elem = 1 ) /* Check ordinate value of previous element, if exists, to compute its size */
and ( (( ( f.new_elem_value – lag(f.new_elem_value,3) over (order by f.id) ) / v_dims ) – 1) = 0 )
then 1/0 /* Not enough ordinates to create a proper line */
when ( f.elem = 1 /* Compute size of last element in whole geometry */
And (lead(f.new_elem_value,3) over (order by f.id)) is null )
And ( ( ( f.max_ords – f.new_elem_value + 1 ) / v_dims ) <= 1 )
then 1/0 /* Last element has only 1 coord */
else f.new_elem_value
end as elem_Value
BULK COLLECT INTO v_elem_info
FROM (SELECT rownum as id,
e.elem,
e.max_ords,
case when e.elem = 1
and ( e.elem_value > 1 And e.elem_value > e.ord_position )
then e.elem_value – v_dims /* Calculate new start ord position for this element */
when e.elem = 3
then /* If this is a multi-point geometry, remove one from the point count in interpretation field*/
case when (<acronym title="e.elem_value,1">LAG over (order by e.rin)) = 1
then e.elem_value – 1
else e.elem_value
end
else e.elem_value
end as new_elem_value
FROM (SELECT rownum as rin,
(( v_position – 1 ) * v_dims) + 1 as ord_position,
( v_coords * v_dims) as max_ords,
rownum – (Ceil(rownum/3) – 1) * 3 as elem,
a.column_value as elem_value
FROM TABLE a
) e
) f;
End If;
Return MDSYS.SDO_Geometry(p_geometry.sdo_gtype,
p_geometry.sdo_srid,
p_geometry.sdo_point,
v_elem_info,
v_ordinates);
EXCEPTION
WHEN NULL_GEOMETRY Then
raise_application_error(CODESYS.CONSTANTS.c_i_null_geometry,
CODESYS.CONSTANTS.c_s_null_geometry,TRUE);
RETURN p_geometry;
WHEN NOT_LINESTRING_MULTIPOINT THEN
raise_application_error(CODESYS.CONSTANTS.c_i_not_line,CODESYS.CONSTANTS.c_s_not_line ||
‘ / ‘ || CODESYS.CONSTANTS.c_s_not_point,true);
RETURN p_geometry;
WHEN IS_COMPOUND THEN
raise_application_error(CODESYS.CONSTANTS.c_i_cmpnd_vector,CODESYS.CONSTANTS.c_s_cmpnd_vector,true);
RETURN p_geometry;
WHEN ZERO_DIVIDE THEN
raise_application_error(-20001,‘point deletion results in an invalid one vertex element.’,true);
return p_geometry;
WHEN INVALID_POSITION THEN
raise_application_error(-20001,‘invalid p_position value’,true);
RETURN p_geometry;
End SDO_RemovePoint;
/* ST_* Wrapper */
Function ST_RemovePoint(p_geometry IN MDSYS.ST_Geometry,
p_position IN Number)
Return MDSYS.ST_Geometry
Is
Begin
Return MDSYS.ST_GEOMETRY.FROM_SDO_GEOM(
CODESYS.GEOM.SDO_RemovePoint(p_geometry.Get_Sdo_Geom(),
p_position));
End ST_RemovePoint;
Note that the IBM and PostGIS function support linestrings only. The functions above support MultiPoint, LineString and MultiLineStrings. The functions do not, as yet, support Polygons, MultiPolygons or geometries with compound elements. I could, of course, modify them to allow for the handling of non-linear geometries, but I have chosen not to in version 1.0. If a user finds the functions useful and wants support for such things then contact me and I will update them.
On to the examples of how to use the functions.
-- Test single point
select geom.SDO_RemovePoint(mdsys.SDO_Geometry(3001,null,sdo_point_type(1.1,2.4,3.5),null,null),
1) as point
from dual;
Error starting at line 2 in command:
select geom.SDO_RemovePoint(mdsys.SDO_Geometry(3001,null,sdo_point_type(1.1,2.4,3.5),null,null),
1) as point
from dual
Error report:
SQL Error: ORA-20116: Input geometry is not a linestring / Input geometry is not a point
ORA-06512: at "CODESYS.GEOM", line 4603
ORA-06510: PL/SQL: unhandled user-defined exception
-- Test single polygon
select Geom.SDO_RemovePoint(b.the_geom,1) as RemoveGeom
from (select sdo_geometry('POLYGON((2 2, 2 7, 12 7, 12 2, 2 2))',NULL) as the_geom
from dual
) b;
Error starting at line 2 in command:
select Geom.SDO_RemovePoint(b.the_geom,1) as RemoveGeom
from (select sdo_geometry('POLYGON((2 2, 2 7, 12 7, 12 2, 2 2))',NULL) as the_geom
from dual
) b
Error report:
SQL Error: ORA-20116: Input geometry is not a linestring / Input geometry is not a point
ORA-06512: at "CODESYS.GEOM", line 4603
ORA-06510: PL/SQL: unhandled user-defined exception
-- Remove point from end of MultiPoint
select geom.SDO_RemovePoint(mdsys.SDO_Geometry(2005,null,null,
sdo_elem_info_array(1,1,3),
sdo_ordinate_array(1.1,1.3,2.4,2.03,3.4,3.5)),
null/* Means remove end coordinate */) as point
from dual;
POINT
--------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2005,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1,2),MDSYS.SDO_ORDINATE_ARRAY(1.1,1.3,2.4,2.03))
1 rows selected
-- Remove points 0-4 in a linestring, note 0 and NULL denote is the last coord
select level-1 as point,
geom.sdo_RemovePoint(mdsys.SDO_Geometry(3002,null,null,sdo_elem_info_array(1,2,1),
sdo_ordinate_array(1.12345,1.3445,9,2.43534,2.03998398,9,3.43513,3.451245,9)),
LEVEL-1) as RemovedPoint
from dual
connect by level <= 4;
POINT REMOVEDPOINT
---------------------- -------------------------------------------------------------------------------------------------------------------------------------
0 MDSYS.SDO_GEOMETRY(3002,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(1.12345,1.3445,9,2.43534,2.03998398,9))
1 MDSYS.SDO_GEOMETRY(3002,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(2.43534,2.03998398,9,3.43513,3.451245,9))
2 MDSYS.SDO_GEOMETRY(3002,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(1.12345,1.3445,9,3.43513,3.451245,9))
3 MDSYS.SDO_GEOMETRY(3002,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(1.12345,1.3445,9,2.43534,2.03998398,9))
4 rows selected
-- Remove first coordinate in standard LineString
SELECT GEOM.ST_RemovePoint(a.linestring,1)/*.Get_WKT()*/ as st_geom
FROM (SELECT mdsys.OGC_LineStringFromText('LINESTRING(1.10 1.30,2.40 2.90,3.40 3.50)') as linestring
FROM dual) a;
ST_GEOM
----------------------------------------------------------------------------------------------------------------------------------
MDSYS.ST_LINESTRING(MDSYS.SDO_GEOMETRY(2002,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(2.4,2.9,3.4,3.5)))
1 rows selected
-- Try to remove single point from two point linestring (should get error)
SELECT GEOM.ST_RemovePoint(GEOM.ST_RemovePoint(a.linestring,1),1).Get_WKT() as st_geom
FROM (SELECT mdsys.OGC_LineStringFromText('LINESTRING(1.10 1.30,2.40 2.90)') as linestring
FROM dual) a;
Error starting at line 2 in command:
SELECT GEOM.ST_RemovePoint(GEOM.ST_RemovePoint(a.linestring,1),1).Get_WKT() as st_geom
FROM (SELECT mdsys.OGC_LineStringFromText('LINESTRING(1.10 1.30,2.40 2.90)') as linestring
FROM dual) a
Error report:
SQL Error: ORA-20001: point deletion results in an invalid one vertex element.
ORA-06512: at "CODESYS.GEOM", line 4610
ORA-01476: divisor is equal to zero
ORA-06512: at "CODESYS.GEOM", line 4624
-- Try to remove 2 points
SELECT GEOM.ST_RemovePoint(GEOM.ST_RemovePoint(a.linestring,1),1).Get_WKT() as st_geom
FROM (SELECT mdsys.OGC_LineStringFromText('LINESTRING(1.10 1.30,2.40 2.90,3.40 3.50)') as linestring
FROM dual) a;
Error starting at line 2 in command:
SELECT GEOM.ST_RemovePoint(GEOM.ST_RemovePoint(a.linestring,1),1).Get_WKT() as st_geom
FROM (SELECT mdsys.OGC_LineStringFromText('LINESTRING(1.10 1.30,2.40 2.90,3.40 3.50)') as linestring
FROM dual) a
Error report:
SQL Error: ORA-20001: point deletion results in an invalid one vertex element.
ORA-06512: at "CODESYS.GEOM", line 4610
ORA-01476: divisor is equal to zero
ORA-06512: at "CODESYS.GEOM", line 4624
-- Demonstrate removing each point in the following multipoint geometry
select level as removedPoint,
codesys.geom.SDO_RemovePoint(
SDO_Geometry(3006,null,null,
sdo_elem_info_array(1,2,1,10,2,1),
sdo_ordinate_array(/*Element 1*/0.2,0.3,9,1.1,1.3,9,2.4,2.5,9,
/*Element 2*/3.4,3.5,9,4.4,4.5,9,5.6,5.4,9)),
level) as new_geom
from dual
connect by level <= 6;
REMOVEDPOINT NEW_GEOM
---------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------
1 MDSYS.SDO_GEOMETRY(3006,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,7,2,1),MDSYS.SDO_ORDINATE_ARRAY(1.1,1.3,9,2.4,2.5,9,3.4,3.5,9,4.4,4.5,9,5.6,5.4,9))
2 MDSYS.SDO_GEOMETRY(3006,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,7,2,1),MDSYS.SDO_ORDINATE_ARRAY(0.2,0.3,9,2.4,2.5,9,3.4,3.5,9,4.4,4.5,9,5.6,5.4,9))
3 MDSYS.SDO_GEOMETRY(3006,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,7,2,1),MDSYS.SDO_ORDINATE_ARRAY(0.2,0.3,9,1.1,1.3,9,3.4,3.5,9,4.4,4.5,9,5.6,5.4,9))
4 MDSYS.SDO_GEOMETRY(3006,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,10,2,1),MDSYS.SDO_ORDINATE_ARRAY(0.2,0.3,9,1.1,1.3,9,2.4,2.5,9,4.4,4.5,9,5.6,5.4,9))
5 MDSYS.SDO_GEOMETRY(3006,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,10,2,1),MDSYS.SDO_ORDINATE_ARRAY(0.2,0.3,9,1.1,1.3,9,2.4,2.5,9,3.4,3.5,9,5.6,5.4,9))
6 MDSYS.SDO_GEOMETRY(3006,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,10,2,1),MDSYS.SDO_ORDINATE_ARRAY(0.2,0.3,9,1.1,1.3,9,2.4,2.5,9,3.4,3.5,9,4.4,4.5,9))
6 rows selected
-- Create two point element in multilinestring and then try and delete a single point
select level as removedPoint,
codesys.geom.SDO_RemovePoint(
SDO_Geometry(3006,null,null,
sdo_elem_info_array(1,2,1,10,2,1,19,2,1),
sdo_ordinate_array(/*Element 1*/0.2,0.3,9,1.1,1.3,9,2.4,2.5,9,
/*Element 2*/3.4,3.5,9,4.4,4.5,9,5.6,5.4,9,
/*Element 3*/19,20,21,22)),
level) as new_geom
from dual
connect by level <= 8;
Error starting at line 2 in command:
select level as removedPoint,
codesys.geom.SDO_RemovePoint(
SDO_Geometry(3006,null,null,
sdo_elem_info_array(1,2,1,10,2,1,19,2,1),
sdo_ordinate_array(/*Element 1*/0.2,0.3,9,1.1,1.3,9,2.4,2.5,9,
/*Element 2*/3.4,3.5,9,4.4,4.5,9,5.6,5.4,9,
/*Element 3*/19,20,21,22)),
level) as new_geom
from dual
connect by level <= 8
Error report:
SQL Error: ORA-20001: point deletion results in an invalid one vertex element.
ORA-06512: at "CODESYS.GEOM", line 4610
ORA-01476: divisor is equal to zero
I hope this article is of help to someone.
Documentation
- GeoRaptor Documentation
- MySQL Spatial General Functions
- Oracle LRS Objects
- Oracle Spatial Exporter (Java + pl/SQL)
- Oracle Spatial Object Functions
- Oracle Spatial Object Functions (Multi Page)
- PostGIS pl/pgSQL Functions
- SC4O Oracle Java Topology Suite (Java + pl/SQL)
- SQL Server Spatial General TSQL Functions
- SQL Server Spatial LRS TSQL Functions