Go to content Go to navigation and search

Home

Current Oracle Spatial Blog Articles


Search

Browse

RSS / Atom

Email me

textpattern

Creative Commons License
All Blog Articles, Data Models and Free Source Code by Simon Greener, The SpatialDB Advisor is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Implementing a SetPoint/ST_SetPoint function in Oracle

Thursday February 12 2009 at 10:48

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.

Creative Commons License

post this at del.icio.uspost this at Diggpost this at Technoratipost this at Redditpost this at Farkpost this at Yahoo! my webpost this at Windows Livepost this at Google Bookmarkspost this to Twitter

Comment