Próbuję obliczyć odległość między dwoma pozycjami na mapie. Zapisałem w swoich danych: długość, szerokość geograficzną, X POS, Y POS.
Wcześniej korzystałem z poniższego fragmentu kodu.
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
Nie ufam jednak wynikającym z tego danym, wydaje się, że dają one nieco niedokładne wyniki.
Przykładowe dane na wypadek, gdybyś ich potrzebował
Latitude Longitude Distance
53.429108 -2.500953 85.2981833133896
Czy ktoś mógłby mi pomóc z moim kodem, nie mam nic przeciwko, jeśli chcesz naprawić to, co już mam, jeśli masz nowy sposób osiągnięcia tego, który byłby świetny.
Podaj jednostkę miary, w której znajdują się Twoje wyniki.
sql
sql-server
tsql
math
sql-server-2008-r2
Murarz dołowy
źródło
źródło
Odpowiedzi:
Ponieważ używasz SQL Server 2008, masz
geography
dostępny typ danych, który jest przeznaczony dokładnie dla tego rodzaju danych:DECLARE @source geography = 'POINT(0 51.5)' DECLARE @target geography = 'POINT(-3 56)' SELECT @source.STDistance(@target)
Daje
---------------------- 538404.100197555 (1 row(s) affected)
Mówiąc nam, że jest to około 538 km od (blisko) Londynu do (blisko) Edynburga.
Oczywiście najpierw trzeba się dużo nauczyć, ale kiedy już się zorientujesz, będzie to o wiele łatwiejsze niż wdrożenie własnych obliczeń Haversine; plus masz DUŻO funkcjonalności.
Jeśli chcesz zachować istniejącą strukturę danych, możesz nadal używać
STDistance
, konstruując odpowiedniegeography
instancje za pomocąPoint
metody: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
źródło
Poniższa funkcja podaje odległość między dwoma współrzędnymi geograficznymi w milach
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
Poniższa funkcja podaje odległość między dwoma współrzędnymi geograficznymi w kilometrach
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
Poniższa funkcja podaje odległość między dwoma współrzędnymi geograficznymi w kilometrach przy użyciu typu danych Geography, który został wprowadzony na serwerze sql 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);
Stosowanie:
select [dbo].[fnCalcDistanceKM](13.077085,80.262675,13.065701,80.258916)
Odniesienie: Ref1 , Ref2
źródło
Wygląda na to, że Microsoft zaatakował mózgi wszystkich pozostałych respondentów i zmusił ich do napisania jak najbardziej skomplikowanych rozwiązań. Oto najprostszy sposób bez żadnych dodatkowych funkcji / deklaracji:
SELECT geography::Point(LATITUDE_1, LONGITUDE_1, 4326).STDistance(geography::Point(LATITUDE_2, LONGITUDE_2, 4326))
Wystarczy podstawić dane zamiast
LATITUDE_1
,LONGITUDE_1
,LATITUDE_2
,LONGITUDE_2
np:SELECT geography::Point(53.429108, -2.500953, 4326).STDistance(geography::Point(c.Latitude, c.Longitude, 4326)) from coordinates c
źródło
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
Stosowanie:
wybierz dbo.DistanceKM (37,848832506474, 37,848732506474, 27,83935546875, 27,83905546875)
Wyjścia:
0,02849639
Możesz zmienić parametr @R za pomocą komentarzy zmiennoprzecinkowych.
źródło
Ponieważ używasz SQL 2008 lub nowszego, polecam zapoznać się z GEOGRAFIĄ typu danych . SQL ma wbudowaną obsługę zapytań geoprzestrzennych.
np. miałbyś kolumnę w swojej tabeli typu GEOGRAFIA, w której byłaby umieszczona geoprzestrzenna reprezentacja współrzędnych (przykłady można znaleźć w odnośniku MSDN, do którego link znajduje się powyżej). Ten typ danych udostępnia następnie metody umożliwiające wykonanie wielu zapytań geoprzestrzennych (np. Znajdowanie odległości między 2 punktami)
źródło
Oprócz poprzednich odpowiedzi, oto sposób obliczenia odległości wewnątrz 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
Stosowanie:
select Distance from Place P1, Place P2, outer apply dbo.Get_Distance(P1.latitude, P1.longitude, P2.latitude, P2.longitude)
Funkcje skalarne również działają, ale są bardzo nieefektywne podczas obliczania dużej ilości danych.
Mam nadzieję, że to może komuś pomóc.
źródło