Spaces:
Paused
Paused
Fix authentication error (#5)
Browse files
main.py
CHANGED
|
@@ -7,15 +7,119 @@ from fastapi import FastAPI, HTTPException
|
|
| 7 |
from pydantic import BaseModel
|
| 8 |
from geemap.ee_tile_layers import _get_tile_url_format, _validate_palette
|
| 9 |
from starlette.middleware.cors import CORSMiddleware
|
|
|
|
| 10 |
|
| 11 |
-
# Earth Engine auth
|
| 12 |
-
if "EARTHENGINE_TOKEN" not in os.environ:
|
| 13 |
-
|
| 14 |
|
| 15 |
-
try:
|
| 16 |
-
|
| 17 |
-
except Exception as e:
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
|
| 21 |
# ---- Shared Tile Logic ----
|
|
@@ -90,6 +194,8 @@ def get_tile(asset_id, vis_params=None, start_date=None, end_date=None, bbox=Non
|
|
| 90 |
return f"Error: {str(e)}"
|
| 91 |
|
| 92 |
|
|
|
|
|
|
|
| 93 |
# ---- FastAPI ----
|
| 94 |
app = FastAPI()
|
| 95 |
app.add_middleware(
|
|
@@ -116,18 +222,50 @@ def get_tile_api(req: TileRequest):
|
|
| 116 |
|
| 117 |
|
| 118 |
# ---- Gradio UI ----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
gradio_ui = gr.Interface(
|
| 120 |
-
fn=
|
| 121 |
inputs=[
|
| 122 |
gr.Textbox(label="Earth Engine Asset ID", placeholder="e.g., USGS/SRTMGL1_003"),
|
| 123 |
gr.Textbox(
|
| 124 |
label="Visualization Parameters (JSON)",
|
| 125 |
placeholder='{"min":0,"max":5000,"palette":"terrain"}',
|
| 126 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
],
|
| 128 |
outputs="text",
|
| 129 |
title="Earth Engine Tile URL Generator",
|
| 130 |
-
description="Supports ee.Image, ee.ImageCollection, ee.FeatureCollection. Tile URL is suitable for basemap usage.",
|
| 131 |
)
|
| 132 |
|
| 133 |
app = gr.mount_gradio_app(app, gradio_ui, path="/")
|
|
|
|
| 7 |
from pydantic import BaseModel
|
| 8 |
from geemap.ee_tile_layers import _get_tile_url_format, _validate_palette
|
| 9 |
from starlette.middleware.cors import CORSMiddleware
|
| 10 |
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
| 11 |
|
| 12 |
+
# # Earth Engine auth
|
| 13 |
+
# if "EARTHENGINE_TOKEN" not in os.environ:
|
| 14 |
+
# raise RuntimeError("EARTHENGINE_TOKEN environment variable not found")
|
| 15 |
|
| 16 |
+
# try:
|
| 17 |
+
# geemap.ee_initialize()
|
| 18 |
+
# except Exception as e:
|
| 19 |
+
# raise RuntimeError(f"Earth Engine authentication failed: {e}")
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def get_env_var(key: str) -> Optional[str]:
|
| 23 |
+
"""Retrieves an environment variable or Colab secret for the given key.
|
| 24 |
+
|
| 25 |
+
Colab secrets have precedence over environment variables.
|
| 26 |
+
|
| 27 |
+
Args:
|
| 28 |
+
key (str): The key that's used to fetch the environment variable.
|
| 29 |
+
|
| 30 |
+
Returns:
|
| 31 |
+
Optional[str]: The retrieved key, or None if no environment variable was found.
|
| 32 |
+
"""
|
| 33 |
+
if not key:
|
| 34 |
+
return None
|
| 35 |
+
|
| 36 |
+
return os.environ.get(key)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def ee_initialize(
|
| 40 |
+
token_name: str = "EARTHENGINE_TOKEN",
|
| 41 |
+
auth_mode: Optional[str] = None,
|
| 42 |
+
auth_args: Optional[Dict[str, Any]] = None,
|
| 43 |
+
project: Optional[str] = None,
|
| 44 |
+
**kwargs: Any,
|
| 45 |
+
) -> None:
|
| 46 |
+
"""Authenticates Earth Engine and initialize an Earth Engine session
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
token_name (str, optional): The name of the Earth Engine token.
|
| 50 |
+
Defaults to "EARTHENGINE_TOKEN". In Colab, you can also set a secret
|
| 51 |
+
named "EE_PROJECT_ID" to initialize Earth Engine.
|
| 52 |
+
auth_mode (str, optional): The authentication mode, can be one of colab,
|
| 53 |
+
notebook, localhost, or gcloud.
|
| 54 |
+
See https://developers.google.com/earth-engine/guides/auth for more
|
| 55 |
+
details. Defaults to None.
|
| 56 |
+
auth_args (dict, optional): Additional authentication parameters for
|
| 57 |
+
aa.Authenticate(). Defaults to {}.
|
| 58 |
+
user_agent_prefix (str, optional): If set, the prefix (version-less)
|
| 59 |
+
value used for setting the user-agent string. Defaults to "geemap".
|
| 60 |
+
project (str, optional): The Google cloud project ID for Earth Engine.
|
| 61 |
+
Defaults to None.
|
| 62 |
+
kwargs (dict, optional): Additional parameters for ee.Initialize().
|
| 63 |
+
For example, opt_url='https://earthengine-highvolume.googleapis.com'
|
| 64 |
+
to use the Earth Engine High-Volume platform. Defaults to {}.
|
| 65 |
+
"""
|
| 66 |
+
import google.oauth2.credentials
|
| 67 |
+
|
| 68 |
+
# pylint: disable-next=protected-access
|
| 69 |
+
if ee.data._get_state().credentials is not None:
|
| 70 |
+
return
|
| 71 |
+
|
| 72 |
+
if get_env_var("EE_SERVICE_ACCOUNT") is not None:
|
| 73 |
+
|
| 74 |
+
key_data = get_env_var("EE_SERVICE_ACCOUNT")
|
| 75 |
+
|
| 76 |
+
try:
|
| 77 |
+
email = json.loads(key_data)["client_email"]
|
| 78 |
+
except json.JSONDecodeError as e:
|
| 79 |
+
raise ValueError(f"Invalid JSON for key_data: {e}")
|
| 80 |
+
except KeyError:
|
| 81 |
+
raise ValueError("key_data JSON does not contain 'client_email'")
|
| 82 |
+
credentials = ee.ServiceAccountCredentials(email=email, key_data=key_data)
|
| 83 |
+
ee.Initialize(credentials)
|
| 84 |
+
return
|
| 85 |
+
|
| 86 |
+
ee_token = get_env_var(token_name)
|
| 87 |
+
if ee_token is not None:
|
| 88 |
+
|
| 89 |
+
stored = json.loads(ee_token)
|
| 90 |
+
credentials = google.oauth2.credentials.Credentials(
|
| 91 |
+
None,
|
| 92 |
+
token_uri="https://oauth2.googleapis.com/token",
|
| 93 |
+
client_id=stored["client_id"],
|
| 94 |
+
client_secret=stored["client_secret"],
|
| 95 |
+
refresh_token=stored["refresh_token"],
|
| 96 |
+
quota_project_id=stored["project"],
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
ee.Initialize(credentials=credentials, **kwargs)
|
| 100 |
+
return
|
| 101 |
+
|
| 102 |
+
if auth_args is None:
|
| 103 |
+
auth_args = {}
|
| 104 |
+
|
| 105 |
+
if project is None:
|
| 106 |
+
kwargs["project"] = get_env_var("EE_PROJECT_ID")
|
| 107 |
+
else:
|
| 108 |
+
kwargs["project"] = project
|
| 109 |
+
|
| 110 |
+
if auth_mode is None:
|
| 111 |
+
# pylint: disable-next=protected-access
|
| 112 |
+
if ee.data._get_state().credentials is None:
|
| 113 |
+
ee.Authenticate()
|
| 114 |
+
ee.Initialize(**kwargs)
|
| 115 |
+
return
|
| 116 |
+
else:
|
| 117 |
+
auth_mode = "notebook"
|
| 118 |
+
|
| 119 |
+
auth_args["auth_mode"] = auth_mode
|
| 120 |
+
|
| 121 |
+
ee.Authenticate(**auth_args)
|
| 122 |
+
ee.Initialize(**kwargs)
|
| 123 |
|
| 124 |
|
| 125 |
# ---- Shared Tile Logic ----
|
|
|
|
| 194 |
return f"Error: {str(e)}"
|
| 195 |
|
| 196 |
|
| 197 |
+
ee_initialize()
|
| 198 |
+
|
| 199 |
# ---- FastAPI ----
|
| 200 |
app = FastAPI()
|
| 201 |
app.add_middleware(
|
|
|
|
| 222 |
|
| 223 |
|
| 224 |
# ---- Gradio UI ----
|
| 225 |
+
def get_tile_gradio(asset_id, vis_params, start_date, end_date, bbox_str):
|
| 226 |
+
"""Wrapper for Gradio that converts string inputs to proper types."""
|
| 227 |
+
# Convert empty strings to None
|
| 228 |
+
start_date = start_date.strip() if start_date and start_date.strip() else None
|
| 229 |
+
end_date = end_date.strip() if end_date and end_date.strip() else None
|
| 230 |
+
|
| 231 |
+
# Convert bbox string to list
|
| 232 |
+
bbox = None
|
| 233 |
+
if bbox_str and bbox_str.strip():
|
| 234 |
+
try:
|
| 235 |
+
bbox = [float(x.strip()) for x in bbox_str.split(",")]
|
| 236 |
+
except ValueError:
|
| 237 |
+
return "Error: bbox must be comma-separated numbers (west,south,east,north)"
|
| 238 |
+
|
| 239 |
+
return get_tile(asset_id, vis_params, start_date, end_date, bbox)
|
| 240 |
+
|
| 241 |
+
|
| 242 |
gradio_ui = gr.Interface(
|
| 243 |
+
fn=get_tile_gradio,
|
| 244 |
inputs=[
|
| 245 |
gr.Textbox(label="Earth Engine Asset ID", placeholder="e.g., USGS/SRTMGL1_003"),
|
| 246 |
gr.Textbox(
|
| 247 |
label="Visualization Parameters (JSON)",
|
| 248 |
placeholder='{"min":0,"max":5000,"palette":"terrain"}',
|
| 249 |
),
|
| 250 |
+
gr.Textbox(
|
| 251 |
+
label="Start Date (Optional)",
|
| 252 |
+
placeholder="e.g., 2023-01-01",
|
| 253 |
+
value="",
|
| 254 |
+
),
|
| 255 |
+
gr.Textbox(
|
| 256 |
+
label="End Date (Optional)",
|
| 257 |
+
placeholder="e.g., 2023-12-31",
|
| 258 |
+
value="",
|
| 259 |
+
),
|
| 260 |
+
gr.Textbox(
|
| 261 |
+
label="Bounding Box (Optional)",
|
| 262 |
+
placeholder="e.g., -122.5,37.5,-122.0,38.0 (west,south,east,north)",
|
| 263 |
+
value="",
|
| 264 |
+
),
|
| 265 |
],
|
| 266 |
outputs="text",
|
| 267 |
title="Earth Engine Tile URL Generator",
|
| 268 |
+
description="Supports ee.Image, ee.ImageCollection, ee.FeatureCollection with optional date range and bbox filtering. Tile URL is suitable for basemap usage.",
|
| 269 |
)
|
| 270 |
|
| 271 |
app = gr.mount_gradio_app(app, gradio_ui, path="/")
|