Spaces:
Running
Running
Add gradio app (#1)
Browse files* Add gradio app
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- .github/workflows/sync-hf.yml +20 -0
- .gitignore +2 -2
- .pre-commit-config.yaml +32 -0
- Dockerfile +14 -0
- README.md +12 -0
- main.py +96 -0
- requirements.txt +4 -0
.github/workflows/sync-hf.yml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Sync to Hugging Face hub
|
| 2 |
+
on:
|
| 3 |
+
push:
|
| 4 |
+
branches: [main]
|
| 5 |
+
|
| 6 |
+
# to run this workflow manually from the Actions tab
|
| 7 |
+
workflow_dispatch:
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
sync-to-hub:
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
steps:
|
| 13 |
+
- uses: actions/checkout@v4
|
| 14 |
+
with:
|
| 15 |
+
fetch-depth: 0
|
| 16 |
+
lfs: true
|
| 17 |
+
- name: Push to hub
|
| 18 |
+
env:
|
| 19 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 20 |
+
run: git push --force https://giswqs:$HF_TOKEN@huggingface.co/spaces/giswqs/ee-tile-request main
|
.gitignore
CHANGED
|
@@ -174,9 +174,9 @@ cython_debug/
|
|
| 174 |
.abstra/
|
| 175 |
|
| 176 |
# Visual Studio Code
|
| 177 |
-
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
| 178 |
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
| 179 |
-
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
| 180 |
# you could uncomment the following to ignore the enitre vscode folder
|
| 181 |
# .vscode/
|
| 182 |
|
|
|
|
| 174 |
.abstra/
|
| 175 |
|
| 176 |
# Visual Studio Code
|
| 177 |
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
| 178 |
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
| 179 |
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
| 180 |
# you could uncomment the following to ignore the enitre vscode folder
|
| 181 |
# .vscode/
|
| 182 |
|
.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
repos:
|
| 2 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 3 |
+
rev: v5.0.0
|
| 4 |
+
hooks:
|
| 5 |
+
- id: check-toml
|
| 6 |
+
- id: check-yaml
|
| 7 |
+
- id: end-of-file-fixer
|
| 8 |
+
types: [python]
|
| 9 |
+
- id: trailing-whitespace
|
| 10 |
+
- id: requirements-txt-fixer
|
| 11 |
+
- id: check-added-large-files
|
| 12 |
+
args: ["--maxkb=500"]
|
| 13 |
+
|
| 14 |
+
- repo: https://github.com/psf/black
|
| 15 |
+
rev: 25.1.0
|
| 16 |
+
hooks:
|
| 17 |
+
- id: black-jupyter
|
| 18 |
+
|
| 19 |
+
- repo: https://github.com/codespell-project/codespell
|
| 20 |
+
rev: v2.4.1
|
| 21 |
+
hooks:
|
| 22 |
+
- id: codespell
|
| 23 |
+
args:
|
| 24 |
+
[
|
| 25 |
+
"--ignore-words-list=aci,acount,acounts,fallow,ges,hart,hist,nd,ned,ois,wqs,watermask,tre,mape",
|
| 26 |
+
"--skip=*.csv,*.geojson,*.json,*.yml*.js,*.html,*cff,*.pdf",
|
| 27 |
+
]
|
| 28 |
+
|
| 29 |
+
- repo: https://github.com/kynan/nbstripout
|
| 30 |
+
rev: 0.8.1
|
| 31 |
+
hooks:
|
| 32 |
+
- id: nbstripout
|
Dockerfile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.12-slim
|
| 2 |
+
|
| 3 |
+
RUN apt-get update && apt-get install -y curl git npm && rm -rf /var/lib/apt/lists/*
|
| 4 |
+
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
COPY requirements.txt .
|
| 8 |
+
COPY main.py .
|
| 9 |
+
|
| 10 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 11 |
+
|
| 12 |
+
EXPOSE 7860
|
| 13 |
+
|
| 14 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Ee Tile Request
|
| 3 |
+
emoji: 😻
|
| 4 |
+
colorFrom: pink
|
| 5 |
+
colorTo: gray
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
short_description: Earth Engine Tile URL Generator
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
main.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import ee
|
| 4 |
+
import geemap
|
| 5 |
+
import gradio as gr
|
| 6 |
+
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 |
+
raise RuntimeError("EARTHENGINE_TOKEN environment variable not found")
|
| 14 |
+
|
| 15 |
+
try:
|
| 16 |
+
geemap.ee_initialize()
|
| 17 |
+
except Exception as e:
|
| 18 |
+
raise RuntimeError(f"Earth Engine authentication failed: {e}")
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# ---- Shared Tile Logic ----
|
| 22 |
+
def get_tile(asset_id, vis_params=None):
|
| 23 |
+
try:
|
| 24 |
+
if asset_id.startswith("ee."):
|
| 25 |
+
ee_object = eval(asset_id)
|
| 26 |
+
else:
|
| 27 |
+
data_dict = ee.data.getAsset(asset_id)
|
| 28 |
+
data_type = data_dict["type"]
|
| 29 |
+
if data_type == "IMAGE":
|
| 30 |
+
ee_object = ee.Image(asset_id)
|
| 31 |
+
elif data_type == "IMAGE_COLLECTION":
|
| 32 |
+
ee_object = ee.ImageCollection(asset_id)
|
| 33 |
+
elif data_type in ["TABLE", "TABLE_COLLECTION"]:
|
| 34 |
+
ee_object = ee.FeatureCollection(asset_id)
|
| 35 |
+
else:
|
| 36 |
+
raise ValueError(f"Unsupported data type: {data_type}")
|
| 37 |
+
|
| 38 |
+
if vis_params is None:
|
| 39 |
+
vis_params = {}
|
| 40 |
+
if isinstance(vis_params, str):
|
| 41 |
+
if len(vis_params) == 0:
|
| 42 |
+
vis_params = "{}"
|
| 43 |
+
if vis_params.startswith("{") and vis_params.endswith("}"):
|
| 44 |
+
vis_params = json.loads(vis_params)
|
| 45 |
+
else:
|
| 46 |
+
raise ValueError(f"Unsupported vis_params type: {type(vis_params)}")
|
| 47 |
+
elif isinstance(vis_params, dict):
|
| 48 |
+
pass
|
| 49 |
+
else:
|
| 50 |
+
raise ValueError(f"Unsupported vis_params type: {type(vis_params)}")
|
| 51 |
+
|
| 52 |
+
if "palette" in vis_params:
|
| 53 |
+
vis_params["palette"] = _validate_palette(vis_params["palette"])
|
| 54 |
+
|
| 55 |
+
url = _get_tile_url_format(ee_object, vis_params)
|
| 56 |
+
return url
|
| 57 |
+
except Exception as e:
|
| 58 |
+
return f"Error: {str(e)}"
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
# ---- FastAPI ----
|
| 62 |
+
app = FastAPI()
|
| 63 |
+
app.add_middleware(
|
| 64 |
+
CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class TileRequest(BaseModel):
|
| 69 |
+
asset_id: str
|
| 70 |
+
vis_params: dict | None = None
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
@app.post("/tile")
|
| 74 |
+
def get_tile_api(req: TileRequest):
|
| 75 |
+
result = get_tile(req.asset_id, req.vis_params)
|
| 76 |
+
if isinstance(result, str) and result.startswith("Error"):
|
| 77 |
+
raise HTTPException(status_code=400, detail=result)
|
| 78 |
+
return {"tile_url": result}
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
# ---- Gradio UI ----
|
| 82 |
+
gradio_ui = gr.Interface(
|
| 83 |
+
fn=get_tile,
|
| 84 |
+
inputs=[
|
| 85 |
+
gr.Textbox(label="Earth Engine Asset ID", placeholder="e.g., USGS/SRTMGL1_003"),
|
| 86 |
+
gr.Textbox(
|
| 87 |
+
label="Visualization Parameters (JSON)",
|
| 88 |
+
placeholder='{"min":0,"max":5000,"palette":"terrain"}',
|
| 89 |
+
),
|
| 90 |
+
],
|
| 91 |
+
outputs="text",
|
| 92 |
+
title="Earth Engine Tile URL Generator",
|
| 93 |
+
description="Supports ee.Image, ee.ImageCollection, ee.FeatureCollection. Tile URL is suitable for basemap usage.",
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
app = gr.mount_gradio_app(app, gradio_ui, path="/")
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
git+https://github.com/gee-community/geemap.git
|
| 3 |
+
gradio
|
| 4 |
+
uvicorn
|