사이트 도구


태풍:베스트트랙

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판이전 판
다음 판
이전 판
태풍:베스트트랙 [2026/05/04 08:37] – [② 베스트트랙 세부내용] admin태풍:베스트트랙 [2026/05/06 08:24] (현재) – [4. 베스트트랙 제공 위치] admin
줄 10: 줄 10:
  
 베스트트랙은 태풍 종료 후 다음과 같은 절차를 통해 생산되고, 확정됩니다. 베스트트랙은 태풍 종료 후 다음과 같은 절차를 통해 생산되고, 확정됩니다.
 +
  
 ^  자료 \\ 수집  ^  베스트트랙 \\ 초안 작성  ^  태풍예보관 \\ 교차검토  ^  재분석 검토(2차) \\ - 내외부 전문가2)-  ^  베스트트랙 확정  ^ ^  자료 \\ 수집  ^  베스트트랙 \\ 초안 작성  ^  태풍예보관 \\ 교차검토  ^  재분석 검토(2차) \\ - 내외부 전문가2)-  ^  베스트트랙 확정  ^
줄 16: 줄 17:
 태풍의 위치(위‧경도), 중심기압, 최대풍속, 강도 등급, 강풍반경, 폭풍반경 정보를 포함합니다. 태풍의 위치(위‧경도), 중심기압, 최대풍속, 강도 등급, 강풍반경, 폭풍반경 정보를 포함합니다.
  
-====① 베스트트랙 양식(예시)====+====3.1 베스트트랙 양식(예시)====
  
  
-^   등급    태풍호수  ^  날짜(UTC)  ^^^^  위치  ^^^  강도  ^^  강풍반경  ^^  폭풍반경  ^^^  태풍이름 + 
-^ ::: ^ ::: ^  년  ^  월  ^  일  ^  시  ^  경도(°E)  ^  위도(°N)  ^  최대풍속(m/s)  ^  중심기압(hPa)  ^  장반경(km)  ^  단반경(km)  ^  반경방향(°)  ^  장반경(km)  ^  단반경(km)  ^  단반경방향(°)  ^ ::: ^+^   등급    태풍 \\ 호수  ^  날짜(UTC)  ^^^^  위치  ^^  강도  ^^  강풍반경  ^^^  폭풍반경  ^^^  태풍이름 
 +^ ::: ^ ::: ^  년  ^  월  ^  일  ^  시  ^  경도(°E)  ^  위도(°N)  ^  최대풍속(m/s)  ^  중심기압(hPa)  ^  장반경(km)  ^  단반경(km)  ^  반경방향(°)  ^  장반경(km)  ^  단반경(km)  ^  단반경방향(°)  ^ ::: ^
 |  TS  |  2401  |  2024  |  05  |  25  |  12  |  122.2  |  13.3  |  18  |  1000  |  180  |  110  |  270.0  |  -999  |  -999  |  -999.9  |  EWINIAR  | |  TS  |  2401  |  2024  |  05  |  25  |  12  |  122.2  |  13.3  |  18  |  1000  |  180  |  110  |  270.0  |  -999  |  -999  |  -999.9  |  EWINIAR  |
-====② 베스트트랙 세부내용====+====3.2 베스트트랙 세부내용====
  
 ^  종류  ^^  자리수  ^  세부내용  ^ ^  종류  ^^  자리수  ^  세부내용  ^
줄 41: 줄 43:
 |  \\ \\ \\ 강풍반경  |  장반경  |  4자리 정수  | 강풍(풍속 15 m/s 이상) 장반경, 단위: km \\ 존재하지 않을 시: -999 | |  \\ \\ \\ 강풍반경  |  장반경  |  4자리 정수  | 강풍(풍속 15 m/s 이상) 장반경, 단위: km \\ 존재하지 않을 시: -999 |
 | ::: |  단반경  |  4자리 정수  | 강풍(풍속 15 m/s 이상) 단반경, 단위: km \\ 존재하지 않을 시: -999 | | ::: |  단반경  |  4자리 정수  | 강풍(풍속 15 m/s 이상) 단반경, 단위: km \\ 존재하지 않을 시: -999 |
