안녕하세요? 이번 글은 앞서 소개드린 SMAP 위성 염분(Salinity) 지도 데이터를 QGIS에서 열어보도록 하겠습니다. 아래 링크 글을 먼저 읽어보시면 좋겠습니다.
SMAP 위성 염분(Salinity) 지도 데이터 소개
안녕하세요? 이번 글은 SMAP 위성으로 관측한 염분(Salinity) 지도 데이터를 소개해 보겠습니다. 공식 웹페이지에 정리된 내용들을 한국어 번역 및 재정리했습니다.염분(Salinity)이란? NASA Salinity: Salin
foss4g.tistory.com
해수면 염분 레이어 열기
이전 글에서 다운로드 받은 아래 파일을 QGIS에서 열어보겠습니다.
SMAP의 공간 해상도는 40km로, 기존의 Aquarius 임무가 제공하던 150km 해상도에 비해 향상된 성능을 보입니다. 그러나 40km 해상도의 SMAP 자료는 Aquarius에 비해 상대적으로 잡음이 많아, 이러한 잡음을 줄이기 위해 공간적으로 평균화된 70km 해상도의 자료도 함께 제공됩니다. 여기서는 sss_smap, 약 70km 해상도로 평활화된 SMAP 해수면 염분(sss: sea surface salinity) 레이어를 열어보겠습니다.
해수면 염분 레이어 범위 재설정
레이어 좌표계는 EPSG:4326으로 정의합니다. Google Satellite 레이어와 함께 중첩한 상태는 아래와 같습니다. sss_smap 레이어의 경도 좌표는 0에서 360으로 설정되어 있어, 이를 -180에서 180 범위로 재설정해야 합니다. 일반적으로는 CDO나 xarray 등을 사용하여 조정하지만, 여기서는 QGIS 파이썬 콘솔에서 gdal_translate와 gdal_merge를 이용해 자체 변경해 보겠습니다.
일단, NetCDF에서 180~360 범위를 잘라내서 -180~0 범위로 이동하겠습니다.
import processing
# 입력 파일
nc_file = r'D:\GEODATA\RSS_smap_SSS_L3_8day_running_2025_094_FNL_v06.0.nc'
var = 'sss_smap'
tif_180_360 = r'D:\GEODATA\sss_smap_180_360.tif'
tif_shifted = r'D:\GEODATA\sss_smap_left.tif'
# NetCDF에서 180~360 범위 잘라내기
processing.run("gdal:translate", {
'INPUT': f'NETCDF:"{nc_file}":{var}',
'TARGET_CRS': 'EPSG:4326',
'EXTRA': '-projwin 180 90 360 -90',
'OUTPUT': tif_180_360
})
print("잘라낸 범위 저장 완료:", tif_180_360)
# 좌표를 -180~0으로 덮어쓰기
processing.run("gdal:translate", {
'INPUT': tif_180_360,
'TARGET_CRS': 'EPSG:4326',
'EXTRA': '-a_ullr -180 90 0 -90',
'OUTPUT': tif_shifted
})
print("좌표 이동 완료:", tif_shifted)
이어서 0~180 범위도 잘라냅니다.
# 입력 파일
nc_file = r'D:\GEODATA\RSS_smap_SSS_L3_8day_running_2025_094_FNL_v06.0.nc'
var = 'sss_smap'
tif_0_180 = r'D:\GEODATA\sss_smap_right.tif'
# NetCDF에서 0~180 범위 잘라내기
processing.run("gdal:translate", {
'INPUT': f'NETCDF:"{input_nc}":{var}',
'TARGET_CRS': 'EPSG:4326',
'EXTRA': '-projwin 0 90 180 -90',
'OUTPUT': tif_0_180
})
print("잘라낸 범위 저장 완료:", tif_0_180)
이제 두 레이어를 하나의 파일로 병합하겠습니다.
from qgis.core import QgsRasterLayer, QgsProject
# 입력 파일
left = r'D:\GEODATA\sss_smap_left.tif'
right = r'D:\GEODATA\sss_smap_right.tif'
merged = r'D:\GEODATA\sss_smap_2025_094.tif'
# 병합
processing.run("gdal:merge", {
'INPUT': [left, right],
'SEPARATE': False,
'NODATA_INPUT': -9999,
'NODATA_OUTPUT': -9999,
'OUTPUT': merged
})
print("병합 완료:", merged)
# 레이어 추가
layer = QgsRasterLayer(merged, "sss_smap_2025_094")
if layer.isValid():
QgsProject.instance().addMapLayer(layer)
print("레이어 추가 성공")
else:
print("레이어 추가 실패")
SMAP 위성 염분 지도 데이터의 표준 시각화 자료를 참고하여, Min과 Max는 33~38도 범위로 설정하고 색상 램프는 Turbo로 선택해 봤습니다.
결과는 다음과 같습니다. 단위는 PSS(Practical Salinity Scale, 실용 염분 척도; Lewis, 1980)를 사용하여 염분의 범위를 시각적으로 보여줍니다. PSS는 대략적으로 천분율(parts per thousand)에 해당하며, PSS와 PSU(Practical Salinity Units, 실용 염분 단위)는 같은 개념을 지칭하는 다른 이름입니다. 지도에서 붉은 색은 염분이 높은 지역(38 psu)을, 보라색은 상대적으로 염분이 낮은 지역(33 psu)을 나타냅니다.
향후 코드 재사용을 위해 실습 내용을 함수로 재정리했습니다.
import processing
import os
from qgis.core import QgsRasterLayer, QgsProject
def convert_nc_to_180_projection(nc_file, var, output_path):
base_dir = os.path.dirname(output_path)
temp_180_360 = os.path.join(base_dir, 'temp_180_360.tif')
temp_left = os.path.join(base_dir, 'temp_left.tif')
temp_right = os.path.join(base_dir, 'temp_right.tif')
# 1. 180~360 범위 잘라내기
processing.run("gdal:translate", {
'INPUT': f'NETCDF:"{nc_file}":{var}',
'TARGET_CRS': 'EPSG:4326',
'EXTRA': '-projwin 180 90 360 -90',
'OUTPUT': temp_180_360
})
# 2. -180~0으로 덮어쓰기
processing.run("gdal:translate", {
'INPUT': temp_180_360,
'TARGET_CRS': 'EPSG:4326',
'EXTRA': '-a_ullr -180 90 0 -90',
'OUTPUT': temp_left
})
# 3. 0~180 범위 잘라내기
processing.run("gdal:translate", {
'INPUT': f'NETCDF:"{nc_file}":{var}',
'TARGET_CRS': 'EPSG:4326',
'EXTRA': '-projwin 0 90 180 -90',
'OUTPUT': temp_right
})
# 4. 병합
processing.run("gdal:merge", {
'INPUT': [temp_left, temp_right],
'SEPARATE': False,
'NODATA_INPUT': -9999,
'NODATA_OUTPUT': -9999,
'OUTPUT': output_path
})
print("병합 완료:", output_path)
# 5. 레이어 추가
layer = QgsRasterLayer(output_path, os.path.basename(output_path))
if layer.isValid():
QgsProject.instance().addMapLayer(layer)
print("레이어 추가 성공")
else:
print("레이어 추가 실패")
# 임시 파일 삭제
os.remove(temp_180_360)
os.remove(temp_left)
os.remove(temp_right)
convert_nc_to_180_projection(
nc_file=r'D:\GEODATA\RSS_smap_SSS_L3_8day_running_2025_094_FNL_v06.0.nc',
var='sss_smap',
output_path=r'D:\GEODATA\sss_smap_2025_094.tif'
)