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 a SetPoint/ST_SetPoint function in Oracle
In PostGIS there is the ST_SetPoint() function that “Replace(s) point N of linestring with given point”.
There is also the SE_VertexUpdate() extension function in IBM’s spatial products. (These are the sorts of oversights in the OGC and SQL/MM standards that I have commented on before.)
There is no such function in Oracle so I thought I would implement one along the lines of the PostGIS function in my free GEOM PL/SQL package.
Here are the relevant headers:
/** ----------------------------------------------------------------------------------------
* function : SDO_SetPoint
* precis : Replace point (p_position) of linestring with given point (1-based index)
* version : 1.0
* usage : FUNCTION SDO_SetPoint(p_geometry in mdsys.SDO_geometry,
* p_point in MDSYS.Vertex_Type,
* p_position in number )
* RETURN mdsys.SDO_geometry DETERMINISTIC;
* param : p_geometry : Original geometry object
* paramtype : p_geometry : mdsys.SDO_geometry
* param : p_point : Actual point coordinates updating existing point
* paramtype : p_area : number
* param : p_position : Position of point to be updated. If NULL the last point is updated otherwise, if a single SDO_point, that point is updated.
* paramType : p_position : Number
* return : input geometry with changed point.
* returnType : mdsys.SDO_geometry
* history : Simon Greener - February 2009 - Original coding.
* copyright : Licensed under a Creative Commons Attribution-Share Alike 2.5 Australia License. (http://creativecommons.org/licenses/by-sa/2.5/au/)
**/
Function SDO_SetPoint(p_geometry IN MDSYS.SDO_Geometry,
p_point IN MDSYS.Vertex_Type,
p_position IN Number )
Return MDSYS.SDO_Geometry Deterministic;
/* ST_* Wrapper */
-- Replace point (p_position) of linestring with given point. Index is 1-based.
Function ST_SetPoint(p_geometry IN MDSYS.ST_Geometry,
p_point IN MDSYS.ST_Point,
p_position IN Number )
Return MDSYS.ST_Geometry Deterministic;
The PostGIS function only handles linestrings (not multilinestrings, polygons etc). Initally I too only supported linestrings with the addition of multilinestrings with a restriction for compound linestrings (that is, linestrings composed of a combination of circular arcs and vertex-connected linestrings).
Then I thought to myself, what is wrong with supporting points or multipoints? So, even though, in many ways, supporting single points coded in the SDO_POINT field of the SDO_GEOMETRY object is fairly trivial I decided to support both of these. Then I thought more about the problem with compound linestrings (and polygons, see following) and decided that I would support the update of the points in a ciruclar arc even if that point broke the arc (it is up to you, dear reader, to know what you are doing). Then, finally, I thought that I would support polygons/multipolygons but not check whether the user updated the first/last point in a inner/outer shell (1003/2003/1005/2005) and so automatically updated the related point. (I show how to handle this in the examples below. I could put in such checks, but this is verion 1.0 of the function. If a user finds the function useful and wants such an automated fix then contact me and I will update the function.)
On to the examples of how to use the function.
1. Points
-- Test update of NULL SDO_POINT structure
select geom.SDO_SetPoint(MDSYS.SDO_Geometry(2001,null,null,null,null),
MDSYS.Vertex_Type(4.555,4.666,null,null,1),
1) as point
from dual;
POINT
--------------------------------------------
MDSYS.SDO_GEOMETRY(2001,null,null,null,null)
1 rows selected
-- Test update of SDO_POINT structure with NULL ordinate values
select geom.SDO_SetPoint(mdsys.SDO_Geometry(2001,null,MDSYS.SDO_point_type(null,null,null),null,null),
MDSYS.Vertex_Type(4.555,4.666,null,null,1),
1) as point
from dual;
POINT
------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2001,null,MDSYS.SDO_POINT_TYPE(4.555,4.666,null),null,null)
1 rows selected
-- Test update of SDO_POINT structure with valid ordinate values
select geom.SDO_SetPoint(mdsys.SDO_Geometry(2001,null,MDSYS.SDO_point_type(1.12345,2.43534,3.43513),null,null),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
1) as point
from dual;
POINT
------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2001,null,MDSYS.SDO_POINT_TYPE(4.555,4.666,null),null,null)
1 rows selected
-- Test to see if NULL p_position is correctly resolved to 1
select geom.SDO_SetPoint(mdsys.SDO_Geometry(3001,null,MDSYS.SDO_point_type(1.12345,2.43534,3.43513),null,null),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
null) as point
from dual;
POINT
----------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(3001,null,MDSYS.SDO_POINT_TYPE(4.555,4.666,10),null,null)
1 rows selected
-- Test if invalid p_position value is supplied
select geom.SDO_SetPoint(mdsys.SDO_Geometry(3001,null,MDSYS.SDO_point_type(1.12345,2.43534,3.43513),null,null),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
2) as point
from dual;
Error starting at line 18 in command:
select geom.SDO_SetPoint(mdsys.SDO_Geometry(3001,null,MDSYS.SDO_point_type(1.12345,2.43534,3.43513),null,null),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
2) as point
from dual
Error report:
SQL Error: ORA-20001: invalid p_position value
ORA-06512: at "CODESYS.GEOM", line 4645
ORA-06510: PL/SQL: unhandled user-defined exception
2. MultiPoints
-- Update last point
select geom.SDO_SetPoint(MDSYS.SDO_Geometry(2001,null,null,MDSYS.SDO_elem_info_array(1,1,3),
MDSYS.SDO_ordinate_array(1.12345,1.3445,2.43534,2.03998398,3.43513,3.451245)),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
null) as point
from dual;
POINT
-------------------------------------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2001,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1,3),MDSYS.SDO_ORDINATE_ARRAY(1.12345,1.3445,2.43534,2.03998398,4.555,4.666))
1 rows selected
-- Update third point in 2D multipoint
select geom.SDO_SetPoint(mdsys.SDO_Geometry(2001,null,null,MDSYS.SDO_elem_info_array(1,1,3),
MDSYS.SDO_ordinate_array(1.12345,1.3445,2.43534,2.03998398,3.43513,3.451245)),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
3) as point
from dual;
POINT
-------------------------------------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2001,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1,3),MDSYS.SDO_ORDINATE_ARRAY(1.12345,1.3445,2.43534,2.03998398,4.555,4.666))
1 rows selected
-- Update third point in 3D multipoint
select geom.SDO_SetPoint(mdsys.SDO_Geometry(3001,null,null,MDSYS.SDO_elem_info_array(1,1,3),
MDSYS.SDO_ordinate_array(1.12345,1.3445,9,2.43534,2.03998398,9,3.43513,3.451245,9)),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
3) as point
from dual;
POINT
--------------------------------------------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(3001,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1,3),MDSYS.SDO_ORDINATE_ARRAY(1.12345,1.3445,9,2.43534,2.03998398,9,4.555,4.666,10))
1 rows selected
-- Update non-existant point
select geom.SDO_SetPoint(MDSYS.SDO_Geometry(2001,null,null,MDSYS.SDO_elem_info_array(1,1,3),
MDSYS.SDO_ordinate_array(1.12345,1.3445,2.43534,2.03998398,3.43513,3.451245)),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
10) as point
from dual;
Error starting at line 19 in command:
select geom.SDO_SetPoint(MDSYS.SDO_Geometry(2001,null,null,MDSYS.SDO_elem_info_array(1,1,3),
MDSYS.SDO_ordinate_array(1.12345,1.3445,2.43534,2.03998398,3.43513,3.451245)),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
10) as point
from dual
Error report:
SQL Error: ORA-20001: invalid p_position value
ORA-06512: at "CODESYS.GEOM", line 4645
ORA-06510: PL/SQL: unhandled user-defined exception
3. LineStrings
-- Change 3rd point in 3D multilinestring
select geom.SDO_SetPoint(MDSYS.SDO_Geometry(3006,null,null,MDSYS.SDO_elem_info_array(1,2,1,10,2,1),
MDSYS.SDO_ordinate_array(1.12345,1.3445,9,2.43534,2.03998398,9,3.43513,3.451245,9,10,10,9,10,20,9)),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
3) as mutlilinestring3
from dual;
MUTLILINESTRING3
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(3006,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,10,2,1),MDSYS.SDO_ORDINATE_ARRAY(1.12345,1.3445,9,2.43534,2.03998398,9,4.555,4.666,10,10,10,9,10,20,9))
1 rows selected
--Change first point in a 2D single linestring from -1 3 to -1 1
SELECT GEOM.ST_SetPoint(a.linestring,a.point,1).Get_WKT() as st_geom
FROM (SELECT mdsys.OGC_LineStringFromText('LINESTRING(-1 2,-1 3)') as linestring,
mdsys.OGC_PointFromText('POINT(-1 -1)') as point
FROM dual) a;
ST_GEOM
--------------------------------
LINESTRING (-1.0 -1.0, -1.0 3.0)
1 rows selected
/* Note: PostGIS Equivalent
SELECT ST_AsText(ST_SetPoint('LINESTRING(-1 2,-1 3)', 0, 'POINT(-1 1)'));
st_astext
-----------------------
LINESTRING(-1 1,-1 3)
*/
-- Change 3rd point in 3D multilinestring
select geom.SDO_SetPoint(MDSYS.SDO_Geometry(3006,null,null,MDSYS.SDO_elem_info_array(1,2,1,10,2,1),
MDSYS.SDO_ordinate_array(1.12345,1.3445,9,2.43534,2.03998398,9,3.43513,3.451245,9,10,10,9,10,20,9)),
MDSYS.Vertex_Type(4.555,4.666,10,null,1),
3) as mutlilinestring3
from dual;
MUTLILINESTRING3
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(3006,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1,10,2,1),MDSYS.SDO_ORDINATE_ARRAY(1.12345,1.3445,9,2.43534,2.03998398,9,4.555,4.666,10,10,10,9,10,20,9))
1 rows selected
--Change first point in a 2D single linestring from -1 3 to -1 1
SELECT GEOM.ST_SetPoint(a.multilinestring,a.point,1).Get_WKT() as st_geom
FROM (SELECT mdsys.OGC_MultiLineStringFromText('MULTILINESTRING ((1.12345 1.3445, 2.43534 2.03998398, 3.43513 3.451245), (10.0 10.0, 10.0 20.0))') as multilinestring,
mdsys.OGC_PointFromText('POINT(-1 -1)') as point
FROM dual) a;
ST_GEOM
-------------------------------------------------------------------------------------------
MULTILINESTRING ((-1.0 -1.0, 2.43534 2.03998398, 3.43513 3.451245), (10.0 10.0, 10.0 20.0))
1 rows selected
4. Polygons
Some polygon tests showing the first/last coordinate in a shell issue.
-- Update the last point in a simple polygon (note result is incorrect)
select Geom.SDO_SetPoint(b.the_geom,
MDSYS.Vertex_Type(1,1,null,null,1),
NULL) as setGeom
from (select MDSYS.SDO_Geometry('POLYGON((2 2, 2 7, 12 7, 12 2, 2 2))',NULL) as the_geom
from dual
) b;
SETGEOM
----------------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2003,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),MDSYS.SDO_ORDINATE_ARRAY(2,2,2,7,12,7,12,2,1,1))
1 rows selected
-- Now do it properly...
select Geom.SDO_SetPoint(Geom.SDO_SetPoint(b.the_geom,b.the_point,1),b.the_point,NULL)
as setGeom
from (select MDSYS.SDO_Geometry('POLYGON((2 2, 2 7, 12 7, 12 2, 2 2))',NULL) as the_geom,
MDSYS.Vertex_Type(1,1,null,null,1) as the_point
from dual
) b;
SETGEOM
----------------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2003,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),MDSYS.SDO_ORDINATE_ARRAY(1,1,2,7,12,7,12,2,1,1))
1 rows selected
-- How to set the first and last points in a single outer shelled compound polygon polygon
select Geom.SDO_SetPoint(
Geom.SDO_SetPoint(
MDSYS.SDO_Geometry(2003,null,null,
SDO_ELEM_INFO_ARRAY(1,1005,2,1,2,2,5,2,1),
SDO_ORDINATE_ARRAY(-0.175,9.998,-0.349,9.994,-0.523,9.986,0,0,-0.175,9.998)),
MDSYS.Vertex_Type(1,1,null,null,1),
NULL),
MDSYS.Vertex_Type(1,1,null,null,1),
1) as setGeom
from dual;
SETGEOM
--------------------------------------------------------------------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2003,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1005,2,1,2,2,5,2,1),MDSYS.SDO_ORDINATE_ARRAY(1,1,-0.349,9.994,-0.523,9.986,0,0,1,1))
1 rows selected
5. MultiPolygons
-- Update the second point in the first outer shell
select Geom.SDO_SetPoint(b.the_geom,
MDSYS.Vertex_Type(2,7.5,null,null,1),
2).Get_WKT() as setGeom
from (select MDSYS.SDO_Geometry('MULTIPOLYGON(((2 2, 2 7, 12 7, 12 2, 2 2)), ((20 20, 20 70, 120 70, 120 20, 20 20)) )',NULL) as the_geom
from dual
) b;
SETGEOM
-----------------------------------------------------------------------------------------------------------------------------
MULTIPOLYGON (((2.0 2.0, 2.0 7.5, 12.0 7.0, 12.0 2.0, 2.0 2.0)), ((20.0 20.0, 20.0 70.0, 120.0 70.0, 120.0 20.0, 20.0 20.0)))
1 rows selected
I hope this is useful to somone.
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