-| ::: |  \\  방향  |  \\ 6자리 실수  | 강풍 단반경의 방향 \\ 단위: 16방위(22.5° 단위, 0~337.5°) \\ 존재하지 않을 시: -999.9 |+| ::: |  \\  방향  |  \\ 6자리 실수  | 강풍(풍속 15 m/s 이상) 단반경의 방향 \\ 단위: 16방위(22.5° 단위, 0~337.5°) \\ 존재하지 않을 시: -999.9 |
 |  \\ \\ \\ 폭풍반경  |  장반경  |  4자리 정수  | 폭풍(풍속 25 m/s 이상) 장반경, 단위: km \\ 존재하지 않을 시: -999 | |  \\ \\ \\ 폭풍반경  |  장반경  |  4자리 정수  | 폭풍(풍속 25 m/s 이상) 장반경, 단위: km \\ 존재하지 않을 시: -999 |
 | ::: | 단반경 |  4자리 정수  | 폭풍(풍속 25 m/s 이상) 단반경, 단위: km \\ 존재하지 않을 시: -999 | | ::: | 단반경 |  4자리 정수  | 폭풍(풍속 25 m/s 이상) 단반경, 단위: km \\ 존재하지 않을 시: -999 |
-| ::: |  \\ 방향  |  \\ 6자리 실수  | 폭풍 단반경의 방향 \\ 단위: 16방위(22.5° 단위, 0~337.5°) \\ 존재하지 않을 시: -999.9 |+| ::: |  \\ 방향  |  \\ 6자리 실수  | 폭풍(풍속 25 m/s 이상) 단반경의 방향 \\ 단위: 16방위(22.5° 단위, 0~337.5°) \\ 존재하지 않을 시: -999.9 |
 |  태풍이름  ||  20자리 문자  | 영문 대문자로 표시 | |  태풍이름  ||  20자리 문자  | 영문 대문자로 표시 |
 =====4. 베스트트랙 제공 위치===== =====4. 베스트트랙 제공 위치=====
  
