Sfoglia il codice sorgente

简化架构,直接调用通用绘图模块,删除中间封装层;移除冗余的映射和可视化模块,更新相关文档以反映新结构和使用指南。

drggboy 1 settimana fa
parent
commit
59b6a13661

+ 0 - 10
Cd_Prediction_Integrated_System/README.md

@@ -50,16 +50,6 @@ Cd_Prediction_Integrated_System/
 python main.py
 ```
 
-## 依赖包
-- numpy
-- pandas
-- torch
-- sklearn
-- geopandas
-- rasterio
-- matplotlib
-- seaborn
-
 ## 注意事项
 - 确保所有模型文件和数据文件已正确放置
 - 运行前请检查配置文件中的路径设置

+ 4 - 3
Cd_Prediction_Integrated_System/analysis/__init__.py

@@ -1,10 +1,11 @@
 """
 数据分析模块
 Analysis module for Cd Prediction Integrated System
+
+注意:架构简化后,mapping 和 visualization 模块已被移除
+现在直接使用通用绘图模块 app.utils.mapping_utils
 """
 
 from .data_processing import DataProcessor
-from .mapping import RasterMapper
-from .visualization import Visualizer
 
-__all__ = ['DataProcessor', 'RasterMapper', 'Visualizer'] 
+__all__ = ['DataProcessor']

+ 0 - 242
Cd_Prediction_Integrated_System/analysis/mapping.py

@@ -1,242 +0,0 @@
-"""
-栅格映射模块
-Raster Mapping Module
-
-基于通用绘图模块 app.utils.mapping_utils 的封装
-提供与原有接口兼容的栅格转换功能,包含空间插值处理
-"""
-
-import os
-import sys
-import logging
-import pandas as pd
-import geopandas as gpd
-from shapely.geometry import Point
-import rasterio
-from rasterio.features import rasterize
-from rasterio.transform import from_origin
-import numpy as np
-
-# 添加项目根目录到路径
-sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
-
-# 导入通用绘图模块
-from app.utils.mapping_utils import MappingUtils
-
-import config
-
-class RasterMapper:
-    """
-    栅格映射器
-    负责将CSV数据转换为GeoTIFF栅格数据
-    """
-    
-    def __init__(self):
-        """
-        初始化栅格映射器
-        """
-        self.logger = logging.getLogger(__name__)
-        # 初始化通用绘图模块
-        self.mapping_utils = MappingUtils()
-        
-    def csv_to_shapefile(self, csv_file, shapefile_output):
-        """
-        将CSV文件转换为Shapefile文件
-        
-        @param csv_file: CSV文件路径
-        @param shapefile_output: 输出Shapefile文件路径
-        """
-        try:
-            # 读取CSV数据
-            df = pd.read_csv(csv_file)
-            
-            # 确保列名正确
-            if 'longitude' not in df.columns or 'latitude' not in df.columns:
-                # 尝试自动识别经纬度列
-                lon_col = None
-                lat_col = None
-                
-                for col in df.columns:
-                    col_lower = col.lower()
-                    if any(keyword in col_lower for keyword in ['lon', '经度', 'x']):
-                        lon_col = col
-                    elif any(keyword in col_lower for keyword in ['lat', '纬度', 'y']):
-                        lat_col = col
-                
-                if lon_col and lat_col:
-                    df = df.rename(columns={lon_col: 'longitude', lat_col: 'latitude'})
-                    self.logger.info(f"自动识别坐标列: {lon_col} -> longitude, {lat_col} -> latitude")
-                else:
-                    raise ValueError("无法识别经纬度列")
-            
-            # 创建几何对象
-            lon = df['longitude']
-            lat = df['latitude']
-            
-            # 获取预测值列
-            value_col = 'Prediction'
-            if value_col not in df.columns:
-                # 寻找可能的预测值列
-                for col in df.columns:
-                    if col not in ['longitude', 'latitude'] and pd.api.types.is_numeric_dtype(df[col]):
-                        value_col = col
-                        break
-                else:
-                    raise ValueError("无法找到预测值列")
-            
-            val = df[value_col]
-            
-            # 创建Point几何对象
-            geometry = [Point(xy) for xy in zip(lon, lat)]
-            
-            # 创建GeoDataFrame
-            gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326")
-            
-            # 确保输出目录存在
-            os.makedirs(os.path.dirname(shapefile_output), exist_ok=True)
-            
-            # 保存为Shapefile
-            gdf.to_file(shapefile_output, driver="ESRI Shapefile")
-            
-            self.logger.info(f"Shapefile创建成功: {shapefile_output}")
-            return shapefile_output
-            
-        except Exception as e:
-            self.logger.error(f"CSV转Shapefile失败: {str(e)}")
-            raise
-    
-    def vector_to_raster(self, input_shapefile, template_tif, output_tif, field='Prediction', resolution_factor=16.0):
-        """
-        将点矢量数据转换为栅格数据(使用通用绘图模块)
-        
-        @param input_shapefile: 输入点矢量数据的Shapefile文件路径
-        @param template_tif: 用作模板的GeoTIFF文件路径
-        @param output_tif: 输出栅格化后的GeoTIFF文件路径
-        @param field: 用于栅格化的属性字段名
-        @param resolution_factor: 分辨率倍数因子,16.0表示分辨率提高16倍(像素单元变为1/16)
-        """
-        try:
-            self.logger.info(f"开始矢量转栅格: {input_shapefile}")
-            self.logger.info(f"分辨率因子: {resolution_factor}")
-            
-            # 获取边界文件
-            boundary_shp = config.ANALYSIS_CONFIG.get("boundary_shp")
-            
-            # 调用通用绘图模块的矢量转栅格方法(包含空间插值)
-            output_path, stats = self.mapping_utils.vector_to_raster(
-                input_shapefile=input_shapefile,
-                template_tif=template_tif,
-                output_tif=output_tif,
-                field=field,
-                resolution_factor=resolution_factor,
-                boundary_shp=boundary_shp,
-                interpolation_method='nearest'
-            )
-            
-            self.logger.info(f"栅格文件创建成功: {output_path}")
-            if stats:
-                self.logger.info(f"统计信息: 有效像素 {stats.get('valid_pixels', 0)}/{stats.get('total_pixels', 0)}")
-            
-            return output_path
-            
-        except Exception as e:
-            self.logger.error(f"矢量转栅格失败: {str(e)}")
-            raise
-    
-    def _create_default_template(self, gdf, template_tif):
-        """
-        创建默认的模板栅格文件
-        
-        @param gdf: GeoDataFrame
-        @param template_tif: 模板文件路径
-        """
-        try:
-            # 获取边界
-            bounds = gdf.total_bounds  # [minx, miny, maxx, maxy]
-            
-            # 设置分辨率(约1公里)
-            resolution = 0.01  # 度
-            
-            # 计算栅格尺寸
-            width = int((bounds[2] - bounds[0]) / resolution)
-            height = int((bounds[3] - bounds[1]) / resolution)
-            
-            # 创建变换矩阵
-            transform = from_origin(bounds[0], bounds[3], resolution, resolution)
-            
-            # 创建空的栅格数据
-            data = np.ones((height, width), dtype='float32') * np.nan
-            
-            # 元数据
-            meta = {
-                'driver': 'GTiff',
-                'dtype': 'float32',
-                'nodata': np.nan,
-                'width': width,
-                'height': height,
-                'count': 1,
-                'crs': gdf.crs,
-                'transform': transform
-            }
-            
-            # 确保目录存在
-            os.makedirs(os.path.dirname(template_tif), exist_ok=True)
-            
-            # 写入模板文件
-            with rasterio.open(template_tif, 'w', **meta) as dst:
-                dst.write(data, 1)
-                
-            self.logger.info(f"默认模板创建成功: {template_tif}")
-            
-        except Exception as e:
-            self.logger.error(f"默认模板创建失败: {str(e)}")
-            raise
-
-    
-    def csv_to_raster(self, csv_file, output_raster=None, output_shp=None):
-        """
-        完整的CSV到栅格转换流程
-        
-        @param csv_file: 输入CSV文件路径
-        @param output_raster: 输出栅格文件路径,如果为None则使用默认路径
-        @param output_shp: 输出shapefile路径,如果为None则使用默认路径
-        @return: 输出栅格文件路径
-        """
-        try:
-            self.logger.info(f"开始CSV到栅格转换: {csv_file}")
-            
-            # 使用默认路径或提供的自定义路径
-            if output_raster is None:
-                output_raster = config.ANALYSIS_CONFIG["output_raster"]
-            if output_shp is None:
-                shapefile_path = config.ANALYSIS_CONFIG["temp_shapefile"]
-            else:
-                shapefile_path = output_shp
-                
-            template_tif = config.ANALYSIS_CONFIG["template_tif"]
-            
-            # 步骤1: CSV转Shapefile
-            self.csv_to_shapefile(csv_file, shapefile_path)
-            
-            # 步骤2: Shapefile转栅格
-            self.vector_to_raster(shapefile_path, template_tif, output_raster)
-            
-            self.logger.info(f"CSV到栅格转换完成: {output_raster}")
-            return output_raster
-            
-        except Exception as e:
-            self.logger.error(f"CSV到栅格转换失败: {str(e)}")
-            raise
-
-if __name__ == "__main__":
-    # 测试代码
-    mapper = RasterMapper()
-    
-    # 假设有一个测试CSV文件
-    test_csv = os.path.join(config.DATA_PATHS["final_dir"], "Final_predictions.csv")
-    
-    if os.path.exists(test_csv):
-        output_raster = mapper.csv_to_raster(test_csv)
-        print(f"栅格转换完成,输出文件: {output_raster}")
-    else:
-        print(f"测试文件不存在: {test_csv}") 

+ 0 - 155
Cd_Prediction_Integrated_System/analysis/visualization.py

@@ -1,155 +0,0 @@
-"""
-可视化模块
-Visualization Module
-
-基于通用绘图模块 app.utils.mapping_utils 的封装
-提供与原有接口兼容的可视化功能
-"""
-
-import os
-import sys
-import logging
-
-# 添加项目根目录到路径
-sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
-
-# 导入通用绘图模块
-from app.utils.mapping_utils import MappingUtils, COLORMAPS
-
-import config
-
-class Visualizer:
-    """
-    可视化器
-    负责创建栅格地图和直方图
-    基于通用绘图模块的封装,保持原有接口兼容性
-    """
-    
-    def __init__(self):
-        """
-        初始化可视化器
-        """
-        self.logger = logging.getLogger(__name__)
-        self.mapping_utils = MappingUtils()
-        
-
-    def create_raster_map(self, 
-                         shp_path=None, 
-                         tif_path=None, 
-                         color_map_name=None,
-                         title_name="Prediction Cd",
-                         output_path=None,
-                         output_size=None,
-                         high_res=False):
-        """
-        创建栅格地图
-        
-        @param shp_path: 输入的矢量数据的路径
-        @param tif_path: 输入的栅格数据的路径
-        @param color_map_name: 使用的色彩方案
-        @param title_name: 输出数据的图的名称
-        @param output_path: 输出保存的图片的路径
-        @param output_size: 图片尺寸
-        @param high_res: 是否使用高分辨率输出(默认False,DPI=300)
-        @return: 输出图片路径
-        """
-        try:
-            # 使用默认值
-            if shp_path is None:
-                shp_path = config.ANALYSIS_CONFIG["boundary_shp"]
-            if tif_path is None:
-                tif_path = config.ANALYSIS_CONFIG["output_raster"]
-            if color_map_name is None:
-                color_map_name = config.VISUALIZATION_CONFIG["color_maps"][config.VISUALIZATION_CONFIG["default_colormap"]]
-            if output_path is None:
-                output_path = os.path.join(config.OUTPUT_PATHS["figures_dir"], "Prediction_results")
-            if output_size is None:
-                output_size = config.VISUALIZATION_CONFIG["figure_size"]
-            
-            # 检查边界文件是否存在
-            if shp_path and not os.path.exists(shp_path):
-                self.logger.warning(f"边界文件不存在: {shp_path},将跳过边界绘制")
-                shp_path = None
-                
-            # 设置DPI
-            dpi = 600 if high_res else config.VISUALIZATION_CONFIG["dpi"]
-            
-            # 调用通用绘图模块
-            return self.mapping_utils.create_raster_map(
-                shp_path=shp_path,
-                tif_path=tif_path,
-                output_path=output_path,
-                colormap=color_map_name,
-                title=title_name,
-                output_size=output_size,
-                dpi=dpi
-            )
-            
-        except Exception as e:
-            self.logger.error(f"栅格地图创建失败: {str(e)}")
-            raise
-    
-    def create_histogram(self, 
-                        file_path=None,
-                        figsize=None,
-                        xlabel='Cd content',
-                        ylabel='Frequency',
-                        title='County level Cd Frequency',
-                        save_path=None,
-                        high_res=False):
-        """
-        绘制GeoTIFF文件的直方图
-        
-        @param file_path: GeoTIFF 文件路径
-        @param figsize: 图像尺寸,如 (10, 6)
-        @param xlabel: 横坐标标签
-        @param ylabel: 纵坐标标签
-        @param title: 图标题
-        @param save_path: 可选,保存图片路径(含文件名和扩展名)
-        @param high_res: 是否使用高分辨率输出(默认False,DPI=300)
-        @return: 输出图片路径
-        """
-        try:
-            # 使用默认值
-            if file_path is None:
-                file_path = config.ANALYSIS_CONFIG["output_raster"]
-            if figsize is None:
-                figsize = (6, 6)
-            if save_path is None:
-                save_path = os.path.join(config.OUTPUT_PATHS["figures_dir"], "Prediction_frequency.jpg")
-            
-            # 设置DPI
-            dpi = 600 if high_res else config.VISUALIZATION_CONFIG["dpi"]
-            
-            # 调用通用绘图模块
-            return self.mapping_utils.create_histogram(
-                file_path=file_path,
-                save_path=save_path,
-                figsize=figsize,
-                xlabel=xlabel,
-                ylabel=ylabel,
-                title=title,
-                dpi=dpi
-            )
-            
-        except Exception as e:
-            self.logger.error(f"直方图创建失败: {str(e)}")
-            raise
-
-if __name__ == "__main__":
-    # 测试代码
-    visualizer = Visualizer()
-    
-    # 测试栅格地图创建
-    try:
-        map_output = visualizer.create_raster_map()
-        print(f"栅格地图创建完成: {map_output}")
-    except Exception as e:
-        print(f"栅格地图创建失败: {e}")
-    
-    # 测试直方图创建
-    try:
-        histogram_output = visualizer.create_histogram()
-        print(f"直方图创建完成: {histogram_output}")
-    except Exception as e:
-        print(f"直方图创建失败: {e}") 

+ 103 - 39
Cd_Prediction_Integrated_System/main.py

@@ -3,6 +3,7 @@
 Main execution script for Cd Prediction Integrated System
 
 Description: 集成运行作物Cd模型、有效态Cd模型和数据分析可视化的完整流程
+架构简化版本:直接调用通用绘图模块,删除中间封装层
 """
 
 import os
@@ -12,15 +13,18 @@ from datetime import datetime
 
 # 添加项目根目录到Python路径
 sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+# 添加上级目录到路径以访问通用绘图模块
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
 import config
 from models.crop_cd_model.predict import CropCdPredictor
 from models.effective_cd_model.predict import EffectiveCdPredictor
 from analysis.data_processing import DataProcessor
-from analysis.mapping import RasterMapper
-from analysis.visualization import Visualizer
 from utils.common import setup_logging, validate_files
 
+# 直接导入通用绘图模块
+from app.utils.mapping_utils import csv_to_raster_workflow, MappingUtils
+
 def main():
     """
     主执行函数
@@ -33,7 +37,7 @@ def main():
     logger = logging.getLogger(__name__)
     
     logger.info("=" * 60)
-    logger.info("开始执行Cd预测集成系统")
+    logger.info("开始执行Cd预测集成系统 (架构简化版本)")
     logger.info("=" * 60)
     
     try:
@@ -62,6 +66,9 @@ def main():
             final_data_files = data_processor.combine_predictions_with_coordinates()
             logger.info(f"数据整合完成,生成文件: {list(final_data_files.keys())}")
         
+        # 初始化通用绘图工具(用于可视化)
+        mapping_utils = MappingUtils()
+        
         # 步骤4: 为每个模型生成栅格文件和可视化图
         model_outputs = {}
         
@@ -92,32 +99,65 @@ def main():
             
             model_outputs[model_name] = {}
             
-            # 步骤4a: 生成栅格文件
+            # 步骤4a: 生成栅格文件(直接调用通用绘图模块)
             if config.WORKFLOW_CONFIG["generate_raster"]:
                 logger.info(f"步骤4a: 为{display_name}模型生成栅格文件...")
-                raster_mapper = RasterMapper()
                 
-                # 为每个模型创建独立的输出路径
-                output_raster_path = os.path.join(
-                    config.OUTPUT_PATHS["raster_dir"],
-                    f"output_{model_name}.tif"
-                )
-                
-                output_raster = raster_mapper.csv_to_raster(
-                    final_data_file, 
-                    output_raster=output_raster_path,
-                    output_shp=os.path.join(
+                # 使用csv_to_raster_workflow直接生成栅格
+                try:
+                    workflow_result = csv_to_raster_workflow(
+                        csv_file=final_data_file,
+                        template_tif=config.ANALYSIS_CONFIG["template_tif"],
+                        output_dir=config.OUTPUT_PATHS["raster_dir"],
+                        boundary_shp=config.ANALYSIS_CONFIG.get("boundary_shp"),
+                        resolution_factor=1.0,  # 高分辨率栅格生成
+                        interpolation_method='nearest',
+                        field_name='Prediction',
+                        lon_col=0,
+                        lat_col=1, 
+                        value_col=2,
+                        enable_interpolation=False  # 禁用空间插值
+                    )
+                    
+                    output_raster = workflow_result['raster']
+                    
+                    # 重命名为特定模型的文件
+                    model_raster_path = os.path.join(
                         config.OUTPUT_PATHS["raster_dir"],
-                        f"points_{model_name}.shp"
+                        f"output_{model_name}.tif"
                     )
-                )
-                model_outputs[model_name]['raster'] = output_raster
-                logger.info(f"{display_name}模型栅格文件生成完成: {output_raster}")
+                    
+                    if os.path.exists(output_raster) and output_raster != model_raster_path:
+                        import shutil
+                        shutil.move(output_raster, model_raster_path)
+                        output_raster = model_raster_path
+                    
+                    model_outputs[model_name]['raster'] = output_raster
+                    logger.info(f"{display_name}模型栅格文件生成完成: {output_raster}")
+                    
+                    # 清理中间shapefile文件
+                    try:
+                        shapefile_path = workflow_result.get('shapefile')
+                        if shapefile_path and os.path.exists(shapefile_path):
+                            # 删除shapefile及其相关文件
+                            import glob
+                            base_path = os.path.splitext(shapefile_path)[0]
+                            for ext in ['.shp', '.shx', '.dbf', '.prj', '.cpg']:
+                                file_to_delete = base_path + ext
+                                if os.path.exists(file_to_delete):
+                                    os.remove(file_to_delete)
+                                    logger.debug(f"已删除中间文件: {file_to_delete}")
+                            logger.info(f"已清理中间shapefile文件: {shapefile_path}")
+                    except Exception as cleanup_error:
+                        logger.warning(f"清理中间文件失败: {str(cleanup_error)}")
+                    
+                except Exception as e:
+                    logger.error(f"栅格生成失败: {str(e)}")
+                    raise
             
-            # 步骤4b: 创建可视化地图
+            # 步骤4b: 创建可视化地图(直接调用通用绘图模块)
             if config.WORKFLOW_CONFIG["create_visualization"]:
                 logger.info(f"步骤4b: 为{display_name}模型创建可视化地图...")
-                visualizer = Visualizer()
                 
                 # 为每个模型创建独立的地图输出路径
                 map_output_path = os.path.join(
@@ -125,15 +165,29 @@ def main():
                     f"Prediction_results_{model_name}"
                 )
                 
-                map_output = visualizer.create_raster_map(
-                    tif_path=output_raster,
-                    title_name=title_name,
-                    output_path=map_output_path
-                )
-                model_outputs[model_name]['map'] = map_output
-                logger.info(f"{display_name}模型地图可视化完成: {map_output}")
+                try:
+                    # 直接调用通用绘图模块的create_raster_map
+                    map_output = mapping_utils.create_raster_map(
+                        shp_path=config.ANALYSIS_CONFIG.get("boundary_shp"),
+                        tif_path=output_raster,
+                        output_path=map_output_path,
+                        title=title_name,
+                        colormap=config.VISUALIZATION_CONFIG["color_maps"][config.VISUALIZATION_CONFIG["default_colormap"]],
+                        output_size=config.VISUALIZATION_CONFIG["figure_size"],
+                        dpi=config.VISUALIZATION_CONFIG["dpi"],
+                        resolution_factor=1.0,  # 保持栅格原始分辨率
+                        enable_interpolation=False,  # 不在可视化阶段插值
+                        interpolation_method='nearest'
+                    )
+                    
+                    model_outputs[model_name]['map'] = map_output
+                    logger.info(f"{display_name}模型地图可视化完成: {map_output}")
+                    
+                except Exception as e:
+                    logger.error(f"地图可视化失败: {str(e)}")
+                    raise
             
-            # 步骤4c: 创建直方图
+            # 步骤4c: 创建直方图(直接调用通用绘图模块)
             if config.WORKFLOW_CONFIG["create_histogram"]:
                 logger.info(f"步骤4c: 为{display_name}模型创建预测值分布直方图...")
                 
@@ -154,14 +208,24 @@ def main():
                     hist_title = f'{model_name} Prediction Frequency'
                     hist_xlabel = f'{model_name} Content'
                 
-                histogram_output = visualizer.create_histogram(
-                    file_path=output_raster,
-                    title=hist_title,
-                    xlabel=hist_xlabel,
-                    save_path=histogram_output_path
-                )
-                model_outputs[model_name]['histogram'] = histogram_output
-                logger.info(f"{display_name}模型直方图创建完成: {histogram_output}")
+                try:
+                    # 直接调用通用绘图模块的create_histogram
+                    histogram_output = mapping_utils.create_histogram(
+                        file_path=output_raster,
+                        save_path=histogram_output_path,
+                        figsize=(6, 6),
+                        xlabel=hist_xlabel,
+                        ylabel='Frequency',
+                        title=hist_title,
+                        dpi=config.VISUALIZATION_CONFIG["dpi"]
+                    )
+                    
+                    model_outputs[model_name]['histogram'] = histogram_output
+                    logger.info(f"{display_name}模型直方图创建完成: {histogram_output}")
+                    
+                except Exception as e:
+                    logger.error(f"直方图生成失败: {str(e)}")
+                    raise
         
         logger.info("=" * 60)
         logger.info("所有流程执行完成!")
@@ -178,7 +242,7 @@ def main():
 def print_summary(model_outputs):
     """打印执行结果摘要"""
     print("\n" + "=" * 60)
-    print("[SUCCESS] Cd预测集成系统执行完成!")
+    print("[SUCCESS] Cd预测集成系统执行完成! (架构简化版本)")
     print("=" * 60)
     print(f"[INFO] 总输出目录: {config.OUTPUT_PATHS}")
     print()
@@ -206,4 +270,4 @@ def print_summary(model_outputs):
     print("=" * 60)
 
 if __name__ == "__main__":
-    main() 
+    main()

+ 0 - 187
Cd_Prediction_Integrated_System/test_system.py

@@ -1,187 +0,0 @@
-"""
-系统测试脚本
-System Test Script
-
-用于验证Cd预测集成系统的各个模块是否正常工作
-"""
-
-import os
-import sys
-import logging
-
-# 添加项目根目录到Python路径
-sys.path.append(os.path.dirname(os.path.abspath(__file__)))
-
-def test_imports():
-    """测试模块导入"""
-    print("🔍 测试模块导入...")
-    
-    try:
-        import config
-        print("✅ config 模块导入成功")
-        
-        from models.crop_cd_model.predict import CropCdPredictor
-        print("✅ CropCdPredictor 导入成功")
-        
-        from models.effective_cd_model.predict import EffectiveCdPredictor
-        print("✅ EffectiveCdPredictor 导入成功")
-        
-        from analysis.data_processing import DataProcessor
-        print("✅ DataProcessor 导入成功")
-        
-        from analysis.mapping import RasterMapper
-        print("✅ RasterMapper 导入成功")
-        
-        from analysis.visualization import Visualizer
-        print("✅ Visualizer 导入成功")
-        
-        from utils.common import setup_logging
-        print("✅ utils.common 导入成功")
-        
-        return True
-        
-    except Exception as e:
-        print(f"❌ 模块导入失败: {str(e)}")
-        return False
-
-def test_file_structure():
-    """测试文件结构"""
-    print("\n🔍 测试文件结构...")
-    
-    required_files = [
-        "config.py",
-        "main.py",
-        "models/crop_cd_model/model_files/cropCdNN.pth",
-        "models/crop_cd_model/model_files/cropCd_mean.npy",
-        "models/crop_cd_model/model_files/cropCd_scale.npy",
-        "models/crop_cd_model/data/areatest.csv",
-        "models/effective_cd_model/model_files/EffCdNN6C.pth",
-        "models/effective_cd_model/model_files/EffCd_mean.npy",
-        "models/effective_cd_model/model_files/EffCd_scale.npy",
-        "models/effective_cd_model/data/areatest.csv",
-        "data/coordinates/坐标.csv",
-        "output/raster/meanTemp.tif",
-        "output/raster/lechang.shp"
-    ]
-    
-    missing_files = []
-    for file_path in required_files:
-        if os.path.exists(file_path):
-            print(f"✅ {file_path}")
-        else:
-            print(f"❌ {file_path} - 文件不存在")
-            missing_files.append(file_path)
-    
-    if missing_files:
-        print(f"\n⚠️  缺少 {len(missing_files)} 个必要文件")
-        return False
-    else:
-        print("\n✅ 所有必要文件都存在")
-        return True
-
-def test_configuration():
-    """测试配置"""
-    print("\n🔍 测试配置...")
-    
-    try:
-        import config
-        
-        # 测试配置项
-        assert hasattr(config, 'CROP_CD_MODEL'), "缺少 CROP_CD_MODEL 配置"
-        assert hasattr(config, 'EFFECTIVE_CD_MODEL'), "缺少 EFFECTIVE_CD_MODEL 配置"
-        assert hasattr(config, 'DATA_PATHS'), "缺少 DATA_PATHS 配置"
-        assert hasattr(config, 'WORKFLOW_CONFIG'), "缺少 WORKFLOW_CONFIG 配置"
-        
-        print("✅ 配置项检查通过")
-        
-        # 创建目录
-        config.ensure_directories()
-        print("✅ 目录创建成功")
-        
-        return True
-        
-    except Exception as e:
-        print(f"❌ 配置测试失败: {str(e)}")
-        return False
-
-def test_data_loading():
-    """测试数据加载"""
-    print("\n🔍 测试数据加载...")
-    
-    try:
-        import pandas as pd
-        
-        # 测试坐标数据
-        coord_file = "data/coordinates/坐标.csv"
-        if os.path.exists(coord_file):
-            coord_data = pd.read_csv(coord_file)
-            print(f"✅ 坐标数据加载成功: {coord_data.shape}")
-        else:
-            print("❌ 坐标数据文件不存在")
-            return False
-        
-        # 测试作物Cd模型数据
-        crop_data_file = "models/crop_cd_model/data/areatest.csv"
-        if os.path.exists(crop_data_file):
-            crop_data = pd.read_csv(crop_data_file)
-            print(f"✅ 作物Cd模型数据加载成功: {crop_data.shape}")
-        else:
-            print("❌ 作物Cd模型数据文件不存在")
-            return False
-        
-        # 测试有效态Cd模型数据
-        eff_data_file = "models/effective_cd_model/data/areatest.csv"
-        if os.path.exists(eff_data_file):
-            eff_data = pd.read_csv(eff_data_file)
-            print(f"✅ 有效态Cd模型数据加载成功: {eff_data.shape}")
-        else:
-            print("❌ 有效态Cd模型数据文件不存在")
-            return False
-        
-        return True
-        
-    except Exception as e:
-        print(f"❌ 数据加载测试失败: {str(e)}")
-        return False
-
-def main():
-    """主测试函数"""
-    print("=" * 60)
-    print("🧪 Cd预测集成系统测试")
-    print("=" * 60)
-    
-    tests = [
-        ("模块导入", test_imports),
-        ("文件结构", test_file_structure),
-        ("配置", test_configuration),
-        ("数据加载", test_data_loading)
-    ]
-    
-    passed = 0
-    total = len(tests)
-    
-    for test_name, test_func in tests:
-        print(f"\n{'='*20} {test_name} {'='*20}")
-        if test_func():
-            passed += 1
-            print(f"✅ {test_name} 测试通过")
-        else:
-            print(f"❌ {test_name} 测试失败")
-    
-    print("\n" + "=" * 60)
-    print(f"📊 测试结果: {passed}/{total} 通过")
-    
-    if passed == total:
-        print("🎉 所有测试通过!系统准备就绪。")
-        print("\n下一步:运行 python main.py 开始完整的预测流程")
-    else:
-        print("⚠️  部分测试失败,请检查上述错误信息")
-        print("\n建议:")
-        print("1. 确保运行了 python setup_data.py")
-        print("2. 检查原始项目文件夹是否在正确位置")
-        print("3. 安装必要的依赖包:pip install -r requirements.txt")
-    
-    print("=" * 60)
-
-if __name__ == "__main__":
-    main() 

+ 0 - 156
Cd_Prediction_Integrated_System/使用说明.md

@@ -1,156 +0,0 @@
-# Cd预测集成系统使用说明
-
-## 项目简介
-
-本项目整合了三个原本独立的模块:
-- **作物Cd模型文件与数据**:用于预测作物中的镉含量
-- **有效态Cd模型文件与数据**:用于预测土壤中有效态镉含量  
-- **Irrigation_Water**:用于数据分析和可视化
-
-整合后的系统可以一键运行完整的预测分析流程,从模型预测到最终的地图可视化。
-
-## 快速开始
-
-### 1. 数据迁移
-首先运行数据迁移脚本,将原有项目的文件复制到新的项目结构中:
-
-```bash
-cd Cd_Prediction_Integrated_System
-python setup_data.py
-```
-
-### 2. 安装依赖
-```bash
-pip install -r requirements.txt
-```
-
-### 3. 运行主程序
-```bash
-python main.py
-```
-
-## 项目结构说明
-
-```
-Cd_Prediction_Integrated_System/
-├── README.md                 # 英文说明文档
-├── 使用说明.md               # 中文使用说明
-├── main.py                   # 主执行脚本
-├── config.py                 # 配置文件
-├── setup_data.py             # 数据迁移脚本
-├── requirements.txt          # 依赖包列表
-├── models/                   # 模型模块
-│   ├── crop_cd_model/        # 作物Cd模型
-│   │   ├── model_files/      # 模型文件(.pth, .npy等)
-│   │   ├── data/            # 输入数据(areatest.csv)
-│   │   └── predict.py       # 预测脚本
-│   └── effective_cd_model/   # 有效态Cd模型
-│       ├── model_files/      # 模型文件(.pth, .npy等)
-│       ├── data/            # 输入数据(areatest.csv)
-│       └── predict.py       # 预测脚本
-├── analysis/                 # 数据分析模块
-│   ├── data_processing.py    # 数据处理(整合预测结果与坐标)
-│   ├── mapping.py           # 栅格映射(CSV转GeoTIFF)
-│   └── visualization.py     # 可视化(地图和直方图)
-├── data/                    # 共享数据
-│   ├── coordinates/         # 坐标数据(坐标.csv)
-│   ├── predictions/         # 模型预测结果
-│   └── final/              # 最终整合数据
-├── output/                  # 输出结果
-│   ├── raster/             # 栅格文件(.tif, .shp等)
-│   ├── figures/            # 图形输出(.jpg)
-│   └── reports/            # 日志文件
-└── utils/                  # 工具函数
-    └── common.py           # 通用函数
-```
-
-## 工作流程
-
-系统按以下顺序执行:
-
-1. **模型预测阶段**
-   - 运行作物Cd模型,生成 `combined_pH.csv`
-   - 运行有效态Cd模型,生成 `pHcombined.csv`
-
-2. **数据整合阶段**
-   - 将预测结果与坐标数据合并
-   - 生成 `Final_predictions.csv`
-
-3. **栅格转换阶段**(相当于原来的 `02_Transfer_csv_to_geotif.py`)
-   - 将CSV数据转换为Shapefile
-   - 将Shapefile栅格化为GeoTIFF
-
-4. **可视化阶段**(相当于原来的 `01_Figure_raster_mapping.py`)
-   - 生成栅格地图
-   - 生成预测值分布直方图
-
-## 配置说明
-
-可以通过修改 `config.py` 文件来调整系统行为:
-
-### 模型配置
-- `CROP_CD_MODEL`: 作物Cd模型的文件路径和参数
-- `EFFECTIVE_CD_MODEL`: 有效态Cd模型的文件路径和参数
-
-### 工作流程控制
-```python
-WORKFLOW_CONFIG = {
-    "run_crop_model": True,        # 是否运行作物Cd模型
-    "run_effective_model": True,   # 是否运行有效态Cd模型
-    "combine_predictions": True,   # 是否整合预测结果
-    "generate_raster": True,       # 是否生成栅格文件
-    "create_visualization": True,  # 是否创建地图
-    "create_histogram": True       # 是否创建直方图
-}
-```
-
-### 可视化配置
-- 6种预定义颜色方案
-- 可调整图片尺寸和分辨率
-- 支持自定义标题和标签
-
-## 输出文件
-
-运行完成后,会在以下位置生成输出文件:
-
-- `data/predictions/`: 模型预测结果CSV文件
-- `data/final/`: 最终整合的数据文件
-- `output/raster/`: 栅格文件(GeoTIFF, Shapefile)
-- `output/figures/`: 可视化图片
-- `output/reports/`: 执行日志
-
-## 常见问题
-
-### Q: 如何只运行某个特定模块?
-A: 修改 `config.py` 中的 `WORKFLOW_CONFIG`,将不需要的步骤设为 `False`。
-
-### Q: 如何更改颜色方案?
-A: 修改 `config.py` 中的 `VISUALIZATION_CONFIG["default_colormap"]`,或者添加自定义颜色方案。
-
-### Q: 模型文件找不到怎么办?
-A: 确保运行了 `setup_data.py` 脚本,并且原始项目文件夹在正确位置。
-
-### Q: 如何查看详细的执行日志?
-A: 查看 `output/reports/` 目录中的日志文件。
-
-## 技术特点
-
-- **模块化设计**:每个功能模块独立,便于维护和扩展
-- **配置驱动**:通过配置文件控制执行流程
-- **完整日志**:详细记录每个步骤的执行情况
-- **错误处理**:完善的异常处理机制
-- **面向对象**:使用类封装功能,代码结构清晰
-- **JSDoc注释**:所有函数都有详细的参数说明
-
-## 扩展说明
-
-如需添加新的模型或功能:
-
-1. 在 `models/` 目录下创建新的模型模块
-2. 在 `config.py` 中添加相应配置
-3. 在 `main.py` 中添加执行步骤
-4. 更新 `analysis/data_processing.py` 以支持新的预测结果
-
-## 联系方式
-
-如有问题或建议,请查看项目日志文件或联系开发团队。 

+ 30 - 6
app/utils/mapping_utils.py

@@ -198,7 +198,7 @@ class MappingUtils:
             return raster
     
     def vector_to_raster(self, input_shapefile, template_tif, output_tif, field, 
-                        resolution_factor=16.0, boundary_shp=None, interpolation_method='nearest'):
+                        resolution_factor=16.0, boundary_shp=None, interpolation_method='nearest', enable_interpolation=True):
         """
         将点矢量数据转换为栅格数据
         
@@ -209,6 +209,7 @@ class MappingUtils:
         @param resolution_factor: 分辨率倍数因子
         @param boundary_shp: 边界Shapefile文件路径,用于创建掩膜
         @param interpolation_method: 插值方法 ('nearest', 'linear', 'cubic')
+        @param enable_interpolation: 是否启用空间插值,默认True
         @return: 输出的GeoTIFF文件路径和统计信息
         """
         try:
@@ -267,10 +268,12 @@ class MappingUtils:
                 boundary_mask = self.create_boundary_mask(raster, transform, boundary_gdf)
                 raster[~boundary_mask] = np.nan
             
-            # 使用插值方法填充NaN值
-            if np.isnan(raster).any():
+            # 使用插值方法填充NaN值(如果启用)
+            if enable_interpolation and np.isnan(raster).any():
                 self.logger.info(f"使用 {interpolation_method} 方法进行插值...")
                 raster = self.interpolate_nan_values(raster, method=interpolation_method)
+            elif not enable_interpolation and np.isnan(raster).any():
+                self.logger.info("插值已禁用,保留原始栅格数据(包含NaN值)")
             
             # 创建输出目录
             os.makedirs(os.path.dirname(output_tif), exist_ok=True)
@@ -315,7 +318,9 @@ class MappingUtils:
     
     def create_raster_map(self, shp_path, tif_path, output_path, 
                          colormap='green_yellow_red_purple', title="Prediction Map",
-                         output_size=12, figsize=None, dpi=300):
+                         output_size=12, figsize=None, dpi=300,
+                         resolution_factor=1.0, enable_interpolation=False,
+                         interpolation_method='nearest'):
         """
         创建栅格地图
         
@@ -327,16 +332,24 @@ class MappingUtils:
         @param output_size: 图片尺寸(正方形),如果指定了figsize则忽略此参数
         @param figsize: 图片尺寸元组 (width, height),优先级高于output_size
         @param dpi: 图片分辨率
+        @param resolution_factor: 分辨率因子,>1提高分辨率,<1降低分辨率
+        @param enable_interpolation: 是否启用空间插值,用于处理NaN值或提高分辨率
+        @param interpolation_method: 插值方法 ('nearest', 'linear', 'cubic')
+        @param enable_interpolation: 是否启用空间插值,默认True
         @return: 输出图片文件路径
         """
         try:
             self.logger.info(f"开始创建栅格地图: {tif_path}")
+            self.logger.info(f"分辨率因子: {resolution_factor}, 启用插值: {enable_interpolation}")
             
             # 读取矢量边界
             gdf = gpd.read_file(shp_path) if shp_path else None
             
             # 读取并裁剪栅格数据
             with rasterio.open(tif_path) as src:
+                original_transform = src.transform
+                original_crs = src.crs
+                
                 if gdf is not None:
                     # 确保坐标系一致
                     if gdf.crs != src.crs:
@@ -358,6 +371,16 @@ class MappingUtils:
             if nodata is not None:
                 raster[raster == nodata] = np.nan
             
+            # 应用分辨率因子重采样
+            if resolution_factor != 1.0:
+                self.logger.info(f"应用分辨率因子重采样: {resolution_factor}")
+                raster, out_transform = self._resample_raster(raster, out_transform, resolution_factor, original_crs)
+            
+            # 应用空间插值(如果启用)
+            if enable_interpolation and np.isnan(raster).any():
+                self.logger.info(f"使用 {interpolation_method} 方法进行空间插值")
+                raster = self.interpolate_nan_values(raster, method=interpolation_method)
+            
             # 检查是否有有效数据
             if np.all(np.isnan(raster)):
                 raise ValueError("栅格数据中没有有效值")
@@ -511,7 +534,7 @@ def get_available_colormaps():
 def csv_to_raster_workflow(csv_file, template_tif, output_dir, 
                           boundary_shp=None, resolution_factor=16.0,
                           interpolation_method='nearest', field_name='Prediction',
-                          lon_col=0, lat_col=1, value_col=2):
+                          lon_col=0, lat_col=1, value_col=2, enable_interpolation=False):
     """
     完整的CSV到栅格转换工作流
     
@@ -525,6 +548,7 @@ def csv_to_raster_workflow(csv_file, template_tif, output_dir,
     @param lon_col: 经度列
     @param lat_col: 纬度列
     @param value_col: 数值列
+    @param enable_interpolation: 是否启用空间插值,默认False
     @return: 输出文件路径字典
     """
     mapper = MappingUtils()
@@ -543,7 +567,7 @@ def csv_to_raster_workflow(csv_file, template_tif, output_dir,
     # 2. Shapefile转栅格
     raster_path, stats = mapper.vector_to_raster(
         shapefile_path, template_tif, raster_path, field_name,
-        resolution_factor, boundary_shp, interpolation_method
+        resolution_factor, boundary_shp, interpolation_method, enable_interpolation
     )
     
     return {

+ 36 - 0
docs/mapping_utils_guide.md

@@ -34,6 +34,8 @@ raster_path, stats = mapper.vector_to_raster(
 ```
 
 ### 3. 栅格地图绘制
