Files
xian_algorithm_new/app/api/earthquake.py
T

129 lines
4.8 KiB
Python
Raw Normal View History

2026-06-06 08:38:19 +08:00
"""
地震灾害链预测接口
"""
import asyncio
from typing import List, Dict, Any, Optional
from fastapi import APIRouter, HTTPException
2026-06-14 16:00:33 +08:00
from app.schemas.api_schemas import EarthquakePredictRequest, PredictResponse, PredictData
2026-06-06 08:38:19 +08:00
from app.utils.api_deps import get_earthquake_model, get_prediction_semaphore
from app.repositories.dbn_repository import dbn_repository
from app.config.paths import get_logger
router = APIRouter(prefix="/earthquake", tags=["地震灾害链"])
logger = get_logger("api.earthquake")
SOURCE_TYPE_MAP = {1: "隐患点", 2: "风险点"}
LEVEL_MAP = {"": "", "": "", "较高": "较高", "": ""}
2026-06-14 15:31:42 +08:00
def _build_prediction_map(results: List[Dict[str, Any]]) -> Dict[str, float]:
"""将模型原始结果转换为存储格式: {id_type: 概率百分比}"""
result_map = {}
2026-06-06 08:38:19 +08:00
for r in results:
probs = r.get("disaster_probabilities", {})
if not probs:
continue
2026-06-14 15:31:42 +08:00
source_id = r["source_id"]
source_type = r.get("source_type")
2026-06-06 08:38:19 +08:00
max_hazard = max(probs, key=probs.get)
2026-06-14 15:31:42 +08:00
# key 格式: {source_id}_{source_type}value 为百分比概率
key = f"{source_id}_{source_type}"
result_map[key] = round(probs[max_hazard] * 100, 2)
return result_map
2026-06-06 08:38:19 +08:00
def _fetch_points(point_ids: Optional[List[int]], region_code: Optional[str]) -> List[Dict[str, Any]]:
"""获取点位列表"""
if point_ids:
return dbn_repository.get_points_by_ids(point_ids)
return dbn_repository.get_all_points(region_code)
def _predict_sync(point_ids: Optional[List[int]], region_code: Optional[str],
magnitude: float, depth: float,
2026-06-06 13:18:25 +08:00
epicenter_lon: float, epicenter_lat: float) -> tuple:
"""
同步执行地震预测(在线程池中运行)
Returns:
2026-06-14 15:31:42 +08:00
(结果map,)
2026-06-06 13:18:25 +08:00
"""
2026-06-06 08:38:19 +08:00
points = _fetch_points(point_ids, region_code)
if not points:
2026-06-14 15:31:42 +08:00
return {}
2026-06-06 08:38:19 +08:00
model = get_earthquake_model()
2026-06-06 13:18:25 +08:00
raw_results = model.predict_multiple_points(
2026-06-06 08:38:19 +08:00
points,
magnitude=magnitude,
depth=depth,
epicenter_lon=epicenter_lon,
epicenter_lat=epicenter_lat,
)
2026-06-14 15:31:42 +08:00
result_map = _build_prediction_map(raw_results)
2026-06-06 13:18:25 +08:00
2026-06-14 15:31:42 +08:00
return result_map
2026-06-06 08:38:19 +08:00
@router.post("/predict", response_model=PredictResponse, summary="地震灾害链预测")
async def predict_earthquake(req: EarthquakePredictRequest):
"""
2026-06-14 15:31:42 +08:00
根据震级、震源深度和震中位置,批量预测隐患点/风险点的次生灾害概率。
2026-06-06 08:38:19 +08:00
2026-06-14 14:38:20 +08:00
- **disaster_name**: 灾害名称
2026-06-06 08:38:19 +08:00
- **point_ids**: 点位ID列表(可选,不传则查询所有点)
- **region_code**: 行政区划代码(可选,不传则不限区域)
- **magnitude**: 震级(Richter)
- **depth**: 震源深度(km),默认10km
- **epicenter_lon**: 震中经度
- **epicenter_lat**: 震中纬度
2026-06-06 13:18:25 +08:00
- **occurred_time**: 地震发生时间
- **operation_type**: 操作类型(如 '实时监测', '情景模拟', '应急评估'
2026-06-06 08:38:19 +08:00
"""
semaphore = get_prediction_semaphore()
async with semaphore:
loop = asyncio.get_event_loop()
try:
2026-06-14 15:31:42 +08:00
result_map = await loop.run_in_executor(
2026-06-06 08:38:19 +08:00
None, _predict_sync, req.point_ids, req.region_code,
req.magnitude, req.depth, req.epicenter_lon, req.epicenter_lat
)
except Exception as e:
logger.error(f"地震预测失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"预测失败: {e}")
2026-06-06 13:18:25 +08:00
# 保存推理结果
record_id = None
2026-06-14 15:31:42 +08:00
if result_map:
2026-06-06 13:18:25 +08:00
try:
2026-06-14 16:29:01 +08:00
# 使用传入的 occurred_time,如果未传则使用当前时间
from datetime import datetime
occurred_time = req.occurred_time if req.occurred_time else datetime.now()
# 存储经过默认值处理的条件
2026-06-06 13:18:25 +08:00
condition = {
"point_ids": req.point_ids,
"region_code": req.region_code,
"magnitude": req.magnitude,
2026-06-14 15:52:33 +08:00
"depth": req.depth, # 已有默认值 10.0
2026-06-06 13:18:25 +08:00
"epicenter_lon": req.epicenter_lon,
2026-06-14 16:29:01 +08:00
"epicenter_lat": req.epicenter_lat,
"occurred_time": occurred_time.isoformat() if hasattr(occurred_time, 'isoformat') else str(occurred_time)
2026-06-06 13:18:25 +08:00
}
record_id = dbn_repository.save_inference_result(
2026-06-14 14:38:20 +08:00
disaster_name=req.disaster_name,
2026-06-06 13:18:25 +08:00
event_type="earthquake",
2026-06-14 16:29:01 +08:00
occurred_time=occurred_time,
2026-06-06 13:18:25 +08:00
operation_type=req.operation_type,
condition=condition,
2026-06-14 15:31:42 +08:00
result=result_map
2026-06-06 13:18:25 +08:00
)
logger.info(f"推理结果已保存,record_id={record_id}")
except Exception as e:
logger.error(f"保存推理结果失败: {e}", exc_info=True)
2026-06-14 16:00:33 +08:00
return PredictResponse(code=200, message="success", data=PredictData(record_id=record_id, list=result_map))