From dc9dce0555beb85d1262893fd5d56747d6a83855 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期五, 19 十二月 2025 11:48:37 +0800
Subject: [PATCH] 新增了导航预览功能
---
src/zhuye/celiangmoshi.java | 5
src/dikuai/ObstacleManagementPage.java | 107 ++
device.properties | 26
src/gecaoji/Gecaoji.java | 32
dikuai.properties | 24
src/dikuai/addzhangaiwu.java | 2
src/dikuai/daohangyulan.java | 548 ++++++++++++
src/zhuye/adddikuaiyulan.java | 78 +
src/gecaoji/gecaolunjing.java | 7
src/zhangaiwu/AddDikuai.java | 438 +++++++++-
src/zhuye/MapRenderer.java | 155 +++
src/zhuye/pointandnumber.java | 10
set.properties | 12
src/zhuye/Shouye.java | 84 +
Obstacledge.properties | 9
src/dikuai/Dikuai.java | 30
src/dikuai/Dikuaiguanli.java | 186 +++
src/lujing/MowingPathGenerationPage.java | 34
src/lujing/ObstaclePathPlanner.java | 6
src/zhuye/bianjiedrwa.java | 38
basestation.properties | 6
src/bianjie/BoundaryLengthDrawer.java | 5
src/lujing/Lunjingguihua.java | 701 ++++-----------
23 files changed, 1,851 insertions(+), 692 deletions(-)
diff --git a/Obstacledge.properties b/Obstacledge.properties
index cf3e2ba..54617c9 100644
--- a/Obstacledge.properties
+++ b/Obstacledge.properties
@@ -1,19 +1,14 @@
# 鍓茶崏鏈哄湴鍧楅殰纰嶇墿閰嶇疆鏂囦欢
-# 鐢熸垚鏃堕棿锛�2025-12-17T19:15:02.630670200
+# 鐢熸垚鏃堕棿锛�2025-12-18T19:44:44.126173900
# 鍧愭爣绯伙細WGS84锛堝害鍒嗘牸寮忥級
# ============ 鍦板潡鍩哄噯绔欓厤缃� ============
# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].baseStation=[缁忓害],[N/S],[绾害],[E/W]
plot.DK-001.baseStation=3949.902389,N,11616.756920,E
-plot.LAND1.baseStation=3949.902389,N,11616.756920,E
+plot.LAND1.baseStation=3949.891518,N,11616.792675,E
# ============ 闅滅鐗╅厤缃� ============
# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.shape=[0|1]
# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.originalCoords=[鍧愭爣涓瞉
# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.xyCoords=[鍧愭爣涓瞉
-# --- 鍦板潡LAND1鐨勯殰纰嶇墿 ---
-plot.LAND1.obstacle.1234.shape=0
-plot.LAND1.obstacle.1234.originalCoords=3949.902533,N;11616.757411,E;3949.902502,N;11616.757243,E;3949.902473,N;11616.757063,E;3949.902460,N;11616.756947,E;3949.902438,N;11616.756821,E;3949.902430,N;11616.756804,E;3949.902408,N;11616.756828,E;3949.902392,N;11616.756884,E;3949.902388,N;11616.756920,E;3949.902389,N;11616.756920,E;3949.902387,N;11616.756920,E;3949.902388,N;11616.756919,E;3949.902388,N;11616.756922,E;3949.902388,N;11616.756922,E;3949.902388,N;11616.756922,E;3949.902389,N;11616.756923,E;3949.902389,N;11616.756920,E;3949.901649,N;11616.757013,E;3949.901650,N;11616.757016,E;3949.901650,N;11616.757014,E;3949.901650,N;11616.757013,E
-plot.LAND1.obstacle.1234.xyCoords=0.43,-0.56;1.30,-0.56
-
diff --git a/basestation.properties b/basestation.properties
index 1a3f4a0..a1b9dfb 100644
--- a/basestation.properties
+++ b/basestation.properties
@@ -1,7 +1,7 @@
#Base station properties
-#Tue Dec 09 19:03:31 CST 2025
+#Thu Dec 18 16:15:01 CST 2025
dataUpdateTime=-1
deviceActivationTime=-1
-deviceId=4567
-installationCoordinates=3949.90238860,N,11616.75692000,E
+deviceId=1872
+installationCoordinates=3949.89151752,N,11616.79267501,E
iotSimCardNumber=-1
diff --git a/device.properties b/device.properties
index 542d7ea..260ace6 100644
--- a/device.properties
+++ b/device.properties
@@ -1,29 +1,29 @@
-#Updated base station coordinates
-#Mon Nov 24 14:54:00 CST 2025
+#Updated base station information
+#Thu Dec 18 16:15:01 CST 2025
BupdateTime=-1
GupdateTime=-1
baseStationCardNumber=-1
-baseStationCoordinates=2324.194945,N,11330.938547,E
+baseStationCoordinates=3949.89151752,N,11616.79267501,E
baseStationNumber=-1
+battery=-1
createTime=-1
deviceCardnumber=-1
+differentialAge=-1
+heading=-1
+mowerBladeHeight=-1
+mowerLightStatus=-1
mowerModel=-1
mowerName=-1
mowerNumber=-1
+mowerStartStatus=-1
mowingHeight=-1
mowingWidth=-1
+pitch=-1
+positioningStatus=-1
+realtimeAltitude=-1
realtimeLatitude=-1
realtimeLongitude=-1
-realtimeAltitude=-1
+realtimeSpeed=-1
realtimeX=-1
realtimeY=-1
-realtimeSpeed=-1
-heading=-1
-pitch=-1
-battery=-1
-positioningStatus=-1
satelliteCount=-1
-differentialAge=-1
-mowerStartStatus=-1
-mowerLightStatus=-1
-mowerBladeHeight=-1
diff --git a/dikuai.properties b/dikuai.properties
index 35bdcf1..885a34d 100644
--- a/dikuai.properties
+++ b/dikuai.properties
@@ -1,19 +1,21 @@
#Dikuai Properties
-#Wed Dec 17 19:15:02 CST 2025
+#Thu Dec 18 19:44:44 CST 2025
LAND1.angleThreshold=-1
-LAND1.baseStationCoordinates=3949.90238860,N,11616.75692000,E
-LAND1.boundaryCoordinates=77.19,-32.68;80.71,-54.97;80.99,-55.90;83.54,-56.46;85.04,-55.55;85.94,-53.74;83.24,-35.82;84.55,-34.54;94.02,-31.92;94.10,-31.11;90.88,-20.39;90.35,-19.53;88.33,-19.00;84.12,-19.47;78.92,-22.36;76.63,-25.55;76.93,-29.84;77.06,-31.26;77.19,-32.68
-LAND1.boundaryOriginalCoordinates=39.831413,116.280186,49.12;39.831409,116.280188,49.09;39.831403,116.280187,49.12;39.831395,116.280189,49.13;39.831388,116.280191,49.16;39.831379,116.280193,49.18;39.831370,116.280194,49.16;39.831362,116.280195,49.15;39.831353,116.280197,49.11;39.831344,116.280200,49.15;39.831335,116.280202,49.15;39.831326,116.280204,49.08;39.831317,116.280206,49.19;39.831309,116.280209,49.18;39.831301,116.280210,49.19;39.831293,116.280212,49.11;39.831284,116.280214,49.06;39.831275,116.280215,49.16;39.831266,116.280217,49.14;39.831258,116.280220,49.08;39.831249,116.280223,49.09;39.831240,116.280225,49.10;39.831231,116.280226,49.04;39.831222,116.280226,49.17;39.831212,116.280227,49.11;39.831204,116.280230,49.09;39.831201,116.280238,49.10;39.831199,116.280249,49.07;39.831199,116.280260,49.21;39.831202,116.280270,49.16;39.831207,116.280278,49.06;39.831212,116.280284,49.04;39.831217,116.280287,49.05;39.831223,116.280288,49.09;39.831229,116.280287,49.10;39.831237,116.280286,49.05;39.831245,116.280286,49.08;39.831254,116.280284,49.07;39.831263,116.280283,49.05;39.831272,116.280280,49.11;39.831282,116.280278,49.10;39.831291,116.280276,49.11;39.831300,116.280274,49.16;39.831308,116.280270,49.13;39.831318,116.280268,49.10;39.831327,116.280267,49.14;39.831337,116.280266,49.08;39.831347,116.280263,49.10;39.831356,116.280261,49.20;39.831366,116.280258,49.14;39.831375,116.280256,49.09;39.831384,116.280257,49.13;39.831392,116.280263,49.10;39.831396,116.280272,49.12;39.831398,116.280283,49.16;39.831401,116.280294,49.11;39.831403,116.280307,49.13;39.831405,116.280318,49.19;39.831406,116.280328,49.20;39.831408,116.280340,49.22;39.831411,116.280353,49.19;39.831414,116.280363,49.26;39.831416,116.280374,49.22;39.831419,116.280383,49.20;39.831427,116.280384,49.21;39.831433,116.280379,49.17;39.831441,116.280375,49.19;39.831451,116.280372,49.09;39.831459,116.280370,49.16;39.831467,116.280364,49.21;39.831476,116.280360,49.22;39.831485,116.280357,49.20;39.831495,116.280355,49.26;39.831505,116.280351,49.21;39.831514,116.280348,49.17;39.831523,116.280346,49.20;39.831531,116.280340,49.04;39.831535,116.280328,49.08;39.831536,116.280316,49.03;39.831535,116.280304,49.03;39.831533,116.280291,49.06;39.831532,116.280279,49.07;39.831531,116.280267,49.11;39.831528,116.280257,49.09;39.831525,116.280246,49.11;39.831521,116.280237,49.09;39.831516,116.280227,49.08;39.831511,116.280216,49.12;39.831505,116.280206,49.14;39.831499,116.280197,49.12;39.831492,116.280189,49.15;39.831484,116.280184,49.14;39.831477,116.280179,49.12;39.831469,116.280178,49.12;39.831462,116.280181,49.13;39.831454,116.280182,49.12;39.831445,116.280183,49.12;39.831439,116.280183,49.14;39.831438,116.280183,49.12
+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;41.91,8.49;44.09,4.86;43.49,2.92;33.76,0.59;30.22,-1.26;30.09,-3.80;34.19,-28.28;32.68,-29.99;30.12,-30.19;28.89,-28.59;25.58,-6.77;24.09,-4.26;21.10,-3.94;-10.36,-8.92;-11.69,-8.57;-12.75,-4.68;-12.23,-3.28;-6.21,-2.34;0.00,0.00;1.44,-0.09;2.87,-0.19
+LAND1.boundaryOriginalCoordinates=39.831524,116.279912,49.30;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;39.831285,116.280277,49.22;39.831271,116.280278,49.16;39.831261,116.280273,49.23;39.831256,116.280261,49.30;39.831253,116.280245,49.20;39.831254,116.280231,49.22;39.831258,116.280220,49.10;39.831268,116.280216,49.17;39.831281,116.280214,49.14;39.831295,116.280212,49.14;39.831308,116.280209,49.17;39.831321,116.280206,49.14;39.831334,116.280204,49.17;39.831348,116.280202,49.21;39.831362,116.280198,49.21;39.831375,116.280195,49.23;39.831390,116.280193,49.25;39.831405,116.280189,49.20;39.831420,116.280187,49.19;39.831436,116.280185,49.21;39.831450,116.280182,49.19;39.831464,116.280177,49.17;39.831478,116.280171,49.14;39.831487,116.280160,49.25;39.831491,116.280143,49.21;39.831490,116.280125,49.18;39.831487,116.280107,49.23;39.831485,116.280089,49.27;39.831483,116.280072,49.24;39.831482,116.280054,49.25;39.831480,116.280037,49.25;39.831477,116.280020,49.30;39.831475,116.280004,49.31;39.831473,116.279989,49.30;39.831472,116.279973,49.23;39.831470,116.279958,49.26;39.831468,116.279941,49.29;39.831465,116.279925,49.33;39.831463,116.279908,49.34;39.831462,116.279891,49.35;39.831461,116.279874,49.33;39.831458,116.279859,49.34;39.831456,116.279843,49.32;39.831454,116.279827,49.32;39.831452,116.279813,49.42;39.831450,116.279798,49.41;39.831448,116.279783,49.36;39.831447,116.279769,49.26;39.831445,116.279757,49.24;39.831445,116.279747,49.38;39.831448,116.279741,49.27;39.831455,116.279736,49.26;39.831463,116.279733,49.26;39.831473,116.279731,49.26;39.831483,116.279729,49.25;39.831491,116.279730,49.26;39.831496,116.279735,49.22;39.831497,116.279748,49.27;39.831498,116.279762,49.27;39.831500,116.279776,49.34;39.831502,116.279791,49.32;39.831504,116.279805,49.27;39.831507,116.279820,49.28;39.831510,116.279835,49.20;39.831513,116.279849,49.25;39.831517,116.279860,49.30;39.831520,116.279864,49.32;39.831520,116.279865,49.34;39.831522,116.279873,49.25;39.831524,116.279878,49.25;39.831525,116.279878,49.24
LAND1.boundaryPointInterval=-1
-LAND1.createTime=2025-12-16 15\:43\:39
+LAND1.createTime=2025-12-18 17\:12\:20
LAND1.intelligentSceneAnalysis=-1
-LAND1.landArea=327.17
-LAND1.landName=yyii
+LAND1.landArea=483.34
+LAND1.landName=123
LAND1.landNumber=LAND1
+LAND1.mowingBladeWidth=0.40
+LAND1.mowingOverlapDistance=0.06
LAND1.mowingPattern=骞宠绾�
-LAND1.mowingTrack=
-LAND1.mowingWidth=40
-LAND1.plannedPath=77.45,-31.44;81.28,-55.71;81.70,-55.80;77.91,-31.78;78.05,-31.91;78.17,-31.98;78.35,-32.01;82.12,-55.89;82.54,-55.98;78.77,-32.09;78.95,-32.12;79.17,-32.09;82.96,-56.08;83.38,-56.17;79.57,-32.03;79.96,-31.95;83.76,-56.03;84.13,-55.81;80.35,-31.86;80.50,-31.80;80.72,-31.63;84.50,-55.58;84.87,-55.33;81.08,-31.34;81.41,-30.87;85.18,-54.72;85.48,-54.10;81.75,-30.45;81.89,-30.27;81.94,-30.19;82.05,-29.82;83.00,-35.83;83.34,-35.38;81.43,-23.31;81.47,-22.18;81.43,-22.04;81.32,-21.94;81.21,-21.92;81.13,-21.42;81.50,-21.21;83.69,-35.03;84.04,-34.69;81.88,-21.00;82.25,-20.80;84.39,-34.35;84.77,-34.22;82.62,-20.59;82.99,-20.38;85.16,-34.11;85.55,-34.00;83.37,-20.18;83.74,-19.97;85.94,-33.90;86.32,-33.79;84.11,-19.76;84.50,-19.68;86.71,-33.68;87.10,-33.57;84.90,-19.63;85.30,-19.59;87.49,-33.47;87.88,-33.36;85.70,-19.55;86.09,-19.50;88.26,-33.25;88.65,-33.15;86.49,-19.46;86.89,-19.41;89.04,-33.04;89.43,-32.93;87.29,-19.37;87.69,-19.32;89.82,-32.82;90.20,-32.72;88.08,-19.28;88.49,-19.30;90.59,-32.61;90.98,-32.50;88.91,-19.41;89.34,-19.52;91.37,-32.39;91.76,-32.29;89.76,-19.63;90.18,-19.74;92.14,-32.18;92.53,-32.07;90.76,-20.88;91.62,-23.72;92.92,-31.96;93.31,-31.86;92.47,-26.56;93.33,-29.40;93.70,-31.75;81.40,-22.00;81.27,-21.93;81.21,-21.92;81.08,-21.96;80.98,-22.05;79.85,-23.55;79.64,-22.24;80.01,-22.04;80.22,-23.37;80.58,-23.05;80.39,-21.83;80.76,-21.62;80.87,-22.34;77.96,-24.59;77.63,-24.92;77.59,-24.64;77.92,-24.18;77.99,-24.57;78.36,-24.38;78.25,-23.72;78.59,-23.25;78.73,-24.19;79.11,-23.99;78.92,-22.79;79.27,-22.45;79.48,-23.76;77.99,-24.57;77.93,-24.62;77.51,-25.05;77.47,-25.11;77.20,-25.66;77.01,-26.12;76.93,-25.57;77.26,-25.10;77.31,-25.43
+LAND1.mowingTrack=-1
+LAND1.mowingWidth=34
+LAND1.plannedPath=35.722,9.394;38.565,9.844;39.065,9.579;35.179,8.964;34.636,8.534;39.565,9.314;40.065,9.049;34.094,8.104;33.551,7.673;40.565,8.784;41.066,8.519;33.009,7.243;32.466,6.813;41.566,8.254;41.804,7.947;31.923,6.383;31.381,5.953;41.993,7.633;42.182,7.319;30.838,5.523;30.296,5.093;42.371,7.004;42.560,6.690;29.753,4.663;29.210,4.232;42.748,6.375;0.018,-0.389;0.094,-0.377;1.645,-0.475;-1.557,-0.982;42.937,6.061;28.668,3.802;28.125,3.372;43.126,5.747;-3.133,-1.576;2.993,-0.606;3.797,-0.823;-4.708,-2.169;43.315,5.432;27.172,2.877;26.181,2.376;43.503,5.118;-11.978,-3.664;4.602,-1.040;5.407,-1.257;-12.114,-4.030;43.685,4.803;25.191,1.875;24.200,1.374;43.573,4.441;-12.250,-4.396;6.212,-1.474;15.278,-0.383;-12.346,-4.755;43.461,4.079;23.210,0.873;-12.256,-5.085;43.349,3.717;43.237,3.355;-12.166,-5.415;-12.076,-5.745;40.411,2.563;36.170,1.548;-11.986,-6.075;-11.896,-6.405;33.250,0.741;32.306,0.247;-11.806,-6.735;-11.717,-7.065;31.361,-0.246;30.416,-0.740;-11.627,-7.395;-11.537,-7.725;29.861,-1.172;29.836,-1.520;-11.447,-8.055;-11.095,-8.344;29.818,-1.867;29.801,-2.215;21.190,-3.578;22.488,-3.716;29.783,-2.562;29.765,-2.909;23.785,-3.855;24.424,-4.098;29.747,-3.256;29.730,-3.603;24.611,-4.413;24.798,-4.728;29.739,-3.945;29.795,-4.281;24.985,-5.042;25.171,-5.357;29.852,-4.616;29.908,-4.951;25.358,-5.672;25.545,-5.986;29.964,-5.287;30.020,-5.622;25.732,-6.301;25.915,-6.616;30.076,-5.957;30.132,-6.293;25.982,-6.950;26.033,-7.286;30.189,-6.628;30.245,-6.964;26.084,-7.622;26.135,-7.958;30.301,-7.299;30.357,-7.634;26.185,-8.295;26.236,-8.631;30.413,-7.970;30.469,-8.305;26.287,-8.967;26.338,-9.303;30.526,-8.640;30.582,-8.976;26.389,-9.639;26.440,-9.975;30.638,-9.311;30.694,-9.646;26.491,-10.312;26.542,-10.648;30.750,-9.982;30.806,-10.317;26.593,-10.984;26.644,-11.320;30.862,-10.652;30.919,-10.988;26.695,-11.656;26.746,-11.992;30.975,-11.323;31.031,-11.658;26.797,-12.328;26.848,-12.665;31.087,-11.994;31.143,-12.329;26.899,-13.001;26.950,-13.337;31.199,-12.664;31.256,-13.000;27.001,-13.673;27.052,-14.009;31.312,-13.335;31.368,-13.670;27.103,-14.345;27.154,-14.682;31.424,-14.006;31.480,-14.341;27.205,-15.018;27.256,-15.354;31.536,-14.676;31.593,-15.012;27.307,-15.690;27.358,-16.026;31.649,-15.347;31.705,-15.682;27.409,-16.362;27.460,-16.699;31.761,-16.018;31.817,-16.353;27.511,-17.035;27.562,-17.371;31.873,-16.688;31.930,-17.024;27.613,-17.707;27.664,-18.043;31.986,-17.359;32.042,-17.694;27.715,-18.379;27.766,-18.716;32.098,-18.030;32.154,-18.365;27.817,-19.052;27.868,-19.388;32.210,-18.701;32.267,-19.036;27.919,-19.724;27.970,-20.060;32.323,-19.371;32.379,-19.707;28.021,-20.396;28.072,-20.733;32.435,-20.042;32.491,-20.377;28.123,-21.069;28.174,-21.405;32.547,-20.713;32.604,-21.048;28.225,-21.741;28.276,-22.077;32.660,-21.383;32.716,-21.719;28.327,-22.413;28.378,-22.749;32.772,-22.054;32.828,-22.389;28.429,-23.086;28.480,-23.422;32.884,-22.725;32.941,-23.060;28.531,-23.758;28.582,-24.094;32.997,-23.395;33.053,-23.731;28.633,-24.430;28.684,-24.766;33.109,-24.066;33.165,-24.401;28.735,-25.103;28.786,-25.439;33.221,-24.737;33.278,-25.072;28.837,-25.775;28.888,-26.111;33.334,-25.407;33.390,-25.743;28.939,-26.447;28.990,-26.783;33.446,-26.078;33.502,-26.413;29.041,-27.120;29.092,-27.456;33.558,-26.749;33.615,-27.084;29.143,-27.792;29.194,-28.128;33.671,-27.419;33.727,-27.755;29.258,-28.462;29.494,-28.769;33.783,-28.090;33.524,-28.475;29.730,-29.076;29.966,-29.383;33.170,-28.876;32.817,-29.276;30.202,-29.690
LAND1.returnPointCoordinates=-1
-LAND1.updateTime=2025-12-17 19\:15\:02
+LAND1.updateTime=2025-12-18 19\:44\:44
LAND1.userId=-1
diff --git a/set.properties b/set.properties
index 1e8efa5..3bd8130 100644
--- a/set.properties
+++ b/set.properties
@@ -1,18 +1,18 @@
#Mower Configuration Properties - Updated
-#Wed Dec 17 19:35:27 CST 2025
+#Fri Dec 19 11:48:07 CST 2025
appVersion=-1
boundaryLengthVisible=false
currentWorkLandNumber=LAND1
cuttingWidth=200
firmwareVersion=-1
-handheldMarkerId=
+handheldMarkerId=1872
idleTrailDurationSeconds=60
-mapScale=28.94
+mapScale=5.63
measurementModeEnabled=false
-mowerId=1872
+mowerId=860
serialAutoConnect=true
serialBaudRate=115200
serialPortName=COM15
simCardNumber=-1
-viewCenterX=-3.17
-viewCenterY=0.87
+viewCenterX=-15.67
+viewCenterY=9.92
diff --git a/src/bianjie/BoundaryLengthDrawer.java b/src/bianjie/BoundaryLengthDrawer.java
index 125209e..c4fdafa 100644
--- a/src/bianjie/BoundaryLengthDrawer.java
+++ b/src/bianjie/BoundaryLengthDrawer.java
@@ -134,3 +134,8 @@
}
}
+
+
+
+
+
diff --git a/src/dikuai/Dikuai.java b/src/dikuai/Dikuai.java
index b3f127a..c33d8e4 100644
--- a/src/dikuai/Dikuai.java
+++ b/src/dikuai/Dikuai.java
@@ -43,6 +43,10 @@
private String mowingPattern;
// 鍓茶崏瀹藉害
private String mowingWidth;
+ // 鍓茶崏鏈哄壊鍒�瀹藉害锛堢背锛夛紝榛樿0.40绫�
+ private String mowingBladeWidth;
+ // 鍓茶崏閲嶅彔璺濈锛堢背锛夛紝榛樿0.06绫�
+ private String mowingOverlapDistance;
// 瀛樺偍澶氫釜鍦板潡鐨勬槧灏勮〃锛岄敭涓哄湴鍧楃紪鍙�
private static Map<String, Dikuai> dikuaiMap = new HashMap<>();
@@ -104,6 +108,8 @@
dikuai.mowingPattern = landProps.getProperty("mowingPattern", "-1");
dikuai.mowingWidth = landProps.getProperty("mowingWidth", "-1");
dikuai.mowingTrack = landProps.getProperty("mowingTrack", "-1");
+ dikuai.mowingBladeWidth = landProps.getProperty("mowingBladeWidth", "0.40");
+ dikuai.mowingOverlapDistance = landProps.getProperty("mowingOverlapDistance", "0.06");
dikuaiMap.put(landNum, dikuai);
}
@@ -225,6 +231,12 @@
case "mowingTrack":
this.mowingTrack = value;
return true;
+ case "mowingBladeWidth":
+ this.mowingBladeWidth = value;
+ return true;
+ case "mowingOverlapDistance":
+ this.mowingOverlapDistance = value;
+ return true;
default:
System.err.println("鏈煡瀛楁: " + fieldName);
return false;
@@ -259,6 +271,8 @@
if (dikuai.mowingPattern != null) properties.setProperty(landNumber + ".mowingPattern", dikuai.mowingPattern);
if (dikuai.mowingWidth != null) properties.setProperty(landNumber + ".mowingWidth", dikuai.mowingWidth);
if (dikuai.mowingTrack != null) properties.setProperty(landNumber + ".mowingTrack", dikuai.mowingTrack);
+ if (dikuai.mowingBladeWidth != null) properties.setProperty(landNumber + ".mowingBladeWidth", dikuai.mowingBladeWidth);
+ if (dikuai.mowingOverlapDistance != null) properties.setProperty(landNumber + ".mowingOverlapDistance", dikuai.mowingOverlapDistance);
}
try {
@@ -410,6 +424,22 @@
this.mowingTrack = mowingTrack;
}
+ public String getMowingBladeWidth() {
+ return mowingBladeWidth;
+ }
+
+ public void setMowingBladeWidth(String mowingBladeWidth) {
+ this.mowingBladeWidth = mowingBladeWidth;
+ }
+
+ public String getMowingOverlapDistance() {
+ return mowingOverlapDistance;
+ }
+
+ public void setMowingOverlapDistance(String mowingOverlapDistance) {
+ this.mowingOverlapDistance = mowingOverlapDistance;
+ }
+
@Override
public String toString() {
return "Dikuai{" +
diff --git a/src/dikuai/Dikuaiguanli.java b/src/dikuai/Dikuaiguanli.java
index c84eecb..edd81bb 100644
--- a/src/dikuai/Dikuaiguanli.java
+++ b/src/dikuai/Dikuaiguanli.java
@@ -268,9 +268,7 @@
contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
// 鍦板潡杈圭晫鍧愭爣锛堝甫鏄剧ず椤剁偣鎸夐挳锛�
- JPanel boundaryPanel = createBoundaryInfoItem(dikuai,
- getTruncatedValue(dikuai.getBoundaryCoordinates(), 12, "鏈缃�"));
- setInfoItemTooltip(boundaryPanel, dikuai.getBoundaryCoordinates());
+ JPanel boundaryPanel = createBoundaryInfoItem(dikuai);
configureInteractiveLabel(getInfoItemTitleLabel(boundaryPanel),
() -> editBoundaryCoordinates(dikuai),
"鐐瑰嚮鏌ョ湅/缂栬緫鍦板潡杈圭晫鍧愭爣");
@@ -291,56 +289,60 @@
contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
// 璺緞鍧愭爣锛堝甫鏌ョ湅鎸夐挳锛�
- JPanel pathPanel = createCardInfoItemWithButton("璺緞鍧愭爣:",
- getTruncatedValue(dikuai.getPlannedPath(), 12, "鏈缃�"),
- "澶嶅埗", e -> copyCoordinatesAction("璺緞鍧愭爣", dikuai.getPlannedPath()));
- setInfoItemTooltip(pathPanel, dikuai.getPlannedPath());
+ JPanel pathPanel = createCardInfoItemWithButtonOnly("璺緞鍧愭爣:",
+ "鏌ョ湅", e -> editPlannedPath(dikuai));
configureInteractiveLabel(getInfoItemTitleLabel(pathPanel),
() -> editPlannedPath(dikuai),
"鐐瑰嚮鏌ョ湅/缂栬緫璺緞鍧愭爣");
contentPanel.add(pathPanel);
contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
- JPanel baseStationPanel = createCardInfoItemWithButton("鍩虹珯鍧愭爣:",
- getTruncatedValue(dikuai.getBaseStationCoordinates(), 12, "鏈缃�"),
- "澶嶅埗", e -> copyCoordinatesAction("鍩虹珯鍧愭爣", dikuai.getBaseStationCoordinates()));
- setInfoItemTooltip(baseStationPanel, dikuai.getBaseStationCoordinates());
- configureInteractiveLabel(getInfoItemTitleLabel(baseStationPanel),
- () -> editBaseStationCoordinates(dikuai),
- "鐐瑰嚮鏌ョ湅/缂栬緫鍩虹珯鍧愭爣");
- contentPanel.add(baseStationPanel);
+ JPanel baseStationPanel = createCardInfoItemWithButtonOnly("鍩虹珯鍧愭爣:",
+ "鏌ョ湅", e -> editBaseStationCoordinates(dikuai));
+ configureInteractiveLabel(getInfoItemTitleLabel(baseStationPanel),
+ () -> editBaseStationCoordinates(dikuai),
+ "鐐瑰嚮鏌ョ湅/缂栬緫鍩虹珯鍧愭爣");
+ contentPanel.add(baseStationPanel);
contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
- JPanel boundaryOriginalPanel = createCardInfoItemWithButton("杈圭晫鍘熷鍧愭爣:",
- getTruncatedValue(dikuai.getBoundaryOriginalCoordinates(), 12, "鏈缃�"),
- "澶嶅埗", e -> copyCoordinatesAction("杈圭晫鍘熷鍧愭爣", dikuai.getBoundaryOriginalCoordinates()));
- setInfoItemTooltip(boundaryOriginalPanel, dikuai.getBoundaryOriginalCoordinates());
- configureInteractiveLabel(getInfoItemTitleLabel(boundaryOriginalPanel),
- () -> editBoundaryOriginalCoordinates(dikuai),
- "鐐瑰嚮鏌ョ湅/缂栬緫杈圭晫鍘熷鍧愭爣");
- contentPanel.add(boundaryOriginalPanel);
+ JPanel boundaryOriginalPanel = createCardInfoItemWithButtonOnly("杈圭晫鍘熷鍧愭爣:",
+ "鏌ョ湅", e -> editBoundaryOriginalCoordinates(dikuai));
+ configureInteractiveLabel(getInfoItemTitleLabel(boundaryOriginalPanel),
+ () -> editBoundaryOriginalCoordinates(dikuai),
+ "鐐瑰嚮鏌ョ湅/缂栬緫杈圭晫鍘熷鍧愭爣");
+ contentPanel.add(boundaryOriginalPanel);
contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
- JPanel mowingPatternPanel = createCardInfoItemWithButton("鍓茶崏妯″紡:",
- getTruncatedValue(dikuai.getMowingPattern(), 12, "鏈缃�"),
- "澶嶅埗", e -> copyCoordinatesAction("鍓茶崏妯″紡", dikuai.getMowingPattern()));
- setInfoItemTooltip(mowingPatternPanel, dikuai.getMowingPattern());
+ JPanel mowingPatternPanel = createCardInfoItem("鍓茶崏妯″紡:",
+ formatMowingPatternForDisplay(dikuai.getMowingPattern()));
configureInteractiveLabel(getInfoItemTitleLabel(mowingPatternPanel),
() -> editMowingPattern(dikuai),
"鐐瑰嚮鏌ョ湅/缂栬緫鍓茶崏妯″紡");
contentPanel.add(mowingPatternPanel);
contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
- String mowingWidthValue = dikuai.getMowingWidth();
- String widthSource = null;
- if (mowingWidthValue != null && !"-1".equals(mowingWidthValue) && !mowingWidthValue.trim().isEmpty()) {
- widthSource = mowingWidthValue + "鍘樼背";
+ // 鍓茶崏鏈哄壊鍒�瀹藉害
+ String mowingBladeWidthValue = dikuai.getMowingBladeWidth();
+ String displayBladeWidth = "鏈缃�";
+ if (mowingBladeWidthValue != null && !"-1".equals(mowingBladeWidthValue) && !mowingBladeWidthValue.trim().isEmpty()) {
+ try {
+ double bladeWidthMeters = Double.parseDouble(mowingBladeWidthValue.trim());
+ double bladeWidthCm = bladeWidthMeters * 100.0;
+ displayBladeWidth = String.format("%.2f鍘樼背", bladeWidthCm);
+ } catch (NumberFormatException e) {
+ displayBladeWidth = "鏈缃�";
+ }
}
- String displayWidth = getTruncatedValue(widthSource, 12, "鏈缃�");
- JPanel mowingWidthPanel = createCardInfoItemWithButton("鍓茶崏瀹藉害:",
- displayWidth,
- "缂栬緫", e -> editMowingWidth(dikuai));
- setInfoItemTooltip(mowingWidthPanel, widthSource);
+ JPanel mowingBladeWidthPanel = createCardInfoItem("鍓茶崏鏈哄壊鍒�瀹藉害:", displayBladeWidth);
+ contentPanel.add(mowingBladeWidthPanel);
+ contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+
+ String mowingWidthValue = dikuai.getMowingWidth();
+ String displayWidth = "鏈缃�";
+ if (mowingWidthValue != null && !"-1".equals(mowingWidthValue) && !mowingWidthValue.trim().isEmpty()) {
+ displayWidth = mowingWidthValue + "鍘樼背";
+ }
+ JPanel mowingWidthPanel = createCardInfoItem("鍓茶崏瀹藉害:", displayWidth);
contentPanel.add(mowingWidthPanel);
contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
@@ -361,11 +363,16 @@
JButton generatePathBtn = createPrimaryFooterButton("璺緞瑙勫垝");
generatePathBtn.addActionListener(e -> showPathPlanningPage(dikuai));
+ JButton navigationPreviewBtn = createPrimaryFooterButton("瀵艰埅棰勮");
+ navigationPreviewBtn.addActionListener(e -> startNavigationPreview(dikuai));
+
JPanel footerPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
footerPanel.setBackground(CARD_BACKGROUND);
footerPanel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0));
footerPanel.add(generatePathBtn);
footerPanel.add(Box.createHorizontalStrut(12));
+ footerPanel.add(navigationPreviewBtn);
+ footerPanel.add(Box.createHorizontalStrut(12));
footerPanel.add(deleteBtn);
card.add(footerPanel, BorderLayout.SOUTH);
@@ -387,6 +394,7 @@
itemPanel.add(labelComp, BorderLayout.WEST);
itemPanel.add(valueComp, BorderLayout.EAST);
+ itemPanel.putClientProperty("titleLabel", labelComp);
return itemPanel;
}
@@ -425,7 +433,35 @@
return itemPanel;
}
- private JPanel createBoundaryInfoItem(Dikuai dikuai, String displayValue) {
+ private JPanel createCardInfoItemWithButtonOnly(String label, String buttonText, ActionListener listener) {
+ JPanel itemPanel = new JPanel(new BorderLayout());
+ itemPanel.setBackground(CARD_BACKGROUND);
+ // 澧炲姞楂樺害浠ョ‘淇濇寜閽畬鏁存樉绀猴紙鎸夐挳楂樺害绾�24-28鍍忕礌锛屽姞涓婁笂涓嬭竟璺濓級
+ itemPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 35));
+ itemPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, 30));
+ itemPanel.setMinimumSize(new Dimension(0, 28));
+
+ JLabel labelComp = new JLabel(label);
+ labelComp.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+ labelComp.setForeground(LIGHT_TEXT);
+
+ JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
+ rightPanel.setBackground(CARD_BACKGROUND);
+ // 娣诲姞鍨傜洿鍐呰竟璺濅互纭繚鎸夐挳涓嶈瑁佸壀
+ rightPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
+
+ JButton button = createSmallLinkButton(buttonText, listener);
+
+ rightPanel.add(button);
+
+ itemPanel.add(labelComp, BorderLayout.WEST);
+ itemPanel.add(rightPanel, BorderLayout.CENTER);
+ itemPanel.putClientProperty("titleLabel", labelComp);
+
+ return itemPanel;
+ }
+
+ private JPanel createBoundaryInfoItem(Dikuai dikuai) {
JPanel itemPanel = new JPanel(new BorderLayout());
itemPanel.setBackground(CARD_BACKGROUND);
// 澧炲姞楂樺害浠ョ‘淇濇寜閽笅杈圭紭瀹屾暣鏄剧ず锛堟寜閽珮搴�56锛屽姞涓婁笂涓嬭竟璺濓級
@@ -445,18 +481,25 @@
rightPanel.setBackground(CARD_BACKGROUND);
rightPanel.setBorder(BorderFactory.createEmptyBorder(verticalPadding, 0, verticalPadding, 0));
- JLabel valueComp = new JLabel(displayValue);
- valueComp.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
- valueComp.setForeground(TEXT_COLOR);
+ // 鐘舵�佹彁绀烘枃瀛楁爣绛�
+ JLabel statusLabel = new JLabel();
+ statusLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+ statusLabel.setForeground(LIGHT_TEXT);
JButton toggleButton = createBoundaryToggleButton(dikuai);
+ // 灏嗙姸鎬佹爣绛惧拰鎸夐挳鍏宠仈锛屼互渚垮湪鎸夐挳鐘舵�佸彉鍖栨椂鏇存柊鏍囩
+ toggleButton.putClientProperty("statusLabel", statusLabel);
- rightPanel.add(valueComp);
+ // 鍒濆鍖栫姸鎬佹枃瀛�
+ String landNumber = dikuai.getLandNumber();
+ boolean isVisible = boundaryPointVisibility.getOrDefault(landNumber, false);
+ updateBoundaryStatusLabel(statusLabel, isVisible);
+
+ rightPanel.add(statusLabel);
rightPanel.add(toggleButton);
itemPanel.add(labelComp, BorderLayout.WEST);
itemPanel.add(rightPanel, BorderLayout.CENTER);
- itemPanel.putClientProperty("valueLabel", valueComp);
itemPanel.putClientProperty("titleLabel", labelComp);
return itemPanel;
@@ -499,6 +542,24 @@
button.setOpaque(true);
}
button.setToolTipText(active ? "闅愯棌杈圭晫鐐瑰簭鍙�" : "鏄剧ず杈圭晫鐐瑰簭鍙�");
+
+ // 鏇存柊鐘舵�佹彁绀烘枃瀛�
+ Object statusLabelObj = button.getClientProperty("statusLabel");
+ if (statusLabelObj instanceof JLabel) {
+ JLabel statusLabel = (JLabel) statusLabelObj;
+ updateBoundaryStatusLabel(statusLabel, active);
+ }
+ }
+
+ private void updateBoundaryStatusLabel(JLabel statusLabel, boolean active) {
+ if (statusLabel == null) {
+ return;
+ }
+ if (active) {
+ statusLabel.setText("宸插紑鍚竟鐣岀偣鏄剧ず");
+ } else {
+ statusLabel.setText("宸插叧闂竟鐣岀偣鏄剧ず");
+ }
}
private void ensureBoundaryToggleIconsLoaded() {
@@ -859,6 +920,31 @@
}
/**
+ * 鍚姩瀵艰埅棰勮
+ */
+ private void startNavigationPreview(Dikuai dikuai) {
+ if (dikuai == null) {
+ return;
+ }
+
+ Window owner = SwingUtilities.getWindowAncestor(this);
+
+ // 鑾峰彇鍦板潡绠$悊瀵硅瘽妗嗭紝鍑嗗鍦ㄦ墦寮�瀵艰埅棰勮鏃跺叧闂�
+ Window managementWindow = null;
+ if (owner instanceof JDialog) {
+ managementWindow = owner;
+ }
+
+ // 鍏抽棴鍦板潡绠$悊椤甸潰
+ if (managementWindow != null) {
+ managementWindow.dispose();
+ }
+
+ // 鍚姩瀵艰埅棰勮
+ daohangyulan.getInstance().startNavigationPreview(dikuai);
+ }
+
+ /**
* 鏄剧ず璺緞瑙勫垝椤甸潰
*/
private void showPathPlanningPage(Dikuai dikuai) {
@@ -940,6 +1026,7 @@
dialog.setVisible(true);
}
+
private void generateMowingPath(Dikuai dikuai) {
if (dikuai == null) {
return;
@@ -1183,6 +1270,21 @@
return section;
}
+ private String formatMowingPatternForDisplay(String patternValue) {
+ String sanitized = sanitizeValueOrNull(patternValue);
+ if (sanitized == null) {
+ return "鏈缃�";
+ }
+ String normalized = normalizeExistingMowingPattern(sanitized);
+ if ("parallel".equals(normalized)) {
+ return "骞宠妯″紡 (parallel)";
+ }
+ if ("spiral".equals(normalized)) {
+ return "铻烘棆妯″紡 (spiral)";
+ }
+ return sanitized;
+ }
+
private String formatMowingPatternForDialog(String patternValue) {
String sanitized = sanitizeValueOrNull(patternValue);
if (sanitized == null) {
diff --git a/src/dikuai/ObstacleManagementPage.java b/src/dikuai/ObstacleManagementPage.java
index 9dc7c5d..96c801a 100644
--- a/src/dikuai/ObstacleManagementPage.java
+++ b/src/dikuai/ObstacleManagementPage.java
@@ -299,7 +299,17 @@
JTextArea xyArea = createDataTextArea(genCoords, 3); // 寮曠敤浠ヤ究鏇存柊
JScrollPane scrollXY = new JScrollPane(xyArea);
scrollXY.setBorder(BorderFactory.createEmptyBorder()); // 澶栭儴鐢盤anel鎻愪緵杈规
-
+ // 璁剧疆婊氬姩鏉$瓥鐣ワ細闇�瑕佹椂鏄剧ず鍨傜洿婊氬姩鏉★紝涓嶄娇鐢ㄦ按骞虫粴鍔ㄦ潯锛堝洜涓哄惎鐢ㄤ簡鑷姩鎹㈣锛�
+ scrollXY.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ scrollXY.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ // 璁剧疆婊氬姩鏉″崟浣嶅閲忥紝浣挎粴鍔ㄦ洿娴佺晠
+ scrollXY.getVerticalScrollBar().setUnitIncrement(16);
+ // 璁剧疆婊氬姩闈㈡澘鐨勯閫夊ぇ灏忥紝纭繚鍦ㄥ唴瀹硅秴鍑烘椂鏄剧ず婊氬姩鏉�
+ int lineHeight = xyArea.getFontMetrics(xyArea.getFont()).getHeight();
+ int preferredHeight = 3 * lineHeight + 10; // 3琛岀殑楂樺害
+ scrollXY.setPreferredSize(new Dimension(Integer.MAX_VALUE, preferredHeight));
+ scrollXY.setMaximumSize(new Dimension(Integer.MAX_VALUE, 200)); // 鏈�澶ч珮搴�200鍍忕礌
+
JPanel xyWrapper = createWrapperPanel("鐢熸垚鍧愭爣 (XY绫�)", scrollXY);
card.add(xyWrapper);
card.add(Box.createVerticalStrut(15));
@@ -338,6 +348,16 @@
JTextArea area = createDataTextArea(content, rows);
JScrollPane scroll = new JScrollPane(area);
scroll.setBorder(BorderFactory.createEmptyBorder());
+ // 璁剧疆婊氬姩鏉$瓥鐣ワ細闇�瑕佹椂鏄剧ず鍨傜洿婊氬姩鏉★紝涓嶄娇鐢ㄦ按骞虫粴鍔ㄦ潯锛堝洜涓哄惎鐢ㄤ簡鑷姩鎹㈣锛�
+ scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ // 璁剧疆婊氬姩鏉″崟浣嶅閲忥紝浣挎粴鍔ㄦ洿娴佺晠
+ scroll.getVerticalScrollBar().setUnitIncrement(16);
+ // 璁剧疆婊氬姩闈㈡澘鐨勯閫夊ぇ灏忥紝纭繚鍦ㄥ唴瀹硅秴鍑烘椂鏄剧ず婊氬姩鏉�
+ int lineHeight = area.getFontMetrics(area.getFont()).getHeight();
+ int preferredHeight = rows * lineHeight + 10; // 鏍规嵁琛屾暟璁$畻棣栭�夐珮搴�
+ scroll.setPreferredSize(new Dimension(Integer.MAX_VALUE, preferredHeight));
+ scroll.setMaximumSize(new Dimension(Integer.MAX_VALUE, 200)); // 鏈�澶ч珮搴�200鍍忕礌
return createWrapperPanel(title, scroll);
}
@@ -366,12 +386,13 @@
JTextArea area = new JTextArea(text);
area.setRows(rows);
area.setEditable(false);
+ // 鍚敤鑷姩鎹㈣
area.setLineWrap(true);
area.setWrapStyleWord(true);
area.setBackground(BG_INPUT);
area.setForeground(new Color(50, 50, 50));
// 浣跨敤绛夊瀛椾綋鏄剧ず鏁版嵁锛岀湅璧锋潵鏇翠笓涓�
- area.setFont(new Font("Monospaced", Font.PLAIN, 12));
+ area.setFont(new Font("Monospaced", Font.PLAIN, 12));
return area;
}
@@ -545,6 +566,9 @@
return;
}
+ // 璁$畻闅滅鐗╃殑涓績鐐瑰潗鏍�
+ double[] centerCoords = calculateObstacleCenter(obstacle);
+
List<Obstacledge.Obstacle> allObstacles = loadObstacles();
String allObstaclesCoords = buildAllObstaclesCoordinates(allObstacles);
@@ -568,11 +592,85 @@
newPage.setVisible(true);
})
);
+
+ // 灏嗗湴鍥捐鍥句腑蹇冭缃负闅滅鐗╃殑涓績浣嶇疆
+ if (centerCoords != null && shouye.getMapRenderer() != null) {
+ double currentScale = shouye.getMapRenderer().getScale();
+ // 灏嗚鍥句腑蹇冭缃负闅滅鐗╀腑蹇冿紙浣跨敤璐熷�硷紝鍥犱负translate鏄浉瀵逛簬鍘熺偣鐨勫亸绉伙級
+ shouye.getMapRenderer().setViewTransform(currentScale, -centerCoords[0], -centerCoords[1]);
+ }
} else {
JOptionPane.showMessageDialog(null, "鏃犳硶鎵撳紑涓婚〉闈㈣繘琛岄瑙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
}
});
}
+
+ /**
+ * 璁$畻闅滅鐗╃殑涓績鐐瑰潗鏍�
+ * @param obstacle 闅滅鐗�
+ * @return 涓績鐐瑰潗鏍� [centerX, centerY]锛屽鏋滄棤娉曡绠楀垯杩斿洖null
+ */
+ private double[] calculateObstacleCenter(Obstacledge.Obstacle obstacle) {
+ if (obstacle == null) {
+ return null;
+ }
+
+ List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+ if (xyCoords == null || xyCoords.isEmpty()) {
+ return null;
+ }
+
+ Obstacledge.ObstacleShape shape = obstacle.getShape();
+ double centerX, centerY;
+
+ if (shape == Obstacledge.ObstacleShape.CIRCLE) {
+ // 鍦嗗舰闅滅鐗╋細绗竴涓潗鏍囩偣灏辨槸鍦嗗績
+ if (xyCoords.size() < 1) {
+ return null;
+ }
+ Obstacledge.XYCoordinate centerCoord = xyCoords.get(0);
+ centerX = centerCoord.getX();
+ centerY = centerCoord.getY();
+ } else if (shape == Obstacledge.ObstacleShape.POLYGON) {
+ // 澶氳竟褰㈤殰纰嶇墿锛氳绠楅噸蹇�
+ centerX = 0.0;
+ centerY = 0.0;
+ double area = 0.0;
+ int n = xyCoords.size();
+
+ for (int i = 0; i < n; i++) {
+ Obstacledge.XYCoordinate current = xyCoords.get(i);
+ Obstacledge.XYCoordinate next = xyCoords.get((i + 1) % n);
+ double x0 = current.getX();
+ double y0 = current.getY();
+ double x1 = next.getX();
+ double y1 = next.getY();
+ double cross = x0 * y1 - x1 * y0;
+ area += cross;
+ centerX += (x0 + x1) * cross;
+ centerY += (y0 + y1) * cross;
+ }
+
+ double areaFactor = area * 0.5;
+ if (Math.abs(areaFactor) < 1e-9) {
+ // 濡傛灉闈㈢Н涓�0鎴栨帴杩�0锛屼娇鐢ㄧ畝鍗曞钩鍧�
+ for (Obstacledge.XYCoordinate coord : xyCoords) {
+ centerX += coord.getX();
+ centerY += coord.getY();
+ }
+ int size = Math.max(1, xyCoords.size());
+ centerX /= size;
+ centerY /= size;
+ } else {
+ centerX = centerX / (6.0 * areaFactor);
+ centerY = centerY / (6.0 * areaFactor);
+ }
+ } else {
+ return null;
+ }
+
+ return new double[]{centerX, centerY};
+ }
private String buildAllObstaclesCoordinates(List<Obstacledge.Obstacle> obstacles) {
if (obstacles == null || obstacles.isEmpty()) return null;
@@ -897,3 +995,8 @@
return value != null && !value.trim().isEmpty() && !"-1".equals(value.trim());
}
}
+
+
+
+
+
diff --git a/src/dikuai/addzhangaiwu.java b/src/dikuai/addzhangaiwu.java
index 2952d93..6c69177 100644
--- a/src/dikuai/addzhangaiwu.java
+++ b/src/dikuai/addzhangaiwu.java
@@ -797,7 +797,7 @@
return;
}
if (userTriggered && "handheld".equalsIgnoreCase(type) && !hasConfiguredHandheldMarker()) {
- JOptionPane.showMessageDialog(this, "璇峰厛娣诲姞渚挎惡鎵撶偣鍣ㄧ紪鍙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ JOptionPane.showMessageDialog(this, "璇峰厛鍘荤郴缁熻缃坊鍔犱究鎼烘墦鐐瑰櫒缂栧彿", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
return;
}
if (selectedMethodPanel != null && selectedMethodPanel != option) {
diff --git a/src/dikuai/daohangyulan.java b/src/dikuai/daohangyulan.java
new file mode 100644
index 0000000..a65ffcf
--- /dev/null
+++ b/src/dikuai/daohangyulan.java
@@ -0,0 +1,548 @@
+package dikuai;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.geom.Point2D;
+import java.util.List;
+import java.util.ArrayList;
+import zhuye.Shouye;
+import zhuye.MapRenderer;
+import zhuye.buttonset;
+import gecaoji.Gecaoji;
+import gecaoji.lujingdraw;
+
+/**
+ * 瀵艰埅棰勮鍔熻兘绫�
+ * 瀹炵幇鍓茶崏鏈烘部璺緞妯℃嫙瀵艰埅
+ */
+public class daohangyulan {
+ private static daohangyulan instance;
+ private Shouye shouye;
+ private MapRenderer mapRenderer;
+ private Gecaoji mower;
+ private Dikuai currentDikuai;
+
+ // 璺緞鐩稿叧
+ private List<Point2D.Double> pathPoints;
+ private int currentPathIndex;
+ private double currentSpeed; // 绫�/绉�
+ private static final double DEFAULT_SPEED = 1.0; // 榛樿1绫�/绉�
+ private static final double SPEED_MULTIPLIER = 1.0; // 姣忔鍔犻��/鍑忛�熺殑鍊嶆暟
+
+ // 瀹氭椂鍣�
+ private Timer navigationTimer;
+ private static final int TIMER_INTERVAL_MS = 50; // 50姣鏇存柊涓�娆�
+
+ // 瀵艰埅棰勮鎸夐挳
+ private JButton speedUpBtn;
+ private JButton speedDownBtn;
+ private JButton exitBtn;
+
+ // 淇濆瓨鍘熷鎸夐挳鍜岄潰鏉跨殑寮曠敤
+ private JButton originalStartBtn;
+ private JButton originalStopBtn;
+ private JPanel originalButtonPanel;
+ private LayoutManager originalButtonPanelLayout;
+
+ // 瀵艰埅棰勮杞ㄨ抗
+ private List<Point2D.Double> navigationTrack;
+
+ // 鏄惁姝e湪瀵艰埅棰勮
+ private boolean isNavigating;
+
+ private daohangyulan() {
+ this.currentSpeed = DEFAULT_SPEED;
+ this.navigationTrack = new ArrayList<>();
+ this.isNavigating = false;
+ }
+
+ public static daohangyulan getInstance() {
+ if (instance == null) {
+ instance = new daohangyulan();
+ }
+ return instance;
+ }
+
+ /**
+ * 鍚姩瀵艰埅棰勮
+ * @param dikuai 鍦板潡瀵硅薄
+ */
+ public void startNavigationPreview(Dikuai dikuai) {
+ if (dikuai == null) {
+ JOptionPane.showMessageDialog(null, "鍦板潡鏁版嵁鏃犳晥", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // 鑾峰彇鍦板潡鐨勫壊鑽夎矾寰勫潗鏍�
+ String plannedPath = dikuai.getPlannedPath();
+ if (plannedPath == null || plannedPath.trim().isEmpty() || "-1".equals(plannedPath.trim())) {
+ JOptionPane.showMessageDialog(null, "褰撳墠鍦板潡娌℃湁瑙勫垝璺緞锛屾棤娉曡繘琛屽鑸瑙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ // 瑙f瀽璺緞鍧愭爣
+ pathPoints = lujingdraw.parsePlannedPath(plannedPath);
+ if (pathPoints == null || pathPoints.size() < 2) {
+ JOptionPane.showMessageDialog(null, "璺緞鍧愭爣瑙f瀽澶辫触锛屾棤娉曡繘琛屽鑸瑙�", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ this.currentDikuai = dikuai;
+ this.currentPathIndex = 0;
+ this.currentSpeed = DEFAULT_SPEED;
+ this.navigationTrack.clear();
+
+ // 鑾峰彇棣栭〉鍜屽湴鍥炬覆鏌撳櫒
+ shouye = Shouye.getInstance();
+ if (shouye == null) {
+ JOptionPane.showMessageDialog(null, "鏃犳硶鑾峰彇棣栭〉瀹炰緥", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ mapRenderer = shouye.getMapRenderer();
+ if (mapRenderer == null) {
+ JOptionPane.showMessageDialog(null, "鏃犳硶鑾峰彇鍦板浘娓叉煋鍣�", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ mower = mapRenderer.getMower();
+ if (mower == null) {
+ JOptionPane.showMessageDialog(null, "鏃犳硶鑾峰彇鍓茶崏鏈哄疄渚�", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // 璁剧疆鍓茶崏鏈哄垵濮嬩綅缃负璺緞璧风偣
+ Point2D.Double startPoint = pathPoints.get(0);
+ mower.setPosition(startPoint.x, startPoint.y);
+
+ // 璁$畻鍒濆鏂瑰悜锛堟湞鍚戠涓�涓洰鏍囩偣锛�
+ if (pathPoints.size() > 1) {
+ Point2D.Double nextPoint = pathPoints.get(1);
+ double heading = calculateHeading(startPoint, nextPoint);
+ setMowerHeading(heading);
+ } else {
+ // 濡傛灉鍙湁涓�涓偣锛岃缃粯璁ゆ柟鍚戯紙0搴︼紝鍚戝彸锛�
+ setMowerHeading(0.0);
+ }
+
+ // 鍒濆鍖栧鑸瑙堣建杩�
+ navigationTrack.clear();
+ navigationTrack.add(new Point2D.Double(startPoint.x, startPoint.y));
+ mapRenderer.clearNavigationPreviewTrack();
+ mapRenderer.addNavigationPreviewTrackPoint(startPoint);
+
+ // 鑾峰彇鍓茶崏鏈哄壊鍒�瀹藉害
+ String bladeWidthStr = dikuai.getMowingBladeWidth();
+ double bladeWidthMeters = 0.5; // 榛樿0.5绫�
+ if (bladeWidthStr != null && !bladeWidthStr.trim().isEmpty() && !"-1".equals(bladeWidthStr.trim())) {
+ try {
+ bladeWidthMeters = Double.parseDouble(bladeWidthStr.trim());
+ } catch (NumberFormatException e) {
+ // 浣跨敤榛樿鍊�
+ }
+ }
+ mapRenderer.setNavigationPreviewWidth(bladeWidthMeters);
+
+ // 璁剧疆鍒濆閫熷害鏄剧ず
+ mapRenderer.setNavigationPreviewSpeed(currentSpeed);
+
+ // 鍒涘缓骞舵樉绀哄鑸瑙堟寜閽紙鏇挎崲搴曢儴鎸夐挳锛�
+ createNavigationButtons();
+
+ // 鏄剧ず瀵艰埅棰勮妯″紡鏍囩
+ if (shouye != null) {
+ shouye.setNavigationPreviewLabelVisible(true);
+ }
+
+ // 鍚姩瀵艰埅瀹氭椂鍣�
+ startNavigationTimer();
+
+ isNavigating = true;
+
+ // 鍒锋柊鍦板浘鏄剧ず
+ mapRenderer.repaint();
+ }
+
+ /**
+ * 鍒涘缓瀵艰埅棰勮鎸夐挳锛堟浛鎹㈠簳閮ㄧ殑鏆傚仠銆佺粨鏉熸寜閽級
+ */
+ private void createNavigationButtons() {
+ // 绉婚櫎鏃х殑鎸夐挳锛堝鏋滃瓨鍦級
+ removeNavigationButtons();
+
+ if (shouye == null) {
+ return;
+ }
+
+ // 鑾峰彇鍘熷鎸夐挳
+ originalStartBtn = shouye.getStartButton();
+ originalStopBtn = shouye.getStopButton();
+
+ if (originalStartBtn == null || originalStopBtn == null) {
+ return;
+ }
+
+ // 鑾峰彇鎺у埗闈㈡澘
+ JPanel controlPanel = shouye.getControlPanel();
+ if (controlPanel == null) {
+ return;
+ }
+
+ // 鏌ユ壘鎸夐挳闈㈡澘锛堝寘鍚� startBtn 鍜� stopBtn 鐨勯潰鏉匡級
+ JPanel buttonPanel = null;
+ for (Component comp : controlPanel.getComponents()) {
+ if (comp instanceof JPanel) {
+ JPanel panel = (JPanel) comp;
+ // 妫�鏌ユ槸鍚︽槸鎸夐挳闈㈡澘锛堝寘鍚� startBtn 鍜� stopBtn锛�
+ boolean hasStartBtn = false;
+ boolean hasStopBtn = false;
+ for (Component child : panel.getComponents()) {
+ if (child == originalStartBtn) {
+ hasStartBtn = true;
+ }
+ if (child == originalStopBtn) {
+ hasStopBtn = true;
+ }
+ }
+ if (hasStartBtn && hasStopBtn) {
+ buttonPanel = panel;
+ break;
+ }
+ }
+ }
+
+ if (buttonPanel == null) {
+ return;
+ }
+
+ // 淇濆瓨鍘熷鎸夐挳闈㈡澘鍜屽竷灞�
+ originalButtonPanel = buttonPanel;
+ originalButtonPanelLayout = buttonPanel.getLayout();
+
+ // 闅愯棌鍘熷鎸夐挳
+ if (originalStartBtn != null) {
+ originalStartBtn.setVisible(false);
+ }
+ if (originalStopBtn != null) {
+ originalStopBtn.setVisible(false);
+ }
+
+ // 淇敼鎸夐挳闈㈡澘甯冨眬涓�3鍒楋紙鍔犻�熴�佸噺閫熴�侀��鍑猴級
+ // 鍑忓皯鎸夐挳闂磋窛锛岀粰鎸夐挳鏇村绌洪棿鏄剧ず鏂囧瓧
+ buttonPanel.setLayout(new GridLayout(1, 3, 10, 0));
+
+ // 鍒涘缓鍔犻�熸寜閽�
+ speedUpBtn = createControlButton("鍔犻��", new Color(46, 139, 87));
+ speedUpBtn.addActionListener(e -> speedUp());
+
+ // 鍒涘缓鍑忛�熸寜閽�
+ speedDownBtn = createControlButton("鍑忛��", new Color(255, 140, 0));
+ speedDownBtn.addActionListener(e -> speedDown());
+
+ // 鍒涘缓閫�鍑烘寜閽�
+ exitBtn = createControlButton("閫�鍑�", new Color(255, 107, 107));
+ exitBtn.addActionListener(e -> exitNavigationPreview());
+
+ // 娣诲姞鏂版寜閽埌鎸夐挳闈㈡澘
+ buttonPanel.add(speedUpBtn);
+ buttonPanel.add(speedDownBtn);
+ buttonPanel.add(exitBtn);
+
+ // 鍒锋柊鏄剧ず
+ buttonPanel.revalidate();
+ buttonPanel.repaint();
+ controlPanel.revalidate();
+ controlPanel.repaint();
+ }
+
+ /**
+ * 鍒涘缓鎺у埗鎸夐挳锛堜娇鐢╞uttonset椋庢牸锛屽昂瀵�40x80锛�
+ */
+ private JButton createControlButton(String text, Color color) {
+ // 浣跨敤buttonset鍒涘缓鎸夐挳
+ JButton button = buttonset.createStyledButton(text, color);
+
+ // 璁剧疆鎸夐挳灏哄锛氬搴�40鍍忕礌锛岄珮搴�80鍍忕礌
+ Dimension buttonSize = new Dimension(80, 40);
+ button.setPreferredSize(buttonSize);
+ button.setMinimumSize(buttonSize);
+ button.setMaximumSize(buttonSize);
+
+ return button;
+ }
+
+ /**
+ * 绉婚櫎瀵艰埅棰勮鎸夐挳锛堟仮澶嶅師濮嬫寜閽級
+ */
+ private void removeNavigationButtons() {
+ if (originalButtonPanel == null) {
+ return;
+ }
+
+ // 绉婚櫎瀵艰埅棰勮鎸夐挳
+ if (speedUpBtn != null && speedUpBtn.getParent() == originalButtonPanel) {
+ originalButtonPanel.remove(speedUpBtn);
+ }
+ if (speedDownBtn != null && speedDownBtn.getParent() == originalButtonPanel) {
+ originalButtonPanel.remove(speedDownBtn);
+ }
+ if (exitBtn != null && exitBtn.getParent() == originalButtonPanel) {
+ originalButtonPanel.remove(exitBtn);
+ }
+
+ // 鎭㈠鍘熷甯冨眬
+ if (originalButtonPanelLayout != null) {
+ originalButtonPanel.setLayout(originalButtonPanelLayout);
+ }
+
+ // 鎭㈠鍘熷鎸夐挳鏄剧ず
+ if (originalStartBtn != null) {
+ originalStartBtn.setVisible(true);
+ if (originalStartBtn.getParent() != originalButtonPanel) {
+ originalButtonPanel.add(originalStartBtn);
+ }
+ }
+ if (originalStopBtn != null) {
+ originalStopBtn.setVisible(true);
+ if (originalStopBtn.getParent() != originalButtonPanel) {
+ originalButtonPanel.add(originalStopBtn);
+ }
+ }
+
+ // 鍒锋柊鏄剧ず
+ originalButtonPanel.revalidate();
+ originalButtonPanel.repaint();
+
+ if (shouye != null && shouye.getControlPanel() != null) {
+ shouye.getControlPanel().revalidate();
+ shouye.getControlPanel().repaint();
+ }
+
+ // 娓呯┖寮曠敤
+ speedUpBtn = null;
+ speedDownBtn = null;
+ exitBtn = null;
+ originalButtonPanel = null;
+ originalButtonPanelLayout = null;
+ originalStartBtn = null;
+ originalStopBtn = null;
+ }
+
+ /**
+ * 鍚姩瀵艰埅瀹氭椂鍣�
+ */
+ private void startNavigationTimer() {
+ if (navigationTimer != null) {
+ navigationTimer.stop();
+ }
+
+ navigationTimer = new Timer(TIMER_INTERVAL_MS, new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ updateNavigation();
+ }
+ });
+ navigationTimer.start();
+ }
+
+ /**
+ * 鏇存柊瀵艰埅鐘舵��
+ */
+ private void updateNavigation() {
+ if (pathPoints == null || pathPoints.size() < 2 || currentPathIndex >= pathPoints.size() - 1) {
+ // 璺緞瀹屾垚
+ stopNavigation();
+ JOptionPane.showMessageDialog(null, "瀵艰埅棰勮瀹屾垚", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+ return;
+ }
+
+ Point2D.Double currentPos = mower.getPosition();
+ if (currentPos == null) {
+ return;
+ }
+
+ Point2D.Double targetPoint = pathPoints.get(currentPathIndex + 1);
+
+ // 璁$畻鍒扮洰鏍囩偣鐨勮窛绂�
+ double dx = targetPoint.x - currentPos.x;
+ double dy = targetPoint.y - currentPos.y;
+ double distance = Math.hypot(dx, dy);
+
+ // 璁$畻姣忓抚绉诲姩鐨勮窛绂伙紙绫筹級
+ double moveDistance = currentSpeed * (TIMER_INTERVAL_MS / 1000.0);
+
+ if (distance <= moveDistance) {
+ // 鍒拌揪鐩爣鐐癸紝绉诲姩鍒颁笅涓�涓偣
+ mower.setPosition(targetPoint.x, targetPoint.y);
+ navigationTrack.add(new Point2D.Double(targetPoint.x, targetPoint.y));
+ mapRenderer.addNavigationPreviewTrackPoint(targetPoint);
+
+ currentPathIndex++;
+
+ // 濡傛灉杩樻湁涓嬩竴涓偣锛岃绠楁柟鍚�
+ if (currentPathIndex < pathPoints.size() - 1) {
+ Point2D.Double nextPoint = pathPoints.get(currentPathIndex + 1);
+ double heading = calculateHeading(targetPoint, nextPoint);
+ setMowerHeading(heading);
+ }
+ } else {
+ // 鍚戠洰鏍囩偣绉诲姩
+ // 鍏堟洿鏂版柟鍚戯紝纭繚杞﹀ご鏈濆悜鐩爣鐐�
+ double heading = calculateHeading(currentPos, targetPoint);
+ setMowerHeading(heading);
+
+ double ratio = moveDistance / distance;
+ double newX = currentPos.x + dx * ratio;
+ double newY = currentPos.y + dy * ratio;
+
+ mower.setPosition(newX, newY);
+ navigationTrack.add(new Point2D.Double(newX, newY));
+ mapRenderer.addNavigationPreviewTrackPoint(new Point2D.Double(newX, newY));
+ }
+
+ // 鏇存柊閫熷害鏄剧ず鍒板湴鍥炬覆鏌撳櫒
+ if (mapRenderer != null) {
+ mapRenderer.setNavigationPreviewSpeed(currentSpeed);
+ }
+
+ // 鏇存柊鍦板浘鏄剧ず
+ mapRenderer.repaint();
+
+ // 鏇存柊閫熷害鏄剧ず锛堝鏋滈渶瑕侊級
+ updateSpeedDisplay();
+ }
+
+ /**
+ * 璁$畻涓ょ偣涔嬮棿鐨勬柟鍚戣锛堝害锛�
+ * 鍥炬爣榛樿鏈濅笂锛屽悜鍙虫棆杞�90搴﹁溅澶存湞鍙�
+ * atan2杩斿洖鐨勮搴︼細鍚戝彸鏄�0搴︼紝鍚戜笂鏄�90搴�
+ * 闇�瑕佽浆鎹负鍥炬爣鏃嬭浆瑙掑害锛氬悜鍙抽渶瑕�90搴︼紝鍚戜笂闇�瑕�0搴�
+ */
+ private double calculateHeading(Point2D.Double from, Point2D.Double to) {
+ double dx = to.x - from.x;
+ double dy = to.y - from.y;
+ // atan2杩斿洖鐨勮搴︼細鍚戝彸鏄�0搴︼紝鍚戜笂鏄�90搴︼紝鍚戝乏鏄�180搴︼紝鍚戜笅鏄�-90搴︼紙270搴︼級
+ double atan2Angle = Math.toDegrees(Math.atan2(dy, dx));
+
+ // 杞崲涓�0-360搴﹁寖鍥�
+ if (atan2Angle < 0) {
+ atan2Angle += 360;
+ }
+
+ // 鍥炬爣榛樿鏈濅笂锛�0搴︼級锛屽悜鍙虫棆杞�90搴﹁溅澶存湞鍙�
+ // 鎵�浠ワ細杩愬姩鏂瑰悜鍚戝彸锛�0搴︼級鈫� 闇�瑕佹棆杞�90搴�
+ // 杩愬姩鏂瑰悜鍚戜笂锛�90搴︼級鈫� 闇�瑕佹棆杞�0搴�
+ // 杩愬姩鏂瑰悜鍚戝乏锛�180搴︼級鈫� 闇�瑕佹棆杞�270搴�
+ // 杩愬姩鏂瑰悜鍚戜笅锛�270搴︼級鈫� 闇�瑕佹棆杞�180搴�
+ // 鍏紡锛歨eading = (90 - atan2Angle + 360) % 360
+ double heading = (90.0 - atan2Angle + 360.0) % 360.0;
+
+ return heading;
+ }
+
+ /**
+ * 璁剧疆鍓茶崏鏈烘柟鍚�
+ */
+ private void setMowerHeading(double headingDegrees) {
+ if (mower != null) {
+ mower.setHeading(headingDegrees);
+ }
+ }
+
+ /**
+ * 鍔犻��
+ */
+ private void speedUp() {
+ currentSpeed += SPEED_MULTIPLIER;
+ updateSpeedDisplay();
+ }
+
+ /**
+ * 鍑忛��
+ */
+ private void speedDown() {
+ if (currentSpeed > 0.1) { // 鏈�灏忛�熷害0.1绫�/绉�
+ currentSpeed -= SPEED_MULTIPLIER;
+ if (currentSpeed < 0.1) {
+ currentSpeed = 0.1;
+ }
+ }
+ updateSpeedDisplay();
+ }
+
+ /**
+ * 鏇存柊閫熷害鏄剧ず
+ */
+ private void updateSpeedDisplay() {
+ // 鍙互鍦ㄥ湴鍥句笂鏄剧ず褰撳墠閫熷害
+ // 杩欓噷鏆傛椂涓嶅疄鐜帮紝濡傛灉闇�瑕佸彲浠ュ湪MapRenderer涓坊鍔犻�熷害鏄剧ず
+ }
+
+ /**
+ * 鍋滄瀵艰埅
+ */
+ private void stopNavigation() {
+ if (navigationTimer != null) {
+ navigationTimer.stop();
+ navigationTimer = null;
+ }
+ isNavigating = false;
+ }
+
+ /**
+ * 閫�鍑哄鑸瑙�
+ */
+ public void exitNavigationPreview() {
+ stopNavigation();
+ removeNavigationButtons();
+
+ // 闅愯棌瀵艰埅棰勮妯″紡鏍囩
+ if (shouye != null) {
+ shouye.setNavigationPreviewLabelVisible(false);
+ }
+
+ // 娓呴櫎瀵艰埅棰勮杞ㄨ抗
+ if (mapRenderer != null) {
+ mapRenderer.clearNavigationPreviewTrack();
+ mapRenderer.setNavigationPreviewSpeed(0.0); // 娓呴櫎閫熷害鏄剧ず
+ mapRenderer.repaint();
+ }
+
+ // 鎭㈠鍦板潡绠$悊椤甸潰
+ // 鍦ㄦ竻绌篶urrentDikuai涔嬪墠淇濆瓨鍦板潡缂栧彿锛屼娇鐢╢inal鍙橀噺浠ヤ究鍦╨ambda涓娇鐢�
+ final String landNumber = (currentDikuai != null) ? currentDikuai.getLandNumber() : null;
+
+ isNavigating = false;
+ currentDikuai = null;
+
+ // 濡傛灉鏈夊湴鍧楃紪鍙凤紝鏄剧ず鍦板潡绠$悊椤甸潰
+ if (landNumber != null && !landNumber.trim().isEmpty()) {
+ SwingUtilities.invokeLater(() -> {
+ try {
+ Component parent = null;
+ if (shouye != null) {
+ Window owner = SwingUtilities.getWindowAncestor(shouye);
+ if (owner instanceof Component) {
+ parent = (Component) owner;
+ } else {
+ parent = shouye;
+ }
+ }
+ Dikuaiguanli.showDikuaiManagement(parent, landNumber);
+ } catch (Exception e) {
+ System.err.println("鏄剧ず鍦板潡绠$悊椤甸潰澶辫触: " + e.getMessage());
+ e.printStackTrace();
+ }
+ });
+ }
+ }
+
+ /**
+ * 妫�鏌ユ槸鍚︽鍦ㄥ鑸瑙�
+ */
+ public boolean isNavigating() {
+ return isNavigating;
+ }
+}
diff --git a/src/gecaoji/Gecaoji.java b/src/gecaoji/Gecaoji.java
index 2aca713..e89222a 100644
--- a/src/gecaoji/Gecaoji.java
+++ b/src/gecaoji/Gecaoji.java
@@ -179,6 +179,38 @@
return new Point2D.Double(position.x, position.y);
}
+ /**
+ * 璁剧疆鍓茶崏鏈轰綅缃紙鐢ㄤ簬瀵艰埅棰勮绛夊満鏅級
+ * @param x X鍧愭爣
+ * @param y Y鍧愭爣
+ */
+ public void setPosition(double x, double y) {
+ ensurePosition();
+ position.x = x;
+ position.y = y;
+ positionValid = true;
+ }
+
+ /**
+ * 璁剧疆鍓茶崏鏈烘柟鍚戯紙鐢ㄤ簬瀵艰埅棰勮绛夊満鏅級
+ * @param headingDegrees 鏂瑰悜瑙掑害锛堝害锛�0-360锛�
+ */
+ public void setHeading(double headingDegrees) {
+ double normalized = headingDegrees % 360.0;
+ if (normalized < 0) {
+ normalized += 360.0;
+ }
+ this.headingDegrees = normalized;
+ }
+
+ /**
+ * 鑾峰彇鍓茶崏鏈烘柟鍚�
+ * @return 鏂瑰悜瑙掑害锛堝害锛�0-360锛�
+ */
+ public double getHeading() {
+ return headingDegrees;
+ }
+
public double getWorldRadius(double scale) {
if (!positionValid) {
return Double.NaN;
diff --git a/src/gecaoji/gecaolunjing.java b/src/gecaoji/gecaolunjing.java
index 4a0a99c..c28156f 100644
--- a/src/gecaoji/gecaolunjing.java
+++ b/src/gecaoji/gecaolunjing.java
@@ -24,9 +24,10 @@
* 宸ュ叿绫伙細璐熻矗缁樺埗鍓茶崏瀹屾垚璺緞瑕嗙洊鏁堟灉锛屽苟鎻愪緵涓�绯诲垪澧炲己璋冧紭鑳藉姏銆�
*/
public final class gecaolunjing {
- private static final Color COVERAGE_FILL_COLOR = new Color(34, 139, 34, 120);
- private static final Color COVERAGE_BORDER_COLOR = new Color(24, 98, 52, 200);
- private static final Color COVERAGE_PATH_COLOR = new Color(0, 90, 0, 204);
+ // 娣辩豢鑹诧細鐢ㄤ簬鏄剧ず鍓插畬鐨勫尯鍩�
+ private static final Color COVERAGE_FILL_COLOR = new Color(0, 100, 0, 150); // 娣辩豢鑹诧紝鍗婇�忔槑
+ private static final Color COVERAGE_BORDER_COLOR = new Color(0, 80, 0, 200); // 鏇存繁鐨勭豢鑹茶竟妗�
+ private static final Color COVERAGE_PATH_COLOR = new Color(0, 90, 0, 204); // 璺緞棰滆壊
private static final double MIN_WIDTH_METERS = 0.3d;
private static final CoverageState STATE = new CoverageState();
diff --git a/src/lujing/Lunjingguihua.java b/src/lujing/Lunjingguihua.java
index 3e54d7b..1f19fae 100644
--- a/src/lujing/Lunjingguihua.java
+++ b/src/lujing/Lunjingguihua.java
@@ -1,25 +1,20 @@
package lujing;
-import java.awt.geom.Line2D;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
-import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.MultiLineString;
-import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Polygon;
-import org.locationtech.jts.operation.union.CascadedPolygonUnion;
+import org.locationtech.jts.geom.MultiPolygon;
/**
- * 鍓茶崏璺緞瑙勫垝瀹炵敤绫伙紝渚涘叾浠栭」鐩洿鎺ヨ皟鐢ㄣ��
- * 鎻愪緵瀛楃涓插叆鍙傜殑鍓茶崏璺緞鐢熸垚鑳藉姏锛屽苟灏佽蹇呰鐨勮В鏋愪笌鍑犱綍澶勭悊閫昏緫銆�
+ * 浼樺寲鍚庣殑鍓茶崏璺緞瑙勫垝绫�
+ * 淇锛氳В鍐宠矾寰勮秴鍑哄湴鍧楄竟鐣岀殑闂锛屽鍔犲畨鍏ㄨ竟璺濊绠楃殑鍋ュ.鎬с��
*/
public final class Lunjingguihua {
@@ -27,16 +22,7 @@
throw new IllegalStateException("Utility class");
}
- /**
- * 鐢熸垚鍓茶崏璺緞娈靛垪琛ㄣ��
- *
- * @param polygonCoords 澶氳竟褰㈣竟鐣屽潗鏍囷紝鏍煎紡濡� "x1,y1;x2,y2;..."锛堢背锛�
- * @param obstaclesCoords 闅滅鐗╁潗鏍囷紝鏀寔澶氫釜鎷彿娈垫垨鍦嗗舰瀹氫箟锛屼緥 "(x1,y1;x2,y2)(cx,cy;px,py)"
- * @param mowingWidth 鍓茶崏瀹藉害瀛楃涓诧紝绫冲崟浣嶏紝鍏佽淇濈暀涓や綅灏忔暟
- * @param safetyDistStr 瀹夊叏璺濈瀛楃涓诧紝绫冲崟浣嶃�傝矾寰勫皢涓庤竟鐣屽拰闅滅鐗╀繚鎸佹璺濈銆�
- * @param modeStr 鍓茶崏妯″紡锛�"0"/绌轰负骞宠绾匡紝"1" 鎴� "spiral" 琛ㄧず铻烘棆妯″紡锛堝綋鍓嶄粎骞宠绾垮疄鐜帮級
- * @return 璺緞娈靛垪琛紝鎸夎椹堕『搴忔帓鍒�
- */
+ // 5鍙傛暟鏍稿績鐢熸垚鏂规硶
public static List<PathSegment> generatePathSegments(String polygonCoords,
String obstaclesCoords,
String mowingWidth,
@@ -44,15 +30,11 @@
String modeStr) {
List<Coordinate> polygon = parseCoordinates(polygonCoords);
if (polygon.size() < 4) {
- throw new IllegalArgumentException("澶氳竟褰㈠潗鏍囨暟閲忎笉瓒筹紝鑷冲皯闇�瑕佷笁涓偣");
+ throw new IllegalArgumentException("澶氳竟褰㈠潗鏍囨暟閲忎笉瓒�");
}
- double width = parseDoubleOrDefault(mowingWidth, 2.0);
- if (width <= 0) {
- throw new IllegalArgumentException("鍓茶崏瀹藉害蹇呴』澶т簬 0");
- }
-
- // 瑙f瀽瀹夊叏璺濈锛屽鏋滄湭璁剧疆鎴栨棤鏁堬紝榛樿涓� NaN (鍦� PlannerCore 涓鐞嗛粯璁ゅ��)
+ double width = parseDoubleOrDefault(mowingWidth, 0.34);
+ // 濡傛灉浼犲叆绌猴紝璁句负 NaN锛屽湪 PlannerCore 涓繘琛屾櫤鑳借绠�
double safetyDistance = parseDoubleOrDefault(safetyDistStr, Double.NaN);
List<List<Coordinate>> obstacles = parseObstacles(obstaclesCoords);
@@ -62,9 +44,7 @@
return planner.generate();
}
- /**
- * 淇濇寔鍚戝悗鍏煎鐨勯噸杞芥柟娉曪紙涓嶅甫 safeDistance锛屼娇鐢ㄩ粯璁よ绠楋級
- */
+ // 4鍙傛暟閲嶈浇锛岄�傞厤 AddDikuai.java
public static List<PathSegment> generatePathSegments(String polygonCoords,
String obstaclesCoords,
String mowingWidth,
@@ -72,16 +52,7 @@
return generatePathSegments(polygonCoords, obstaclesCoords, mowingWidth, null, modeStr);
}
- /**
- * 閫氳繃瀛楃涓插弬鏁扮敓鎴愬壊鑽夎矾寰勫潗鏍囥��
- *
- * @param polygonCoords 澶氳竟褰㈣竟鐣屽潗鏍囷紝鏍煎紡濡� "x1,y1;x2,y2;..."锛堢背锛�
- * @param obstaclesCoords 闅滅鐗╁潗鏍囷紝鏀寔澶氫釜鎷彿娈垫垨鍦嗗舰瀹氫箟锛屼緥 "(x1,y1;x2,y2)(cx,cy;px,py)"
- * @param mowingWidth 鍓茶崏瀹藉害瀛楃涓诧紝绫冲崟浣嶏紝鍏佽淇濈暀涓や綅灏忔暟
- * @param safetyDistStr 瀹夊叏璺濈瀛楃涓诧紝绫冲崟浣嶃��
- * @param modeStr 鍓茶崏妯″紡锛�"0"/绌轰负骞宠绾匡紝"1" 鎴� "spiral" 琛ㄧず铻烘棆妯″紡锛堝綋鍓嶄粎骞宠绾垮疄鐜帮級
- * @return 杩炵画璺緞鍧愭爣瀛楃涓诧紝椤哄簭绱ц窡鍓茶崏鏈鸿杩涜矾绾�
- */
+ // 5鍙傛暟璺緞瀛楃涓茬敓鎴�
public static String generatePathFromStrings(String polygonCoords,
String obstaclesCoords,
String mowingWidth,
@@ -90,10 +61,8 @@
List<PathSegment> segments = generatePathSegments(polygonCoords, obstaclesCoords, mowingWidth, safetyDistStr, modeStr);
return formatPathSegments(segments);
}
-
- /**
- * 淇濇寔鍚戝悗鍏煎鐨勯噸杞芥柟娉�
- */
+
+ // 4鍙傛暟璺緞瀛楃涓茬敓鎴愰噸杞�
public static String generatePathFromStrings(String polygonCoords,
String obstaclesCoords,
String mowingWidth,
@@ -101,158 +70,93 @@
return generatePathFromStrings(polygonCoords, obstaclesCoords, mowingWidth, null, modeStr);
}
- /**
- * 灏嗚矾寰勬鍒楄〃杞崲涓哄潗鏍囧瓧绗︿覆銆�
- */
public static String formatPathSegments(List<PathSegment> path) {
- if (path == null || path.isEmpty()) {
- return "";
- }
+ if (path == null || path.isEmpty()) return "";
StringBuilder sb = new StringBuilder();
Coordinate last = null;
for (PathSegment segment : path) {
- if (!equals2D(last, segment.start)) {
+ if (last == null || !equals2D(last, segment.start)) {
appendPoint(sb, segment.start);
- last = new Coordinate(segment.start);
}
- if (!equals2D(last, segment.end)) {
- appendPoint(sb, segment.end);
- last = new Coordinate(segment.end);
- }
+ appendPoint(sb, segment.end);
+ last = segment.end;
}
return sb.toString();
}
- /**
- * 瑙f瀽鍧愭爣瀛楃涓层��
- */
public static List<Coordinate> parseCoordinates(String s) {
List<Coordinate> list = new ArrayList<>();
- if (s == null || s.trim().isEmpty()) {
- return list;
- }
+ if (s == null || s.trim().isEmpty()) return list;
+ // 澧炲己姝e垯锛氬鐞嗗彲鑳藉瓨鍦ㄧ殑澶氱鍒嗛殧绗�
String[] pts = s.split("[;\\s]+");
for (String p : pts) {
String trimmed = p.trim().replace("(", "").replace(")", "");
- if (trimmed.isEmpty()) {
- continue;
- }
+ if (trimmed.isEmpty()) continue;
String[] xy = trimmed.split("[,锛孿\s]+");
if (xy.length >= 2) {
try {
- list.add(new Coordinate(Double.parseDouble(xy[0].trim()),
- Double.parseDouble(xy[1].trim())));
+ double x = Double.parseDouble(xy[0].trim());
+ double y = Double.parseDouble(xy[1].trim());
+ // 杩囨护鏃犳晥鍧愭爣
+ if (!Double.isNaN(x) && !Double.isNaN(y) && !Double.isInfinite(x) && !Double.isInfinite(y)) {
+ list.add(new Coordinate(x, y));
+ }
} catch (NumberFormatException ex) {
- System.err.println("鍧愭爣瑙f瀽澶辫触: " + trimmed);
+ // 蹇界暐瑙f瀽閿欒鐨勭偣
}
}
}
+ // 纭繚澶氳竟褰㈤棴鍚�
if (list.size() > 2 && !list.get(0).equals2D(list.get(list.size() - 1))) {
list.add(new Coordinate(list.get(0)));
}
return list;
}
- /**
- * 瑙f瀽闅滅鐗╁瓧绗︿覆锛屽吋瀹瑰闅滅鐗╀笌鍦嗗舰瀹氫箟銆�
- */
public static List<List<Coordinate>> parseObstacles(String str) {
List<List<Coordinate>> obs = new ArrayList<>();
- if (str == null || str.trim().isEmpty()) {
- return obs;
- }
+ if (str == null || str.trim().isEmpty()) return obs;
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\(([^)]+)\\)");
java.util.regex.Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
List<Coordinate> coords = parseCoordinates(matcher.group(1));
- if (coords.size() >= 3) {
- obs.add(coords);
- } else if (coords.size() == 2) {
- List<Coordinate> circle = approximateCircle(coords.get(0), coords.get(1));
- if (!circle.isEmpty()) {
- obs.add(circle);
- }
- }
+ if (coords.size() >= 3) obs.add(coords);
}
if (obs.isEmpty()) {
List<Coordinate> coords = parseCoordinates(str);
- if (coords.size() >= 3) {
- obs.add(coords);
- } else if (coords.size() == 2) {
- List<Coordinate> circle = approximateCircle(coords.get(0), coords.get(1));
- if (!circle.isEmpty()) {
- obs.add(circle);
- }
- }
+ if (coords.size() >= 3) obs.add(coords);
}
return obs;
}
private static double parseDoubleOrDefault(String value, double defaultValue) {
- if (value == null || value.trim().isEmpty()) {
- return defaultValue;
- }
+ if (value == null || value.trim().isEmpty()) return defaultValue;
try {
return Double.parseDouble(value.trim());
} catch (NumberFormatException ex) {
- throw new IllegalArgumentException("鏍煎紡涓嶆纭�: " + value, ex);
+ return defaultValue;
}
}
private static String normalizeMode(String modeStr) {
- if (modeStr == null) {
- return "parallel";
- }
- String trimmed = modeStr.trim().toLowerCase();
- if ("1".equals(trimmed) || "spiral".equals(trimmed)) {
- return "spiral";
- }
- return "parallel";
+ return (modeStr != null && (modeStr.equals("1") || modeStr.equalsIgnoreCase("spiral"))) ? "spiral" : "parallel";
}
private static boolean equals2D(Coordinate a, Coordinate b) {
- if (a == b) {
- return true;
- }
- if (a == null || b == null) {
- return false;
- }
- return a.equals2D(b);
+ if (a == b) return true;
+ if (a == null || b == null) return false;
+ return a.distance(b) < 1e-4;
}
private static void appendPoint(StringBuilder sb, Coordinate point) {
- if (sb.length() > 0) {
- sb.append(";");
- }
- sb.append(String.format("%.2f,%.2f", point.x, point.y));
+ if (sb.length() > 0) sb.append(";");
+ sb.append(String.format("%.3f,%.3f", point.x, point.y));
}
- private static List<Coordinate> approximateCircle(Coordinate center, Coordinate edge) {
- double radius = center.distance(edge);
- if (radius <= 0) {
- return Collections.emptyList();
- }
- int segments = 36;
- List<Coordinate> circle = new ArrayList<>(segments + 1);
- for (int i = 0; i < segments; i++) {
- double angle = 2 * Math.PI * i / segments;
- double x = center.x + radius * Math.cos(angle);
- double y = center.y + radius * Math.sin(angle);
- circle.add(new Coordinate(x, y));
- }
- circle.add(new Coordinate(circle.get(0)));
- return circle;
- }
-
- /**
- * 璺緞娈垫暟鎹粨鏋勩��
- */
public static final class PathSegment {
- public Coordinate start;
- public Coordinate end;
+ public Coordinate start, end;
public boolean isMowing;
- public boolean isStartPoint;
- public boolean isEndPoint;
+ public boolean isStartPoint, isEndPoint;
public PathSegment(Coordinate start, Coordinate end, boolean isMowing) {
this.start = start;
@@ -260,419 +164,228 @@
this.isMowing = isMowing;
}
- public void setAsStartPoint() {
- this.isStartPoint = true;
- }
-
- public void setAsEndPoint() {
- this.isEndPoint = true;
- }
-
- @Override
- public String toString() {
- return String.format("PathSegment[(%.2f,%.2f)->(%.2f,%.2f) mowing=%b start=%b end=%b]",
- start.x, start.y, end.x, end.y, isMowing, isStartPoint, isEndPoint);
- }
+ public void setAsStartPoint() { this.isStartPoint = true; }
+ public void setAsEndPoint() { this.isEndPoint = true; }
}
- /**
- * 鍐呴儴鏍稿績瑙勫垝鍣紝瀹炵幇涓� MowingPathPlanner 绛夋晥鐨勯�昏緫銆�
- */
- static final class PlannerCore {
+ public static final class PlannerCore {
private final List<Coordinate> polygon;
private final double width;
- private final double safetyDistance; // 鏂板瀹夊叏璺濈瀛楁
+ private final double safetyDistance;
private final String mode;
private final List<List<Coordinate>> obstacles;
private final GeometryFactory gf = new GeometryFactory();
- PlannerCore(List<Coordinate> polygon, double width, double safetyDistance, String mode, List<List<Coordinate>> obstacles) {
+ // 1. 鍏ㄥ弬鏁版瀯閫犲嚱鏁�
+ public PlannerCore(List<Coordinate> polygon, double width, double safetyDistance, String mode, List<List<Coordinate>> obstacles) {
this.polygon = polygon;
this.width = width;
this.mode = mode;
this.obstacles = obstacles != null ? obstacles : new ArrayList<>();
- // 鍒濆鍖栧畨鍏ㄨ窛绂婚�昏緫
- if (Double.isNaN(safetyDistance)) {
- // 濡傛灉鏈彁渚涳紝浣跨敤榛樿鍊硷細瀹藉害鐨勪竴鍗� + 0.05绫�
- this.safetyDistance = width / 2.0 + 0.05;
+ // FIX: 澧炲姞榛樿瀹夊叏杈硅窛銆傚師閫昏緫涓� width/2 + 0.05锛屽鏄撻�犳垚璇樊鍑虹晫銆�
+ // 鐜版敼涓� width/2 + 0.2 (20cm浣欓噺)锛岀‘淇濆壊鑽夋満瀹炰綋瀹屽叏鍦ㄧ晫鍐呫��
+ if (Double.isNaN(safetyDistance) || safetyDistance <= 0) {
+ this.safetyDistance = (width / 2.0) + 0.20;
} else {
- // 濡傛灉鎻愪緵浜嗭紝浣跨敤鎻愪緵鐨勫�硷紝浣嗚嚦灏戣淇濊瘉鏈哄櫒涓績涓嶇澹侊紙瀹藉害涓�鍗婏級
- // 鍏佽鐢ㄦ埛璁剧疆姣� width/2 鏇村ぇ鐨勫�兼潵杩滅杈圭晫
- this.safetyDistance = Math.max(safetyDistance, width / 2.0);
+ this.safetyDistance = safetyDistance;
}
}
-
- // 鍏煎鏃ф瀯閫犲嚱鏁�
- PlannerCore(List<Coordinate> polygon, double width, String mode, List<List<Coordinate>> obstacles) {
+
+ // 2. 4鍙傛暟鏋勯�犲嚱鏁�
+ public PlannerCore(List<Coordinate> polygon, double width, String mode, List<List<Coordinate>> obstacles) {
this(polygon, width, Double.NaN, mode, obstacles);
}
- List<PathSegment> generate() {
- // 濡傛灉鏈夐殰纰嶇墿锛屼娇鐢ㄥ甫闅滅鐗╅伩璁╃殑璺緞瑙勫垝鍣�
- if (!obstacles.isEmpty()) {
- // 浣跨敤璁$畻濂界殑瀹夊叏璺濈
- ObstaclePathPlanner obstaclePlanner = new ObstaclePathPlanner(
- polygon, width, mode, obstacles, this.safetyDistance);
- return obstaclePlanner.generate();
- }
+ public List<PathSegment> generate() {
+ if ("spiral".equals(mode)) return generateSpiralPath();
+ return generateDividedParallelPath();
+ }
+
+ public List<PathSegment> generateParallelPath() {
+ return generateDividedParallelPath();
+ }
+
+ private List<PathSegment> generateDividedParallelPath() {
+ List<PathSegment> totalPath = new ArrayList<>();
+ Geometry safeArea = buildSafeArea();
- // 娌℃湁闅滅鐗╂椂浣跨敤鍘熸湁閫昏緫
- if ("spiral".equals(mode)) {
- return generateSpiralPath();
- }
- return generateParallelPath();
- }
+ if (safeArea == null || safeArea.isEmpty()) return totalPath;
- List<PathSegment> generateParallelPath() {
- List<PathSegment> path = new ArrayList<>();
- Geometry safeArea = buildSafeArea();
- if (safeArea == null || safeArea.isEmpty()) {
- System.err.println("瀹夊叏鍖哄煙涓虹┖锛屾棤娉曠敓鎴愯矾寰�");
- return path;
+ List<Polygon> subRegions = new ArrayList<>();
+ if (safeArea instanceof Polygon) subRegions.add((Polygon) safeArea);
+ else if (safeArea instanceof MultiPolygon) {
+ for (int i = 0; i < safeArea.getNumGeometries(); i++) {
+ subRegions.add((Polygon) safeArea.getGeometryN(i));
+ }
}
- LineSegment longest = findLongestEdge(polygon);
- Vector2D baseDir = new Vector2D(longest.end.x - longest.start.x,
- longest.end.y - longest.start.y).normalize();
- Vector2D perp = baseDir.rotate90CCW();
- Vector2D baseStartVec = new Vector2D(longest.start.x, longest.start.y);
- double baseProjection = perp.dot(baseStartVec);
+ for (Polygon region : subRegions) {
+ if (region.isEmpty()) continue;
- double minProj = Double.POSITIVE_INFINITY;
- double maxProj = Double.NEGATIVE_INFINITY;
- Coordinate[] supportCoords = safeArea.getCoordinates();
- if (supportCoords != null && supportCoords.length > 0) {
- for (Coordinate coord : supportCoords) {
- double projection = perp.dot(new Vector2D(coord.x, coord.y)) - baseProjection;
- if (projection < minProj) {
- minProj = projection;
- }
- if (projection > maxProj) {
- maxProj = projection;
+ Vector2D baseDir = calculateMainDirection(region);
+ Vector2D perp = baseDir.rotate90CCW();
+ Envelope env = region.getEnvelopeInternal();
+
+ double minProj = Double.MAX_VALUE, maxProj = -Double.MAX_VALUE;
+ Coordinate[] coords = region.getCoordinates();
+ for (Coordinate c : coords) {
+ double p = perp.dot(new Vector2D(c));
+ minProj = Math.min(minProj, p);
+ maxProj = Math.max(maxProj, p);
+ }
+
+ int lineIdx = 0;
+ // 浠� minProj + width/2 寮�濮嬶紝纭繚绗竴鏉$嚎鍦ㄥ畨鍏ㄥ尯鍩熷唴渚�
+ for (double d = minProj + width / 2.0; d <= maxProj; d += width) {
+ LineString scanLine = createScanLine(d, perp, baseDir, env);
+
+ try {
+ Geometry intersections = region.intersection(scanLine);
+ if (intersections.isEmpty()) continue;
+
+ List<LineString> parts = extractLineStrings(intersections);
+
+ // 鎸夌収鎵弿鏂瑰悜鎺掑簭锛屽鐞嗗嚬澶氳竟褰㈡垨闅滅鐗�
+ parts.sort((a, b) -> Double.compare(
+ baseDir.dot(new Vector2D(a.getCoordinateN(0))),
+ baseDir.dot(new Vector2D(b.getCoordinateN(0)))
+ ));
+
+ // 铔囧舰璺緞锛氬鏁拌鍙嶈浆
+ if (lineIdx % 2 != 0) Collections.reverse(parts);
+
+ for (LineString part : parts) {
+ Coordinate[] cs = part.getCoordinates();
+ if (cs.length < 2) continue;
+
+ if (lineIdx % 2 != 0) reverseArray(cs);
+
+ // 纭繚鐐瑰潗鏍囨湁鏁�
+ totalPath.add(new PathSegment(cs[0], cs[cs.length - 1], true));
+ }
+ lineIdx++;
+ } catch (Exception e) {
+ // 蹇界暐鏋佸叾缃曡鐨勬嫇鎵戝紓甯革紝闃叉宕╂簝
}
}
- } else {
- Envelope env = safeArea.getEnvelopeInternal();
- minProj = perp.dot(new Vector2D(env.getMinX(), env.getMinY())) - baseProjection;
- maxProj = perp.dot(new Vector2D(env.getMaxX(), env.getMaxY())) - baseProjection;
}
- if (minProj > maxProj) {
- double tmp = minProj;
- minProj = maxProj;
- maxProj = tmp;
- }
- double first = minProj - width / 2.0;
-
- Geometry originalPoly = createPolygonFromCoordinates(polygon);
- Coordinate lastEnd = null;
- int idx = 0;
-
- for (double offset = first; offset <= maxProj + width / 2.0; offset += width) {
- Line2D.Double raw = createInfiniteLine(longest, perp, offset);
- List<Line2D.Double> segs = clipLineToPolygon(raw, safeArea);
- if (segs.isEmpty()) {
- continue;
- }
-
- List<Line2D.Double> finalSegs = new ArrayList<>();
- for (Line2D.Double seg : segs) {
- finalSegs.addAll(clipLineToPolygon(seg, originalPoly));
- }
- if (finalSegs.isEmpty()) {
- continue;
- }
-
- finalSegs.sort((a, b) -> Double.compare(baseDir.dot(midV(a)), baseDir.dot(midV(b))));
- boolean even = (idx % 2 == 0);
- for (Line2D.Double seg : finalSegs) {
- Coordinate entry = even ? new Coordinate(seg.x1, seg.y1)
- : new Coordinate(seg.x2, seg.y2);
- Coordinate exit = even ? new Coordinate(seg.x2, seg.y2)
- : new Coordinate(seg.x1, seg.y1);
-
- if (lastEnd != null && lastEnd.distance(entry) > 1e-3) {
- path.add(new PathSegment(lastEnd, entry, false));
- }
-
- PathSegment mowingSeg = new PathSegment(entry, exit, true);
- if (path.isEmpty()) {
- mowingSeg.setAsStartPoint();
- }
- path.add(mowingSeg);
- lastEnd = exit;
- }
- idx++;
- }
-
- if (!path.isEmpty()) {
- path.get(path.size() - 1).setAsEndPoint();
- }
-
- postProcess(path);
- return path;
- }
-
- List<PathSegment> generateSpiralPath() {
- Geometry safeArea = buildSafeArea();
- if (safeArea == null || safeArea.isEmpty()) {
- System.err.println("瀹夊叏鍖哄煙涓虹┖锛屾棤娉曠敓鎴愯灪鏃嬭矾寰�");
- return new ArrayList<>();
- }
-
- List<PathSegment> spiral = luoxuan.generateSpiralPath(safeArea, width);
- if (spiral.isEmpty()) {
- return spiral;
- }
-
- postProcess(spiral);
- PathSegment firstMowing = null;
- PathSegment endCandidate = null;
- for (int i = 0; i < spiral.size(); i++) {
- PathSegment seg = spiral.get(i);
- if (seg != null && seg.isMowing) {
- if (firstMowing == null) {
- firstMowing = seg;
- }
- endCandidate = seg;
- }
- }
- if (firstMowing != null) {
- firstMowing.setAsStartPoint();
- }
- if (endCandidate != null && endCandidate != firstMowing) {
- endCandidate.setAsEndPoint();
- }
- return spiral;
+ return markStartEnd(totalPath);
}
private Geometry buildSafeArea() {
try {
- Coordinate[] coords = polygon.toArray(new Coordinate[0]);
- if (!coords[0].equals2D(coords[coords.length - 1])) {
- coords = Arrays.copyOf(coords, coords.length + 1);
- coords[coords.length - 1] = coords[0];
- }
- Polygon origin = gf.createPolygon(gf.createLinearRing(coords));
- Geometry result = origin;
+ Polygon poly = gf.createPolygon(gf.createLinearRing(polygon.toArray(new Coordinate[0])));
+
+ // 1. 鍒濆淇锛氬鐞嗚嚜鐩镐氦
+ if (!poly.isValid()) poly = (Polygon) poly.buffer(0);
+
+ // 2. 鍐呯缉鐢熸垚瀹夊叏鍖哄煙
+ Geometry safe = poly.buffer(-safetyDistance);
+
+ // 3. 浜屾淇锛氳礋缂撳啿鍚庡彲鑳戒骇鐢熶笉瑙勮寖鍑犱綍浣�
+ if (!safe.isValid()) safe = safe.buffer(0);
- if (!obstacles.isEmpty()) {
- List<Geometry> obsGeom = new ArrayList<>();
- for (List<Coordinate> obs : obstacles) {
- Geometry g = createPolygonFromCoordinates(obs);
- if (g != null && !g.isEmpty()) {
- obsGeom.add(g);
- }
- }
- if (!obsGeom.isEmpty()) {
- Geometry union = CascadedPolygonUnion.union(obsGeom);
- result = origin.difference(union);
+ // 4. 澶勭悊闅滅鐗�
+ for (List<Coordinate> obsCoords : obstacles) {
+ if (obsCoords.size() < 3) continue;
+ try {
+ Polygon obs = gf.createPolygon(gf.createLinearRing(obsCoords.toArray(new Coordinate[0])));
+ if (!obs.isValid()) obs = (Polygon) obs.buffer(0);
+ // 闅滅鐗╁鎵╁畨鍏ㄨ窛绂�
+ safe = safe.difference(obs.buffer(safetyDistance));
+ } catch (Exception e) {
+ // 蹇界暐閿欒鐨勯殰纰嶇墿鏁版嵁
}
}
-
- // 淇敼锛氫娇鐢ㄤ紶鍏ョ殑 safetyDistance 鏉ヨ繘琛岃竟鐣屽唴缂�
- // 涔嬪墠鏄� width / 2.0锛岀幇鍦ㄤ娇鐢� this.safetyDistance
- // 杩欑‘淇濅簡璺緞瑙勫垝鍖哄煙涓庤竟鐣屼繚鎸佺敤鎴锋寚瀹氱殑璺濈
- Geometry shrunk = shrinkStraight(result, this.safetyDistance);
- return shrunk.isEmpty() ? result : shrunk;
- } catch (Exception ex) {
- System.err.println("鏋勫缓瀹夊叏鍖哄煙澶辫触: " + ex.getMessage());
+
+ // 5. 鏈�缁堟竻鐞�
+ if (!safe.isValid()) safe = safe.buffer(0);
+ return safe;
+ } catch (Exception e) {
+ // 濡傛灉鍑犱綍鏋勫缓瀹屽叏澶辫触锛岃繑鍥炵┖
return gf.createPolygon();
}
}
- private LineSegment findLongestEdge(List<Coordinate> ring) {
- double max = -1.0;
- LineSegment best = null;
- for (int i = 0; i < ring.size() - 1; i++) {
- double d = ring.get(i).distance(ring.get(i + 1));
- if (d > max) {
- max = d;
- best = new LineSegment(ring.get(i), ring.get(i + 1), i);
+ private Vector2D calculateMainDirection(Polygon region) {
+ Coordinate[] coords = region.getExteriorRing().getCoordinates();
+ double maxLen = -1;
+ Vector2D bestDir = new Vector2D(1, 0);
+
+ // 瀵绘壘鏈�闀胯竟浣滀负涓绘柟鍚戯紝鍑忓皯杞集娆℃暟
+ for (int i = 0; i < coords.length - 1; i++) {
+ double d = coords[i].distance(coords[i+1]);
+ if (d > maxLen && d > 1e-4) {
+ maxLen = d;
+ bestDir = new Vector2D(coords[i+1].x - coords[i].x, coords[i+1].y - coords[i].y).normalize();
}
}
- return best;
+ return bestDir;
}
- private Line2D.Double createInfiniteLine(LineSegment base, Vector2D perp, double offset) {
- Vector2D baseStart = new Vector2D(base.start.x, base.start.y);
- Vector2D baseDir = new Vector2D(base.end.x - base.start.x,
- base.end.y - base.start.y).normalize();
- Vector2D center = baseStart.add(perp.mul(offset));
- double ext = 1.5 * diagonalLength();
- Vector2D p1 = center.sub(baseDir.mul(ext));
- Vector2D p2 = center.add(baseDir.mul(ext));
- return new Line2D.Double(p1.x, p1.y, p2.x, p2.y);
- }
-
- private List<Line2D.Double> clipLineToPolygon(Line2D.Double line, Geometry poly) {
- List<Line2D.Double> list = new ArrayList<>();
- LineString ls = gf.createLineString(new Coordinate[]{
- new Coordinate(line.x1, line.y1),
- new Coordinate(line.x2, line.y2)
- });
- Geometry inter = poly.intersection(ls);
- if (inter.isEmpty()) {
- return list;
- }
-
- if (inter instanceof LineString) {
- addCoords((LineString) inter, list);
- } else if (inter instanceof MultiLineString) {
- MultiLineString mls = (MultiLineString) inter;
- for (int i = 0; i < mls.getNumGeometries(); i++) {
- addCoords((LineString) mls.getGeometryN(i), list);
+ private List<LineString> extractLineStrings(Geometry geom) {
+ List<LineString> list = new ArrayList<>();
+ if (geom instanceof LineString) list.add((LineString) geom);
+ else if (geom instanceof MultiLineString) {
+ for (int i = 0; i < geom.getNumGeometries(); i++) list.add((LineString) geom.getGeometryN(i));
+ } else if (geom instanceof org.locationtech.jts.geom.GeometryCollection) {
+ for (int i = 0; i < geom.getNumGeometries(); i++) {
+ if (geom.getGeometryN(i) instanceof LineString) {
+ list.add((LineString) geom.getGeometryN(i));
+ }
}
}
return list;
}
- private void addCoords(LineString ls, List<Line2D.Double> bucket) {
- Coordinate[] cs = ls.getCoordinateSequence().toCoordinateArray();
- for (int i = 0; i < cs.length - 1; i++) {
- bucket.add(new Line2D.Double(cs[i].x, cs[i].y, cs[i + 1].x, cs[i + 1].y));
+ private LineString createScanLine(double dist, Vector2D perp, Vector2D baseDir, Envelope env) {
+ // 鎵╁ぇ鎵弿绾块暱搴︼紝纭繚瑕嗙洊鏃嬭浆鍚庣殑澶氳竟褰�
+ double size = Math.max(env.getWidth(), env.getHeight());
+ // 澶勭悊閫�鍖栧寘鍥寸洅
+ if (size < 1.0) size = 1000.0;
+
+ double len = size * 3.0; // 3鍊嶅昂瀵哥‘淇濊冻澶熼暱
+
+ // 涓績鐐硅绠楋細鍦ㄥ瀭鐩存柟鍚戜笂璺濈鍘熺偣 dist 鐨勪綅缃�
+ Vector2D center = perp.mul(dist);
+
+ return gf.createLineString(new Coordinate[]{
+ new Coordinate(center.x + baseDir.x * len, center.y + baseDir.y * len),
+ new Coordinate(center.x - baseDir.x * len, center.y - baseDir.y * len)
+ });
+ }
+
+ private List<PathSegment> markStartEnd(List<PathSegment> path) {
+ if (!path.isEmpty()) {
+ path.get(0).setAsStartPoint();
+ path.get(path.size() - 1).setAsEndPoint();
+ }
+ return path;
+ }
+
+ private void reverseArray(Coordinate[] arr) {
+ for (int i = 0; i < arr.length / 2; i++) {
+ Coordinate t = arr[i];
+ arr[i] = arr[arr.length - 1 - i];
+ arr[arr.length - 1 - i] = t;
}
}
- private Geometry createPolygonFromCoordinates(List<Coordinate> coords) {
- if (coords.size() < 3) {
- return gf.createPolygon();
- }
- List<Coordinate> closed = new ArrayList<>(coords);
- if (!closed.get(0).equals2D(closed.get(closed.size() - 1))) {
- closed.add(new Coordinate(closed.get(0)));
- }
- LinearRing shell = gf.createLinearRing(closed.toArray(new Coordinate[0]));
- Polygon polygonGeom = gf.createPolygon(shell);
- return polygonGeom.isValid() ? polygonGeom : (Polygon) polygonGeom.buffer(0);
- }
-
- private double diagonalLength() {
- double minX = polygon.stream().mapToDouble(c -> c.x).min().orElse(0);
- double maxX = polygon.stream().mapToDouble(c -> c.x).max().orElse(0);
- double minY = polygon.stream().mapToDouble(c -> c.y).min().orElse(0);
- double maxY = polygon.stream().mapToDouble(c -> c.y).max().orElse(0);
- return Math.hypot(maxX - minX, maxY - minY);
- }
-
- private Vector2D midV(Line2D.Double l) {
- return new Vector2D((l.x1 + l.x2) / 2.0, (l.y1 + l.y2) / 2.0);
- }
-
- private void postProcess(List<PathSegment> path) {
- if (path == null || path.isEmpty()) {
- return;
- }
- List<PathSegment> tmp = new ArrayList<>(path);
- path.clear();
- Coordinate prevEnd = null;
- for (PathSegment seg : tmp) {
- if (prevEnd != null && seg.start.distance(prevEnd) < 1e-3) {
- seg.start = prevEnd;
- }
- if (!seg.isMowing && !path.isEmpty()) {
- PathSegment last = path.get(path.size() - 1);
- if (!last.isMowing && isCollinear(last.start, last.end, seg.end)) {
- last.end = seg.end;
- prevEnd = seg.end;
- continue;
- }
- }
- path.add(seg);
- prevEnd = seg.end;
- }
- }
-
- private boolean isCollinear(Coordinate a, Coordinate b, Coordinate c) {
- double dx1 = b.x - a.x;
- double dy1 = b.y - a.y;
- double dx2 = c.x - b.x;
- double dy2 = c.y - b.y;
- double cross = dx1 * dy2 - dy1 * dx2;
- return Math.abs(cross) < 1e-6;
- }
-
- private Geometry shrinkStraight(Geometry outer, double dist) {
- Geometry buf = outer.buffer(-dist);
- if (buf.isEmpty()) {
- return buf;
- }
-
- Geometry poly = (buf instanceof Polygon) ? buf
- : (buf instanceof MultiPolygon) ? ((MultiPolygon) buf).getGeometryN(0) : null;
- if (!(poly instanceof Polygon)) {
- return buf;
- }
-
- Coordinate[] ring = ((Polygon) poly).getExteriorRing().getCoordinateSequence().toCoordinateArray();
- List<Coordinate> straight = new ArrayList<>();
- final double EPS = 1e-3;
- for (int i = 0; i < ring.length - 1; i++) {
- Coordinate prev = (i == 0) ? ring[ring.length - 2] : ring[i - 1];
- Coordinate curr = ring[i];
- Coordinate next = ring[i + 1];
- double cross = Math.abs((next.x - curr.x) * (curr.y - prev.y)
- - (curr.x - prev.x) * (next.y - curr.y));
- if (cross > EPS) {
- straight.add(curr);
- }
- }
- if (straight.isEmpty()) {
- return buf;
- }
- straight.add(new Coordinate(straight.get(0)));
- return straight.size() < 4 ? gf.createPolygon()
- : gf.createPolygon(gf.createLinearRing(straight.toArray(new Coordinate[0])));
- }
+ List<PathSegment> generateSpiralPath() { return new ArrayList<>(); }
}
private static final class Vector2D {
- final double x;
- final double y;
+ final double x, y;
+ Vector2D(double x, double y) { this.x = x; this.y = y; }
+ Vector2D(Coordinate c) { this.x = c.x; this.y = c.y; }
- Vector2D(double x, double y) {
- this.x = x;
- this.y = y;
+ Vector2D normalize() {
+ double len = Math.hypot(x, y);
+ return len < 1e-9 ? new Vector2D(1, 0) : new Vector2D(x / len, y / len);
}
-
- Vector2D normalize() {
- double len = Math.hypot(x, y);
- if (len < 1e-12) {
- return new Vector2D(1, 0);
- }
- return new Vector2D(x / len, y / len);
- }
-
- Vector2D rotate90CCW() {
- return new Vector2D(-y, x);
- }
-
- double dot(Vector2D v) {
- return x * v.x + y * v.y;
- }
-
- Vector2D sub(Vector2D v) {
- return new Vector2D(x - v.x, y - v.y);
- }
-
- Vector2D add(Vector2D v) {
- return new Vector2D(x + v.x, y + v.y);
- }
-
- Vector2D mul(double k) {
- return new Vector2D(x * k, y * k);
- }
- }
-
- private static final class LineSegment {
- final Coordinate start;
- final Coordinate end;
- final int index;
-
- LineSegment(Coordinate start, Coordinate end, int index) {
- this.start = start;
- this.end = end;
- this.index = index;
- }
+ Vector2D rotate90CCW() { return new Vector2D(-y, x); }
+ double dot(Vector2D v) { return x * v.x + y * v.y; }
+ Vector2D mul(double k) { return new Vector2D(x * k, y * k); }
}
}
\ No newline at end of file
diff --git a/src/lujing/MowingPathGenerationPage.java b/src/lujing/MowingPathGenerationPage.java
index 5c080b0..3e0cd48 100644
--- a/src/lujing/MowingPathGenerationPage.java
+++ b/src/lujing/MowingPathGenerationPage.java
@@ -477,33 +477,22 @@
obstacleList = new ArrayList<>();
}
- // 鍒ゆ柇鏄惁鏈夐殰纰嶇墿锛氬彧瑕佸師濮嬭緭鍏ユ湁闅滅鐗╁唴瀹癸紝灏变娇鐢∣bstaclePathPlanner
- // 鍗充娇瑙f瀽鍚庡垪琛ㄤ负绌猴紝涔熷皾璇曚娇鐢∣bstaclePathPlanner锛堝畠浼氬鐞嗙┖闅滅鐗╁垪琛ㄧ殑鎯呭喌锛�
- boolean hasObstacles = hasObstacleInput && !obstacleList.isEmpty();
-
- // 濡傛灉鍘熷杈撳叆鏈夐殰纰嶇墿浣嗚В鏋愬け璐ワ紝缁欏嚭鎻愮ず
- if (hasObstacleInput && obstacleList.isEmpty()) {
- if (showMessages) {
- JOptionPane.showMessageDialog(parentComponent,
- "闅滅鐗╁潗鏍囨牸寮忓彲鑳戒笉姝g‘锛屽皢灏濊瘯鐢熸垚璺緞銆傚鏋滆矾寰勪笉姝g‘锛岃妫�鏌ラ殰纰嶇墿鍧愭爣鏍煎紡銆�",
- "鎻愮ず", JOptionPane.WARNING_MESSAGE);
- }
- // 浠嶇劧灏濊瘯浣跨敤ObstaclePathPlanner锛屽嵆浣块殰纰嶇墿鍒楄〃涓虹┖
- // 杩欐牱鑷冲皯鍙互纭繚浣跨敤姝g‘鐨勮矾寰勮鍒掑櫒
- }
+ // 鍒ゆ柇鏄惁鏈夋湁鏁堢殑闅滅鐗╋細鍙湁褰撹В鏋愭垚鍔熶笖鍒楄〃涓嶄负绌烘椂锛屾墠璁や负鏈夐殰纰嶇墿
+ boolean hasValidObstacles = !obstacleList.isEmpty();
String generated;
- if (!hasObstacles && !hasObstacleInput) {
- // 瀹屽叏娌℃湁闅滅鐗╄緭鍏ユ椂锛屼娇鐢↙unjingguihua绫荤殑鏂规硶鐢熸垚璺緞
+ if (!hasValidObstacles) {
+ // 闅滅鐗╁潗鏍囦笉瀛樺湪鎴栦负绌烘椂锛屼娇鐢↙unjingguihua绫荤殑鏂规硶鐢熸垚璺緞
generated = Lunjingguihua.generatePathFromStrings(
boundary,
obstacles != null ? obstacles : "",
plannerWidth,
+ null, // safetyDistStr锛屼娇鐢ㄩ粯璁ゅ��
mode
);
} else {
- // 鏈夐殰纰嶇墿杈撳叆鏃讹紙鍗充娇瑙f瀽澶辫触锛夛紝浣跨敤ObstaclePathPlanner澶勭悊璺緞鐢熸垚
+ // 鏈夋湁鏁堥殰纰嶇墿鏃讹紝浣跨敤ObstaclePathPlanner澶勭悊璺緞鐢熸垚
List<Coordinate> polygon = Lunjingguihua.parseCoordinates(boundary);
if (polygon.size() < 4) {
if (showMessages) {
@@ -513,15 +502,8 @@
return null;
}
- // 鏍规嵁鏄惁鏈夐殰纰嶇墿璁剧疆涓嶅悓鐨勫畨鍏ㄨ窛绂�
- double safetyDistance;
- if (!obstacleList.isEmpty()) {
- // 鏈夐殰纰嶇墿鏃朵娇鐢ㄥ壊鑽夊搴︾殑涓�鍗� + 0.05绫抽澶栧畨鍏ㄨ窛绂�
- safetyDistance = widthMeters / 2.0 + 0.05;
- } else {
- // 闅滅鐗╄В鏋愬け璐ヤ絾杈撳叆瀛樺湪锛屼娇鐢ㄨ緝灏忕殑瀹夊叏璺濈
- safetyDistance = 0.01;
- }
+ // 鏈夐殰纰嶇墿鏃朵娇鐢ㄥ壊鑽夊搴︾殑涓�鍗� + 0.05绫抽澶栧畨鍏ㄨ窛绂�
+ double safetyDistance = widthMeters / 2.0 + 0.05;
ObstaclePathPlanner pathPlanner = new ObstaclePathPlanner(
polygon, widthMeters, mode, obstacleList, safetyDistance);
diff --git a/src/lujing/ObstaclePathPlanner.java b/src/lujing/ObstaclePathPlanner.java
index 8c89235..96f189a 100644
--- a/src/lujing/ObstaclePathPlanner.java
+++ b/src/lujing/ObstaclePathPlanner.java
@@ -533,4 +533,8 @@
}
}
}
-}
\ No newline at end of file
+}
+
+
+
+
diff --git a/src/zhangaiwu/AddDikuai.java b/src/zhangaiwu/AddDikuai.java
index 02ac96d..b80b9ec 100644
--- a/src/zhangaiwu/AddDikuai.java
+++ b/src/zhangaiwu/AddDikuai.java
@@ -65,7 +65,9 @@
private JTextField landNumberField;
private JTextField areaNameField;
private JComboBox<String> mowingPatternCombo;
- private JSpinner mowingWidthSpinner;
+ private JTextField mowingWidthField; // 鍓茶崏鏈哄壊鍒�瀹藉害
+ private JTextField overlapDistanceField; // 鐩搁偦琛岄噸鍙犺窛绂�
+ private JLabel calculatedMowingWidthLabel; // 璁$畻鍚庣殑鍓茶崏瀹藉害鏄剧ず
private JPanel previewPanel;
private Map<String, JPanel> drawingOptionPanels = new HashMap<>();
@@ -768,7 +770,7 @@
return false;
}
if (userTriggered && "handheld".equalsIgnoreCase(type) && !hasConfiguredHandheldMarker()) {
- JOptionPane.showMessageDialog(this, "璇峰厛娣诲姞渚挎惡鎵撶偣鍣ㄧ紪鍙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ JOptionPane.showMessageDialog(this, "璇峰厛鍘荤郴缁熻缃坊鍔犱究鎼烘墦鐐瑰櫒缂栧彿", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
return false;
}
@@ -898,7 +900,7 @@
settingsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
// 鍓茶崏妯″紡閫夋嫨
- JPanel patternPanel = createFormGroup("鍓茶崏妯″紡", "閫夋嫨鍓茶崏璺緞鐨勭敓鎴愭ā寮�");
+ JPanel patternPanel = createFormGroupWithoutHint("鍓茶崏妯″紡");
mowingPatternCombo = new JComboBox<>(new String[]{"骞宠绾�", "铻烘棆寮�"});
mowingPatternCombo.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
mowingPatternCombo.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
@@ -930,29 +932,27 @@
patternPanel.add(mowingPatternCombo);
settingsPanel.add(patternPanel);
- settingsPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+ settingsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
- // 鍓茶崏瀹藉害璁剧疆
- JPanel widthPanel = createFormGroup("鍓茶崏瀹藉害", "璁剧疆鍓茶崏鏈哄崟娆″壊鑽夌殑瀹藉害");
+ // 鍓茶崏鏈哄壊鍒�瀹藉害璁剧疆
+ JPanel widthPanel = createFormGroupWithoutHint("鍓茶崏鏈哄壊鍒�瀹藉害");
JPanel widthInputPanel = new JPanel(new BorderLayout());
widthInputPanel.setBackground(WHITE);
widthInputPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
widthInputPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
- SpinnerNumberModel widthModel = new SpinnerNumberModel(40, 20, 60, 1);
- mowingWidthSpinner = new JSpinner(widthModel);
- mowingWidthSpinner.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
- JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) mowingWidthSpinner.getEditor();
- editor.getTextField().setBorder(BorderFactory.createCompoundBorder(
+ mowingWidthField = new JTextField("0.40");
+ mowingWidthField.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
+ mowingWidthField.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(BORDER_COLOR, 2),
BorderFactory.createEmptyBorder(10, 12, 10, 12)
));
- // 娣诲姞寰皟鍣ㄧ劍鐐规晥鏋�
- mowingWidthSpinner.addFocusListener(new FocusAdapter() {
+ // 娣诲姞鏂囨湰妗嗙劍鐐规晥鏋滃拰鍙樺寲鐩戝惉
+ mowingWidthField.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
- editor.getTextField().setBorder(BorderFactory.createCompoundBorder(
+ mowingWidthField.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(PRIMARY_COLOR, 2),
BorderFactory.createEmptyBorder(10, 12, 10, 12)
));
@@ -960,27 +960,97 @@
@Override
public void focusLost(FocusEvent e) {
- editor.getTextField().setBorder(BorderFactory.createCompoundBorder(
+ mowingWidthField.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(BORDER_COLOR, 2),
BorderFactory.createEmptyBorder(10, 12, 10, 12)
));
+ updateCalculatedMowingWidth();
}
});
- JLabel unitLabel = new JLabel("鍘樼背");
+ JLabel unitLabel = new JLabel("绫�");
unitLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
unitLabel.setForeground(LIGHT_TEXT);
unitLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
- widthInputPanel.add(mowingWidthSpinner, BorderLayout.CENTER);
+ widthInputPanel.add(mowingWidthField, BorderLayout.CENTER);
widthInputPanel.add(unitLabel, BorderLayout.EAST);
widthPanel.add(widthInputPanel);
settingsPanel.add(widthPanel);
+ settingsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+
+ // 鐩搁偦琛岄噸鍙犺窛绂昏缃�
+ JPanel overlapPanel = createFormGroupWithoutHint("鐩搁偦琛岄噸鍙犺窛绂�");
+ JPanel overlapInputPanel = new JPanel(new BorderLayout());
+ overlapInputPanel.setBackground(WHITE);
+ overlapInputPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
+ overlapInputPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ overlapDistanceField = new JTextField("0.06");
+ overlapDistanceField.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
+ overlapDistanceField.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(BORDER_COLOR, 2),
+ BorderFactory.createEmptyBorder(10, 12, 10, 12)
+ ));
+
+ // 娣诲姞鏂囨湰妗嗙劍鐐规晥鏋滃拰鍙樺寲鐩戝惉
+ overlapDistanceField.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ overlapDistanceField.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(PRIMARY_COLOR, 2),
+ BorderFactory.createEmptyBorder(10, 12, 10, 12)
+ ));
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ overlapDistanceField.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(BORDER_COLOR, 2),
+ BorderFactory.createEmptyBorder(10, 12, 10, 12)
+ ));
+ updateCalculatedMowingWidth();
+ }
+ });
+
+ JLabel overlapUnitLabel = new JLabel("绫�");
+ overlapUnitLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+ overlapUnitLabel.setForeground(LIGHT_TEXT);
+ overlapUnitLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
+
+ overlapInputPanel.add(overlapDistanceField, BorderLayout.CENTER);
+ overlapInputPanel.add(overlapUnitLabel, BorderLayout.EAST);
+
+ overlapPanel.add(overlapInputPanel);
+ settingsPanel.add(overlapPanel);
+ settingsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+
+ // 鍓茶崏瀹藉害鏄剧ず锛堣嚜鍔ㄨ绠楋紝涓嶅彲淇敼锛�
+ JPanel calculatedWidthPanel = createFormGroup("鍓茶崏瀹藉害", "鍓茶崏瀹藉害 = 鍓茶崏鏈哄壊鍒�瀹藉害 - 閲嶅彔璺濈锛堣嚜鍔ㄨ绠楋級");
+ JPanel calculatedWidthDisplayPanel = new JPanel(new BorderLayout());
+ calculatedWidthDisplayPanel.setBackground(LIGHT_GRAY);
+ calculatedWidthDisplayPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
+ calculatedWidthDisplayPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ calculatedWidthDisplayPanel.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(BORDER_COLOR, 2),
+ BorderFactory.createEmptyBorder(10, 12, 10, 12)
+ ));
+
+ calculatedMowingWidthLabel = new JLabel("0.00 绫�");
+ calculatedMowingWidthLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
+ calculatedMowingWidthLabel.setForeground(TEXT_COLOR);
+ calculatedWidthDisplayPanel.add(calculatedMowingWidthLabel, BorderLayout.CENTER);
+
+ calculatedWidthPanel.add(calculatedWidthDisplayPanel);
+ settingsPanel.add(calculatedWidthPanel);
settingsPanel.add(Box.createRigidArea(new Dimension(0, 25)));
stepPanel.add(settingsPanel);
+ // 鍒濆鍖栬绠楀悗鐨勫壊鑽夊搴�
+ updateCalculatedMowingWidth();
+
JButton generatePathButton = createPrimaryButton("鐢熸垚鍓茶崏璺緞", 16);
generatePathButton.setAlignmentX(Component.LEFT_ALIGNMENT);
generatePathButton.setMaximumSize(new Dimension(Integer.MAX_VALUE, 55));
@@ -1016,6 +1086,41 @@
return stepPanel;
}
+ /**
+ * 鏇存柊璁$畻鍚庣殑鍓茶崏瀹藉害鏄剧ず
+ */
+ private void updateCalculatedMowingWidth() {
+ if (calculatedMowingWidthLabel == null || mowingWidthField == null || overlapDistanceField == null) {
+ return;
+ }
+
+ try {
+ String widthText = mowingWidthField.getText().trim();
+ String overlapText = overlapDistanceField.getText().trim();
+
+ if (widthText.isEmpty() || overlapText.isEmpty()) {
+ calculatedMowingWidthLabel.setText("0.00 绫�");
+ return;
+ }
+
+ double bladeWidthMeters = Double.parseDouble(widthText);
+ double overlapMeters = Double.parseDouble(overlapText);
+ double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+
+ if (calculatedWidthMeters <= 0) {
+ calculatedMowingWidthLabel.setText("鏃犳晥锛堝壊鍒�瀹藉害闇�澶т簬閲嶅彔璺濈锛�");
+ calculatedMowingWidthLabel.setForeground(ERROR_COLOR);
+ } else {
+ calculatedMowingWidthLabel.setText(String.format(Locale.US, "%.2f 绫�", calculatedWidthMeters));
+ calculatedMowingWidthLabel.setForeground(TEXT_COLOR);
+ }
+ } catch (NumberFormatException e) {
+ calculatedMowingWidthLabel.setText("0.00 绫�");
+ } catch (Exception e) {
+ calculatedMowingWidthLabel.setText("0.00 绫�");
+ }
+ }
+
private JPanel createInstructionPanel(String text) {
JPanel instructionPanel = new JPanel(new BorderLayout());
instructionPanel.setBackground(PRIMARY_LIGHT);
@@ -1066,6 +1171,23 @@
return formGroup;
}
+
+ private JPanel createFormGroupWithoutHint(String label) {
+ JPanel formGroup = new JPanel();
+ formGroup.setLayout(new BoxLayout(formGroup, BoxLayout.Y_AXIS));
+ formGroup.setBackground(WHITE);
+ formGroup.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ JLabel nameLabel = new JLabel(label);
+ nameLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+ nameLabel.setForeground(TEXT_COLOR);
+ nameLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ formGroup.add(nameLabel);
+ formGroup.add(Box.createRigidArea(new Dimension(0, 8)));
+
+ return formGroup;
+ }
private void generateMowingPath() {
if (!dikuaiData.containsKey("boundaryDrawn")) {
@@ -1104,25 +1226,79 @@
String patternDisplay = (String) mowingPatternCombo.getSelectedItem();
dikuaiData.put("mowingPattern", patternDisplay);
- Object widthObj = mowingWidthSpinner.getValue();
- if (!(widthObj instanceof Number)) {
- JOptionPane.showMessageDialog(this, "鍓茶崏瀹藉害杈撳叆鏃犳晥", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ String widthText = mowingWidthField.getText().trim();
+ if (widthText.isEmpty()) {
+ JOptionPane.showMessageDialog(this, "鍓茶崏鏈哄壊鍒�瀹藉害涓嶈兘涓虹┖", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
dikuaiData.remove("plannedPath");
- showPathGenerationMessage("鍓茶崏瀹藉害杈撳叆鏃犳晥锛岃閲嶆柊杈撳叆銆�", false);
+ showPathGenerationMessage("鍓茶崏鏈哄壊鍒�瀹藉害涓嶈兘涓虹┖锛岃閲嶆柊杈撳叆銆�", false);
setPathAvailability(false);
return;
}
- double widthCm = ((Number) widthObj).doubleValue();
- if (widthCm <= 0) {
- JOptionPane.showMessageDialog(this, "鍓茶崏瀹藉害蹇呴』澶т簬0", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ double bladeWidthMeters;
+ try {
+ bladeWidthMeters = Double.parseDouble(widthText);
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(this, "鍓茶崏鏈哄壊鍒�瀹藉害杈撳叆鏃犳晥", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
dikuaiData.remove("plannedPath");
- showPathGenerationMessage("鍓茶崏瀹藉害蹇呴』澶т簬0锛岃閲嶆柊璁剧疆銆�", false);
+ showPathGenerationMessage("鍓茶崏鏈哄壊鍒�瀹藉害杈撳叆鏃犳晥锛岃閲嶆柊杈撳叆銆�", false);
setPathAvailability(false);
return;
}
- dikuaiData.put("mowingWidth", widthObj.toString());
+ if (bladeWidthMeters <= 0) {
+ JOptionPane.showMessageDialog(this, "鍓茶崏鏈哄壊鍒�瀹藉害蹇呴』澶т簬0", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ dikuaiData.remove("plannedPath");
+ showPathGenerationMessage("鍓茶崏鏈哄壊鍒�瀹藉害蹇呴』澶т簬0锛岃閲嶆柊璁剧疆銆�", false);
+ setPathAvailability(false);
+ return;
+ }
+ // 淇濆瓨鍓茶崏鏈哄壊鍒�瀹藉害锛堢背锛�
+ dikuaiData.put("mowingBladeWidth", String.format(Locale.US, "%.2f", bladeWidthMeters));
+
+ // 鑾峰彇閲嶅彔璺濈
+ String overlapText = overlapDistanceField.getText().trim();
+ if (overlapText.isEmpty()) {
+ JOptionPane.showMessageDialog(this, "鐩搁偦琛岄噸鍙犺窛绂讳笉鑳戒负绌�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ dikuaiData.remove("plannedPath");
+ showPathGenerationMessage("鐩搁偦琛岄噸鍙犺窛绂讳笉鑳戒负绌猴紝璇烽噸鏂拌緭鍏ャ��", false);
+ setPathAvailability(false);
+ return;
+ }
+ double overlapMeters;
+ try {
+ overlapMeters = Double.parseDouble(overlapText);
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(this, "鐩搁偦琛岄噸鍙犺窛绂昏緭鍏ユ棤鏁�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ dikuaiData.remove("plannedPath");
+ showPathGenerationMessage("鐩搁偦琛岄噸鍙犺窛绂昏緭鍏ユ棤鏁堬紝璇烽噸鏂拌緭鍏ャ��", false);
+ setPathAvailability(false);
+ return;
+ }
+ if (overlapMeters < 0) {
+ JOptionPane.showMessageDialog(this, "鐩搁偦琛岄噸鍙犺窛绂讳笉鑳戒负璐熸暟", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ dikuaiData.remove("plannedPath");
+ showPathGenerationMessage("鐩搁偦琛岄噸鍙犺窛绂讳笉鑳戒负璐熸暟锛岃閲嶆柊璁剧疆銆�", false);
+ setPathAvailability(false);
+ return;
+ }
+
+ // 淇濆瓨閲嶅彔璺濈鍒板湴鍧楁暟鎹�
+ dikuaiData.put("mowingOverlapDistance", String.format(Locale.US, "%.2f", overlapMeters));
+
+ // 璁$畻瀹為檯浣跨敤鐨勫壊鑽夊搴︼紙鍓插垁瀹藉害 - 閲嶅彔璺濈锛�
+ double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+ if (calculatedWidthMeters <= 0) {
+ JOptionPane.showMessageDialog(this, "璁$畻鍚庣殑鍓茶崏瀹藉害蹇呴』澶т簬0锛堝壊鍒�瀹藉害蹇呴』澶т簬閲嶅彔璺濈锛�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ dikuaiData.remove("plannedPath");
+ showPathGenerationMessage("璁$畻鍚庣殑鍓茶崏瀹藉害蹇呴』澶т簬0锛岃璋冩暣鍓插垁瀹藉害鎴栭噸鍙犺窛绂汇��", false);
+ setPathAvailability(false);
+ return;
+ }
- String widthMeters = String.format(Locale.US, "%.2f", widthCm / 100.0);
+ // 淇濆瓨鍓茶崏瀹藉害锛堣绠楀悗鐨勫�硷紝杞崲涓哄帢绫冲瓨鍌紝淇濇寔涓庡師鏈夋暟鎹牸寮忓吋瀹癸級
+ double calculatedWidthCm = calculatedWidthMeters * 100.0;
+ dikuaiData.put("mowingWidth", String.format(Locale.US, "%.0f", calculatedWidthCm));
+
+ String widthMeters = String.format(Locale.US, "%.2f", calculatedWidthMeters);
String plannerMode = resolvePlannerMode(patternDisplay);
try {
@@ -1245,13 +1421,51 @@
}
}
- if (mowingWidthSpinner != null) {
- Object widthValue = mowingWidthSpinner.getValue();
- if (widthValue instanceof Number) {
- int widthInt = ((Number) widthValue).intValue();
- dikuaiData.put("mowingWidth", Integer.toString(widthInt));
- } else if (widthValue != null) {
- dikuaiData.put("mowingWidth", widthValue.toString());
+ // 淇濆瓨鍓茶崏鏈哄壊鍒�瀹藉害
+ if (mowingWidthField != null) {
+ String bladeWidthText = mowingWidthField.getText().trim();
+ if (!bladeWidthText.isEmpty()) {
+ try {
+ double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+ dikuaiData.put("mowingBladeWidth", String.format(Locale.US, "%.2f", bladeWidthMeters));
+ } catch (NumberFormatException e) {
+ // 濡傛灉瑙f瀽澶辫触锛岀洿鎺ヤ繚瀛樻枃鏈�
+ dikuaiData.put("mowingBladeWidth", bladeWidthText);
+ }
+ }
+ }
+
+ // 淇濆瓨鐩搁偦琛岄噸鍙犺窛绂�
+ if (overlapDistanceField != null) {
+ String overlapText = overlapDistanceField.getText().trim();
+ if (!overlapText.isEmpty()) {
+ try {
+ double overlapMeters = Double.parseDouble(overlapText);
+ dikuaiData.put("mowingOverlapDistance", String.format(Locale.US, "%.2f", overlapMeters));
+ } catch (NumberFormatException e) {
+ // 濡傛灉瑙f瀽澶辫触锛岀洿鎺ヤ繚瀛樻枃鏈�
+ dikuaiData.put("mowingOverlapDistance", overlapText);
+ }
+ }
+ }
+
+ // 璁$畻骞朵繚瀛樺壊鑽夊搴︼紙鍓插垁瀹藉害 - 閲嶅彔璺濈锛�
+ if (mowingWidthField != null && overlapDistanceField != null) {
+ try {
+ String bladeWidthText = mowingWidthField.getText().trim();
+ String overlapText = overlapDistanceField.getText().trim();
+ if (!bladeWidthText.isEmpty() && !overlapText.isEmpty()) {
+ double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+ double overlapMeters = Double.parseDouble(overlapText);
+ double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+ if (calculatedWidthMeters > 0) {
+ // 杞崲涓哄帢绫冲瓨鍌紝淇濇寔涓庡師鏈夋暟鎹牸寮忓吋瀹�
+ int widthCm = (int) Math.round(calculatedWidthMeters * 100.0);
+ dikuaiData.put("mowingWidth", Integer.toString(widthCm));
+ }
+ }
+ } catch (NumberFormatException e) {
+ // 濡傛灉璁$畻澶辫触锛屼笉淇濆瓨鍓茶崏瀹藉害
}
}
}
@@ -1724,7 +1938,53 @@
case 3:
dikuaiData.put("mowingPattern", (String) mowingPatternCombo.getSelectedItem());
- dikuaiData.put("mowingWidth", mowingWidthSpinner.getValue().toString());
+
+ // 淇濆瓨鍓茶崏鏈哄壊鍒�瀹藉害
+ if (mowingWidthField != null) {
+ String bladeWidthText = mowingWidthField.getText().trim();
+ if (!bladeWidthText.isEmpty()) {
+ try {
+ double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+ dikuaiData.put("mowingBladeWidth", String.format(Locale.US, "%.2f", bladeWidthMeters));
+ } catch (NumberFormatException e) {
+ dikuaiData.put("mowingBladeWidth", bladeWidthText);
+ }
+ }
+ }
+
+ // 淇濆瓨鐩搁偦琛岄噸鍙犺窛绂�
+ if (overlapDistanceField != null) {
+ String overlapText = overlapDistanceField.getText().trim();
+ if (!overlapText.isEmpty()) {
+ try {
+ double overlapMeters = Double.parseDouble(overlapText);
+ dikuaiData.put("mowingOverlapDistance", String.format(Locale.US, "%.2f", overlapMeters));
+ } catch (NumberFormatException e) {
+ dikuaiData.put("mowingOverlapDistance", overlapText);
+ }
+ }
+ }
+
+ // 璁$畻骞朵繚瀛樺壊鑽夊搴︼紙鍓插垁瀹藉害 - 閲嶅彔璺濈锛�
+ if (mowingWidthField != null && overlapDistanceField != null) {
+ try {
+ String bladeWidthText = mowingWidthField.getText().trim();
+ String overlapText = overlapDistanceField.getText().trim();
+ if (!bladeWidthText.isEmpty() && !overlapText.isEmpty()) {
+ double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+ double overlapMeters = Double.parseDouble(overlapText);
+ double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+ if (calculatedWidthMeters > 0) {
+ // 杞崲涓哄帢绫冲瓨鍌紝淇濇寔涓庡師鏈夋暟鎹牸寮忓吋瀹�
+ int widthCm = (int) Math.round(calculatedWidthMeters * 100.0);
+ dikuaiData.put("mowingWidth", Integer.toString(widthCm));
+ }
+ }
+ } catch (NumberFormatException e) {
+ // 濡傛灉璁$畻澶辫触锛屼笉淇濆瓨鍓茶崏瀹藉害
+ }
+ }
+
if (!hasGeneratedPath()) {
JOptionPane.showMessageDialog(this, "璇峰厛鐢熸垚鍓茶崏璺緞", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
return false;
@@ -2005,21 +2265,51 @@
}
}
- if (mowingWidthSpinner != null) {
- String width = data.get("mowingWidth");
- if (isMeaningfulValue(width)) {
+ // 鎭㈠鍓茶崏鏈哄壊鍒�瀹藉害锛堜紭鍏堜粠mowingBladeWidth鑾峰彇锛�
+ if (mowingWidthField != null) {
+ String bladeWidth = data.get("mowingBladeWidth");
+ if (isMeaningfulValue(bladeWidth)) {
try {
- double parsed = Double.parseDouble(width.trim());
- SpinnerNumberModel model = (SpinnerNumberModel) mowingWidthSpinner.getModel();
- int min = ((Number) model.getMinimum()).intValue();
- int max = ((Number) model.getMaximum()).intValue();
- int rounded = (int) Math.round(parsed);
- if (rounded < min) {
- rounded = min;
- } else if (rounded > max) {
- rounded = max;
+ double bladeWidthMeters = Double.parseDouble(bladeWidth.trim());
+ mowingWidthField.setText(String.format(Locale.US, "%.2f", bladeWidthMeters));
+ } catch (NumberFormatException ignored) {
+ // 濡傛灉mowingBladeWidth涓嶅瓨鍦ㄦ垨瑙f瀽澶辫触锛屽皾璇曚粠mowingWidth鎭㈠
+ String width = data.get("mowingWidth");
+ if (isMeaningfulValue(width)) {
+ try {
+ // 濡傛灉瀛樺偍鐨勬槸鍘樼背锛岃浆鎹负绫虫樉绀�
+ double parsed = Double.parseDouble(width.trim());
+ // 鍋囪濡傛灉鍊煎ぇ浜�10锛屽垯鏄帢绫筹紝闇�瑕佽浆鎹负绫筹紱鍚﹀垯宸茬粡鏄背
+ double widthMeters = parsed > 10 ? parsed / 100.0 : parsed;
+ mowingWidthField.setText(String.format(Locale.US, "%.2f", widthMeters));
+ } catch (NumberFormatException ignored2) {
+ // 淇濇寔褰撳墠鍊�
+ }
}
- mowingWidthSpinner.setValue(rounded);
+ }
+ } else {
+ // 濡傛灉mowingBladeWidth涓嶅瓨鍦紝灏濊瘯浠巑owingWidth鎭㈠
+ String width = data.get("mowingWidth");
+ if (isMeaningfulValue(width)) {
+ try {
+ // 濡傛灉瀛樺偍鐨勬槸鍘樼背锛岃浆鎹负绫虫樉绀�
+ double parsed = Double.parseDouble(width.trim());
+ // 鍋囪濡傛灉鍊煎ぇ浜�10锛屽垯鏄帢绫筹紝闇�瑕佽浆鎹负绫筹紱鍚﹀垯宸茬粡鏄背
+ double widthMeters = parsed > 10 ? parsed / 100.0 : parsed;
+ mowingWidthField.setText(String.format(Locale.US, "%.2f", widthMeters));
+ } catch (NumberFormatException ignored) {
+ // 淇濇寔褰撳墠鍊�
+ }
+ }
+ }
+ }
+
+ if (overlapDistanceField != null) {
+ String overlap = data.get("mowingOverlapDistance");
+ if (isMeaningfulValue(overlap)) {
+ try {
+ double overlapMeters = Double.parseDouble(overlap.trim());
+ overlapDistanceField.setText(String.format(Locale.US, "%.2f", overlapMeters));
} catch (NumberFormatException ignored) {
// 淇濇寔褰撳墠鍊�
}
@@ -2128,8 +2418,60 @@
if (dikuaiData.containsKey("mowingPattern")) {
dikuai.setMowingPattern(dikuaiData.get("mowingPattern"));
}
+
+ // 淇濆瓨鍓茶崏鏈哄壊鍒�瀹藉害锛堜紭鍏堜粠dikuaiData鑾峰彇锛屽惁鍒欎粠TextField鑾峰彇锛�
+ if (dikuaiData.containsKey("mowingBladeWidth")) {
+ dikuai.setMowingBladeWidth(dikuaiData.get("mowingBladeWidth"));
+ } else if (mowingWidthField != null) {
+ String bladeWidthText = mowingWidthField.getText().trim();
+ if (!bladeWidthText.isEmpty()) {
+ try {
+ double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+ dikuai.setMowingBladeWidth(String.format(Locale.US, "%.2f", bladeWidthMeters));
+ } catch (NumberFormatException e) {
+ dikuai.setMowingBladeWidth(bladeWidthText);
+ }
+ }
+ }
+
+ // 淇濆瓨鐩搁偦琛岄噸鍙犺窛绂伙紙浼樺厛浠巇ikuaiData鑾峰彇锛屽惁鍒欎粠TextField鑾峰彇锛�
+ if (dikuaiData.containsKey("mowingOverlapDistance")) {
+ dikuai.setMowingOverlapDistance(dikuaiData.get("mowingOverlapDistance"));
+ } else if (overlapDistanceField != null) {
+ String overlapText = overlapDistanceField.getText().trim();
+ if (!overlapText.isEmpty()) {
+ try {
+ double overlapMeters = Double.parseDouble(overlapText);
+ dikuai.setMowingOverlapDistance(String.format(Locale.US, "%.2f", overlapMeters));
+ } catch (NumberFormatException e) {
+ dikuai.setMowingOverlapDistance(overlapText);
+ }
+ }
+ }
+
+ // 淇濆瓨鍓茶崏瀹藉害锛堣绠楀悗鐨勫�硷紝浼樺厛浠巇ikuaiData鑾峰彇锛�
if (dikuaiData.containsKey("mowingWidth")) {
dikuai.setMowingWidth(dikuaiData.get("mowingWidth"));
+ } else {
+ // 濡傛灉娌℃湁鍦╠ikuaiData涓紝鍒欎粠TextField璁$畻
+ if (mowingWidthField != null && overlapDistanceField != null) {
+ try {
+ String bladeWidthText = mowingWidthField.getText().trim();
+ String overlapText = overlapDistanceField.getText().trim();
+ if (!bladeWidthText.isEmpty() && !overlapText.isEmpty()) {
+ double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+ double overlapMeters = Double.parseDouble(overlapText);
+ double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+ if (calculatedWidthMeters > 0) {
+ // 杞崲涓哄帢绫冲瓨鍌紝淇濇寔涓庡師鏈夋暟鎹牸寮忓吋瀹�
+ int widthCm = (int) Math.round(calculatedWidthMeters * 100.0);
+ dikuai.setMowingWidth(Integer.toString(widthCm));
+ }
+ }
+ } catch (NumberFormatException e) {
+ // 濡傛灉璁$畻澶辫触锛屼繚鎸佸師鏈夊�兼垨浣跨敤榛樿鍊�
+ }
+ }
}
String plannedPath = dikuaiData.get("plannedPath");
diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index 368e316..31b93b8 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -85,6 +85,7 @@
private CircleCaptureOverlay circleCaptureOverlay;
private final List<double[]> circleSampleMarkers = new ArrayList<>();
private final List<Point2D.Double> realtimeMowingTrack = new ArrayList<>();
+ private final List<Point2D.Double> navigationPreviewTrack = new ArrayList<>(); // 瀵艰埅棰勮杞ㄨ抗
private final Deque<tuowei.TrailSample> idleMowerTrail = new ArrayDeque<>();
private final List<Point2D.Double> handheldBoundaryPreview = new ArrayList<>();
private double boundaryPreviewMarkerScale = 1.0d;
@@ -417,9 +418,19 @@
if (!realtimeMowingTrack.isEmpty()) {
drawRealtimeMowingCoverage(g2d);
}
+
+ // 缁樺埗瀵艰埅棰勮宸插壊鍖哄煙
+ if (!navigationPreviewTrack.isEmpty()) {
+ drawNavigationPreviewCoverage(g2d);
+ }
drawMower(g2d);
+ // 缁樺埗瀵艰埅棰勮閫熷害锛堝鏋滄鍦ㄥ鑸瑙堬級
+ if (navigationPreviewSpeed > 0 && mower != null && mower.hasValidPosition()) {
+ drawNavigationPreviewSpeed(g2d, scale);
+ }
+
// 缁樺埗娴嬮噺妯″紡锛堝鏋滄縺娲伙級
if (measurementModeActive) {
drawMeasurementMode(g2d, scale);
@@ -458,6 +469,60 @@
private void drawMower(Graphics2D g2d) {
mower.draw(g2d, scale);
}
+
+ /**
+ * 缁樺埗瀵艰埅棰勮閫熷害锛堝湪鍓茶崏鏈哄浘鏍囦笂鏂癸級
+ */
+ private void drawNavigationPreviewSpeed(Graphics2D g2d, double scale) {
+ if (mower == null || !mower.hasValidPosition()) {
+ return;
+ }
+
+ Point2D.Double mowerPos = mower.getPosition();
+ if (mowerPos == null) {
+ return;
+ }
+
+ // 灏嗛�熷害浠庣背/绉掕浆鎹负KM/h
+ double speedKmh = navigationPreviewSpeed * 3.6;
+ String speedText = String.format("%.1f km/h", speedKmh);
+
+ // 淇濆瓨鍘熷鍙樻崲
+ AffineTransform originalTransform = g2d.getTransform();
+
+ // 灏嗕笘鐣屽潗鏍囪浆鎹负灞忓箷鍧愭爣
+ Point2D.Double screenPos = worldToScreen(mowerPos);
+
+ // 鎭㈠鍘熷鍙樻崲浠ョ粯鍒舵枃瀛楋紙鍥哄畾澶у皬锛屼笉闅忕缉鏀惧彉鍖栵級
+ g2d.setTransform(new AffineTransform());
+
+ // 璁剧疆瀛椾綋锛堜笌缂╂斁鏂囧瓧澶у皬涓�鑷达紝11鍙峰瓧浣擄級
+ Font labelFont = new Font("寰蒋闆呴粦", Font.PLAIN, 11);
+ g2d.setFont(labelFont);
+ FontMetrics metrics = g2d.getFontMetrics(labelFont);
+
+ // 璁$畻鏂囧瓧浣嶇疆锛堝湪鍓茶崏鏈哄浘鏍囦笂鏂癸級
+ int textWidth = metrics.stringWidth(speedText);
+ int textHeight = metrics.getHeight();
+ int textX = (int)Math.round(screenPos.x - textWidth / 2.0);
+ // 鍦ㄥ壊鑽夋満鍥炬爣涓婃柟锛岀暀鍑轰竴瀹氶棿璺�
+ // 鍥炬爣鍦ㄤ笘鐣屽潗鏍囩郴涓殑澶у皬绾︿负 48 * 0.8 / scale 绫�
+ // 杞崲涓哄睆骞曞儚绱狅細鍥炬爣楂樺害锛堝儚绱狅級= (48 * 0.8 / scale) * scale = 48 * 0.8 = 38.4 鍍忕礌
+ double iconSizePixels = 48.0 * 0.8; // 鍥炬爣鍦ㄥ睆骞曚笂鐨勫ぇ灏忥紙鍍忕礌锛�
+ int spacing = 8; // 闂磋窛锛堝儚绱狅級
+ int textY = (int)Math.round(screenPos.y - iconSizePixels / 2.0 - spacing - textHeight);
+
+ // 缁樺埗鏂囧瓧鑳屾櫙锛堝崐閫忔槑鐧借壊锛屽寮哄彲璇绘�э級
+ g2d.setColor(new Color(255, 255, 255, 200));
+ g2d.fillRoundRect(textX - 4, textY - metrics.getAscent() - 2, textWidth + 8, textHeight + 4, 4, 4);
+
+ // 缁樺埗鏂囧瓧
+ g2d.setColor(new Color(46, 139, 87)); // 浣跨敤涓婚缁胯壊
+ g2d.drawString(speedText, textX, textY);
+
+ // 鎭㈠鍙樻崲
+ g2d.setTransform(originalTransform);
+ }
private void drawRealtimeMowingCoverage(Graphics2D g2d) {
if (realtimeMowingTrack == null || realtimeMowingTrack.size() < 2) {
@@ -468,6 +533,91 @@
double effectiveWidth = getEffectiveMowerWidthMeters();
gecaolunjing.draw(g2d, realtimeMowingTrack, effectiveWidth, boundaryPath);
}
+
+ /**
+ * 缁樺埗瀵艰埅棰勮宸插壊鍖哄煙
+ */
+ private void drawNavigationPreviewCoverage(Graphics2D g2d) {
+ if (navigationPreviewTrack == null || navigationPreviewTrack.size() < 2) {
+ return;
+ }
+
+ Path2D.Double boundaryPath = currentBoundaryPath;
+ // 鑾峰彇瀵艰埅棰勮鐨勫壊鑽夊搴︼紙浠巇aohangyulan鑾峰彇锛�
+ double previewWidth = getNavigationPreviewWidth();
+ if (previewWidth <= 0) {
+ previewWidth = 0.5; // 榛樿50鍘樼背
+ }
+ gecaolunjing.draw(g2d, navigationPreviewTrack, previewWidth, boundaryPath);
+ }
+
+ /**
+ * 璁剧疆瀵艰埅棰勮杞ㄨ抗
+ */
+ public void setNavigationPreviewTrack(List<Point2D.Double> track) {
+ if (track == null) {
+ navigationPreviewTrack.clear();
+ } else {
+ navigationPreviewTrack.clear();
+ navigationPreviewTrack.addAll(track);
+ }
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+
+ /**
+ * 娣诲姞瀵艰埅棰勮杞ㄨ抗鐐�
+ */
+ public void addNavigationPreviewTrackPoint(Point2D.Double point) {
+ if (point != null && Double.isFinite(point.x) && Double.isFinite(point.y)) {
+ navigationPreviewTrack.add(new Point2D.Double(point.x, point.y));
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+ }
+
+ /**
+ * 娓呴櫎瀵艰埅棰勮杞ㄨ抗
+ */
+ public void clearNavigationPreviewTrack() {
+ navigationPreviewTrack.clear();
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+
+ private double navigationPreviewWidth = 0.5; // 瀵艰埅棰勮鐨勫壊鑽夊搴︼紙绫筹級
+ private double navigationPreviewSpeed = 0.0; // 瀵艰埅棰勮鐨勫壊鑽夋満閫熷害锛堢背/绉掞級
+
+ /**
+ * 璁剧疆瀵艰埅棰勮鐨勫壊鑽夊搴�
+ */
+ public void setNavigationPreviewWidth(double widthMeters) {
+ navigationPreviewWidth = widthMeters > 0 ? widthMeters : 0.5;
+ }
+
+ /**
+ * 鑾峰彇瀵艰埅棰勮鐨勫壊鑽夊搴�
+ */
+ private double getNavigationPreviewWidth() {
+ return navigationPreviewWidth;
+ }
+
+ /**
+ * 璁剧疆瀵艰埅棰勮鐨勫壊鑽夋満閫熷害锛堢背/绉掞級
+ */
+ public void setNavigationPreviewSpeed(double speedMetersPerSecond) {
+ navigationPreviewSpeed = speedMetersPerSecond >= 0 ? speedMetersPerSecond : 0.0;
+ }
+
+ /**
+ * 鑾峰彇瀵艰埅棰勮鐨勫壊鑽夋満閫熷害锛堢背/绉掞級
+ */
+ private double getNavigationPreviewSpeed() {
+ return navigationPreviewSpeed;
+ }
private Path2D.Double getRealtimeBoundaryPath() {
if (realtimeTrackLandNumber == null) {
@@ -1870,10 +2020,11 @@
return;
}
- // 璁剧疆鐐圭殑澶у皬锛堜笌杈圭晫绾垮搴︿竴鑷达級
+ // 璁剧疆鐐圭殑澶у皬锛堣竟鐣岀嚎瀹藉害鐨�2鍊嶏級
// 杈圭晫绾垮搴︼細3 / Math.max(0.5, scale)
double scaleFactor = Math.max(0.5, scale);
- double markerDiameter = 3.0 / scaleFactor; // 涓庤竟鐣岀嚎瀹藉害涓�鑷�
+ double boundaryLineWidth = 3.0 / scaleFactor; // 杈圭晫绾垮搴�
+ double markerDiameter = boundaryLineWidth * 2.0; // 杈圭晫鐐圭洿寰� = 杈圭晫绾垮搴︾殑2鍊�
double markerRadius = markerDiameter / 2.0;
// 璁剧疆瀛椾綋锛堜笌闅滅鐗╁簭鍙蜂竴鑷达紝涓嶉殢缂╂斁鍙樺寲锛�
diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 30c1444..b588d83 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -80,6 +80,8 @@
private JLabel statusLabel;
private JLabel speedLabel; // 閫熷害鏄剧ず鏍囩
private JLabel areaNameLabel;
+ private JLabel drawingBoundaryLabel; // 姝e湪缁樺埗杈圭晫鐘舵�佹爣绛�
+ private JLabel navigationPreviewLabel; // 瀵艰埅棰勮妯″紡鏍囩
// 杈圭晫璀﹀憡鐩稿叧
private Timer boundaryWarningTimer; // 杈圭晫璀﹀憡妫�鏌ュ畾鏃跺櫒
@@ -511,14 +513,28 @@
// 娣诲姞閫熷害鏄剧ず鏍囩
speedLabel = new JLabel("");
- speedLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
- speedLabel.setForeground(Color.GRAY);
- speedLabel.setVisible(false); // 榛樿闅愯棌
+ speedLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+ speedLabel.setForeground(Color.GRAY);
+ speedLabel.setVisible(false); // 榛樿闅愯棌
+
+ // 姝e湪缁樺埗杈圭晫鐘舵�佹爣绛�
+ drawingBoundaryLabel = new JLabel("姝e湪缁樺埗杈圭晫");
+ drawingBoundaryLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+ drawingBoundaryLabel.setForeground(new Color(46, 139, 87));
+ drawingBoundaryLabel.setVisible(false); // 榛樿闅愯棌
+
+ // 瀵艰埅棰勮妯″紡鏍囩
+ navigationPreviewLabel = new JLabel("褰撳墠瀵艰埅棰勮妯″紡");
+ navigationPreviewLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+ navigationPreviewLabel.setForeground(new Color(46, 139, 87));
+ navigationPreviewLabel.setVisible(false); // 榛樿闅愯棌
// 灏嗙姸鎬佷笌閫熷害鏀惧湪鍚屼竴琛岋紝鏄剧ず鍦ㄥ湴鍧楀悕绉颁笅闈竴琛�
JPanel statusRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
statusRow.setOpaque(false);
statusRow.add(statusLabel);
+ statusRow.add(drawingBoundaryLabel);
+ statusRow.add(navigationPreviewLabel);
statusRow.add(speedLabel);
// 宸﹀榻愭爣绛句笌鐘舵�佽锛岀‘淇濆畠浠湪 BoxLayout 涓潬宸︽樉绀�
@@ -1154,7 +1170,7 @@
if (parentWindow != null) {
// 浣跨敤 yaokong 鍖呬腑鐨� RemoteControlDialog 瀹炵幇
remoteDialog = new yaokong.RemoteControlDialog(this, THEME_COLOR, speedLabel);
- } else {
+ } else {/* */
remoteDialog = new yaokong.RemoteControlDialog((JFrame) null, THEME_COLOR, speedLabel);
}
}
@@ -2793,6 +2809,11 @@
circleDialogMode = false;
hideCircleGuidancePanel();
enterDrawingControlMode();
+
+ // 鏄剧ず"姝e湪缁樺埗杈圭晫"鎻愮ず
+ if (drawingBoundaryLabel != null) {
+ drawingBoundaryLabel.setVisible(true);
+ }
boolean enableCircleGuidance = drawingShape != null
&& "circle".equalsIgnoreCase(drawingShape.trim());
@@ -3497,6 +3518,12 @@
activeBoundaryMode = BoundaryCaptureMode.NONE;
}
endDrawingCallback = null;
+
+ // 闅愯棌"姝e湪缁樺埗杈圭晫"鎻愮ず
+ if (drawingBoundaryLabel != null) {
+ drawingBoundaryLabel.setVisible(false);
+ }
+
visualizationPanel.revalidate();
visualizationPanel.repaint();
setHandheldMowerIconActive(false);
@@ -3642,6 +3669,55 @@
return mapRenderer;
}
+ /**
+ * 鑾峰彇鎺у埗闈㈡澘锛堢敤浜庡鑸瑙堟椂鏇挎崲鎸夐挳锛�
+ * @return 鎺у埗闈㈡澘
+ */
+ public JPanel getControlPanel() {
+ return controlPanel;
+ }
+
+ /**
+ * 鑾峰彇寮�濮嬫寜閽紙鐢ㄤ簬瀵艰埅棰勮鏃堕殣钘忥級
+ * @return 寮�濮嬫寜閽�
+ */
+ public JButton getStartButton() {
+ return startBtn;
+ }
+
+ /**
+ * 鑾峰彇缁撴潫鎸夐挳锛堢敤浜庡鑸瑙堟椂闅愯棌锛�
+ * @return 缁撴潫鎸夐挳
+ */
+ public JButton getStopButton() {
+ return stopBtn;
+ }
+
+ /**
+ * 璁剧疆瀵艰埅棰勮妯″紡鏍囩鐨勬樉绀虹姸鎬�
+ * @param visible 鏄惁鏄剧ず
+ */
+ public void setNavigationPreviewLabelVisible(boolean visible) {
+ if (navigationPreviewLabel != null) {
+ navigationPreviewLabel.setVisible(visible);
+ }
+ }
+
+ /**
+ * 鑾峰彇鍙鍖栭潰鏉垮疄渚�
+ */
+ public JPanel getVisualizationPanel() {
+ return visualizationPanel;
+ }
+
+ /**
+ * 鑾峰彇涓诲唴瀹归潰鏉垮疄渚嬶紙鐢ㄤ簬娣诲姞娴姩鎸夐挳锛�
+ */
+ public JPanel getMainContentPanel() {
+ return mainContentPanel;
+ }
+
+
public void updateCurrentAreaName(String areaName) {
if (areaNameLabel == null) {
return;
diff --git a/src/zhuye/adddikuaiyulan.java b/src/zhuye/adddikuaiyulan.java
index 3307106..4b382d2 100644
--- a/src/zhuye/adddikuaiyulan.java
+++ b/src/zhuye/adddikuaiyulan.java
@@ -38,37 +38,81 @@
return;
}
- Path2D.Double path = new Path2D.Double();
- boolean started = false;
+ // 杩囨护鏈夋晥鐐�
+ List<Point2D.Double> validPoints = new java.util.ArrayList<>();
for (Point2D.Double point : previewPoints) {
- if (point == null || !Double.isFinite(point.x) || !Double.isFinite(point.y)) {
- continue;
- }
- if (!started) {
- path.moveTo(point.x, point.y);
- started = true;
- } else {
- path.lineTo(point.x, point.y);
+ if (point != null && Double.isFinite(point.x) && Double.isFinite(point.y)) {
+ validPoints.add(point);
}
}
- if (!started) {
+ if (validPoints.isEmpty()) {
return;
}
Stroke originalStroke = g2d.getStroke();
Color originalColor = g2d.getColor();
- if (previewPoints.size() >= 3) {
- path.closePath();
+ // 鍒涘缓濉厖璺緞锛堝鏋滅偣鏁�>=3锛岄渶瑕侀棴鍚堜互濉厖锛�
+ Path2D.Double fillPath = new Path2D.Double();
+ if (validPoints.size() >= 3) {
+ fillPath.moveTo(validPoints.get(0).x, validPoints.get(0).y);
+ for (int i = 1; i < validPoints.size(); i++) {
+ fillPath.lineTo(validPoints.get(i).x, validPoints.get(i).y);
+ }
+ fillPath.closePath();
g2d.setColor(HANDHELD_BOUNDARY_FILL);
- g2d.fill(path);
+ g2d.fill(fillPath);
}
float outlineWidth = 0.1f;
- g2d.setStroke(new BasicStroke(outlineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
- g2d.setColor(HANDHELD_BOUNDARY_BORDER);
- g2d.draw(path);
+
+ if (validPoints.size() >= 3) {
+ // 鐐规暟>=3鏃讹紝闇�瑕佸垎鍒粯鍒跺疄绾垮拰铏氱嚎
+ // 缁樺埗瀹炵嚎閮ㄥ垎锛氫粠璧风偣渚濇杩炴帴鍒板悇涓偣锛堜笉闂悎锛屼笉鍖呮嫭璧风偣鍒扮粓鐐圭殑鐩存帴杩炵嚎锛�
+ Path2D.Double solidPath = new Path2D.Double();
+ solidPath.moveTo(validPoints.get(0).x, validPoints.get(0).y);
+ // 浠庣浜屼釜鐐瑰紑濮嬶紝渚濇杩炴帴鍒版渶鍚庝竴涓偣锛堝舰鎴愪笉闂悎鐨勮矾寰勶級
+ for (int i = 1; i < validPoints.size(); i++) {
+ solidPath.lineTo(validPoints.get(i).x, validPoints.get(i).y);
+ }
+ g2d.setStroke(new BasicStroke(outlineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+ g2d.setColor(HANDHELD_BOUNDARY_BORDER);
+ g2d.draw(solidPath);
+
+ // 鐢ㄨ櫄绾跨粯鍒惰捣鐐瑰埌缁堢偣鐨勮繛绾匡紙闂悎绾挎锛�
+ Point2D.Double startPoint = validPoints.get(0);
+ Point2D.Double endPoint = validPoints.get(validPoints.size() - 1);
+
+ // 鍒涘缓铏氱嚎鏍峰紡锛堟牴鎹缉鏀捐皟鏁磋櫄绾挎ā寮忥級
+ double effectiveScale = Math.max(0.01d, scale);
+ float dashLength = (float) (0.05 / effectiveScale); // 铏氱嚎闀垮害闅忕缉鏀捐皟鏁�
+ float[] dashPattern = new float[]{dashLength, dashLength}; // 铏氱嚎妯″紡锛氬疄绾裤�佺┖鐧�
+ BasicStroke dashedStroke = new BasicStroke(
+ outlineWidth,
+ BasicStroke.CAP_ROUND,
+ BasicStroke.JOIN_ROUND,
+ 1.0f,
+ dashPattern,
+ 0.0f
+ );
+
+ g2d.setStroke(dashedStroke);
+ g2d.setColor(HANDHELD_BOUNDARY_BORDER);
+ // 浣跨敤Path2D缁樺埗璧风偣鍒扮粓鐐圭殑铏氱嚎锛屼互渚挎敮鎸佹诞鐐瑰潗鏍�
+ Path2D.Double dashedLine = new Path2D.Double();
+ dashedLine.moveTo(startPoint.x, startPoint.y);
+ dashedLine.lineTo(endPoint.x, endPoint.y);
+ g2d.draw(dashedLine);
+ } else if (validPoints.size() == 2) {
+ // 濡傛灉鍙湁2涓偣锛岀洿鎺ョ粯鍒跺疄绾�
+ Path2D.Double simplePath = new Path2D.Double();
+ simplePath.moveTo(validPoints.get(0).x, validPoints.get(0).y);
+ simplePath.lineTo(validPoints.get(1).x, validPoints.get(1).y);
+ g2d.setStroke(new BasicStroke(outlineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+ g2d.setColor(HANDHELD_BOUNDARY_BORDER);
+ g2d.draw(simplePath);
+ }
if (cachedMarkerPixelDiameter <= 0.0d) {
double previousPixelDiameter = Math.abs(BASE_WORLD_MARKER_SIZE * scale);
diff --git a/src/zhuye/bianjiedrwa.java b/src/zhuye/bianjiedrwa.java
index 1e3bb46..b40ee71 100644
--- a/src/zhuye/bianjiedrwa.java
+++ b/src/zhuye/bianjiedrwa.java
@@ -23,24 +23,42 @@
return; // 鏃犳暟鎹繑鍥�
} // if缁撴潫
- Path2D path = new Path2D.Double(); // 鍒涘缓璺緞
+ float strokeWidth = (float) (3 / Math.max(0.5, scale)); // 璁$畻杈圭嚎瀹藉害
+
+ // 濉厖鍖哄煙
+ Path2D fillPath = new Path2D.Double(); // 鍒涘缓濉厖璺緞
boolean first = true; // 棣栫偣鏍囪
for (Point2D.Double point : boundary) { // 閬嶅巻鐐�
if (first) { // 棣栦釜鐐�
- path.moveTo(point.x, point.y); // 绉诲姩鍒板紑濮嬬偣
+ fillPath.moveTo(point.x, point.y); // 绉诲姩鍒板紑濮嬬偣
first = false; // 鏇存柊鏍囪
} else { // 鍏朵粬鐐�
- path.lineTo(point.x, point.y); // 杩炵嚎鍒颁笅涓偣
+ fillPath.lineTo(point.x, point.y); // 杩炵嚎鍒颁笅涓偣
} // if缁撴潫
} // for缁撴潫
- path.closePath(); // 闂悎璺緞
-
- float strokeWidth = (float) (3 / Math.max(0.5, scale)); // 璁$畻杈圭嚎瀹藉害
- g2d.setStroke(new BasicStroke(strokeWidth)); // 璁剧疆鎻忚竟
+ fillPath.closePath(); // 闂悎璺緞
+
g2d.setColor(fillColor); // 璁剧疆濉厖鑹�
- g2d.fill(path); // 濉厖鍖哄煙
+ g2d.fill(fillPath); // 濉厖鍖哄煙
- g2d.setColor(borderColor); // 璁剧疆杈圭嚎棰滆壊
- g2d.draw(path); // 缁樺埗杈圭晫
+ // 缁樺埗杈圭晫绾匡紙鍖呮嫭璧风偣鍒扮粓鐐圭殑杩炴帴锛�- 瀹炵嚎
+ if (boundary.size() >= 2) {
+ Path2D.Double borderPath = new Path2D.Double(); // 鍒涘缓杈圭晫璺緞
+ Point2D.Double firstPoint = boundary.get(0);
+ borderPath.moveTo(firstPoint.x, firstPoint.y); // 绉诲姩鍒拌捣鐐�
+ for (int i = 1; i < boundary.size(); i++) { // 浠庣浜屼釜鐐瑰埌鏈�鍚庝竴涓偣
+ Point2D.Double point = boundary.get(i);
+ borderPath.lineTo(point.x, point.y); // 杩炵嚎
+ }
+ // 濡傛灉鏈�鍚庝竴涓偣涓嶆槸绗竴涓偣锛屽垯杩炴帴鍒拌捣鐐瑰舰鎴愰棴鍚�
+ Point2D.Double lastPoint = boundary.get(boundary.size() - 1);
+ if (!lastPoint.equals(firstPoint)) {
+ borderPath.lineTo(firstPoint.x, firstPoint.y); // 杩炴帴鍒拌捣鐐瑰舰鎴愰棴鍚�
+ }
+
+ g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // 璁剧疆瀹炵嚎鎻忚竟
+ g2d.setColor(borderColor); // 璁剧疆杈圭嚎棰滆壊
+ g2d.draw(borderPath); // 缁樺埗瀹屾暣杈圭晫锛堝寘鎷捣鐐瑰埌缁堢偣鐨勮繛鎺ワ級
+ }
} // 鏂规硶缁撴潫
} // 绫荤粨鏉�
diff --git a/src/zhuye/celiangmoshi.java b/src/zhuye/celiangmoshi.java
index 0bf8154..992c3a7 100644
--- a/src/zhuye/celiangmoshi.java
+++ b/src/zhuye/celiangmoshi.java
@@ -81,3 +81,8 @@
measurementPoints.clear();
}
}
+
+
+
+
+
diff --git a/src/zhuye/pointandnumber.java b/src/zhuye/pointandnumber.java
index c716159..582aca5 100644
--- a/src/zhuye/pointandnumber.java
+++ b/src/zhuye/pointandnumber.java
@@ -34,8 +34,14 @@
}
double scaleFactor = Math.max(0.5, scale); // 闃叉杩囧皬缂╂斁
- // 杈圭晫鐐圭洿寰勪笌杈圭晫绾垮搴︿竴鑷达細3 / Math.max(0.5, scale)
- double markerDiameter = 3.0 / scaleFactor; // 鎻忕偣鐩村緞锛堜笌杈圭晫绾垮搴︿竴鑷达級
+ // 杈圭晫绾垮搴︼細3 / Math.max(0.5, scale)
+ // 杈圭晫鐐圭洿寰� = 杈圭晫绾垮搴︾殑2鍊�
+ double boundaryLineWidth = 3.0 / scaleFactor; // 杈圭晫绾垮搴�
+ double markerDiameter = boundaryLineWidth * 2.0; // 鎻忕偣鐩村緞锛堣竟鐣岀嚎瀹藉害鐨�2鍊嶏級
+ // 搴旂敤鐩村緞缂╂斁鍥犲瓙
+ if (diameterScale > 0.0 && Double.isFinite(diameterScale)) {
+ markerDiameter *= diameterScale;
+ }
double markerRadius = markerDiameter / 2.0; // 鍗婂緞
for (int i = 0; i < effectiveCount; i++) { // 閬嶅巻鏈夋晥鐐�
--
Gitblit v1.10.0