- • 기상청 누리집(https://www.kma.go.kr/kma/): 알림‧자료 > 기상간행물 > 국가태풍센터 간행물 > 베스트트랙+ • **기상청 누리집(https://www.kma.go.kr/kma/)**: 알림‧자료 > 기상간행물 > 국가태풍센터 간행물 > 베스트트랙
  
- • 기상청 API허브(https://apihub.kma.go.kr/): 태풍 > 태풍 베스트트랙+ • **기상청 API허브(https://apihub.kma.go.kr/)**: 태풍 > 태풍 베스트트랙
  
- • IBTrACS(International Best Track Archive for Climate Stewardship)+ • **IBTrACS(International Best Track Archive for Climate Stewardship)** 
 + 
 +===== 5. 활용방안 =====
  
-=====5. 활용방안===== 
 소스코드 : {{ :태풍:typhoon_besttrack_example.py |}} 소스코드 : {{ :태풍:typhoon_besttrack_example.py |}}
  
-====① 데터 제 함수====+==== 5.1. 라브러리 설치 및 기본 설정 ==== 
 + 
 +태풍 베스트트랙 자료를 조회하고 태풍 중심 위치 및 강풍반경을 시각화하기 위해 필요한 라이브러리를 설치합니다. 
 <code py> <code py>
-   def clean_besttrack_df(df): +pip install requests pandas matplotlib cartopy numpy
-      if df.empty: +
-          return df +
-      df = df.copy() +
-      numeric_cols = ["TYNO", "YY", "MM", "DD", "HH", "LON", "LAT", "MAX_WS", "PRES", "R15", "R25"+
-      for col in numeric_cols: +
-          df[col] = pd.to_numeric(df[col], errors="coerce"+
-      # 결측값 처리 +
-      for col in ["MAX_WS", "PRES", "R15", "R25"]: +
-          df.loc[df[col] <= -9, col] = pd.NA +
-      # YY가 2자리면 2000년대 보정 +
-      if df["YY"].dropna().max() < 100: +
-          df["YYYY"] = df["YY"].apply(lambda x: int(2000 + x) if pd.notna(x) else pd.NA) +
-      else: +
-          df["YYYY"] = df["YY"+
-      def make_dt(row): +
-          try: +
-              return datetime( +
-                  int(row["YYYY"]), +
-                  int(row["MM"]), +
-                  int(row["DD"]), +
-                  int(row["HH"]) +
-              ) +
-          except Exception: +
-              return pd.NaT +
-      df["datetime"] = df.apply(make_dt, axis=1) +
-      # 유효한 범위만 남김 +
-      df = df.dropna(subset=["LAT", "LON"]).copy() +
-      df = df[(df["LAT"] >= -90) & (df["LAT"] <= 90)] +
-      df = df[(df["LON"] >= 0) & (df["LON"] <= 360)] +
-      # 0~360 경도를 -180~180으로 바꾸고 싶으면 아래 사용 +
-      # df["LON"] = df["LON"].apply(lambda x: x - 360 if x > 180 else x) +
-      df = df.sort_values("datetime").reset_index(drop=True) +
-      return df+
 </code> </code>
-====② 지도 시각화 함수====      +
 <code py> <code py>
-   def plot_typhoon_track(df): +import os 
-       # 1. 지도 및 투영법 설정 +from datetime import datetime 
-       fig = plt.figure(figsize=(12, 10)) + 
-       ax = plt.axes(projection=ccrs.PlateCarree()) +import numpy as np 
-       # 2. 지형지물 추가 (육지, 바다, 해안선) +import requests 
-       ax.add_feature(cfeature.LAND, facecolor="#f2f2f2") +import pandas as pd 
-       ax.add_feature(cfeature.OCEAN, facecolor="#e6f2ff") + 
-       ax.add_feature(cfeature.COASTLINE, linewidth=0.8) +import matplotlib 
-       # 3. 태풍 경로 및 중심점 그리기 +matplotlib.use("Agg") 
-       ax.plot(df["LON"], df["LAT"], color="black", transform=ccrs.PlateCarree()) +import matplotlib.pyplot as plt 
-       ax.scatter(df["LON"], df["LAT"], color="royalblue", transform=ccrs.PlateCarree()) +from matplotlib import font_manager 
-       # 4. 강풍 반경 원 그리기 (반복문을 통해 R15, R25 표시) +from matplotlib.lines import Line2D 
-       for i, row in df.iterrows(): +from matplotlib.patches import Patch 
-           draw_wind_radius(ax, row["LON"], row["LAT"], row["R15"], "orange", "gold"+ 
-           draw_wind_radius(ax, row["LON"], row["LAT"], row["R25"], "red", "red") +import cartopy.crs as ccrs 
-       # 5결과물 저장 +import cartopy.feature as cfeature 
-       plt.savefig(OUTPUT_PNG, dpi=150, bbox_inches="tight")+from cartopy.geodesic import Geodesic
 </code> </code>
-====③ 예상결과====    
  
 +API 인증키와 API 주소, 조회할 태풍 정보를 설정합니다.
 +
 +<code py>
 +AUTH_KEY = "본인의_API_인증키"
 +
 +BASE_URL = "https://apihub.kma.go.kr/api/typ01/url/typ_besttrack.php"
 +
 +YEAR = 2023
 +TCID = "2311"
 +TY_NAME = f"Typhoon {TCID}"
 +
 +HELP = 1
 +
 +RAW_TEXT_FILE = f"typhoon_besttrack_{YEAR}_{TCID}_raw.txt"
 +OUTPUT_CSV = f"typhoon_besttrack_{YEAR}_{TCID}.csv"
 +OUTPUT_PNG = f"typhoon_besttrack_map_{YEAR}_{TCID}.png"
 +</code>
 +
 +BASE_URL은 (신)기상자료개방포털 > API > 태풍 > 태풍 베스트트랙메뉴의 호출 URL을 사용합니다.
 +
 +YEAR에는 태풍 발생 연도를 입력합니다.
 +
 +TCID에는 태풍 번호를 입력합니다.
 +
 +예를 들어 2023년 11호 태풍은 2311로 입력합니다.
 +
 +==== 5.2. 태풍 베스트트랙 자료 조회 및 정리 ====
 +
 +태풍 베스트트랙 API를 호출하여 응답 원문 TEXT를 받아옵니다.
 +
 +<code py>
 +def fetch_besttrack_text(year, tcid):
 +    params = {
 +        "year": year,
 +        "tcid": tcid,
 +        "help": HELP,
 +        "authKey": AUTH_KEY,
 +    }
 +
 +    response = requests.get(BASE_URL, params=params, timeout=60)
 +    response.raise_for_status()
 +
 +    text = response.text
 +
 +    with open(RAW_TEXT_FILE, "w", encoding="utf-8") as f:
 +        f.write(text)
 +
 +    return text
 +</code>
 +
 +API 응답에는 설명 줄과 실제 데이터 줄이 함께 포함됩니다.
 +
 +설명 줄은 #으로 시작하므로 제외하고 실제 데이터 줄만 사용합니다.
 +
 +<code py>
 +def extract_data_lines(text):
 +    lines = []
 +
 +    for raw_line in text.splitlines():
 +        line = raw_line.strip()
 +
 +        if not line:
 +            continue
 +
 +        if line.startswith("#"):
 +            continue
 +
 +        lines.append(line)
 +
 +    return lines
 +</code>
 +
 +베스트트랙 응답 자료의 컬럼은 다음과 같습니다.
 +
 +<code py>
 +COLS = [
 +    "GRADE",
 +    "TYNO",
 +    "YY",
 +    "MM",
 +    "DD",
 +    "HH",
 +    "LON",
 +    "LAT",
 +    "MAX_WS",
 +    "PRES",
 +    "R15",
 +    "R25",
 +]
 +</code>
 +
 +응답 텍스트를 pandas DataFrame으로 변환합니다.
 +
 +<code py>
 +def parse_besttrack_text(text):
 +    data_lines = extract_data_lines(text)
 +
 +    rows = []
 +
 +    for line in data_lines:
 +        clean = line.replace(",", " ")
 +        parts = [p for p in clean.split() if p]
 +
 +        if len(parts) < 12:
 +            continue
 +
 +        rows.append(parts[:12])
 +
 +    df = pd.DataFrame(rows, columns=COLS)
 +    return df
 +</code>
 +
 +숫자형 컬럼과 시간 컬럼을 변환하고 결측값을 처리합니다.
 +
 +<code py>
 +def clean_besttrack_df(df):
 +    numeric_cols = [
 +        "TYNO", "YY", "MM", "DD", "HH",
 +        "LON", "LAT", "MAX_WS",
 +        "PRES", "R15", "R25"
 +    ]
 +
 +    for col in numeric_cols:
 +        df[col] = pd.to_numeric(df[col], errors="coerce")
 +
 +    for col in ["MAX_WS", "PRES", "R15", "R25"]:
 +        df.loc[df[col] <= -9, col] = pd.NA
 +
 +    if df["YY"].dropna().max() < 100:
 +        df["YYYY"] = df["YY"].apply(
 +            lambda x: int(2000 + x) if pd.notna(x) else pd.NA
 +        )
 +    else:
 +        df["YYYY"] = df["YY"]
 +
 +    def make_dt(row):
 +        try:
 +            return datetime(
 +                int(row["YYYY"]),
 +                int(row["MM"]),
 +                int(row["DD"]),
 +                int(row["HH"])
 +            )
 +        except Exception:
 +            return pd.NaT
 +
 +    df["datetime"] = df.apply(make_dt, axis=1)
 +
 +    df = df.dropna(subset=["LAT", "LON"]).copy()
 +
 +    df = df.sort_values("datetime").reset_index(drop=True)
 +
 +    return df
 +</code>
 +
 +==== 5.3. 태풍 중심 위치 및 강풍반경 표출 ====
 +
 +지도 표출을 위한 기본 지도를 생성합니다.
 +
 +<code py>
 +def setup_map():
 +    fig = plt.figure(figsize=(12, 10))
 +    ax = plt.axes(projection=ccrs.PlateCarree())
 +
 +    ax.add_feature(cfeature.LAND, facecolor="#f2f2f2")
 +    ax.add_feature(cfeature.OCEAN, facecolor="#e6f2ff")
 +    ax.add_feature(cfeature.COASTLINE, linewidth=0.8)
 +    ax.add_feature(cfeature.BORDERS, linewidth=0.5)
 +
 +    ax.gridlines(
 +        draw_labels=True,
 +        linewidth=0.4,
 +        linestyle="--",
 +        alpha=0.5
 +    )
 +
 +    return fig, ax
 +</code>
 +
 +태풍 중심 위치를 선과 점으로 연결하여 태풍 경로를 표출합니다.
 +
 +<code py>
 +ax.plot(
 +    df["LON"],
 +    df["LAT"],
 +    color="black",
 +    linewidth=1.5,
 +    linestyle="-",
 +    transform=ccrs.PlateCarree()
 +)
 +
 +ax.scatter(
 +    df["LON"],
 +    df["LAT"],
 +    s=30,
 +    color="royalblue",
 +    edgecolors="black",
 +    linewidths=0.3,
 +    transform=ccrs.PlateCarree()
 +)
 +</code>
 +
 +15m/s 및 25m/s 풍속반경을 원 형태로 표출합니다.
 +
 +<code py>
 +draw_wind_radius(
 +    ax=ax,
 +    lon=row["LON"],
 +    lat=row["LAT"],
 +    radius_value=row["R15"],
 +    edgecolor="orange",
 +    facecolor="gold",
 +    alpha=0.10,
 +    linewidth=0.8
 +)
 +
 +draw_wind_radius(
 +    ax=ax,
 +    lon=row["LON"],
 +    lat=row["LAT"],
 +    radius_value=row["R25"],
 +    edgecolor="red",
 +    facecolor="red",
 +    alpha=0.06,
 +    linewidth=1.0
 +)
 +</code>
 +
 +태풍 생성 지점, 소멸 지점, 최대세력 지점을 함께 표시합니다.
 +
 +<code py>
 +ax.scatter(
 +    first_row["LON"],
 +    first_row["LAT"],
 +    s=120,
 +    color="limegreen",
 +    edgecolors="black",
 +    marker="o"
 +)
 +
 +ax.scatter(
 +    last_row["LON"],
 +    last_row["LAT"],
 +    s=120,
 +    color="black",
 +    edgecolors="white",
 +    marker="X"
 +)
 +
 +ax.scatter(
 +    peak_row["LON"],
 +    peak_row["LAT"],
 +    s=180,
 +    color="magenta",
 +    edgecolors="black",
 +    marker="*"
 +)
 +</code>
 +
 +최종 태풍 경로 지도는 PNG 파일로 저장합니다.
 +
 +<code py>
 +plt.savefig(
 +    OUTPUT_PNG,
 +    dpi=150,
 +    bbox_inches="tight"
 +)
 +</code>
 +
 +전체 실행 코드는 다음과 같습니다.
 +
 +<code py>
 +text = fetch_besttrack_text(YEAR, TCID)
 +
 +df = parse_besttrack_text(text)
 +
 +df = clean_besttrack_df(df)
 +
 +df.to_csv(
 +    OUTPUT_CSV,
 +    index=False,
 +    encoding="utf-8-sig"
 +)
 +
 +plot_typhoon_track(df)
 +</code>
 +
 +실행하면 다음 파일이 생성됩니다.
  
 {{:태풍:typhoon_besttrack_map_2023_2301.png?nolink500}} {{:태풍:typhoon_besttrack_map_2023_2301.png?nolink500}}
 {{:태풍:typhoon_besttrack_map_2023_2311.png?nolink500}} {{:태풍:typhoon_besttrack_map_2023_2311.png?nolink500}}
 +