mcgibs/scripts/fetch_readme_images.py
Ryan Malloy dcd75dad44 Fix polar projection bbox transform and image format auto-detection
- Add polar stereographic coordinate transform (EPSG:3413/3031) in geo.py
  so geographic bboxes are properly converted to projected meters
- Auto-detect image format: PNG for colormapped layers (preserves exact
  colors for query_point reverse-mapping), JPEG for true-color
- get_imagery format parameter now defaults to "auto" instead of "jpeg"
- Embed NDVI seasonal timelapse and polar stereo images in README
- 96 tests passing
2026-02-19 12:49:24 -07:00

141 lines
5.2 KiB
Python

"""Fetch example images for the README via the mcgibs MCP server.
Uses the FastMCP in-process Client to call tools through the MCP protocol,
exactly as any MCP client would. Images are saved to docs/images/.
"""
import asyncio
import base64
import sys
from pathlib import Path
from fastmcp import Client
# Wire up the server module so the client is initialized
import mcgibs.server as server_module
from mcgibs.client import GIBSClient
from mcgibs.server import mcp
OUT = Path(__file__).parent.parent / "docs" / "images"
async def save_imagery(client: Client, filename: str, tool: str, params: dict) -> None:
"""Call an imagery tool and save the returned image to disk."""
print(f" Fetching {filename}...", file=sys.stderr)
result = await client.call_tool(tool, params)
# Find the ImageContent in the response
for content in result.content:
if content.type == "image":
img_bytes = base64.b64decode(content.data)
path = OUT / filename
path.write_bytes(img_bytes)
size_kb = len(img_bytes) / 1024
print(f" Saved {path} ({size_kb:.0f} KB)", file=sys.stderr)
return
# If we got text only (error case), print it
for content in result.content:
if content.type == "text":
print(f" Warning: {content.text[:200]}", file=sys.stderr)
async def main():
OUT.mkdir(parents=True, exist_ok=True)
# Initialize GIBS client (normally done by middleware on first connection)
print("Initializing GIBS client...", file=sys.stderr)
gibs = GIBSClient()
await gibs.initialize()
server_module._client = gibs
print(f"Ready — {len(gibs.layer_index)} layers loaded", file=sys.stderr)
try:
async with Client(mcp) as client:
# 1. Amazon true color — natural satellite view
await save_imagery(client, "amazon-true-color.jpg", "get_imagery", {
"layer_id": "MODIS_Terra_CorrectedReflectance_TrueColor",
"date": "2025-08-01",
"place": "Amazon Rainforest",
"width": 1024,
"height": 768,
"format": "jpeg",
})
# 2. Global surface air temperature — scientific data product
await save_imagery(client, "temperature-global.jpg", "get_imagery", {
"layer_id": "AIRS_L3_Surface_Air_Temperature_Daily_Day",
"date": "2025-07-15",
"bbox": [-180, -90, 180, 90],
"width": 1024,
"height": 512,
"format": "jpeg",
})
# 3. Arctic sea ice comparison — March (max) vs September (min)
await save_imagery(
client, "arctic-ice-comparison.jpg", "compare_dates", {
"layer_id": "AMSRU2_Sea_Ice_Concentration_12km",
"date_before": "2025-03-01",
"date_after": "2025-09-01",
"bbox": [-180, 60, 180, 90],
},
)
# 4. Surface temperature colormap legend
await save_imagery(
client, "temperature-legend.png", "get_legend", {
"layer_id": "AIRS_L3_Surface_Air_Temperature_Daily_Day",
"orientation": "horizontal",
},
)
# 5. Colormap explanation (text, not image)
print(" Fetching temperature colormap explanation...", file=sys.stderr)
result = await client.call_tool(
"explain_layer_colormap",
{"layer_id": "AIRS_L3_Surface_Air_Temperature_Daily_Day"},
)
explanation = result.content[0].text
(OUT / "temperature-colormap.txt").write_text(explanation)
print(" Saved colormap explanation", file=sys.stderr)
# 6. Arctic sea ice in polar stereographic projection (PNG for clean colors)
await save_imagery(
client, "arctic-polar-stereo.png", "get_imagery", {
"layer_id": "AMSRU2_Sea_Ice_Concentration_12km",
"date": "2025-03-01",
"bbox": [-180, 60, 180, 90],
"width": 1024,
"height": 1024,
"projection": "3413",
},
)
# 7. NDVI time series — 3 frames (March, June, September)
# Use explicit bbox for the Great Plains (Texas panhandle → Dakotas)
# instead of geocoding, which returns a point-sized bbox.
plains_bbox = [-104, 35, -95, 45]
ndvi_dates = [
("2025-03-01", "ndvi-march.png"),
("2025-06-01", "ndvi-june.png"),
("2025-09-01", "ndvi-september.png"),
]
for ndvi_date, ndvi_name in ndvi_dates:
await save_imagery(client, ndvi_name, "get_imagery", {
"layer_id": "MODIS_Terra_NDVI_8Day",
"date": ndvi_date,
"bbox": plains_bbox,
"width": 768,
"height": 768,
})
finally:
await gibs.close()
server_module._client = None
print("Done!", file=sys.stderr)
asyncio.run(main())