+
+#### 基础用法
 ```python
 map_path = mapper.create_raster_map(
     shp_path="boundary.shp",
@@ -46,6 +48,40 @@ map_path = mapper.create_raster_map(
 )
 ```
 
+#### 高级功能 - 分辨率调整和空间插值
+```python
+# 高分辨率输出(2倍分辨率)
+map_path = mapper.create_raster_map(
+    shp_path="boundary.shp",
+    tif_path="data.tif",
+    output_path="high_res_map",
+    resolution_factor=2.0,           # 提高分辨率
+    enable_interpolation=True,       # 启用插值
+    interpolation_method='linear',   # 使用线性插值
+    title="高分辨率地图"
+)
+
+# 低分辨率快速预览(0.5倍分辨率)
+map_path = mapper.create_raster_map(
+    shp_path="boundary.shp",
+    tif_path="data.tif",
+    output_path="preview_map",
+    resolution_factor=0.5,           # 降低分辨率
+    enable_interpolation=False,      # 不使用插值
+    title="预览地图"
+)
+```
+
+**新增参数说明:**
+- `resolution_factor`: 分辨率因子(默认1.0)
+  - `> 1.0`: 提高分辨率
+  - `< 1.0`: 降低分辨率
+- `enable_interpolation`: 是否启用空间插值(默认False)
+- `interpolation_method`: 插值方法(默认'nearest')
+  - `'nearest'`: 最近邻插值(速度最快)
+  - `'linear'`: 线性插值(平衡效果和速度)
+  - `'cubic'`: 三次插值(效果最好但速度较慢)
+
 ### 4. 直方图绘制
 ```python
 hist_path = mapper.create_histogram(