Calcular la distancia entre dos puntos (Latitud, Longitud)


Estoy tratando de calcular la distancia entre dos posiciones en un mapa. He almacenado en mis datos: Longitud, Latitud, X POS, Y POS.

He estado usando previamente el siguiente fragmento de código.

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 salen de esto, parece estar dando resultados ligeramente inexactos.

Algunos datos de muestra en caso de que los necesite

Latitude        Longitude     Distance 
53.429108       -2.500953     85.2981833133896

¿Podría alguien ayudarme con mi código, no me importa si quieres arreglar lo que ya tengo si tienes un nuevo manera de lograr esto que sería genial.

Indique en qué unidad de medida se encuentran sus resultados.

Author: shA.t, 2012-10-23

6 answers

Dado que está utilizando SQL Server 2008, tiene la 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 está a unos 538 km de (cerca) Londres a (cerca) Edimburgo.

Naturalmente, habrá una cantidad de aprendizaje que hacer primero, pero una vez que lo sepas es mucho más fácil que implementar tu propio cálculo de Haversine; además, obtienes una GRAN cantidad de funcionalidad.


Si desea conservar su estructura de datos existente, todavía puede usar STDistance, construyendo instancias geography adecuadas utilizando el Point 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
 98
Author: AakashM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-11 14:54:20

La siguiente función da distancia entre dos geocoordinados 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 da distancia entre dos geocoordinados 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 da distancia entre dos geocoordinados en kilómetros usando Geografía tipo de datos 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

 31
Author: Durai Amuthan.H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:31

Como está utilizando SQL 2008 o posterior, le recomiendo que revise el tipo de datos GEOGRAPHY. SQL ha incorporado soporte para consultas geoespaciales.

Por ejemplo, tendría una columna en su tabla de tipo GEOGRAPHY que se rellenaría con una representación geoespacial de las coordenadas (consulte la referencia MSDN enlazada 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)

 3
Author: AdaTheDev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-23 09:00:45
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.Distancia(37.848832506474, 37.848732506474, 27.83935546875, 27.83905546875)

Salidas:

0,02849639

Puede cambiar el parámetro @R con flotadores comentados.

 3
Author: Fatih K.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-09 14:34:58

Además de las respuestas anteriores, aquí hay una manera 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 computan grandes cantidades de datos.

Espero que esto pueda ayudar a alguien.

 1
Author: Thurfir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-06 17:41:44

Parece que Microsoft invadió los cerebros de todos los demás encuestados y les hizo escribir soluciones tan complicadas como sea posible. Aquí está la forma más simple sin ninguna función adicional/declarar declaraciones:

SELECT geography::Point(LATITUDE_1, LONGITUDE_1, 4326).STDistance(geography::Point(LATITUDE_2, LONGITUDE_2, 4326))

Simplemente sustituya 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
 1
Author: Stalinko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-10 13:25:04