REMOTE SENSING

Google Earth Engine: 이미지를 Google Drive로 내보내는 방법 소개

유병혁 2024. 8. 13. 22:19

안녕하세요? 이번 글은 Google Earth Engine 이미지를 Google Drive로 내보내는 방법을 소개해 보겠습니다. 이어서 내보낸 이미지를 다시 읽어와 플롯도 그려보겠습니다.

 

먼저, Earth Engine을 인증 및 초기화합니다.

import ee

# Earth Engine 인증
ee.Authenticate()

# Earth Engine 초기화
ee.Initialize(project='ee-foss4g')

 

저는 Google Colab을 사용하고 있는데요, 이번 실습은 일부는 rasterio를 사용하고 있어서 해당 패키지를 설치해주도록 하겠습니다.

!pip install -q -U rasterio

 

이어서 실습에 필요한 패키지들을 불러옵니다.

import geemap
import geemap.colormaps as cm
import os, requests
import pandas as pd, geopandas as gpd
import matplotlib.pyplot as plt
import rasterio
import numpy as np

from google.colab import files
from google.colab import drive

 

첫번째로 대한민국 사각 영역을 "FeatureCollection"으로 정의해 보겠습니다. 해당 범위는 이미지를 잘라낼 때 사용합니다.

# 대한민국 범위 좌표 지정(좌하단 : 우상단 좌표)
rectangle_coords = [
    [125.0765578311700068, 33.1124998462386984],
    [131.8727812628719960, 38.4000004985049017],
]

# 직사각형 생성
rectangle = ee.Geometry.Rectangle(rectangle_coords)

# 직사각형을 Feature로 변환
feature = ee.Feature(rectangle)

# FeatureCollection 생성
kr = ee.FeatureCollection([feature])

m = geemap.Map(layout={'height':'400px', 'width':'800px'})
m.addLayer(kr, {}, "Republic of Korea")
m.centerObject(kr, 6)
m

 

이미지는 NASA SRTM Digital Elevation 30m를 선택하겠습니다. Elevation이 0보다 큰 범위를 수역 마스크로 하여 DEM을 마스킹하고 대한민국 범위로 잘라내 줍니다.

# NASA SRTM Digital Elevation 30m
elevation = ee.Image("USGS/SRTMGL1_003").select("elevation")

# 수역 마스크 생성
watermask = elevation.gt(0)
elevation = elevation.updateMask(watermask).clip(kr.geometry())

# Elevation layer
Map = geemap.Map(layout={"height": "400px", "width": "800px"})

vis_params = {
    "bands": ["elevation"],
    "min": 0,
    "max": 1800,
    "palette": cm.palettes.terrain,
}
Map.addLayer(elevation, vis_params, "Elevation")
Map.add_colorbar(
    vis_params, label="Elevation (m)", orientation="vertical", layer_name="elevation"
)
Map.centerObject(kr.geometry(), 6)
Map

 

이제 이 이미지를 Google Drive로 내보내는 방법입니다. Google Drive 내 원하는 폴더에 해당 이미지를 이름, 해상도, 영역, 파일 포맷 등을 설정해서 내보낼 수 있습니다. 아래 코드는 60초마다 작업 상태를 확인할 수 있도록 출력문을 설정하였습니다.

%%time

import time

# 이미지를 Google Drive에 내보내기
task = ee.batch.Export.image.toDrive(
    image=elevation,
    description='DEM',
    folder='kari-sdm',
    scale=30,  # 이미지의 해상도
    region=kr.geometry(),  # 내보낼 영역
    maxPixels=1e13,
    fileFormat='GeoTIFF'
)

# 내보내기 작업 시작
task.start()

# 내보내기 작업 상태 확인
print('Export task started. Checking status...')

while True:
    status = task.status()
    state = status['state']
    print('Polling for task (id: {}). Status: {}'.format(task.id, state))
    if state in ['COMPLETED', 'FAILED']:
        break
    time.sleep(60)  # 60초 간격으로 상태 확인

# 완료 후 상태 출력
print('Task completed. Final status:')
print(status)

 

9분 4초 후에 해당 작업 완료 메시지를 확인하였습니다. 아래와 같이 DEM.tif가 Google Drive에 저장된 것을 확인할 수 있습니다. 이번에는 Google Drive를 마운트해서 해당 이미지를 바로 읽어와 보겠습니다.

# Google Drive를 마운트
drive.mount('/content/drive')

 

대한민국 경계와 함께 해당 이미지를 읽어와 데이터 플롯을 그려봅니다. 결과는 아래와 같습니다. 공유드린 코드를 재사용해서 Google Earth Engine 이미지를 가져올 수 있습니다.

# 경로 설정
folder_path = "/content/drive/My Drive/kari-sdm"
dem_path = os.path.join(folder_path, "DEM.tif")
kr_path = os.path.join(folder_path, "KR.gpkg")

# 대한민국 경계 읽기
kr = gpd.read_file(kr_path)

# DEM 데이터 읽기
with rasterio.open(dem_path) as src:
    dem_data = src.read(1)  # 첫 번째 밴드 읽기
    dem_extent = src.bounds  # DEM의 범위 가져오기
    dem_transform = src.transform  # 변환 정보 가져오기
    dem_meta = src.meta.copy()  # 메타데이터 복사

# 픽셀 값이 0인 부분을 마스킹
dem_data_masked = np.ma.masked_equal(dem_data, 0)

# 플롯 설정
fig, ax = plt.subplots(figsize=(6, 6))

# DEM 데이터 플롯 (마스킹 적용)
dem_img = ax.imshow(
    dem_data_masked,
    cmap="terrain",
    extent=[dem_extent.left, dem_extent.right, dem_extent.bottom, dem_extent.top],
    origin="upper"
)

# 대한민국 경계 플롯
kr.plot(ax=ax, color="none", edgecolor="black")

# 컬러바(범례) 추가
cbar = fig.colorbar(dem_img, ax=ax, orientation='vertical', shrink=0.5)
cbar.set_label('Elevation (meters)')

# 제목, 축 레이블, 그리드 설정
plt.title("Elevation")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.grid(True)

# 좌표 비율 유지
ax.set_aspect("equal", "box")

# 플롯 표시
plt.show()