본문 바로가기
👨‍💻 프로그래밍/책잇아웃(포트폴리오)

특정 거리 범위 내 위도/경도 계산하기 (의외로 어려움)

by 개발자 진개미 2023. 5. 27.
반응형

해야 하는 것

현재 위치를 기준으로 특정 거리 내의 위도/경도 범위를 계산

 

JavaMethod Signature로 따지면 아래와 같습니다.

public double[] getLatitudeRange(double latitude, int radiusInMeters) {}
public double[] getLongitudeRange(double latitude, double longitude, int radiusInMeters) {}

우선 위도 / 경도에 대해 알아보자

우선, 위도 / 경도는 단순히 지구의 x / y 좌표가 아닙니다. 위도 / 경도는 각도입니다. 지구를 선으로 나누고, 그 선을 기준으로 몇 도 떨어져 있는지를 측정합니다.

 

 

위도는 적도에서 몇 도 떨어져 있느냐를 측정합니다. 

 

적도는 지구의 자전축과 수직인 선입니다. 쉽게 말해서 지구의 가로 중심에 있는 선입니다. 지리 시간에 적도 부근은 덥다는 말을 들어보셨을 겁니다. 위도는 그 적도를 기준으로 세로로 얼마나 떨어져 있는지를 측정합니다. 정확히 적도면 0도이고, 북극과 남극이 모두 90도입니다. (0도 ~ 90도 내)

 

경도본초 자오선에서 몇 도 떨어져 있느냐를 측정합니다.

본초 자오선은 영국의 그리니치 천문대를 지나는 세로 선입니다. 경도는 이 선을 0도로 시작해서 180도까지 얼마나 떨어져 있는지를 측정합니다.


지구를 어떻게 보느냐에 따라 계산법이 달라진다

 

지구는 둥글기 때문에, 어디에 있느냐에 따라 1도의 거리가 달라집니다. 위로 갈수록 경사가 가팔라지기 때문에 1도의 거리가 더 짧습니다.

게다가 사실 지구는 완전 둥글지고 않고 찌그러져 있습니다. 즉, 지구의 모델을 얼마나 정확히 가정하느냐에 따라 위도 / 경도의 계산법이 달라집니다. 보통 정확한 모델을 사용할수록 계산법이 복잡해집니다.


지구가 평평하다고 가정하면

지구를 만약 평평하게 본다면 어디에 있든 1도의 거리가 똑같으니 계산을 간단해집니다. 단순히 위도 / 경도의 1도에 해당하는 거리 1개를 계산하고 곱하면 끝입니다.

 


지구가 완전한 구라고 가정하면 (Haversine Formula(하버사인) 공식)

완전한 구라고 가정하면 원의 각도에 따라 1도의 거리가 짧아지는 걸 가정하면 됩니다. 공식으로 따지면 아래와 같습니다.

거리를 따지는건 결국 지구 표면에서 따지는 것이기 때문에 원주가 나타나는 거고, 처음에는 온전했다가 위로 올라갈수록 원주에 따라 감소하니 자연스럽게 코사인이 나오는 겁니다.

 


지구가 어느정도 찌르러 진 구라고 가정하면 (Vincenty's(벤센트리) 공식)

이미 충분히 복잡하지만, 거리를 더 정확히 계산하고 싶다면 지구를 어느 정도 찌르러 진 구라고 더 정확하게 가정하면 됩니다. 


최종적인 코드 (Java)

저는 현재 위치에서 2km 내에 있는 도서관을 모두 보여주기 위해 위도/경도 계산이 필요했던 건데, 정확성이 그렇게까지 Business Logic에 Critical 하다고 생각하지는 않아서 지구를 완전한 구라고 가정한 하버사인 공식을 사용했습니다.

 

private static final double LATITUDE_DEGREE_PER_METER = 1.0 / (2 * Math.PI * EARTH_RADIUS_IN_M / 360);

public double[] getLatitudeRange(double latitude, int radiusInMeters) {
    double degreeRange = radiusInMeters * LATITUDE_DEGREE_PER_METER;

    double minLatitude = latitude - degreeRange;
    double maxLatitude = latitude + degreeRange;

    return new double[]{minLatitude, maxLatitude};
}

 

public double[] getLongitudeRange(double latitude, double longitude, int radiusInMeters) {
    double longitudeDegreePerMeter = 360 / (2 * Math.PI * EARTH_RADIUS_METERS * Math.cos(Math.toRadians(latitude)));
    double degreeRange =  longitudeDegreePerMeter * radiusInMeters;

    double minLongitude = longitude - degreeRange;
    double maxLongitude = longitude + degreeRange;

    return new double[]{minLongitude, maxLongitude};
}

반응형