COGO: Converting (Google Earth) Formatted Longitude/Latitude points to decimal degrees (SQL Server)

In an article on Oracle I described how to convert Google Earth textual representations of a longitude or latitude value to its decimal equivalent.

Here is a version that will work with SQL Server 2008/2012 Spatial.

 /** ----------------------------------------------------------------------------------------   * @function   : DMSS2DD
   * @precis     : Function parses provided string, say extracted from Google Earth,   *               that represents DD MM SS.SS values, extracts and creates a single   *               floating point decimal degrees value.
   * @version    : 1.0
   * @usage      : Function DMSS2DD(@p_strDegMinSec nvarchar(100))
   *                Returns Float
   *               eg SELECT 3 as id, DMSS2DD('43� 0'' 50.00"S') as DD;
   * @param      : p_strDegMinSec : DD MM SS.SS description eg 43� 0'' 50.00"S
   * @param      : p_strDegMinSec : String
   * @return     : Decimal Degree : Returns decimal degrees equivalent eg -43.0138888888889   * @returnType : Float
   * @depends    : dbo.Tokenizer()
   * @history    : Simon Greener - Feb 2011 - Original coding for Oracle in PL/SQL   * @history    : Simon Greener - May 2011 - Original coding for SQL Server in TSQL   * @copyright  : Licensed under a Creative Commons Attribution-Share Alike 2.5 Australia License.   *               (http://creativecommons.org/licenses/by-sa/2.5/au/)
 **/
 CREATE FUNCTION DMSS2DD(@p_strDegMinSec nvarchar(100))
 RETURNS FLOAT
 AS
 BEGIN
    DECLARE
        @i               INT = 0,
        @intDmsLen       INT = -1,            --Length of original string
        @strCompassPoint NVarChar(1),
        @strNorm         Nvarchar(100) = N'', --Will contain normalized string
        @strDegMinSecB   Nvarchar(100),
        @token           Nvarchar(100),
        @strChr          NVarChar(1),
        @blnGotSeparator INTEGER = -1,        -- Keeps track of separator sequences        @dDeg            FLOAT   = 0,
        @dMin            FLOAT   = 0,
        @dSec            FLOAT   = 0,
        @dReturnDecimal  FLOAT   = 0.0;
     BEGIN
        -- Remove leading and trailing spaces
        SET @strDegMinSecB = REPLACE(@p_strDegMinSec,' ','');
        -- assume no leading and trailing spaces?
        SET @intDmsLen = DATALENGTH(@strDegMinSecB);
        SET @blnGotSeparator = 0; -- Not in separator sequence right now
        -- Loop over string, replacing anything that is not a digit or a
        -- decimal separator with
        -- a single blank
        SET @i = 0;
        WHILE ( @i <= @intDmsLen)
        BEGIN
           SET @i = @i + 1;
           -- Get current character
           SET @strChr = SUBSTRING(@strDegMinSecB, @i, 1);
           -- either add character to normalized string or replace
           -- separator sequence with single blank
           IF (CHARINDEX(@strChr,N'0123456789,.') > 0 )
           BEGIN
              -- add character but replace comma with point
              IF ((@strChr <> N',') )
              BEGIN
                 SET @strNorm = @strNorm + @strChr;
              END
              ELSE
              BEGIN
                 SET @strNorm = @strNorm + N'.';
              END;
              SET @blnGotSeparator = 0;
           END;
           ELSE
           BEGIN
             IF (CHARINDEX(@strChr,N'neswNESW') > 0 ) -- Extract Compass Point IF (present             BEGIN
               SET @strCompassPoint = UPPER(@strChr);
             END;
             ELSE
             BEGIN
                -- ensure only one separator is replaced with a marker -
                -- suppress the rest
                IF (@blnGotSeparator = 0 )
                BEGIN
                   SET @strNorm = @strNorm + N'@';
                   SET @blnGotSeparator = 0;
                END;
              END;
           END;
        END /* LOOP */
        -- Split normalized string into array of max 3 components
        DECLARE tokenList CURSOR FOR
           SELECT a.token
             FROM dbo.Tokenizer(@strNorm,N'@') a;
      OPEN tokenList
      FETCH NEXT FROM tokenList
     INTO @token
        SET @i = 1;
      WHILE ( ==@@==FETCH_STATUS = 0 )
      BEGIN
           --convert specified components to double
          IF ( @i = 1 ) SET @dDeg = CAST(@token AS FLOAT);
          IF ( @i = 2 ) SET @dMin = CAST(@token AS FLOAT);
          IF ( @i = 3 ) SET @dSec = CAST(@token AS FLOAT);
          SET @i = @i + 1
          FETCH NEXT FROM tokenList INTO @token
      END;
      CLOSE tokenList
      DEALLOCATE tokenList
        -- convert components to value
        SET @dReturnDecimal = CASE WHEN UPPER(@strCompassPoint) IN (N'S',N'W')
                                   THEN -1
                                   ELSE 1
                               END
                              *
                              (@dDeg + @dMin / 60 + @dSec / 3600);
        RETURN @dReturnDecimal;
     END;
 END
 GO

And here are a few examples.

 SELECT a.DD
   FROM (
       SELECT 1 AS id, DMSS2DD('43� 0''   50.00"S') AS DD
 UNION SELECT 2 AS id, DMSS2DD('43� 30''  45.50"N') AS DD
 UNION SELECT 3 AS id, DMSS2DD('147� 50'' 30.60"E') AS DD
 UNION SELECT 4 AS id, DMSS2DD('65� 10''  12.60"W') AS DD
 ) a
 ORDER BY a.id;

Result

DD
-43.0138888888889
43.5126388888889
147.841833333333
-65.1701666666667

I hope this is of use to someone.

Leave a Reply

Your email address will not be published. Required fields are marked *