gk2a RGB 합성 그림

gk2a RGB 합성 그림

in

GK2A RGB 합성 그림을 그려봅시다.

먼저 자료를 다운 받아야합니다. 우리는 level 2자료를 사용합니다. 자료는 기상청 홈페이지에서도 다운받을 수 있습니다.

필요 라이브러리 설치 및 구글 드라이브 연결

google colab에서 작업을 할 겁니다. 위에 자료들을 구글드라이브에 넣고 연결해줍니다.

!pip install netCDF4
!pip install cartopy

netCDF4와 cartopy는 필요한 라이브러리 이름입니다. cartopy는 주로 지도를 그릴 때 사용합니다.

from google.colab import drive
drive.mount('/content/drive')

본인의 드라이브주소를 복사해서 넣어줍니다. 본인 구글 드라이브와 연결하는 과정입니다.

import netCDF4 as nc
import pandas as pd
import numpy as np
import xarray as xr
import seaborn as sns
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cv2

from matplotlib.patches import Circle
import matplotlib.colors as mcolors

라이브러리를 어떻게 부를지 짧게 줄여놨습니다.

RGB 함수 정의

def bystscl(data, min_val, max_val, top):

    #Scale the data based on input type (integer or float)
    if np.issubdtype(data.dtype, np.integer):
        scaled_data = ((top + 1) * (data - min_val - 1) / (max_val - min_val)).astype(np.int16)
    elif np.issubdtype(data.dtype, np.floating):
        scaled_data = (((top + 0.9999) * (data - min_val)) / (max_val - min_val)).astype(np.int16)
    else:
        raise ValueError("Input data type not supported.")


    #Clip values to the byte range [0, top]
    scaled_data = np.clip(scaled_data, 0, top)

    return scaled_data

함수를 정의해줍니다. if문을 사용해 데이터가 원하는 범위 내에 있는 게 아니면 버려줍니다. bytscl함수는 array를 byte 데이터 유형으로 확장하고 변환하는데 사용합니다. max는 고려할 배열 요소의 최대값, min은 최소값, top은 조정된 결과의 최대값입니다. 값들을 0~255까지 정규화 시켜줍니다. int16은 데이터 형식의 범위네요.

longitude, latitude 설정

#lon, lat 정의

lonlat_file_name = "/content/drive/MyDrive/gk2a/GK2A_RGB/gk2a_ami_ko010lc_latlon.nc"

lonlat = nc.Dataset(lonlat_file_name, 'r', format='netcdf4')
lonlat

lon = pd.DataFrame(lonlat.variables['lon'][:])
lon = np.array(lon).flatten()
lon[lon == -999] = np.nan

lat = pd.DataFrame(lonlat.variables["lat"][:])
lat = np.array(lat).flatten()
lat[lat == -999] = np.nan

print(lon.shape)
print(lat.shape)

드라이브 주소는 꼭 본인의 위치로 정리해줍니다. 위도, 경도를 정리해줬습니다. flatten은 array를 1차원으로 바꾸어주는 역할을 합니다. -999로 표시된 값은 누락된 값이므로 np.nan으로 바꾸어 줍니다. print하여 중간중간 크기를 확인해줍니다. 위도, 경도의 크기가 같아야합니다.

채널별 설정

#Blue (vi004)
B_file_name = "/content/drive/MyDrive/gk2a/GK2A_RGB/gk2a_ami_le1b_vi004_ko010lc_202305240450.nc"

ds = xr.open_dataset(B_file_name, engine = "netcdf4")

data_variables_list = list(ds.data_vars)
#data_variables_list

var_name = "image_pixel_values"

var_2d = np.array(ds.data_vars[var_name])

var_1d_blue = var_2d.flatten().astype(float)

var_1d_blue
var_1d_blue.shape

var_1d_blue[var_1d_blue == 32768] = np.nan

데이터를 여는데 모두 netcdf4를 써주네요. astype은 dtype을 변경하는 함수입니다. 이걸 R, G, B 3가지 모두 해줍니다.

#Green (vi005)
G_file_name = "/content/drive/MyDrive/gk2a/GK2A_RGB/gk2a_ami_le1b_vi005_ko010lc_202305240450.nc"

ds = xr.open_dataset(G_file_name, engine = "netcdf4")

data_variables_list = list(ds.data_vars)
#data_variables_list

var_name = "image_pixel_values"

var_2d = np.array(ds.data_vars[var_name])

var_1d_green = var_2d.flatten().astype(float)

var_1d_green
var_1d_green.shape

var_1d_green[var_1d_green == 32768] = np.nan

#Red (vi006)
R_file_name = "/content/drive/MyDrive/gk2a/GK2A_RGB/gk2a_ami_le1b_vi006_ko005lc_202305240450.nc"

