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)
Swapping Ordinates in an SDO_GEOMETRY object
I was chatting with a friend and colleague on the telephone today when he mentioned that his project was being delayed because they had been provided some SDO_GEOMETRY data in which the latitude and longitude ordinates had been swapped out of their normal positions. For those who don’t know, latitude/longitude data (known as geodetic data) is stored in Oracle as follows:
X = Longitude
Y = Latitude
But the supplied data was:
X = Latitude
Y = Longitude
Of course, my friend was quite capable of writing a PL/SQL procedure to swap the order but I thought I would create one and add it to my free PL/SQL packages for the use of anyone else who has had this problem.
Here is the function:
/** ---------------------------------------------------------------------------------------- * @function : SwapOrdinates * @precis : Allows for swapping ordinate pairs in a geometry. * @description : Sometimes the ordinates of a geometry can be swapped such as latitude for X * and Longitude for Y when it should be reversed. This function allows for the * swapping of pairs of ordinates controlled by the p_pair parameter. * @version : 1.0 * @usage : Function SwapOrdinates( p_geom in mdsys.sdo_geometry, * p_pair in varchar default 'XY' ) * RETURN MDSYS.SDO_GEOMETRY DETERMINISTIC; * @param : p_geom : Geometry * @paramtype : p_geom : MDSY.SDO_GEOMETRY * @param : p_pair : The ordinate pair to swap = XY, XZ, XM, YZ, YM or ZM * @paramtype : p_pair : varchar2 * @history : Simon Greener - Aug 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 SwapOrdinates( p_geom in mdsys.sdo_geometry, p_pair in varchar2 default 'XY' /* Can be XY, XZ, XM, YZ, YM, ZM */ ) return mdsys.sdo_geometry Is v_geom mdsys.sdo_geometry := p_geom; v_pair VARCHAR2(2) := UPPER(SUBSTR(p_pair,1,2)); v_dim NUMBER; v_gtype NUMBER; v_vertices CODESYS.ST_PointSet; v_i PLS_INTEGER; v_j PLS_INTEGER; v_isMeasured Boolean; NULL_GEOMETRY EXCEPTION; INVALID_PAIR EXCEPTION; INVALID_SWAP EXCEPTION; Begin IF ( v_pair is NULL Or v_pair not in ('XY', 'XZ', 'XM', 'YZ', 'YM', 'ZM' ) ) Then RAISE INVALID_PAIR; End If; IF ( v_geom is NULL ) THEN RAISE NULL_GEOMETRY; END IF; v_isMeasured := isMeasured(p_geom.sdo_gtype); v_dim := GetDimensions(p_geom.sdo_gtype); If ( ( v_dim = 2 And v_pair in ('XZ', 'XM', 'YZ', 'YM', 'ZM' ) ) Or ( v_dim = 3 And v_isMeasured And v_pair in ('XZ', 'YZ' ) ) Or ( v_dim = 3 And Not v_isMeasured And v_pair in ('XM', 'YM', 'ZM' ) ) ) Then RAISE INVALID_SWAP; End If; -- Get basic geometry type (point, line, polygon) v_gtype := GetShortGType(p_geom.sdo_gtype); If ( v_geom.sdo_point is not null And v_pair in ('XY','XZ','YZ') ) Then v_geom.sdo_point.x := CASE v_pair WHEN 'XY' THEN p_geom.sdo_point.y WHEN 'XZ' THEN p_geom.sdo_point.z ELSE p_geom.sdo_point.x END; v_geom.sdo_point.y := CASE v_pair WHEN 'XY' THEN p_geom.sdo_point.x WHEN 'YZ' THEN p_geom.sdo_point.z ELSE p_geom.sdo_point.y END; v_geom.sdo_point.z := CASE v_pair WHEN 'XZ' THEN p_geom.sdo_point.x WHEN 'YZ' THEN p_geom.sdo_point.y ELSE p_geom.sdo_point.z END; End If; -- If Single Point, all done, else... IF ( v_gtype != 1 And p_geom.sdo_ordinates is not null ) Then -- It's not a single point ... -- Process the geometry's ordinate array -- Copy the ordinates array If ( v_version < 10 ) Then SELECT CODESYS.ST_Point(a.x,a.y,a.z,a.m) Bulk Collect into v_vertices FROM TABLE(CODESYS.GEOM.GetPointSet(p_geom)) a; Else EXECUTE IMMEDIATE ' SELECT CODESYS.ST_Point( v.x, v.y, CASE WHEN :1 <> 3 THEN v.z ELSE NULL END, CASE WHEN :2 = 3 THEN v.z ELSE v.w END ) FROM TABLE(sdo_util.GetVertices(:3)) v' BULK COLLECT INTO v_vertices USING MOD(trunc(p_geom.sdo_gtype/100),10), MOD(trunc(p_geom.sdo_gtype/100),10), p_geom; End If; v_i := v_vertices.FIRST; -- index into input coordinate array v_j := v_geom.sdo_ordinates.FIRST; -- index into output ordinate array FOR i IN 1 .. v_vertices.COUNT LOOP v_geom.sdo_ordinates (v_j) := CASE WHEN ( v_pair = 'XY' ) THEN v_vertices(v_i).y WHEN ( v_pair = 'XZ' ) And ( ( v_dim = 4 ) Or ( v_dim = 3 And Not v_isMeasured ) ) THEN v_vertices(v_i).z WHEN ( v_pair = 'XM' ) And ( v_dim = 4 ) THEN v_vertices(v_i).m WHEN ( v_pair = 'XM' ) And ( v_dim = 3 And v_isMeasured ) THEN v_vertices(v_i).z ELSE v_vertices(v_i).x END; v_geom.sdo_ordinates (v_j + 1) := CASE WHEN ( v_pair = 'XY' ) THEN v_vertices(v_i).x WHEN ( v_pair = 'YZ' ) And ( ( v_dim = 4 ) Or ( v_dim = 3 And Not v_isMeasured ) ) THEN v_vertices(v_i).z WHEN ( v_pair = 'YM' ) And ( v_dim = 4 ) THEN v_vertices(v_i).m WHEN ( v_pair = 'YM' ) And ( v_dim = 3 And v_isMeasured ) THEN v_vertices(v_i).z ELSE v_vertices(v_i).y END; -- Do Z only if exists If ( ( v_dim = 4 ) OR ( ( v_dim = 3 ) And Not v_isMeasured ) And v_pair in ('XZ', 'YZ', 'ZM' ) ) Then v_geom.sdo_ordinates (v_j + 2) := CASE WHEN ( v_pair = 'XZ' ) THEN v_vertices(v_i).x WHEN ( v_pair = 'YZ' ) And ( ( v_dim = 4 ) Or ( v_dim = 3 And Not v_isMeasured ) ) THEN v_vertices(v_i).y WHEN ( v_pair = 'ZM' ) And ( v_dim = 4 ) THEN v_vertices(v_i).m ELSE v_vertices(v_i).z END; End If; -- Do M only if exists If ( ( v_dim = 4 ) OR ( ( v_dim = 3 ) And v_isMeasured ) And v_pair in ('XM', 'YM', 'ZM' ) ) Then v_geom.sdo_ordinates (v_j + ( v_dim - 1)) := CASE WHEN ( v_pair = 'XM' ) THEN v_vertices(v_i).x WHEN ( v_pair = 'YM' ) And ( ( v_dim = 4 ) Or ( v_dim = 3 And v_isMeasured ) ) THEN v_vertices(v_i).y WHEN ( v_pair = 'ZM' ) And ( v_dim = 4 ) THEN v_vertices(v_i).z ELSE v_vertices(v_i).m END; End If; v_i := v_i + 1; v_j := v_j + v_dim; END LOOP; End If; RETURN v_geom; EXCEPTION WHEN NULL_GEOMETRY THEN raise_application_error(-20001,'p_geom may not be NULL.'); WHEN INVALID_PAIR THEN raise_application_error(-20001,'p_pair (' || v_pair || ') must be one of XY, XZ, XM, YZ, YM, ZM only.'); WHEN INVALID_SWAP THEN raise_application_error(-20001,'Requested swap (' || v_dim || ',' || v_pair ||')cannot occur as sdo_geometry dimensionality does not support it.' ); End SwapOrdinates;
This function uses some functions in my PL/SQL packages which can be removed for a standalone function fairly simply. If anyone wants such a function let me know (remember to tell me which version of Oracle you are running).
Now some tests:
select codesys.Geom.SwapOrdinates(mdsys.sdo_geometry(3001,null,mdsys.sdo_point_type(1,20,30),null,null),'XY') as Geom from dual; GEOM --------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(3001,null,MDSYS.SDO_POINT_TYPE(20,1,30),null,null) 1 rows selected select codesys.Geom.SwapOrdinates(mdsys.sdo_geometry(3001,null,mdsys.sdo_point_type(1,20,30),null,null),'XZ') as Geom from dual; GEOM --------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(3001,null,MDSYS.SDO_POINT_TYPE(30,20,1),null,null) 1 rows selected select codesys.Geom.SwapOrdinates(mdsys.sdo_geometry(3001,null,mdsys.sdo_point_type(1,20,30),null,null),'YZ') as Geom from dual; GEOM --------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(3001,null,MDSYS.SDO_POINT_TYPE(1,30,20),null,null) 1 rows selected select codesys.Geom.SwapOrdinates(mdsys.sdo_geometry('LINESTRING (-32 147, -33 180)'),'XY') as Geom from dual; GEOM ------------------------------------------------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(2002,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(147,-32,180,-33)) 1 rows selected select codesys.Geom.SwapOrdinates(mdsys.sdo_geometry('LINESTRING (0 50, 10 50, 10 55, 10 60, 20 50)'),'XY') as Geom from dual; GEOM -------------------------------------------------------------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(2002,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(50,0,50,10,55,10,60,10,50,20)) 1 rows selected select codesys.Geom.SwapOrdinates(mdsys.sdo_geometry(3002,null,null, mdsys.sdo_elem_info_array(1,2,1), mdsys.sdo_ordinate_array(0,50,105, 10,50,110, 10,55,115, 10,60,120, 20,50,125)),'XZ') as Geom from dual; GEOM ---------------------------------------------------------------------------------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(3002,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(105,50,0,110,50,10,115,55,10,120,60,10,125,50,20)) 1 rows selected select codesys.Geom.SwapOrdinates(mdsys.sdo_geometry(3002,null, mdsys.sdo_point_type(1,20,30), mdsys.sdo_elem_info_array(1,2,1), mdsys.sdo_ordinate_array(0,50,105, 10,50,110, 10,55,115, 10,60,120, 20,50,125)),'YZ') as Geom from dual; GEOM ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(3002,null,MDSYS.SDO_POINT_TYPE(1,30,20),MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(0,105,50,10,110,50,10,115,55,10,120,60,20,125,50)) 1 rows selected select codesys.Geom.SwapOrdinates(MDSYS.SDO_GEOMETRY(3302, NULL, NULL, MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1), MDSYS.SDO_ORDINATE_ARRAY(5,10,0, 20,5,NULL, 35,10,NULL, 55,10,100)),'XM') as Geom from dual; GEOM --------------------------------------------------------------------------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(3302,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(null,10,5,null,5,20,null,10,35,null,10,55)) 1 rows selected select codesys.Geom.SwapOrdinates(MDSYS.SDO_GEOMETRY(4402, NULL, NULL, MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1), MDSYS.SDO_ORDINATE_ARRAY(5,10,500,0, 20,5,501,NULL, 35,10,502,NULL, 55,10,503,100)),'ZM') as Geom from dual; GEOM --------------------------------------------------------------------------------------------------------------------------------------------------- MDSYS.SDO_GEOMETRY(4402,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),MDSYS.SDO_ORDINATE_ARRAY(5,10,0,500,20,5,null,501,35,10,null,502,55,10,100,503)) 1 rows selected
I hope this function is useful to someone out there.
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