사이트 도구


수치모델:단중기모델:한국형수치예보모델_kim

차이

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

차이 보기로 링크

양쪽 이전 판이전 판
다음 판
이전 판
수치모델:단중기모델:한국형수치예보모델_kim [2024/05/30 04:47] – [한국형수치예보모델(KIM)] admin수치모델:단중기모델:한국형수치예보모델_kim [2026/08/19 09:23] (현재) – [1. 개요] 118.235.66.82
줄 1: 줄 1:
 ======  한국형수치예보모델(KIM)  ====== ======  한국형수치예보모델(KIM)  ======
-===== 1. 개요 =====+[[수치모델:단중기모델:한국형수치예보모델_kim#4. 활용방안|{{:wiki:활용방안클릭.png?linkonly250|}}]] 
 +===== 1. 개
 ==== 1.1. 수치예보 설명 ==== ==== 1.1. 수치예보 설명 ====
 수치예보(Numerical Weather Prediction : NWP)란 대기현상의 역학 및 물리적 원리에 대한 지배방정식들을 컴퓨터를 활용하여 연속적으로 수치적분함으로써 현재의 대기상태를 분석하고 나아가 미래의 대기상태를 정략적으로 예측하는 일련의 과정입니다. 수치예보(Numerical Weather Prediction : NWP)란 대기현상의 역학 및 물리적 원리에 대한 지배방정식들을 컴퓨터를 활용하여 연속적으로 수치적분함으로써 현재의 대기상태를 분석하고 나아가 미래의 대기상태를 정략적으로 예측하는 일련의 과정입니다.
줄 101: 줄 102:
  
  
-===== 2. 현업 수치예보모델 =====+===== 2. 현업 수치예보모델 =====
 ==== 2.1. KIM-전구(KIM-Global) ==== ==== 2.1. KIM-전구(KIM-Global) ====
 === 2.1.1. 기본정보 === === 2.1.1. 기본정보 ===
줄 148: 줄 149:
 === 2.2.2 파일명 === === 2.2.2 파일명 ===
 r030_v040_ne36_{pres|unis|etas}_h{000~072}.yyyymmddhh.gb2 r030_v040_ne36_{pres|unis|etas}_h{000~072}.yyyymmddhh.gb2
 +
 +==== 2.3. 한국형 수치모델 grib파일별 격자 및 변수 참고 정보 ====
 +^ 구분 ^ 종류 ^ 파일명 ^ 격자 및 변수 정보 ^
 +|KIM 전구 | 등압면 | kim_g128_ne36_pres_h???.년월일시.gb2 |[[nwp_header_kim_g128_pres|KIM 전구 등압면]] |
 +|KIM 전구 | 단일면 | kim_g128_ne36_unis_h???.년월일시.gb2 |[[nwp_header_kim_g128_unis|KIM 전구 단일면]] |
 +|KIM 지역(12km) | 등압면 | kim_g120_ne36_pres_h???.년월일시.gb2 |[[nwp_header_kim_g120_pres|KIM 지역 등압면(12km)]] |
 +|KIM 지역(12km) | 단일면 | kim_g120_ne36_unis_h???.년월일시.gb2 |[[nwp_header_kim_g120_unis|KIM 지역 단일면(12km)]] |
 +|KIM 지역(3km) | 등압면 | r030_v040_ne36_pres_h???.년월일시.gb2|[[nwp_header_kim_r03_pres|KIM 지역 등압면(3km)]] |
 +|KIM 지역(3km) | 단일면 | r030_v040_ne36_unis_h???.년월일시.gb2|[[nwp_header_kim_r03_unis|KIM 지역 단일면(3km)]] |
 +
 ===== 3. 검색 및 획득 ===== ===== 3. 검색 및 획득 =====
  
줄 160: 줄 171:
   *  호출 방법: {{기상관측:지상:기상청_API허브_사용_안내서.pdf|기상청 API허브 사용자 안내서}}   *  호출 방법: {{기상관측:지상:기상청_API허브_사용_안내서.pdf|기상청 API허브 사용자 안내서}}
  
 +===== 4. 활용방안 =====
 +
 +소스코드: {{ vertical_cross_section.ipynb }}
 +
 +=== 4.1. 라이브러리 설치 ===
 +
 +<code py>
 +# API 요청을 위한 requests 라이브러리와
 +# 지도 표출을 위한 cartopy 라이브러리,
 +# 연직단면도 표출을 위한 metpy 라이브러리를 설치합니다.
 +%pip install requests cartopy metpy
 +</code>
 +
 +=== 4.2. KIM 모델 자료 다운로드 ===
 +
 +기상청 API허브에 회원가입 후 자신의 인증키를 사용하여 API 요청을 할 수 있습니다.
 +
 +API 인증키를 코드상에 직접 남겨 사용하는 방식은 다른 사람에게 코드를 공유할때 인증키가 노출되는 문제가 생길 수 있습니다.
 +
 +따라서 인증키를 환경변수로 등록하여 코드상에 노출되지 않도록 사용하는 것이 바람직합니다.
 +
 +<code py>
 +import os # 환경변수를 불러오는 표준 라이브러리
 +
 +# 바람직하지 않은 방법
 +# my_api_key = 'MY_PRIVATE_API_AUTHENTICATE_KEY'
 +
 +# 인증키를 'myapikey'라는 이름의 환경변수로 미리 등록해놓은 뒤 이를 불러와서 사용합니다.
 +my_api_key = os.environ['myapikey']
 +</code>
 +
 +<code py>
 +import requests     # API 요청을 보내고 받는 라이브러리
 +import numpy as np  # 배열을 다루는 라이브러리
 +
 +# KIM 자료를 txt파일로 조회하는 API url
 +api_url = 'https://apihub.kma.go.kr/api/typ06/cgi-bin/url/nph-kim_nc_xy_txt1'
 +
 +# 요청인자로 모델 구분, 모델 기반 종류, 자료 종류, 변수명, 고도,
 +# 사용영역 및 격자영역, 분석시간, 예측시각, 제공형태가 있습니다.
 +
 +# 1. 모델 구분으로 KIMG를 입력합니다.
 +group = 'KIMG'
 +
 +# 2. 모델 기반 종류로 NE36을 입력합니다.
 +nwp = 'NE36'
 +
 +# 3. 자료 종류로 등압면(P), 단일면(U), 단일면 강수/해면기압(A)이 있습니다.
 +# 여기선 등압면을 사용합니다.
 +data = 'P'
 +
 +# 4. 변수명을 입력합니다.
 +# 여기선 사용할 변수명으로 기온(Air Temperature, T)을 사용합니다.
 +name = 'T'
 +
 +# 5. 고도를 입력합니다.
 +# 여기선 등압면 24개 레벨을 모두 사용합니다.
 +pres_levels = np.array([
 +    1000, 975, 950, 925, 900, 875, 850, 800, 750, 700, 650, 600, 550,
 +    500, 450, 400, 350, 300, 250, 200, 150, 100, 70, 50
 +])
 +
 +# 6. 사용영역을 전체(F), 일부(S), LCC변환 일부(R)중 하나를 입력합니다.
 +# 여기선 동아시아 일부 영역을 사용하므로 S를 입력합니다.
 +# 동아시아 영역의 좌표를 입력하기 위해 전체 영역이 사용하는 좌표계를 이해해야 합니다.
 +# 전체 영역은 위도[-89.9375, 89.9375]를 1440개로 나누고 (0.125간격),
 +# 경도[0, 360)를 2880개로 나눈 격자 좌표(0.125간격)를 사용합니다.
 +# 동아시아 영역이 나타내는 범위를 위도 25.8125 ~ 50.3125, 경도 104 ~ 148로 가정합니다.
 +# 마지막으로 격자영역은 xmin, ymin, xmax, ymax 형태로 입력합니다.
 +map = 'S'
 +degree_interval = 0.125
 +lat_1, lon_1, lat_2, lon_2 = 25.8125, 104, 50.3125, 148
 +
 +# 격자 좌표를 계산하기위해 전체영역의 시작 위도를 더해줍니다.
 +# 그리고 첫 시작점을 1부터 시작시키며 정수로 변환합니다.
 +sub = (
 +    np.array([lon_1, lat_1 + 89.9375, lon_2, lat_2 + 89.9375]) / degree_interval + 1
 +).astype(int)
 +
 +# 7. 분석시간을 년월일시(UTC)로 입력합니다.
 +# 예시로 2023년 8월 9일 06시(UTC)를 입력합니다.
 +tmfc_utc = '2023080906'
 +
 +# 8. 예측시각을 입력합니다.
 +# 예시로 24시간 후의 예측을 입력합니다.
 +hf = 24
 +
 +# 9. 마지막으로 제공형태를 ASCI(A), Binary(B) 중 하나를 입력합니다.
 +# 활용 편의를 위해 이진형태의 파일을 선택합니다.
 +disp = 'B'
 +
 +# 파일 명명 규칙에 따라 24개 레벨의 파일 이름을 저장할 배열을 정의합니다.
 +# API 응답을 저장할 파일 형식은 bin 파일로 저장합니다.
 +file_name_list = np.array([
 +    f"{group.lower()}_{nwp.lower()}_pres_{name}_p{(level % 1000):03d}_h{hf:03d}.{tmfc_utc[:10]}.bin"
 +    for level in pres_levels
 +])
 +
 +# 파일 목록을 출력합니다.
 +file_name_list
 +</code>
 +       array(['kimg_ne36_pres_T_p000_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p975_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p950_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p925_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p900_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p875_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p850_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p800_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p750_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p700_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p650_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p600_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p550_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p500_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p450_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p400_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p350_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p300_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p250_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p200_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p150_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p100_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p070_h024.2023080906.bin',
 +              'kimg_ne36_pres_T_p050_h024.2023080906.bin'], dtype='<U41')
 +
 +<code py>
 +# 각 파일의 존재 여부를 확인합니다.
 +file_exists_list = np.vectorize(os.path.exists)(file_name_list)
 +
 +# 저장된 파일이 없는 경우에만 API를 요청해 파일을 다운로드합니다.
 +if not file_exists_list.all():
 +    # API 요청인자들을 묶어 dictionary로 정의합니다.
 +    api_parameters = {
 +        'group': group,
 +        'nwp': nwp,
 +        'data': data,
 +        'name': name,
 +        'map': map,
 +        'sub': ','.join([str(x) for x in sub]),
 +        'tmfc': tmfc_utc,
 +        'hf': hf,
 +        'disp': disp,
 +        'authKey': my_api_key
 +    }
 +    # 파일 다운로드를 위한 세션을 만듭니다.
 +    with requests.Session() as session:
 +        # 존재하지 않는 파일에 대해서 반복합니다.
 +        for idx in np.where(~file_exists_list)[0]:
 +            # API 요청인자에 레벨을 입력합니다.
 +            api_parameters['level'] = pres_levels[idx]
 +
 +            # API 요청인자와 함께 API 요청
 +            response = session.get(api_url, params=api_parameters)
 +
 +            # 잘못된 응답을 받거나 짧은 에러메세지를 응답으로 받은 경우 에러 메세지 출력
 +            if response.status_code != 200 or len(response.content) < 300:
 +                raise requests.RequestException(
 +                    response.content.decode('euc-kr')
 +                )
 +            else:
 +                # 그 외의 올바른 응답에 대해서만 파일로 저장합니다.
 +                with open(file_name_list[idx], 'wb') as f:
 +                    f.write(response.content)
 +                print(f'{file_name_list[idx]} 파일 다운로드 완료')
 +</code>
 +    kimg_ne36_pres_T_p000_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p975_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p950_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p925_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p900_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p875_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p850_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p800_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p750_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p700_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p650_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p600_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p550_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p500_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p450_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p400_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p350_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p300_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p250_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p200_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p150_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p100_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p070_h024.2023080906.bin 파일 다운로드 완료
 +    kimg_ne36_pres_T_p050_h024.2023080906.bin 파일 다운로드 완료
 +
 +=== 4.3. 연직단면도 표출 ===
 +
 +다운로드받은 24개 레벨의 파일을 읽어 하나의 xarray 데이터셋으로 합쳐서 다룹니다.
 +
 +<code py>
 +import xarray as xr # 자료를 다루기 위한 라이브러리
 +
 +# 24개 레벨의 파일을 읽어 xarray 데이터셋을 만듭니다.
 +# xarray 데이터셋은 크게 variables, coordinates, attributes로 구성되어 있습니다.
 +# attributes는 데이터에 사용된 각종 정보들을 나타냅니다. 
 +xr_array = xr.Dataset(
 +    # variable은 실제 데이터를 담고 있는 변수로 변수이름과 (차원, 데이터)를 dictionary 형태로 입력합니다.
 +    # 데이터는 파일로부터 읽어들이며 이진 파일의 앞의 4byte, 4byte는 데이터의 가로, 세로 격자수를 나타내며
 +    # 나머지 데이터는 4byte 소수형으로 읽어들입니다.
 +    data_vars={
 +        'temperature': (
 +            ['isobaric', 'lat', 'lon'], (data_array:=np.stack([
 +                np.fromfile(f, offset=8, dtype=np.float32).reshape(
 +                    sub[3] - sub[1] + 1, sub[2] - sub[0] + 1
 +                )
 +                for f in file_name_list]))
 +            )
 +        },
 +    # coordinates는 variable의 차원을 나타내는 변수입니다.
 +    # 앞서 variable의 차원부분에 입력한 이름에 맞게 실제 차원 수를 dictionary 형태로 입력합니다.
 +    coords={
 +        'isobaric': pres_levels,
 +        'lat': np.linspace(lat_1, lat_2, num=data_array.shape[1]),
 +        'lon': np.linspace(lon_1, lon_2, num=data_array.shape[2]),
 +    },
 +    # attributes는 데이터에 사용된 각종 정보들을 나타냅니다.
 +    # API 요청시 사용한 인자들을 dictionary 형태로 입력합니다.
 +    attrs={
 +        'group': group,
 +        'nwp': nwp,
 +        'data': data,
 +        'name': name,
 +        'map': map,
 +        'sub': ','.join([str(x) for x in sub]),
 +        'tmfc': tmfc_utc,
 +        'hf': hf,
 +    }
 +)
 +
 +# 생성한 xarray 데이터셋을 출력합니다.
 +xr_array
 +</code>
 +    <xarray.Dataset> Size: 7MB
 +    Dimensions:      (isobaric: 24, lat: 197, lon: 353)
 +    Coordinates:
 +    * isobaric     (isobaric) int64 192B 1000 975 950 925 900 ... 150 100 70 50
 +    * lat          (lat) float64 2kB 25.81 25.94 26.06 26.19 ... 50.06 50.19 50.31
 +    * lon          (lon) float64 3kB 104.0 104.1 104.2 104.4 ... 147.8 147.9 148.0
 +    Data variables:
 +        temperature  (isobaric, lat, lon) float32 7MB 305.4 305.2 ... 216.9 216.9
 +    Attributes:
 +        group:    KIMG
 +        nwp:      NE36
 +        data:     P
 +        name:     T
 +        map:      S
 +        sub:      833,927,1185,1123
 +        tmfc:     2023080906
 +        hf:       24
 +
 +연직단면도를 표출하기 앞서, 기압 레벨에 따른 한반도 지역의 기온 분포도를 표출합니다.
 +
 +<code py>
 +import matplotlib.pyplot as plt     # 이미지 표출에 사용되는 라이브러리
 +import cartopy.crs as ccrs          # 지도 표출에 사용되는 라이브러리
 +import cartopy.feature as cfeature  # 지도 표출에 사용되는 라이브러리
 +from cartopy.crs import PlateCarree # 지도 표출에 사용되는 라이브러리
 +
 +# 분포도에서 색상표 위치, 크기를 조절하는 함수
 +from mpl_toolkits.axes_grid1 import make_axes_locatable
 +
 +# 비교할 분포도는 1000hPa과 200hPa입니다.
 +# 분포도 내의 최대, 최소 값을 이용해 값의 범위를 구합니다.
 +isobaric_1 = 1000
 +isobaric_2 = 200
 +isobaric_1_arr = xr_array['temperature'][np.where(pres_levels == isobaric_1)[0][0]]
 +isobaric_2_arr = xr_array['temperature'][np.where(pres_levels == isobaric_2)[0][0]]
 +vmin1, vmax1 = isobaric_1_arr.min() - isobaric_1_arr.min() % 5, isobaric_1_arr.max() - isobaric_1_arr.max() % 5 + 5
 +vmin2, vmax2 = isobaric_2_arr.min() - isobaric_2_arr.min() % 5, isobaric_2_arr.max() - isobaric_2_arr.max() % 5 + 5
 +
 +# 이미지를 그릴 크기를 지정합니다.
 +kw = {'projection': PlateCarree()}
 +fig, axes = plt.subplots(1, 2, figsize=(15, 5), subplot_kw=kw)
 +fig.tight_layout(pad=3)
 +
 +# 이미지의 제목을 지정합니다.
 +fig.suptitle(f"Forecast after {hf} hours from {tmfc_utc}")
 +axes[0].set_title(f"Temperature at {isobaric_1}hPa")
 +axes[1].set_title(f"Temperature at {isobaric_2}hPa")
 +
 +# 이미지를 각 영역에 그립니다.
 +im1 = axes[0].imshow(isobaric_1_arr, extent=[lon_1, lon_2, lat_1, lat_2], cmap='turbo', origin='lower', vmin=vmin1, vmax=vmax1)
 +im2 = axes[1].imshow(isobaric_2_arr, extent=[lon_1, lon_2, lat_1, lat_2], cmap='turbo', origin='lower', vmin=vmin2, vmax=vmax2)
 +
 +# 이미지에 해안선과 경계를 표시합니다.
 +axes[0].coastlines()
 +axes[1].coastlines()
 +axes[0].add_feature(cfeature.STATES.with_scale('50m'), edgecolor='k', alpha=0.2, zorder=0)
 +axes[1].add_feature(cfeature.STATES.with_scale('50m'), edgecolor='k', alpha=0.2, zorder=0)
 +
 +# 색상표의 크기와 위치를 조절합니다.
 +divider1 = make_axes_locatable(axes[0])
 +cax1 = divider1.append_axes('right', size=0.2, pad=0, **kw)
 +divider2 = make_axes_locatable(axes[1])
 +cax2 = divider2.append_axes('right', size=0.2, pad=0, **kw)
 +
 +# 색상표에 표시될 글자 크기, 제목, 범위를 설정합니다.
 +cbar1 = fig.colorbar(im1, cax=cax1)
 +cbar2 = fig.colorbar(im2, cax=cax2)
 +cbar1.ax.tick_params(labelsize=8)
 +cbar1.ax.set_yticks(np.linspace(vmin1, vmax1, 11))
 +cbar1.ax.set_title('K', fontsize=8)
 +cbar2.ax.tick_params(labelsize=8)
 +cbar2.ax.set_yticks(np.linspace(vmin2, vmax2, 11))
 +cbar2.ax.set_title('K', fontsize=8)
 +
 +# 그린 이미지를 표출합니다.
 +plt.show()
 +</code>
 +
 +{{:수치모델:단중기모델:kim_forecast.png?nolink|}}
 +
 +마지막으로 연직단면도를 표출합니다.
 +
 +<code py>
 +from metpy.interpolate import cross_section # 연직단면도 계산을 위한 함수
 +from pyproj import CRS  # 좌표계 형식을 사용하기 위한 라이브러리
 +
 +
 +# 연직단면도를 위한 두 점의 좌표를 위·경도 좌표로 입력합니다.
 +start = (36.9, 120.7)
 +end = (37.5, 133.4)
 +
 +# metpy를 활용하기 위해 데이터가 좌표계(CRS)를 인식할 수 있도록 CRS를 등록(assign_crs)합니다.
 +xr_array = xr_array.metpy.assign_crs(CRS('EPSG:4326').to_cf())
 +
 +# 데이터를 두 점을 연결하는 선으로 자른 연직단면도로 변환합니다.
 +cross = cross_section(xr_array, start, end).set_coords(('lat', 'lon'))
 +
 +# 변환된 연직단면도 데이터를 표출합니다.
 +# 기온 변수의 차원이 lat, lon좌표에서 index(100개)로 변환된 것을 확인할 수 있습니다.
 +cross
 +</code>
 +
 +    <xarray.Dataset> Size: 22kB
 +    Dimensions:      (isobaric: 24, index: 100)
 +    Coordinates:
 +    * isobaric     (isobaric) int64 192B 1000 975 950 925 900 ... 150 100 70 50
 +        metpy_crs    object 8B Projection: latitude_longitude
 +        lon          (index) float64 800B 120.7 120.8 121.0 ... 133.1 133.3 133.4
 +        lat          (index) float64 800B 36.9 36.91 36.93 36.94 ... 37.5 37.5 37.5
 +    * index        (index) int64 800B 0 1 2 3 4 5 6 7 ... 92 93 94 95 96 97 98 99
 +    Data variables:
 +        temperature  (isobaric, index) float64 19kB 303.6 303.5 ... 211.0 210.7
 +    Attributes:
 +        group:    KIMG
 +        nwp:      NE36
 +        data:     P
 +        name:     T
 +        map:      S
 +        sub:      833,927,1185,1123
 +        tmfc:     2023080906
 +        hf:       24
 +
 +<code py>
 +# 연직단면도를 그릴 크기를 지정합니다.
 +fig, ax = plt.subplots(figsize=(16, 9))
 +
 +# 기온을 표출하기 위해 등치선을 채우는 방식(filled contour, contourf)을 사용합니다.
 +# 레벨은 최소값과 최대값을 이용해 구합니다.
 +temperature_contour = ax.contourf(
 +    cross['lon'], cross['isobaric'], cross['temperature'], cmap='turbo',
 +    levels=np.arange(
 +        cross['temperature'].min() - cross['temperature'].min() % 10,
 +        cross['temperature'].max() - cross['temperature'].max() % 10 + 10, 5
 +    )
 +)
 +# 등치선의 색상표를 표출합니다.
 +temperature_colorbar = fig.colorbar(temperature_contour, pad=0)
 +temperature_colorbar.ax.set_title('K')
 +
 +# Y축을 로그 스케일(log scale)로 변환합니다. 또, 표출할 범위를 지정합니다.
 +ax.set_yscale('symlog')
 +ax.set_ylim(cross['isobaric'].max(), cross['isobaric'].min())
 +ax.set_yticks(np.arange(1000, 50, -100))
 +ax.set_yticklabels(np.arange(1000, 50, -100))
 +
 +# 데이터의 좌표계를 정의하고, 지도를 오른쪽 상단에 표시합니다.
 +data_crs = cross['temperature'].metpy.cartopy_crs
 +ax_inset = fig.add_axes([0.85, 0.65, 0.25, 0.25], projection=data_crs)
 +
 +# start, end 두 점의 위·경도 좌표를 지도가 사용하는 좌표계로 변환하여 지도에 표출합니다.
 +# 또, 앞서 그린 분포도를 표출합니다.
 +endpoints = data_crs.transform_points(
 +    ccrs.Geodetic(), *np.vstack([start, end]).transpose()[::-1]
 +)
 +ax_inset.imshow(isobaric_2_arr, origin='lower', extent=[lon_1, lon_2, lat_1, lat_2], cmap='turbo', vmin=vmin2, vmax=vmax2)
 +ax_inset.scatter(endpoints[:, 0], endpoints[:, 1], c='k', zorder=2)
 +ax_inset.plot(cross['lon'], cross['lat'], c='k', zorder=2)
 +
 +# 지도에 해안선 및 경계를 표출합니다.
 +ax_inset.coastlines()
 +ax_inset.add_feature(cfeature.STATES.with_scale('50m'), edgecolor='k', alpha=0.2, zorder=0)
 +
 +# 단면도의 제목과 축의 라벨을 지정합니다.
 +ax_inset.set_title('')
 +ax.set_title(f'Forecast after {hf} hours from {tmfc_utc} Cross-Section {start} to {end} Temperature(K)')
 +ax.set_ylabel('Pressure (hPa)')
 +ax.set_xlabel('Longitude (degrees east)')
 +temperature_colorbar.set_label('Temperature (dimensionless)')
 +
 +# 단면도를 표출합니다.
 +plt.show()
 +</code>
  
 +{{:수치모델:단중기모델:vertical_cross_section.png?nolink|}}