SQL Server Forums
Profile | Register | Active Topics | Members | Search | Forum FAQ
 
Register Now and get your question answered!
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 General SQL Server Forums
 Script Library
 Function F_FIND_SEARCH_LIMITS
 New Topic  Reply to Topic
 Printer Friendly
Author Previous Topic Topic Next Topic  

Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)

USA
7020 Posts

Posted - 10/10/2008 :  16:39:39  Show Profile  Reply with Quote
The returned values from function F_FIND_SEARCH_LIMITS can be used to setup a bounded search of a table containing min/max Latitude and Longitude values for applications that need to return values within a specified distance from a central point.

This allows use of table indexes to do an efficient search for values than may be within the search radius. The result set can then be trimmed by calculating the great circle distance to eliminate values outside the search radius. Function F_GREAT_CIRCLE_DISTANCE can be used to calculate the distances of that result set. Typically, about 75% of the values from the bounded query will be within the search radius.

The algorithm used in F_FIND_SEARCH_LIMITS is a loop where the calculation is refined on each loop until the error is very small. A direct calculation would be better, but this produces results accurate to less than a meter over the widest possible search ranges, and handles especially difficult search limit calculations with a large search radius and/or high latitudes close to the Earths pole. Note that it will not allow a search radius of less than 1 meter or that comes within 100 kilometers of the Earths North or South poles.

This function uses function F_GREAT_CIRCLE_DISTANCE on the link below.
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=81360




if object_id('dbo.F_FIND_SEARCH_LIMITS') is not null
	drop function dbo.F_FIND_SEARCH_LIMITS
go
create function dbo.F_FIND_SEARCH_LIMITS
	(
	@Latitude	float,
	@Longitude 	float,
	@SearchRadius	float
	)
returns	@search_Limits table
	(
	MaxLongitude		decimal(9,6)	not null,
	MinLongitude		decimal(9,6)	not null,
	MaxLatitude		decimal(9,6)	not null,
	MinLatitude		decimal(9,6)	not null
	)
as
/*

Function: F_FIND_SEARCH_LIMITS

Function F_FIND_SEARCH_LIMITS is used to find the min/max limits
of Latitude and Longitude for the search radius passed in
parameter @SearchRadius from the point on the earth defined by
input parameters @Latitude and @Longitude.

The returned values can be used to setup a search of a table
containing Latitude and Longitude values for applications
that need to return values within a specified distance from
a central point.  This allows use of table indexes to
do an efficient search for values than may be within the 
search radius.  The result set can then be trimmed by
calculating the great circle distance to eliminate
values outside the search radius.

The calculation is refined in a loop to produce a result that
is accurate to less than 1 meter from the actual distance,
and is slightly larger that the search radius to prevent
excluding valid points.

Parameter @SearchRadius is in kilometers.
To use it with miles, multiply @SearchRadius by 1.609344E

This function uses function dbo.F_GREAT_CIRCLE_DISTANCE
to calculate great circle distance.
 
*/

begin

declare @Degrees	float
declare @MaxLon		float
declare @MinLon		float
declare @MaxLat		float
declare @MinLat		float
declare @DegreePerKM 	float
declare @DistanceToPole	float
declare @WorkDistance	float

declare	@mess		varchar(400)
declare @cr 		varchar(2)
declare @x 		int

set @cr = char(13)+Char(10)
set @mess	= ''

-- Verify parameter @Latitude is valid
if abs(@Latitude) >= 90.0E
	begin
	set @mess = 'Greater than maximum allowed Latitude, '+
	', Latitude = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(abs(@Latitude),6))),'NULL')
	goto Error_Exit
	end

-- Verify parameter @Longitude is valid
if abs(@Longitude) > 180.0E
	begin
	set @mess = 'Greater than maximum allowed Longitude, '+
	', Longitude = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(abs(@Longitude),6))),'NULL')
	goto Error_Exit
	end

-- Verify parameter @SearchRadius is greater than .001 kilometer
if @SearchRadius < .001E
	begin
	set @mess = 'Minimum search radius is .001'+
	', Search Radius = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(@SearchRadius,6))),'NULL')
	goto Error_Exit
	end

set @DistanceToPole = dbo.F_GREAT_CIRCLE_DISTANCE(0E,abs(@Latitude),0E,90E)

