فهرست منبع

补充当年cd浓度和净通量计算逻辑

yangtaodemon 5 روز پیش
والد
کامیت
422c587837
4فایلهای تغییر یافته به همراه264 افزوده شده و 117 حذف شده
  1. 107 31
      app/api/cd_flux.py
  2. 52 2
      app/models/FluxCd_output.py
  3. 64 62
      app/services/cd_flux_service.py
  4. 41 22
      app/static/cd_flux/latest_result.json

+ 107 - 31
app/api/cd_flux.py

@@ -67,54 +67,55 @@ def get_default_file_path(filename: str) -> str:
     return path
 
 
-@router.post("/calculate",
-             summary="上传CSV文件更新数据并生成Cd通量可视化结果",
-             description="上传包含镉通量数据的CSV文件,更新数据库,并生成空间分布图和直方图")
+@router.post("/calculate")
 async def update_and_generate_fluxcd_visualization(
         background_tasks: BackgroundTasks,
-        csv_file: UploadFile = File(..., description="CSV文件,包含经纬度和通量数据"),
-        boundary_shp: Optional[str] = Form(None, description="可选,边界SHP文件路径(如果使用默认则不提供)")
+        csv_file: Optional[UploadFile] = File(None),
+        boundary_shp: Optional[str] = Form(None)
 ) -> Dict[str, Any]:
     try:
-        logger.info("开始处理Cd通量数据更新和可视化")
-        # 创建临时目录保存上传的CSV文件
-        temp_dir = tempfile.mkdtemp()
-        temp_csv_path = os.path.join(temp_dir, csv_file.filename)
-
-        # 保存上传的文件
-        with open(temp_csv_path, "wb") as f:
-            content = await csv_file.read()
-            f.write(content)
+        logger.info("开始处理Cd通量数据可视化")
 
         # 初始化服务
         service = FluxCdVisualizationService()
-
-        # 步骤1: 更新数据
-        update_result = service.update_from_csv(temp_csv_path)
-        if not update_result.get("success"):
-            logger.error(f"更新数据失败: {update_result.get('message')}")
-            raise HTTPException(status_code=500, detail=update_result.get('message'))
-
-        # 步骤2: 生成可视化结果
-        # 使用静态目录作为输出目录
         static_dir = get_static_dir()
         os.makedirs(static_dir, exist_ok=True)
 
-        # 生成三种通量结果
+        # 如果有上传文件,先更新数据
+        if csv_file:
+            logger.info("检测到上传文件,开始更新数据")
+            temp_dir = tempfile.mkdtemp()
+            temp_csv_path = os.path.join(temp_dir, csv_file.filename)
+
+            with open(temp_csv_path, "wb") as f:
+                content = await csv_file.read()
+                f.write(content)
+
+            update_result = service.update_from_csv(temp_csv_path)
+            if not update_result.get("success"):
+                logger.error(f"更新数据失败: {update_result.get('message')}")
+                raise HTTPException(status_code=500, detail=update_result.get('message'))
+
+            # 清理临时文件
+            background_tasks.add_task(shutil.rmtree, temp_dir, ignore_errors=True)
+
+        # 生成所有结果(直接从数据库获取数据)
         input_result = service.generate_cd_input_flux_map(
             output_dir=static_dir,
             boundary_shp=boundary_shp
         )
-
         output_result = service.generate_cd_output_flux_map(
             output_dir=static_dir,
             boundary_shp=boundary_shp
         )
-
         net_result = service.generate_cd_net_flux_map(
             output_dir=static_dir,
             boundary_shp=boundary_shp
         )
+        end_cd_result = service.generate_end_cd_map(
+            output_dir=static_dir,
+            boundary_shp=boundary_shp
+        )
 
         # 保存最新结果信息
         latest_result_path = os.path.join(static_dir, LATEST_RESULT_FILENAME)
