I have two geometry variables and I want to find out if they intersect.
My first variable is called #orderBounds2.
DECLARE #orderBounds2 geography
I set it to a value
set #orderBounds2 = 0xE6100000010405000000B81E85EB51F837C07CF2B0506B3A40C0B81E85EB51F837C01283C0CAA1955C4097900F7A363343401283C0CAA1955C4097900F7A363343407CF2B0506B3A40C0B81E85EB51F837C07CF2B0506B3A40C001000000020000000001000000FFFFFFFF0000000003;
I look to see what it is in text.
select #orderBounds2.STAsText() --POLYGON ((-32.4564 -23.97, 114.338 -23.97, 114.338 38.4001, -32.4564 38.4001, -32.4564 -23.97))
My 2nd variable is #geo
declare #geo geography
I set it to a value.
select #geo = 0xE6100000010405000000A228D027F2C6514033F9669B1B034B40CDCCCCCCCC4C514033F9669B1B034B40CDCCCCCCCC4C51400000000000404F40A228D027F2C651400000000000404F40A228D027F2C6514033F9669B1B034B4001000000020000000001000000FFFFFFFF0000000003;
I look to see what it is in text.
select #geo.STAsText() --POLYGON ((54.02428 71.10853, 54.02428 69.2, 62.5 69.2, 62.5 71.10853, 54.02428 71.10853))
Now I want to find out if the polygons that my two variables represent intersect?
select #geo.STIntersects(#orderBounds2) --= 1
The answer is 1, so yes the two areas do intersect.
However, I don't understand why . The polygons in text show (longitude.latitude) points and I cannot see how they intersect.
The 2nd Polygon is between latitude 69.2 and 71.1
The 1st Polygon is between latitude -23.4 and 38.4
The edges of a geography polygon follow great circle arcs, so the segment from 114.338 38.4001 to -32.4564 38.4001 goes very far north.
DECLARE #orderBounds2 geography = cast(0xE6100000010405000000B81E85EB51F837C07CF2B0506B3A40C0B81E85EB51F837C01283C0CAA1955C4097900F7A363343401283C0CAA1955C4097900F7A363343407CF2B0506B3A40C0B81E85EB51F837C07CF2B0506B3A40C001000000020000000001000000FFFFFFFF0000000003 as geography)
declare #geo geography = cast( 0xE6100000010405000000A228D027F2C6514033F9669B1B034B40CDCCCCCCCC4C514033F9669B1B034B40CDCCCCCCCC4C51400000000000404F40A228D027F2C651400000000000404F40A228D027F2C6514033F9669B1B034B4001000000020000000001000000FFFFFFFF0000000003 as geography)
select #orderBounds2
union all
select #geo
outputs
How do I calculate distance between two latitude, longitude points in miles using standard SQL without trigonometry?
Without trig you get incorrect results. Find an explanation with answer, formula and example here:
https://jonisalonen.com/2014/computing-distance-between-coordinates-can-be-simple-and-fast
Approximation without precision and trig
DECLARE #SqDegreeLatInMiles AS REAL = 4774.81
DECLARE #SqDegreeLongInMiles AS REAL = 2809
-- VA, Zip=23452
DECLARE #Point_A_Lat AS REAL = 36.8366
DECLARE #Point_A_Long AS REAL = 76.0952
-- TX, Zip=75225
DECLARE #Point_B_Lat AS REAL = 32.8644
DECLARE #Point_B_Long AS REAL = 96.7946
DECLARE #DistanceInMiles AS REAL
SET #DistanceInMiles = SQRT (#SqDegreeLatInMiles * POWER(#Point_A_Lat - #Point_B_Lat,2) + #SqDegreeLongInMiles * POWER(#Point_A_Long - #Point_B_Long,2))
PRINT #DistanceInMiles
Given
DECLARE #p1 geography
DECLARE #p2 geography
DECLARE #distance int
SET #distance = 10000 -- meters
SET #p1 = geography::Point(51.5001524, -0.1262362, 4326)
How can I set #p2 such that its latitude is #distance meters north or south of #p1?
I have no clue how well this will work in practice, but it can be done just using inbuilt functions.
DECLARE #Distance INT = 10000
DECLARE #latitude DECIMAL(20,10) = 51.5001524
DECLARE #longitude DECIMAL(20,10) = -0.1262362
DECLARE #OriginalPoint GEOGRAPHY = GEOGRAPHY::Point(#latitude, #longitude, 4326)
--This will create a point at the same longitude but at the south pole.
DECLARE #DueSouthPoint GEOGRAPHY = GEOGRAPHY::Point(-90, #longitude, 4326)
DECLARE #SouthLineWithLengthOfDistance GEOGRAPHY =
#OriginalPoint.ShortestLineTo(#DueSouthPoint) --This is a line due south
.STIntersection( --This will return the line segment inside the circle
#OriginalPoint.STBuffer(#distance) --This will draw a circle around the original point with a radius of #distance
)
--Now we have to return the lower point on the line.
--It seems to be fairly inconsistent in which point is first in the line
--I don't want to spend the time to figure it out, so I'm just using a case to determine which point to return.
SELECT
CASE
WHEN #OriginalPoint.STDistance(#SouthLineWithLengthOfDistance.STPointN(1)) = 0
THEN #SouthLineWithLengthOfDistance.STPointN(2)
ELSE #SouthLineWithLengthOfDistance.STPointN(1)
END
I have several thousand records in a development environment, each associated with a centroid of a particular zip code. For testing purposes, I need to randomly scatter each SQL Server geography point 0-5 miles around that centroid.
So in the example below I want to update LocationGeo so it is 0-5 miles away from its respective ZipGeo. Do I have to use a random % applied to each Lon/Lat or is there a better option?
LocationID int
LocationGeo geography
ZipCode char(5)
ZipCode char(5)
ZipGeo geography
I found an answer from this post https://gis.stackexchange.com/a/25883/37373
I adapted the response into SQL Server code.
DECLARE #geo as GEOGRAPHY,#newgeo as GEOGRAPHY
SET #geo = (SELECT ZipGeo FROM Location.ZipCodes WHERE ZipCode='90210')
DECLARE #r float,#t float, #w float, #x float, #y float, #u float, #v float;
SET #u=RAND();
SET #v=RAND();
--8046m = ~ 5 miles
SET #r= 8046/(111300*1.0);
SET #w = #r * sqrt(#u);
SET #t = 2 * PI() * #v;
SET #x = #w * cos(#t);
SET #y = #w * sin(#t);
SET #x = #x / cos(#geo.Lat);
SET #newgeo = geography::STPointFromText('POINT('+CAST(#geo.Long+#x AS VARCHAR(MAX))+' '+CAST(#geo.Lat+#y AS VARCHAR(MAX))+')',4326)
--Convert the distance back to miles to validate
SELECT #geo.STDistance(#newgeo)/1609.34
I am trying to calculate the distance between two positions on a map.
I have stored in my data: Longitude, Latitude, X POS, Y POS.
I have been previously using the below snippet.
DECLARE #orig_lat DECIMAL
DECLARE #orig_lng DECIMAL
SET #orig_lat=53.381538 set #orig_lng=-1.463526
SELECT *,
3956 * 2 * ASIN(
SQRT( POWER(SIN((#orig_lat - abs(dest.Latitude)) * pi()/180 / 2), 2)
+ COS(#orig_lng * pi()/180 ) * COS(abs(dest.Latitude) * pi()/180)
* POWER(SIN((#orig_lng - dest.Longitude) * pi()/180 / 2), 2) ))
AS distance
--INTO #includeDistances
FROM #orig dest
I don't however trust the data coming out of this, it seems to be giving slightly inaccurate results.
Some sample data in case you need it
Latitude Longitude Distance
53.429108 -2.500953 85.2981833133896
Could anybody help me out with my code, I don't mind if you want to fix what I already have if you have a new way of achieving this that would be great.
Please state what unit of measurement your results are in.
Since you're using SQL Server 2008, you have the geography data type available, which is designed for exactly this kind of data:
DECLARE #source geography = 'POINT(0 51.5)'
DECLARE #target geography = 'POINT(-3 56)'
SELECT #source.STDistance(#target)
Gives
----------------------
538404.100197555
(1 row(s) affected)
Telling us it is about 538 km from (near) London to (near) Edinburgh.
Naturally there will be an amount of learning to do first, but once you know it it's far far easier than implementing your own Haversine calculation; plus you get a LOT of functionality.
If you want to retain your existing data structure, you can still use STDistance, by constructing suitable geography instances using the Point method:
DECLARE #orig_lat DECIMAL(12, 9)
DECLARE #orig_lng DECIMAL(12, 9)
SET #orig_lat=53.381538 set #orig_lng=-1.463526
DECLARE #orig geography = geography::Point(#orig_lat, #orig_lng, 4326);
SELECT *,
#orig.STDistance(geography::Point(dest.Latitude, dest.Longitude, 4326))
AS distance
--INTO #includeDistances
FROM #orig dest
The below function gives distance between two geocoordinates in miles
create function [dbo].[fnCalcDistanceMiles] (#Lat1 decimal(8,4), #Long1 decimal(8,4), #Lat2 decimal(8,4), #Long2 decimal(8,4))
returns decimal (8,4) as
begin
declare #d decimal(28,10)
-- Convert to radians
set #Lat1 = #Lat1 / 57.2958
set #Long1 = #Long1 / 57.2958
set #Lat2 = #Lat2 / 57.2958
set #Long2 = #Long2 / 57.2958
-- Calc distance
set #d = (Sin(#Lat1) * Sin(#Lat2)) + (Cos(#Lat1) * Cos(#Lat2) * Cos(#Long2 - #Long1))
-- Convert to miles
if #d <> 0
begin
set #d = 3958.75 * Atan(Sqrt(1 - power(#d, 2)) / #d);
end
return #d
end
The below function gives distance between two geocoordinates in kilometres
CREATE FUNCTION dbo.fnCalcDistanceKM(#lat1 FLOAT, #lat2 FLOAT, #lon1 FLOAT, #lon2 FLOAT)
RETURNS FLOAT
AS
BEGIN
RETURN ACOS(SIN(PI()*#lat1/180.0)*SIN(PI()*#lat2/180.0)+COS(PI()*#lat1/180.0)*COS(PI()*#lat2/180.0)*COS(PI()*#lon2/180.0-PI()*#lon1/180.0))*6371
END
The below function gives distance between two geocoordinates in kilometres
using Geography data type which was introduced in sql server 2008
DECLARE #g geography;
DECLARE #h geography;
SET #g = geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656)', 4326);
SET #h = geography::STGeomFromText('POINT(-122.34900 47.65100)', 4326);
SELECT #g.STDistance(#h);
Usage:
select [dbo].[fnCalcDistanceKM](13.077085,80.262675,13.065701,80.258916)
Reference: Ref1,Ref2
It looks like Microsoft invaded brains of all other respondents and made them write as complicated solutions as possible.
Here is the simplest way without any additional functions/declare statements:
SELECT geography::Point(LATITUDE_1, LONGITUDE_1, 4326).STDistance(geography::Point(LATITUDE_2, LONGITUDE_2, 4326))
Simply substitute your data instead of LATITUDE_1, LONGITUDE_1, LATITUDE_2, LONGITUDE_2 e.g.:
SELECT geography::Point(53.429108, -2.500953, 4326).STDistance(geography::Point(c.Latitude, c.Longitude, 4326))
from coordinates c
Create Function [dbo].[DistanceKM]
(
#Lat1 Float(18),
#Lat2 Float(18),
#Long1 Float(18),
#Long2 Float(18)
)
Returns Float(18)
AS
Begin
Declare #R Float(8);
Declare #dLat Float(18);
Declare #dLon Float(18);
Declare #a Float(18);
Declare #c Float(18);
Declare #d Float(18);
Set #R = 6367.45
--Miles 3956.55
--Kilometers 6367.45
--Feet 20890584
--Meters 6367450
Set #dLat = Radians(#lat2 - #lat1);
Set #dLon = Radians(#long2 - #long1);
Set #a = Sin(#dLat / 2)
* Sin(#dLat / 2)
+ Cos(Radians(#lat1))
* Cos(Radians(#lat2))
* Sin(#dLon / 2)
* Sin(#dLon / 2);
Set #c = 2 * Asin(Min(Sqrt(#a)));
Set #d = #R * #c;
Return #d;
End
GO
Usage:
select dbo.DistanceKM(37.848832506474, 37.848732506474, 27.83935546875, 27.83905546875)
Outputs:
0,02849639
You can change #R parameter with commented floats.
As you're using SQL 2008 or later, I'd recommend checking out the GEOGRAPHY data type. SQL has built in support for geospatial queries.
e.g. you'd have a column in your table of type GEOGRAPHY which would be populated with a geospatial representation of the coordinates (check out the MSDN reference linked above for examples). This datatype then exposes methods allowing you to perform a whole host of geospatial queries (e.g. finding the distance between 2 points)
In addition to the previous answers, here is a way to calculate the distance inside a SELECT:
CREATE FUNCTION Get_Distance
(
#La1 float , #Lo1 float , #La2 float, #Lo2 float
)
RETURNS TABLE
AS
RETURN
-- Distance in Meters
SELECT GEOGRAPHY::Point(#La1, #Lo1, 4326).STDistance(GEOGRAPHY::Point(#La2, #Lo2, 4326))
AS Distance
GO
Usage:
select Distance
from Place P1,
Place P2,
outer apply dbo.Get_Distance(P1.latitude, P1.longitude, P2.latitude, P2.longitude)
Scalar functions also work but they are very inefficient when computing large amount of data.
I hope this might help someone.