-- Verify that the Search Radius is no closer than 100 kilometers from a pole of the Earth
if @SearchRadius+100 > @DistanceToPole
	begin
	set @mess = 'Latitude too close to pole for search radius,'+@cr+
	'Latitude = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(@Latitude,6))),'NULL')+
	', Search Radius = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(@SearchRadius,6))),'NULL')+
	', Distance to Pole = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(@DistanceToPole,6))),'NULL')
	goto Error_Exit
	end

-- Get number of degrees for a search at the equator
set @Degrees = @SearchRadius/(dbo.F_GREAT_CIRCLE_DISTANCE(0E,0E,0E,90E)/90.000000000E)

-- Get great circle distance 90 degrees away on the same latitude
-- for initial estimate of degrees per kilometer.
set @DegreePerKM = (90E)/dbo.F_GREAT_CIRCLE_DISTANCE(@Latitude,0E,@Latitude,90E)

select
	@MaxLon = @Longitude + (@DegreePerKM*@SearchRadius),
	@MinLon = @Longitude - (@DegreePerKM*@SearchRadius),
	@MaxLat	= @Latitude + @Degrees+.0000000001E ,
	@MinLat	= @Latitude - @Degrees-.0000000001E

declare @count int
set @count = 0

while 1=1
begin
	-- Calculate great circle distance along latitude line
	set @WorkDistance = dbo.F_GREAT_CIRCLE_DISTANCE(@Latitude,@Longitude,@Latitude,@MaxLon)

	-- Exit when calculation is good enough or loop count greater than 15
	if @WorkDistance-@SearchRadius < 0.0000005E or 
	   @count > 15
		begin
		break
		end

	set @count = @count+1

	-- Refine estimate of degrees per kilometer
	set @DegreePerKM = (@MaxLon-@Longitude)/@WorkDistance
	
	-- Recalculate the min/max estimate with the refined degrees per kilometer
	set @MaxLon = @Longitude+(@DegreePerKM*@SearchRadius)
	set @MinLon = @Longitude-(@DegreePerKM*@SearchRadius)

	end  -- End of While loop

-- Add increment to ensure limits are slightly outside search radius
select
	@MaxLon = round(@MaxLon+0.0000005E,6),
	@MinLon = round(@MinLon-0.0000005E,6),
	@MaxLat	= round(@MaxLat+0.0000005E,6),
	@MinLat	= round(@MinLat-0.0000005E,6)

insert into @search_Limits
select
	@MaxLon,
	@MinLon,
	@MaxLat,
	@MinLat

return

Error_Exit:
-- Cause an conversion error to occur to send an error massage
if @mess <> ''
	select @x=
	convert(int,@cr+@cr+@cr+replicate('*',90)+@cr+@mess+@cr+replicate('*',90)+@cr+@cr+@cr)

return

end
go


-- Test Script to confirm output is valid


declare @Latitude	float,
	@Longitude 	float,
	@SearchRadius	float

set @SearchRadius 	= 3000.000E
set @Latitude		= 50E
set @Longitude		= 0E

select
	Latitude	= convert(numeric(12,6),round(@Latitude,6)),
	Longitude	= convert(numeric(12,6),round(@Longitude,6)),
	SearchRadius	= convert(numeric(12,6),round(@SearchRadius,6))

select
	MaxLat	= a.MaxLatitude,
	MinLat	= a.MinLatitude,
 	MaxLon	= a.MaxLongitude,
	MinLon	= a.MinLongitude
from
	dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) a

Select
	KmToMaxLon =
		convert(numeric(12,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE(@Latitude, @Longitude, @Latitude, a.MaxLongitude),6)) ,
	KmToMinLon =
		convert(numeric(12,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE(@Latitude, @Longitude, @Latitude, a.MinLongitude),6)) ,
	KmToMaxLat =
		convert(numeric(12,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.MaxLatitude, @Longitude),6)) ,
	KmToMinLat =
		convert(numeric(12,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.MinLatitude, @Longitude),6))
from
	dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) a



--End of Test Script


Results of Test Script:

Latitude       Longitude      SearchRadius   
-------------- -------------- -------------- 
50.000000      .000000        3000.000000

(1 row(s) affected)

