Estoy tratando de calcular la distancia entre dos posiciones en un mapa. Tengo almacenados en mis datos: Longitud, Latitud, X POS, Y POS.
Anteriormente he estado usando el siguiente fragmento.
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
Sin embargo, no confío en los datos que surgen de esto, parece dar resultados ligeramente inexactos.
Algunos datos de muestra en caso de que los necesite
Latitude Longitude Distance
53.429108 -2.500953 85.2981833133896
¿Alguien podría ayudarme con mi código? No me importa si quieres arreglar lo que ya tengo si tienes una nueva forma de lograrlo que sería genial.
Indique en qué unidad de medida se encuentran sus resultados.
sql
sql-server
tsql
math
sql-server-2008-r2
Waller
fuente
fuente
Respuestas:
Dado que está utilizando SQL Server 2008, tiene el
geography
tipo de datos disponible, que está diseñado exactamente para este tipo de datos:DECLARE @source geography = 'POINT(0 51.5)' DECLARE @target geography = 'POINT(-3 56)' SELECT @source.STDistance(@target)
Da
---------------------- 538404.100197555 (1 row(s) affected)
Nos dice que hay unos 538 km desde (cerca) de Londres a (cerca) de Edimburgo.
Naturalmente, primero habrá mucho que aprender, pero una vez que lo sepa, será mucho más fácil que implementar su propio cálculo de Haversine; además, obtienes MUCHA funcionalidad.
Si desea conservar su estructura de datos existente, aún puede usar
STDistance
, construyendogeography
instancias adecuadas usando elPoint
método: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
fuente
La siguiente función proporciona la distancia entre dos geocoordenadas en millas
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
La siguiente función proporciona la distancia entre dos geocoordenadas en kilómetros
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
La siguiente función proporciona la distancia entre dos geocoordenadas en kilómetros utilizando el tipo de datos Geografía que se introdujo en 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);
Uso:
select [dbo].[fnCalcDistanceKM](13.077085,80.262675,13.065701,80.258916)
Referencia: Ref1 , Ref2
fuente
Parece que Microsoft invadió los cerebros de todos los demás encuestados y les hizo escribir soluciones lo más complicadas posible. Esta es la forma más sencilla sin funciones adicionales / declaraciones de declaración:
SELECT geography::Point(LATITUDE_1, LONGITUDE_1, 4326).STDistance(geography::Point(LATITUDE_2, LONGITUDE_2, 4326))
Basta con sustituir sus datos en lugar de
LATITUDE_1
,LONGITUDE_1
,LATITUDE_2
,LONGITUDE_2
por ejemplo:SELECT geography::Point(53.429108, -2.500953, 4326).STDistance(geography::Point(c.Latitude, c.Longitude, 4326)) from coordinates c
fuente
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
Uso:
seleccione dbo.DistanceKM (37.848832506474, 37.848732506474, 27.83935546875, 27.83905546875)
Salidas:
0,02849639
Puede cambiar el parámetro @R con flotantes comentados.
fuente
Como está utilizando SQL 2008 o posterior, le recomiendo que consulte el tipo de datos GEOGRAPHY . SQL tiene soporte integrado para consultas geoespaciales.
por ejemplo, tendría una columna en su tabla de tipo GEOGRAPHY que se llenaría con una representación geoespacial de las coordenadas (consulte la referencia de MSDN vinculada anteriormente para ver ejemplos). Este tipo de datos luego expone métodos que le permiten realizar una gran cantidad de consultas geoespaciales (por ejemplo, encontrar la distancia entre 2 puntos)
fuente
Además de las respuestas anteriores, aquí hay una forma de calcular la distancia dentro de un 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
Uso:
select Distance from Place P1, Place P2, outer apply dbo.Get_Distance(P1.latitude, P1.longitude, P2.latitude, P2.longitude)
Las funciones escalares también funcionan, pero son muy ineficientes cuando se calculan grandes cantidades de datos.
Espero que esto pueda ayudar a alguien.
fuente