개요
NetCDF나 Grib2 데이터에는 다차원 정보를 담을 수 있습니다.
이번에는 netcdf의 3차원(경도, 위도, level-높이) 정보를 Cesium.js를 이용하여 3차원 시각화 테스트해봤습니다.
데이터
좌표와 높이, 측정값이 포함된 데이터를 구글링하다가 경도, 위도, 높이 Level, 측정값이 들어있는 netcdf를 찾았습니다.
데이터 가공
Cesium.js 시각화 및 이를 위한 데이터 가공에서는 ChatGPT의 도움을 많이 받았습니다.
Cesium.js에서 netcdf를 바로 쓰기 어렵고 json으로 변환된 정보를 이용하는 게 좋겠다는 권고와 제안 코드를 이용하여 파이썬에서 netcdf를 json으로 변환하였습니다.
# 필요한 패키지 로딩
import numpy as np
import xarray as xr
import json
# NetCDF 또는 GRIB2 파일을 로드합니다.
ds = xr.open_dataset("data/ta_Amon_GFDL-ESM2G_historical_r1i1p1_200101-200512.nc")
# 각 차원 및 온도 값들을 1차원 배열 형태로 받음
lon = ds.variables['lon'][:]
lat = ds.variables['lat'][:]
level = ds.variables['plev'][:]
air = ds.variables['ta'][:][0, :, :]
# 데이터를 JSON 형식으로 변환
data = []
for i in range(len(level)):
for j in range(len(lat)):
for k in range(len(lon)):
air_value = air[i,j,k]
# NaN 값을 걸러냄
if not np.isnan(air_value):
data_point = {"level": int(level[i]), "lat": float(lat[j]), "lon": float(lon[k]), "air": float(air_value)}
data.append(data_point)
# JSON 파일로 저장
with open('air_data.json', 'w') as outfile:
json.dump(data, outfile)
시각화
아래와 같이 html을 작성합니다. head에서는 cesium.js를 로딩합니다. 아래는 body 부분만 담았습니다.
<body>
<div id="cesiumContainer"></div>
<script>
Cesium.Ion.defaultAccessToken = '세슘 토큰';
var viewer = new Cesium.Viewer('cesiumContainer', {
});
// JSON 파일에서 데이터 불러오기
fetch('data/air_data.json')
.then(function(response) {
return response.json();
})
.then(function(jsonData) {
// 최소, 최대 air 값을 구하기
var minAir = jsonData[0].air;
var maxAir = jsonData[0].air;
for (var i = 1; i < jsonData.length; i++) {
var airValue = jsonData[i].air;
if (airValue < minAir) {
minAir = airValue;
} else if (airValue > maxAir) {
maxAir = airValue;
}
}
// 각 데이터 포인트를 Cesium의 Entity로 추가
for (var i = 0; i < jsonData.length; i++) {
var dataPoint = jsonData[i];
// air 값에 따른 색상 계산
var airRatio = (dataPoint.air - minAir) / (maxAir - minAir);
var color = new Cesium.Color(airRatio, 0, 1 - airRatio, 1);
viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(dataPoint.lon, dataPoint.lat, dataPoint.level),
point : {
pixelSize : 20,
color : color,
outlineColor : Cesium.Color.BLACK,
outlineWidth : 0,
heightReference : Cesium.HeightReference.RELATIVE_TO_GROUND,
material: new Cesium.ColorMaterialProperty(color)
},
air: dataPoint.air
});
}
});
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(123, 31, 150000),
orientation : {
heading : Cesium.Math.toRadians(40), // 동서방향 각도, 동쪽부터 시계방향으로 각도 지정
pitch : Cesium.Math.toRadians(-20.0), // 위아래 각도, 수평선 위가 양수, 아래가 음수
roll : 0.0 // 카메라 회전 각도, 시계방향이 양수
}
});
</script>
</div>
</body>
동일한 데이터를 3D Box를 이용하면 cube와 유사한 형태로 표시할 수도 있습니다.
정리
netcdf를 세슘에서 3차원 시각화하는 것을 테스트해봤습니다. 더 나아가서는 시간 차원(time)의 변화까지 반영하여 4차원 시각화도 가능할 것으로 보여집니다.