Postgres에서 지리적 거리별로 장소를 정렬하는 방법

7251 단어 databasemathpostgres
마지막 프로젝트에서 사용자에게 지리적으로 가까운 장소 목록을 제시하고 싶었습니다. 분명히 나는 ​​목록이 현재 위치로부터의 거리에 따라 정렬되기를 원했고, 사소해 보일 수 있지만 장소 좌표만 저장하므로 각 사용자의 좌표에 대한 거리를 계산해야 하기 때문에 복잡한 쿼리입니다. 이것은 우리가 가지고 있는 테이블입니다:

CREATE TABLE places (
    name TEXT PRIMARY KEY,
    latitude FLOAT8,
    longitude FLOAT8
);

이 기사에서는 이 거리를 계산하는 두 가지 방법을 다룰 것입니다. 첫 번째는 피타고라스의 정리를 이용한 유클리드 거리에 의한 것으로 지표면을 기준으로 짧은 거리에서만 정확하고, 다른 하나는 하버사인 공식을 이용한 구면 거리에 의한 것으로 지구의 반지름을 이용하여 더 정확하다. 지구의 곡률을 고려합니다.

유클리드 거리



우리가 말했듯이 평면에서 두 점 사이의 거리를 계산하는 공식은 피타고라스 정리를 기반으로 하며 운 좋게도 사용하기가 매우 쉽습니다. 교체만 하면 됩니다

(x1,y1)(x_1,y_1)(x1 ,y1 )

사용자의 좌표와

(x2,y2)(x_2,y_2)(x2 ,y2 )

각 장소의 좌표와 함께.

d=(x1−x2)2+(y1−y2)2
d =\sqrt {\left( {x_1 - x_2 }\right)^2 +\left( {y_1 - y_2 }\right)^2 }
d=(x1 −x2 )2+(y1 −y2 )2


이제 그것을 사용하기 위해 그것을 구현하는 PostgreSQL 함수를 만들었습니다. 이 함수는 사용자의 위도와 경도라는 두 개의 매개변수를 사용하고 위 수식의 결과인 두 지점 사이의 거리를 지정하는 새 열이 추가된 테이블을 반환합니다. 거리는 킬로미터나 마일이 아니라 도 단위이므로 다른 단위의 경우 다음 공식을 사용해야 합니다.

CREATE OR REPLACE FUNCTION order_by_distance(lat float8, long float8)
RETURNS TABLE (name text, latitude float8, longitude float8, distance float8)
AS $$
SELECT name, latitude, longitude, sqrt((latitude - lat)^2 + (longitude - long)^2) AS distance
FROM places
ORDER BY distance ASC;
$$
LANGUAGE SQL;

구면 거리



구면 거리(대원 거리 또는 직교 거리라고도 함)를 계산하는 것은 조금 더 복잡하며 지구 반지름과 함께 Haversine 공식을 사용해야 합니다.

d=6371⋅arccos⁡cos⁡(x1)⋅cos⁡(x2)⋅cos⁡(y1−y2)+sin⁡(x1)⋅sin⁡(x2)
d =\frac{6371\cdot\arccos}{\cos(x_1)\cdot\cos(x_2)\cdot\cos(y_1 - y_2) +\sin(x_1)\cdot\sin(x_2)}
d=cos(x1 )⋅cos(x2 )⋅cos(y1 −y2 )+sin(x1 )⋅sin(x2 )6371⋅arccos


PostgreSQL 함수에서 이를 구현하기 위해 우리는 사용 중인 수식만 대체하여 위와 동일한 접근 방식을 취합니다.

CREATE OR REPLACE FUNCTION order_by_distance(lat float8, long float8)
RETURNS TABLE (name text, latitude float8, longitude float8, distance float8)
AS $$
SELECT name, latitude, longitude, 6371 * acos(cos(radians(lat)) * cos(radians(latitude)) * cos(radians(longitude) - radians(long)) + sin(radians(lat)) * sin(radians(latitude))) AS distance
FROM places
ORDER BY distance ASC;
$$
LANGUAGE SQL;


기능 사용



이제 우리가 해야 할 일은 다음 쿼리로 함수를 호출하는 것입니다(물론 사용자의 좌표를 인수로 제공). 안전한 여행!

SELECT * FROM order_by_distance(40.758896, -73.985130)


추가 정보


  • 조반니 데얀, The Magic of Haversine Distance
  • 이동 가능한 유형 스크립트, Calculate distance, bearing and more between Latitude/Longitude points
  • 좋은 웹페이지 즐겨찾기