Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2008 Forums
 Transact-SQL (2008)
 Is this the best way to calculate cardinal dir?

Author  Topic 

timgaunt
Posting Yak Master

115 Posts

Posted - 2012-05-17 : 04:23:02
I'm porting some C# code over to SQL and need to calculate the cardinal direction (North, East, South, West etc) from two sets of co-ordinates (currently stored as floats).

I've pretty much ported the code directly and I was wondering whether this the best way to be doing it -from both the perspective of the function and the code itself.

The return int represents an enum identifier from the code.

This will be used to update about 1m records at a time so although not crucial (it's a background task), performance is important.

CREATE FUNCTION CalculateCardinalDirection
(
@fromLatitude FLOAT,
@fromLongitude FLOAT,
@toLatitude FLOAT,
@toLongitude FLOAT
)
RETURNS int
AS
BEGIN

DECLARE @dLon float,
@dPhi float,
@bearing float,
@pi180 FLOAT,
@piDoubled FLOAT,
@piPlus2 FLOAT,
@direction int

SET @pi180 = pi() / 180
SET @piDoubled = 2 * pi()
SET @piPlus2 = pi() / 4

SET @dLon = ((@toLongitude - @fromLongitude) * @pi180)
SET @dPhi = log(tan((@toLatitude * @pi180) / 2 + @piPlus2) / tan((@fromLatitude * @pi180) / 2 + @piPlus2))

IF (abs(@dLon) > pi())
BEGIN
IF @dLon > 0
BEGIN
SET @dLon = -(@piDoubled - @dLon)
END
ELSE
BEGIN
SET @dLon = (@piDoubled + @dLon)
END
END

SET @bearing = cast(((ATN2(@dLon, @dPhi) * 180 / pi()) + 360) AS DECIMAL(18,15)) % cast(360 AS DECIMAL(18,15))

RETURN CASE
--NE
WHEN @bearing >= 22.5 AND @bearing < 67.5 THEN 2
--E
WHEN @bearing >= 67.5 AND @bearing < 112.5 THEN 4
--SE
WHEN @bearing >= 112.5 AND @bearing < 157.5 THEN 6
--S
WHEN @bearing >= 157.5 AND @bearing < 202.5 THEN 8
--SW
WHEN @bearing >= 202.5 AND @bearing < 247.5 THEN 10
--W
WHEN @bearing >= 247.5 AND @bearing < 292.5 THEN 12
--NW
WHEN @bearing >= 292.5 AND @bearing < 337.5 THEN 14
--N
ELSE 0
END

END
GO


----------------------------
http://blogs.thesitedoctor.co.uk/tim/

yosiasz
Master Smack Fu Yak Hacker

1635 Posts

Posted - 2012-05-17 : 09:24:29
Tim

1. why are you porting it over from c# to sql
2. Have you benchmarked this and does it scale better than c#
3. Have you looked at using CLR

<><><><><><><><><><><><><><><><><>
If you don't have the passion to help people, you have no passion
Go to Top of Page

timgaunt
Posting Yak Master

115 Posts

Posted - 2012-05-17 : 10:20:30
1. Because it's being executed across so many records one at a time rather than bulk (there's no way to work around this in the application) and it takes over an hour to run
2. Benchmarked no but running across the same record set it takes a matter of seconds
3. Not in this instance no, that's a good shout if you think it will be faster

----------------------------
I've finally started blogging (all be it badly!)

Check it out:
http://blogs.thesitedoctor.co.uk/tim/
Go to Top of Page

yosiasz
Master Smack Fu Yak Hacker

1635 Posts

Posted - 2012-05-17 : 12:33:55
Tim

functions are also called for each row. for 1 million rows , 1 million calls to your function. your porting the function to sql will not accomplish anything different than the c# code. you might want to do an inline calculation instead of using a function for such large dataset. if someone else could chime in to confirm or object what I am saying.

<><><><><><><><><><><><><><><><><>
If you don't have the passion to help people, you have no passion
Go to Top of Page

timgaunt
Posting Yak Master

115 Posts

Posted - 2012-05-17 : 12:47:23
I don't want to get too technical but in the instance of this codebase the data access layer will effectively move the entire dataset it's about to work on into memory and try and work on that. We've tried chunking etc.

The speed issue is not caused by the method which works out the location, it's a data retrieval/update issue. We're currently working on resolving that side of things but my interest is more about the performance of the above SQL code and whether I'm missing a trick :)

----------------------------
I've finally started blogging (all be it badly!)

Check it out:
http://blogs.thesitedoctor.co.uk/tim/
Go to Top of Page

yosiasz
Master Smack Fu Yak Hacker

1635 Posts

Posted - 2012-05-18 : 14:29:01
Tim

If you could provide a large set of data for long+lat, I think we could provide a more accurate answer. As is the code looks good to me. I am testing it out with a very small subset of data and it is not bringing the result I am expecting.

can you provide long lat points that has

IF (abs(@dLon) > pi())


<><><><><><><><><><><><><><><><><>
If you don't have the passion to help people, you have no passion
Go to Top of Page

yosiasz
Master Smack Fu Yak Hacker

1635 Posts

