# COGO: TSQL Function For Creating a Point from Point, Bearing and Distance

Sometimes the definition of an object can be as a bearing and distance from a known point. This often happens in unbiased statistical sampling, or when creating polygons describing land titles from coordinate geometry (COGO) measurements.

Here is a version of the STPointFromBearingAndDistance PL/SQL function I wrote for Oracle Spatial many years ago for SQL Server.

Note that I have a schema call cogo in which I create functions like this. You can use anything you like.

```CREATE FUNCTION [cogo].[STPointFromBearingAndDistance]
(
@p_dStartE   Float,
@p_dStartN   Float,
@p_dBearing  Float,
@p_dDistance Float,
@p_round_xy  int = 3,
@p_srid      int = 0
)
RETURNS geometry
AS
/****f* COGO/STPointFromBearingAndDistance (2008)
*  NAME
*    STPointFromBearingAndDistance -- Returns a projected point given starting point, a bearing in Degrees, and a distance (geometry SRID units).
*  SYNOPSIS
*    Function STPointFromBearingAndDistance (
*               @p_dStartE   float,
*               @p_dStartN   float,
*               @p_dBearing  float,
*               @p_dDistance float
*               @p_round_xy  int = 3,
*               @p_srid      int = 0
*             )
*     Returns float
*  DESCRIPTION
*    Function that computes a new point given a starting coordinate, a whole circle bearing and a distance (SRID Units).
*    Returned point's XY ordinates are rounded to @p_round_xy decimal digits of precision.
*    @p_SRID is the SRID of the supplied start point.
*  INPUTS
*    @p_dStartE   (float) - Easting of starting point.
*    @p_dStartN   (float) - Northing of starting point.
*    @p_dBearing  (float) - Whole circle bearing between 0 and 360 degrees.
*    @p_dDistance (float) - Distance in SRID units from starting point to required point.
*    @p_round_xy    (int) - XY ordinates decimal digitis of precision.
*    @p_srid        (int) - SRID associated with @p_dStartE/p_dStartN.
*  RESULT
*    point    (geometry) - Point
*  EXAMPLE
*    SELECT [cogo].[STPointFromBearingAndDistance] (0,0,45,100,3,0).STAsText() as endPoint;
*    GO
*
*    endPoint
*    POINT (70.711 70.711)
*  AUTHOR
*    Simon Greener
*  HISTORY
*    Simon Greener - December 2011 - Original TSQL Coding for SQL Server.
*    (c) 2008-2018 by TheSpatialDBAdvisor/Simon Greener
******/
Begin
Declare
@dBearing   Float,
@dAngle1    Float,
@dDeltaN    Float,
@dDeltaE    Float,
@dEndE      Float,
@dEndN      Float,
@v_round_xy int = ISNULL(@p_round_xy,3),
@v_srid     int = ISNULL(@p_srid    ,0);
BEGIN
IF ( @p_dStartE   IS NULL OR
@p_dStartN   IS NULL OR
@p_dBearing  IS NULL OR
@p_dDistance IS NULL )
RETURN NULL;

SET @dBearing = ABS(@p_dBearing);

-- First calculate dDeltaE and dDeltaN
IF ( @dBearing < CAST(90.0 as float) )
BEGIN
SET @dAngle1    = CAST(90.0 as float) - @dBearing;
SET @dAngle1Rad = @dAngle1 * PI() / CAST(180.0 as float)
SET @dDeltaE    = Cos(@dAngle1Rad) * @p_dDistance;
SET @dDeltaN    = Sin(@dAngle1Rad) * @p_dDistance;
-- Calculate the easting and northing of the end point
SET @dEndE      = @p_dStartE + @dDeltaE;
SET @dEndN      = @p_dStartN + @dDeltaN;
RETURN geometry::Point(ROUND(@dEndE,@v_round_xy),ROUND(@dEndN,@v_round_xy), @v_srid);
END;

IF ( @dBearing < CAST(180.0 as float) )
BEGIN
SET @dAngle1    = @dBearing - CAST(90.0 AS float);
SET @dAngle1Rad = @dAngle1 * PI() / CAST(180.0 AS float);
SET @dDeltaE    = Cos(@dAngle1Rad) * @p_dDistance;
SET @dDeltaN    = Sin(@dAngle1Rad) * @p_dDistance * -1;
-- Calculate the easting and northing of the end point
SET @dEndE      = @p_dStartE + @dDeltaE;
SET @dEndN      = @p_dStartN + @dDeltaN;
RETURN geometry::Point(ROUND(@dEndE,@v_round_xy),ROUND(@dEndN,@v_round_xy), @v_srid);
END;

IF ( @dBearing < CAST(270.0 AS float) )
BEGIN
SET @dAngle1    = CAST(270.0 AS float) - @dBearing;
SET @dAngle1Rad = @dAngle1 * PI() / CAST(180.0 AS float);
SET @dDeltaE    = Cos(@dAngle1Rad) * @p_dDistance * -1;
SET @dDeltaN    = Sin(@dAngle1Rad) * @p_dDistance * -1;
-- Calculate the easting and northing of the end point
SET @dEndE      = @p_dStartE + @dDeltaE;
SET @dEndN      = @p_dStartN + @dDeltaN;
RETURN geometry::Point(ROUND(@dEndE,@v_round_xy),ROUND(@dEndN,@v_round_xy), @v_srid);
END;

IF ( @dBearing <= CAST(360.0 AS float) )
BEGIN
SET @dAngle1    = @dBearing - CAST(270.0 AS float);
SET @dAngle1Rad = @dAngle1 * PI() / CAST(180.0 as float);
SET @dDeltaE    = Cos(@dAngle1Rad) * @p_dDistance * -1;
SET @dDeltaN    = Sin(@dAngle1Rad) * @p_dDistance;
-- Calculate the easting and northing of the end point
SET @dEndE      = @p_dStartE + @dDeltaE;
SET @dEndN      = @p_dStartN + @dDeltaN;
RETURN geometry::Point(ROUND(@dEndE,@v_round_xy),ROUND(@dEndN,@v_round_xy), @v_srid);
End;
Return null;
END;
END;
GO
```

Here is an example of how to generate the geometry::Point object at the end of a bearing of 45 degrees and distance of 100 meters from ( 0,0 ) .

``` select cogo.STPointFromBearingAndDistance(0,0,45,100,3,0).STAsText() as NewPoint;
```
NewPoint
POINT (70.711 70.711)

Or more visually:

``` DECLARE
@XY Float = SQRT(POWER(100,2)/2);
select geometry::STGeomFromText('POINT(0 0)',0).STBuffer(10) as geom
union all
select geometry::STGeomFromText('LINESTRING(0 0,' + STR(@XY,10,5) + ' ' + STR(@XY,10,5) + ')',0) as geom
union all
select cogo.STPointFromBearingAndDistance(0,0,45,100,3,0).STBuffer(10) as geom;
``` I hope this is helpful to someone.