RでGoogle Earth Engineを操作できるようにする
変更履歴・概要
2025/3/29 更新
LandsatのCollection 1データセットが利用できなくなったのでコードを修正。あわせて,earthengine-api
について,rgee
ライブラリで未検証の最新バージョンを利用するための対応を追記。
2024/3/28 更新
rgee
ライブラリを1.1.5から1.1.7に更新したので,対応するearthengine-apiのバージョンを0.1.323から0.1.370に変更。
宙畑の衛星データ分析におススメの言語は?言語別の特徴まとめとElixir紹介へのリンクを追加。
2024/2/1 初版
2023年6~9月に行った環境構築作業のメモを整理したものです。一部足りないところや分かりにくいところがあるかもしれません。また,それぞれの環境によって違いがあるかもしれませんが,ご容赦ください。
できるようになること
RからGoogle Earth Engine(GEE)を操作できる環境を構築します。
Google Earth Engine(GEE)って何?
Googleから提供される,地球観測衛星が取得した大量のデータを,クラウド上で(研究・教育・非営利目的であれば)無料で解析・利用できるサービスです。
https://earthengine.google.com/
https://developers.google.com/earth-engine
2023年10月にはGEEに関する書籍もでています。
Cloud-Based Remote Sensing with Google Earth Engine
日本語での説明は以下のサイトがあります。
なお,データを見るだけなら,Earth Engine Explorerで可能です。使い方は,Google Earth Engine の概要を参考にしてください。
ほかにも,RESTEC(一般財団法人リモート・センシング技術センター)が作成しているVEGAでも,LandsatシリーズやSentinelシリーズなどの衛星データを見ることができます。
Rで操作するためには
GoogleからはJavaScriptとPythonがサポートされています。Rから操作するには,rgee
ライブラリを利用します。rgee
ライブラリはPython経由でGEEにアクセスすることを可能にしたライブラリです。
https://cran.r-project.org/web/packages/rgee/index.html
https://github.com/r-spatial/rgee
https://qiita.com/iwasaki_kenichi/items/b4db19204cf4343e82ce
なお,宙畑に衛星データ分析に用いられる言語について説明記事がありました。Rについても言及があるので,参考にしてください。
衛星データ分析におススメの言語は?言語別の特徴まとめとElixir紹介
rgeeライブラリのインストール
Rへはrgee
ライブラリは以下によりインストールできます。
install.packages('rgee')
ついでに,rgeeExtra
ライブラリもインストールしておきます。
https://github.com/r-earthengine/rgeeExtra
remotes::install_github("r-earthengine/rgeeExtra")
ただし,rgee
ライブラリを利用するには以下の条件を満たす必要があります。
- GEEへのGoogleアカウント登録
- Python環境の構築
GEEへのアカウント登録
GEEを利用するにはGoogleアカウントが必要ですので,まずは用意してください。
GEEへのアカウント登録は,sindicumさんのGoogle Earth EngineとPythonで始める衛星データ利用入門第2章に説明があるので参考にしてください。概要としては,GEEのWebページにアクセスし,右上のGet Startedをクリックして,画面の指示にしたがってCloud Projectを作成します。
成功したらEarth Engine Code Editorが立ち上がります。JavaScriptで操作するための画面で,普段は使いませんので,詳しい説明は省略します。
Python 環境の構築
最初に書いたとおりrgee
ライブラリは(reticulate
ライブラリを利用して)PythonのEarth Engine APIからアクセスします。そのため,GEEにアクセスできるPython環境を用意する必要があります。
rgee
ライブラリの説明では,Pythonの操作経験がない場合には以下で構築することが推奨されています。
rgee::ee_install(py_env = "rgee")
ここでは,(既にcondaをインストールしてあったので)独自にPython環境を構築し,Rから操作できるようにします。インストールはGEEの以下の説明を参考にしました。
https://developers.google.com/earth-engine/guides/python_install
https://developers.google.com/earth-engine/guides/python_install-conda
まず,condaをインストールします。私の環境はAnacondaで構築していますが,Minicondaでもいいと思います。インストール方法は他のサイトでたくさん紹介されているので,そちらを参考にしてください。
次に,GEE用の仮想環境を構築します。condaのコマンドで以下のとおり入力していきます。最後のeemont
は必須ではありませんが,rgeeExtra
ライブラリをインストールすると利用可能なeeExtraの読み込みが,EEextra_PYTHON_PACKAGE$
を介さずに利用できます(https://eemont.readthedocs.io/en/latest/guide/eemontR.html)。
conda create -n r_gee python=3.8.16
conda activate r_gee
conda install -c conda-forge earthengine-api==0.1.323 # rgee1.1.7の検証が0.1.323のため。
conda install numpy # earthengine-apiにはnumpyが必要
conda install -c conda-forge eemont #ついでにインストール
GEEへのアクセス認証を行うため,r_gee仮想環境で以下のコマンドを入力し,求められるとおりに操作していってください。
earthengine authenticate
Python環境を構築できたかは,r_gee仮想環境でPythonを起動して以下のとおり試してみてください。なお,プロジェクト名にはGEEへのアカウント登録で入力したproject-IDを入力します(https://developers.google.com/earth-engine/guides/access)。
import ee
ee.Initialize(project='プロジェクト名')
print(ee.Image("NASA/NASADEM_HGT/001").get("title").getInfo())
rgeeライブラリでの環境構築
次に,以下でrgee
ライブラリの環境を構築します。なお,RStudio(V1.4以上)のPythonパスの設定でも可能だったようです。
rgee::ee_install_set_pyenv(
py_path = "C:/Users/####/anaconda3/envs/r_gee", # Change it for your own Python PATH
py_env = "r_gee" # Change it for your own Python ENV
)
GEEを始めるには,ee_Initialize(project = 'プロジェクト名')
とします。なお,データ保存用にGoogle Driveを利用するにはee_Initialize(project = 'プロジェクト名', drive=T)
とします(最初にはtokenの設定が必要ですが,特に意識しなくても,表示される画面に従えば設定ができるようです)。
インストール後には以下で環境を確認できます。
rgee::ee_check_python()
rgee::ee_check_python_packages()
rgee::ee_check_credentials()
最新のearth engine apiを利用する方法
RでHansenGlobalForestChangeデータと雲除去したSentinel画像を表示してみるで記載したlinkcollection関数を利用するためにはearthengine-api
のバージョンをあげる必要がありますが,rgeeライブラリで検証されたバージョンではなく,ee_Initialize()
でee_check_root_folder
に関するエラーがでてGEEの認証ができません。
https://github.com/r-spatial/rgee/issues/355
https://github.com/r-spatial/rgee/issues/370
そこで,reticulate
ライブラリを利用してpythonコードを直接実行します。
reticulate::py_run_string("import ee")
reticulate::py_run_string("ee.Initialize(project='プロジェクト名')")
これだけでは,rgee
ライブラリにある関数のうち,RへGoogleDrive等を経由してデータを読み込ませるee_as_sf()
などが機能しません。原因は,これらの関数がee_Initialize()
により生成されるrgee_sessioninfo.txt
を読み込もうとするのですが,上記のreticulate
ライブラリから直接認証させた状態ではtxtファイルが生成されていないことによるものです。以下のファイルをRスクリプトファイルとして保存した上で,source()
により読み込みます。
ee_sessioninfo.R
## ee_sessioninfo.R
## rgeeライブラリで`rgee_sessioninfo.txt`を生成する関数のみを抽出したファイル
# ee_utils.R
ee_utils_py_to_r <- function(x) {
p_r <- suppressWarnings(try(reticulate::py_to_r(x), silent = TRUE))
if (class(p_r) %in% 'try-error') {
return(x)
} else {
return(p_r)
}
}
# utils-auth.R
ee_source_python <- function(oauth_func_path) {
module_name <- gsub("\\.py$", "", basename(oauth_func_path))
module_path <- dirname(oauth_func_path)
reticulate::import_from_path(module_name, path = module_path, convert = FALSE)
}
ee_connect_to_py <- function(path, n = 5) {
ee_utils <- try(ee_source_python(oauth_func_path = path), silent = TRUE)
# counter added to prevent problems with reticulate
con_reticulate_counter <- 1
while (any(class(ee_utils) %in% "try-error")) {
ee_utils <- try(ee_source_python(path), silent = TRUE)
con_reticulate_counter <- con_reticulate_counter + 1
if (con_reticulate_counter == (n + 1)) {
python_path <- reticulate::py_discover_config()
message_con <- c(
sprintf("The current Python PATH: %s",
bold(python_path[["python"]])),
"does not have the Python package \"earthengine-api\" installed. Do you restarted/terminated",
"your R session after install miniconda or run ee_install()?",
"If this is not the case, try:",
"> ee_install_upgrade(): Install the latest Earth Engine Python version.",
"> reticulate::use_python(): Refresh your R session and manually set the Python environment with all rgee dependencies.",
"> ee_install(): To create and set a Python environment with all rgee dependencies.",
"> ee_install_set_pyenv(): To set a specific Python environment."
)
stop(paste(message_con,collapse = "\n"))
}
}
return(ee_utils)
}
ee_check_init <- function() {
# if EARTHENGINE_PYTHON is defined, then send it to RETICULATE_PYTHON
earthengine_python <- Sys.getenv("EARTHENGINE_PYTHON", unset = NA)
if (!is.na(earthengine_python))
Sys.setenv(RETICULATE_PYTHON = earthengine_python)
# Check ee_utils.py sanity
ee_current_version <- system.file("python/ee_utils.py", package = "rgee")
if (!file.exists(ee_current_version)) {
stop(
sprintf(
"The file %s does not exist in your system. Please re-install rgee: %s",
ee_current_version,
"remotes::install_github(\"r-spatial/rgee\")."
)
)
}
ee_utils <- ee_connect_to_py(path = ee_current_version, n = 5)
earthengine_version <- ee_utils_py_to_r(ee_utils$ee_getversion())
# is earthengine-api greater than 0.1.317?
#if (as.numeric(gsub("\\.","",earthengine_version)) < 01317) {
# warning(
# "Update your earthengine-api installations to v0.1.317 or greater. ",
# "Earlier versions are not compatible with recent ",
# "changes to the Earth Engine backend."
# )
# }
list(earthengine_version = earthengine_version, ee_utils = ee_utils)
}
ee_check_packages <- function(fn_name, packages) {
pkg_exists <- rep(NA, length(packages))
counter <- 0
for(package in packages) {
counter <- counter + 1
pkg_exists[counter] <- requireNamespace(package, quietly = TRUE)
}
if (!all(pkg_exists)) {
to_install <- packages[!pkg_exists]
to_install_len <- length(to_install)
error_msg <- sprintf(
"%s required the %s: %s. Please install %s first.",
bold(fn_name),
if (to_install_len == 1) "package" else "packages",
paste0(bold(to_install), collapse = ", "),
if (to_install_len == 1) "it" else "them"
)
stop(error_msg)
}
}
# utils-credentials.R
ee_create_credentials_drive <- function(user=NULL, ee_utils, quiet) {
# check googledrive R package installation
ee_check_packages("ee_Initialize", "googledrive")
# Check sanity of earth-engine and return ee_utils.py module
init <- ee_check_init()
ee_utils <- init$ee_utils
# setting drive folder
if (is.null(user)) {
ee_path <- ee_utils_py_to_r(ee_utils$ee_path())
ee_path_user <- ee_path
} else {
ee_path <- ee_utils_py_to_r(ee_utils$ee_path())
ee_path_user <- sprintf("%s/%s", ee_path, user)
}
# Load GD credentials (googledrive::drive_auth)
repeat {
full_credentials <- list.files(path = ee_path_user, full.names = TRUE)
drive_condition <- grepl(".*_.*@.*", basename(full_credentials))
# If the googledrive credential does not exist run googledrive::drive_auth
if (!any(drive_condition)) {
suppressMessages(
googledrive::drive_auth(
email = NULL,
cache = ee_path_user
)
)
} else {
drive_credentials <- full_credentials[drive_condition]
email <- sub("^[^_]*_", "", basename(drive_credentials)) # Obtain the email
suppressMessages(
googledrive::drive_auth(
email = email,
cache = ee_path_user
)
)
# This lines is to avoid that users have multiple token file.
# It delete the older one if the system detect two different token files.
new_full_credentials <- list.files(path = ee_path_user, full.names = TRUE)
new_drive_condition <- grepl(".*_.*@.*", basename(new_full_credentials))
if (sum(new_drive_condition) > 1) {
files_credentials_time <- file.info(new_full_credentials[new_drive_condition])$ctime
drive_credential_to_remove <- new_full_credentials[which.min(files_credentials_time)]
if (!quiet) {
message("Removing previous Google Drive Token ....")
}
file.remove(drive_credential_to_remove)
}
break
}
}
# Move credential to the main folder is user is set
if (!is.null(user)) {
# Clean previous and copy new GD credentials in ./earthengine folder
clean_drive <- list.files(ee_path, ".*_.*@.*", full.names = TRUE) %in% list.dirs(ee_path)
unlink(
list.files(ee_path, ".*_.*@.*", full.names = TRUE)[!clean_drive]
)
file.copy(
from = drive_credentials,
to = sprintf("%s/%s", ee_path, basename(drive_credentials)),
overwrite = TRUE
)
}
invisible(drive_credentials)
}
ee_create_credentials_gcs_ <- function(user, ee_utils) {
# check packages
ee_check_packages("ee_Initialize", "googleCloudStorageR")
# Check sanity of earth-engine and return ee_utils.py module
init <- ee_check_init()
ee_utils <- init$ee_utils
# setting gcs folder
if (is.null(user)) {
ee_path <- suppressWarnings(ee_utils_py_to_r(ee_utils$ee_path()))
ee_path_user <- ee_path
} else {
ee_path <- suppressWarnings(ee_utils_py_to_r(ee_utils$ee_path()))
ee_path_user <- sprintf("%s/%s", ee_path, user)
}
# gcs_credentials
full_credentials <- list.files(path = ee_path_user, full.names = TRUE)
gcs_condition <- grepl(".json", full_credentials)
if (!any(gcs_condition)) {
gcs_text <- paste(
sprintf("Unable to find a service account key (SAK) file in: %s", crayon::bold(ee_path_user)),
"To solve this problem:",
" 1) download it from your Google cloud console",
" 2) validate it using rgee::ee_utils_sak_validate [OPTIONAL].",
" 3) Use rgee::ee_utils_sak_copy to set the SaK in rgee.",
"A tutorial to obtain the SAK file is available at:",
"> https://r-spatial.github.io/rgee/articles/rgee05.html",
crayon::bold("As long as you haven't saved a SKA file, the following functions will not work:"),
"- rgee::ee_gcs_to_local()",
"- ee_extract(..., via = \"gcs\")",
"- ee_as_raster(..., via = \"gcs\")",
"- ee_as_stars(..., via = \"gcs\")",
"- ee_as_sf(..., via = \"gcs\")",
"- sf_as_ee(..., via = \"gcs_to_asset\")",
"- gcs_to_ee_image",
"- raster_as_ee",
"- local_to_gcs",
"- stars_as_ee",
sep = "\n"
)
gcs_info <- list(path = NA, message = gcs_text)
} else {
gcs_credentials <- full_credentials[gcs_condition]
googleCloudStorageR::gcs_auth(gcs_credentials)
if (!is.null(user)) {
unlink(list.files(ee_path, ".json", full.names = TRUE))
file.copy(
from = gcs_credentials,
to = sprintf("%s/%s", ee_path, basename(gcs_credentials)),
overwrite = TRUE
)
gcs_info <- list(path = gcs_credentials, message = NA)
} else {
gcs_info <- list(path = gcs_credentials, message = NA)
}
}
gcs_info
}
# utils-auth.R & ee_Initialize.R
ee_sessioninfo <- function(email = NULL,
user = NULL,
drive = NULL,
gcs = NULL) {
init <- ee_check_init()
ee_utils <- init$ee_utils
oauth_func_path <- system.file("python/ee_utils.py", package = "rgee")
utils_py <- ee_source_python(oauth_func_path)
sessioninfo <- sprintf(
"%s/rgee_sessioninfo.txt",
ee_utils_py_to_r(utils_py$ee_path())
)
if (is.null(email)) {
email <- NA
}
# Loading all the credentials: earthengine, drive and GCS.
drive_credentials <- NA
gcs_credentials <- list(path = NA, message = NA)
if (drive) {
ee_check_packages("ee_Initialize", "googledrive")
# drive init message
# if (!quiet) ee_message_02(init = TRUE)
# If the user is not NULL copy the drive credential in the subfolder
drive_credentials <- ee_create_credentials_drive(user, ee_utils, quiet = quiet)
# test_drive_privileges(user)
# if (!quiet) ee_message_02(init = FALSE)
}
if (gcs) {
ee_check_packages("ee_Initialize", "googleCloudStorageR")
# if (!quiet) ee_message_03(init=TRUE, gcs_credentials)
# Load GCS credentials
gcs_credentials <- ee_create_credentials_gcs(user, ee_utils)
# if (!quiet) ee_message_03(init=FALSE, gcs_credentials)
}
## rgee session file
# options(rgee.gcs.auth = gcs_credentials[["path"]])
# if (!quiet) ee_message_04(init = TRUE)
df <- data.frame(
email = email, user = user, drive_cre = drive_credentials, gcs_cre = gcs_credentials[["path"]],
stringsAsFactors = FALSE
)
write.table(df, sessioninfo, row.names = FALSE)
}
source("ee_sessioninfo.R")
ee_sessioninfo(email = "ndef", user = "GEEのユーザ名", drive=T, gcs=F)
試してみよう
library(rgee)
library(leaflet) # attributionの挿入に必要
ee_Initialize(project = 'プロジェクト名')
Earth Engine JavaScript API Documentationをもとに,サンフランシスコ湾周辺のLandsat8による撮影画像を表示させてみましょう。
# Load an image.
landsat <- ee$Image("LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318")
# Define the visualization parameters.
vizParams <- list(
bands = c("B5", "B4", "B3"),
min = 0,
max = 0.5,
gamma = c(0.95, 1.1, 1)
)
# Center the map and display the image.
Map$setCenter(lon = -122.1899, lat = 37.5010, zoom = 10) # San Francisco Bay
Map$addLayer(landsat, vizParams, "false color composite") %>%
addTiles(urlTemplate = "", attribution = '| Landsat-8 image courtesy of the U.S. Geological Survey')
Session info
sessionInfo()
## R version 4.3.3 (2024-02-29 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
##
##
## locale:
## [1] LC_COLLATE=Japanese_Japan.utf8 LC_CTYPE=Japanese_Japan.utf8 LC_MONETARY=Japanese_Japan.utf8
## [4] LC_NUMERIC=C LC_TIME=Japanese_Japan.utf8
##
## time zone: Etc/GMT-9
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] leaflet_2.2.2 rgee_1.1.7 here_1.0.1 fs_1.6.5 rmarkdown_2.29
##
## loaded via a namespace (and not attached):
## [1] class_7.3-22 KernSmooth_2.23-22 stringi_1.8.7 lattice_0.22-5
## [5] digest_0.6.37 magrittr_2.0.3 evaluate_1.0.3 grid_4.3.3
## [9] fastmap_1.2.0 rprojroot_2.0.4 jsonlite_2.0.0 Matrix_1.6-5
## [13] processx_3.8.6 e1071_1.7-16 chromote_0.5.0 DBI_1.2.3
## [17] promises_1.3.2 ps_1.9.0 crosstalk_1.2.1 codetools_0.2-19
## [21] jquerylib_0.1.4 cli_3.6.1 rlang_1.1.5 crayon_1.5.3
## [25] units_0.8-7 base64enc_0.1-3 withr_3.0.2 yaml_2.3.10
## [29] tools_4.3.3 raster_3.6-32 webshot_0.5.5 reticulate_1.42.0
## [33] vctrs_0.6.5 R6_2.6.1 png_0.1-8 proxy_0.4-27
## [37] lifecycle_1.0.4 classInt_0.4-11 stringr_1.5.1 leaflet.providers_2.0.0
## [41] htmlwidgets_1.6.4 pkgconfig_2.0.3 later_1.4.1 terra_1.8-29
## [45] pillar_1.10.1 glue_1.8.0 Rcpp_1.0.14 sf_1.0-20
## [49] xfun_0.51 tibble_3.2.1 rstudioapi_0.17.1 knitr_1.50
## [53] websocket_1.4.2 htmltools_0.5.8.1 webshot2_0.1.1 leafem_0.2.3
## [57] compiler_4.3.3 sp_2.2-0
Discussion