목차
해양기상부이 데이터
1. 관측장비소개
1.1. 개요
해양기상부이는 해수면에서 해양기상현상을 각종 관측 장비로 측정하고, 측정한 값을 일정한 물리량으로 변환·처리한 후 통신장비를 이용하여 관측자료를 전송하는 장비입니다.
해양기상부이에서 관측되는 기본요소는 풍향, 풍속, 기압, 기온, 습도, 파고, 파주기, 파향, 수온, 시정(10m 해양기상부이)이다. 파고와 파주기 관측의 경우에는 해수면에서 부이 몸체의 움직이는 가속도를 측정하여 변위 자료를 산출한 후, 스펙트럼 방법에 의해 유의파고와 파주기 등을 산출합니다.
1.2. 구성
해양기상부이는 부이 본체, 센서부, 항해안전부, 통신부, 전원부, 자료수집처리부, 계류장치, 영상촬영부 등으로 구성됩니다.
2. 관측환경
해양기상부이는 주변의 지형적 설치환경에 크게 제약을 받지 않습니다. 다만, 도서지역 인근에 설치하는 것은 섬에 의한 파랑 변형을 고려하여 섬의 폭 보다 약 2배 정도 떨어진 지점에 설치하는 것이 바람직하지만, 설치해역의 주 풍계 등을 고려하여 최적의 장소를 선 정합니다. 또한, 항로에 설치 시 선박에 의한 충돌로 관측자료 수집 중단과 신뢰성에 문 제가 발생하기 때문에 이를 고려하여 설치합니다.
3. 관측센서
3.1. 정의
해양기상부이는 해수면에서 해양기상현상을 각종 관측 장비로 측정하고, 측정한 값을 일정한 물리량으로 변환·처리한 후 통신장비를 이용하여 관측자료를 전송하는 장비입니다. 해양기상부이에서 관측되는 기본요소는 풍향, 풍속, 기압, 기온, 습도, 파고, 파주기, 파향, 수온, 시정(10m 해양기상부이)입니다. 파고와 파주기 관측의 경우에는 해수면에서 부이 몸체의 움직이는 가속도를 측정하여 변위 자료를 산출한 후, 스펙트럼 방법에 의해 유의파고와 파주기 등을 산출합니다.
3.2. 구성
해양기상부이는 부이 본체, 센서부, 항해안전부, 통신부, 전원부, 자료수집처리부, 계류장치, 영상촬영부 등으로 구성
3.2.1. 부이 본체
해양기상부이 본체는 몸체(Hull), 상부구조물(Mast), 하부구조물(Bridle 또는Mooring Yake)으로 구성
(1) 몸체(Hull)
몸체는 해양기상부이를 해수면 위에 뜨게 하는 부력을 갖춘 구조물로서 해양환경에서 부식에 강한 재질로 구성. 몸체의 형태는 일반적으로 원반형과 선박형으로 구분하며, 관측환경 및 용도에 따라 다양한 형태와 크기의 해양기상부이 몸체가 있습니다. 몸체는 자료처리기, 배터리, 파고계, 수온계, 통신장비 등 다양한 전자 및 통신장비를 설치할 수 있는 격실로 구분한 독립된 공간으로 구성됩니다. 전자나침반, 풍향·풍속계, 파고계 설치 시 방위(향) 일치 및 조정을 용이하게 하기 위하여 몸체 내부의 장치대(mounting rack) 상부에 요철(凹凸)로 표시된 화살표 또는 문자 등으로 방위표시를 합니다. 몸체 가장자리에는 고무방현물(duty rubber)을 부착하여 외부 충격을 흡수해야 하며, 격실의 뚜껑(Hatch)은 열고 닫기가 용이하고 닫았을 때 완전 방수가 되도록 합니다.
(2) 상부구조물(Mast)
상부구조물은 부이 몸체 상부에 설치되는 프레임으로 각종 관측기기와 등명기, 통신장비, 태양전지판, 안테나, 레이더증폭기, 레이더 반사경 등이 설치되며, 부식에 강한 재질로 구성됩니다. 상부구조물은 몸체 상부에 볼트·너트를 이용하여 장착하고, 순간최대풍속 75m/s, 최대파고 20m에서 이탈되거나 휘어지지 않도록 합니다. 또한, 몸체 내부의 배터리에서 방출되는 가스가 외부로 배출될 수 있는 환기구를 설치하며, 환기구를 통해 해수가 침수되지않도록 합니다.
(3) 하부구조물(Bridle 또는 Mooring Yake)
하부구조물은 부이 몸체 하부에 설치되는 프레임으로 계류장치 등이 연결됩니다. 하부 구조물의 형태는 부이의 무게중심을 잡을 수 있는 역삼각형으로 하여 중심 추 역할을 하고, 하단에는 계류용 체인 연결 시 마모 방지용 골무(Thimble)를 부착 하며, 부식 방지를 위한 장치 등을 부착합니다.
3.2.2. 센서부
해양기상부이의 센서부는 풍향·풍속계, 기온계, 습도계, 기압계, 파고계 및 수온계 등으로 구성
(1) 풍향·풍속계
풍향·풍속계는 수평풍향과 풍속 측정용 프로펠러가 일체형으로 된 측기를 사용. 풍향·풍속계는 선박 등 부유이동 물체에 의한 충돌사고와 열악한 해양환경에서 장기간 사용에 따른 노후화(센서부의 베어링·플라스틱 재질 등의 마모)로 장애발생률이 높기 때문에 상부구조물 상단에 별도의 지지대를 이용하여 풍향·풍속계 2조를 설치합니다. 해양기상부이는 해수면 위에서 고정되어 있지 않고 항상 움직이는 상태이기 때문에, 풍향의 정확한 방위를 측정하기 위하여 부이의 선수방향을 표시하여야 합니다. 풍향·풍속계의 베어링은 밀봉되어 있고, 외부에 노출되어 있는 모든 부분은 플라스틱 으로 되어 있지만, 시간이 지남에 따라서 손상되거나 마모되기 때문에 정기적으로 베어링 또는 풍향·풍속계를 신품으로 교체·관리하여야 합니다.
(2) 기온계
기온계는 차광통 내부에 설치. 차광통는 기온계를 눈, 비, 직사광선으로부터 보 호하며, 센서주변에 적절한 자연 통풍이 이루어지게 하여 센서의 반응에 지연이 없도록 합니다. 기온계는 염분, 해수 또는 안개 등에 의해 손상이 자주 발생하므로 정기적으로 점검 및 교체 등 관리하여야 합니다.
(3) 습도계
습도계는 기온계와 같이 차광통 내부에 설치. 습도계는 염분, 해수 또는 안개 등에 의 한 필터 손상이 자주 발생하므로 정기적으로 점검 및 교체 등 관리하여야 합니다.
(4) 기압계
부이 몸체에 기압측정을 위해 인입관로를 만들어 공기가 외부와 자유롭게 순환되게 기압계를 설치하며, 해수가 침투하지 않도록 틈새는 밀폐합니다. 기압계 압력포트에는 습기 제거를 위해 비닐튜브 등을 이용하여 이산화규소 교화제(Slica gel)등을 채웁니다.
(5) 파고계
해양기상부이를 이용하여 파고를 측정하기 위하여 3축 가속도센서로 구성된 파고계를 부이 몸체의 중심부 바닥에 설치한다. 파고계를 운반하거나 설치할 경우 센서 내부의 전기적 회로 보호를 위하여 6회전/2분 이상 회전해서는 절대 안 됩니다.
(6) 수온계
수온계는 몸체 내부 바닥에 설치하는 간접 측정방법과 몸체 외부에 설치하는 직접 측 정방법이 있습니다. 몸체 내부에 설치된 수온계는 부이 몸체 내부 바닥에 접속되어 해수온도가 몸체 바닥에 전도 되면 바닥의 온도를 측정합니다. 외부의 수온을 측정하는 경우 수심 50 ∼100cm 깊이의 부이 몸체 외부에 수온계를 설치하여 직접 해수의 수온을 측정할 수 있 도록 하며, 수온계 설치 관로는 센서 및 케이블의 보호를 위하여 부이몸체와 동일한 재질을 사용합니다.
3.2.3. 항해안전부
(1) 등명기
등명기는 「항로표지법」 (2022.1.4.), 항로표지 장비·용품의 기능 및 규격 기준(해양수산 부고시 제2020-4호)에 의한 항로표지 장비·용품 표준규격을 충족하는 제품을 사용합니다. 그 리고 설치 위치는 선박에서 식별을 용이하게 하기 위해 상부구조물 상단부에 설치합니다.
(2) 레이더반사경
레이더 반사경은 선박 항해 시 선박의 레이더 영상에 부이를 실물보다 크게 표출하도록 하여 선박 충돌을 경감하는 기능이 있습니다. 부이 상부구조물 지지대의 바깥을 향하게 하여 레이더 반사경을 설치합니다.
(3) 레이더반사증폭기
레이더반사증폭기는 선박의 레이더에 반사 주파수를 송출하여 선박의 레이더 영상에서 부이를 실물보다 크게 표출하도록 하여 선박에서 부이의 위치를 보다 용이하게 인지할 수 있도록 합니다. 레이더반사증폭기는 상부구조물 상단부에 설치합니다.
3.2.4.통신부
해양기상부이 관측자료를 실시간으로 전송하기 위하여 GPS 기능이 있는 위성통신 단 말기를 부이 본체에 설치. 위성통신을 통해 관측자료를 해양자료수집서버에 전송합니다.
3.2.5. 전원부
(1) 태양전지판
해양기상부이에서 필요한 전력을 자체에서 생산 할 수 있도록 태양전지판을 사용합니다. 해양기상부이의 소비전력과 배터리 충전을 감안하여 태양전지판 용량을 산정합니다. 태양전 지판은 유지보수 없이 1년 이상 작동할 수 있도록 설치합니다.
(2) 배터리
태양전지판에서 생산된 전력을 충전하여 관측기기 및 통신장비에 전력을 안정적으로 공급하기 위하여 무보수 밀폐형 충전배터리를 사용합니다. 배터리는 병렬로 구성하며, 과충전 및 과방전에 보호되고 온도보상된 부동충전방식을 사용합니다. 외부의 전력 공급 없이 해양기상부이가 5일 이상 정상 가동될 수 있도록 배터리를 구성합니다.
3.2.6.자료수집처리부
자료수집처리부는 각종 관측기기 등에서 생산된 신호를 정해진 시간 간격으로 수집 하여 일정한 자료 형식과 구조로 각 관측 자료를 산출하고 통신부를 이용하여 전송합니다. 자료수집처리부는 관측된 자료를 일정기간 자체에 보관하여 재전송할 수 있으며, 통신 부를 이용한 제어명령을 수행합니다.
3.2.7.계류장치
계류장치는 해양기상부이를 해저에 계류시키는 역할을 합니다. 계류장치는 설치해역의 열악한 해양 환경에 서도 장기간 사용할 수 있도록 부식에 강하고 견고한 재질로 구성 하며, 계류선의 길이와 구성은 설치해역의 해양환경 여건을 고려하여 결정합니다. 계류장치의 인장강도는 부이 부력의 1.5배 이상이 되도록 한다. 또한 설치지점 주변 해역의 해양환경을 고려하여 순간최대풍속 75m/s, 최대파고 20m, 유속 4knots에 대한 복원력, 조 력, 파력, 인장력 등을 감안한 안정성을 고려하여야 합니다. 계류장치에 사용되는 샤클에는 안전 핀을 부착하며, 계류 시에는 이탈 방지를 위해 용접합니다. 모든 계류장치 자재는 해양수산부 (한국선급)의 장비 성능 보전과 안전도 검사를 만족하는 제품을 사용합니다.
3.2.8.영상촬영부
해양기상부이 설치 부근의 해양기상 상태를 파악하기 위하여 300°이상의 파노라마 형식 이미지(동시에 촬영한 3개 파일)를 06시~18시까지 1시간 마다 종합기상정보시스 템에 전송합니다. 영상촬영부는 전원부와 통신부를 별도로 구성하여 설치하며 전원중단, 통신장애에 대비하여 2개월 이상의 자료를 저장할 수 있습니다.
4. 관측지점
덕적도, 외연도, 거문도, 거제도, 마라도 등 28대의 해양기상부이를 운영하고 있습니다. (2023년 9월 1일 기준)
| 장비명 | 지 점 명 |
|---|---|
| 해양기상부이(28개소) | (서해12) 덕적도, 외연도, 인천, 칠발도, 신안, 부안, 가거도, 홍도, 서해170, 서해206, 서해190, 풍도 |
| (남해9) 거문도, 거제도, 통영, 추자도, 마라도, 서귀포, 남해239, 남해465, 남해244 | |
| (동해7) 동해, 포항, 울릉도-독도, 울산, 울진, 동해78, 동해57 |
5. 생산
5.1. 관측요소별 관측단위, 관측범위, 불확도, 최소단위
해양기상관측의 관측요소별 관측범위 및 불확도 등은 해양기상관측장비 표준규격 (기상청고시 제2019-7호)와 자동기상관측장비의 표준규격(기상청고시 제2019-9호)을 따르는 것을 원칙으로 하며, 세부적인 내용은 다음과 같습니다.
| 관측요소 | 관측단위 | 관측범위 | 불확도 | 최소단위 |
|---|---|---|---|---|
| 기온 | 섭씨(℃) | -40∼+60 | ±0.3℃ 이내 | 0.1 |
| 풍향 | 도(°) | 0∼360 | ±5° 이내(그레이코드식, 전위차계식) ±2° 이내(초음파식) | 3(그레이코드식) 1(전위차계식, 초음파식) |
| 풍속 | 초당미터(㎧) | 0∼75 | 0.5m/s 이내(10m/s 미만) 5% 이내(10m/s 이상) (광초퍼식, 자기유도식, 초음파식) | 0.1 |
| 기압 | 헥토파스칼(hPa) | 500∼1080 | ±0.5hPa이내 (750 ~ 1080hPa) | 0.1 |
| 습도(상대습도) | 백분율(%) | 0∼100 | ±3% 이내(0 ~ 90%) ±5% 이내(91 ~ 100%) | 1 |
| 파고(유의파고) | 미터(m) | 0∼15 | ±0.5m 또는 ±10% 이내 | 0.1 |
| 파주기 | 초(s) | 3∼18 | ±1.0초 이내 | 0.1 |
| 수온 | 섭씨(℃) | -2∼+40 | ±0.1℃ 이내 | 0.1 |
| 파향 | 도(°) | 1∼360 | ±5° 이내 | 1 |
| 수위 | 미터(m) | 0∼7 | ±1cm 이내 | 0.01 |
5.2. 관측요소별 자료처리 방법
해양기상관측은 각 관측요소를 아래 표의 자료처리 방법에 따라 매 정시 자료를 생산 하는 것을 기본으로 합니다. 해양기상관측장비 표준규격(기상청고시 제2019-7호)와 자 동기상관측장비의 표준규격(기상청고시 제2019-9호)을 따릅니다.
| 관측요소 | 자료처리방법 |
|---|---|
| 기온 기압 습도 수온 | ○ 자료단위 : 0.1℃(기온), 0.1hPa(기압), 1%(습도), 0.1℃(수온) ○ 샘플링 간격 : 10초 ○ 관측시간 : 매 정시 10분 전부터 정시까지 ○ 자료처리 시간간격 : 30분 또는 1시간 1분 또는 30분 - 매 분전 6개 자료를 평균하여 1분자료 산출 |
| 풍향 풍속 | ○ 자료단위 : 0.1㎧(풍속), 1˚(풍향) ○ 샘플링 간격 : 1초 ○ 관측시간 : 매 정시 10분 전부터 정시까지 ○ 자료처리 시간간격 : 30분 또는 1시간 1분 또는 30분 - 매 1초 간격으로 3초 동안 3개의 샘플링된 자료를 평균하고 1초 간격으로 이동 평균하여 순간풍향·풍속 산출 - 매 정시 전 10분 동안의 600개 풍향·풍속자료를 벡터 평균하여 정시자료 산출 - 매 정시 전 10분 동안의 600개 풍속자료를 비교하여 최대값과 그 때의 풍향을 1시간 최대순간풍향·풍속으로 산출 |
| 유의파고 파주기 | ○ 자료단위: 0.1m(파고), 0.1초(주기) ○ 샘플링 간격 : 1초 이하 ○ 관측자료 수 : 512개 이상으로 한다. 512×2n(n=0,1,2…)으로 증대 ○ 관측시간 : 관측자료 수와 샘플링 간격을 고려하여 결정 - 예 : 1024개 자료를 관측할 경우 8.533분(=1024×0.5초/60초) 전부터 정시까지 |
| 유의파고와 파주기는 해수면의 변위를 스펙트럼 분석하여 산출(아래 이미지참고) |
6. 검색 및 획득
6.1. 검색·획득 – 외부용. 기상자료개방포털
6.1.1. 자료 접근
- 사이트 : 기상청 기상자료개방포털 (data.kma.go.kr)
- 메뉴 : 데이터 → 기상관측 → 해양 → 해양기상부이
6.1.2. 자료 검색
- 조건 : 자료형태, 기간, 지점, 요소
6.2. 검색·획득 - 기상청 API허브
6.2.1. 자료 접근
- 사이트 : 기상청 API허브 (apihub.kma.go.kr)
- 메뉴 : 해양관측 → 해양기상부이파고부이관측
6.2.2. 자료 획득
- 절차: 회원가입 및 로그인 → API 메뉴 이동 → API 활용신청 → API호출
- 호출 방법: 기상청_api허브_사용_안내서.pdf
7. 활용방안
소스코드: buoy_notebook.ipynb
7.1. 라이브러리 설치
# API 요청을 위한 requests 라이브러리와
# 그래프를 그리기 위한 seaborn 라이브러리,
# 지도를 표출하기 위한 folium 라이브러리를 설치합니다.
%pip install requests seaborn folium
7.2. 해양관측 지점정보 다운로드
기상청 API허브에 회원가입 후 자신의 인증키를 사용하여 API 요청을 할 수 있습니다.
API 인증키를 코드상에 직접 남겨 사용하는 방식은 다른 사람에게 코드를 공유할때 인증키가 노출되는 문제가 생길 수 있습니다.
따라서 인증키를 환경변수로 등록하여 코드상에 노출되지 않도록 사용하는 것이 바람직합니다.
import os # 환경변수를 불러오는 표준 라이브러리
# 바람직하지 않은 방법
# my_api_key = 'MY_PRIVATE_API_AUTHENTICATE_KEY'
# 인증키를 'myapikey'라는 이름의 환경변수로 미리 등록해놓은 뒤 이를 불러와서 사용합니다.
my_api_key = os.environ['myapikey']
먼저 해양관측 지점 정보를 조회해 파일로 다운로드합니다.
import requests # API 요청을 보내고 받는 라이브러리
import json # API 응답을 json형태로 가공하기 위한 라이브러리
# 해양관측 지점정보 API url
api_url = 'https://apihub.kma.go.kr/api/typ01/url/stn_inf.php'
# 요청인자로 지점종류, 해당 시점, 지점번호, 도움말추가 여부가 있습니다.
# 1. 지점종류를 입력합니다.
# 해양 관측은 BUOY(부이)를 입력합니다.
stn_type = 'BUOY'
# 2. 해당 시점 및 특정 지점번호를 입력합니다.
# 여기선 현재 시각에 존재하는 모든 지점번호를 조회하기 위해 값을 입력하지 않습니다.
# 3. 도움말추가 여부를 입력합니다.
# 파일 내에 각 필드에 대한 도움말을 확인할 수 있습니다.
help = 1
# API 요청인자들을 묶어 dictionary로 정의합니다.
api_parameters = {
'inf': stn_type,
'help': help,
'authKey': my_api_key
}
# API 응답을 저장할 파일 형식은 txt 파일로 저장합니다.
file_name = f'{stn_type.lower()}_stn_inf.txt'
# 저장된 파일이 없는 경우에만 API를 요청해 파일을 다운로드합니다.
if not os.path.exists(file_name):
# API 요청인자와 함께 API 요청
response = requests.get(api_url, params=api_parameters)
# 잘못된 응답을 받을 경우 에러 메세지 출력
if response.status_code != 200:
raise requests.RequestException(
json.loads(response.content)['result']['message']
)
else:
# 그외의 올바른 응답에 대해서만 UTF-8 txt 파일 형태로 저장합니다.
with open(file_name, 'wb') as f:
f.write(response.content.decode('euc-kr').encode('utf-8'))
print(f'{file_name} 파일 다운로드 완료')
buoy_stn_inf.txt 파일 다운로드 완료
다운로드 받은 파일을 열면 BUOY 각 지점 번호와 이름, 지점 정보를 확인할 수 있습니다.
BUOY 각 지점들의 위치를 지도로 표출합니다.
import pandas as pd # dataframe을 다루는 패키지
# 다운로드 받은 txt파일을 dataframe으로 변환합니다.
# 도움말을 비롯한 파일의 첫 20줄과 마지막 1줄을 제외하여 변환합니다.
# 각 필드를 구분하도록 너비를 정의하고 존재하는 모든 필드를 사용합니다.
stn_info = pd.read_fwf(
file_name,
skiprows=20,
skipfooter=1,
names=['STN_ID', 'LON', 'LAT', 'STN_SP', 'HT', 'STN_AD', 'STN_KO', 'STN_EN', 'FCT_ID'],
index_col=['STN_ID'],
colspecs=((0, 5), (7, 19), (22, 33), (34, 47), (48, 52), (53, 56), (57, 71), (72, 95), (96, 104)),
converters={'STN_SP': str}
)
# dataframe의 첫 5개의 데이터를 표출합니다.
stn_info.head(5)
| STN_ID | LON | LAT | STN_SP | HT | STN_AD | STN_KO | STN_EN | FCT_ID |
|---|---|---|---|---|---|---|---|---|
| 1791 | 126.3933 | 37.1133 | 40000004 | 0.0 | 0 | 평택풍도 | —- | NaN |
| 2787 | 126.6161 | 37.3800 | 40000003 | 0.0 | 0 | 인천송도 | —- | NaN |
| 4777 | 126.3039 | 36.9781 | 40000006 | 0.0 | 0 | 대산 | —- | NaN |
| 5776 | 126.5864 | 35.9742 | 40000005 | 0.0 | 0 | 군산 | —- | NaN |
| 6785 | 126.3097 | 34.7514 | 40000007 | 0.0 | 0 | 목포 | —- | NaN |
해양관측 지점정보에는 해양기상부이 외에도 등표기상관측장비, 파고부이가 존재합니다.
이들 중 관리관서번호(STN_AD)가 존재하는 지점이 해양기상부이입니다.
# 관리관서번호(STN_AD)가 존재하는 해양기상부이 지점으로 필터링해
# dataframe의 첫 5개의 데이터를 표출합니다.
buoy_stn_info = stn_info[stn_info.STN_AD > 0]
buoy_stn_info.head(5)
| STN_ID | LON | LAT | STN_SP | HT | STN_AD | STN_KO | STN_EN | FCT_ID |
|---|---|---|---|---|---|---|---|---|
| 21229 | 131.1144 | 37.4554 | 00000001 | 0.0 | 143 | 울릉도 | Ulleungdo | 12C20000 |
| 22101 | 126.0188 | 37.2361 | 00000001 | 0.0 | 112 | 덕적도 | Deokjeokdo | 12A20000 |
| 22102 | 125.7769 | 34.7933 | 00000001 | 0.0 | 165 | 칠발도 | Chilbaldo | 12A30000 |
| 22103 | 127.5014 | 34.0014 | 00000001 | 0.0 | 156 | 거문도 | Geomundo | 12B10000 |
| 22104 | 128.9000 | 34.7667 | 00000001 | 0.0 | 159 | 거제도 | Geojedo | 12B20000 |
import folium # 지도로 표출하기 위한 라이브러리
# 너비, 높이 크기가 800인 지도 생성
fig = folium.Figure(width=800, height=800)
# 지도의 위·경도 중심과 범위를 정해 맵을 생성합니다.
map = folium.Map(
location=[37.5, 127],
zoom_start=6,
min_zoom=6,
min_lat=30,
max_lat=44,
min_lon=123,
max_lon=132,
max_bounds=True,
).add_to(fig)
# dataframe의 데이터를 읽어 지도위에 마커를 추가합니다.
# marker를 클릭하면 팝업으로 지점명, 위·경도와 해발고도를 표출하도록 html을 추가합니다.
for idx, row in buoy_stn_info.iterrows():
html=f"""
<h3>{row['STN_KO']}({idx})</h3>
<p>위도: {row['LAT']}</p>
<p>경도: {row['LON']}</p>
<p>해발고도: {row['HT']}m</p>
"""
iframe = folium.IFrame(html=html, width=170, height=170)
popup = folium.Popup(iframe, max_width=1000)
folium.Marker(
location=row[['LAT', 'LON']].tolist(),
popup=popup,
).add_to(map)
# 지도를 표출합니다.
fig
해양기상부이 지점들 중 하나를 골라 시계열 그래프를 그려봅니다.
7.3. 시계열 그래프 표출
시계열 그래프를 표출하기 전, 그래프에 한글을 표출하려면 한글 폰트의 설정이 필요합니다.
만약, 한글 표출을 하지 않는다면 이 부분을 실행하지 않아도 좋습니다.
# Linux Ubuntu에서의 나눔폰트 설치와 캐시를 제거합니다.
!sudo apt-get install -y fonts-nanum
!sudo fc-cache -fv
!rm -rf ~/.cache/matplotlib
import seaborn as sns # 그래프를 그리는 라이브러리
import matplotlib.pyplot as plt # 그래프를 그리는 라이브러리
# 한글을 출력하기 위해 사용하는 기본 폰트를 나눔고딕으로 설정합니다.
plt.rc("font", family = "NanumGothic")
sns.set_theme(font="NanumGothic", rc={"axes.unicode_minus": False}, style='darkgrid')
import requests # API 요청을 보내고 받는 라이브러리
import json # API 응답을 json형태로 가공하기 위한 라이브러리
# 해양기상부이 자료 조회 API url
api_url = 'https://apihub.kma.go.kr/api/typ01/url/kma_buoy2.php'
# 요청인자로 시작시간, 종료시간, 지점번호, 도움말추가 여부가 있습니다.
# 1. 시작시간과 종료시간을 년월일시분 형태로 입력합니다.
start_time = '202402010000'
end_time = '202403010000'
# 2. 앞에서 확인한 지점번호를 입력합니다.
stn_id = 22185
# 3. 도움말추가 여부를 입력합니다.
# 파일 내에 각 필드에 대한 도움말을 확인할 수 있습니다.
help = 1
# API 요청인자들을 묶어 dictionary로 정의합니다.
api_parameters = {
'tm1': start_time,
'tm2': end_time,
'stn': stn_id,
'help': help,
'authKey': my_api_key
}
# API 응답을 저장할 파일 형식은 txt 파일로 저장합니다.
file_name = f"buoy_data_{stn_id}_{start_time}_{end_time}.txt"
# 저장된 파일이 없는 경우에만 API를 요청해 파일을 다운로드합니다.
if not os.path.exists(file_name):
# API 요청인자와 함께 API 요청
response = requests.get(api_url, params=api_parameters)
# 잘못된 응답을 받을 경우 에러 메세지 출력
if response.status_code != 200:
raise requests.RequestException(
json.loads(response.content)['result']['message']
)
# 짧은 에러메세지를 응답으로 받은 경우 에러 메세지 출력
elif len(response.content) < 100:
raise ValueError(response.content.decode('euc-kr').split('#')[2])
else:
# 그외의 올바른 응답에 대해서만 UTF-8 txt 파일 형태로 저장합니다.
with open(file_name, 'wb') as f:
f.write(response.content.decode('euc-kr').encode('utf-8'))
print(f"{file_name} 파일 다운로드 완료")
buoy_data_22185_202402010000_202403010000.txt 파일 다운로드 완료
다운로드 받은 해양기상부이 파일을 열어 자료를 확인할 수 있습니다.
풍속, 기압, 습도, 기온, 파고 시계열 그래프를 표출합니다.
import pandas as pd # dataframe을 다루는 패키지
import numpy as np # 배열을 다루는 패키지
# 다운로드 받은 txt파일을 dataframe으로 변환합니다.
# 도움말을 비롯한 파일의 첫 28줄과 마지막 1줄을 제외하여 변환합니다.
# 각 필드는 쉼표로 구분되어있으며 QC필드를 제외한 모든 필드를 사용합니다.
column_names = [
'TIME', 'STN_ID', 'WD1', 'WS1', 'WS1_GST', 'WD2', 'WS2', 'WS2_GST',
'PA', 'HM', 'TA', 'TW', 'WH_MAX', 'WH_SIG', 'WH_AVE', 'WP', 'WO',
'AQC', 'MQC', 'NONE'
]
buoy_data = pd.read_csv(
file_name,
skiprows=28,
skipfooter=1,
names=column_names,
parse_dates=['TIME'],
index_col=['TIME', 'STN_ID'],
usecols=column_names[:-3],
engine='python'
)
# 도움말에 따르면 값이 -50 이하라면 관측이 없거나, 에러처리된 것을 표시합니다.
# 따라서 그 값을 NaN으로 변환합니다.
buoy_data[buoy_data <= -50] = np.nan
# dataframe의 첫 5개의 데이터를 표출합니다.
buoy_data.head(5)
| TIME | STN_ID | WD1 | WS1 | WS1_GST | WD2 | WS2 | WS2_GST | PA | HM | TA | TW | WH_MAX | WH_SIG | WH_AVE | WP | WO |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2024-02-01 00:00:00 | 22185 | NaN | 8.5 | 10.6 | 340 | 8.5 | 10.6 | 1026.2 | 84.0 | 2.0 | 4.7 | 1.8 | 1.2 | 0.9 | 4.1 | 303 |
| 2024-02-01 00:30:00 | 22185 | NaN | 9.2 | 11.3 | 345 | 9.2 | 11.. | 1026.3 | 78.0 | 1.7 | 4.7 | 1.8 | 1.2 | 0.9 | 4.1 | 312 |
| 2024-02-01 01:00:00 | 22185 | NaN | 9.3 | 11.5 | 339 | 9.3 | 11.4 | 1026.4 | 75.0 | 1.7 | 4.5 | 2.1 | 1.2 | 0.9 | 3.9 | 316 |
| 2024-02-01 01:30:00 | 22185 | NaN | 8.5 | 11.1 | 347 | 8.5 | 11.3 | 1026.3 | 73.0 | 1.6 | 4.3 | 2.3 | 1.3 | 0.9 | 4.0 | 316 |
| 2024-02-01 02:00:00 | 22185 | NaN | 7.6 | 10.0 | 355 | 7.7 | 10.1 | 1026.6 | 77.0 | 1.1 | 4.0 | 2.2 | 1.3 | 1.0 | 4.0 | 309 |
# 필드 중 풍속(WS), GUST 풍속(WS_GST)을 표출합니다.
vars = ['WS1', 'WS1_GST']
# 그래프를 그릴 크기를 지정합니다.
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
# 그래프의 제목을 지정합니다.
# 제목에 한글 지점명이 사용됩니다.
ax.set_title(
f"{stn_info[stn_info.index == stn_id]['STN_KO'].iloc[0]}({stn_id}) "
f"{buoy_data.index.get_level_values('TIME')[0]} - {buoy_data.index.get_level_values('TIME')[-1]}")
# x축의 라벨을 45도 기울여서 사용합니다.
ax.tick_params(axis='x', rotation=45)
# 각 요소별로 시계열 그래프를 그립니다.
for var in vars:
sns.lineplot(buoy_data, x=buoy_data.index.get_level_values('TIME'), y=var, label=var, ax=ax)
# 그래프를 표출합니다.
plt.show()
# 필드 중 해면기압(PA)을 표출합니다.
vars = ['PA']
# 그래프를 그릴 크기를 지정합니다.
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
# 그래프의 제목을 지정합니다.
# 제목에 한글 지점명이 사용됩니다.
ax.set_title(
f"{stn_info[stn_info.index == stn_id]['STN_KO'].iloc[0]}({stn_id}) "
f"{buoy_data.index.get_level_values('TIME')[0]} - {buoy_data.index.get_level_values('TIME')[-1]}")
# x축의 라벨을 45도 기울여서 사용합니다.
ax.tick_params(axis='x', rotation=45)
# 해면기압 시계열 그래프를 그립니다.
for var in vars:
sns.lineplot(buoy_data, x=buoy_data.index.get_level_values('TIME'), y=var, label=var, ax=ax)
# 그래프를 표출합니다.
plt.show()
# 필드 중 상대 습도(HM)를 표출합니다.
vars = ['HM']
# 그래프를 그릴 크기를 지정합니다.
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
# 그래프의 제목을 지정합니다.
# 제목에 한글 지점명이 사용됩니다.
ax.set_title(
f"{stn_info[stn_info.index == stn_id]['STN_KO'].iloc[0]}({stn_id}) "
f"{buoy_data.index.get_level_values('TIME')[0]} - {buoy_data.index.get_level_values('TIME')[-1]}")
# x축의 라벨을 45도 기울여서 사용합니다.
ax.tick_params(axis='x', rotation=45)
# 상대 습도 시계열 그래프를 그립니다.
for var in vars:
sns.lineplot(buoy_data, x=buoy_data.index.get_level_values('TIME'), y=var, label=var, ax=ax)
# 그래프를 표출합니다.
plt.show()
# 필드 중 기온(TA), 해수면 온도(TW)를 표출합니다.
vars = ['TA', 'TW']
# 그래프를 그릴 크기를 지정합니다.
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
# 그래프의 제목을 지정합니다.
# 제목에 한글 지점명이 사용됩니다.
ax.set_title(
f"{stn_info[stn_info.index == stn_id]['STN_KO'].iloc[0]}({stn_id}) "
f"{buoy_data.index.get_level_values('TIME')[0]} - {buoy_data.index.get_level_values('TIME')[-1]}")
# x축의 라벨을 45도 기울여서 사용합니다.
ax.tick_params(axis='x', rotation=45)
# 각 요소별로 시계열 그래프를 그립니다.
for var in vars:
sns.lineplot(buoy_data, x=buoy_data.index.get_level_values('TIME'), y=var, label=var, ax=ax)
# 그래프를 표출합니다.
plt.show()
# 필드 중 최대파고(WH_MAX), 유의파고(WH_SIG), 평균파고(WH_AVE)를 표출합니다.
vars = ['WH_MAX', 'WH_SIG', 'WH_AVE']
# 그래프를 그릴 크기를 지정합니다.
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
# 그래프의 제목을 지정합니다.
# 제목에 한글 지점명이 사용됩니다.
ax.set_title(
f"{stn_info[stn_info.index == stn_id]['STN_KO'].iloc[0]}({stn_id}) "
f"{buoy_data.index.get_level_values('TIME')[0]} - {buoy_data.index.get_level_values('TIME')[-1]}")
# x축의 라벨을 45도 기울여서 사용합니다.
ax.tick_params(axis='x', rotation=45)
# 각 요소별로 시계열 그래프를 그립니다.
for var in vars:
sns.lineplot(buoy_data, x=buoy_data.index.get_level_values('TIME'), y=var, label=var, ax=ax)
# 그래프를 표출합니다.
plt.show()