MaxLat      MinLat      MaxLon      MinLon      
----------- ----------- ----------- ----------- 
76.979649   23.020351   42.557769   -42.557769

(1 row(s) affected)

KmToMaxLon     KmToMinLon     KmToMaxLat     KmToMinLat     
-------------- -------------- -------------- -------------- 
3000.000026    3000.000026    3000.000091    3000.000091

(1 row(s) affected)



/*
Sample script for a typical search against a Zip Code table
*/

declare @MaxLongitude		decimal(9,6)
declare @MinLongitude		decimal(9,6)
declare @MaxLatitude		decimal(9,6)
declare @MinLatitude		decimal(9,6)

declare @Latitude	float,
	@Longitude 	float,
	@SearchRadius	float

-- Set Search Radius in miles
-- set @SearchRadius 	=  20E * 1.609344E

-- Set Search Radius in kilometers
set @SearchRadius 	=  35E

-- Set Latitude/Longitude of search center
set @Latitude		=  38.980022E
set @Longitude		= -83.276192E

-- Get Search limits
select
	@MaxLongitude	= a.MaxLongitude,
	@MinLongitude	= a.MinLongitude,
	@MaxLatitude	= a.MaxLatitude,
	@MinLatitude	= a.MinLatitude
from
	dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) a


-- Query Zip code Table
select
	-- Show distance
	Distance =
		convert(decimal(9,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.LATITUDE, a.LONGITUDE),6)),
	*
from
	dbo.T_ZIP_CODE a
where
	-- Select only items within search limits
	a.LATITUDE  between @MinLatitude  and @MaxLatitude	and
	a.LONGITUDE between @MinLongitude and @MaxLongitude	and
	-- Select only items where distance is within search radius
	dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.LATITUDE, a.LONGITUDE) <= @SearchRadius
order by
	-- Order closest to farthest
	dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.LATITUDE, a.LONGITUDE)

/*
End of script for typical search against a Zip Code table
*/








CODO ERGO SUM

Edited by - Michael Valentine Jones on 10/10/2008 18:13:46

dmbware
Starting Member

4 Posts

Posted - 12/02/2008 :  12:22:02  Show Profile  Reply with Quote
Michael I have a DB with 50k records of properties each having a lat and long, map code, zip code and address. What I am trying to do is pull all properties that fall within a 1 mile radius of a given property. Can you lend me your thoughts on how to approach this equation? Should I use zip code with long and lat?


quote:
Originally posted by Michael Valentine Jones

The returned values from function F_FIND_SEARCH_LIMITS can be used to setup a bounded search of a table containing min/max Latitude and Longitude values for applications that need to return values within a specified distance from a central point.

This allows use of table indexes to do an efficient search for values than may be within the search radius. The result set can then be trimmed by calculating the great circle distance to eliminate values outside the search radius. Function F_GREAT_CIRCLE_DISTANCE can be used to calculate the distances of that result set. Typically, about 75% of the values from the bounded query will be within the search radius.

The algorithm used in F_FIND_SEARCH_LIMITS is a loop where the calculation is refined on each loop until the error is very small. A direct calculation would be better, but this produces results accurate to less than a meter over the widest possible search ranges, and handles especially difficult search limit calculations with a large search radius and/or high latitudes close to the Earths pole. Note that it will not allow a search radius of less than 1 meter or that comes within 100 kilometers of the Earths North or South poles.

This function uses function F_GREAT_CIRCLE_DISTANCE on the link below.
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=81360




if object_id('dbo.F_FIND_SEARCH_LIMITS') is not null
	drop function dbo.F_FIND_SEARCH_LIMITS
go
create function dbo.F_FIND_SEARCH_LIMITS
	(
	@Latitude	float,
	@Longitude 	float,
	@SearchRadius	float
	)
returns	@search_Limits table
	(
	MaxLongitude		decimal(9,6)	not null,
	MinLongitude		decimal(9,6)	not null,
	MaxLatitude		decimal(9,6)	not null,
	MinLatitude		decimal(9,6)	not null
	)
