본문 바로가기

학원 일기/학원-Project

위도,경도 데이터를 통해 (n)km이내의 값 구하기

위도,경도를 가지고있는 테이블이 있다

입력한 위도,경도의 반경 xxkm안에 있는 값들을 조회하는게 목표이다

 

구현할 로직

* 입력하는 좌표와 조회되는 테이블의 좌표값의  거리를구한다

* 거리의 가까운순서로 테이블을 정렬하고 입력한값의 반경이되는 조건거리를 넘어가는경우는 조회하지않는다.

 

우선 도를 반지름으로 계산하는 함수가 필요하다

다른 DB에서는 내장SQL문이 있었지만 ORACLE에서는 존재하지 않기떄문에 만들어야 했다

 

stackoverflow의 소스를 통해서 oracle기반의 함수를 이해하면서 구현해보도록 하자

 

1) 위도,경도를 반지름으로 바꾼다. => 해당함수를 구현한다

 

도를 라디안으로 바꿀수있는 RADIANS함수를 생성해야한다 (RADIAN == 도를 값으로 나타내는 단위)

왜냐하면 프로그래밍에서 각도를넣어 값을 구할수 없기때문에 상수로바꿔줄 필요가있는것같다

 

CREATE OR REPLACE FUNCTION RADIANS(nDegrees IN NUMBER) RETURN NUMBER DETERMINISTIC IS

BEGIN

-- radians = degrees / (180 / pi)

-- RETURN nDegrees / (180 / ACOS(-1)); -- but 180/pi is a constant, so...

RETURN nDegrees / 57.29577951308232087679815481410517033235;

END RADIANS;

 

<< 함수이름은 RADIANS이며 nDegrees라는 숫자타입의변수를 받는다

반환값은 NUMBER(숫자)이며 그값은 IS 이하로 결정된다

BEGIN ~~ END RADIANS

 

2) RADIANS를 활용한 공식 

 

 

ACOS(

         COS(RADIANS(90-지점1의위도))

         *COS(RADIANS(90-지점2의위도))

         +

         SIN(RADIANS(90-지점1의위도))

         *SIN(RADIANS(90-지점2의위도))

         *COS(RADIANS(지점1의경도-지점2의경도))

         )

         *6371

 

위에 공식을 이해하기위한 개념 

 

1) 빗변의 길이가 1인 직각삼각형은 cos(세타) == sin(90-세타)이다

2) 2파이r은 원의 둘레의 공식에서 빗변의길이 == 2세타 라는것을 알수있다

3) r은 반지름이고 지구의반지름은 6371이다

4) 위도,경도로 가까운거리를구한다는것은 지구의표면상의 거리를구한다는것과 같고 이것은 원의둘레의 중 일부의길이     를 구하는것과 같다

5) 즉 세타*6371이 두개의 위도,경도가 주어졌을때 서로간의 길이를 나타내는것이다

6) ACOS로 세타의값을 상수로 바꿔준다

 

느낀점

 

해당 공식을 구글링통해 처음 접하게되었다

하지만 oracle에 바로 적용가능한 sql문이 없었고 

결국 해당 sql문이 왜 이렇게 짜여진것인지 이해할 필요가 있었다.

 

 

그과정에서 삼각함수,arcCos,호의길이를 구하는 공식을 다시 알아보았고

살면서 수학공식을 처음 실생활에 접한 계기가 되었다.

수학에대한 필요성을 처음 느끼게된 계기였다.

 

 

 

 

 

 

 

 

 

 

 

 

 

================================================

 

 

SELECT * FROM (SELECT dest.ZC_ZIP,

                                dest.ZC_LOCATION_NAME,

                                ACOS(SIN(RADIANS(src.ZC_LAT)) * SIN(RADIANS(dest.ZC_LAT))

                                + COS(RADIANS(src.ZC_LAT)) * COS(RADIANS(dest.ZC_LAT))

                                * COS(RADIANS(src.ZC_LON) - RADIANS(dest.ZC_LON)) )

                                * 6371 AS DISTANCE

             FROM USER.ZC_COORDINATES dest

             CROSS JOIN USER.ZC_COORDINATES src

             WHERE src.ZC_ID = (SELECT ZC_ID

                                                     FROM USER.ZIP_COORDINATES

                                                     WHERE ZC_ZIP = '64289' GROUP BY ZC_ZIP) AND

                          (dest.ZC_ID <> src.ZC_ID OR

                           dest.ZC_ID = src.ZC_ID))

HAVING DISTANCE <= 25 /* km */

ORDER BY DISTANCE;