Posted - 2012-05-18 : 15:00:29
this is what I was thinking to use as inline calculation. you leverage a well indexed table


drop table dbo.bearings

create table dbo.bearings(bearingstart decimal(10,1), bearingend decimal(10,1), bearingid int,
CONSTRAINT [PK_bearings] PRIMARY KEY CLUSTERED
(
bearingid asc,
bearingstart ASC,
bearingend ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

insert into dbo.bearings
select 22.5, 67.4, 2
UNION
select 67.5, 112.4, 4
UNION
select 112.5, 157.4, 6
UNION
select 157.5, 202.4, 8
UNION
select 202.5, 247.4, 10
UNION
select 247.5, 292.4, 10
UNION
select 292.5, 337.4, 14

select * from bearings

--Addis Abeba 09°02'N 38°42'E
--Bahir Dar 11°37'N 37°10'E


declare @fromLatitude FLOAT,
@fromLongitude FLOAT,
@toLatitude FLOAT,
@toLongitude FLOAT

SET @fromLatitude = 9.022736
SET @fromLongitude = 38.746799
SET @toLatitude = 11.616667
SET @toLongitude = 38.066667

DROP TABLE dbo.Cities

create table dbo.Cities(FromCityName varchar(50), FromLatitude float, FromLongitude float, ToCityName varchar(50),ToLatitude float, ToLongitude float) ON [PRIMARY]


insert into Cities
SELECT 'Addis Abeba', @fromLatitude, @fromLongitude, 'Bahir Dar', @toLatitude , @toLongitude

select * from Cities

DECLARE @dLon float,
@dPhi float,
@bearing float,
@pi180 FLOAT,
@piDoubled FLOAT,
@piPlus2 FLOAT,
@direction int

SET @pi180 = pi() / 180
SET @piDoubled = 2 * pi()
SET @piPlus2 = pi() / 4
PRINT @pi180
PRINT @piDoubled
PRINT @piPlus2
SELECT [FromCityName]
,[FromLatitude]
,[FromLongitude]
,[ToCityName]
,[ToLatitude]
,[ToLongitude] ,
ABS( (( ToLongitude- FromLongitude) * @pi180) ) dLon,
log(tan((ToLongitude * @pi180) / 2 + @piPlus2) / tan((FromLongitude * @pi180) / 2 + @piPlus2)) dPhi,
CASE
WHEN (( ToLongitude- FromLongitude) * @pi180) > 0 THEN -( @piDoubled - ((ToLongitude - FromLongitude) * @pi180) )
ELSE ( @piDoubled + ((ToLongitude - FromLongitude) * @pi180) )
END dLonCalculated

FROM dbo.Cities

;with cteCities([FromCityName]
,[FromLatitude]
,[FromLongitude]
,[ToCityName]
,[ToLatitude]
,[ToLongitude]
,dLon
,dPhi
,dLonCalculated
,bearing

)
AS
(
SELECT [FromCityName]
,[FromLatitude]
,[FromLongitude]
,[ToCityName]
,[ToLatitude]
,[ToLongitude] ,
ABS( (( ToLongitude- FromLongitude) * @pi180) ) dLon,
log(tan((ToLongitude * @pi180) / 2 + @piPlus2) / tan((FromLongitude * @pi180) / 2 + @piPlus2)) dPhi,
CASE
WHEN (( ToLongitude- FromLongitude) * @pi180) > 0 THEN -( @piDoubled - ((ToLongitude - FromLongitude) * @pi180) )
ELSE ( @piDoubled + ((ToLongitude - FromLongitude) * @pi180) )
END dLonCalculated,
CASE
WHEN (( ToLongitude- FromLongitude) * @pi180) > 0
THEN cast(((ATN2(-( @piDoubled - ((ToLongitude - FromLongitude) * @pi180) ), (log(tan((ToLongitude * @pi180) / 2 + @piPlus2) / tan((FromLongitude * @pi180) / 2 + @piPlus2))) ) * 180 / pi()) + 360) AS DECIMAL(18,15)) % cast(360 AS DECIMAL(18,15))
ELSE cast(((ATN2(( @piDoubled + ((ToLongitude - FromLongitude) * @pi180) ), (log(tan((ToLongitude * @pi180) / 2 + @piPlus2) / tan((FromLongitude * @pi180) / 2 + @piPlus2))) ) * 180 / pi()) + 360) AS DECIMAL(18,15)) % cast(360 AS DECIMAL(18,15))
END bearing



FROM dbo.Cities
)
SELECT *
FROM cteCities cte
cross apply dbo.bearings br
where bearing between br.bearingstart and bearingend



Something seems wrong with the result. The resulting bearing says Bahir Dar is E of Addis Abeba when actually it is the opposite.

<><><><><><><><><><><><><><><><><>
If you don't have the passion to help people, you have no passion
Go to Top of Page

timgaunt
Posting Yak Master

115 Posts

Posted - 2012-05-18 : 20:13:48
Interesting. Thanks

----------------------------
I've finally started blogging (all be it badly!)

Check it out:
http://blogs.thesitedoctor.co.uk/tim/
Go to Top of Page
   

- Advertisement -