Post

Earth Engine과 Streamlit

본 문서는 Python에서 Earth Engine API를 사용해 NDVI 시각화 Streamlit 앱을 만드는 방법을 담고 있다. 앱에는 NDVI 시계열 차트와 가장 최근 NDVI 지도를 담을 것이다.

1. NDVI 값 구하기

1-1. 지점과 위성영상 Import

MultiPolygon을 생성해서 roi를 지정하고, 필요한 위성영상을 불러온다. NDVI 값을 구할 것이기 때문에 LANDSAT8을 Import했다. LANDSAT9, Sentinel 영상을 가져와도 된다. 그리고 NDVI 변화 추이를 보여줄 수 있는 시계열 차트를 그릴 것이기 때문에 2020년부터의 Imagecollection을 가져왔다. 마지막으로 NDVI 값을 구하는 데 필요한 B4와 B4 밴드를 선택하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
# 지점 설정
roi = ee.Geometry.MultiPolygon(
    [[[128.4289105328613, 38.215684321736994],
      [128.39801148500973, 38.14767698416663],
      [128.49894837465817, 38.13363582412555],
      [128.51817444887692, 38.19949785659509],
      [128.4289105328613, 38.215684321736994], ]]
)
# 위성영상 불러오기: 2020년~, B5 & B4 밴드(NDVI 계산 위해)
collection = ee.ImageCollection('LANDSAT/LC08/C02/T1') \
    .filterBounds(roi) \
    .filterDate('2020-01-01', str(today)) \
    .select(['B5', 'B4'])

1-2. NDVI 밴드와 데이터프레임

normalizedDifference를 사용하여 각 이미지에 NDVI 밴드를 추가해주었다. ImageCollection에는 2020년부터 주기별로 찍은 이미지의 묶음이기 때문에 ‘각 이미지’에 NDVI 밴드를 추가한 것이다.

1
2
3
4
def addNDVI(image, band):
    return image.addBands(image.normalizedDifference(band).rename('NDVI'))

collection = collection.map(lambda image: addNDVI(image, ['B5', 'B4']))

시계열 차트를 그리기 위해서는 하나의 값만 필요하다. 그런데 이 이미지에는 여러 개의 값이 들어있다. 따라서 이 지역의 NDVI 평균값을 구할 것이다. 그리고 날짜와 NDVI 값을 담은 데이터프레임으로 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
def reduceToMean(image, roi):
    mean = image.reduceRegion(reducer=ee.Reducer.mean(), geometry=roi, scale=30)
    ndvi_mean = mean.get('NDVI')
    return ee.Image(image.select('NDVI').set('NDVI_mean', ndvi_mean))

ndvi_lst = collection.map(lambda image: reduceToMean(image, roi)).reduceColumns(ee.Reducer.toList(2), ['system:time_start', 'NDVI_mean']) \
    .get('list') \
    .getInfo()

df = pd.DataFrame(ndvi_lst, columns=['date', 'ndvi'])
df['date'] = pd.to_datetime(df['date'], unit='ms').dt.strftime('%Y-%m-%d')
df = df.sort_values(by='date').reset_index()

그림1

1-3. NDVI 시계열 차트

plotly를 사용하여 그려본다. 마우스 커서를 그래프 위에 올리면 값을 확인할 수 있다는 장점이 있다.

1
2
3
4
5
fig = go.Figure(data=[go.Scatter(x=df["date"], y=df['ndvi'], name='ndvi')])
fig.update_layout(title_text=f'NDVI')
fig.update_xaxes(title_text='날짜')
fig.update_yaxes(title_text=f'NDVI')
st.plotly_chart(fig, use_container_width=True)

그림1

1-4. 가장 최근 NDVI 지도

Landsat 위성의 촬영주기는 16일이기 때문에 오늘의 영상을 가져오는 것은 불가능하다. 때문에 가장 최근의 지도를 띄울 것이다. 배경지도를 Google의 위성영상으로 설정하고, 지도의 중심을 roi로 잡는다. 그리고 이 지도에 가장 최근의 NDVI layer를 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
tiles = "http://mt0.google.com/vt/lyrs=s&hl=ko&x={x}&y={y}&z={z}"
attr = "Google"
figure = folium.Figure()

m = folium.Map(
    location=[38.18, 128.45],
    zoom_start=15,
    tiles=tiles,
    attr=attr)

m.add_to(figure)

vis_paramsNDVI = {
    'min': 0,
    'max': 1,
    'palette': ['brown', 'yellow', 'green', 'blue']
}

# 날짜 순으로 정렬하고 가장 최근 이미지를 가져온 뒤, roi로 자름
ndvi_values = collection.select('NDVI').sort('system:time_start', False).first().clip(roi)
map_dict = ee.Image(ndvi_values).getMapId(vis_paramsNDVI)

folium.raster_layers.TileLayer(
    tiles=map_dict['tile_fetcher'].url_format,
    attr='Google Earth Engine',
    name='NDVI',
    overlay=True,
    control=True
).add_to(m)

st_folium(m, width=700, height=400, key='ndvi')

그림1

1-5. Streamlit APP

1
streamlit run {파일명}.py

이제 웹 페이지에서 확인할 수 있다.

그림1

This post is licensed under CC BY 4.0 by the author.