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로 제공할 것이다.

image

주요 컴포넌트 기능

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 &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    });

    var roi2Layer = L.tileLayer(roi2Data.map_url, {
        maxZoom: 19,
        attribution: 'Map data &copy; <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: '&copy; 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);
}

결과

렌더링 페이지

image

docs 페이지

기본 url 뒤에 /docs를 입력하면 FastAPI에서 자동으로 생성한 API 문서를 확인할 수 있다. image