ST_Azimuth for Oracle: AKA Cogo.Bearing

Many years ago I wrote a Coordinate Geometry (COGO) package for Oracle Sdo_Geometry that contained a Bearing function that, given two points, would return the angle in radians from the horizontal of the vector defined by point A and B.

PostGIS has a similar function called “ST_Azimuth”:http://postgis.refractions.net/documentation/manual-svn/ST_Azimuth.html.

Here are the relevant declarations of the Bearing function and overloaded ST_Azimuth interfaces that implement ST_Azimuth function wrappers which would allow some level of portability between Oracle and PostGIS.

 CREATE OR REPLACE PACKAGE "COGO"
 AUTHID CURRENT_USER
 As
 
 .........
 
 /* ----------------------------------------------------------------------------------------
 ** function   : Bearing
 ** precis     : Returns a value between 0 and 2*PI representing the bearing
 **               North = 0, East = PI/2, South = PI, West = 3*PI/4
 **               To convert to degrees multiply by (180/PI).
 ** version.   : 1.0
 ** usage      : FUNCTION Bearing( dE1 in number,
 **                               dN1 in number,
 **                               dE2 in number,
 **                               dN2 in number)
 **                        RETURN NUMBER DETERMINISTIC;
 **               eg :new.shape := CODESYS.cogo.Bearing(299900, 5200000, 300000, 5200100);
 ** param      : dE1     : X Ordinate of the start point for the vector
 ** paramtype  : dE1     : NUMBER
 ** param      : dN1     : Y Ordinate of the start point for the vector
 ** paramtype  : dN1     : NUMBER
 ** param      : dE2     : X Ordinate of the end point for the vector
 ** paramtype  : dE2     : NUMBER
 ** param      : dN2     : Y Ordinate of the end point for the vector
 ** paramtype  : dN2     : NUMBER
 ** return     : Bearing : the angle in radians between 0 and 2*PI representing the bearing
 ** rtnType    : Bearing : NUMBER
 ** note       : Does not throw exceptions
 ** note       : Assumes planar projection eg UTM.
 ** @history   : Simon Greener - Feb 2005 - Original coding.
 */
 Function Bearing( dE1 in number,
                    dN1 in number,
                    dE2 in number,
                    dN2 in number)
          Return Number Deterministic;
 
 /** Alternate binding: 1
 **/
 Function Bearing( startCoord in mdsys.sdo_point_type,
                      endCoord in mdsys.sdo_point_type)
          Return Number Deterministic;
 
 /** Alternate binding: 2
 **/
 Function Bearing( p_startCoord in mdsys.sdo_point_type,
                      p_endCoord in mdsys.sdo_point_type,
                   p_planar_srid in number,
               p_geographic_srid in number := 8311
                        )
  Return Number Deterministic;
 
 /* Alternate binding for SQL/MM
 **/
 Function ST_Azimuth( p_startCoord in mdsys.ST_Point,
                       p_endCoord   in mdsys.ST_Point)
   Return Number Deterministic;
 
 .....
 
 END Cogo;

Now, some examples of the use of these functions.

 /* What happens when NULL points are provided */
 SELECT COGO.Bearing(NULL,NULL)
   FROM DUAL;
 
 AZIMUTH
 ----------------------
 NULL
 
 1 rows selected
 
 /* What happens if start and end point are the same? */
 SELECT ROUND(COGO.Bearing(mdsys.sdo_point_type(1,2,null), mdsys.sdo_point_type(1,2,null)) / ( 2 * Constants.pi ) * 360) as degAz
   FROM DUAL;
 
 DEGAZ
 ---------------------- 
 NULL
 
 1 rows selected
 
 /* PostGIS example */
 SELECT ST_Azimuth(ST_MakePoint(1,2), ST_MakePoint(3,4))/(2*pi())*360 as degAz,
         ST_Azimuth(ST_MakePoint(3,4), ST_MakePoint(1,2))/(2*pi())*360 As degAzrev
 
 degaz  degazrev
 ------ ---------
 45     225
 
 /* Oracle version of PostGIS example using "native" interfaces */
 SELECT ROUND(COGO.Bearing(mdsys.sdo_point_type(1,2,null), mdsys.sdo_point_type(3,4,null)) / ( 2 * Constants.pi ) * 360) as degAz,
         ROUND(COGO.Bearing(mdsys.sdo_point_type(3,4,null), mdsys.sdo_point_type(1,2,null)) / ( 2 * Constants.pi ) * 360) as degAzRev
   FROM DUAL;
 
 DEGAZ                  DEGAZREV
 ---------------------- ----------------------
 45                     225
 
 1 rows selected
 
 /* PostGIS example using "equivalent" ST_* interfaces */
 SELECT Round(Cogo.ST_Azimuth(mdsys.ST_Point(1,2), mdsys.ST_Point(3,4))/(2*Constants.pi)*360) as degAz,
         Round(Cogo.ST_Azimuth(mdsys.ST_Point(3,4), mdsys.ST_Point(1,2))/(2*Constants.pi)*360) As degAzrev
   FROM dual;
 
 DEGAZ                  DEGAZREV
 ---------------------- ----------------------
 45                     225
 
 1 rows selected

I hope this is useful to someone.