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.
Author |
Topic |
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2008-10-10 : 16:39:39
|
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=81360if object_id('dbo.F_FIND_SEARCH_LIMITS') is not null drop function dbo.F_FIND_SEARCH_LIMITSgocreate 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_LIMITSFunction F_FIND_SEARCH_LIMITS is used to find the min/max limitsof Latitude and Longitude for the search radius passed inparameter @SearchRadius from the point on the earth defined byinput parameters @Latitude and @Longitude.The returned values can be used to setup a search of a tablecontaining Latitude and Longitude values for applicationsthat need to return values within a specified distance froma central point. This allows use of table indexes todo an efficient search for values than may be within the search radius. The result set can then be trimmed bycalculating the great circle distance to eliminatevalues outside the search radius.The calculation is refined in a loop to produce a result thatis accurate to less than 1 meter from the actual distance,and is slightly larger that the search radius to preventexcluding valid points.Parameter @SearchRadius is in kilometers.To use it with miles, multiply @SearchRadius by 1.609344EThis function uses function dbo.F_GREAT_CIRCLE_DISTANCEto calculate great circle distance. */begindeclare @Degrees floatdeclare @MaxLon floatdeclare @MinLon floatdeclare @MaxLat floatdeclare @MinLat floatdeclare @DegreePerKM floatdeclare @DistanceToPole floatdeclare @WorkDistance floatdeclare @mess varchar(400)declare @cr varchar(2)declare @x intset @cr = char(13)+Char(10)set @mess = ''-- Verify parameter @Latitude is validif 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 validif 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 kilometerif @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 endset @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 Earthif @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 equatorset @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-.0000000001Edeclare @count intset @count = 0while 1=1begin -- 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 radiusselect @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_Limitsselect @MaxLon, @MinLon, @MaxLat, @MinLatreturnError_Exit:-- Cause an conversion error to occur to send an error massageif @mess <> '' select @x= convert(int,@cr+@cr+@cr+replicate('*',90)+@cr+@mess+@cr+replicate('*',90)+@cr+@cr+@cr)returnendgo-- Test Script to confirm output is validdeclare @Latitude float, @Longitude float, @SearchRadius floatset @SearchRadius = 3000.000Eset @Latitude = 50Eset @Longitude = 0Eselect 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.MinLongitudefrom dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) aSelect 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 kilometersset @SearchRadius = 35E-- Set Latitude/Longitude of search centerset @Latitude = 38.980022Eset @Longitude = -83.276192E-- Get Search limitsselect @MaxLongitude = a.MaxLongitude, @MinLongitude = a.MinLongitude, @MaxLatitude = a.MaxLatitude, @MinLatitude = a.MinLatitudefrom dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) a-- Query Zip code Tableselect -- 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 awhere -- 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) <= @SearchRadiusorder 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 |
|
dmbware
Starting Member
4 Posts |
Posted - 2008-12-02 : 12:22:02
|
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=81360if object_id('dbo.F_FIND_SEARCH_LIMITS') is not null drop function dbo.F_FIND_SEARCH_LIMITSgocreate 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_LIMITSFunction F_FIND_SEARCH_LIMITS is used to find the min/max limitsof Latitude and Longitude for the search radius passed inparameter @SearchRadius from the point on the earth defined byinput parameters @Latitude and @Longitude.The returned values can be used to setup a search of a tablecontaining Latitude and Longitude values for applicationsthat need to return values within a specified distance froma central point. This allows use of table indexes todo an efficient search for values than may be within the search radius. The result set can then be trimmed bycalculating the great circle distance to eliminatevalues outside the search radius.The calculation is refined in a loop to produce a result thatis accurate to less than 1 meter from the actual distance,and is slightly larger that the search radius to preventexcluding valid points.Parameter @SearchRadius is in kilometers.To use it with miles, multiply @SearchRadius by 1.609344EThis function uses function dbo.F_GREAT_CIRCLE_DISTANCEto calculate great circle distance. */begindeclare @Degrees floatdeclare @MaxLon floatdeclare @MinLon floatdeclare @MaxLat floatdeclare @MinLat floatdeclare @DegreePerKM floatdeclare @DistanceToPole floatdeclare @WorkDistance floatdeclare @mess varchar(400)declare @cr varchar(2)declare @x intset @cr = char(13)+Char(10)set @mess = ''-- Verify parameter @Latitude is validif 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 validif 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 kilometerif @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 endset @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 Earthif @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 equatorset @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-.0000000001Edeclare @count intset @count = 0while 1=1begin -- 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 radiusselect @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_Limitsselect @MaxLon, @MinLon, @MaxLat, @MinLatreturnError_Exit:-- Cause an conversion error to occur to send an error massageif @mess <> '' select @x= convert(int,@cr+@cr+@cr+replicate('*',90)+@cr+@mess+@cr+replicate('*',90)+@cr+@cr+@cr)returnendgo-- Test Script to confirm output is validdeclare @Latitude float, @Longitude float, @SearchRadius floatset @SearchRadius = 3000.000Eset @Latitude = 50Eset @Longitude = 0Eselect 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.MinLongitudefrom dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) aSelect 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 kilometersset @SearchRadius = 35E-- Set Latitude/Longitude of search centerset @Latitude = 38.980022Eset @Longitude = -83.276192E-- Get Search limitsselect @MaxLongitude = a.MaxLongitude, @MinLongitude = a.MinLongitude, @MaxLatitude = a.MaxLatitude, @MinLatitude = a.MinLatitudefrom dbo.F_FIND_SEARCH_LIMITS(@Latitude,@Longitude,@SearchRadius) a-- Query Zip code Tableselect -- 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 awhere -- 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) <= @SearchRadiusorder 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
|
|
|
|
|
|
|
|