Earth Engine과 FastAPI, Leaflet
Earth Engine 라이브러리
Google Earth Engine은 Google에서 제공하는 클라우드 기반의 플랫폼으로, 지구 관련 데이터를 저장하고 처리할 수 있는 도구를 제공한다. 위성 데이터, 기상 데이터, 지리 공간 벡터 데이터를 비롯한 다양한 데이터 소스를 정제하여 제공하여 손쉽게 활용할 수 있다. 기본적으로 JavaScript를 사용하는 Code Editor를 제공하며, Python API도 제공하고 있다.
FastAPI 라이브러리
FastAPI는 Python 3.6+에서 사용할 수 있는 웹 프레임워크로, 빠르고, 유연하며, 간단하게 Restful API를 구축할 수 있다. 비동기적으로 동작하며, 타입 힌트를 활용한 코드 자동 문서화 기능을 제공한다.
동기적 vs. 비동기적
- 동기적(Synchronous): 요청과 응답이 순차적으로 처리되는 방식
- 비동기적(Asynchronous): 요청과 응답이 병렬적으로 처리되는 방식
- 작업을 요청했을 때, 결과가 나올 때까지 기다리지 않고 다른 작업을 수행할 수 있다.
FastAPI와 Earth Engine 연동 프레임워크
Google Earth Engine을 활용하여 위성 데이터를 처리 및 분석하고, FastAPI를 통해 사용자에게 해당 결과물을 Restful API로 제공할 것이다.
주요 컴포넌트 기능
1. 데이터 검색 및 처리
gee_data
를 따로 만들어 Earth Engine에서 데이터를 가져오는 기능을 구현했다.- 특정 지역(station_name)과 기간(start_date, end_date)을 입력받아 해당 지역의 위성 데이터를 가져온다.
2. FastAPI를 통한 API 구축
/load-data
엔드포인트를 통해 클라이언트가 지역, 기간, 위성영상을 지정하여 데이터를 요청할 수 있다.- 응답은 JSON 형식으로 처리되어 클라이언트가 쉽게 데이터를 활용할 수 있도록 한다.
3. Jinja2와 정적파일을 활용한 프론트엔드
- Jinja2 템플릿 엔진을 활용하여 서버에서 데이터를 받아와 HTML로 렌더링한다.
/
루트 경로에서 사용자에게 HTML 페이지를 제공하며, 정적 파일(CSS, JS 등)은StaticFiles
를 통해 서빙된다.- 프런트엔드에서는 API를 호출하여 결과 데이터를 시각화하거나 사용자 입력을 기반으로 작동한다.
FastAPI 기반 엔드포인트 설계
엔드포인트란 클라이언트의 요청을 처리하고 응답을 반환하는 API 지점으로, 요청을 받아 처리하고 응답하는 함수를 의미한다.
- HTTP 메서드 + URL 경로
1. /
(HTML 페이지 반환)
- 정적 HTML 페이지를 반환하여 사용자 인터페이스를 제공한다.
@app.get("/", response_class=HTMLResponse) async def get_index(): """HTML 페이지 반환""" with open("templates/index.html", encoding='UTF8') as f: return f.read()
2. /load-data
(데이터 로드 및 반환)
- 클라이언트가 요청한 위성이미지(image_name), 지역 이름(
station_name
), 시작일(start_date
), 종료일(end_date
)을 기반으로 데이터를 로드하고 JSON 형식으로 반환한다. - 반환한 데이터는 시계열 데이터(
value_data
)와 위성이미지(image_data
), 지역 중심 좌표(location
)로 구성된다.@app.get("/load-data") async def load_data(image_name: str, station_name: str, start_date: str = '2022-11-01', end_date: str = '2023-07-01'): """주어진 날짜 범위를 기반으로 데이터프레임 생성""" start_date = datetime.strptime(start_date, "%Y-%m-%d") end_date = datetime.strptime(end_date, "%Y-%m-%d") datas = gee_data.get_data(image_name, station_name, start_date, end_date) value_df = datas[0] image_df = datas[1] response = { 'value_data': value_df.to_dict(orient='records'), 'image_data': image_df.to_dict(orient='records'), 'location': get_location(station_name) } return JSONResponse(response)
실행 환경 및 배포
- FastAPI는 ASGI 서버를 사용하므로,
uvicorn
을 사용하여 실행할 수 있다.if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8900)
위성 이미지 데이터(image_data
) 처리 및 시각화
위성 이미지 데이터를 leaflet.js
에서 타일 형식으로 표시할 수 있도록 준비하는 것이 중요하다.
1. image_data
생성 과정
Pythone 코드에서 image_data
는 다음과 같이 생성된다.
vi_values = collection.select(band).sort('system:time_start', False).first().clip(area)
map_dict = vi_values.getMapId(vis_params[band])
map_url = map_dict['tile_fetcher'].url_format
collection.select(band)
: 특정 밴드를 추출한다.sort('system:time_start', False)
: 시간 순으로 정렬한다.first()
: 가장 최근의 이미지를 선택한다.clip(area)
: 특정 관심 영역(area)로 이미지를 클리핑하여 해당 영역만을 다룬다.vi_values.getMapId(vis_params[band])
: 선택한 밴드와 시각화 파라미터에 맞는 지도 타일 ID를 반환한다. 이 반환 값은 위성 이미지를 지도에 표시할 수 있는 URL(map_url
)을 제공한다.
map_url
은 다음과 같은 형식으로 반환된다.
http://mt0.google.com/vt/lyrs=s&hl=ko&x={x}&y={y}&z={z}
2. map_url
을 Javascript에서 처리
leaflet.js
를 사용하여 위성 이미지(map_url
)를 표시할 수 있다.
function drawMap(location, imageData, selectedIndex) {
var map = L.map('map').setView([location.lat, location.lot], 17);
// ROI1과 ROI2 데이터를 필터링하여 선택된 지표에 맞는 데이터를 추출
const roi1Data = imageData.filter(item => item.area === 'roi1' && item.band_name === selectedIndex)[0];
const roi2Data = imageData.filter(item => item.area === 'roi2' && item.band_name === selectedIndex)[0];
// ROI1과 ROI2 레이어 생성 (각각 map_url을 사용)
var roi1Layer = L.tileLayer(roi1Data.map_url, {
maxZoom: 19,
attribution: 'Map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
});
var roi2Layer = L.tileLayer(roi2Data.map_url, {
maxZoom: 19,
attribution: 'Map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
});
// 기본 지도 레이어 추가 (Google Maps)
var baseLayer = L.tileLayer('http://mt0.google.com/vt/lyrs=s&hl=ko&x={x}&y={y}&z={z}', {
attribution: '© Google'
}).addTo(map);
// ROI1과 ROI2 레이어를 지도에 추가
roi1Layer.addTo(map);
roi2Layer.addTo(map);
// 레이어 제어를 추가하여 사용자가 ROI1과 ROI2를 선택할 수 있게 함
var overlayMaps = {
"ROI 1": roi1Layer,
"ROI 2": roi2Layer
};
L.control.layers(null, overlayMaps, {collapsed: false}).addTo(map);
}
결과
렌더링 페이지
docs 페이지
기본 url 뒤에 /docs
를 입력하면 FastAPI에서 자동으로 생성한 API 문서를 확인할 수 있다.