ds = xr.open_dataset(R_file_name, engine = "netcdf4")

data_variables_list = list(ds.data_vars)
#data_variables_list

var_name = "image_pixel_values"

var_2d = np.array(ds.data_vars[var_name]).astype(float)

var_2d[var_2d == 32768] = np.nan

var_2d2 = cv2.resize(var_2d, dsize = (1800, 1800), interpolation = cv2.INTER_AREA)

var_1d_red = var_2d2.flatten().astype(float)

var_1d_red
#print(var_1d_red.shape)

var_1d_red[var_1d_red == 32768] = np.nan

var_2d의 크기를 지정해줍니다. np.nan은 누락된 값을 나타냅니다. None과 헷갈리기 쉬운데, None은 값이 존재하지 않음을 나타냅니다.

데이터 프레임 만들기

df2 = pd.DataFrame({'lon': lon, 'lat': lat,
                    'blue': var_1d_blue,
                    'green': var_1d_green,
                    'red': var_1d_red,
                    })

lon, lat, blue, green, red라는 값으로 표를 만들어 줬습니다. df2를 그리면 3240000줄이나 되는 표가 나옵니다. 확인해가며 진행해 봅시다.

DN to Radiance

df2["rad_blue"] = df2["blue"] * 0.363545805315835 -7.27090454101562
df2["rad_green"] = df2["green"] * 0.343625485897064 -6.87249755859375
df2["rad_red"] = df2["red"] * 0.154856294393539 -6.19424438476562

rad_blue, rad_green, rad_red라는 새로운 값을 만들어 줬습니다. 옆에 숫자는 값을 보정해주기 위해 쓰입니다. 이제 다시 df2를 그리면 rad_blue, rad_green, rad_red 행이 추가된 것을 알 수 있습니다.

Radiance to RGB

red_1 = bystscl(df2['rad_red'].values, min_val = 0, max_val = 300, top = 255)
green_1 = bystscl(df2['rad_green'].values, min_val = 0, max_val = 300, top = 255)
blue_1 = bystscl(df2['rad_blue'].values, min_val = 0, max_val = 300, top = 255)

df2["R"] = red_1
df2["G"] = green_1
df2["B"] = blue_1

보정된 R, G, B의 크기를 정리해줍니다.

#데이터 프레임에서 R, G, B 컬럼을 추출합니다.
R_channel = df2["R"].values
G_channel = df2["G"].values
B_channel = df2["B"].values

#R, G, B 값을 0~1 사이로 정규화 합니다.
R_normalized = R_channel / 255.0
G_normalized = G_channel / 255.0
B_normalized = B_channel / 255.0

#데이터 프레임에서 lon과 lat 칼럼을 추출합니다.
lon = df2["lon"].values
lat = df2["lat"].values

#색상 데이터를 합성하여 RGB 배열을 생성합니다.
colors = np.dstack((R_normalized, G_normalized, B_normalized))

#3D 배열에서 첫번쨰 차원을 제거하여 2D 배열로 변경합니다.
colors_2d = colors[0]

한반도 지역 Plot

%%time
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(1,1,1, projection=ccrs.PlateCarree())

ax.coastlines(resolution='10m', color='yellow', linewidth=1)

gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
                  linewidth=1, color= 'gray', alpha=0.5, linestyle='--')
gl.xlabels_top = False
gl.xline = True
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER

ax.scatter(lon, lat, c=colors_2d,
           s = 1,
           marker = ",",
           zorder = 1,
           alpha = 1,
           transform=ccrs.PlateCarree(central_longitude=0)
           )

ax.set_xlim(124,130)
ax.set_ylim(33, 40)

plt.title("RGB", color="black", loc="right", size=10)
ax.set_title("GK2A", fontsize=15)
ax.set_title('202305240450', loc = "left", fontsize=10)

x_center = 36.3740
y_center = 127.3525
radius = 0.1

circle = Circle((y_center, x_center), radius, transform=ccrs.PlateCarree(central_longitude=0), linewidth=2, edgecolor="red", facecolor="none" )
ax.add_patch(circle)

드디어 그림을 그렸습니다! 위도, 경도를 회색 점선으로 나타내고 대한민국 지도는 노란색으로 그렸습니다. 대전에 빨간색 동그라미를 해놨습니다. 위치와 크기, 색도 변경할 수 있습니다. 하지만 기상청 자료와 비교했을 때 똑같게 보이려면 이게 좋겠죠.

우주과학 및 실습 시간에 조교 실습으로 진행한 위성자료 그리기입니다. 주어진 자료를 가지고 나도 그림을 그릴 수 있다니! 다들 배워보면 좋겠습니다.