2015-05-21 4 views
4

Итак, у меня есть база данных с несколькими строками координат GPS. Я знаю, как вычислить расстояние от заданного lat/lng от любого из них в базе данных, но я хочу в основном посмотреть на координаты набора строк и получить две строки, которые находятся на расстоянии друг от друга. Мне бы это понравилось, если бы я мог сделать это в SQL, но если мне нужно сделать это в коде приложения, который будет работать. Вот то, что я делаю, чтобы вычислить расстояние между двумя точками:Получение наибольшего расстояния от группы координат GPS

ROUND((3960 * acos(cos(radians(:lat)) * 
cos(radians(p.latitude)) * cos(radians( p.longitude ) - radians(:lng)) + 
sin(radians(:lat)) * sin(radians( p.latitude )))),1) AS distance 

То, что мы пытаемся сделать, посмотреть на данные GPS для конкретного пользователя и убедитесь, что они не двигаются дико по всей стране , Все координаты для пользователя должны находиться в пределах нескольких миль друг от друга. Флаг, который содержит вредоносную активность в нашей системе, - это координаты по всей стране. Поэтому я хотел бы иметь возможность быстро просматривать данные для пользователя spcicic и знать, какое максимальное расстояние они были.

Я думал о том, чтобы просто запустить Max/Min на lat и lng отдельно и установить внутренний порог для приемлемого. И, может быть, это проще, но если то, что я попросил в первой части, возможно, было бы лучше.

ответ

1

Если у вас есть SQL Server 2008 или более поздней версии, то вы можете использовать ГЕОГРАФИЮ для вычисления расстояния, например .:

DECLARE @lat1 DECIMAL(19,6) = 44.968046; 
DECLARE @lon1 DECIMAL(19,6) = -94.420307; 
DECLARE @lat2 DECIMAL(19,6) = 44.33328; 
DECLARE @lon2 DECIMAL(19,6) = -89.132008; 
SELECT GEOGRAPHY::Point(@lat1, @lon1, 4326).STDistance(GEOGRAPHY::Point(@lat2, @lon2, 4326)); 

Это делает проблему довольно тривиально?

Для набора lats/longs для пользователя вам нужно будет рассчитать расстояние между каждым набором, а затем вернуть максимальное расстояние. Сведя все это вместе, вы могли бы, вероятно, сделать что-то вроде этого:

DECLARE @UserGPS TABLE (
    UserId INT, --the user 
    GPSId INT, --the incrementing unique id associated with this GPS reading (could link to a table with more details, e.g. time, date) 
    Lat DECIMAL(19,6), --lattitude 
    Lon DECIMAL(19,6)); --longitude 
INSERT INTO @UserGPS SELECT 1, 1, 44.968046, -94.420307; --User #1 goes on a very long journey 
INSERT INTO @UserGPS SELECT 1, 2, 44.33328, -89.132008; 
INSERT INTO @UserGPS SELECT 1, 3, 34.12345, -92.21369; 
INSERT INTO @UserGPS SELECT 1, 4, 44.978046, -94.430307; 
INSERT INTO @UserGPS SELECT 2, 1, 44.968046, -94.420307; --User #2 doesn't get far 
INSERT INTO @UserGPS SELECT 2, 2, 44.978046, -94.430307; 

--Make a working table to store the distances between each set of co-ordinates 
--This isn't strictly necessary; we could change this into a common-table expression 
DECLARE @WorkTable TABLE (
    UserId INT, --the user 
    GPSIdFrom INT, --the id of the first set of co-ordinates 
    GPSIdTo INT, --the id of the second set of co-ordinates being compared 
    Distance NUMERIC(19,6)); --the distance 

--Get the distance between each and every combination of co-ordinates for each user 
INSERT INTO 
    @WorkTable 
SELECT 
    c1.UserId, 
    c1.GPSId, 
    c2.GPSId, 
    GEOGRAPHY::Point(c1.Lat, c1.Lon, 4326).STDistance(GEOGRAPHY::Point(c2.Lat, c2.Lon, 4326)) 
FROM 
    @UserGPS c1 
    INNER JOIN @UserGPS c2 ON c2.UserId = c1.UserId AND c2.GPSId > c1.GPSId; 
--Note this is a self-join, but single-tailed. So we compare each set of co-ordinates to each other set of co-ordinates for a user 
--This is handled by the "c2.GPSID > c1.GPSId" in the JOIN clause 
--As an example, say we have three sets of co-ordinates for a user 
--We would compare set #1 to set #2 
--We would compare set #1 to set #3 
--We would compare set #2 to set #3 
--We wouldn't compare set #3 to anything (as we already did this) 

--Determine the maximum distance between all the GPS co-ordinates per user 
WITH MaxDistance AS (
    SELECT 
     UserId, 
     MAX(Distance) AS Distance 
    FROM 
     @WorkTable 
    GROUP BY 
     UserId) 
--Report the results 
SELECT 
    w.UserId, 
    g1.GPSId, 
    g1.Lat, 
    g1.Lon, 
    g2.GPSId, 
    g2.Lat, 
    g2.Lon, 
    md.Distance AS MaxDistance 
FROM 
    MaxDistance md 
    INNER JOIN @WorkTable w ON w.UserId = md.UserId AND w.Distance = md.Distance 
    INNER JOIN @UserGPS g1 ON g1.UserId = md.UserId AND g1.GPSId = w.GPSIdFrom 
    INNER JOIN @UserGPS g2 ON g2.UserId = md.UserId AND g2.GPSId = w.GPSIdTo; 

Результаты:

UserId GPSId Lat Lon GPSId Lat Lon MaxDistance 
1 3 34.123450 -92.213690 4 44.978046 -94.430307 1219979.460185 
2 1 44.968046 -94.420307 2 44.978046 -94.430307 1362.820895 

Теперь я сделал много предположений о том, что данные, которые вы держите в руках, поскольку не было никакой информации о подробно об этом в вашем вопросе. Вам, вероятно, придется в некоторой степени адаптировать это?

+0

Итак, это похоже на то, что он сравнивает только две строки. Что делать, если у меня было 50 строк, каждый с разными значениями lat/lng. Есть ли способ сравнить их друг с другом и вернуть самое большое расстояние между тем, что два из них находятся дальше всего. – Leeish

+0

Нет, это сравнивает все строки для пользователя, он использует односторонний метод, поэтому, если бы было три строки, он сравнивал строки # 1 с строкой # 2 и строкой # 3, затем строки # 2 в строку # 3 и затем верните матч с наибольшим расстоянием. В моем примере первый пользователь имел четыре набора координат и вычислял наибольшее расстояние как между значением № 3 и значением # 4. –

+0

Хотел бы я понять, как это делается. – Leeish

Смежные вопросы