new branch

This commit is contained in:
rayd1o
2026-03-07 13:06:37 +08:00
parent 3145ff083b
commit 4ada75ca14
64 changed files with 4324 additions and 35 deletions

View File

@@ -10,6 +10,7 @@ from app.api.v1 import (
alerts,
settings,
collected_data,
visualization,
)
api_router = APIRouter()
@@ -25,3 +26,4 @@ api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"])
api_router.include_router(dashboard.router, prefix="/dashboard", tags=["dashboard"])
api_router.include_router(alerts.router, prefix="/alerts", tags=["alerts"])
api_router.include_router(settings.router, prefix="/settings", tags=["settings"])
api_router.include_router(visualization.router, prefix="/visualization", tags=["visualization"])

View File

@@ -0,0 +1,81 @@
"""Visualization API - GeoJSON endpoints for 3D Earth display"""
from fastapi import APIRouter, HTTPException
import httpx
router = APIRouter()
CABLE_DATA_URL = "https://services.arcgis.com/6DIQcwlPy8knb6sg/arcgis/rest/services/SubmarineCables/FeatureServer/2/query?where=1%3D1&outFields=*&returnGeometry=true&f=geojson"
LANDING_POINT_CSV_URL = "https://data.apps.fao.org/catalog/dataset/1b75ff21-92f2-4b96-9b7b-98e8aa65ad5d/resource/b6071077-d1d4-4e97-aa00-42e902847c87/download/landing-point-geo.csv"
@router.get("/geo/cables")
async def get_cables_geojson():
"""获取海底电缆 GeoJSON 数据 (LineString)"""
try:
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.get(CABLE_DATA_URL)
response.raise_for_status()
return response.json()
except httpx.HTTPError as e:
raise HTTPException(status_code=502, detail=f"Failed to fetch cable data: {str(e)}")
except Exception as e:
raise HTTPException(status_code=500, detail=f"Internal error: {str(e)}")
@router.get("/geo/landing-points")
async def get_landing_points_geojson():
"""获取登陆点 GeoJSON 数据 (Point)"""
try:
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.get(LANDING_POINT_CSV_URL)
response.raise_for_status()
lines = response.text.strip().split("\n")
if not lines:
raise HTTPException(status_code=500, detail="Empty CSV data")
features = []
for line in lines[1:]:
if not line.strip():
continue
parts = line.split(",")
if len(parts) >= 4:
try:
lon = float(parts[0])
lat = float(parts[1])
feature_id = parts[2]
name = parts[3].strip('"')
is_tbd = parts[4].strip() == "true" if len(parts) > 4 else False
features.append(
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [lon, lat]},
"properties": {"id": feature_id, "name": name, "is_tbd": is_tbd},
}
)
except (ValueError, IndexError):
continue
return {"type": "FeatureCollection", "features": features}
except httpx.HTTPError as e:
raise HTTPException(status_code=502, detail=f"Failed to fetch landing point data: {str(e)}")
except Exception as e:
raise HTTPException(status_code=500, detail=f"Internal error: {str(e)}")
@router.get("/geo/all")
async def get_all_geojson():
"""获取所有可视化数据 (电缆 + 登陆点)"""
cables = await get_cables_geojson()
points = await get_landing_points_geojson()
return {
"cables": cables,
"landing_points": points,
"stats": {
"cable_count": len(cables.get("features", [])) if cables else 0,
"landing_point_count": len(points.get("features", [])) if points else 0,
},
}