본문 바로가기
코딩일기/날씨앱 만들기 프로젝트

날씨 앱 만들기 : 위도와 경도 기상청 격자 정보 X,Y 로 변환 (Java)

by 욱파이어니어 2021. 9. 9.
728x90
반응형

지난번에 GPS로 받아온 위도와 경도로 기상청 API에 날씨정보를 요청하려면 기상청에서 사용하는 격자정보 X,Y로 변환을 해줘야 한다.

 

https://fronteer.kr/service/kmaxy

 

기상청 격자정보 - 위경도 변환 : Grid XY - Lat, Lon

데이터형식 : 위도, 경도 37.579871128849334, 126.98935225645432 35.101148844565955, 129.02478725562108 33.500946412305076, 126.54663058817043

fronteer.kr

 

위 사이트를 들어가면 위도와 경도를 격자정보로 변환할수 있고 그 격자 정보에 맞는 데이터는 

기상청에서 제공해주는 엑셀파일을 보면 해당 격자정보에 맞는 위치정보들을 알수가 있다.

 

 

그리고 제공해주는 워드파일을 보면 위도경도를 격자정보로 변환해주는 샘플코드가 주어진다.

 

샘플코드가 C언어로 주어졌지만 각자 원하는 언어로 다시 만들수가 있다.

구글링을 해보니 다른 사람들이 친절하게도 자바로 만들어 주신분이 있어서 난 그 소스를 토대로 만들었고 나의 방식대로 약간 변경해주었다.

 

위도와 경도를 격자정보로 바꾸는 소스는 아래와 같다.

 

package wook.co.weather.models.dto;

public class GpsTransfer {

    private double lat; //gps로 반환받은 위도
    private double lon; //gps로 반환받은 경도

    private double xLat; //x좌표로 변환된 위도
    private double yLon; //y좌표로 변환된 경도

    public GpsTransfer() {}

    public GpsTransfer(double lat, double lon) {
        this.lat = lat;
        this.lon = lon;
    }



    public double getLat() {
        return lat;
    }

    public double getLon() {
        return lon;
    }

    public double getxLat() {
        return xLat;
    }

    public double getyLon() {
        return yLon;
    }

    public void setLat(double lat) {
        this.lat = lat;
    }

    public void setLon(double lon) {
        this.lon = lon;
    }

    public void setxLat(double xLat) {
        this.xLat = xLat;
    }

    public void setyLon(double yLon) {
        this.yLon = yLon;
    }

    //x,y좌표로 변환해주는것
    public void transfer(GpsTransfer gpt, int mode){

        double RE = 6371.00877; // 지구 반경(km)
        double GRID = 5.0; // 격자 간격(km)
        double SLAT1 = 30.0; // 투영 위도1(degree)
        double SLAT2 = 60.0; // 투영 위도2(degree)
        double OLON = 126.0; // 기준점 경도(degree)
        double OLAT = 38.0; // 기준점 위도(degree)
        double XO = 43; // 기준점 X좌표(GRID)
        double YO = 136; // 기1준점 Y좌표(GRID)

        //
        // LCC DFS 좌표변환 ( code : "TO_GRID"(위경도->좌표, lat_X:위도,  lng_Y:경도), "TO_GPS"(좌표->위경도,  lat_X:x, lng_Y:y) )
        //


        double DEGRAD = Math.PI / 180.0;
        double RADDEG = 180.0 / Math.PI;

        double re = RE / GRID;
        double slat1 = SLAT1 * DEGRAD;
        double slat2 = SLAT2 * DEGRAD;
        double olon = OLON * DEGRAD;
        double olat = OLAT * DEGRAD;

        double sn = Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5);
        sn = Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(sn);
        double sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
        sf = Math.pow(sf, sn) * Math.cos(slat1) / sn;
        double ro = Math.tan(Math.PI * 0.25 + olat * 0.5);
        ro = re * sf / Math.pow(ro, sn);

        if (mode == 0) {
//            rs.lat = lat_X; //gps 좌표 위도
//            rs.lng = lng_Y; //gps 좌표 경도
            double ra = Math.tan(Math.PI * 0.25 + (gpt.getLat()) * DEGRAD * 0.5);
            ra = re * sf / Math.pow(ra, sn);
            double theta = gpt.getLon() * DEGRAD - olon;
            if (theta > Math.PI) theta -= 2.0 * Math.PI;
            if (theta < -Math.PI) theta += 2.0 * Math.PI;
            theta *= sn;
            double x = Math.floor(ra * Math.sin(theta) + XO + 0.5);
            double y = Math.floor(ro - ra * Math.cos(theta) + YO + 0.5);
            gpt.setxLat(x);
            gpt.setyLon(y);
//            rs.x = Math.floor(ra * Math.sin(theta) + XO + 0.5);
//            rs.y = Math.floor(ro - ra * Math.cos(theta) + YO + 0.5);
        }
        else {
//            rs.x = lat_X; //기존의 x좌표
//            rs.y = lng_Y; //기존의 경도
            double xlat = gpt.getxLat();
            double ylon = gpt.getyLon();
            double xn = xlat - XO;
            double yn = ro - ylon + YO;
            double ra = Math.sqrt(xn * xn + yn * yn);
            if (sn < 0.0) {
                ra = -ra;
            }
            double alat = Math.pow((re * sf / ra), (1.0 / sn));
            alat = 2.0 * Math.atan(alat) - Math.PI * 0.5;

            double theta = 0.0;
            if (Math.abs(xn) <= 0.0) {
                theta = 0.0;
            }
            else {
                if (Math.abs(yn) <= 0.0) {
                    theta = Math.PI * 0.5;
                    if (xn < 0.0) {
                        theta = -theta;
                    }
                }
                else theta = Math.atan2(xn, yn);
            }
            double alon = theta / sn + olon;
//            rs.lat = alat * RADDEG; //gps 좌표 위도
//            rs.lng = alon * RADDEG; //gps 좌표 경도
            gpt.setLat(alat * RADDEG);
            gpt.setLon(alon * RADDEG);
        }
    }

    @Override
    public String toString() {
        return "GpsTransfer{" +
                "lat=" + lat +
                ", lon=" + lon +
                ", xLat=" + xLat +
                ", yLon=" + yLon +
                '}';
    }
}

 

 

위 소스를 토대로 이제 나는 격자정보로 변환을 했으니 변환한 정보를 가지고 날씨정보를 요청하면 된다.

하는 방법은 내가 이전에 만든 방법과 비슷하니 내 이전글들을 확인해보면 될것 같다.

 

이제 위 소스를 토대로 실제로 내 핸드폰에서 작동을 해봤는데 

아무리 해도 내 핸드폰에서는 onLocationChanged()가 콜백이 되지 않았다.

그래서 구글링을 해보니 실제 기기에서는 GPS_PROVIDER 대신 NETWORK_PROVIDER를 사용해주니 해결이 됐다고 해서

나도 그렇게 해보니 다행히 해결이 됐다.

다만 문제가 그렇게 되면 에뮬레이터에서는 돌아가지 않았기 때문에 나는 소스에서 

 

//lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0,this); //위치정보를 update하는 함수 이건 실제 기기용
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0,this); //위치정보를 update 한다. 이건 에뮬레이터용

 

이렇게 두개를 사용하고 있다.

반응형