张世豪
11 小时以前 5b685e9066ccfbc432c29739b5524f1d42a20891
进一步优化边界管理页面功能
已修改6个文件
456 ■■■■■ 文件已修改
dikuai.properties 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
set.properties 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/bianjie/bianjieguihua2.java 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dikuai/Dikuanbianjipage.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/zhuye/MapRenderer.java 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/zhuye/Shouye.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dikuai.properties
@@ -1,10 +1,13 @@
#Dikuai Properties
#Mon Dec 22 18:52:36 CST 2025
#Mon Dec 22 19:37:01 CST 2025
LAND1.angleThreshold=-1
LAND1.baseStationCoordinates=3949.89151752,N,11616.79267501,E
LAND1.boundaryCoordinates=2.87,-0.19;6.73,-1.23;22.76,1.06;27.88,3.65;35.84,9.96;38.40,10.35;40.83,9.40;44.09,4.86;43.49,2.92;32.39,0.16;30.22,-1.26;30.00,-2.37;33.85,-25.32;32.30,-24.06;30.75,-22.81;29.20,-21.55;27.65,-20.29;26.11,-19.04;24.56,-17.78;23.01,-16.52;21.46,-15.27;19.91,-14.01;18.36,-12.75;16.81,-11.50;15.26,-10.24;13.72,-8.98;12.17,-7.73;10.62,-6.47;9.07,-5.21;7.52,-3.96;5.97,-2.70;4.42,-1.45;2.87,-0.19
LAND1.boundaryCoordinates=2.88,-0.19;2.85,-0.19;2.84,-0.28;3.13,-0.43;4.02,-0.92;5.27,-1.24;6.74,-1.23;8.20,-1.02;9.67,-0.79;11.10,-0.46;12.55,-0.12;14.05,0.13;15.51,0.36;16.95,0.41;18.40,0.49;19.82,0.62;21.31,0.81;22.79,1.06;24.17,1.50;26.64,2.85;27.91,3.66;29.17,4.56;30.42,5.45;31.60,6.39;32.65,7.37;33.72,8.31;34.76,9.22;35.88,9.97;37.11,10.36;38.44,10.36;39.71,10.04;40.88,9.41;41.96,8.50;42.85,7.41;43.60,6.15;44.14,4.87;44.17,3.71;43.54,2.92;42.33,2.47;40.93,2.10;39.53,1.77;38.12,1.53;36.68,1.24;35.24,0.94;33.80,0.59;32.43,0.16;31.15,-0.38;30.26,-1.26;30.03,-2.37;30.12,-3.81;30.34,-5.22;30.87,-7.98;31.10,-9.36;31.33,-10.77;31.57,-12.20;31.82,-13.68;32.07,-15.16;32.28,-16.60;32.52,-17.92;32.78,-19.37;33.07,-20.80;33.35,-22.37;33.62,-23.91;33.89,-25.35
LAND1.boundaryOriginalCoordinates=39.831524,116.279912,49.30;39.831524,116.279911,49.29;39.831523,116.279911,49.23;39.831521,116.279915,49.31;39.831517,116.279925,49.34;39.831514,116.279940,49.30;39.831514,116.279957,49.28;39.831516,116.279974,49.28;39.831518,116.279991,49.29;39.831521,116.280008,49.24;39.831524,116.280025,49.30;39.831526,116.280042,49.24;39.831529,116.280059,49.29;39.831529,116.280076,49.26;39.831530,116.280093,49.32;39.831531,116.280110,49.28;39.831533,116.280127,49.28;39.831535,116.280144,49.26;39.831539,116.280161,49.27;39.831544,116.280175,49.25;39.831551,116.280190,49.24;39.831558,116.280204,49.26;39.831566,116.280219,49.26;39.831574,116.280234,49.22;39.831583,116.280248,49.24;39.831591,116.280260,49.24;39.831600,116.280272,49.23;39.831608,116.280285,49.18;39.831615,116.280298,49.12;39.831618,116.280312,49.11;39.831618,116.280328,49.12;39.831615,116.280342,49.15;39.831610,116.280356,49.21;39.831602,116.280369,49.23;39.831592,116.280379,49.25;39.831581,116.280388,49.25;39.831569,116.280394,49.19;39.831559,116.280395,49.23;39.831552,116.280387,49.28;39.831547,116.280373,49.32;39.831544,116.280357,49.33;39.831541,116.280340,49.29;39.831539,116.280324,49.27;39.831536,116.280307,49.24;39.831534,116.280290,49.25;39.831531,116.280273,49.26;39.831527,116.280257,49.28;39.831522,116.280242,49.21;39.831514,116.280232,49.28;39.831504,116.280229,49.24;39.831491,116.280230,49.33;39.831478,116.280233,49.34;39.831466,116.280236,49.31;39.831454,116.280239,49.31;39.831441,116.280242,49.26;39.831429,116.280244,49.23;39.831416,116.280247,49.25;39.831402,116.280250,49.22;39.831389,116.280253,49.25;39.831376,116.280256,49.26;39.831364,116.280258,49.24;39.831351,116.280261,49.25;39.831338,116.280265,49.26;39.831324,116.280268,49.20;39.831311,116.280271,49.16;39.831298,116.280274,49.17
LAND1.boundaryOriginalXY=2.88,-0.19;2.85,-0.19;2.84,-0.28;3.13,-0.43;4.02,-0.92;5.27,-1.24;6.74,-1.23;8.20,-1.02;9.67,-0.79;11.10,-0.46;12.55,-0.12;14.05,0.13;15.51,0.36;16.95,0.41;18.40,0.49;19.82,0.62;21.31,0.81;22.79,1.06;24.17,1.50;25.41,2.13;26.64,2.85;27.91,3.66;29.17,4.56;30.42,5.45;31.60,6.39;32.65,7.37;33.72,8.31;34.76,9.22;35.88,9.97;37.11,10.36;38.44,10.36;39.71,10.04;40.88,9.41;41.96,8.50;42.85,7.41;43.60,6.15;44.14,4.87;44.17,3.71;43.54,2.92;42.33,2.47;40.93,2.10;39.53,1.77;38.12,1.53;36.68,1.24;35.24,0.94;33.80,0.59;32.43,0.16;31.15,-0.38;30.26,-1.26;30.03,-2.37;30.12,-3.81;30.34,-5.22;30.59,-6.60;30.87,-7.98;31.10,-9.36;31.33,-10.77;31.57,-12.20;31.82,-13.68;32.07,-15.16;32.28,-16.60;32.52,-17.92;32.78,-19.37;33.07,-20.80;33.35,-22.37;33.62,-23.91;33.89,-25.35
LAND1.boundaryPointInterval=-1
LAND1.createTime=2025-12-22 18\:52\:36
LAND1.intelligentSceneAnalysis=-1
LAND1.landArea=459.07
LAND1.landName=12344
LAND1.landNumber=LAND1
@@ -12,6 +15,11 @@
LAND1.mowingOverlapDistance=0.06
LAND1.mowingPattern=平行线
LAND1.mowingSafetyDistance=0.50
LAND1.mowingTrack=-1
LAND1.mowingWidth=34
LAND1.plannedPath=4.652,-1.162;4.633,-1.048;4.994,-1.145;5.051,-1.483;5.449,-1.805;5.355,-1.243;5.716,-1.340;5.848,-2.126;6.247,-2.448;6.077,-1.437;6.438,-1.535;6.646,-2.773;7.045,-3.097;6.793,-1.595;7.130,-1.547;7.444,-3.422;7.843,-3.746;7.467,-1.499;7.803,-1.450;8.242,-4.067;8.641,-4.389;8.140,-1.402;8.477,-1.354;9.040,-4.710;9.438,-5.033;8.813,-1.306;9.150,-1.258;9.838,-5.357;10.237,-5.682;9.487,-1.210;9.823,-1.162;10.636,-6.006;11.035,-6.331;10.160,-1.114;10.497,-1.066;11.434,-6.655;11.834,-6.980;10.833,-1.018;11.170,-0.969;12.233,-7.304;12.632,-7.627;11.507,-0.921;11.843,-0.873;13.030,-7.948;13.429,-8.270;12.180,-0.825;12.517,-0.777;13.828,-8.592;14.227,-8.917;12.853,-0.729;13.190,-0.681;14.627,-9.244;15.026,-9.571;13.527,-0.633;13.864,-0.585;15.426,-9.898;15.825,-10.223;14.200,-0.537;14.537,-0.488;16.224,-10.547;16.623,-10.872;14.874,-0.440;15.210,-0.392;17.023,-11.196;17.421,-11.518;15.547,-0.344;15.884,-0.296;17.820,-11.839;18.219,-12.161;16.220,-0.248;16.557,-0.200;18.617,-12.482;19.017,-12.807;16.894,-0.152;17.230,-0.104;19.416,-13.131;19.815,-13.456;17.567,-0.056;17.904,-0.008;20.214,-13.780;20.613,-14.105;18.240,0.041;18.577,0.089;21.013,-14.429;21.412,-14.754;18.914,0.137;19.250,0.185;21.811,-15.078;22.209,-15.399;19.587,0.233;19.924,0.281;22.608,-15.721;23.007,-16.042;20.260,0.329;20.597,0.377;23.406,-16.365;23.805,-16.689;20.934,0.425;21.271,0.473;24.204,-17.014;24.603,-17.338;21.607,0.522;21.944,0.570;25.003,-17.663;25.402,-17.987;22.281,0.618;22.617,0.666;25.801,-18.312;26.200,-18.636;22.949,0.741;23.267,0.902;26.599,-18.961;26.998,-19.284;23.585,1.063;23.903,1.223;27.397,-19.608;27.796,-19.932;24.221,1.384;24.538,1.545;28.196,-20.257;28.595,-20.581;24.856,1.706;25.174,1.866;28.994,-20.906;29.393,-21.230;25.492,2.027;25.809,2.188;29.792,-21.555;30.192,-21.879;26.127,2.349;26.445,2.509;30.591,-22.204;30.990,-22.528;26.763,2.670;27.081,2.831;31.389,-22.850;31.787,-23.171;27.398,2.992;27.716,3.152;32.186,-23.493;32.585,-23.815;28.034,3.313;28.340,3.543;32.984,-24.139;29.652,-2.222;28.644,3.784;28.949,4.025;29.839,-1.280;30.117,-0.885;29.253,4.266;29.557,4.507;30.428,-0.682;30.738,-0.479;29.862,4.749;30.166,4.990;31.049,-0.275;31.360,-0.072;30.470,5.231;30.774,5.472;31.670,0.131;31.981,0.335;31.079,5.714;31.383,5.955;32.295,0.517;32.626,0.600;31.687,6.196;31.992,6.437;32.957,0.682;33.288,0.765;32.296,6.678;32.600,6.920;33.619,0.847;33.950,0.929;32.904,7.161;33.209,7.402;34.281,1.011;34.612,1.094;33.513,7.643;33.817,7.884;34.943,1.176;35.274,1.258;34.122,8.126;34.426,8.367;35.605,1.341;35.936,1.423;34.730,8.608;35.034,8.849;36.266,1.505;36.597,1.587;35.339,9.091;35.643,9.332;36.928,1.670;37.259,1.752;35.947,9.573;36.279,9.653;37.590,1.834;37.921,1.917;36.615,9.704;36.951,9.755;38.252,1.999;38.583,2.081;37.287,9.806;37.623,9.857;38.914,2.163;39.245,2.246;37.960,9.909;38.296,9.960;39.576,2.328;39.907,2.410;38.659,9.852;39.028,9.707;40.238,2.493;40.569,2.575;39.396,9.563;39.765,9.419;40.900,2.657;41.231,2.739;40.134,9.275;40.503,9.130;41.562,2.822;41.893,2.904;40.933,8.622;41.383,7.995;42.224,2.986;42.554,3.069;41.833,7.369;42.283,6.742;42.885,3.151;43.210,3.268;42.733,6.116;43.183,5.489;43.434,3.991;43.657,4.714;43.633,4.863
LAND1.updateTime=2025-12-22 18\:52\:36
LAND1.returnPathCoordinates=-1
LAND1.returnPathRawCoordinates=-1
LAND1.returnPointCoordinates=-1
LAND1.updateTime=2025-12-22 19\:37\:01
LAND1.userId=-1
set.properties
@@ -1,5 +1,5 @@
#Mower Configuration Properties - Updated
#Mon Dec 22 19:03:58 CST 2025
#Mon Dec 22 19:37:04 CST 2025
appVersion=-1
boundaryLengthVisible=false
currentWorkLandNumber=LAND1
@@ -8,12 +8,12 @@
handheldMarkerId=1872
idleTrailDurationSeconds=60
manualBoundaryDrawingMode=false
mapScale=11.18
mapScale=6.12
measurementModeEnabled=false
mowerId=860
serialAutoConnect=true
serialBaudRate=115200
serialPortName=COM15
simCardNumber=-1
viewCenterX=-21.01
viewCenterY=3.68
viewCenterX=-18.02
viewCenterY=7.50
src/bianjie/bianjieguihua2.java
@@ -7,6 +7,176 @@
public class bianjieguihua2 {
    /**
     * 优化边界XY坐标字符串
     *
     * @param boundaryXYString 边界XY坐标字符串,格式:"X0,Y0;X1,Y1;X2,Y2;..."
     * @return 优化后的边界坐标字符串,格式:"X0,Y0;X1,Y1;X2,Y2;..."
     */
    public static String optimizeBoundaryXYString(String boundaryXYString) {
        try {
            // 检查输入数据
            if (boundaryXYString == null || boundaryXYString.trim().isEmpty()) {
                throw new IllegalArgumentException("边界坐标字符串不能为空");
            }
            // 解析XY坐标字符串
            List<BoundaryAlgorithm.Coordinate> localCoordinates = parseXYString(boundaryXYString);
            if (localCoordinates == null || localCoordinates.isEmpty()) {
                throw new IllegalArgumentException("无法解析边界坐标字符串");
            }
            // 三角形小区域特殊处理,避免过度插值导致点数扩增
            if (localCoordinates.size() == 3) {
                double triangleArea = calculatePolygonArea(localCoordinates);
                double trianglePerimeter = calculatePerimeter(localCoordinates);
                System.out.println("检测到三角形边界,面积=" + String.format("%.2f", triangleArea) +
                        "m², 周长=" + String.format("%.2f", trianglePerimeter) + "m");
                if (triangleArea < 100.0 || trianglePerimeter < 30.0) {
                    System.out.println("小三角形,跳过插值优化");
                    BoundaryAlgorithm.Coordinate firstPoint = localCoordinates.get(0);
                    List<BoundaryAlgorithm.Coordinate> trianglePoints = new ArrayList<>(localCoordinates);
                    trianglePoints.add(new BoundaryAlgorithm.Coordinate(
                            firstPoint.x,
                            firstPoint.y,
                            firstPoint.lat,
                            firstPoint.lon));
                    return convertBoundaryPointsToString(trianglePoints);
                }
            }
            // 创建算法实例
            BoundaryAlgorithm algorithm = new BoundaryAlgorithm();
            // 自动场景分析(基于XY坐标,无高程数据)
            BoundaryAlgorithm.SceneAnalysis sceneAnalysis = analyzeSceneFromXYCoordinates(localCoordinates);
            System.out.println("自动场景分析结果:");
            System.out.println(sceneAnalysis.toString());
            // 根据场景分析结果获取参数
            BoundaryAlgorithm.BoundaryParameters params =
                algorithm.getParametersForPreset(sceneAnalysis.suggestedPreset);
            System.out.println("自动选择的参数: 间隔=" + params.interval + "米, 角度阈值=" +
                              params.angleThreshold + "度");
            // 使用优化算法处理边界
            List<BoundaryAlgorithm.Coordinate> optimizedPoints =
                algorithm.optimizeBoundaryPointsAdvanced(localCoordinates, params);
            // 质量评估
            BoundaryAlgorithm.BoundaryQuality boundaryQuality =
                algorithm.evaluateBoundaryQuality(optimizedPoints);
            System.out.println("边界质量评估结果:");
            System.out.println(boundaryQuality.toString());
            // 转换为输出字符串格式
            return convertBoundaryPointsToString(optimizedPoints);
        } catch (Exception e) {
            throw new RuntimeException("优化边界坐标字符串时发生错误: " + e.getMessage(), e);
        }
    }
    /**
     * 解析XY坐标字符串为Coordinate列表
     *
     * @param xyString XY坐标字符串,格式:"X0,Y0;X1,Y1;X2,Y2;..."
     * @return Coordinate列表
     */
    private static List<BoundaryAlgorithm.Coordinate> parseXYString(String xyString) {
        List<BoundaryAlgorithm.Coordinate> coordinates = new ArrayList<>();
        if (xyString == null || xyString.trim().isEmpty()) {
            return coordinates;
        }
        String[] points = xyString.split(";");
        for (String point : points) {
            point = point.trim();
            if (point.isEmpty()) {
                continue;
            }
            String[] parts = point.split(",");
            if (parts.length >= 2) {
                try {
                    double x = Double.parseDouble(parts[0].trim());
                    double y = Double.parseDouble(parts[1].trim());
                    // lat和lon设为0,因为我们只需要XY坐标
                    coordinates.add(new BoundaryAlgorithm.Coordinate(x, y, 0.0, 0.0));
                } catch (NumberFormatException e) {
                    System.err.println("解析坐标失败: " + point + ", 错误: " + e.getMessage());
                }
            }
        }
        return coordinates;
    }
    /**
     * 基于XY坐标进行场景分析(无高程数据)
     */
    private static BoundaryAlgorithm.SceneAnalysis analyzeSceneFromXYCoordinates(
            List<BoundaryAlgorithm.Coordinate> localCoords) {
        BoundaryAlgorithm.SceneAnalysis analysis = new BoundaryAlgorithm.SceneAnalysis();
        if (localCoords.size() < 3) {
            analysis.suggestedPreset = "复杂小区域";
            return analysis;
        }
        // 计算基本统计信息
        calculateBasicStatisticsFromCoordinates(localCoords, analysis);
        // 计算边界复杂度
        calculateBoundaryComplexityFromCoordinates(localCoords, analysis);
        // 无高程数据,设置为0
        analysis.elevationRange = 0;
        // 自动选择预设场景(不考虑高程因素)
        selectPresetAutomaticallyFromXYCoordinates(analysis);
        return analysis;
    }
    /**
     * 从XY坐标自动选择预设场景(不考虑高程因素)
     */
    private static void selectPresetAutomaticallyFromXYCoordinates(BoundaryAlgorithm.SceneAnalysis analysis) {
        // 决策逻辑基于面积和复杂度
        double areaWeight = 0.6;
        double complexityWeight = 0.4;
        // 计算综合得分
        double score = 0;
        // 面积因素:面积越大,越适合大间隔
        double areaScore = Math.min(1.0, analysis.area / 1000.0); // 1000平方米为基准
        score += areaScore * areaWeight;
        // 复杂度因素:复杂度越高,越需要小间隔
        double complexityScore = analysis.complexity;
        score += complexityScore * complexityWeight;
        // 根据得分选择预设
        if (score < 0.3) {
            analysis.suggestedPreset = "平坦大区域";
        } else if (score < 0.6) {
            analysis.suggestedPreset = "常规区域";
        } else {
            analysis.suggestedPreset = "复杂小区域";
        }
        System.out.println("自动场景选择得分: " + String.format("%.2f", score) + " -> " + analysis.suggestedPreset);
    }
    /**
     * 自动处理Coordinate列表并生成优化后的边界坐标(无需传入间隔和角度阈值)
     * 
     * @param coordinates Coordinate对象列表
src/dikuai/Dikuanbianjipage.java
@@ -105,43 +105,17 @@
                JOptionPane.showMessageDialog(this, "未找到地块信息,无法优化", "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }
            String baseStation = dikuai.getBaseStationCoordinates();
            if (baseStation == null || baseStation.trim().isEmpty() || "-1".equals(baseStation)) {
                JOptionPane.showMessageDialog(this, "基站坐标未设置,无法优化", "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }
            
            // 准备 Coordinate 列表
            String originalCoords = dikuai.getBoundaryOriginalCoordinates();
            if (originalCoords == null || originalCoords.trim().isEmpty() || "-1".equals(originalCoords)) {
                JOptionPane.showMessageDialog(this, "原始边界坐标为空,无法优化", "错误", JOptionPane.ERROR_MESSAGE);
            // 从原始边界XY坐标文本域获取输入
            String inputXY = rawXYArea.getText();
            if (inputXY == null || inputXY.trim().isEmpty() || "-1".equals(inputXY.trim())) {
                JOptionPane.showMessageDialog(this, "原始边界XY坐标为空,无法优化", "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }
            try {
                // 解析原始坐标到 Coordinate.coordinates
                List<Coordinate> coords = new ArrayList<>();
                String[] points = originalCoords.split(";");
                for (String point : points) {
                    String[] parts = point.split(",");
                    if (parts.length >= 2) {
                        double lonDecimal = Double.parseDouble(parts[0].trim());
                        double latDecimal = Double.parseDouble(parts[1].trim());
                        double alt = parts.length > 2 ? Double.parseDouble(parts[2].trim()) : 0.0;
                        // 将十进制度转换为度分格式字符串
                        String latDM = decimalToDegreeMinute(latDecimal);
                        String lonDM = decimalToDegreeMinute(lonDecimal);
                        String latDir = latDecimal >= 0 ? "N" : "S";
                        String lonDir = lonDecimal >= 0 ? "E" : "W";
                        coords.add(new Coordinate(latDM, latDir, lonDM, lonDir, alt));
                    }
                }
                Coordinate.coordinates = coords;
                // 调用优化算法
                String optimized = bianjieguihua2.processCoordinateListAuto(baseStation);
                // 调用优化算法(直接使用XY坐标字符串)
                String optimized = bianjieguihua2.optimizeBoundaryXYString(inputXY.trim());
                optTextArea.setText(optimized);
                JOptionPane.showMessageDialog(this, "边界优化完成", "提示", JOptionPane.INFORMATION_MESSAGE);
            } catch (Exception ex) {
@@ -160,11 +134,22 @@
            // 获取当前优化后的边界
            String currentOptimized = optTextArea.getText();
            
            // 保存地块编号,用于返回回调
            final String targetLandNumber = dikuai.getLandNumber();
            // 调用首页显示预览
            SwingUtilities.invokeLater(() -> {
                Shouye.showBoundaryPreview(dikuai, currentOptimized, () -> {
                    // 返回回调:重新打开此页面
                    // 返回回调:重新打开此页面,从地块对象重新读取最新的边界坐标
                    // 从 dikuaiMap 重新获取地块对象(确保获取到最新的值)
                    Dikuai updatedDikuai = Dikuai.getDikuai(targetLandNumber);
                    if (updatedDikuai != null) {
                        String latestBoundary = updatedDikuai.getBoundaryCoordinates();
                        new Dikuanbianjipage(getOwner(), getTitle(), latestBoundary, updatedDikuai).setVisible(true);
                    } else {
                        // 如果获取失败,使用当前值
                    new Dikuanbianjipage(getOwner(), getTitle(), currentOptimized, dikuai).setVisible(true);
                    }
                });
            });
        });
@@ -189,7 +174,8 @@
            Dikuai.saveToProperties();
            this.result = trimmed;
            JOptionPane.showMessageDialog(this, "地块边界坐标已更新", "成功", JOptionPane.INFORMATION_MESSAGE);
            dispose();
            // 不退出页面,只更新显示
            // dispose(); // 移除退出逻辑
        });
        closeBtn.addActionListener(e -> dispose());
src/zhuye/MapRenderer.java
@@ -247,6 +247,10 @@
                if (handleMowerClick(e.getPoint())) {
                    return;
                }
                // 优先处理优化后边界坐标点点击(边界预览模式下)
                if (boundaryPreviewActive && handleOptimizedBoundaryPointClick(e.getPoint())) {
                    return;
                }
                // 优先处理障碍物边界点点击(如果可见)
                if (obstaclePointsVisible && handleObstaclePointClick(e.getPoint())) {
                    return;
@@ -424,8 +428,8 @@
        // 绘制鼠标实时位置(手动绘制边界模式时)
        manualBoundaryDrawer.drawMousePosition(g2d, scale);
        // 绘制导航路径(中层)
        if (hasPlannedPath) {
        // 绘制导航路径(中层)- 边界预览模式下不显示导航路径
        if (hasPlannedPath && !boundaryPreviewActive) {
            drawCurrentPlannedPath(g2d);
        }
@@ -3156,7 +3160,184 @@
        // 绘制优化后边界(绿色,与正常边界颜色一致)
        if (previewOptimizedBoundary != null && previewOptimizedBoundary.size() >= 2) {
            bianjiedrwa.drawBoundary(g2d, previewOptimizedBoundary, scale, GRASS_FILL_COLOR, GRASS_BORDER_COLOR);
            // 绘制优化后边界坐标点(紫色实心圆圈,显示序号)
            drawOptimizedBoundaryPointsWithNumbers(g2d, previewOptimizedBoundary, scale);
        }
    }
    /**
     * 绘制优化后边界坐标点(紫色实心圆圈,显示序号)
     * 序号显示在点中心,字体大小11号,不随缩放变化
     */
    private void drawOptimizedBoundaryPointsWithNumbers(Graphics2D g2d, List<Point2D.Double> boundary, double scale) {
        if (boundary == null || boundary.isEmpty()) {
            return;
        }
        // 保存原始变换
        AffineTransform originalTransform = g2d.getTransform();
        // 设置点的大小(实心圆圈,直径约0.3米)
        double scaleFactor = Math.max(0.5, scale);
        double markerDiameter = 0.3; // 圆圈直径(米)
        double markerRadius = markerDiameter / 2.0;
        // 设置字体(11号,不随缩放变化)
        Font labelFont = new Font("微软雅黑", Font.PLAIN, 11);
        g2d.setFont(labelFont);
        FontMetrics fontMetrics = g2d.getFontMetrics(labelFont);
        // 紫色实心圆圈颜色
        Color purpleColor = new Color(128, 0, 128, 255); // 紫色
        // 绘制每个点及其序号
        for (int i = 0; i < boundary.size(); i++) {
            Point2D.Double point = boundary.get(i);
            double x = point.x;
            double y = point.y;
            // 绘制紫色实心圆圈(在世界坐标系中,随缩放变化)
            g2d.setColor(purpleColor);
            Ellipse2D.Double marker = new Ellipse2D.Double(
                x - markerRadius,
                y - markerRadius,
                markerDiameter,
                markerDiameter
            );
            g2d.fill(marker);
            // 将世界坐标转换为屏幕坐标以绘制序号(不随缩放变化)
            Point2D.Double worldPoint = new Point2D.Double(x, y);
            Point2D.Double screenPoint = new Point2D.Double();
            originalTransform.transform(worldPoint, screenPoint);
            // 保存当前变换
            AffineTransform savedTransform = g2d.getTransform();
            // 重置变换为屏幕坐标系统
            g2d.setTransform(new AffineTransform());
            // 绘制序号(在屏幕坐标系中,不随缩放变化)
            String numberText = String.valueOf(i + 1);
            int textWidth = fontMetrics.stringWidth(numberText);
            int textHeight = fontMetrics.getHeight();
            // 在点中心绘制序号
            int textX = (int)(screenPoint.x - textWidth / 2.0);
            int textY = (int)(screenPoint.y + textHeight / 4.0);
            // 绘制序号文字(黑色)
            g2d.setColor(Color.BLACK);
            g2d.drawString(numberText, textX, textY);
            // 恢复变换
            g2d.setTransform(savedTransform);
        }
        // 恢复原始变换
        g2d.setTransform(originalTransform);
    }
    /**
     * 处理优化后边界坐标点点击
     * @param screenPoint 屏幕坐标点
     * @return 是否处理了点击
     */
    private boolean handleOptimizedBoundaryPointClick(Point screenPoint) {
        if (previewOptimizedBoundary == null || previewOptimizedBoundary.isEmpty()) {
            return false;
        }
        // 计算选择阈值(像素)
        double threshold = computeOptimizedBoundaryPointSelectionThreshold();
        // 查找被点击的点
        int hitIndex = -1;
        for (int i = 0; i < previewOptimizedBoundary.size(); i++) {
            Point2D.Double worldPoint = previewOptimizedBoundary.get(i);
            Point2D.Double screenPosition = worldToScreen(worldPoint);
            double dx = screenPosition.x - screenPoint.x;
            double dy = screenPosition.y - screenPoint.y;
            if (Math.hypot(dx, dy) <= threshold) {
                hitIndex = i;
                break;
            }
        }
        if (hitIndex < 0) {
            return false;
        }
        // 弹出确认对话框
        String pointLabel = String.valueOf(hitIndex + 1);
        int choice = JOptionPane.showConfirmDialog(
            visualizationPanel,
            "确定要删除第" + pointLabel + "号优化后边界坐标点吗?",
            "删除边界坐标点",
            JOptionPane.OK_CANCEL_OPTION,
            JOptionPane.WARNING_MESSAGE
        );
        if (choice == JOptionPane.OK_OPTION) {
            // 删除坐标点
            List<Point2D.Double> updated = new ArrayList<>(previewOptimizedBoundary);
            updated.remove(hitIndex);
            // 更新预览边界
            previewOptimizedBoundary = updated;
            // 转换为字符串格式并保存
            String updatedBoundaryString = convertBoundaryToString(updated);
            // 通知 Shouye 保存更新后的边界坐标
            if (boundaryPreviewUpdateCallback != null) {
                boundaryPreviewUpdateCallback.accept(updatedBoundaryString);
            }
            // 刷新显示
            visualizationPanel.repaint();
        }
        return true;
    }
    /**
     * 计算优化后边界坐标点的选择阈值(像素)
     */
    private double computeOptimizedBoundaryPointSelectionThreshold() {
        double scaleFactor = Math.max(0.5, scale);
        double markerDiameterWorld = 0.3; // 圆圈直径(米)
        double markerDiameterPixels = markerDiameterWorld * scale;
        return Math.max(8.0, markerDiameterPixels * 1.5);
    }
    /**
     * 将边界点列表转换为字符串格式
     */
    private String convertBoundaryToString(List<Point2D.Double> boundary) {
        if (boundary == null || boundary.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < boundary.size(); i++) {
            Point2D.Double point = boundary.get(i);
            sb.append(String.format(Locale.US, "%.2f,%.2f", point.x, point.y));
            if (i < boundary.size() - 1) {
                sb.append(";");
            }
        }
        return sb.toString();
    }
    /**
     * 设置边界预览更新回调
     */
    private java.util.function.Consumer<String> boundaryPreviewUpdateCallback;
    public void setBoundaryPreviewUpdateCallback(java.util.function.Consumer<String> callback) {
        this.boundaryPreviewUpdateCallback = callback;
    }
}
src/zhuye/Shouye.java
@@ -133,6 +133,7 @@
    private JButton saveManualBoundaryButton;  // 保存手动绘制边界的按钮
    private String previewRestoreLandNumber;
    private String previewRestoreLandName;
    private Dikuai currentBoundaryPreviewDikuai;  // 当前边界预览的地块引用
    private boolean drawingPaused;
    private ImageIcon pauseIcon;
    private ImageIcon pauseActiveIcon;
