cd_flux.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import os
  2. import json
  3. import tempfile
  4. import shutil
  5. import logging
  6. import sys
  7. from pathlib import Path
  8. from typing import Optional, Dict, Any
  9. from fastapi import APIRouter, File, UploadFile, Form, HTTPException, BackgroundTasks
  10. from fastapi.responses import FileResponse, JSONResponse
  11. from app.services.cd_flux_service import FluxCdVisualizationService
  12. # 固定文件名
  13. DEFAULT_MAP_FILENAME = "fluxcd_input_map.jpg"
  14. DEFAULT_HIST_FILENAME = "fluxcd_input_histogram.jpg"
  15. DEFAULT_CSV_FILENAME = "fluxcd_input.csv"
  16. LATEST_RESULT_FILENAME = "latest_result.json"
  17. # 配置日志
  18. logger = logging.getLogger(__name__)
  19. logger.setLevel(logging.INFO)
  20. handler = logging.StreamHandler()
  21. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  22. handler.setFormatter(formatter)
  23. logger.addHandler(handler)
  24. router = APIRouter()
  25. def get_base_dir():
  26. """获取基础目录路径(与土地数据处理函数一致)"""
  27. if getattr(sys, 'frozen', False):
  28. # 打包后的可执行文件
  29. return os.path.dirname(sys.executable)
  30. else:
  31. # 脚本运行模式
  32. return os.path.dirname(os.path.abspath(__file__))
  33. def get_static_dir():
  34. """获取静态资源目录"""
  35. base_dir = get_base_dir()
  36. return os.path.join(base_dir, "..", "static", "cd_flux")
  37. STATIC_DIR = get_static_dir() # 静态目录,即static/cd_flux
  38. def get_default_file_path(filename: str) -> str:
  39. """获取默认文件路径"""
  40. static_dir = get_static_dir()
  41. path = os.path.join(static_dir, filename)
  42. return path
  43. @router.post("/calculate",
  44. summary="上传CSV文件更新数据并生成Cd通量可视化结果",
  45. description="上传包含镉通量数据的CSV文件,更新数据库,并生成空间分布图和直方图")
  46. async def update_and_generate_fluxcd_visualization(
  47. background_tasks: BackgroundTasks,
  48. csv_file: UploadFile = File(..., description="CSV文件,包含经纬度和通量数据"),
  49. boundary_shp: Optional[str] = Form(None, description="可选,边界SHP文件路径(如果使用默认则不提供)")
  50. ) -> Dict[str, Any]:
  51. try:
  52. logger.info("开始处理Cd通量数据更新和可视化")
  53. # 创建临时目录保存上传的CSV文件
  54. temp_dir = tempfile.mkdtemp()
  55. temp_csv_path = os.path.join(temp_dir, csv_file.filename)
  56. # 保存上传的文件
  57. with open(temp_csv_path, "wb") as f:
  58. content = await csv_file.read()
  59. f.write(content)
  60. # 初始化服务
  61. service = FluxCdVisualizationService()
  62. # 步骤1: 更新数据
  63. update_result = service.update_from_csv(temp_csv_path)
  64. if not update_result.get("success"):
  65. logger.error(f"更新数据失败: {update_result.get('message')}")
  66. raise HTTPException(status_code=500, detail=update_result.get('message'))
  67. # 步骤2: 生成可视化结果
  68. # 使用静态目录作为输出目录
  69. static_dir = get_static_dir()
  70. os.makedirs(static_dir, exist_ok=True)
  71. generation_result = service.generate_cd_input_flux_map(
  72. output_dir=static_dir,
  73. boundary_shp=boundary_shp
  74. )
  75. return generation_result
  76. except HTTPException as he:
  77. raise he
  78. except Exception as e:
  79. logger.error(f"处理过程中出错: {str(e)}", exc_info=True)
  80. raise HTTPException(status_code=500, detail=f"服务器内部错误: {str(e)}")
  81. finally:
  82. # 清理临时目录(使用后台任务)
  83. background_tasks.add_task(shutil.rmtree, temp_dir, ignore_errors=True)
  84. @router.get("/map",
  85. summary="获取Cd输入通量空间分布图",
  86. description="返回默认的Cd输入通量空间分布图")
  87. async def get_fluxcd_map() -> FileResponse:
  88. try:
  89. # 直接返回静态目录中的默认地图
  90. map_path = get_default_file_path("fluxcd_input_map.jpg")
  91. if os.path.exists(map_path):
  92. return FileResponse(map_path, media_type="image/jpeg")
  93. else:
  94. raise HTTPException(status_code=404, detail="默认地图不存在,请先生成")
  95. except Exception as e:
  96. logger.error(f"获取地图失败: {str(e)}")
  97. raise HTTPException(status_code=500, detail=f"获取地图失败: {str(e)}")
  98. @router.get("/histogram",
  99. summary="获取Cd输入通量直方图",
  100. description="返回默认的Cd输入通量直方图")
  101. async def get_fluxcd_histogram() -> FileResponse:
  102. try:
  103. # 直接返回静态目录中的默认直方图
  104. hist_path = get_default_file_path("fluxcd_input_histogram.jpg")
  105. if os.path.exists(hist_path):
  106. return FileResponse(hist_path, media_type="image/jpeg")
  107. else:
  108. raise HTTPException(status_code=404, detail="默认直方图不存在,请先生成")
  109. except Exception as e:
  110. logger.error(f"获取直方图失败: {str(e)}")
  111. raise HTTPException(status_code=500, detail=f"获取直方图失败: {str(e)}")
  112. @router.get("/statistics",
  113. summary="获取Cd输入通量统计信息",
  114. description="返回默认的Cd输入通量统计信息")
  115. @router.get("/statistics",
  116. summary="获取Cd输入通量统计信息",
  117. description="返回最新生成的Cd输入通量统计信息")
  118. async def get_fluxcd_statistics() -> JSONResponse:
  119. try:
  120. latest_result_path = os.path.join(STATIC_DIR, LATEST_RESULT_FILENAME)
  121. if not os.path.exists(latest_result_path):
  122. raise HTTPException(status_code=404, detail="无可用统计信息,请先生成")
  123. with open(latest_result_path, "r", encoding="utf-8") as f:
  124. result = json.load(f)
  125. if not result.get("success"):
  126. raise HTTPException(status_code=404, detail="结果无效")
  127. stats = result.get("data", {}).get("statistics")
  128. if stats:
  129. return JSONResponse(content=stats)
  130. else:
  131. raise HTTPException(status_code=404, detail="统计信息不存在")
  132. except Exception as e:
  133. logger.error(f"获取统计信息失败: {str(e)}")
  134. raise HTTPException(status_code=500, detail=f"获取统计信息失败: {str(e)}")
  135. @router.get("/export-csv",
  136. summary="导出fluxcd_input.csv文件",
  137. description="导出默认的fluxcd_input.csv文件")
  138. async def export_fluxcd_csv() -> FileResponse:
  139. try:
  140. # 直接返回静态目录中的CSV文件
  141. csv_path = get_default_file_path("fluxcd_input.csv")
  142. if os.path.exists(csv_path):
  143. return FileResponse(
  144. csv_path,
  145. filename="fluxcd_input.csv",
  146. media_type="text/csv"
  147. )
  148. else:
  149. raise HTTPException(status_code=404, detail="CSV文件不存在,请先生成")
  150. except Exception as e:
  151. logger.error(f"导出CSV失败: {str(e)}")
  152. raise HTTPException(status_code=500, detail=f"导出CSV失败: {str(e)}")