添加火星TMS服务
This commit is contained in:
98
app_mars.py
Normal file
98
app_mars.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
"""rio-tiler dynamic tile server for Mars MGS MOLA ClrShade global basemap"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response
|
||||||
|
|
||||||
|
from pyproj import CRS
|
||||||
|
from morecantile import TileMatrixSet
|
||||||
|
|
||||||
|
from rio_tiler.errors import TileOutsideBounds
|
||||||
|
from rio_tiler.io import Reader
|
||||||
|
from rio_tiler.profiles import img_profiles
|
||||||
|
|
||||||
|
DATA_PATH = str(
|
||||||
|
Path(__file__).parent / "data" / "Mars_MGS_MOLA_ClrShade_merge_global_463m.tif"
|
||||||
|
)
|
||||||
|
|
||||||
|
# TIF 的原生 CRS 是 SimpleCylindrical Mars(等距柱状投影,单位:米)
|
||||||
|
# 用 proj4 字符串定义,直接匹配 TIF —— 参考 lunaserv-python 的 proj4 字符串方式
|
||||||
|
#
|
||||||
|
# 关键设计决策:TMS 与 TIF 使用同一个 CRS(eqc 米制),rio-tiler 读取时无需任何跨 CRS 转换。
|
||||||
|
# 若改用 longlat(度),PROJ 会尝试通过 WGS84 做 datum shift:火星球体(R=3396km) vs
|
||||||
|
# 地球椭球(R=6378km) 差异极大,导致中间计算出现 "PROJ: eqc: Invalid latitude" 错误。
|
||||||
|
MARS_CRS = CRS.from_proj4(
|
||||||
|
"+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +a=3396190 +b=3396190 +units=m +no_defs"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 火星球体在等距柱状投影下的全球范围(米)
|
||||||
|
# 等距柱状: x = R × lon_rad, y = R × lat_rad
|
||||||
|
# → max_x = R × π(对应 180°),max_y = R × π/2(对应 90°)
|
||||||
|
_R = 3396190
|
||||||
|
_MAX_X = _R * math.pi # ≈ 10,669,442 m
|
||||||
|
_MAX_Y = _R * math.pi / 2 # ≈ 5,334,721 m
|
||||||
|
|
||||||
|
# WorldCRS84Quad 等效 TMS(米制版本):
|
||||||
|
# - 范围覆盖全球等距柱状投影
|
||||||
|
# - matrix_scale=[2, 1] → zoom 0 有 2×1 个瓦片,与 Cesium GeographicTilingScheme(2×1) 对齐
|
||||||
|
# - 瓦片编号 (z, x, y) 与地理位置的对应关系和经纬度版完全一致,Cesium 无需任何修改
|
||||||
|
MARS_TMS = TileMatrixSet.custom(
|
||||||
|
crs=MARS_CRS,
|
||||||
|
extent=(-_MAX_X, -_MAX_Y, _MAX_X, _MAX_Y),
|
||||||
|
matrix_scale=[2, 1],
|
||||||
|
)
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title="rio-tiler Mars",
|
||||||
|
description="Mars MGS MOLA Color-Shaded Relief global tile server",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get(
|
||||||
|
"/tiles/{z}/{x}/{y}.png",
|
||||||
|
responses={
|
||||||
|
200: {"content": {"image/png": {}}, "description": "Return a PNG tile."},
|
||||||
|
404: {"description": "Tile outside bounds."},
|
||||||
|
},
|
||||||
|
response_class=Response,
|
||||||
|
description="Read Mars tile and return PNG (uint8 RGB, no rescaling needed)",
|
||||||
|
)
|
||||||
|
def tile(z: int, x: int, y: int):
|
||||||
|
"""Return a map tile for the given z/x/y coordinates."""
|
||||||
|
try:
|
||||||
|
with Reader(DATA_PATH, tms=MARS_TMS) as cog:
|
||||||
|
img = cog.tile(x, y, z)
|
||||||
|
except TileOutsideBounds:
|
||||||
|
raise HTTPException(status_code=404, detail="Tile outside bounds")
|
||||||
|
|
||||||
|
content = img.render(
|
||||||
|
img_format="PNG",
|
||||||
|
**img_profiles.get("png", {}),
|
||||||
|
)
|
||||||
|
return Response(content, media_type="image/png")
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/tilejson.json", responses={200: {"description": "Return a TileJSON document"}})
|
||||||
|
def tilejson(request: Request):
|
||||||
|
"""Return a TileJSON 2.2.0 document."""
|
||||||
|
with Reader(DATA_PATH, tms=MARS_TMS) as cog:
|
||||||
|
bounds = cog.get_geographic_bounds(cog.tms.rasterio_geographic_crs)
|
||||||
|
minzoom = cog.minzoom
|
||||||
|
maxzoom = cog.maxzoom
|
||||||
|
|
||||||
|
base_url = str(request.base_url).rstrip("/")
|
||||||
|
tile_url = f"{base_url}/tiles/{{z}}/{{x}}/{{y}}.png"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"tilejson": "2.2.0",
|
||||||
|
"name": "Mars MGS MOLA ClrShade",
|
||||||
|
"description": "Mars MGS MOLA Color-Shaded Relief Global Mosaic 463m/px",
|
||||||
|
"tiles": [tile_url],
|
||||||
|
"bounds": list(bounds),
|
||||||
|
"minzoom": minzoom,
|
||||||
|
"maxzoom": maxzoom,
|
||||||
|
"center": [0, 0, minzoom],
|
||||||
|
}
|
||||||
1
lunaserv-python
Submodule
1
lunaserv-python
Submodule
Submodule lunaserv-python added at b60eac11a5
Reference in New Issue
Block a user