[Python] 기상청 단기예보 API 활용하기

Cherry·2022년 3월 28일
0

🌤공공 오픈 API활용하기

이번에 인턴을 하면서 거의 처음으로 개발 일을 주셨다... 해삐~~!!☺️ 어떤 일을 주셨나면... 건물들의 위치에 해당하는 날씨정보를 주기적으로 불러서 데이터베이스에 저장하는 API를 개발하는 것이었다. 기존에 장고를 사용해보긴했지만 오픈 API를 사용해본적이 없어서 걱정이 앞섰다ㅜㅜㅜ근데 개발해보니까 오픈api가이드나 다른 블로그들 참고하면서 생각보다 어렵지 않게 해낸것 같다. 우선 날씨정보를 제공해주는 API를 찾기 위해서 공공포털정보에 들어가서 검색해보니까 단기예보서비스가 제일 적합해보였다.
🌧기상청 단기예보🌧

1시간 단위로 날씨 정보를 주는 초단기예보를 사용해서 개발해보려한다.
우선 공공 데이터 포털 사이트에서 api를 활용할 수 있도록 미리 제시한 파이썬 샘플 코드는 아래와 같다.

import requests

url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst'
params ={'serviceKey' : '서비스키', 'pageNo' : '1', 'numOfRows' : '1000', 'dataType' : 'XML', 'base_date' : '20210628', 'base_time' : '0600', 'nx' : '55', 'ny' : '127' }

response = requests.get(url, params=params)
print(response.content)

구현해보기

프로그램 코드를 구현하는 부분에 있어서 가장 많이 참고한 블로그는 공공데이터 가져오기이다. 너무 잘 정리해놓은 것 같아서 api다룰일 있으면 꼭 한번 읽어보는 것을 추천한다. 우선 프로젝트 파일 밑에 weather_api.py라는 파일을 만들어서 날씨 관련 데이터들을 가져왔다. 코드가 살짝 길어서 주석을 중간중간 달았다.

초단기 예보의 요청 메세지에는 아래 사진과 같은 항목들이 필요하다.

api/weather_api.py


# 기상청_동네 예보 조회 서비스 api 데이터 url 주소, 초단기이기때문에 getUltraSrtFcst 사용
url = "https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtFcst"

    serviceKey = "서비스키" # 공공데이터 포털에서 생성된 본인의 서비스 키를 복사 / 붙여넣기
    serviceKeyDecoded = unquote(serviceKey, 'UTF-8') # 공데이터 포털에서 제공하는 서비스키는 이미 인코딩된 상태이므로, 디코딩하여 사용해야 함

    now = datetime.now()
    today = datetime.today().strftime("%Y%m%d")
    y = date.today() - timedelta(days=1)
    yesterday = y.strftime("%Y%m%d")
    nx = 60 # 위도와 경도를 x,y좌표로 변경
    ny = 127
    
        if now.minute<45: # base_time와 base_date 구하는 함수
        if now.hour==0:
            base_time = "2330"
            base_date = yesterday
        else:
            pre_hour = now.hour-1
            if pre_hour<10:
                base_time = "0" + str(pre_hour) + "30"
            else:
                base_time = str(pre_hour) + "30"
            base_date = today
    else:
        if now.hour < 10:
            base_time = "0" + str(now.hour) + "30"
        else:
            base_time = str(now.hour) + "30"
        base_date = today

위에 있는 함수는 예보 제공 시각이 매시간 30분마다 생성 되고 api제공 시간이 45분 이후이기 때문에 함수를 저렇게 구현해보았다 원래는 노가다로 짜다가 24번이나 if문을 사용해야 돼서 그냥 for문으로 알고리즘을 만들었다...


    queryParams = '?' + urlencode({ quote_plus('serviceKey') : serviceKeyDecoded, quote_plus('base_date') : base_date,
                                    quote_plus('base_time') : base_time, quote_plus('nx') : nx, quote_plus('ny') : ny,
                                    quote_plus('dataType') : 'json', quote_plus('numOfRows') : '60'}) #페이지로 안나누고 한번에 받아오기 위해 numOfRows=60으로 설정해주었다
                                   

    # 값 요청 (웹 브라우저 서버에서 요청 - url주소와 파라미터)
    res = requests.get(url + queryParams, verify=False) # verify=False이거 안 넣으면 에러남ㅜㅜ
    items = res.json().get('response').get('body').get('items') #데이터들 아이템에 저장
    #print(items)# 테스트

    weather_data = dict()

    for item in items['item']:
        # 기온
        if item['category'] == 'T1H':
            weather_data['tmp'] = item['fcstValue']
        # 습도
        if item['category'] == 'REH':
            weather_data['hum'] = item['fcstValue']
        # 하늘상태: 맑음(1) 구름많은(3) 흐림(4)
        if item['category'] == 'SKY':
            weather_data['sky'] = item['fcstValue']
        # 1시간 동안 강수량
        if item['category'] == 'RN1':
            weather_data['rain'] = item['fcstValue']

    print("response: ", weather_data)