as
/*

Function: F_FIND_SEARCH_LIMITS

Function F_FIND_SEARCH_LIMITS is used to find the min/max limits
of Latitude and Longitude for the search radius passed in
parameter @SearchRadius from the point on the earth defined by
input parameters @Latitude and @Longitude.

The returned values can be used to setup a search of a table
containing Latitude and Longitude values for applications
that need to return values within a specified distance from
a central point.  This allows use of table indexes to
do an efficient search for values than may be within the 
search radius.  The result set can then be trimmed by
calculating the great circle distance to eliminate
values outside the search radius.

The calculation is refined in a loop to produce a result that
is accurate to less than 1 meter from the actual distance,
and is slightly larger that the search radius to prevent
excluding valid points.

Parameter @SearchRadius is in kilometers.
To use it with miles, multiply @SearchRadius by 1.609344E

This function uses function dbo.F_GREAT_CIRCLE_DISTANCE
to calculate great circle distance.
 
*/

begin

declare @Degrees	float
declare @MaxLon		float
declare @MinLon		float
declare @MaxLat		float
declare @MinLat		float
declare @DegreePerKM 	float
declare @DistanceToPole	float
declare @WorkDistance	float

declare	@mess		varchar(400)
declare @cr 		varchar(2)
declare @x 		int

set @cr = char(13)+Char(10)
set @mess	= ''

-- Verify parameter @Latitude is valid
if abs(@Latitude) >= 90.0E
	begin
	set @mess = 'Greater than maximum allowed Latitude, '+
	', Latitude = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(abs(@Latitude),6))),'NULL')
	goto Error_Exit
	end

-- Verify parameter @Longitude is valid
if abs(@Longitude) > 180.0E
	begin
	set @mess = 'Greater than maximum allowed Longitude, '+
	', Longitude = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(abs(@Longitude),6))),'NULL')
	goto Error_Exit
	end

-- Verify parameter @SearchRadius is greater than .001 kilometer
if @SearchRadius < .001E
	begin
	set @mess = 'Minimum search radius is .001'+
	', Search Radius = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(@SearchRadius,6))),'NULL')
	goto Error_Exit
	end

set @DistanceToPole = dbo.F_GREAT_CIRCLE_DISTANCE(0E,abs(@Latitude),0E,90E)

-- Verify that the Search Radius is no closer than 100 kilometers from a pole of the Earth
if @SearchRadius+100 > @DistanceToPole
	begin
	set @mess = 'Latitude too close to pole for search radius,'+@cr+
	'Latitude = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(@Latitude,6))),'NULL')+
	', Search Radius = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(@SearchRadius,6))),'NULL')+
	', Distance to Pole = '+
	isnull(convert(varchar(20),convert(decimal(20,6),round(@DistanceToPole,6))),'NULL')
	goto Error_Exit
	end

-- Get number of degrees for a search at the equator
set @Degrees = @SearchRadius/(dbo.F_GREAT_CIRCLE_DISTANCE(0E,0E,0E,90E)/90.000000000E)

-- Get great circle distance 90 degrees away on the same latitude
-- for initial estimate of degrees per kilometer.
set @DegreePerKM = (90E)/dbo.F_GREAT_CIRCLE_DISTANCE(@Latitude,0E,@Latitude,90E)

select
	@MaxLon = @Longitude + (@DegreePerKM*@SearchRadius),
	@MinLon = @Longitude - (@DegreePerKM*@SearchRadius),
	@MaxLat	= @Latitude + @Degrees+.0000000001E ,
	@MinLat	= @Latitude - @Degrees-.0000000001E

declare @count int
set @count = 0

while 1=1
begin
	-- Calculate great circle distance along latitude line
	set @WorkDistance = dbo.F_GREAT_CIRCLE_DISTANCE(@Latitude,@Longitude,@Latitude,@MaxLon)

	-- Exit when calculation is good enough or loop count greater than 15
	if @WorkDistance-@SearchRadius < 0.0000005E or 
	   @count > 15
		begin
		break
		end

	set @count = @count+1

	-- Refine estimate of degrees per kilometer
	set @DegreePerKM = (@MaxLon-@Longitude)/@WorkDistance
	
	-- Recalculate the min/max estimate with the refined degrees per kilometer
	set @MaxLon = @Longitude+(@DegreePerKM*@SearchRadius)
	set @MinLon = @Longitude-(@DegreePerKM*@SearchRadius)

	end  -- End of While loop