@@ -4309,12 +4310,32 @@
            return;
        }
        
        // 保存当前地块引用
        shouye.currentBoundaryPreviewDikuai = dikuai;
        // 获取原始边界XY坐标
        String originalBoundaryXY = dikuai.getBoundaryOriginalXY();
        
        // 设置边界预览
        shouye.mapRenderer.setBoundaryPreview(originalBoundaryXY, optimizedBoundary);
        
        // 设置边界预览更新回调,用于保存删除坐标点后的边界
        shouye.mapRenderer.setBoundaryPreviewUpdateCallback(updatedBoundary -> {
            if (shouye.currentBoundaryPreviewDikuai != null && updatedBoundary != null) {
                // 保存更新后的边界坐标
                Dikuai.updateField(shouye.currentBoundaryPreviewDikuai.getLandNumber(), "boundaryCoordinates", updatedBoundary);
                java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Dikuai.updateField(shouye.currentBoundaryPreviewDikuai.getLandNumber(), "updateTime", sdf.format(new java.util.Date()));
                Dikuai.saveToProperties();
                // 同步更新当前地块对象的内存值(确保返回时能获取到最新值)
                shouye.currentBoundaryPreviewDikuai.setBoundaryCoordinates(updatedBoundary);
                // 更新预览边界(重新设置以刷新显示)
                shouye.mapRenderer.setBoundaryPreview(originalBoundaryXY, updatedBoundary);
            }
        });
        // 停止绘制割草机实时拖尾
        if (shouye.mapRenderer != null) {
            shouye.mapRenderer.setIdleTrailSuppressed(true);
@@ -4367,6 +4388,9 @@
    private void exitBoundaryPreview() {
        pathPreviewActive = false;
        
        // 清除当前地块引用
        currentBoundaryPreviewDikuai = null;
        // 恢复绘制割草机实时拖尾
        if (mapRenderer != null) {
            mapRenderer.setIdleTrailSuppressed(false);
@@ -4375,6 +4399,7 @@
        // 清除边界预览
        if (mapRenderer != null) {
            mapRenderer.clearBoundaryPreview();
            mapRenderer.setBoundaryPreviewUpdateCallback(null);
        }
        
        // 隐藏返回按钮