전체적인 코드들은 이렇다 실행이 아아주 잘된다!!!🥰

위도 경도를 x y좌표로 바꾸기

위도 경도를 x y좌표로 바꿔줘야하기 때문에 구글링해서 바꾸는 함수를 찾아냈다

import math
NX = 149            ## X축 격자점 수
NY = 253            ## Y축 격자점 수

Re = 6371.00877     ##  지도반경
grid = 5.0          ##  격자간격 (km)
slat1 = 30.0        ##  표준위도 1
slat2 = 60.0        ##  표준위도 2
olon = 126.0        ##  기준점 경도
olat = 38.0         ##  기준점 위도
xo = 210 / grid     ##  기준점 X좌표
yo = 675 / grid     ##  기준점 Y좌표
first = 0

if first == 0 :
    PI = math.asin(1.0) * 2.0
    DEGRAD = PI/ 180.0
    RADDEG = 180.0 / PI


    re = Re / grid
    slat1 = slat1 * DEGRAD
    slat2 = slat2 * DEGRAD
    olon = olon * DEGRAD
    olat = olat * DEGRAD

    sn = math.tan(PI * 0.25 + slat2 * 0.5) / math.tan(PI * 0.25 + slat1 * 0.5)
    sn = math.log(math.cos(slat1) / math.cos(slat2)) / math.log(sn)
    sf = math.tan(PI * 0.25 + slat1 * 0.5)
    sf = math.pow(sf, sn) * math.cos(slat1) / sn
    ro = math.tan(PI * 0.25 + olat * 0.5)
    ro = re * sf / math.pow(ro, sn)
    first = 1

def mapToGrid(lat, lon, code = 0 ):
    ra = math.tan(PI * 0.25 + lat * DEGRAD * 0.5)
    ra = re * sf / pow(ra, sn)
    theta = lon * DEGRAD - olon
    if theta > PI :
        theta -= 2.0 * PI
    if theta < -PI :
        theta += 2.0 * PI
    theta *= sn
    x = (ra * math.sin(theta)) + xo
    y = (ro - ra * math.cos(theta)) + yo
    x = int(x + 1.5)
    y = int(y + 1.5)
    return x, y

def gridToMap(x, y, code = 1):
    x = x - 1
    y = y - 1
    xn = x - xo
    yn = ro - y + yo
    ra = math.sqrt(xn * xn + yn * yn)
    if sn < 0.0 :
        ra = -ra
    alat = math.pow((re * sf / ra), (1.0 / sn))
    alat = 2.0 * math.atan(alat) - PI * 0.5
    if math.fabs(xn) <= 0.0 :
        theta = 0.0
    else :
        if math.fabs(yn) <= 0.0 :
            theta = PI * 0.5
            if xn < 0.0 :
                theta = -theta
        else :
            theta = math.atan2(xn, yn)
    alon = theta / sn + olon
    lat = alat * RADDEG
    lon = alon * RADDEG

    return lat, lon

# print(mapToGrid(37.579871128849334, 126.98935225645432))
# print(mapToGrid(35.101148844565955, 129.02478725562108))
# print(mapToGrid(33.500946412305076, 126.54663058817043))
# ### result :
# #(60, 127)
# #(97, 74)
# #(53, 38)
#
# print(gridToMap(60, 127))
# print(gridToMap(97, 74))
# print(gridToMap(53, 38))
### result
# 37.579871128849334, 126.98935225645432
# 35.101148844565955, 129.02478725562108
# 33.500946412305076, 126.54663058817043

0개의 댓글