-- Add increment to ensure limits are slightly outside search radius
select
	@MaxLon = round(@MaxLon+0.0000005E,6),
	@MinLon = round(@MinLon-0.0000005E,6),
	@MaxLat	= round(@MaxLat+0.0000005E,6),
	@MinLat	= round(@MinLat-0.0000005E,6)

insert into @search_Limits
select
	@MaxLon,
	@MinLon,
	@MaxLat,
	@MinLat

return

Error_Exit:
-- Cause an conversion error to occur to send an error massage
if @mess <> ''
	select @x=
	convert(int,@cr+@cr+@cr+replicate('*',90)+@cr+@mess+@cr+replicate('*',90)+@cr+@cr+@cr)

return

end
go


-- Test Script to confirm output is valid


declare @Latitude	float,
	@Longitude 	float,
	@SearchRadius	float

set @SearchRadius 	= 3000.000E
set @Latitude		= 50E
set @Longitude		= 0E

select
	Latitude	= convert(numeric(12,6),round(@Latitude,6)),
	Longitude	= convert(numeric(12,6),round(@Longitude,6)),
	SearchRadius	= convert(numeric(12,6),round(@SearchRadius,6))

select
	MaxLat	= a.MaxLatitude,
	MinLat	= a.MinLatitude,
 	MaxLon	= a.MaxLongitude,
	MinLon	= a.MinLongitude
from
	dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) a

Select
	KmToMaxLon =
		convert(numeric(12,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE(@Latitude, @Longitude, @Latitude, a.MaxLongitude),6)) ,
	KmToMinLon =
		convert(numeric(12,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE(@Latitude, @Longitude, @Latitude, a.MinLongitude),6)) ,
	KmToMaxLat =
		convert(numeric(12,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.MaxLatitude, @Longitude),6)) ,
	KmToMinLat =
		convert(numeric(12,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.MinLatitude, @Longitude),6))
from
	dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) a



--End of Test Script


Results of Test Script:

Latitude       Longitude      SearchRadius   
-------------- -------------- -------------- 
50.000000      .000000        3000.000000

(1 row(s) affected)

MaxLat      MinLat      MaxLon      MinLon      
----------- ----------- ----------- ----------- 
76.979649   23.020351   42.557769   -42.557769

(1 row(s) affected)

KmToMaxLon     KmToMinLon     KmToMaxLat     KmToMinLat     
-------------- -------------- -------------- -------------- 
3000.000026    3000.000026    3000.000091    3000.000091

(1 row(s) affected)



/*
Sample script for a typical search against a Zip Code table
*/

declare @MaxLongitude		decimal(9,6)
declare @MinLongitude		decimal(9,6)
declare @MaxLatitude		decimal(9,6)
declare @MinLatitude		decimal(9,6)

declare @Latitude	float,
	@Longitude 	float,
	@SearchRadius	float

-- Set Search Radius in miles
-- set @SearchRadius 	=  20E * 1.609344E

-- Set Search Radius in kilometers
set @SearchRadius 	=  35E

-- Set Latitude/Longitude of search center
set @Latitude		=  38.980022E
set @Longitude		= -83.276192E

-- Get Search limits
select
	@MaxLongitude	= a.MaxLongitude,
	@MinLongitude	= a.MinLongitude,
	@MaxLatitude	= a.MaxLatitude,
	@MinLatitude	= a.MinLatitude
from
	dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) a


-- Query Zip code Table
select
	-- Show distance
	Distance =
		convert(decimal(9,6),round(
		dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.LATITUDE, a.LONGITUDE),6)),
	*
from
	dbo.T_ZIP_CODE a
where
	-- Select only items within search limits
	a.LATITUDE  between @MinLatitude  and @MaxLatitude	and
	a.LONGITUDE between @MinLongitude and @MaxLongitude	and
	-- Select only items where distance is within search radius
	dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.LATITUDE, a.LONGITUDE) <= @SearchRadius
order by
	-- Order closest to farthest
	dbo.F_GREAT_CIRCLE_DISTANCE( @Latitude, @Longitude, a.LATITUDE, a.LONGITUDE)

/*
End of script for typical search against a Zip Code table
*/








CODO ERGO SUM

Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
 Printer Friendly
Jump To:
SQL Server Forums © 2000-2009 SQLTeam Publishing, LLC Go To Top Of Page
This page was generated in 0.14 seconds. Powered By: Snitz Forums 2000