@@ -122,13 +123,15 @@ async def update_and_generate_fluxcd_visualization(
             json.dump({
                 "input": input_result,
                 "output": output_result,
-                "net": net_result
+                "net": net_result,
+                "end_cd": end_cd_result
             }, f, ensure_ascii=False, indent=2)
 
         return {
             "input": input_result,
             "output": output_result,
-            "net": net_result
+            "net": net_result,
+            "end_cd": end_cd_result
         }
 
     except HTTPException as he:
@@ -136,11 +139,84 @@ async def update_and_generate_fluxcd_visualization(
     except Exception as e:
         logger.error(f"处理过程中出错: {str(e)}", exc_info=True)
         raise HTTPException(status_code=500, detail=f"服务器内部错误: {str(e)}")
-    finally:
-        # 清理临时目录(使用后台任务)
-        background_tasks.add_task(shutil.rmtree, temp_dir, ignore_errors=True)
 
 
+# 添加常量定义
+DEFAULT_END_CD_MAP_FILENAME = "fluxcd_end_cd_map.jpg"
+DEFAULT_END_CD_HIST_FILENAME = "fluxcd_end_cd_histogram.jpg"
+DEFAULT_END_CD_CSV_FILENAME = "fluxcd_end_cd.csv"
+
+# 添加API路由
+@router.get("/end-cd/map",
+            summary="获取当年Cd浓度空间分布图",
+            description="返回当年Cd浓度的空间分布图")
+async def get_end_cd_map() -> FileResponse:
+    try:
+        map_path = get_default_file_path(DEFAULT_END_CD_MAP_FILENAME)
+        if os.path.exists(map_path):
+            return FileResponse(map_path, media_type="image/jpeg")
+        else:
+            raise HTTPException(status_code=404, detail="当年Cd浓度地图不存在,请先生成")
+    except Exception as e:
+        logger.error(f"获取当年Cd浓度地图失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"获取当年Cd浓度地图失败: {str(e)}")
+
+@router.get("/end-cd/histogram",
+            summary="获取当年Cd浓度直方图",
+            description="返回当年Cd浓度的直方图")
+async def get_end_cd_histogram() -> FileResponse:
+    try:
+        hist_path = get_default_file_path(DEFAULT_END_CD_HIST_FILENAME)
+        if os.path.exists(hist_path):
+            return FileResponse(hist_path, media_type="image/jpeg")
+        else:
+            raise HTTPException(status_code=404, detail="当年Cd浓度直方图不存在,请先生成")
+    except Exception as e:
+        logger.error(f"获取当年Cd浓度直方图失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"获取当年Cd浓度直方图失败: {str(e)}")
+
+@router.get("/end-cd/statistics",
+            summary="获取当年Cd浓度统计信息",
+            description="返回当年Cd浓度的统计信息")
+async def get_end_cd_statistics() -> JSONResponse:
+    try:
+        latest_result_path = os.path.join(STATIC_DIR, LATEST_RESULT_FILENAME)
+        if not os.path.exists(latest_result_path):
+            raise HTTPException(status_code=404, detail="无可用统计信息,请先生成")
+
+        with open(latest_result_path, "r", encoding="utf-8") as f:
+            result = json.load(f)
+
+        if not result.get("end_cd") or not result["end_cd"].get("success"):
+            raise HTTPException(status_code=404, detail="当年Cd浓度结果无效")
+
+        stats = result["end_cd"].get("data", {}).get("statistics")
+        if stats:
+            return JSONResponse(content=stats)
+        else:
+            raise HTTPException(status_code=404, detail="当年Cd浓度统计信息不存在")
+    except Exception as e:
+        logger.error(f"获取当年Cd浓度统计信息失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"获取当年Cd浓度统计信息失败: {str(e)}")
+
+@router.get("/end-cd/export-csv",
+            summary="导出当年Cd浓度CSV文件",
+            description="导出当年Cd浓度的CSV文件")
+async def export_end_cd_csv() -> FileResponse:
+    try:
+        csv_path = get_default_file_path(DEFAULT_END_CD_CSV_FILENAME)
+        if os.path.exists(csv_path):
+            return FileResponse(
+                csv_path,
+                filename="fluxcd_end_cd.csv",
+                media_type="text/csv"
+            )
+        else:
+            raise HTTPException(status_code=404, detail="当年Cd浓度CSV文件不存在,请先生成")
+    except Exception as e:
+        logger.error(f"导出当年Cd浓度CSV失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"导出当年Cd浓度CSV失败: {str(e)}")
+
 @router.get("/input/map",
             summary="获取Cd输入通量空间分布图",
             description="返回默认的Cd输入通量空间分布图")

+ 52 - 2
app/models/FluxCd_output.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float, ForeignKeyConstraint
 from app.database import Base
-from app.models import FluxCdInputData
+from app.models import FluxCdInputData, SoilData
 
 
 class FluxCdOutputData(Base):
@@ -38,4 +38,54 @@ class FluxCdOutputData(Base):
         """根据输入数据计算通量"""
         self.in_cd = input_data.input_flux()
         self.out_cd = input_data.output_flux()  # 使用输出通量方法
-        self.net_cd = self.in_cd - self.out_cd
+        self.net_cd = self.in_cd - self.out_cd
+
+    # 在 app/models/effcd_output.py 的 FluxCdOutputData 类中添加以下方法
+    def calculate_end_cd(self, db_session):
+        """
+        计算当年Cd浓度(mg/kg)
+        公式: (Initial_Cd + Net_Cd) / (2000 * bd020_90)
+
+        @param db_session: 数据库会话
+        @return: 计算后的Cd浓度值
+        """
+        try:
+            # 1. 获取初始Cd浓度(Initial_Cd) - 从FluxCdInputData表
+            input_data = db_session.query(FluxCdInputData).filter(
+                FluxCdInputData.farmland_id == self.farmland_id,
+                FluxCdInputData.sample_id == self.sample_id
+            ).first()
+
+            if not input_data:
+                raise ValueError("未找到对应的输入通量数据")
+
+            initial_cd = input_data.initial_cd  # 单位: g/ha
+
+            # 2. 获取土壤容重(bd020_90) - 从SoilData表
+            soil_data = db_session.query(SoilData).filter(
+                SoilData.farmland_id == self.farmland_id,
+                SoilData.sample_id == self.sample_id
+            ).first()
+
+            if not soil_data:
+                raise ValueError("未找到对应的土壤数据")
+
+            bd020_90 = soil_data.bd020_90  # 单位: g/cm³
+
+            # 3. 计算当年Cd浓度
+            # 注意单位转换:
+            # - Initial_Cd和Net_Cd单位是g/ha
+            # - 1 ha = 10,000 m²
+            # - 假设计算深度为20cm(0.2m),所以体积为2000 m³/ha
+            # - bd020_90单位是g/cm³ = 1000 kg/m³
+            # 公式推导:
+            # (g/ha) / (2000 m³/ha * 1000 kg/m³) = (g/ha) / (2,000,000 kg/ha) = mg/kg
+
+            end_cd = (initial_cd + self.net_cd) / (2000 * bd020_90)
+
+            # 更新并返回结果
+            self.end_cd = end_cd
+            return end_cd
+
+        except Exception as e:
+            raise ValueError(f"计算当年Cd浓度失败: {str(e)}")

+ 64 - 62
app/services/cd_flux_service.py

@@ -102,86 +102,83 @@ class FluxCdVisualizationService:
         return self._generate_cd_flux_map(field='net_cd', title_prefix="net",
                                           output_dir=output_dir, boundary_shp=boundary_shp)
 
+    def generate_end_cd_map(self, output_dir: str = None, boundary_shp: str = None) -> Dict[str, Any]:
+        """
+        生成当年Cd浓度(End_Cd)的空间分布图和直方图
+        """
+        return self._generate_cd_flux_map(
+            field='end_cd',
+            title_prefix="End",
+            output_dir=output_dir,
+            boundary_shp=boundary_shp
+        )
+
     def _generate_cd_flux_map(self, field: str, title_prefix: str,
                               output_dir: str = None, boundary_shp: str = None) -> Dict[str, Any]:
         """
-        通用的Cd通量地图生成方法
-
-        @param field: 要可视化的字段('in_cd'、'out_cd'或'net_cd')
-        @param title_prefix: 标题前缀('input'、'output'或'net')
-        @param output_dir: 输出文件目录
-        @param boundary_shp: 边界Shapefile文件路径
-        @return: 包含输出文件路径的字典
+        通用的Cd通量地图生成方法(直接从数据库获取数据)
         """
         try:
-            # 字段到标题的映射
-            field_titles = {
-                'in_cd': "Input",
-                'out_cd': "Output",
-                'net_cd': "Net"
-            }
-
-            # 字段到单位的映射
-            field_units = {
-                'in_cd': "g/ha/a",
-                'out_cd': "g/ha/a",
-                'net_cd': "g/ha/a"
+            # 字段配置
+            field_config = {
+                'in_cd': {"title": "Input", "unit": "g/ha/a", "label": "input Cd flux"},
+                'out_cd': {"title": "Output", "unit": "g/ha/a", "label": "output Cd flux"},
+                'net_cd': {"title": "Net", "unit": "g/ha/a", "label": "net Cd flux"},
+                'end_cd': {"title": "End", "unit": "mg/kg", "label": "end Cd concentration"}
             }
 
-            # 字段到轴标签的映射
-            field_labels = {
-                'in_cd': 'input Cd flux',
-                'out_cd': 'output Cd flux',
-                'net_cd': 'net Cd flux'
-            }
+            config = field_config.get(field, field_config['in_cd'])
 
-            # 如果未提供数据库会话,则创建新的会话
+            # 获取数据库会话
             db = self.db if self.db else SessionLocal()
             should_close = self.db is None
 
-            # 1. 从数据库查询数据
-            data = self._fetch_fluxcd_data(db, field)
+            # 从数据库查询数据
+            query = db.query(
+                FarmlandData.lon,
+                FarmlandData.lan,
+                getattr(FluxCdOutputData, field)
+            ).join(
+                FluxCdOutputData,
+                (FarmlandData.farmland_id == FluxCdOutputData.farmland_id) &
+                (FarmlandData.sample_id == FluxCdOutputData.sample_id)
+            ).filter(
+                getattr(FluxCdOutputData, field) != None  # 排除空值
+            )
+
+            data = query.all()
 
             if not data:
                 if should_close:
                     db.close()
                 return {
                     "success": False,
-                    "message": f"数据库中未找到任何农田{field_titles[field]}Cd通量数据",
+                    "message": f"数据库中未找到任何{config['title']}Cd数据",
                     "data": None
                 }
 
-            # 2. 设置输出目录
+            # 设置输出目录
             static_dir = get_static_dir()
             if output_dir is None:
                 output_dir = static_dir
             os.makedirs(output_dir, exist_ok=True)
 
-            # 3. 设置边界SHP(如果未提供则使用默认)
+            # 设置边界SHP
             if boundary_shp is None:
                 boundary_shp = get_default_boundary_shp()
-                if boundary_shp:
-                    self.logger.info(f"使用默认边界文件: {boundary_shp}")
-                else:
-                    self.logger.warning("未找到默认边界文件,将不使用边界裁剪")
 
-            # 4. 生成CSV文件
-            base_name = f"fluxcd_{title_prefix}"
+            # 生成CSV文件
+            base_name = f"fluxcd_{field}"
             csv_path = os.path.join(output_dir, f"{base_name}.csv")
             self._generate_csv(data, csv_path, field_names=['lon', 'lan', field])
 
-            # 5. 设置模板TIFF路径(使用土地数据处理中的模板)
+            # 使用模板TIFF
             template_tif = os.path.join(static_dir, "meanTemp.tif")
             if not os.path.exists(template_tif):
-                # 尝试其他可能的模板路径
-                template_tif = os.path.join(static_dir, "template.tif")
-                if not os.path.exists(template_tif):
-                    raise FileNotFoundError(f"未找到模板TIFF文件")
+                raise FileNotFoundError("未找到模板TIFF文件")
 
-            # 6. 使用csv_to_raster_workflow将CSV转换为栅格
+            # 转换为栅格
             raster_path = os.path.join(output_dir, f"{base_name}_raster.tif")
-
-            # 关键修改:确保传递边界文件
             workflow_result = csv_to_raster_workflow(
                 csv_file=csv_path,
                 template_tif=template_tif,
@@ -189,43 +186,41 @@ class FluxCdVisualizationService:
                 resolution_factor=4.0,
                 interpolation_method='linear',
                 field_name=field,
-                lon_col=0,  # CSV中经度列索引
-                lat_col=1,  # CSV中纬度列索引
-                value_col=2,  # CSV中数值列索引
+                lon_col=0,
+                lat_col=1,
+                value_col=2,
                 enable_interpolation=True
             )
 
-            # 获取栅格路径和统计信息
             raster_path = workflow_result['raster']
             stats = workflow_result['statistics']
 
-            # 7. 创建栅格地图 - 关键修改:使用边界文件裁剪
+            # 创建地图
             map_path = os.path.join(output_dir, f"{base_name}_map")
             map_file = self.mapper.create_raster_map(
-                shp_path=boundary_shp,  # 边界文件
-                tif_path=raster_path,  # 栅格文件
+                shp_path=boundary_shp,
+                tif_path=raster_path,
                 output_path=map_path,
                 colormap='green_yellow_red_purple',
-                title=f"{title_prefix} Cd flux map",
+                title=f"{config['title']} Cd {field.split('_')[0]} map",
                 output_size=12,
                 dpi=300,
-                enable_interpolation=False,
-                interpolation_method='linear'
+                enable_interpolation=False
             )
 
-            # 8. 创建直方图
+            # 创建直方图
             histogram_path = self.mapper.create_histogram(
                 raster_path,
                 save_path=os.path.join(output_dir, f"{base_name}_histogram.jpg"),
-                xlabel=f'{field_labels[field]}({field_units[field]})',
+                xlabel=f"{config['label']} ({config['unit']})",
                 ylabel='frequency',
-                title=f'{title_prefix} Cd flux histogram',
+                title=f"{config['title']} Cd {field.split('_')[0]} histogram",
                 bins=100
             )
 
             result = {
                 "success": True,
-                "message": f"成功生成{field_titles[field]}Cd通量可视化结果",
+                "message": f"成功生成{config['title']}Cd数据可视化结果",
                 "data": {
                     "csv": csv_path,
                     "raster": raster_path,
@@ -242,7 +237,7 @@ class FluxCdVisualizationService:
             return result
 
         except Exception as e:
-            self.logger.error(f"生成Cd通量可视化结果时发生错误: {str(e)}", exc_info=True)
+            self.logger.error(f"生成Cd数据可视化结果时发生错误: {str(e)}", exc_info=True)
             return {
                 "success": False,
                 "message": f"生成失败: {str(e)}",
@@ -530,8 +525,15 @@ class FluxCdUpdateService:
 
         # 更新输出通量记录
         output_data.in_cd = input_data.input_flux()
-        output_data.out_cd = out_cd  # 添加输出通量计算
-        output_data.net_cd = output_data.in_cd - out_cd  # 更新净通量计算
+        output_data.out_cd = out_cd
+        output_data.net_cd = output_data.in_cd - out_cd
+
+        # 新增:计算并更新当年Cd浓度
+        try:
+            output_data.calculate_end_cd(self.db)
+            self.logger.info(f"成功更新当年Cd浓度: {output_data.end_cd} mg/kg")
+        except Exception as e:
+            self.logger.error(f"更新当年Cd浓度失败: {str(e)}")
 
         self.logger.info(f"更新输出通量: Farmland_ID={farmland.farmland_id}, Sample_ID={farmland.sample_id}")
 

+ 41 - 22
app/static/cd_flux/latest_result.json

@@ -1,17 +1,17 @@
 {
   "input": {
     "success": true,
-    "message": "成功生成InputCd通量可视化结果",
+    "message": "成功生成InputCd数据可视化结果",
     "data": {
-      "csv": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_input.csv",
-      "raster": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_input_raster.tif",
-      "map": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_input_map.jpg",
-      "histogram": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_input_histogram.jpg",
+      "csv": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_in_cd.csv",
+      "raster": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_in_cd_raster.tif",
+      "map": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_in_cd_map.jpg",
+      "histogram": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_in_cd_histogram.jpg",
       "statistics": {
         "min": 3.4116151332855225,
         "max": 100.34626007080078,
-        "mean": 5.61509136418512,
-        "std": 3.798631197888755,
+        "mean": 5.618910267296008,
+        "std": 3.806083521999358,
         "valid_pixels": 62068,
         "total_pixels": 715504
       },
@@ -20,17 +20,17 @@
   },
   "output": {
     "success": true,
-    "message": "成功生成OutputCd通量可视化结果",
+    "message": "成功生成OutputCd数据可视化结果",
     "data": {
-      "csv": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_output.csv",
-      "raster": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_output_raster.tif",
-      "map": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_output_map.jpg",
-      "histogram": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_output_histogram.jpg",
+      "csv": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_out_cd.csv",
+      "raster": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_out_cd_raster.tif",
+      "map": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_out_cd_map.jpg",
+      "histogram": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_out_cd_histogram.jpg",
       "statistics": {
-        "min": 0.39100000262260437,
+        "min": 0.3799999952316284,
         "max": 11.176603317260742,
-        "mean": 3.3491851019653964,
-        "std": 2.2461722103177286,
+        "mean": 3.3488993183057354,
+        "std": 2.246421439271441,
         "valid_pixels": 62068,
         "total_pixels": 715504
       },
@@ -39,17 +39,36 @@
   },
   "net": {
     "success": true,
-    "message": "成功生成NetCd通量可视化结果",
+    "message": "成功生成NetCd数据可视化结果",
     "data": {
-      "csv": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_net.csv",
-      "raster": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_net_raster.tif",
-      "map": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_net_map.jpg",
-      "histogram": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_net_histogram.jpg",
+      "csv": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_net_cd.csv",
+      "raster": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_net_cd_raster.tif",
+      "map": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_net_cd_map.jpg",
+      "histogram": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_net_cd_histogram.jpg",
       "statistics": {
         "min": -7.432056903839111,
         "max": 96.24049377441406,
-        "mean": 2.2659062665456235,
-        "std": 4.301549697243313,
+        "mean": 2.2700109570627895,
+        "std": 4.310772153919442,
+        "valid_pixels": 62068,
+        "total_pixels": 715504
+      },
+      "boundary_used": "D:\\17417\\Documents\\backend\\app\\services\\..\\static\\cd_flux\\lechang.shp"
+    }
+  },
+  "end_cd": {
+    "success": true,
+    "message": "成功生成EndCd数据可视化结果",
+    "data": {
+      "csv": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_end_cd.csv",
+      "raster": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_end_cd_raster.tif",
+      "map": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_end_cd_map.jpg",
+      "histogram": "D:\\17417\\Documents\\backend\\app\\api\\..\\static\\cd_flux\\fluxcd_end_cd_histogram.jpg",
+      "statistics": {
+        "min": 0.0,
+        "max": 8.841265678405762,
+        "mean": 0.5252596194382306,
+        "std": 0.2819332507197959,
         "valid_pixels": 62068,
         "total_pixels": 715504
       },