@fleet-frontend/mower-maps 0.0.9-beta.1 → 0.0.9-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/config/constants.d.ts +3 -25
  2. package/dist/config/constants.d.ts.map +1 -1
  3. package/dist/config/styles.d.ts +2 -2
  4. package/dist/config/styles.d.ts.map +1 -1
  5. package/dist/index.d.ts +2 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.esm.js +950 -638
  8. package/dist/index.js +949 -637
  9. package/dist/processor/MapDataProcessor.d.ts +1 -1
  10. package/dist/processor/MapDataProcessor.d.ts.map +1 -1
  11. package/dist/processor/PathDataProcessor.d.ts +2 -21
  12. package/dist/processor/PathDataProcessor.d.ts.map +1 -1
  13. package/dist/processor/builder/AntennaDataBuilder.d.ts +2 -1
  14. package/dist/processor/builder/AntennaDataBuilder.d.ts.map +1 -1
  15. package/dist/processor/builder/BoundaryDataBuilder.d.ts +1 -1
  16. package/dist/processor/builder/BoundaryDataBuilder.d.ts.map +1 -1
  17. package/dist/processor/builder/ChannelDataBuilder.d.ts +1 -1
  18. package/dist/processor/builder/ChannelDataBuilder.d.ts.map +1 -1
  19. package/dist/processor/builder/ChargingPileDataBuilder.d.ts +1 -1
  20. package/dist/processor/builder/ChargingPileDataBuilder.d.ts.map +1 -1
  21. package/dist/processor/builder/ObstacleDataBuilder.d.ts +2 -1
  22. package/dist/processor/builder/ObstacleDataBuilder.d.ts.map +1 -1
  23. package/dist/processor/builder/PathDataBuilder.d.ts +1 -1
  24. package/dist/processor/builder/PathDataBuilder.d.ts.map +1 -1
  25. package/dist/processor/builder/PointDataBuilder.d.ts +2 -1
  26. package/dist/processor/builder/PointDataBuilder.d.ts.map +1 -1
  27. package/dist/processor/builder/SvgElementDataBuilder.d.ts +1 -1
  28. package/dist/processor/builder/SvgElementDataBuilder.d.ts.map +1 -1
  29. package/dist/processor/builder/VisionOffDataBuilder.d.ts +2 -1
  30. package/dist/processor/builder/VisionOffDataBuilder.d.ts.map +1 -1
  31. package/dist/processor/index.d.ts +2 -1
  32. package/dist/processor/index.d.ts.map +1 -1
  33. package/dist/render/AntennaManager.d.ts +2 -24
  34. package/dist/render/AntennaManager.d.ts.map +1 -1
  35. package/dist/render/BoundaryLabelsManager.d.ts +8 -22
  36. package/dist/render/BoundaryLabelsManager.d.ts.map +1 -1
  37. package/dist/render/ChargingPileManager.d.ts +3 -18
  38. package/dist/render/ChargingPileManager.d.ts.map +1 -1
  39. package/dist/render/MowerMapOverlay.d.ts +9 -3
  40. package/dist/render/MowerMapOverlay.d.ts.map +1 -1
  41. package/dist/render/MowerMapRenderer.d.ts.map +1 -1
  42. package/dist/render/MowerPositionManager.d.ts +3 -6
  43. package/dist/render/MowerPositionManager.d.ts.map +1 -1
  44. package/dist/render/SvgMapView.d.ts +0 -25
  45. package/dist/render/SvgMapView.d.ts.map +1 -1
  46. package/dist/render/layers/BoundaryBorderLayer.d.ts +6 -1
  47. package/dist/render/layers/BoundaryBorderLayer.d.ts.map +1 -1
  48. package/dist/render/layers/ChannelLayer.d.ts.map +1 -1
  49. package/dist/render/layers/DrawLayer.d.ts +1 -1
  50. package/dist/render/layers/DrawLayer.d.ts.map +1 -1
  51. package/dist/render/layers/PathLayer.d.ts +10 -2
  52. package/dist/render/layers/PathLayer.d.ts.map +1 -1
  53. package/dist/render/layers/SvgElementLayer.d.ts.map +1 -1
  54. package/dist/render/layers/index.d.ts +0 -1
  55. package/dist/render/layers/index.d.ts.map +1 -1
  56. package/dist/render/layers/types.d.ts +1 -38
  57. package/dist/render/layers/types.d.ts.map +1 -1
  58. package/dist/store/processMowingState.d.ts +1 -5
  59. package/dist/store/processMowingState.d.ts.map +1 -1
  60. package/dist/store/useSubBoundaryBorderStore.d.ts +9 -5
  61. package/dist/store/useSubBoundaryBorderStore.d.ts.map +1 -1
  62. package/dist/types/constants.d.ts +38 -0
  63. package/dist/types/constants.d.ts.map +1 -0
  64. package/dist/types/index.d.ts +6 -0
  65. package/dist/types/index.d.ts.map +1 -1
  66. package/dist/types/layers.d.ts +50 -0
  67. package/dist/types/layers.d.ts.map +1 -0
  68. package/dist/types/processor.d.ts +26 -0
  69. package/dist/types/processor.d.ts.map +1 -0
  70. package/dist/types/realTime.d.ts +11 -1
  71. package/dist/types/realTime.d.ts.map +1 -1
  72. package/dist/types/renderer.d.ts +8 -4
  73. package/dist/types/renderer.d.ts.map +1 -1
  74. package/dist/types/store.d.ts +22 -0
  75. package/dist/types/store.d.ts.map +1 -0
  76. package/dist/types/utils.d.ts +102 -0
  77. package/dist/types/utils.d.ts.map +1 -0
  78. package/dist/utils/boundaryUtils.d.ts +1 -29
  79. package/dist/utils/boundaryUtils.d.ts.map +1 -1
  80. package/dist/utils/common.d.ts +11 -5
  81. package/dist/utils/common.d.ts.map +1 -1
  82. package/dist/utils/coordinates.d.ts +1 -7
  83. package/dist/utils/coordinates.d.ts.map +1 -1
  84. package/dist/utils/handleRealTime.d.ts +2 -7
  85. package/dist/utils/handleRealTime.d.ts.map +1 -1
  86. package/dist/utils/mapBounds.d.ts +1 -10
  87. package/dist/utils/mapBounds.d.ts.map +1 -1
  88. package/dist/utils/math.d.ts +3 -10
  89. package/dist/utils/math.d.ts.map +1 -1
  90. package/dist/utils/mower.d.ts +4 -4
  91. package/dist/utils/mower.d.ts.map +1 -1
  92. package/dist/utils/pathSegments.d.ts +1 -23
  93. package/dist/utils/pathSegments.d.ts.map +1 -1
  94. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3,6 +3,136 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
5
 
6
+ /**
7
+ * 常量和枚举类型定义
8
+ */
9
+ /**
10
+ * 机器人状态枚举
11
+ */
12
+ var RobotStatus;
13
+ (function (RobotStatus) {
14
+ RobotStatus[RobotStatus["PARKED"] = 1] = "PARKED";
15
+ RobotStatus[RobotStatus["CHARGING"] = 2] = "CHARGING";
16
+ RobotStatus[RobotStatus["STANDBY"] = 3] = "STANDBY";
17
+ RobotStatus[RobotStatus["MOWING"] = 4] = "MOWING";
18
+ RobotStatus[RobotStatus["WORKING"] = 5] = "WORKING";
19
+ RobotStatus[RobotStatus["MAPPING"] = 6] = "MAPPING";
20
+ RobotStatus[RobotStatus["ERROR"] = 7] = "ERROR";
21
+ RobotStatus[RobotStatus["UPGRADING"] = 8] = "UPGRADING";
22
+ RobotStatus[RobotStatus["DISCONNECTED"] = 9] = "DISCONNECTED";
23
+ RobotStatus[RobotStatus["UNKNOWN"] = -1] = "UNKNOWN";
24
+ RobotStatus[RobotStatus["TASK_DELAY"] = 10] = "TASK_DELAY";
25
+ })(RobotStatus || (RobotStatus = {}));
26
+ /**
27
+ * RTK状态枚举
28
+ */
29
+ var RTK_STATE;
30
+ (function (RTK_STATE) {
31
+ RTK_STATE[RTK_STATE["LOW_RTK"] = 1] = "LOW_RTK";
32
+ RTK_STATE[RTK_STATE["MIDDLE_RTK"] = 2] = "MIDDLE_RTK";
33
+ RTK_STATE[RTK_STATE["HIGH_RTK"] = 3] = "HIGH_RTK";
34
+ RTK_STATE[RTK_STATE["NO_POSTURE"] = 10] = "NO_POSTURE";
35
+ RTK_STATE[RTK_STATE["OUT_OF_RANGE"] = 11] = "OUT_OF_RANGE";
36
+ RTK_STATE[RTK_STATE["OFF_LINE"] = 19] = "OFF_LINE";
37
+ })(RTK_STATE || (RTK_STATE = {}));
38
+ /**
39
+ * 实时数据类型枚举
40
+ */
41
+ var REAL_TIME_DATA_TYPE;
42
+ (function (REAL_TIME_DATA_TYPE) {
43
+ REAL_TIME_DATA_TYPE[REAL_TIME_DATA_TYPE["LOCATION"] = 1] = "LOCATION";
44
+ REAL_TIME_DATA_TYPE[REAL_TIME_DATA_TYPE["PROCESS"] = 2] = "PROCESS";
45
+ })(REAL_TIME_DATA_TYPE || (REAL_TIME_DATA_TYPE = {}));
46
+
47
+ /**
48
+ * 地图渲染相关常量配置
49
+ */
50
+ /**
51
+ * 缩放因子 - 将米转换为像素
52
+ * 与Python代码中的SVG比例一致
53
+ */
54
+ const SCALE_FACTOR = 50; // 50像素/米
55
+ /**
56
+ * 默认线宽设置
57
+ */
58
+ const DEFAULT_LINE_WIDTHS = {
59
+ OBSTACLE: 2,
60
+ CHARGING_PILE: 2,
61
+ CHANNEL: 2,
62
+ PATH: 20,
63
+ VISION_OFF_AREA: 2,
64
+ TIME_LIMIT_OBSTACLE: 1,
65
+ };
66
+ /**
67
+ * 默认透明度设置
68
+ */
69
+ const DEFAULT_OPACITIES = {
70
+ FULL: 1.0,
71
+ HIGH: 0.7,
72
+ MEDIUM: 0.6,
73
+ DOODLE: 0.8};
74
+ /**
75
+ * 默认半径设置
76
+ */
77
+ const DEFAULT_RADII = {
78
+ CHARGING_PILE: 12};
79
+ /**
80
+ * 图层等级
81
+ */
82
+ const LAYER_LEVELS = {
83
+ BOUNDARY: 2,
84
+ BOUNDARY_BORDER: 4};
85
+ /**
86
+ * 图层默认id
87
+ */
88
+ const LAYER_DEFAULT_TYPE = {
89
+ CHANNEL: 'channel',
90
+ BOUNDARY: 'boundary',
91
+ PATH: 'path',
92
+ BOUNDARY_BORDER: 'boundary_border',
93
+ OBSTACLE: 'obstacle',
94
+ CHARGING_PILE: 'charging_pile',
95
+ POINT: 'point',
96
+ SVG: 'svg',
97
+ VISION_OFF_AREA: 'vision_off_area',
98
+ ANTENNA: 'antenna',
99
+ };
100
+ const ISOLATED_BOUNDARY_SVG = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
101
+ <g opacity="0.6">
102
+ <rect width="24" height="24" rx="12" fill="#1E1E1F" fill-opacity="0.5"/>
103
+ <path d="M8.20573 12.4961C6.739 11.1707 4.85775 11.2284 3.60938 11.1707C6.17744 13.0156 6.05584 15.7887 5.67404 16.9446H14.327C13.837 13.1673 15.5321 10.1289 16.4408 9.08187C15.0342 9.46118 13.5794 10.8303 13.0278 11.4674C12.8102 8.94572 13.992 5.97489 14.61 4.80469C12.5235 5.92214 11.0501 8.26056 10.4938 9.56261C9.22803 7.5947 6.83894 7.17806 5.883 6.9432C8.20573 10.2373 8.00039 11.1707 8.20573 12.4961Z" fill="white"/>
104
+ <mask id="path-3-outside-1_9822_43516" maskUnits="userSpaceOnUse" x="13.2344" y="7.20545" width="12.0208" height="12.0208" fill="black">
105
+ <rect fill="white" x="13.2344" y="7.20545" width="12.0208" height="12.0208"/>
106
+ <path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z"/>
107
+ </mask>
108
+ <path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z" fill="white"/>
109
+ <path d="M20.6852 11.1208L21.7458 10.0601L21.7459 10.0601L20.6852 11.1208ZM17.8568 11.1208L16.7961 10.0601L16.7962 10.0601L17.8568 11.1208ZM17.2733 11.7043L16.2126 10.6437L16.2126 10.6436L17.2733 11.7043ZM16.8631 13.9319L17.9238 14.9926L16.381 16.5354L15.493 14.5425L16.8631 13.9319ZM17.5916 13.2034L19.0855 13.0678L19.149 13.7674L18.6523 14.2641L17.5916 13.2034ZM20.4056 12.1421L21.4662 11.0814L21.4665 11.0817L20.4056 12.1421ZM18.9699 14.5348L17.9093 13.4741L18.3741 13.0093L19.0309 13.036L18.9699 14.5348ZM18.2235 15.2812L17.6298 16.6587L15.5997 15.7837L17.1628 14.2206L18.2235 15.2812ZM20.429 14.86L21.4897 15.9207L21.4897 15.9207L20.429 14.86ZM21.0125 14.2765L22.0732 15.3371L22.0732 15.3372L21.0125 14.2765ZM20.6852 11.1208L19.6246 12.1815C19.4294 11.9862 19.1127 11.9863 18.9174 12.1815L17.8568 11.1208L16.7962 10.0601C18.163 8.69337 20.379 8.6934 21.7458 10.0601L20.6852 11.1208ZM17.8568 11.1208L18.9175 12.1814L18.334 12.7649L17.2733 11.7043L16.2126 10.6436L16.7961 10.0601L17.8568 11.1208ZM17.2733 11.7043L18.334 12.7649C18.1877 12.9112 18.1488 13.1318 18.2333 13.3214L16.8631 13.9319L15.493 14.5425C14.9228 13.2629 15.1537 11.7026 16.2126 10.6437L17.2733 11.7043ZM16.8631 13.9319L15.8025 12.8713L16.531 12.1428L17.5916 13.2034L18.6523 14.2641L17.9238 14.9926L16.8631 13.9319ZM17.5916 13.2034L16.0978 13.339C16.0334 12.6302 16.2727 11.8914 16.8196 11.3445L17.8803 12.4052L18.9409 13.4658C19.051 13.3558 19.098 13.206 19.0855 13.0678L17.5916 13.2034ZM17.8803 12.4052L16.8196 11.3445L17.5067 10.6574L18.5674 11.7181L19.628 12.7788L18.9409 13.4658L17.8803 12.4052ZM18.5674 11.7181L17.5067 10.6574C18.483 9.68112 20.0659 9.68112 21.0422 10.6574L19.9816 11.7181L18.9209 12.7788C19.1162 12.974 19.4328 12.974 19.628 12.7788L18.5674 11.7181ZM19.9816 11.7181L21.0422 10.6574L21.4662 11.0814L20.4056 12.1421L19.3449 13.2027L18.9209 12.7788L19.9816 11.7181ZM20.4056 12.1421L21.4665 11.0817C22.4419 12.0577 22.4428 13.6404 21.4662 14.617L20.4056 13.5563L19.3449 12.4956C19.1493 12.6913 19.1498 13.0076 19.3446 13.2024L20.4056 12.1421ZM20.4056 13.5563L21.4662 14.617L20.7791 15.304L19.7185 14.2434L18.6578 13.1827L19.3449 12.4956L20.4056 13.5563ZM19.7185 14.2434L20.7791 15.304C20.2628 15.8204 19.5762 16.0607 18.909 16.0335L18.9699 14.5348L19.0309 13.036C18.9025 13.0308 18.7629 13.0777 18.6578 13.1827L19.7185 14.2434ZM18.9699 14.5348L20.0306 15.5954L19.2841 16.3419L18.2235 15.2812L17.1628 14.2206L17.9093 13.4741L18.9699 14.5348ZM18.2235 15.2812L18.8172 13.9037C19.0042 13.9843 19.2223 13.9455 19.3684 13.7993L20.429 14.86L21.4897 15.9207C20.4427 16.9676 18.9034 17.2077 17.6298 16.6587L18.2235 15.2812ZM20.429 14.86L19.3684 13.7994L19.9519 13.2159L21.0125 14.2765L22.0732 15.3372L21.4897 15.9207L20.429 14.86ZM21.0125 14.2765L19.9518 13.2159C20.1471 13.0206 20.1471 12.704 19.9519 12.5088L21.0125 11.4481L22.0732 10.3874C23.4401 11.7543 23.4399 13.9703 22.0732 15.3371L21.0125 14.2765ZM21.0125 11.4481L19.9519 12.5088L19.6246 12.1814L20.6852 11.1208L21.7459 10.0601L22.0732 10.3874L21.0125 11.4481Z" fill="#8E8E8F" mask="url(#path-3-outside-1_9822_43516)"/>
110
+ <mask id="path-5-outside-2_9822_43516" maskUnits="userSpaceOnUse" x="9.22265" y="10.388" width="12.7279" height="12.7279" fill="black">
111
+ <rect fill="white" x="9.22265" y="10.388" width="12.7279" height="12.7279"/>
112
+ <path d="M16.1708 14.5077C15.4353 14.1778 14.5422 14.3133 13.9383 14.9172L13.3548 15.5007C12.574 16.2818 12.5738 17.5481 13.3548 18.3291L13.6821 18.6564C14.4631 19.4374 15.7295 19.4373 16.5105 18.6564L17.094 18.0729C17.6895 17.4775 17.8299 16.6003 17.5173 15.8708L16.7743 16.6138C16.7828 16.8803 16.6863 17.1494 16.4829 17.3527L15.7952 18.0405C15.4046 18.4309 14.7714 18.431 14.3809 18.0405L13.957 17.6165C13.5667 17.226 13.5666 16.5927 13.957 16.2023L14.6447 15.5145C14.8654 15.2939 15.1639 15.1979 15.452 15.2266L16.1708 14.5077Z"/>
113
+ </mask>
114
+ <path d="M16.1708 14.5077C15.4353 14.1778 14.5422 14.3133 13.9383 14.9172L13.3548 15.5007C12.574 16.2818 12.5738 17.5481 13.3548 18.3291L13.6821 18.6564C14.4631 19.4374 15.7295 19.4373 16.5105 18.6564L17.094 18.0729C17.6895 17.4775 17.8299 16.6003 17.5173 15.8708L16.7743 16.6138C16.7828 16.8803 16.6863 17.1494 16.4829 17.3527L15.7952 18.0405C15.4046 18.4309 14.7714 18.431 14.3809 18.0405L13.957 17.6165C13.5667 17.226 13.5666 16.5927 13.957 16.2023L14.6447 15.5145C14.8654 15.2939 15.1639 15.1979 15.452 15.2266L16.1708 14.5077Z" fill="white"/>
115
+ <path d="M16.1708 14.5077L16.7847 13.1391L18.7701 14.0297L17.2315 15.5684L16.1708 14.5077ZM13.3548 15.5007L12.294 14.4402L12.2941 14.44L13.3548 15.5007ZM16.5105 18.6564L17.5712 19.7171L17.5711 19.7172L16.5105 18.6564ZM17.5173 15.8708L16.4567 14.8102L18.0236 13.2433L18.8962 15.2802L17.5173 15.8708ZM16.7743 16.6138L15.2751 16.6613L15.2545 16.0123L15.7137 15.5532L16.7743 16.6138ZM15.7952 18.0405L16.8558 19.1011L16.8556 19.1013L15.7952 18.0405ZM13.957 17.6165L12.8963 18.6772L12.8959 18.6767L13.957 17.6165ZM13.957 16.2023L12.8961 15.1418L12.8963 15.1416L13.957 16.2023ZM15.452 15.2266L16.5126 16.2872L16.0102 16.7896L15.3032 16.7192L15.452 15.2266ZM16.1708 14.5077L15.5569 15.8763C15.3688 15.792 15.1469 15.8299 14.999 15.9779L13.9383 14.9172L12.8776 13.8565C13.9375 12.7967 15.5018 12.5636 16.7847 13.1391L16.1708 14.5077ZM13.9383 14.9172L14.999 15.9779L14.4155 16.5614L13.3548 15.5007L12.2941 14.44L12.8776 13.8565L13.9383 14.9172ZM13.3548 15.5007L14.4156 16.5612C14.2202 16.7567 14.2204 17.0734 14.4155 17.2685L13.3548 18.3291L12.2941 19.3898C10.9272 18.0229 10.9277 15.8069 12.294 14.4402L13.3548 15.5007ZM13.3548 18.3291L14.4155 17.2685L14.7428 17.5958L13.6821 18.6564L12.6215 19.7171L12.2941 19.3898L13.3548 18.3291ZM13.6821 18.6564L14.7428 17.5958C14.9378 17.7908 15.2546 17.791 15.45 17.5956L16.5105 18.6564L17.5711 19.7172C16.2044 21.0836 13.9884 21.084 12.6215 19.7171L13.6821 18.6564ZM16.5105 18.6564L15.4499 17.5958L16.0334 17.0123L17.094 18.0729L18.1547 19.1336L17.5712 19.7171L16.5105 18.6564ZM17.094 18.0729L16.0334 17.0123C16.1795 16.8662 16.2185 16.6481 16.1385 16.4615L17.5173 15.8708L18.8962 15.2802C19.4413 16.5526 19.1996 18.0887 18.1547 19.1336L17.094 18.0729ZM17.5173 15.8708L18.578 16.9315L17.835 17.6745L16.7743 16.6138L15.7137 15.5532L16.4567 14.8102L17.5173 15.8708ZM16.7743 16.6138L18.2736 16.5664C18.2945 17.226 18.0544 17.9026 17.5436 18.4134L16.4829 17.3527L15.4223 16.2921C15.3182 16.3961 15.2711 16.5346 15.2751 16.6613L16.7743 16.6138ZM16.4829 17.3527L17.5436 18.4134L16.8558 19.1011L15.7952 18.0405L14.7345 16.9798L15.4223 16.2921L16.4829 17.3527ZM15.7952 18.0405L16.8556 19.1013C15.8795 20.0771 14.2967 20.0776 13.3203 19.1011L14.3809 18.0405L15.4416 16.9798C15.2461 16.7844 14.9297 16.7847 14.7347 16.9797L15.7952 18.0405ZM14.3809 18.0405L13.3203 19.1011L12.8963 18.6772L13.957 17.6165L15.0176 16.5558L15.4416 16.9798L14.3809 18.0405ZM13.957 17.6165L12.8959 18.6767C11.9207 17.7008 11.92 16.1182 12.8961 15.1418L13.957 16.2023L15.0178 17.2628C15.2133 17.0672 15.2128 16.7512 15.018 16.5563L13.957 17.6165ZM13.957 16.2023L12.8963 15.1416L13.5841 14.4539L14.6447 15.5145L15.7054 16.5752L15.0176 17.2629L13.957 16.2023ZM14.6447 15.5145L13.5841 14.4539C14.1364 13.9015 14.8848 13.6626 15.6007 13.734L15.452 15.2266L15.3032 16.7192C15.443 16.7331 15.5943 16.6862 15.7054 16.5752L14.6447 15.5145ZM15.452 15.2266L14.3913 14.1659L15.1101 13.4471L16.1708 14.5077L17.2315 15.5684L16.5126 16.2872L15.452 15.2266Z" fill="#8E8E8F" mask="url(#path-5-outside-2_9822_43516)"/>
116
+ <path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z" fill="white"/>
117
+ <rect width="2.59942" height="2.97264" rx="1" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 21.1133 12.8486)" fill="#8E8E8F"/>
118
+ <rect width="1.91578" height="4.24403" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 19.6992 13.8066)" fill="#8E8E8F"/>
119
+ <rect width="2.59942" height="2.97264" rx="1" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 17.3125 16.6455)" fill="#8E8E8F"/>
120
+ </g>
121
+ </svg>`;
122
+ /**
123
+ * 遍历割草任务,下述四个字段可以在路径中唯一确定遍历的位置(当前区域、当前块、当前行、在当前行上的路程)
124
+ */
125
+ const ACTION_BLOCK_COVER = 5;
126
+ /**
127
+ * 遍历割草块转移任务(在同一区域中的一个块转移到下一个块),下述四个字段可以在路径中唯一确定转移的位置(当前区域、前置块、当前转移路径线序号、在当前线上的路程)
128
+ */
129
+ const ACTION_BLOCK_TRANSFER = 6;
130
+ /**
131
+ * 边界割草任务(割草任务内部的巡边任务)
132
+ */
133
+ const ACTION_BOUNDARY_TASK = 8;
134
+ const SVG_MAP_VIEW_ID = 'fleet-maps-svg-map-view';
135
+
6
136
  /**
7
137
  * SVG基础MapView
8
138
  * 使用真正的矢量SVG渲染替代Canvas位图渲染
@@ -16,7 +146,6 @@ class SvgMapView {
16
146
  this.lineScale = 1; // 线条缩放系数
17
147
  // 状态标志
18
148
  this.destroyed = false;
19
- this.showScale = false;
20
149
  // 渲染系统 - 移除节流以确保用户操作的实时响应
21
150
  // 拖动功能
22
151
  this.isDragging = false;
@@ -59,6 +188,7 @@ class SvgMapView {
59
188
  this.svg.setAttribute('shape-rendering', 'geometricPrecision');
60
189
  this.svg.setAttribute('text-rendering', 'geometricPrecision');
61
190
  this.svg.setAttribute('image-rendering', 'optimizeQuality');
191
+ this.svg.setAttribute('id', SVG_MAP_VIEW_ID);
62
192
  }
63
193
  /**
64
194
  * 创建SVG组元素
@@ -83,7 +213,6 @@ class SvgMapView {
83
213
  this.layers.sort((a, b) => a.getLevel() - b.getLevel());
84
214
  }
85
215
  getLayer(type) {
86
- console.log('getLayer----->', this.layers, type);
87
216
  return this.layers.find((layer) => layer.getType() === type) || null;
88
217
  }
89
218
  /**
@@ -121,7 +250,7 @@ class SvgMapView {
121
250
  * 设置自适应视图变换 - 让SVG刚好包裹住图形
122
251
  */
123
252
  fitToView(bounds) {
124
- const padding = 10; // 添加一些边距以避免内容贴边
253
+ const padding = 20; // 添加一些边距以避免内容贴边
125
254
  const boundWidth = bounds.maxX - bounds.minX;
126
255
  const boundHeight = bounds.maxY - bounds.minY;
127
256
  // 防止宽高为0的情况
@@ -144,8 +273,8 @@ class SvgMapView {
144
273
  this.viewBox = {
145
274
  x: bounds.minX - padding,
146
275
  y: bounds.minY - padding,
147
- width: boundWidth + padding,
148
- height: boundHeight + padding,
276
+ width: boundWidth + padding * 2,
277
+ height: boundHeight + padding * 2,
149
278
  };
150
279
  // 根据宽高比选择合适的preserveAspectRatio设置
151
280
  if (Math.abs(contentAspectRatio - containerAspectRatio) < 0.01) {
@@ -157,42 +286,6 @@ class SvgMapView {
157
286
  this.svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
158
287
  }
159
288
  this.updateViewBox();
160
- this.refresh();
161
- }
162
- /**
163
- * 重置变换
164
- */
165
- resetTransform() {
166
- this.scale = 1;
167
- // 重置viewBox到默认状态
168
- const containerRect = this.container.getBoundingClientRect();
169
- this.viewBox = {
170
- x: 0,
171
- y: 0,
172
- width: containerRect.width,
173
- height: containerRect.height,
174
- };
175
- this.updateViewBox();
176
- this.refresh();
177
- }
178
- /**
179
- * 设置缩放级别
180
- */
181
- setZoom(zoomLevel) {
182
- if (zoomLevel <= 0)
183
- return;
184
- const oldScale = this.scale;
185
- this.scale = zoomLevel;
186
- // 调整viewBox以实现缩放
187
- const scaleFactor = oldScale / this.scale;
188
- const centerX = this.viewBox.x + this.viewBox.width / 2;
189
- const centerY = this.viewBox.y + this.viewBox.height / 2;
190
- this.viewBox.width *= scaleFactor;
191
- this.viewBox.height *= scaleFactor;
192
- this.viewBox.x = centerX - this.viewBox.width / 2;
193
- this.viewBox.y = centerY - this.viewBox.height / 2;
194
- this.updateViewBox();
195
- this.refresh();
196
289
  }
197
290
  /**
198
291
  * 获取当前缩放级别
@@ -200,15 +293,6 @@ class SvgMapView {
200
293
  getZoom() {
201
294
  return this.scale;
202
295
  }
203
- /**
204
- * 设置线条缩放系数
205
- */
206
- setLineScale(lineScale) {
207
- if (lineScale <= 0)
208
- return;
209
- this.lineScale = lineScale;
210
- this.refresh();
211
- }
212
296
  /**
213
297
  * 获取当前线条缩放系数
214
298
  */
@@ -312,7 +396,7 @@ class SvgMapView {
312
396
  // const layerType = layerIdParts.slice(1, -1).join('-'); // 处理类型名中可能包含连字符的情况
313
397
  // const layerLevel = parseInt(layerIdParts[layerIdParts.length - 1]);
314
398
  // // 查找对应的图层对象
315
- // const targetLayer = this.layers.find(layer =>
399
+ // const targetLayer = this.layers.find(layer =>
316
400
  // layer.getType() === layerType && layer.getLevel() === layerLevel
317
401
  // );
318
402
  // if (targetLayer && targetLayer.isVisible()) {
@@ -349,12 +433,6 @@ class SvgMapView {
349
433
  return;
350
434
  this.render();
351
435
  }
352
- /**
353
- * 重新初始化SVG(用于容器大小变化)
354
- */
355
- reinitializeSVG() {
356
- this.refresh();
357
- }
358
436
  // ==================== 拖拽功能 ====================
359
437
  /**
360
438
  * 设置拖拽事件处理器
@@ -479,22 +557,6 @@ class SvgMapView {
479
557
  return null;
480
558
  }
481
559
  }
482
- /**
483
- * 自动适配viewBox到实际内容
484
- */
485
- autoFitToContent() {
486
- if (this.destroyed || this.layers.length === 0)
487
- return;
488
- const bounds = this.getLayersGroupBounds();
489
- if (bounds) {
490
- this.fitToView({
491
- minX: bounds.x,
492
- minY: bounds.y,
493
- maxX: bounds.x + bounds.width,
494
- maxY: bounds.y + bounds.height,
495
- });
496
- }
497
- }
498
560
  /**
499
561
  * 获取ViewBox信息
500
562
  */
@@ -515,13 +577,6 @@ class SvgMapView {
515
577
  * 诊断SVG尺寸信息
516
578
  */
517
579
  diagnosticSizeInfo() { }
518
- /**
519
- * 设置是否显示比例尺
520
- */
521
- setShowScale(show) {
522
- this.showScale = show;
523
- this.refresh();
524
- }
525
580
  /**
526
581
  * 获取SVG元素
527
582
  */
@@ -723,8 +778,6 @@ const create = (createState) => createState ? createImpl(createState) : createIm
723
778
 
724
779
  const useSubBoundaryBorderStore = create((set, get) => ({
725
780
  subBoundaryBorder: {},
726
- // 覆盖所有数据
727
- setSubBoundaryBorder: (subBoundaryBorder) => set({ subBoundaryBorder }),
728
781
  // 追加单个数据
729
782
  addSubBoundaryBorder: (key, element) => set((state) => ({
730
783
  subBoundaryBorder: {
@@ -732,134 +785,28 @@ const useSubBoundaryBorderStore = create((set, get) => ({
732
785
  [key]: element,
733
786
  },
734
787
  })),
735
- // 追加多个数据
736
- addMultipleSubBoundaryBorders: (borders) => set((state) => ({
737
- subBoundaryBorder: {
738
- ...state.subBoundaryBorder,
739
- ...borders,
740
- },
741
- })),
742
788
  // 清空所有数据
743
789
  clearSubBoundaryBorder: () => set({ subBoundaryBorder: {} }),
790
+ // 障碍物
791
+ obstacles: {},
792
+ addObstacles: (key, element) => set((state) => ({
793
+ obstacles: {
794
+ ...state.obstacles,
795
+ [key]: element,
796
+ },
797
+ })),
798
+ clearObstacles: () => set({ obstacles: {} }),
799
+ // svg数据
800
+ svgElements: {},
801
+ addSvgElements: (key, element) => set((state) => ({
802
+ svgElements: {
803
+ ...state.svgElements,
804
+ [key]: element,
805
+ },
806
+ })),
807
+ clearSvgElements: () => set({ svgElements: {} }),
744
808
  }));
745
809
 
746
- /**
747
- * 地图渲染相关常量配置
748
- */
749
- /**
750
- * 缩放因子 - 将米转换为像素
751
- * 与Python代码中的SVG比例一致
752
- */
753
- const SCALE_FACTOR = 50; // 50像素/米
754
- /**
755
- * 默认线宽设置
756
- */
757
- const DEFAULT_LINE_WIDTHS = {
758
- OBSTACLE: 2,
759
- CHARGING_PILE: 2,
760
- CHANNEL: 2,
761
- PATH: 20,
762
- VISION_OFF_AREA: 2,
763
- TIME_LIMIT_OBSTACLE: 1,
764
- };
765
- /**
766
- * 默认透明度设置
767
- */
768
- const DEFAULT_OPACITIES = {
769
- FULL: 1.0,
770
- HIGH: 0.7,
771
- LOW: 0.4};
772
- /**
773
- * 默认半径设置
774
- */
775
- const DEFAULT_RADII = {
776
- CHARGING_PILE: 12};
777
- /**
778
- * 图层等级
779
- */
780
- const LAYER_LEVELS = {
781
- BOUNDARY: 2,
782
- BOUNDARY_BORDER: 4};
783
- /**
784
- * 图层默认id
785
- */
786
- const LAYER_DEFAULT_TYPE = {
787
- CHANNEL: 'channel',
788
- BOUNDARY: 'boundary',
789
- PATH: 'path',
790
- BOUNDARY_BORDER: 'boundary_border',
791
- OBSTACLE: 'obstacle',
792
- CHARGING_PILE: 'charging_pile',
793
- POINT: 'point',
794
- SVG: 'svg',
795
- VISION_OFF_AREA: 'vision_off_area',
796
- ANTENNA: 'antenna',
797
- };
798
- var RobotStatus;
799
- (function (RobotStatus) {
800
- RobotStatus[RobotStatus["PARKED"] = 1] = "PARKED";
801
- RobotStatus[RobotStatus["CHARGING"] = 2] = "CHARGING";
802
- RobotStatus[RobotStatus["STANDBY"] = 3] = "STANDBY";
803
- RobotStatus[RobotStatus["MOWING"] = 4] = "MOWING";
804
- RobotStatus[RobotStatus["WORKING"] = 5] = "WORKING";
805
- RobotStatus[RobotStatus["MAPPING"] = 6] = "MAPPING";
806
- RobotStatus[RobotStatus["ERROR"] = 7] = "ERROR";
807
- RobotStatus[RobotStatus["UPGRADING"] = 8] = "UPGRADING";
808
- RobotStatus[RobotStatus["DISCONNECTED"] = 9] = "DISCONNECTED";
809
- RobotStatus[RobotStatus["UNKNOWN"] = -1] = "UNKNOWN";
810
- RobotStatus[RobotStatus["TASK_DELAY"] = 10] = "TASK_DELAY";
811
- // WAITING = 'Waiting',
812
- })(RobotStatus || (RobotStatus = {}));
813
- // RTK状态
814
- var RTK_STATE;
815
- (function (RTK_STATE) {
816
- RTK_STATE[RTK_STATE["LOW_RTK"] = 1] = "LOW_RTK";
817
- RTK_STATE[RTK_STATE["MIDDLE_RTK"] = 2] = "MIDDLE_RTK";
818
- RTK_STATE[RTK_STATE["HIGH_RTK"] = 3] = "HIGH_RTK";
819
- RTK_STATE[RTK_STATE["NO_POSTURE"] = 10] = "NO_POSTURE";
820
- RTK_STATE[RTK_STATE["OUT_OF_RANGE"] = 11] = "OUT_OF_RANGE";
821
- RTK_STATE[RTK_STATE["OFF_LINE"] = 19] = "OFF_LINE";
822
- })(RTK_STATE || (RTK_STATE = {}));
823
- var REAL_TIME_DATA_TYPE;
824
- (function (REAL_TIME_DATA_TYPE) {
825
- REAL_TIME_DATA_TYPE[REAL_TIME_DATA_TYPE["LOCATION"] = 1] = "LOCATION";
826
- REAL_TIME_DATA_TYPE[REAL_TIME_DATA_TYPE["PROCESS"] = 2] = "PROCESS";
827
- })(REAL_TIME_DATA_TYPE || (REAL_TIME_DATA_TYPE = {}));
828
- const ISOLATED_BOUNDARY_SVG = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
829
- <g opacity="0.6">
830
- <rect width="24" height="24" rx="12" fill="#1E1E1F" fill-opacity="0.5"/>
831
- <path d="M8.20573 12.4961C6.739 11.1707 4.85775 11.2284 3.60938 11.1707C6.17744 13.0156 6.05584 15.7887 5.67404 16.9446H14.327C13.837 13.1673 15.5321 10.1289 16.4408 9.08187C15.0342 9.46118 13.5794 10.8303 13.0278 11.4674C12.8102 8.94572 13.992 5.97489 14.61 4.80469C12.5235 5.92214 11.0501 8.26056 10.4938 9.56261C9.22803 7.5947 6.83894 7.17806 5.883 6.9432C8.20573 10.2373 8.00039 11.1707 8.20573 12.4961Z" fill="white"/>
832
- <mask id="path-3-outside-1_9822_43516" maskUnits="userSpaceOnUse" x="13.2344" y="7.20545" width="12.0208" height="12.0208" fill="black">
833
- <rect fill="white" x="13.2344" y="7.20545" width="12.0208" height="12.0208"/>
834
- <path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z"/>
835
- </mask>
836
- <path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z" fill="white"/>
837
- <path d="M20.6852 11.1208L21.7458 10.0601L21.7459 10.0601L20.6852 11.1208ZM17.8568 11.1208L16.7961 10.0601L16.7962 10.0601L17.8568 11.1208ZM17.2733 11.7043L16.2126 10.6437L16.2126 10.6436L17.2733 11.7043ZM16.8631 13.9319L17.9238 14.9926L16.381 16.5354L15.493 14.5425L16.8631 13.9319ZM17.5916 13.2034L19.0855 13.0678L19.149 13.7674L18.6523 14.2641L17.5916 13.2034ZM20.4056 12.1421L21.4662 11.0814L21.4665 11.0817L20.4056 12.1421ZM18.9699 14.5348L17.9093 13.4741L18.3741 13.0093L19.0309 13.036L18.9699 14.5348ZM18.2235 15.2812L17.6298 16.6587L15.5997 15.7837L17.1628 14.2206L18.2235 15.2812ZM20.429 14.86L21.4897 15.9207L21.4897 15.9207L20.429 14.86ZM21.0125 14.2765L22.0732 15.3371L22.0732 15.3372L21.0125 14.2765ZM20.6852 11.1208L19.6246 12.1815C19.4294 11.9862 19.1127 11.9863 18.9174 12.1815L17.8568 11.1208L16.7962 10.0601C18.163 8.69337 20.379 8.6934 21.7458 10.0601L20.6852 11.1208ZM17.8568 11.1208L18.9175 12.1814L18.334 12.7649L17.2733 11.7043L16.2126 10.6436L16.7961 10.0601L17.8568 11.1208ZM17.2733 11.7043L18.334 12.7649C18.1877 12.9112 18.1488 13.1318 18.2333 13.3214L16.8631 13.9319L15.493 14.5425C14.9228 13.2629 15.1537 11.7026 16.2126 10.6437L17.2733 11.7043ZM16.8631 13.9319L15.8025 12.8713L16.531 12.1428L17.5916 13.2034L18.6523 14.2641L17.9238 14.9926L16.8631 13.9319ZM17.5916 13.2034L16.0978 13.339C16.0334 12.6302 16.2727 11.8914 16.8196 11.3445L17.8803 12.4052L18.9409 13.4658C19.051 13.3558 19.098 13.206 19.0855 13.0678L17.5916 13.2034ZM17.8803 12.4052L16.8196 11.3445L17.5067 10.6574L18.5674 11.7181L19.628 12.7788L18.9409 13.4658L17.8803 12.4052ZM18.5674 11.7181L17.5067 10.6574C18.483 9.68112 20.0659 9.68112 21.0422 10.6574L19.9816 11.7181L18.9209 12.7788C19.1162 12.974 19.4328 12.974 19.628 12.7788L18.5674 11.7181ZM19.9816 11.7181L21.0422 10.6574L21.4662 11.0814L20.4056 12.1421L19.3449 13.2027L18.9209 12.7788L19.9816 11.7181ZM20.4056 12.1421L21.4665 11.0817C22.4419 12.0577 22.4428 13.6404 21.4662 14.617L20.4056 13.5563L19.3449 12.4956C19.1493 12.6913 19.1498 13.0076 19.3446 13.2024L20.4056 12.1421ZM20.4056 13.5563L21.4662 14.617L20.7791 15.304L19.7185 14.2434L18.6578 13.1827L19.3449 12.4956L20.4056 13.5563ZM19.7185 14.2434L20.7791 15.304C20.2628 15.8204 19.5762 16.0607 18.909 16.0335L18.9699 14.5348L19.0309 13.036C18.9025 13.0308 18.7629 13.0777 18.6578 13.1827L19.7185 14.2434ZM18.9699 14.5348L20.0306 15.5954L19.2841 16.3419L18.2235 15.2812L17.1628 14.2206L17.9093 13.4741L18.9699 14.5348ZM18.2235 15.2812L18.8172 13.9037C19.0042 13.9843 19.2223 13.9455 19.3684 13.7993L20.429 14.86L21.4897 15.9207C20.4427 16.9676 18.9034 17.2077 17.6298 16.6587L18.2235 15.2812ZM20.429 14.86L19.3684 13.7994L19.9519 13.2159L21.0125 14.2765L22.0732 15.3372L21.4897 15.9207L20.429 14.86ZM21.0125 14.2765L19.9518 13.2159C20.1471 13.0206 20.1471 12.704 19.9519 12.5088L21.0125 11.4481L22.0732 10.3874C23.4401 11.7543 23.4399 13.9703 22.0732 15.3371L21.0125 14.2765ZM21.0125 11.4481L19.9519 12.5088L19.6246 12.1814L20.6852 11.1208L21.7459 10.0601L22.0732 10.3874L21.0125 11.4481Z" fill="#8E8E8F" mask="url(#path-3-outside-1_9822_43516)"/>
838
- <mask id="path-5-outside-2_9822_43516" maskUnits="userSpaceOnUse" x="9.22265" y="10.388" width="12.7279" height="12.7279" fill="black">
839
- <rect fill="white" x="9.22265" y="10.388" width="12.7279" height="12.7279"/>
840
- <path d="M16.1708 14.5077C15.4353 14.1778 14.5422 14.3133 13.9383 14.9172L13.3548 15.5007C12.574 16.2818 12.5738 17.5481 13.3548 18.3291L13.6821 18.6564C14.4631 19.4374 15.7295 19.4373 16.5105 18.6564L17.094 18.0729C17.6895 17.4775 17.8299 16.6003 17.5173 15.8708L16.7743 16.6138C16.7828 16.8803 16.6863 17.1494 16.4829 17.3527L15.7952 18.0405C15.4046 18.4309 14.7714 18.431 14.3809 18.0405L13.957 17.6165C13.5667 17.226 13.5666 16.5927 13.957 16.2023L14.6447 15.5145C14.8654 15.2939 15.1639 15.1979 15.452 15.2266L16.1708 14.5077Z"/>
841
- </mask>
842
- <path d="M16.1708 14.5077C15.4353 14.1778 14.5422 14.3133 13.9383 14.9172L13.3548 15.5007C12.574 16.2818 12.5738 17.5481 13.3548 18.3291L13.6821 18.6564C14.4631 19.4374 15.7295 19.4373 16.5105 18.6564L17.094 18.0729C17.6895 17.4775 17.8299 16.6003 17.5173 15.8708L16.7743 16.6138C16.7828 16.8803 16.6863 17.1494 16.4829 17.3527L15.7952 18.0405C15.4046 18.4309 14.7714 18.431 14.3809 18.0405L13.957 17.6165C13.5667 17.226 13.5666 16.5927 13.957 16.2023L14.6447 15.5145C14.8654 15.2939 15.1639 15.1979 15.452 15.2266L16.1708 14.5077Z" fill="white"/>
843
- <path d="M16.1708 14.5077L16.7847 13.1391L18.7701 14.0297L17.2315 15.5684L16.1708 14.5077ZM13.3548 15.5007L12.294 14.4402L12.2941 14.44L13.3548 15.5007ZM16.5105 18.6564L17.5712 19.7171L17.5711 19.7172L16.5105 18.6564ZM17.5173 15.8708L16.4567 14.8102L18.0236 13.2433L18.8962 15.2802L17.5173 15.8708ZM16.7743 16.6138L15.2751 16.6613L15.2545 16.0123L15.7137 15.5532L16.7743 16.6138ZM15.7952 18.0405L16.8558 19.1011L16.8556 19.1013L15.7952 18.0405ZM13.957 17.6165L12.8963 18.6772L12.8959 18.6767L13.957 17.6165ZM13.957 16.2023L12.8961 15.1418L12.8963 15.1416L13.957 16.2023ZM15.452 15.2266L16.5126 16.2872L16.0102 16.7896L15.3032 16.7192L15.452 15.2266ZM16.1708 14.5077L15.5569 15.8763C15.3688 15.792 15.1469 15.8299 14.999 15.9779L13.9383 14.9172L12.8776 13.8565C13.9375 12.7967 15.5018 12.5636 16.7847 13.1391L16.1708 14.5077ZM13.9383 14.9172L14.999 15.9779L14.4155 16.5614L13.3548 15.5007L12.2941 14.44L12.8776 13.8565L13.9383 14.9172ZM13.3548 15.5007L14.4156 16.5612C14.2202 16.7567 14.2204 17.0734 14.4155 17.2685L13.3548 18.3291L12.2941 19.3898C10.9272 18.0229 10.9277 15.8069 12.294 14.4402L13.3548 15.5007ZM13.3548 18.3291L14.4155 17.2685L14.7428 17.5958L13.6821 18.6564L12.6215 19.7171L12.2941 19.3898L13.3548 18.3291ZM13.6821 18.6564L14.7428 17.5958C14.9378 17.7908 15.2546 17.791 15.45 17.5956L16.5105 18.6564L17.5711 19.7172C16.2044 21.0836 13.9884 21.084 12.6215 19.7171L13.6821 18.6564ZM16.5105 18.6564L15.4499 17.5958L16.0334 17.0123L17.094 18.0729L18.1547 19.1336L17.5712 19.7171L16.5105 18.6564ZM17.094 18.0729L16.0334 17.0123C16.1795 16.8662 16.2185 16.6481 16.1385 16.4615L17.5173 15.8708L18.8962 15.2802C19.4413 16.5526 19.1996 18.0887 18.1547 19.1336L17.094 18.0729ZM17.5173 15.8708L18.578 16.9315L17.835 17.6745L16.7743 16.6138L15.7137 15.5532L16.4567 14.8102L17.5173 15.8708ZM16.7743 16.6138L18.2736 16.5664C18.2945 17.226 18.0544 17.9026 17.5436 18.4134L16.4829 17.3527L15.4223 16.2921C15.3182 16.3961 15.2711 16.5346 15.2751 16.6613L16.7743 16.6138ZM16.4829 17.3527L17.5436 18.4134L16.8558 19.1011L15.7952 18.0405L14.7345 16.9798L15.4223 16.2921L16.4829 17.3527ZM15.7952 18.0405L16.8556 19.1013C15.8795 20.0771 14.2967 20.0776 13.3203 19.1011L14.3809 18.0405L15.4416 16.9798C15.2461 16.7844 14.9297 16.7847 14.7347 16.9797L15.7952 18.0405ZM14.3809 18.0405L13.3203 19.1011L12.8963 18.6772L13.957 17.6165L15.0176 16.5558L15.4416 16.9798L14.3809 18.0405ZM13.957 17.6165L12.8959 18.6767C11.9207 17.7008 11.92 16.1182 12.8961 15.1418L13.957 16.2023L15.0178 17.2628C15.2133 17.0672 15.2128 16.7512 15.018 16.5563L13.957 17.6165ZM13.957 16.2023L12.8963 15.1416L13.5841 14.4539L14.6447 15.5145L15.7054 16.5752L15.0176 17.2629L13.957 16.2023ZM14.6447 15.5145L13.5841 14.4539C14.1364 13.9015 14.8848 13.6626 15.6007 13.734L15.452 15.2266L15.3032 16.7192C15.443 16.7331 15.5943 16.6862 15.7054 16.5752L14.6447 15.5145ZM15.452 15.2266L14.3913 14.1659L15.1101 13.4471L16.1708 14.5077L17.2315 15.5684L16.5126 16.2872L15.452 15.2266Z" fill="#8E8E8F" mask="url(#path-5-outside-2_9822_43516)"/>
844
- <path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z" fill="white"/>
845
- <rect width="2.59942" height="2.97264" rx="1" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 21.1133 12.8486)" fill="#8E8E8F"/>
846
- <rect width="1.91578" height="4.24403" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 19.6992 13.8066)" fill="#8E8E8F"/>
847
- <rect width="2.59942" height="2.97264" rx="1" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 17.3125 16.6455)" fill="#8E8E8F"/>
848
- </g>
849
- </svg>`;
850
- /**
851
- * 遍历割草任务,下述四个字段可以在路径中唯一确定遍历的位置(当前区域、当前块、当前行、在当前行上的路程)
852
- */
853
- const ACTION_BLOCK_COVER = 5;
854
- /**
855
- * 遍历割草块转移任务(在同一区域中的一个块转移到下一个块),下述四个字段可以在路径中唯一确定转移的位置(当前区域、前置块、当前转移路径线序号、在当前线上的路程)
856
- */
857
- const ACTION_BLOCK_TRANSFER = 6;
858
- /**
859
- * 边界割草任务(割草任务内部的巡边任务)
860
- */
861
- const ACTION_BOUNDARY_TASK = 8;
862
-
863
810
  /**
864
811
  * 路径图层
865
812
  * 专门处理路径元素的渲染
@@ -910,51 +857,90 @@ class ChannelLayer extends BaseLayer {
910
857
  }
911
858
  // �� 修改:计算包含所有分区和通道的边界框
912
859
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
913
- // 1. 先计算所有分区的边界
914
- for (const partitionId in subBoundaryBorder) {
915
- const boundaryData = subBoundaryBorder[partitionId];
916
- if (boundaryData && boundaryData.coordinates && boundaryData.coordinates.length > 0) {
917
- for (const coord of boundaryData.coordinates) {
918
- minX = Math.min(minX, coord[0]);
919
- minY = Math.min(minY, coord[1]);
920
- maxX = Math.max(maxX, coord[0]);
921
- maxY = Math.max(maxY, coord[1]);
860
+ // 1. 先计算所有分区的边界,如果能拿到边界的svg的大小,就使用这个如果拿不到,就根据分区去计算
861
+ const svg = document.getElementById(SVG_MAP_VIEW_ID);
862
+ if (svg && svg instanceof SVGSVGElement && svg.viewBox) {
863
+ const viewBox = svg.viewBox.baseVal;
864
+ minX = viewBox.x;
865
+ minY = viewBox.y;
866
+ maxX = viewBox.x + viewBox.width;
867
+ maxY = viewBox.y + viewBox.height;
868
+ }
869
+ else {
870
+ for (const partitionId in subBoundaryBorder) {
871
+ const boundaryData = subBoundaryBorder[partitionId];
872
+ if (boundaryData && boundaryData.coordinates && boundaryData.coordinates.length > 0) {
873
+ for (const coord of boundaryData.coordinates) {
874
+ minX = Math.min(minX, coord[0]);
875
+ minY = Math.min(minY, coord[1]);
876
+ maxX = Math.max(maxX, coord[0]);
877
+ maxY = Math.max(maxY, coord[1]);
878
+ }
922
879
  }
923
880
  }
924
881
  }
925
882
  // 2. 再计算所有通道的边界
926
883
  for (const element of this.elements) {
927
- const tunnelConnection = element.originalData?.connection;
928
- if (tunnelConnection && Array.isArray(tunnelConnection)) {
929
- const clipPathId = `channel-exclude-${element.originalData?.id || Math.random().toString(36).substr(2, 9)}`;
930
- // 检查是否已存在该 clipPath
931
- const existingClipPath = defs.querySelector(`#${clipPathId}`);
932
- if (existingClipPath)
933
- continue;
934
- // 创建 clipPath
935
- const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
936
- clipPath.setAttribute('id', clipPathId);
937
- clipPath.setAttribute('clipPathUnits', 'userSpaceOnUse');
938
- // === 合成一个 path ===
939
- let d = `M ${minX} ${minY} L ${maxX} ${minY} L ${maxX} ${maxY} L ${minX} ${maxY} Z`;
940
- for (const partitionId of tunnelConnection) {
941
- const boundaryData = subBoundaryBorder[partitionId];
942
- if (boundaryData && boundaryData.coordinates.length >= 3) {
943
- d += ` M ${boundaryData.coordinates[0][0]} ${boundaryData.coordinates[0][1]}`;
944
- for (let i = 1; i < boundaryData.coordinates.length; i++) {
945
- d += ` L ${boundaryData.coordinates[i][0]} ${boundaryData.coordinates[i][1]}`;
946
- }
947
- d += ' Z';
884
+ // const tunnelConnection = element.originalData?.connection;
885
+ // if (tunnelConnection && Array.isArray(tunnelConnection)) {
886
+ // const clipPathId = `channel-exclude-${
887
+ // element.originalData?.id || Math.random().toString(36).substr(2, 9)
888
+ // }`;
889
+ // // 检查是否已存在该 clipPath
890
+ // const existingClipPath = defs.querySelector(`#${clipPathId}`);
891
+ // if (existingClipPath) continue;
892
+ // // 创建 clipPath
893
+ // const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
894
+ // clipPath.setAttribute('id', clipPathId);
895
+ // clipPath.setAttribute('clipPathUnits', 'userSpaceOnUse');
896
+ // // === 合成一个 path ===
897
+ // let d = `M ${minX} ${minY} L ${maxX} ${minY} L ${maxX} ${maxY} L ${minX} ${maxY} Z`;
898
+ // for (const partitionId of tunnelConnection) {
899
+ // const boundaryData = subBoundaryBorder[partitionId];
900
+ // if (boundaryData && boundaryData.coordinates.length >= 3) {
901
+ // d += ` M ${boundaryData.coordinates[0][0]} ${boundaryData.coordinates[0][1]}`;
902
+ // for (let i = 1; i < boundaryData.coordinates.length; i++) {
903
+ // d += ` L ${boundaryData.coordinates[i][0]} ${boundaryData.coordinates[i][1]}`;
904
+ // }
905
+ // d += ' Z';
906
+ // }
907
+ // }
908
+ // const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
909
+ // path.setAttribute('d', d);
910
+ // path.setAttribute('clip-rule', 'evenodd'); // 关键
911
+ // clipPath.appendChild(path);
912
+ // defs.appendChild(clipPath);
913
+ // clipPathIdsMap[element.originalData?.id.toString()] = clipPathId;
914
+ // } else {
915
+ const clipPathId = `channel-exclude-all-${element.originalData?.id || Math.random().toString(36).substr(2, 9)}`;
916
+ // 检查是否已存在该 clipPath
917
+ const existingClipPath = defs.querySelector(`#${clipPathId}`);
918
+ if (existingClipPath)
919
+ continue;
920
+ // 创建 clipPath
921
+ const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
922
+ clipPath.setAttribute('id', clipPathId);
923
+ clipPath.setAttribute('clipPathUnits', 'userSpaceOnUse');
924
+ // === 合成一个 path ===
925
+ let d = `M ${minX} ${minY} L ${maxX} ${minY} L ${maxX} ${maxY} L ${minX} ${maxY} Z`;
926
+ for (const partitionId in subBoundaryBorder) {
927
+ const boundaryData = subBoundaryBorder[partitionId];
928
+ if (boundaryData && boundaryData.coordinates.length >= 3) {
929
+ d += ` M ${boundaryData.coordinates[0][0]} ${boundaryData.coordinates[0][1]}`;
930
+ for (let i = 1; i < boundaryData.coordinates.length; i++) {
931
+ d += ` L ${boundaryData.coordinates[i][0]} ${boundaryData.coordinates[i][1]}`;
948
932
  }
933
+ d += ' Z';
949
934
  }
950
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
951
- path.setAttribute('d', d);
952
- path.setAttribute('clip-rule', 'evenodd'); // 关键
953
- clipPath.appendChild(path);
954
- defs.appendChild(clipPath);
955
- clipPathIdsMap[element.originalData?.id.toString()] = clipPathId;
956
935
  }
936
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
937
+ path.setAttribute('d', d);
938
+ path.setAttribute('clip-rule', 'evenodd'); // 关键
939
+ clipPath.appendChild(path);
940
+ defs.appendChild(clipPath);
941
+ clipPathIdsMap[element.originalData?.id.toString()] = clipPathId;
957
942
  }
943
+ // }
958
944
  return clipPathIdsMap;
959
945
  }
960
946
  /**
@@ -1025,7 +1011,7 @@ class PathLayer extends BaseLayer {
1025
1011
  * 创建所有分区并集的 clipPath
1026
1012
  */
1027
1013
  createUnionClipPath(svgGroup) {
1028
- const { subBoundaryBorder } = useSubBoundaryBorderStore.getState();
1014
+ const { subBoundaryBorder, obstacles, svgElements } = useSubBoundaryBorderStore.getState();
1029
1015
  // 确保 defs 元素存在
1030
1016
  let defs = svgGroup.querySelector('defs');
1031
1017
  if (!defs) {
@@ -1039,7 +1025,7 @@ class PathLayer extends BaseLayer {
1039
1025
  defs.removeChild(existing);
1040
1026
  // 合成所有分区的 path
1041
1027
  let d = '';
1042
- // 合成所有边界
1028
+ // 1. 外圈(主边界,顺时针)
1043
1029
  Object.values(subBoundaryBorder).forEach((item) => {
1044
1030
  const bCoords = item.coordinates;
1045
1031
  if (bCoords.length >= 3) {
@@ -1050,6 +1036,48 @@ class PathLayer extends BaseLayer {
1050
1036
  d += ' Z ';
1051
1037
  }
1052
1038
  });
1039
+ // 2. 内圈(禁区,逆时针)
1040
+ Object.values(obstacles).forEach((item) => {
1041
+ const bCoords = item.coordinates;
1042
+ if (bCoords.length >= 3) {
1043
+ d += `M ${bCoords[bCoords.length - 1][0]} ${bCoords[bCoords.length - 1][1]}`;
1044
+ for (let i = bCoords.length - 2; i >= 0; i--) {
1045
+ d += ` L ${bCoords[i][0]} ${bCoords[i][1]}`;
1046
+ }
1047
+ d += ' Z ';
1048
+ }
1049
+ });
1050
+ // 3. svgElements(解析 SVG 字符串并提取 path 数据)
1051
+ Object.values(svgElements).forEach((svgPath) => {
1052
+ const svgPathString = svgPath?.metadata?.svg;
1053
+ if (svgPathString && typeof svgPathString === 'string' && svgPathString.trim()) {
1054
+ // 处理转义字符
1055
+ const processedSvgString = svgPathString.replace(/\\n/g, '\n').replace(/\\"/g, '"');
1056
+ // 解析 SVG 字符串
1057
+ const parser = new DOMParser();
1058
+ const svgDoc = parser.parseFromString(processedSvgString, 'image/svg+xml');
1059
+ const svgElement = svgDoc.documentElement;
1060
+ if (svgElement.tagName === 'svg') {
1061
+ // 查找 path 元素
1062
+ const pathElement = svgElement.querySelector('path');
1063
+ if (pathElement) {
1064
+ const pathData = pathElement.getAttribute('d');
1065
+ if (pathData) {
1066
+ // 获取 SVG 元素的变换参数
1067
+ const centerCoords = svgPath.coordinates?.[0] || [0, 0];
1068
+ const center = [centerCoords[0], centerCoords[1]];
1069
+ const userScale = svgPath.metadata.scale || 1;
1070
+ const direction = svgPath.metadata?.direction || 0;
1071
+ const originalWidth = parseFloat(svgElement.getAttribute('width') || '76');
1072
+ const originalHeight = parseFloat(svgElement.getAttribute('height') || '68');
1073
+ // 应用变换到路径数据
1074
+ const transformedPathData = this.transformSvgPath(pathData, center, userScale, direction, originalWidth, originalHeight);
1075
+ d += transformedPathData + ' ';
1076
+ }
1077
+ }
1078
+ }
1079
+ }
1080
+ });
1053
1081
  const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1054
1082
  path.setAttribute('d', d);
1055
1083
  const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
@@ -1074,47 +1102,132 @@ class PathLayer extends BaseLayer {
1074
1102
  // 2. 创建一个组,应用 clipPath
1075
1103
  const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
1076
1104
  group.setAttribute('clip-path', `url(#${clipPathId})`);
1077
- group.setAttribute('opacity', '0.35'); // 统一透明度,防止叠加脏乱
1078
- // 3. 渲染所有路径
1105
+ group.setAttribute('opacity', '0.5'); // 统一透明度,防止叠加脏乱
1106
+ // 3. 优化渲染:按样式分组并合并路径
1107
+ this.renderOptimizedPaths(group);
1108
+ svgGroup.appendChild(group);
1109
+ }
1110
+ /**
1111
+ * 优化渲染:按样式分组并合并路径,减少 DOM 节点数量
1112
+ */
1113
+ renderOptimizedPaths(group) {
1114
+ // 按样式分组存储路径数据
1115
+ const styleGroups = new Map();
1116
+ // 收集所有路径数据并按样式分组
1079
1117
  for (const element of this.elements) {
1080
- const { id, elements } = element;
1118
+ // 类型断言:PathLayer 中的 elements 实际上是 PathElements 结构
1119
+ const pathElement = element;
1120
+ const { id, elements } = pathElement;
1081
1121
  this.boundaryPaths[id] = [];
1122
+ elements.forEach((pathElement) => {
1123
+ const { coordinates, style } = pathElement;
1124
+ if (coordinates.length < 2)
1125
+ return;
1126
+ // 生成样式键(用于分组)
1127
+ const styleKey = this.generateStyleKey(style);
1128
+ // 构建路径数据
1129
+ let pathData = `M ${coordinates[0][0]} ${coordinates[0][1]}`;
1130
+ for (let i = 1; i < coordinates.length; i++) {
1131
+ pathData += ` L ${coordinates[i][0]} ${coordinates[i][1]}`;
1132
+ }
1133
+ // 按样式分组存储
1134
+ if (!styleGroups.has(styleKey)) {
1135
+ styleGroups.set(styleKey, { pathData: [], elements: [] });
1136
+ }
1137
+ styleGroups.get(styleKey).pathData.push(pathData);
1138
+ styleGroups.get(styleKey).elements.push(pathElement);
1139
+ });
1140
+ }
1141
+ // 为每种样式创建一个合并的 path 元素
1142
+ styleGroups.forEach((groupData) => {
1143
+ const { pathData, elements } = groupData;
1144
+ if (pathData.length === 0)
1145
+ return;
1146
+ // 使用第一个元素的样式作为该组的样式
1147
+ const firstElement = elements[0];
1148
+ const style = firstElement.style;
1149
+ // 创建合并的 path 元素
1150
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1151
+ // 合并所有路径数据
1152
+ const mergedPathData = pathData.join(' ');
1153
+ path.setAttribute('d', mergedPathData);
1154
+ // 设置样式属性
1155
+ path.setAttribute('fill', 'none');
1156
+ path.setAttribute('stroke', style.lineColor || '#000000');
1157
+ path.setAttribute('mix-blend-mode', 'normal');
1158
+ const lineWidth = Math.max(style.lineWidth || 1, 0.5);
1159
+ path.setAttribute('stroke-width', lineWidth.toString());
1160
+ path.setAttribute('stroke-linecap', 'round');
1161
+ path.setAttribute('stroke-linejoin', 'round');
1162
+ path.classList.add('vector-path');
1163
+ // 将合并的 path 添加到组中
1164
+ group.appendChild(path);
1165
+ // 保存引用到 boundaryPaths 中(保持兼容性)
1082
1166
  elements.forEach((element) => {
1083
- this.renderPathToGroup(group, id, element);
1167
+ const { id } = element;
1168
+ if (!this.boundaryPaths[id]) {
1169
+ this.boundaryPaths[id] = [];
1170
+ }
1171
+ this.boundaryPaths[id].push(path);
1084
1172
  });
1173
+ });
1174
+ }
1175
+ /**
1176
+ * 变换 SVG 路径数据
1177
+ */
1178
+ transformSvgPath(pathData, center, scale, direction, originalWidth, originalHeight) {
1179
+ // 解析路径数据并应用变换
1180
+ const commands = pathData.match(/[MmLlHhVvCcSsQqTtAaZz][^MmLlHhVvCcSsQqTtAaZz]*/g) || [];
1181
+ let transformedCommands = [];
1182
+ for (const command of commands) {
1183
+ const type = command[0];
1184
+ const params = command
1185
+ .slice(1)
1186
+ .trim()
1187
+ .split(/[\s,]+/)
1188
+ .filter(Boolean)
1189
+ .map(Number);
1190
+ if (type === 'Z' || type === 'z') {
1191
+ // 闭合路径,不需要变换
1192
+ transformedCommands.push(command);
1193
+ continue;
1194
+ }
1195
+ // 处理坐标参数
1196
+ let transformedParams = [];
1197
+ for (let i = 0; i < params.length; i += 2) {
1198
+ if (i + 1 < params.length) {
1199
+ let x = params[i];
1200
+ let y = params[i + 1];
1201
+ // 应用变换:先平移到中心,然后缩放、旋转,最后平移到目标位置
1202
+ // 1. 平移到原点(相对于原始尺寸的中心)
1203
+ x -= originalWidth / 2;
1204
+ y -= originalHeight / 2;
1205
+ // 2. 应用缩放
1206
+ x *= scale;
1207
+ y *= scale;
1208
+ // 3. 应用旋转
1209
+ const cos = Math.cos(-direction);
1210
+ const sin = Math.sin(-direction);
1211
+ const newX = x * cos - y * sin;
1212
+ const newY = x * sin + y * cos;
1213
+ // 4. 平移到目标位置
1214
+ x = newX + center[0];
1215
+ y = newY + center[1];
1216
+ transformedParams.push(x, y);
1217
+ }
1218
+ }
1219
+ // 重建命令
1220
+ if (transformedParams.length > 0) {
1221
+ transformedCommands.push(type + transformedParams.join(' '));
1222
+ }
1085
1223
  }
1086
- svgGroup.appendChild(group);
1224
+ return transformedCommands.join(' ');
1087
1225
  }
1088
1226
  /**
1089
- * 渲染单个路径到指定的组中
1227
+ * 生成样式键,用于路径分组
1090
1228
  */
1091
- renderPathToGroup(group, id, element) {
1092
- const { coordinates, style } = element;
1093
- if (coordinates.length < 2)
1094
- return;
1095
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1096
- // 构建路径数据
1097
- let pathData = `M ${coordinates[0][0]} ${coordinates[0][1]}`;
1098
- for (let i = 1; i < coordinates.length; i++) {
1099
- pathData += ` L ${coordinates[i][0]} ${coordinates[i][1]}`;
1100
- }
1101
- path.style.mixBlendMode = 'normal';
1102
- // 设置路径属性
1103
- path.setAttribute('d', pathData);
1104
- // 直接给fill的颜色设置透明度会导致path重叠的部分颜色叠加,所以使用fill填充实色,通过fill-opacity设置透明度
1105
- path.setAttribute('fill', 'none');
1106
- // path.setAttribute('fill-opacity', '0.4');
1107
- path.setAttribute('stroke', style.lineColor || '#000000');
1108
- path.setAttribute('mix-blend-mode', 'normal');
1109
- const lineWidth = Math.max(style.lineWidth || 1, 0.5);
1110
- path.setAttribute('stroke-width', lineWidth.toString());
1111
- path.setAttribute('stroke-linecap', 'round');
1112
- path.setAttribute('stroke-linejoin', 'round');
1113
- // 注意:这里不设置 opacity,因为透明度由父组控制
1114
- // path.setAttribute('vector-effect', 'non-scaling-stroke');
1115
- path.classList.add('vector-path');
1116
- this.boundaryPaths[id].push(path);
1117
- group.appendChild(path);
1229
+ generateStyleKey(style) {
1230
+ return `${style.lineColor || '#000000'}-${style.lineWidth || 1}-${style.opacity || 1}`;
1118
1231
  }
1119
1232
  }
1120
1233
 
@@ -1222,7 +1335,7 @@ class ObstacleLayer extends BaseLayer {
1222
1335
  }
1223
1336
  }
1224
1337
 
1225
- var chargingPileImage = "data:image/png;base64,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";
1338
+ var chargingPileImage = "data:image/png;base64,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";
1226
1339
 
1227
1340
  /**
1228
1341
  * 充电桩图层
@@ -1412,7 +1525,7 @@ class SvgElementLayer extends BaseLayer {
1412
1525
  // 在transformGroup上应用变换:平移到中心,旋转,缩放,然后居中SVG
1413
1526
  const transform = [
1414
1527
  `translate(${center[0]}, ${center[1]})`,
1415
- `rotate(${(direction * 180) / Math.PI})`,
1528
+ `rotate(${-(direction * 180) / Math.PI})`,
1416
1529
  `scale(${userScale})`,
1417
1530
  `translate(${-originalWidth / 2}, ${-originalHeight / 2})`,
1418
1531
  ].join(' ');
@@ -1510,7 +1623,7 @@ class VisionOffLayer extends BaseLayer {
1510
1623
  */
1511
1624
  const BOUNDARY_STYLES = {
1512
1625
  lineColor: '#ffffff',
1513
- fillColor: 'rgba(239, 255, 237, 0.15)', // 更鲜艳的绿色半透明填充,增强可见性
1626
+ fillColor: 'rgba(239, 255, 237, 0.1)', // 更鲜艳的绿色半透明填充,增强可见性
1514
1627
  lineWidth: 2,
1515
1628
  opacity: DEFAULT_OPACITIES.FULL,
1516
1629
  mowingLineColor: 'rgba(99, 216, 174, 1)',
@@ -1538,14 +1651,14 @@ const DOODLE_STYLES = {
1538
1651
  lineColor: '#ff5722',
1539
1652
  fillColor: '#ff9800', // 粉色半透明填充
1540
1653
  lineWidth: DEFAULT_LINE_WIDTHS.TIME_LIMIT_OBSTACLE,
1541
- opacity: DEFAULT_OPACITIES.HIGH,
1654
+ opacity: DEFAULT_OPACITIES.DOODLE,
1542
1655
  };
1543
1656
  const PATH_EDGE_STYLES = {
1544
1657
  lineWidth: DEFAULT_LINE_WIDTHS.PATH,
1545
- opacity: DEFAULT_OPACITIES.LOW,
1546
- edgeLineColor: 'rgba(194, 203, 212)',
1658
+ opacity: DEFAULT_OPACITIES.MEDIUM,
1659
+ edgeLineColor: 'rgba(231, 238, 246)',
1547
1660
  transLineColor: 'transparent',
1548
- mowedLineColor: 'rgba(194, 203, 212)',
1661
+ mowedLineColor: 'rgba(231, 238, 246)',
1549
1662
  mowingLineColor: 'rgba(123, 200, 187)',
1550
1663
  };
1551
1664
  const CHANNEL_STYLES = {
@@ -1614,15 +1727,6 @@ function convertCoordinate(x, y) {
1614
1727
  };
1615
1728
  }
1616
1729
 
1617
- /**
1618
- * 路径段类型
1619
- */
1620
- var PathSegmentType;
1621
- (function (PathSegmentType) {
1622
- PathSegmentType["EDGE"] = "edge";
1623
- PathSegmentType["MOWING"] = "mowing";
1624
- PathSegmentType["TRANS"] = "trans";
1625
- })(PathSegmentType || (PathSegmentType = {}));
1626
1730
  /**
1627
1731
  * 按Python逻辑创建路径段:根据连续的两点之间的关系确定线段类型
1628
1732
  */
@@ -1897,26 +2001,7 @@ function generateBoundaryData(mapData, pathData) {
1897
2001
  }
1898
2002
  // 第一步:收集所有TUNNEL数据的connection信息
1899
2003
  const connectedBoundaryIds = new Set();
1900
- // 1.1 遍历子地图中的TUNNEL元素
1901
- for (const subMap of mapData.sub_maps) {
1902
- if (!subMap.elements)
1903
- continue;
1904
- // 找到该子地图中所有 type 为 TUNNEL 的元素
1905
- const tunnelElements = subMap.elements.filter(element => element.type === 'TUNNEL');
1906
- for (const tunnelElement of tunnelElements) {
1907
- const connection = tunnelElement.connection;
1908
- if (connection) {
1909
- // connection可能是单个数字或数组
1910
- if (Array.isArray(connection)) {
1911
- connection.forEach(id => connectedBoundaryIds.add(id));
1912
- }
1913
- else if (typeof connection === 'number') {
1914
- connectedBoundaryIds.add(connection);
1915
- }
1916
- }
1917
- }
1918
- }
1919
- // 1.2 遍历mapData中的tunnels字段
2004
+ // 遍历mapData中的tunnels字段
1920
2005
  if (mapData.tunnels && Array.isArray(mapData.tunnels)) {
1921
2006
  for (const tunnel of mapData.tunnels) {
1922
2007
  const connection = tunnel.connection;
@@ -1938,6 +2023,8 @@ function generateBoundaryData(mapData, pathData) {
1938
2023
  continue;
1939
2024
  // 每个sub_map的elements是边界坐标,没有sub_map只有一个boundary数据
1940
2025
  const boundaryElement = subMap.elements.find(element => element.type === 'BOUNDARY');
2026
+ // 如果当前subMap存在充电桩且充电桩存在tunnel,说明当前subMap中的boundary是初始boundary,这个boundary不为孤立区域
2027
+ const hasTunnelToChargingPile = subMap.elements.some(element => element.type === 'CHARGING_PILE' && element.tunnel);
1941
2028
  // 创建基础的 boundary 数据(来自 mapData)
1942
2029
  const boundary = {
1943
2030
  // 从 BOUNDARY 元素复制属性
@@ -1947,7 +2034,7 @@ function generateBoundaryData(mapData, pathData) {
1947
2034
  points: convertPointsFormat(boundaryElement?.points) || [],
1948
2035
  type: boundaryElement.type,
1949
2036
  // 判断是否为孤立子区域
1950
- isIsolated: !connectedBoundaryIds.has(boundaryElement.id)
2037
+ isIsolated: hasTunnelToChargingPile ? false : !connectedBoundaryIds.has(boundaryElement.id)
1951
2038
  };
1952
2039
  // 如果有 pathData,尝试匹配对应的分区数据
1953
2040
  if (pathData) {
@@ -1968,6 +2055,14 @@ function generateBoundaryData(mapData, pathData) {
1968
2055
  return boundaryData;
1969
2056
  }
1970
2057
 
2058
+ var RealTimeDataType;
2059
+ (function (RealTimeDataType) {
2060
+ RealTimeDataType[RealTimeDataType["LOCATION"] = 1] = "LOCATION";
2061
+ RealTimeDataType[RealTimeDataType["PROCESS"] = 2] = "PROCESS";
2062
+ RealTimeDataType[RealTimeDataType["PARTITION"] = 3] = "PARTITION";
2063
+ RealTimeDataType[RealTimeDataType["STATUS"] = 4] = "STATUS";
2064
+ })(RealTimeDataType || (RealTimeDataType = {}));
2065
+
1971
2066
  /**
1972
2067
  * 射线法判断点是否在多边形内部
1973
2068
  * @param x 点的x坐标
@@ -2085,7 +2180,6 @@ const handleMultipleRealTimeData = ({ realTimeData, isMowing, pathData, partitio
2085
2180
  // 割草轨迹
2086
2181
  const { postureX, postureY, vehicleState } = item;
2087
2182
  const currentPartitionId = getPartitionId(partitionBoundary, Number(postureX), Number(postureY));
2088
- console.log('currentPartitionId===', currentPartitionId);
2089
2183
  if (currentPartitionId && newPathData?.[currentPartitionId]) {
2090
2184
  const currentPathData = newPathData[currentPartitionId];
2091
2185
  newPathData[currentPartitionId] = {
@@ -2138,7 +2232,7 @@ const getProcessMowingDataFromRealTimeData = ({ realTimeData, isMowing, pathData
2138
2232
  let newMowingStatus = isMowing;
2139
2233
  let newPathData = pathData || {};
2140
2234
  // 找到返回的第一个实时进度的点
2141
- const firstProcessData = realTimeData.find((item) => item.type === REAL_TIME_DATA_TYPE.PROCESS);
2235
+ const firstProcessData = realTimeData.find((item) => item.type === RealTimeDataType.PROCESS);
2142
2236
  if (firstProcessData) {
2143
2237
  // console.log('firstProcessData==', firstProcessData);
2144
2238
  const { action, subAction, currentMowBoundary, currentMowProgress } = firstProcessData;
@@ -2216,10 +2310,10 @@ function getNoPositionMowerImageByModal(mowerModal) {
2216
2310
  }
2217
2311
  return iNoPosition;
2218
2312
  }
2219
- function getMowerImage(positonConfig) {
2313
+ function getMowerImage(positonConfig, modelType) {
2220
2314
  if (!positonConfig)
2221
2315
  return '';
2222
- const model = positonConfig.vehicleModel || '';
2316
+ const model = modelType?.toLowerCase() || 'i';
2223
2317
  const state = positonConfig.vehicleState;
2224
2318
  const mowerImage = getMowerImageByModal(model);
2225
2319
  const disabledImage = getDisabledMowerImageByModal(model);
@@ -4684,7 +4778,37 @@ var merge = createAssigner(function(object, source, srcIndex) {
4684
4778
  * _.round(4060, -2);
4685
4779
  * // => 4100
4686
4780
  */
4687
- var round = createRound('round');
4781
+ var round = createRound('round');
4782
+
4783
+ /**
4784
+ * 工具模块类型定义
4785
+ */
4786
+ /**
4787
+ * 路径段类型枚举
4788
+ */
4789
+ var PathSegmentType;
4790
+ (function (PathSegmentType) {
4791
+ PathSegmentType["EDGE"] = "edge";
4792
+ PathSegmentType["MOWING"] = "mowing";
4793
+ PathSegmentType["TRANS"] = "trans";
4794
+ })(PathSegmentType || (PathSegmentType = {}));
4795
+ /**
4796
+ * 单位类型枚举
4797
+ */
4798
+ var UnitsType;
4799
+ (function (UnitsType) {
4800
+ UnitsType["Metric"] = "Metric";
4801
+ UnitsType["Imperial"] = "Imperial";
4802
+ })(UnitsType || (UnitsType = {}));
4803
+ /**
4804
+ * 面积单位类型枚举
4805
+ */
4806
+ var UnitsAreaType;
4807
+ (function (UnitsAreaType) {
4808
+ UnitsAreaType["SQUARE_METER"] = "m\u00B2";
4809
+ UnitsAreaType["SQUARE_FOOT"] = "ft\u00B2";
4810
+ UnitsAreaType["ACRE"] = "ac";
4811
+ })(UnitsAreaType || (UnitsAreaType = {}));
4688
4812
 
4689
4813
  /**
4690
4814
  * 默认航向相对于canvas的偏移角度: 航向默认是东
@@ -4759,17 +4883,6 @@ function formatNumberWithMetricPrefix(value, round = true, decimals = 2) {
4759
4883
  return `${mathFn(value / 1000000000, decimals)}B`;
4760
4884
  }
4761
4885
  }
4762
- var UnitsType;
4763
- (function (UnitsType) {
4764
- UnitsType["Metric"] = "metric";
4765
- UnitsType["Imperial"] = "imperial";
4766
- })(UnitsType || (UnitsType = {}));
4767
- var UnitsAreaType;
4768
- (function (UnitsAreaType) {
4769
- UnitsAreaType["SQUARE_METER"] = "m\u00B2";
4770
- UnitsAreaType["SQUARE_FOOT"] = "ft\u00B2";
4771
- UnitsAreaType["ACRE"] = "ac";
4772
- })(UnitsAreaType || (UnitsAreaType = {}));
4773
4886
  /**
4774
4887
  * 转换割草面积的方法
4775
4888
  * @param area 面积数值(单位:m²)
@@ -4959,6 +5072,18 @@ class BoundaryBorderLayer extends BaseLayer {
4959
5072
  this.level = LAYER_LEVELS.BOUNDARY_BORDER; // 中等层级
4960
5073
  this.type = LAYER_DEFAULT_TYPE.BOUNDARY_BORDER;
4961
5074
  this.boudaryBorderPaths = {};
5075
+ this.mowingBoundarys = [];
5076
+ }
5077
+ /**
5078
+ * 设置当前割草任务的边界
5079
+ */
5080
+ setMowingBoundarys(mowingBoundarys) {
5081
+ if (!mowingBoundarys) {
5082
+ this.mowingBoundarys = this.elements?.map(item => item?.originalData?.id);
5083
+ }
5084
+ else {
5085
+ this.mowingBoundarys = mowingBoundarys;
5086
+ }
4962
5087
  }
4963
5088
  /**
4964
5089
  * SVG渲染方法
@@ -5004,7 +5129,8 @@ class BoundaryBorderLayer extends BaseLayer {
5004
5129
  * 创建直接路径(type=2)
5005
5130
  */
5006
5131
  createDirectPath(svgGroup, points, style, id) {
5007
- const strokeColor = style.lineColor;
5132
+ const isMowing = this.mowingBoundarys.includes(Number(id));
5133
+ const strokeColor = isMowing ? style.mowingLineColor : style.lineColor;
5008
5134
  const lineWidth = dp2px(style.lineWidth || 3);
5009
5135
  const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
5010
5136
  // 构建路径数据
@@ -5037,7 +5163,8 @@ class BoundaryBorderLayer extends BaseLayer {
5037
5163
  * 使用PathMeasure逻辑创建平行路径(type=1)
5038
5164
  */
5039
5165
  createParallelPathsWithMeasure(svgGroup, points, style, id) {
5040
- const strokeColor = style.lineColor;
5166
+ const isMowing = this.mowingBoundarys.includes(Number(id));
5167
+ const strokeColor = isMowing ? style.mowingLineColor : style.lineColor;
5041
5168
  const lineWidth = dp2px(style.lineWidth || 3);
5042
5169
  // 获取当前SVG的缩放级别,计算固定屏幕像素间距
5043
5170
  const fixedScreenDistance = lineWidth; // 固定的屏幕像素距离
@@ -5383,6 +5510,14 @@ class BoundaryDataBuilder {
5383
5510
  * 创建边界元素数据
5384
5511
  */
5385
5512
  static create(type, coordinates, style) {
5513
+ const len = coordinates?.length || 0;
5514
+ const firstPoint = coordinates?.[0];
5515
+ const lastPoint = coordinates?.[len - 1];
5516
+ const isClosed = firstPoint?.[0] === lastPoint?.[0] && firstPoint?.[1] === lastPoint?.[1];
5517
+ // 如果地图没有闭合,则手动新增闭合点,避免border最后一部分没有闭合的情况
5518
+ if (!isClosed) {
5519
+ coordinates.push([firstPoint?.[0], firstPoint?.[1], lastPoint?.[2]]);
5520
+ }
5386
5521
  return {
5387
5522
  type,
5388
5523
  coordinates,
@@ -5785,8 +5920,13 @@ class MapDataProcessor {
5785
5920
  // 为ObstacleData创建兼容的MapElement接口
5786
5921
  const mapElement = element;
5787
5922
  const obstacleElement = ObstacleDataBuilder.fromMapElement(mapElement, this.mapConfig.obstacle);
5788
- if (obstacleElement)
5923
+ if (obstacleElement) {
5789
5924
  result.push(obstacleElement);
5925
+ const { addObstacles } = useSubBoundaryBorderStore.getState();
5926
+ addObstacles(`obstacle-${obstacleElement.originalData.id}`, {
5927
+ ...obstacleElement,
5928
+ });
5929
+ }
5790
5930
  }
5791
5931
  catch (error) {
5792
5932
  console.warn(`Error processing OBSTACLE element:`, element, error);
@@ -5847,8 +5987,13 @@ class MapDataProcessor {
5847
5987
  element.direction !== undefined) {
5848
5988
  const mapElement = element;
5849
5989
  const svgElement = SvgElementDataBuilder.fromMapElement(mapElement, this.mapConfig.doodle);
5850
- if (svgElement)
5990
+ if (svgElement) {
5851
5991
  result.push(svgElement);
5992
+ const { addSvgElements } = useSubBoundaryBorderStore.getState();
5993
+ addSvgElements(`time-limit-obstacle-${svgElement.originalData.id}`, {
5994
+ ...svgElement,
5995
+ });
5996
+ }
5852
5997
  }
5853
5998
  // 如果有points数据,按传统方式绘制
5854
5999
  else if ('points' in element &&
@@ -5857,8 +6002,13 @@ class MapDataProcessor {
5857
6002
  element.points.length >= 3) {
5858
6003
  const mapElement = element;
5859
6004
  const polygonElement = ObstacleDataBuilder.createTimeLimitObstacle(mapElement, this.mapConfig.obstacle);
5860
- if (polygonElement)
6005
+ if (polygonElement) {
5861
6006
  result.push(polygonElement);
6007
+ const { addObstacles } = useSubBoundaryBorderStore.getState();
6008
+ addObstacles(`time-limit-obstacle-${polygonElement.originalData.id}`, {
6009
+ ...polygonElement,
6010
+ });
6011
+ }
5862
6012
  }
5863
6013
  }
5864
6014
  catch (error) {
@@ -6008,7 +6158,6 @@ class PathDataProcessor {
6008
6158
  elements,
6009
6159
  };
6010
6160
  });
6011
- console.log('result->', result);
6012
6161
  return result;
6013
6162
  }
6014
6163
  }
@@ -6018,7 +6167,7 @@ class PathDataProcessor {
6018
6167
  * 专门处理边界标签的创建、定位和管理
6019
6168
  */
6020
6169
  class BoundaryLabelsManager {
6021
- constructor(svgView, boundaryData) {
6170
+ constructor(svgView, boundaryData, { unitType, language }) {
6022
6171
  this.container = null;
6023
6172
  this.overlayDiv = null;
6024
6173
  this.globalClickHandler = null;
@@ -6029,6 +6178,8 @@ class BoundaryLabelsManager {
6029
6178
  this.svgView = svgView;
6030
6179
  this.boundaryData = boundaryData;
6031
6180
  this.initializeContainer();
6181
+ this.unitType = unitType;
6182
+ this.language = language;
6032
6183
  }
6033
6184
  /**
6034
6185
  * 初始化容器
@@ -6128,9 +6279,10 @@ class BoundaryLabelsManager {
6128
6279
  this.currentExpandedBoundaryId === boundary.id ? 'block' : 'none';
6129
6280
  extendedContent.style.borderTop = '1px solid rgba(255,255,255,0.2)';
6130
6281
  extendedContent.style.paddingTop = '6px';
6282
+ console.log('this.unitType->', this.unitType);
6131
6283
  // 面积信息
6132
- const totalArea = convertAreaByUnits(boundary.area || 0, 'metric');
6133
- const finishedArea = convertAreaByUnits(boundary.finishedArea || 0, 'metric');
6284
+ const totalArea = convertAreaByUnits(boundary.area || 0, this.unitType);
6285
+ const finishedArea = convertAreaByUnits(boundary.finishedArea || 0, this.unitType);
6134
6286
  const coverageText = `Coverage: ${finishedArea.value}/${totalArea.value}`;
6135
6287
  // 日期信息
6136
6288
  const dateText = formatBoundaryDateText(boundary.endTime || 0);
@@ -6196,7 +6348,6 @@ class BoundaryLabelsManager {
6196
6348
  this.collapseOtherLabels(boundaryId);
6197
6349
  // 展开当前标签
6198
6350
  extendedContent.style.display = 'block';
6199
- labelDiv.style.whiteSpace = 'normal';
6200
6351
  this.currentExpandedBoundaryId = boundaryId;
6201
6352
  }
6202
6353
  /**
@@ -6239,7 +6390,6 @@ class BoundaryLabelsManager {
6239
6390
  * @param viewBox SVG的viewBox信息
6240
6391
  */
6241
6392
  updatePositionsWithPrecomputedData(divWidth, divHeight, viewBox) {
6242
- console.log('updatePositionsWithPrecomputedData----->', this.rotation);
6243
6393
  if (!this.container || !this.svgView)
6244
6394
  return;
6245
6395
  const dw = parseFloat(this.svgView.getSVG().getAttribute('width')) || divWidth;
@@ -6433,12 +6583,6 @@ class BoundaryLabelsManager {
6433
6583
  this.container.style.display = visible ? 'block' : 'none';
6434
6584
  }
6435
6585
  }
6436
- /**
6437
- * 获取边界数据
6438
- */
6439
- getBoundaryData() {
6440
- return this.boundaryData;
6441
- }
6442
6586
  /**
6443
6587
  * 根据ID获取特定边界的标签元素
6444
6588
  */
@@ -6488,18 +6632,6 @@ class BoundaryLabelsManager {
6488
6632
  }
6489
6633
  });
6490
6634
  }
6491
- /**
6492
- * 重置标签层级(公共方法)
6493
- */
6494
- resetZIndex() {
6495
- this.resetLabelZIndex();
6496
- }
6497
- /**
6498
- * 获取层级常量(静态方法)
6499
- */
6500
- static getZIndexConstants() {
6501
- return BoundaryLabelsManager.Z_INDEX;
6502
- }
6503
6635
  /**
6504
6636
  * 设置标签旋转角度,使其与地图旋转相反,保持水平状态
6505
6637
  * @param rotation 地图的旋转角度(度)
@@ -6516,19 +6648,6 @@ class BoundaryLabelsManager {
6516
6648
  labelElement.style.transform = `translate(-50%, -50%) rotate(${counterRotation}deg)`;
6517
6649
  });
6518
6650
  }
6519
- /**
6520
- * 重置标签旋转角度
6521
- */
6522
- resetLabelsRotation() {
6523
- if (!this.container)
6524
- return;
6525
- const labels = this.container.querySelectorAll('.boundary-label');
6526
- labels.forEach((label) => {
6527
- const labelElement = label;
6528
- // 重置为默认的居中变换
6529
- labelElement.style.transform = 'translate(-50%, -50%)';
6530
- });
6531
- }
6532
6651
  }
6533
6652
  // 简化的层级定义
6534
6653
  BoundaryLabelsManager.Z_INDEX = {
@@ -6704,26 +6823,6 @@ class ChargingPileManager {
6704
6823
  this.container.innerHTML = '';
6705
6824
  }
6706
6825
  }
6707
- /**
6708
- * 设置可见性
6709
- */
6710
- setVisible(visible) {
6711
- if (this.container) {
6712
- this.container.style.display = visible ? 'block' : 'none';
6713
- }
6714
- }
6715
- /**
6716
- * 获取充电桩数量
6717
- */
6718
- getElementCount() {
6719
- return this.chargingPileElements.length;
6720
- }
6721
- /**
6722
- * 充电桩不需要动态层级调整(为了接口统一而保留)
6723
- */
6724
- resetZIndex() {
6725
- // 充电桩层级始终保持固定,无需重置
6726
- }
6727
6826
  /**
6728
6827
  * 销毁管理器
6729
6828
  */
@@ -6746,21 +6845,10 @@ class ChargingPileManager {
6746
6845
  pileElement.style.transform = `translate(-50%, -50%) rotate(${this.originalRotation - this.rotation}deg)`;
6747
6846
  });
6748
6847
  }
6749
- /**
6750
- * 重置充电桩旋转角度
6751
- */
6752
- resetRotation() {
6753
- this.rotation = 0;
6754
- const allContainers = this.container.querySelectorAll('.charging-pile');
6755
- allContainers.forEach((container) => {
6756
- const pileElement = container;
6757
- pileElement.style.transform = `translate(-50%, -50%) rotate(${this.originalRotation - this.rotation}deg)`;
6758
- });
6759
- }
6760
6848
  }
6761
6849
  // 简化的层级定义 - 充电桩只需要一个固定层级
6762
6850
  ChargingPileManager.Z_INDEX = {
6763
- CHARGING_PILE: 800, // 充电桩图标固定层级
6851
+ CHARGING_PILE: 750, // 充电桩图标固定层级
6764
6852
  };
6765
6853
 
6766
6854
  /**
@@ -7142,32 +7230,6 @@ class AntennaManager {
7142
7230
  this.container.innerHTML = '';
7143
7231
  }
7144
7232
  }
7145
- /**
7146
- * 设置可见性
7147
- */
7148
- setVisible(visible) {
7149
- if (this.container) {
7150
- this.container.style.display = visible ? 'block' : 'none';
7151
- }
7152
- }
7153
- /**
7154
- * 获取天线数量
7155
- */
7156
- getElementCount() {
7157
- return this.antennaElements.length;
7158
- }
7159
- /**
7160
- * 重置天线层级(公共方法)
7161
- */
7162
- resetZIndex() {
7163
- this.resetAntennaZIndex();
7164
- }
7165
- /**
7166
- * 获取层级常量(静态方法)
7167
- */
7168
- static getZIndexConstants() {
7169
- return AntennaManager.Z_INDEX;
7170
- }
7171
7233
  /**
7172
7234
  * 销毁管理器
7173
7235
  */
@@ -7195,21 +7257,10 @@ class AntennaManager {
7195
7257
  antennaContainer.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
7196
7258
  });
7197
7259
  }
7198
- /**
7199
- * 重置天线旋转角度
7200
- */
7201
- resetRotation() {
7202
- this.rotation = 0;
7203
- const allContainers = this.container.querySelectorAll('.antenna-container-item');
7204
- allContainers.forEach((container) => {
7205
- const antennaContainer = container;
7206
- antennaContainer.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
7207
- });
7208
- }
7209
7260
  }
7210
7261
  // 简化的层级定义
7211
7262
  AntennaManager.Z_INDEX = {
7212
- DEFAULT: 750, // 默认层级
7263
+ DEFAULT: 800, // 默认层级
7213
7264
  ACTIVE: 9999, // 点击激活时的高层级
7214
7265
  };
7215
7266
 
@@ -7261,7 +7312,7 @@ const EDIT_BEHAVIOR = {
7261
7312
  };
7262
7313
 
7263
7314
  class MowerPositionManager {
7264
- constructor(svgView, mowerPositionConfig, overlayDiv, onAnimationComplete, onMowingPositionChange) {
7315
+ constructor(svgView, mowerPositionConfig, modelType, overlayDiv, onAnimationComplete, onMowingPositionChange) {
7265
7316
  this.container = null;
7266
7317
  this.overlayDiv = null;
7267
7318
  this.mowerElement = null;
@@ -7279,6 +7330,7 @@ class MowerPositionManager {
7279
7330
  this.onlyUpdateTheta = false;
7280
7331
  this.svgView = svgView;
7281
7332
  this.mowerPositionConfig = mowerPositionConfig;
7333
+ this.modelType = modelType;
7282
7334
  this.overlayDiv = overlayDiv;
7283
7335
  this.onAnimationComplete = onAnimationComplete;
7284
7336
  this.onMowingPositionChange = onMowingPositionChange;
@@ -7333,6 +7385,7 @@ class MowerPositionManager {
7333
7385
  }
7334
7386
  this.mowerElement.appendChild(imgElement);
7335
7387
  this.container.appendChild(this.mowerElement);
7388
+ this.updatePosition(this.mowerPositionConfig);
7336
7389
  }
7337
7390
  /**
7338
7391
  * 设置叠加层div引用(用于坐标转换)
@@ -7353,11 +7406,22 @@ class MowerPositionManager {
7353
7406
  if (!chargingPilesPositionConfig)
7354
7407
  return;
7355
7408
  this.mowerPositionConfig = chargingPilesPositionConfig;
7356
- console.log('manager updatePositionByLastPosition----->', chargingPilesPositionConfig, this.lastPosition);
7409
+ const positonOutOfRange = isOutOfRange(chargingPilesPositionConfig);
7410
+ const positionInValid = isInvalidPosition(chargingPilesPositionConfig);
7411
+ let postureX = 0;
7412
+ let postureY = 0;
7413
+ let postureTheta = 0;
7357
7414
  const lastPosition = this.lastPosition;
7358
- const postureX = chargingPilesPositionConfig.postureX || chargingPilesPositionConfig.lastPostureX || lastPosition?.x;
7359
- const postureY = chargingPilesPositionConfig.postureY || chargingPilesPositionConfig.lastPostureY || lastPosition?.y;
7360
- const postureTheta = chargingPilesPositionConfig.postureTheta || chargingPilesPositionConfig.lastPostureTheta || lastPosition?.rotation;
7415
+ if (positonOutOfRange || positionInValid) {
7416
+ postureX = lastPosition?.x || 0;
7417
+ postureY = lastPosition?.y || 0;
7418
+ postureTheta = lastPosition?.rotation || 0;
7419
+ }
7420
+ else {
7421
+ postureX = chargingPilesPositionConfig.postureX || 0;
7422
+ postureY = chargingPilesPositionConfig.postureY || 0;
7423
+ postureTheta = chargingPilesPositionConfig.postureTheta || 0;
7424
+ }
7361
7425
  // 检查是否需要更新图片
7362
7426
  this.updateMowerImage(chargingPilesPositionConfig);
7363
7427
  // 立即更新位置
@@ -7367,12 +7431,13 @@ class MowerPositionManager {
7367
7431
  * 更新割草机位置
7368
7432
  */
7369
7433
  updatePosition(positionConfig, animationTime = 0) {
7370
- console.log('manager updatePosition----->', positionConfig);
7371
7434
  // 检查是否需要更新图片
7372
7435
  this.updateMowerImage(positionConfig);
7373
- const postureX = positionConfig.postureX || this.lastPosition?.x;
7374
- const postureY = positionConfig.postureY || this.lastPosition?.y;
7375
- const postureTheta = positionConfig.postureTheta || this.lastPosition?.rotation;
7436
+ // 更新配置
7437
+ this.mowerPositionConfig = positionConfig;
7438
+ const postureX = positionConfig?.postureX || this.lastPosition?.x || 0;
7439
+ const postureY = positionConfig?.postureY || this.lastPosition?.y || 0;
7440
+ const postureTheta = positionConfig?.postureTheta || this.lastPosition?.rotation || 0;
7376
7441
  // 停止当前动画(如果有)
7377
7442
  this.stopAnimation();
7378
7443
  // 第一个点
@@ -7393,8 +7458,6 @@ class MowerPositionManager {
7393
7458
  // 立即更新位置
7394
7459
  this.setElementPosition(positionConfig.postureX, positionConfig.postureY, positionConfig.postureTheta);
7395
7460
  }
7396
- // 更新配置
7397
- this.mowerPositionConfig = positionConfig;
7398
7461
  }
7399
7462
  /**
7400
7463
  * 更新割草机图片
@@ -7405,9 +7468,14 @@ class MowerPositionManager {
7405
7468
  const imgElement = this.mowerElement.querySelector('img');
7406
7469
  if (!imgElement)
7407
7470
  return;
7408
- const imageSrc = getMowerImage(positonConfig);
7471
+ const imageSrc = getMowerImage(positonConfig, this.modelType);
7409
7472
  if (imageSrc) {
7410
7473
  imgElement.src = imageSrc;
7474
+ imgElement.style.display = 'block';
7475
+ }
7476
+ else {
7477
+ imgElement.style.display = 'none';
7478
+ return;
7411
7479
  }
7412
7480
  }
7413
7481
  /**
@@ -7417,17 +7485,9 @@ class MowerPositionManager {
7417
7485
  const { x: pointX, y: pointY } = convertCoordinate(x, y);
7418
7486
  const targetPixelPosition = this.convertMapCoordinateToOverlayPixel(pointX, pointY);
7419
7487
  const targetRotation = radToDegree(theta);
7420
- const positonOutOfRange = isOutOfRange({
7421
- postureX: x,
7422
- postureY: y});
7423
- const positionValid = isInvalidPosition({
7424
- postureX: x,
7425
- postureY: y,
7426
- postureTheta: theta,
7427
- });
7428
- if (!positonOutOfRange && !positionValid) {
7429
- this.lastPosition = { x, y, rotation: theta };
7430
- }
7488
+ this.currentPosition = { x: x, y: y, rotation: theta };
7489
+ this.lastPosition = { x, y, rotation: theta };
7490
+ // console.log('setElementPosition', x, y, theta, targetRotation, positonOutOfRange, positionValid, targetPixelPosition);
7431
7491
  if (!this.mowerElement)
7432
7492
  return;
7433
7493
  this.mowerElement.style.left = `${targetPixelPosition?.x}px`;
@@ -7453,15 +7513,29 @@ class MowerPositionManager {
7453
7513
  startAnimationToPosition(positionConfig, duration) {
7454
7514
  if (!this.mowerElement || !this.currentPosition)
7455
7515
  return;
7456
- this.startPosition = {
7457
- ...this.currentPosition,
7458
- rotation: radNormalize(this.currentPosition.rotation),
7459
- };
7460
7516
  this.targetPosition = {
7461
7517
  x: positionConfig.postureX,
7462
7518
  y: positionConfig.postureY,
7463
7519
  rotation: positionConfig.postureTheta,
7464
7520
  };
7521
+ const isTargetPositionInvalid = isInvalidPosition({
7522
+ postureX: this.targetPosition.x,
7523
+ postureY: this.targetPosition.y,
7524
+ postureTheta: this.targetPosition.rotation,
7525
+ });
7526
+ const isTargetPositionOutOfRange = isOutOfRange({
7527
+ postureX: this.targetPosition.x,
7528
+ postureY: this.targetPosition.y,
7529
+ postureTheta: this.targetPosition.rotation,
7530
+ });
7531
+ // 如果目标坐标点不合理,则舍弃不使用
7532
+ if (isTargetPositionInvalid || isTargetPositionOutOfRange) {
7533
+ return;
7534
+ }
7535
+ this.startPosition = {
7536
+ ...this.currentPosition,
7537
+ rotation: radNormalize(this.currentPosition.rotation),
7538
+ };
7465
7539
  this.animationDuration = duration;
7466
7540
  this.startTime = window.performance.now();
7467
7541
  this.isAnimating = true;
@@ -7491,7 +7565,7 @@ class MowerPositionManager {
7491
7565
  * 动画步骤
7492
7566
  */
7493
7567
  animateStep() {
7494
- if (!this.isAnimating || !this.currentPosition || !this.targetPosition || !this.startPosition)
7568
+ if (!this.isAnimating || !this.targetPosition || !this.startPosition)
7495
7569
  return;
7496
7570
  const currentTime = window.performance.now();
7497
7571
  const elapsed = currentTime - this.startTime;
@@ -7502,25 +7576,22 @@ class MowerPositionManager {
7502
7576
  const currentX = this.startPosition.x + this.deltaPosition.x * easedProgress;
7503
7577
  const currentY = this.startPosition.y + this.deltaPosition.y * easedProgress;
7504
7578
  const currentRotation = this.startPosition.rotation + this.deltaPosition.rotation * easedProgress;
7505
- this.currentPosition = { x: currentX, y: currentY, rotation: currentRotation };
7506
7579
  // 假设在这里进行更新路径数据
7507
7580
  if (this.onMowingPositionChange) {
7508
7581
  this.onMowingPositionChange({
7509
7582
  x: currentX,
7510
7583
  y: currentY,
7511
- vehicleState: this.mowerPositionConfig.vehicleState,
7584
+ vehicleState: this.mowerPositionConfig?.vehicleState,
7512
7585
  });
7513
7586
  }
7514
- // console.log('animateStep----->', this.onlyUpdateTheta, currentX, currentY, currentRotation);
7515
- //
7587
+ // console.log('animateStep-->', this.startPosition, this.deltaPosition, this.targetPosition, easedProgress)
7588
+ this.setElementPosition(currentX, currentY, currentRotation);
7516
7589
  // 继续动画或结束
7517
7590
  if (progress < 1) {
7518
7591
  // 设置当前位置
7519
- this.setElementPosition(currentX, currentY, currentRotation);
7520
7592
  this.animationId = window.requestAnimationFrame(() => this.animateStep());
7521
7593
  }
7522
7594
  else {
7523
- this.setElementPosition(currentX, currentY, currentRotation);
7524
7595
  // 动画完成
7525
7596
  this.stopAnimation();
7526
7597
  // 通知动画完成
@@ -7567,12 +7638,6 @@ class MowerPositionManager {
7567
7638
  this.container.style.display = visible ? 'block' : 'none';
7568
7639
  }
7569
7640
  }
7570
- /**
7571
- * 检查是否正在动画中
7572
- */
7573
- getIsAnimating() {
7574
- return this.isAnimating;
7575
- }
7576
7641
  /**
7577
7642
  * 销毁管理器
7578
7643
  */
@@ -7634,10 +7699,34 @@ function throttleAdvanced(func, delay, options = { leading: true, trailing: true
7634
7699
  }
7635
7700
  };
7636
7701
  }
7702
+ /**
7703
+ * 检测当前设备是否为移动设备
7704
+ * @returns {boolean} 如果是移动设备返回true,否则返回false
7705
+ */
7706
+ function isMobileDevice() {
7707
+ // 确保在浏览器环境中运行
7708
+ if (typeof window === 'undefined' || typeof navigator === 'undefined') {
7709
+ return false;
7710
+ }
7711
+ // 检查用户代理字符串
7712
+ const userAgent = navigator.userAgent.toLowerCase();
7713
+ const mobileKeywords = [
7714
+ 'android', 'webos', 'iphone', 'ipad', 'ipod',
7715
+ 'blackberry', 'windows phone', 'mobile'
7716
+ ];
7717
+ const isMobileUserAgent = mobileKeywords.some(keyword => userAgent.includes(keyword));
7718
+ // 检查触摸屏支持
7719
+ const hasTouchScreen = 'ontouchstart' in window ||
7720
+ (navigator.maxTouchPoints && navigator.maxTouchPoints > 0);
7721
+ // 检查屏幕尺寸(移动设备通常屏幕较小)
7722
+ const isSmallScreen = window.innerWidth <= 768;
7723
+ // 综合判断:用户代理包含移动设备关键词,或者有触摸屏且屏幕较小
7724
+ return isMobileUserAgent || (hasTouchScreen && isSmallScreen);
7725
+ }
7637
7726
 
7638
7727
  // Google Maps 叠加层类 - 带编辑功能
7639
7728
  class MowerMapOverlay {
7640
- constructor(bounds, mapData, partitionBoundary, mowerPositionConfig, pathData, isEditMode = false, mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
7729
+ constructor(bounds, mapData, partitionBoundary, mowerPositionConfig, modelType, pathData, isEditMode = false, unitType = UnitsType.Imperial, language = 'en', mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
7641
7730
  this.div = null;
7642
7731
  this.svgMapView = null;
7643
7732
  this.offscreenContainer = null;
@@ -7685,6 +7774,8 @@ class MowerMapOverlay {
7685
7774
  this.partitionBoundary = partitionBoundary;
7686
7775
  this.pathData = pathData;
7687
7776
  this.isEditMode = isEditMode;
7777
+ this.unitType = unitType;
7778
+ this.language = language;
7688
7779
  this.mapConfig = mapConfig;
7689
7780
  this.antennaConfig = antennaConfig;
7690
7781
  this.onMapLoad = onMapLoad;
@@ -7692,6 +7783,7 @@ class MowerMapOverlay {
7692
7783
  this.dragCallbacks = dragCallbacks;
7693
7784
  this.mowerPositionConfig = mowerPositionConfig;
7694
7785
  this.mowPartitionData = mowPartitionData;
7786
+ this.modelType = modelType;
7695
7787
  // 设置默认的transform
7696
7788
  if (defaultTransform) {
7697
7789
  this.defaultTransform = {
@@ -7724,7 +7816,6 @@ class MowerMapOverlay {
7724
7816
  this.boundaryData = generateBoundaryData(mapData, pathData);
7725
7817
  }
7726
7818
  updatePosition(positionConfig, animationTime = 2200) {
7727
- console.log('updatePosition==', positionConfig, animationTime);
7728
7819
  // 保存当前动画时长
7729
7820
  this.currentAnimationTime = animationTime;
7730
7821
  // 标记是否为用户动画(大于0表示用户主动触发的动画)
@@ -7737,7 +7828,7 @@ class MowerMapOverlay {
7737
7828
  }
7738
7829
  }
7739
7830
  updatePositionByLastPosition(chargingPilesPositionConfig) {
7740
- console.log('overlay updatePositionByLastPosition----->', this.mowerPositionManager, chargingPilesPositionConfig);
7831
+ this.mowerPositionConfig = chargingPilesPositionConfig;
7741
7832
  // 更新配置
7742
7833
  if (this.mowerPositionManager) {
7743
7834
  this.mowerPositionManager.updatePositionByLastPosition(chargingPilesPositionConfig);
@@ -7758,32 +7849,24 @@ class MowerMapOverlay {
7758
7849
  return this.overlayView ? this.overlayView.getPanes() : null;
7759
7850
  }
7760
7851
  resetBorderLayerHighlight() {
7852
+ this.mowPartitionData = null;
7761
7853
  const boundaryBorderLayer = this.svgMapView?.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
7762
- Object.keys(boundaryBorderLayer?.boudaryBorderPaths || {}).forEach((key) => {
7763
- const paths = boundaryBorderLayer?.boudaryBorderPaths?.[key];
7764
- if (paths) {
7765
- paths.forEach((path) => {
7766
- path.setAttribute('stroke', BOUNDARY_STYLES.lineColor);
7767
- });
7768
- }
7769
- });
7854
+ if (!boundaryBorderLayer)
7855
+ return;
7856
+ boundaryBorderLayer.setMowingBoundarys([]);
7857
+ this.svgMapView?.renderLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
7770
7858
  }
7771
7859
  setBorderLayerHighlight(mowPartitionData) {
7772
7860
  this.mowPartitionData = mowPartitionData;
7773
7861
  const boundaryBorderLayer = this.svgMapView?.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
7774
7862
  const partitionIds = mowPartitionData?.partitionIds || [];
7775
- console.log('setBorderLayerHighlight----->', partitionIds);
7776
- for (const partitionId of partitionIds) {
7777
- const boundaryBorderPaths = boundaryBorderLayer?.boudaryBorderPaths?.[partitionId];
7778
- console.log('boundaryBorderPaths----->', boundaryBorderLayer, boundaryBorderPaths);
7779
- if (boundaryBorderPaths) {
7780
- boundaryBorderPaths.forEach((path) => {
7781
- path.setAttribute('stroke', BOUNDARY_STYLES.mowingLineColor);
7782
- });
7783
- }
7784
- }
7863
+ if (!boundaryBorderLayer)
7864
+ return;
7865
+ boundaryBorderLayer.setMowingBoundarys(partitionIds);
7866
+ this.svgMapView?.renderLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
7785
7867
  }
7786
7868
  onAdd() {
7869
+ console.log('onAdd');
7787
7870
  // 创建包含SVG的div
7788
7871
  this.div = document.createElement('div');
7789
7872
  this.div.style.borderStyle = 'none';
@@ -7805,6 +7888,7 @@ class MowerMapOverlay {
7805
7888
  this.createBoundaryLabelsManager();
7806
7889
  // 创建割草机位置管理器
7807
7890
  this.createMowerPositionManager();
7891
+ this.setManagerRotation(this.defaultTransform.rotation);
7808
7892
  // 如果处于编辑模式,创建编辑界面
7809
7893
  if (this.isEditMode) {
7810
7894
  this.createEditInterface();
@@ -7843,16 +7927,6 @@ class MowerMapOverlay {
7843
7927
  const map = this.getMap();
7844
7928
  if (!map || !this.svgMapView)
7845
7929
  return;
7846
- // const currentZoom = map.getZoom();
7847
- // const center = map.getCenter();
7848
- // 基础公式:像素/米 = 156543.03392 * cos(latitude) / (2 ^ zoom)
7849
- // const metersPerPixel =
7850
- // (156543.03392 * Math.cos((center.lat() * Math.PI) / 180)) / Math.pow(2, currentZoom);
7851
- // 缩放比例 = 1 / 米/像素
7852
- // const scale = (1 / metersPerPixel) * 50;
7853
- // 应用缩放到SVG
7854
- // this.svgMapView.setZoom(scale);
7855
- // 当缩放变化时,重新绘制以确保位置正确
7856
7930
  this.draw();
7857
7931
  }
7858
7932
  // 创建边界标签管理器
@@ -7860,7 +7934,10 @@ class MowerMapOverlay {
7860
7934
  if (!this.div || !this.svgMapView)
7861
7935
  return;
7862
7936
  // 创建边界标签管理器
7863
- this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData);
7937
+ this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData, {
7938
+ unitType: this.unitType,
7939
+ language: this.language,
7940
+ });
7864
7941
  // 设置叠加层div引用
7865
7942
  this.boundaryLabelsManager.setOverlayDiv(this.div);
7866
7943
  // 添加所有边界标签
@@ -7904,9 +7981,7 @@ class MowerMapOverlay {
7904
7981
  if (!this.div || !this.svgMapView)
7905
7982
  return;
7906
7983
  // 创建割草机位置管理器,传入动画完成回调
7907
- this.mowerPositionManager = new MowerPositionManager(this.svgMapView, this.mowerPositionConfig, this.div, () => {
7908
- console.log('动画完成');
7909
- }, this.updatePathDataByMowingPositionThrottled.bind(this));
7984
+ this.mowerPositionManager = new MowerPositionManager(this.svgMapView, this.mowerPositionConfig, this.modelType, this.div, () => { }, this.updatePathDataByMowingPositionThrottled.bind(this));
7910
7985
  // 设置叠加层div引用
7911
7986
  this.mowerPositionManager.setOverlayDiv(this.div);
7912
7987
  // 获取容器并添加到主div
@@ -7937,7 +8012,7 @@ class MowerMapOverlay {
7937
8012
  this.boundaryLabelsManager.updatePositionsWithPrecomputedData(width, height, viewBoxInfo);
7938
8013
  }
7939
8014
  // 更新管理器位置
7940
- updateManagerPositions(width, height) {
8015
+ updateManagerPositions(_width, _height) {
7941
8016
  if (!this.div)
7942
8017
  return;
7943
8018
  // 更新充电桩位置
@@ -7988,7 +8063,7 @@ class MowerMapOverlay {
7988
8063
  this.rotateHandle.style.pointerEvents = 'auto';
7989
8064
  this.rotateHandle.innerHTML = DEFAULT_ROTATE_ICON;
7990
8065
  this.editContainer.appendChild(this.rotateHandle);
7991
- // 创建拖拽手柄(左上角)
8066
+ // 创建拖拽手柄(左下角)- 仅在移动设备上显示
7992
8067
  this.dragHandle = document.createElement('div');
7993
8068
  this.dragHandle.style.position = 'absolute';
7994
8069
  this.dragHandle.style.bottom = '-20px';
@@ -7999,6 +8074,10 @@ class MowerMapOverlay {
7999
8074
  this.dragHandle.style.zIndex = EDIT_STYLES.Z_INDEX.HANDLE;
8000
8075
  this.dragHandle.style.pointerEvents = 'auto';
8001
8076
  this.dragHandle.innerHTML = DEFAULT_DRAG_ICON;
8077
+ // 在PC设备上隐藏拖拽手柄
8078
+ if (!isMobileDevice()) {
8079
+ this.dragHandle.style.display = 'none';
8080
+ }
8002
8081
  this.editContainer.appendChild(this.dragHandle);
8003
8082
  // 将编辑容器添加到主div
8004
8083
  this.div.appendChild(this.editContainer);
@@ -8040,7 +8119,6 @@ class MowerMapOverlay {
8040
8119
  this.boundaryLabelsManager.collapseAllLabels();
8041
8120
  }
8042
8121
  this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
8043
- console.log('开始旋转操作');
8044
8122
  });
8045
8123
  // 旋转手柄的触摸事件
8046
8124
  this.rotateHandle.addEventListener('touchstart', (e) => {
@@ -8056,39 +8134,41 @@ class MowerMapOverlay {
8056
8134
  this.boundaryLabelsManager.collapseAllLabels();
8057
8135
  }
8058
8136
  this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
8059
- console.log('开始旋转操作(触摸)');
8060
- }, { passive: false });
8061
- // 拖拽手柄的鼠标事件
8062
- this.dragHandle.addEventListener('mousedown', (e) => {
8063
- e.preventDefault();
8064
- e.stopPropagation();
8065
- e.stopImmediatePropagation();
8066
- this.isDragging = true;
8067
- this.startPos = { x: e.clientX, y: e.clientY };
8068
- this.dragHandle.style.cursor = 'grabbing';
8069
- // 开始编辑时关闭所有展开的边界标签
8070
- if (this.boundaryLabelsManager) {
8071
- this.boundaryLabelsManager.collapseAllLabels();
8072
- }
8073
- this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
8074
- console.log('开始拖动操作(通过手柄)');
8075
- });
8076
- // 拖拽手柄的触摸事件
8077
- this.dragHandle.addEventListener('touchstart', (e) => {
8078
- e.preventDefault();
8079
- e.stopPropagation();
8080
- e.stopImmediatePropagation();
8081
- this.isDragging = true;
8082
- const touch = e.touches[0];
8083
- this.startPos = { x: touch.clientX, y: touch.clientY };
8084
- this.dragHandle.style.cursor = 'grabbing';
8085
- this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
8086
- console.log('开始拖动操作(通过手柄,触摸)');
8087
8137
  }, { passive: false });
8138
+ // 拖拽手柄的鼠标事件 - 仅在移动设备上启用
8139
+ if (isMobileDevice()) {
8140
+ this.dragHandle.addEventListener('mousedown', (e) => {
8141
+ e.preventDefault();
8142
+ e.stopPropagation();
8143
+ e.stopImmediatePropagation();
8144
+ this.isDragging = true;
8145
+ this.startPos = { x: e.clientX, y: e.clientY };
8146
+ this.dragHandle.style.cursor = 'grabbing';
8147
+ // 开始编辑时关闭所有展开的边界标签
8148
+ if (this.boundaryLabelsManager) {
8149
+ this.boundaryLabelsManager.collapseAllLabels();
8150
+ }
8151
+ this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
8152
+ });
8153
+ // 拖拽手柄的触摸事件
8154
+ this.dragHandle.addEventListener('touchstart', (e) => {
8155
+ e.preventDefault();
8156
+ e.stopPropagation();
8157
+ e.stopImmediatePropagation();
8158
+ this.isDragging = true;
8159
+ const touch = e.touches[0];
8160
+ this.startPos = { x: touch.clientX, y: touch.clientY };
8161
+ this.dragHandle.style.cursor = 'grabbing';
8162
+ this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
8163
+ }, { passive: false });
8164
+ }
8088
8165
  // 编辑容器的鼠标事件(整个区域拖拽)
8089
8166
  this.editContainer.addEventListener('mousedown', (e) => {
8090
- console.log('开始拖动操作(整个叠加层)');
8091
- if (e.target === this.dragHandle || e.target === this.rotateHandle) {
8167
+ // 在移动设备上,检查是否点击了拖拽手柄或旋转手柄
8168
+ // 在PC设备上,只检查旋转手柄(拖拽手柄已隐藏)
8169
+ const isDragHandleClick = isMobileDevice() && e.target === this.dragHandle;
8170
+ const isRotateHandleClick = e.target === this.rotateHandle;
8171
+ if (isDragHandleClick || isRotateHandleClick) {
8092
8172
  return;
8093
8173
  }
8094
8174
  e.preventDefault();
@@ -8105,8 +8185,11 @@ class MowerMapOverlay {
8105
8185
  });
8106
8186
  // 编辑容器的触摸事件(整个区域拖拽)
8107
8187
  this.editContainer.addEventListener('touchstart', (e) => {
8108
- console.log('开始拖动操作(整个叠加层,触摸)');
8109
- if (e.target === this.dragHandle || e.target === this.rotateHandle) {
8188
+ // 在移动设备上,检查是否点击了拖拽手柄或旋转手柄
8189
+ // 在PC设备上,只检查旋转手柄(拖拽手柄已隐藏)
8190
+ const isDragHandleClick = isMobileDevice() && e.target === this.dragHandle;
8191
+ const isRotateHandleClick = e.target === this.rotateHandle;
8192
+ if (isDragHandleClick || isRotateHandleClick) {
8110
8193
  return;
8111
8194
  }
8112
8195
  e.preventDefault();
@@ -8166,7 +8249,6 @@ class MowerMapOverlay {
8166
8249
  e.preventDefault();
8167
8250
  e.stopPropagation();
8168
8251
  e.stopImmediatePropagation();
8169
- console.log('结束编辑操作');
8170
8252
  }
8171
8253
  // 如果是拖拽结束,将像素偏移量转换为地理坐标偏移量
8172
8254
  if (this.isDragging) {
@@ -8188,7 +8270,6 @@ class MowerMapOverlay {
8188
8270
  e.preventDefault();
8189
8271
  e.stopPropagation();
8190
8272
  e.stopImmediatePropagation();
8191
- console.log('结束编辑操作(触摸)');
8192
8273
  }
8193
8274
  // 如果是拖拽结束,将像素偏移量转换为地理坐标偏移量
8194
8275
  if (this.isDragging) {
@@ -8300,7 +8381,6 @@ class MowerMapOverlay {
8300
8381
  this.div.style.transform = transform;
8301
8382
  // 更新鼠标起始位置为当前位置,为下次计算做准备
8302
8383
  this.startPos = { x: mouseCurrentX, y: mouseCurrentY };
8303
- console.log('旋转角度:', this.currentRotation, '角度增量:', angleDifferenceDegrees);
8304
8384
  }
8305
8385
  // 将像素偏移量转换为地理坐标偏移量
8306
8386
  convertPixelOffsetToLatLng() {
@@ -8330,14 +8410,6 @@ class MowerMapOverlay {
8330
8410
  // 累积更新地理坐标偏移量(不是直接赋值!)
8331
8411
  this.latLngOffset.lat += latOffset;
8332
8412
  this.latLngOffset.lng += lngOffset;
8333
- console.log('精确转换偏移量:', {
8334
- pixelOffset: this.tempPixelOffset,
8335
- centerLatLng: { lat: centerLatLng.lat(), lng: centerLatLng.lng() },
8336
- offsetLatLng: { lat: offsetLatLng.lat(), lng: offsetLatLng.lng() },
8337
- latOffset,
8338
- lngOffset,
8339
- newLatLngOffset: this.latLngOffset,
8340
- });
8341
8413
  // 重置临时像素偏移量
8342
8414
  this.tempPixelOffset = { x: 0, y: 0 };
8343
8415
  this.draw();
@@ -8375,8 +8447,6 @@ class MowerMapOverlay {
8375
8447
  editData: editData,
8376
8448
  timestamp: new Date().toISOString(),
8377
8449
  };
8378
- // 在这里可以添加保存逻辑,比如发送到服务器
8379
- console.log('保存编辑数据:', saveData);
8380
8450
  // 显示保存成功提示
8381
8451
  this.showSaveSuccess();
8382
8452
  return saveData;
@@ -8469,6 +8539,9 @@ class MowerMapOverlay {
8469
8539
  y: transform.y,
8470
8540
  rotation: transform.rotation,
8471
8541
  };
8542
+ // defaultTransform的x对应经度偏移量,y对应纬度偏移量
8543
+ this.latLngOffset.lng = this.defaultTransform.x;
8544
+ this.latLngOffset.lat = this.defaultTransform.y;
8472
8545
  this.setManagerRotation(this.currentRotation);
8473
8546
  this.draw();
8474
8547
  }
@@ -8502,12 +8575,10 @@ class MowerMapOverlay {
8502
8575
  try {
8503
8576
  // 创建SvgMapView实例
8504
8577
  this.svgMapView = new SvgMapView(this.offscreenContainer, 800, 600);
8505
- window.svgMapView = this.svgMapView;
8506
8578
  // 加载地图数据
8507
8579
  this.loadMapData();
8508
8580
  // 加载路径数据
8509
8581
  if (this.pathData && this.svgMapView) {
8510
- console.log('initializeSvgMapView->', this.pathData, this.mowPartitionData);
8511
8582
  this.loadPathData(this.pathData, this.mowPartitionData);
8512
8583
  }
8513
8584
  // 刷新绘制图层
@@ -8534,9 +8605,6 @@ class MowerMapOverlay {
8534
8605
  // 使用现有的MapDataProcessor处理地图数据
8535
8606
  const elements = MapDataProcessor.processMapData(this.mapData, this.mapConfig);
8536
8607
  // 分离充电桩和天线元素,其他元素添加到SVG图层
8537
- // const svgElements = elements.filter(element =>
8538
- // element.type !== 'charging_pile' && element.type !== 'antenna'
8539
- // );
8540
8608
  const svgElements = elements.filter((element) => element.type !== 'charging_pile' && element.type !== 'antenna');
8541
8609
  const chargingPileElements = elements.filter((element) => element.type === 'charging_pile');
8542
8610
  // 处理SVG图层元素
@@ -8551,6 +8619,10 @@ class MowerMapOverlay {
8551
8619
  const layers = drawLayer.getLayers();
8552
8620
  this.svgMapView.clear();
8553
8621
  this.svgMapView.addLayers(layers);
8622
+ const boundaryBorderLayer = this.svgMapView.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
8623
+ if (boundaryBorderLayer) {
8624
+ boundaryBorderLayer.setMowingBoundarys(this.mowPartitionData?.partitionIds || []);
8625
+ }
8554
8626
  this.createChargingPileManager();
8555
8627
  // 使用管理器处理充电桩和天线
8556
8628
  if (this.chargingPileManager && chargingPileElements.length > 0) {
@@ -8580,7 +8652,6 @@ class MowerMapOverlay {
8580
8652
  try {
8581
8653
  // 使用现有的PathDataProcessor处理路径数据
8582
8654
  const pathElements = PathDataProcessor.processPathData(pathData, this.mapConfig);
8583
- console.log('pathdaata->', pathElements, mowPartitionData);
8584
8655
  const newPathElements = pathElements.map((pathElement) => {
8585
8656
  const { id, elements } = pathElement;
8586
8657
  const isMowBoundary = mowPartitionData && mowPartitionData?.partitionIds?.includes(id);
@@ -8628,7 +8699,6 @@ class MowerMapOverlay {
8628
8699
  // 找到当前position所在的分区id,将该点更新到pathData中
8629
8700
  const currentPartitionId = getPartitionId(this.partitionBoundary, position.x, position.y);
8630
8701
  const processStateIsMowing = useProcessMowingState.getState().processStateIsMowing;
8631
- // console.log('processStateIsMowing==', processStateIsMowing);
8632
8702
  if (currentPartitionId && this.pathData?.[currentPartitionId]) {
8633
8703
  const currentPathData = this.pathData[currentPartitionId];
8634
8704
  this.pathData[currentPartitionId] = {
@@ -8817,15 +8887,41 @@ class MowerMapOverlay {
8817
8887
  this.mowerPositionManager.setVisible(visible);
8818
8888
  }
8819
8889
  }
8820
- }
8821
-
8822
- var RealTimeDataType;
8823
- (function (RealTimeDataType) {
8824
- RealTimeDataType[RealTimeDataType["LOCATION"] = 1] = "LOCATION";
8825
- RealTimeDataType[RealTimeDataType["PROCESS"] = 2] = "PROCESS";
8826
- RealTimeDataType[RealTimeDataType["PARTITION"] = 3] = "PARTITION";
8827
- })(RealTimeDataType || (RealTimeDataType = {}));
8828
-
8890
+ // 获取SvgMapView实例(用于debug)
8891
+ getSvgMapView() {
8892
+ return this.svgMapView;
8893
+ }
8894
+ }
8895
+
8896
+ // 获取车辆状态的中文文案
8897
+ const getVehicleStateText = (vehicleState) => {
8898
+ switch (vehicleState) {
8899
+ case RobotStatus.PARKED:
8900
+ return 'PARKED';
8901
+ case RobotStatus.CHARGING:
8902
+ return 'CHARGING';
8903
+ case RobotStatus.STANDBY:
8904
+ return 'STANDBY';
8905
+ case RobotStatus.MOWING:
8906
+ return 'MOWING';
8907
+ case RobotStatus.WORKING:
8908
+ return 'WORKING';
8909
+ case RobotStatus.MAPPING:
8910
+ return 'MAPPING';
8911
+ case RobotStatus.ERROR:
8912
+ return 'ERROR';
8913
+ case RobotStatus.UPGRADING:
8914
+ return 'UPGRADING';
8915
+ case RobotStatus.DISCONNECTED:
8916
+ return 'DISCONNECTED';
8917
+ case RobotStatus.TASK_DELAY:
8918
+ return 'TASK_DELAY';
8919
+ case RobotStatus.UNKNOWN:
8920
+ return '未知';
8921
+ default:
8922
+ return `未知状态(${vehicleState})`;
8923
+ }
8924
+ };
8829
8925
  // 验证GPS坐标是否有效
8830
8926
  const isValidGpsCoordinate = (coordinate) => {
8831
8927
  if (!coordinate || coordinate.length < 2)
@@ -8839,35 +8935,76 @@ const isValidGpsCoordinate = (coordinate) => {
8839
8935
  !(Math.abs(lng) < 0.001 && Math.abs(lat) < 0.001) // 排除接近(0,0)的坐标
8840
8936
  );
8841
8937
  };
8938
+ // 旋转坐标点
8939
+ const rotateCoordinate = (point, center, angleRadians) => {
8940
+ const [x, y] = point;
8941
+ const [cx, cy] = center;
8942
+ // 将点移动到原点
8943
+ const dx = x - cx;
8944
+ const dy = y - cy;
8945
+ // 应用旋转矩阵
8946
+ const cos = Math.cos(angleRadians);
8947
+ const sin = Math.sin(angleRadians);
8948
+ const rotatedX = dx * cos - dy * sin;
8949
+ const rotatedY = dx * sin + dy * cos;
8950
+ // 移回原位置
8951
+ return [rotatedX + cx, rotatedY + cy];
8952
+ };
8842
8953
  // 获取有效的GPS边界
8843
- const getValidGpsBounds = (mapData) => {
8954
+ const getValidGpsBounds = (mapData, rotation = 0) => {
8955
+ let bounds;
8844
8956
  // 首先尝试使用地图数据中的GPS坐标
8845
8957
  if (isValidGpsCoordinate(mapData.sw_gps) && isValidGpsCoordinate(mapData.ne_gps)) {
8846
- return {
8958
+ bounds = {
8847
8959
  sw: mapData.sw_gps,
8848
8960
  ne: mapData.ne_gps,
8849
8961
  };
8850
8962
  }
8851
- // 如果GPS坐标无效,尝试从地图几何数据估算
8852
- const { sw, ne } = estimateGpsFromMapBounds(mapData);
8853
- if (sw && ne) {
8854
- console.warn('GPS坐标无效,使用地图几何数据估算边界:', sw, ne);
8855
- return {
8856
- sw: [sw[0], sw[1]],
8857
- ne: [ne[0], ne[1]],
8963
+ else {
8964
+ // 如果GPS坐标无效,尝试从地图几何数据估算
8965
+ const { sw, ne } = estimateGpsFromMapBounds(mapData);
8966
+ if (sw && ne) {
8967
+ console.warn('GPS坐标无效,使用地图几何数据估算边界:', sw, ne);
8968
+ bounds = {
8969
+ sw: [sw[0], sw[1]],
8970
+ ne: [ne[0], ne[1]],
8971
+ };
8972
+ }
8973
+ else {
8974
+ // 最后的fallback:使用默认坐标
8975
+ console.warn('无法获取有效的GPS边界,使用默认坐标');
8976
+ bounds = {
8977
+ sw: [-9.1562, -37.7503],
8978
+ ne: [31.247, 5.797],
8979
+ };
8980
+ }
8981
+ }
8982
+ // 如果有旋转角度,计算旋转后的边界
8983
+ if (rotation !== 0) {
8984
+ const angleRadians = (rotation * Math.PI) / 180; // 转换为弧度
8985
+ // 计算边界中心点
8986
+ const centerLng = (bounds.sw[0] + bounds.ne[0]) / 2;
8987
+ const centerLat = (bounds.sw[1] + bounds.ne[1]) / 2;
8988
+ const center = [centerLng, centerLat];
8989
+ // 旋转四个角点
8990
+ const sw = rotateCoordinate(bounds.sw, center, angleRadians);
8991
+ const ne = rotateCoordinate(bounds.ne, center, angleRadians);
8992
+ const se = rotateCoordinate([bounds.ne[0], bounds.sw[1]], center, angleRadians);
8993
+ const nw = rotateCoordinate([bounds.sw[0], bounds.ne[1]], center, angleRadians);
8994
+ // 计算旋转后的边界框(包含所有旋转后的点)
8995
+ const lngs = [sw[0], ne[0], se[0], nw[0]];
8996
+ const lats = [sw[1], ne[1], se[1], nw[1]];
8997
+ bounds = {
8998
+ sw: [Math.min(...lngs), Math.min(...lats)],
8999
+ ne: [Math.max(...lngs), Math.max(...lats)],
8858
9000
  };
8859
9001
  }
8860
- // 最后的fallback:使用默认坐标
8861
- console.warn('无法获取有效的GPS边界,使用默认坐标');
8862
- return {
8863
- sw: [-9.1562, -37.7503],
8864
- ne: [31.247, 5.797],
8865
- };
9002
+ return bounds;
8866
9003
  };
8867
9004
  // 默认配置
8868
9005
  const defaultMapConfig = DEFAULT_STYLES;
8869
9006
  // 地图渲染器组件
8870
- const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJson, pathJson, realTimeData, antennaConfig, onMapLoad, onPathLoad, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, defaultTransform, }, ref) => {
9007
+ const MowerMapRenderer = React.forwardRef(({ unitType = UnitsType.Imperial, language = 'en', mapConfig, modelType, mapRef, mapJson, pathJson, realTimeData, antennaConfig, onMapLoad, onPathLoad, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, defaultTransform, debug = false, }, ref) => {
8871
9008
  const [elementCount, setElementCount] = React.useState(0);
8872
9009
  const [pathCount, setPathCount] = React.useState(0);
8873
9010
  const [currentError, setCurrentError] = React.useState(null);
@@ -8875,10 +9012,12 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8875
9012
  // const mapRef = useMap();
8876
9013
  const [isGoogleMapsReady, setIsGoogleMapsReady] = React.useState(false);
8877
9014
  const [hasInitializedBounds, setHasInitializedBounds] = React.useState(false);
8878
- const { clearSubBoundaryBorder } = useSubBoundaryBorderStore();
9015
+ const { clearSubBoundaryBorder, clearObstacles, clearSvgElements } = useSubBoundaryBorderStore();
8879
9016
  const currentProcessMowingStatusRef = React.useRef(false);
8880
9017
  const { updateProcessStateIsMowing, processStateIsMowing } = useProcessMowingState();
8881
9018
  const [mowPartitionData, setMowPartitionData] = React.useState(null);
9019
+ // Debug相关状态
9020
+ const [debugInfo, setDebugInfo] = React.useState({});
8882
9021
  // 处理地图分区边界
8883
9022
  const partitionBoundary = React.useMemo(() => {
8884
9023
  const allBoundaryElements = [];
@@ -8903,7 +9042,12 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8903
9042
  const mowerPositionData = React.useMemo(() => {
8904
9043
  // realTimeData 中包含三个种类的数据,之需要实时坐标的数据即可。
8905
9044
  if (!realTimeData || realTimeData.length === 0)
8906
- return undefined;
9045
+ return {
9046
+ postureX: 0,
9047
+ postureY: 0,
9048
+ postureTheta: 0,
9049
+ vehicleState: RobotStatus.DISCONNECTED,
9050
+ };
8907
9051
  let currentPositionData;
8908
9052
  if (realTimeData.length === 1 && realTimeData[0].type === RealTimeDataType.LOCATION) {
8909
9053
  currentPositionData = realTimeData[0];
@@ -8922,13 +9066,15 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8922
9066
  lastPostureTheta: currentPositionData?.lastPostureTheta
8923
9067
  ? Number(currentPositionData.lastPostureTheta)
8924
9068
  : 0,
8925
- lastPostureX: currentPositionData?.lastPostureX ? Number(currentPositionData.lastPostureX) : 0,
8926
- lastPostureY: currentPositionData?.lastPostureY ? Number(currentPositionData.lastPostureY) : 0,
8927
- vehicleState: currentPositionData?.vehicleState || RobotStatus.CHARGING,
8928
- vehicleModel: modelType?.toLowerCase() || '',
9069
+ lastPostureX: currentPositionData?.lastPostureX
9070
+ ? Number(currentPositionData.lastPostureX)
9071
+ : 0,
9072
+ lastPostureY: currentPositionData?.lastPostureY
9073
+ ? Number(currentPositionData.lastPostureY)
9074
+ : 0,
9075
+ vehicleState: currentPositionData?.vehicleState || RobotStatus.DISCONNECTED,
8929
9076
  };
8930
9077
  }, [realTimeData, modelType]);
8931
- console.log('mowerPositionData', mowerPositionData);
8932
9078
  // 处理错误
8933
9079
  const handleError = (error) => {
8934
9080
  setCurrentError(error);
@@ -8944,18 +9090,17 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8944
9090
  return;
8945
9091
  }
8946
9092
  // 将自定义边界转换为Google Maps LatLngBounds(使用有效的GPS坐标)
8947
- const validBounds = getValidGpsBounds(mapJson);
9093
+ const validBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
8948
9094
  // 地图数据中的坐标格式是 [longitude, latitude]
8949
- const swLat = validBounds.sw[1];
8950
- const swLng = validBounds.sw[0];
8951
- const neLat = validBounds.ne[1];
8952
- const neLng = validBounds.ne[0];
9095
+ const swLat = validBounds.sw[1] + defaultTransform.y;
9096
+ const swLng = validBounds.sw[0] + defaultTransform.x;
9097
+ const neLat = validBounds.ne[1] + defaultTransform.y;
9098
+ const neLng = validBounds.ne[0] + defaultTransform.x;
8953
9099
  const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
8954
9100
  new window.google.maps.LatLng(neLat, neLng) // 东北角
8955
9101
  );
8956
- console.log('fitBounds----->', googleBounds);
8957
9102
  mapRef.fitBounds(googleBounds);
8958
- }, [mapJson, mapRef]);
9103
+ }, [mapJson, mapRef, defaultTransform]);
8959
9104
  // 初始化Google Maps叠加层
8960
9105
  const initializeGoogleMapsOverlay = async () => {
8961
9106
  if (!mapJson)
@@ -8979,7 +9124,8 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8979
9124
  return;
8980
9125
  }
8981
9126
  // 将自定义边界转换为Google Maps LatLngBounds(使用有效的GPS坐标)
8982
- const validBounds = getValidGpsBounds(mapJson);
9127
+ // 这里需要使用0度,需要原始的坐标点去计算元素的实际位置,只有在fitbounds的时候才需要使用旋转后的坐标点
9128
+ const validBounds = getValidGpsBounds(mapJson, 0);
8983
9129
  // 地图数据中的坐标格式是 [longitude, latitude]
8984
9130
  const swLat = validBounds.sw[1];
8985
9131
  const swLng = validBounds.sw[0];
@@ -8994,7 +9140,7 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8994
9140
  overlayRef.current = null;
8995
9141
  }
8996
9142
  // 创建叠加层
8997
- const overlay = new MowerMapOverlay(googleBounds, mapJson, partitionBoundary, mowerPositionData, pathJson || {}, isEditMode, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
9143
+ const overlay = new MowerMapOverlay(googleBounds, mapJson, partitionBoundary, mowerPositionData, modelType, pathJson || {}, isEditMode, unitType, language, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
8998
9144
  setElementCount(count);
8999
9145
  onMapLoad?.(count);
9000
9146
  }, (count) => {
@@ -9015,12 +9161,27 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
9015
9161
  handleError(`初始化Google Maps叠加层失败: ${error instanceof Error ? error.message : String(error)}`);
9016
9162
  }
9017
9163
  };
9164
+ const resetInCharginPie = React.useCallback(() => {
9165
+ const elements = MapDataProcessor.processMapData(mapJson, mergedMapConfig);
9166
+ const chargingPiles = elements.find((element) => element.type === 'charging_pile');
9167
+ if (!overlayRef.current)
9168
+ return;
9169
+ // 如果在充电桩上,则直接更新位置到充电桩的位置
9170
+ overlayRef.current.updatePosition({
9171
+ ...mowerPositionData,
9172
+ postureX: chargingPiles?.originalData.position[0],
9173
+ postureY: chargingPiles?.originalData.position[1],
9174
+ postureTheta: chargingPiles?.originalData.direction - Math.PI || 0,
9175
+ }, 0);
9176
+ }, [mapJson, mowerPositionData]);
9018
9177
  // 初始化效果
9019
9178
  React.useEffect(() => {
9020
9179
  initializeGoogleMapsOverlay();
9021
9180
  // 清理函数
9022
9181
  return () => {
9023
9182
  clearSubBoundaryBorder();
9183
+ clearObstacles();
9184
+ clearSvgElements();
9024
9185
  updateProcessStateIsMowing(false);
9025
9186
  currentProcessMowingStatusRef.current = false;
9026
9187
  if (overlayRef.current) {
@@ -9051,10 +9212,9 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
9051
9212
  const inChargingPiles = [RobotStatus.CHARGING, RobotStatus.PARKED];
9052
9213
  const isOffLine = mowerPositionData.vehicleState === RobotStatus.DISCONNECTED;
9053
9214
  const isInChargingPile = inChargingPiles.includes(mowerPositionData.vehicleState);
9054
- console.log('isInChargingPile-----------》', isInChargingPile, mowerPositionData);
9055
9215
  // 如果在充电桩上,则直接更新位置到充电桩的位置
9216
+ console.log('usefeect mowerPositionData----->', mowerPositionData, isInChargingPile);
9056
9217
  if (isInChargingPile) {
9057
- console.log('isInChargingPile', isInChargingPile, mowerPositionData);
9058
9218
  overlayRef.current.updatePosition({
9059
9219
  ...mowerPositionData,
9060
9220
  postureX: chargingPiles?.originalData.position[0],
@@ -9067,7 +9227,6 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
9067
9227
  const positonOutOfRange = isOutOfRange(mowerPositionData);
9068
9228
  const positionValid = isInvalidPosition(mowerPositionData);
9069
9229
  const isStandby = mowerPositionData.vehicleState === RobotStatus.STANDBY;
9070
- console.log('positonOutOfRange', positonOutOfRange, positionValid, isOffLine);
9071
9230
  if (positonOutOfRange || positionValid || isOffLine) {
9072
9231
  // 初始信息是通过后端接口获取的,此时当前位置数据不可用的时候,可以取上一次的位置数据
9073
9232
  // mowerPositionData 中可能会包含上一次的位置数据,
@@ -9087,29 +9246,165 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
9087
9246
  }
9088
9247
  }
9089
9248
  else {
9090
- console.log('hook updatePosition----->', mowerPositionData);
9091
9249
  overlayRef.current.updatePosition(mowerPositionData, isStandby ? 0 : 2000);
9092
9250
  }
9093
9251
  }
9094
9252
  }, [mowerPositionData]);
9253
+ // 更新debug信息
9254
+ React.useEffect(() => {
9255
+ if (!debug)
9256
+ return;
9257
+ const updateDebugInfo = () => {
9258
+ const newDebugInfo = {};
9259
+ // 获取地图GPS边界
9260
+ if (mapJson) {
9261
+ newDebugInfo.mapBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
9262
+ }
9263
+ // 获取SVG viewBox信息
9264
+ if (overlayRef.current) {
9265
+ const overlay = overlayRef.current;
9266
+ const svgMapView = overlay.getSvgMapView?.();
9267
+ if (svgMapView) {
9268
+ const viewBoxInfo = svgMapView.getViewBoxInfo();
9269
+ // 计算实际的米单位数据(除以缩放比例)
9270
+ const SCALE_FACTOR = 50; // 根据项目中的缩放因子
9271
+ newDebugInfo.viewBox = {
9272
+ x: viewBoxInfo.x / SCALE_FACTOR,
9273
+ y: viewBoxInfo.y / SCALE_FACTOR,
9274
+ width: viewBoxInfo.width / SCALE_FACTOR,
9275
+ height: viewBoxInfo.height / SCALE_FACTOR,
9276
+ scale: SCALE_FACTOR,
9277
+ // 计算左下角和右上角坐标
9278
+ sw: {
9279
+ x: viewBoxInfo.x / SCALE_FACTOR,
9280
+ y: viewBoxInfo.y / SCALE_FACTOR,
9281
+ },
9282
+ ne: {
9283
+ x: (viewBoxInfo.x + viewBoxInfo.width) / SCALE_FACTOR,
9284
+ y: (viewBoxInfo.y + viewBoxInfo.height) / SCALE_FACTOR,
9285
+ },
9286
+ };
9287
+ }
9288
+ }
9289
+ // 获取当前割草机位置
9290
+ if (mowerPositionData) {
9291
+ newDebugInfo.mowerPosition = {
9292
+ x: mowerPositionData.postureX || 0,
9293
+ y: mowerPositionData.postureY || 0,
9294
+ theta: mowerPositionData.postureTheta || 0,
9295
+ lastX: mowerPositionData.lastPostureX || 0,
9296
+ lastY: mowerPositionData.lastPostureY || 0,
9297
+ lastTheta: mowerPositionData.lastPostureTheta || 0,
9298
+ vehicleState: mowerPositionData.vehicleState || RobotStatus.UNKNOWN,
9299
+ vehicleStateText: getVehicleStateText(mowerPositionData.vehicleState || RobotStatus.UNKNOWN),
9300
+ };
9301
+ }
9302
+ // 获取当前割草地块数据
9303
+ newDebugInfo.partitionData = mowPartitionData;
9304
+ setDebugInfo(newDebugInfo);
9305
+ };
9306
+ updateDebugInfo();
9307
+ }, [debug, mapJson, mowerPositionData, mowPartitionData, defaultTransform]);
9308
+ // 当关键数据变化时立即更新debug信息
9309
+ React.useEffect(() => {
9310
+ if (!debug)
9311
+ return;
9312
+ const updateDebugInfo = () => {
9313
+ const newDebugInfo = {};
9314
+ // 获取地图GPS边界
9315
+ if (mapJson) {
9316
+ newDebugInfo.mapBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
9317
+ }
9318
+ // 获取SVG viewBox信息
9319
+ if (overlayRef.current) {
9320
+ const overlay = overlayRef.current;
9321
+ const svgMapView = overlay.getSvgMapView?.();
9322
+ if (svgMapView) {
9323
+ const viewBoxInfo = svgMapView.getViewBoxInfo();
9324
+ // 计算实际的米单位数据(除以缩放比例)
9325
+ const SCALE_FACTOR = 50; // 根据项目中的缩放因子
9326
+ newDebugInfo.viewBox = {
9327
+ x: viewBoxInfo.x / SCALE_FACTOR,
9328
+ y: viewBoxInfo.y / SCALE_FACTOR,
9329
+ width: viewBoxInfo.width / SCALE_FACTOR,
9330
+ height: viewBoxInfo.height / SCALE_FACTOR,
9331
+ scale: SCALE_FACTOR,
9332
+ // 计算左下角和右上角坐标
9333
+ sw: {
9334
+ x: viewBoxInfo.x / SCALE_FACTOR,
9335
+ y: viewBoxInfo.y / SCALE_FACTOR,
9336
+ },
9337
+ ne: {
9338
+ x: (viewBoxInfo.x + viewBoxInfo.width) / SCALE_FACTOR,
9339
+ y: (viewBoxInfo.y + viewBoxInfo.height) / SCALE_FACTOR,
9340
+ },
9341
+ };
9342
+ }
9343
+ }
9344
+ // 获取当前割草机位置
9345
+ if (mowerPositionData) {
9346
+ newDebugInfo.mowerPosition = {
9347
+ x: mowerPositionData.postureX || 0,
9348
+ y: mowerPositionData.postureY || 0,
9349
+ theta: mowerPositionData.postureTheta || 0,
9350
+ lastX: mowerPositionData.lastPostureX || 0,
9351
+ lastY: mowerPositionData.lastPostureY || 0,
9352
+ lastTheta: mowerPositionData.lastPostureTheta || 0,
9353
+ vehicleState: mowerPositionData.vehicleState || RobotStatus.UNKNOWN,
9354
+ vehicleStateText: getVehicleStateText(mowerPositionData.vehicleState || RobotStatus.UNKNOWN),
9355
+ };
9356
+ }
9357
+ // 获取当前割草地块数据
9358
+ newDebugInfo.partitionData = mowPartitionData;
9359
+ setDebugInfo(newDebugInfo);
9360
+ };
9361
+ updateDebugInfo();
9362
+ }, [debug, mowerPositionData, mowPartitionData]);
9095
9363
  React.useEffect(() => {
9096
- if (!mapJson ||
9097
- !pathJson ||
9098
- !realTimeData ||
9099
- realTimeData.length === 0 ||
9100
- !Array.isArray(realTimeData) ||
9101
- !overlayRef.current)
9364
+ if (!realTimeData || realTimeData.length === 0 || !Array.isArray(realTimeData)) {
9365
+ return;
9366
+ }
9367
+ console.log('usefeect realTimeData----->', realTimeData, mapJson, pathJson, overlayRef.current);
9368
+ let curMowPartitionData = mowPartitionData;
9369
+ // realtime中包含当前割草任务的数据,根据数据进行path路径和边界的高亮操作,
9370
+ const mowingPartition = realTimeData.find((item) => item.type === RealTimeDataType.PARTITION);
9371
+ if (mowingPartition) {
9372
+ setMowPartitionData(mowingPartition);
9373
+ curMowPartitionData = mowingPartition;
9374
+ }
9375
+ const positionData = realTimeData?.find((item) => item?.type === RealTimeDataType.LOCATION);
9376
+ const statusData = realTimeData?.find((item) => item?.type === RealTimeDataType.STATUS);
9377
+ if (statusData || positionData) {
9378
+ const currentStatus = statusData?.vehicleState || positionData?.vehicleState;
9379
+ // 车辆回桩不会回传最后的park的位置,所以根据实时数据的状态数据判断车辆回到桩上
9380
+ if ([RobotStatus.CHARGING, RobotStatus.PARKED].includes(currentStatus || RobotStatus.UNKNOWN)) {
9381
+ resetInCharginPie();
9382
+ }
9383
+ else if (currentStatus === RobotStatus.WORKING) {
9384
+ // 兜底收不到割草地块的实时数据,使用状态来兜底
9385
+ overlayRef.current.resetBorderLayerHighlight();
9386
+ setMowPartitionData(null);
9387
+ curMowPartitionData = null;
9388
+ }
9389
+ else if (currentStatus === RobotStatus.MOWING &&
9390
+ curMowPartitionData &&
9391
+ !curMowPartitionData?.partitionIds) {
9392
+ // 如果当前是割草状态,但是地块数据初始化过且不存在则认为是全局割草,则把所有地块都高亮
9393
+ const allPartitionIds = mapJson?.sub_maps?.map((item) => item?.id);
9394
+ setMowPartitionData({
9395
+ partitionIds: allPartitionIds,
9396
+ });
9397
+ curMowPartitionData = {
9398
+ partitionIds: allPartitionIds,
9399
+ };
9400
+ }
9401
+ }
9402
+ if (!mapJson || !pathJson || !overlayRef.current)
9102
9403
  return;
9103
9404
  // 根据后端推送的实时数据,进行不同处理
9104
- // TODO:需要根据返回的数据,处理车辆的移动位置
9105
- console.log('realTimeData----->', realTimeData);
9106
- // realtime中包含当前割草任务的数据,根据数据进行path路径和边界的高亮操作
9107
- const curMowPartitionData = realTimeData.find((item) => item.type === RealTimeDataType.PARTITION) || mowPartitionData;
9108
9405
  if (curMowPartitionData) {
9109
- setMowPartitionData(curMowPartitionData);
9110
9406
  const isMowing = curMowPartitionData?.partitionIds && curMowPartitionData.partitionIds.length > 0;
9111
9407
  overlayRef.current.updateMowPartitionData(curMowPartitionData);
9112
- console.log('isMowing', isMowing, curMowPartitionData);
9113
9408
  if (!isMowing) {
9114
9409
  overlayRef.current.resetBorderLayerHighlight();
9115
9410
  }
@@ -9147,14 +9442,11 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
9147
9442
  }
9148
9443
  }, [realTimeData, mapJson, pathJson]);
9149
9444
  React.useEffect(() => {
9150
- console.log('defaultTransform----->', defaultTransform);
9151
9445
  if (!overlayRef.current || !defaultTransform)
9152
9446
  return;
9153
- if (defaultTransform.x === 0 && defaultTransform.y === 0 && defaultTransform.rotation === 0) {
9154
- return;
9155
- }
9447
+ console.log('defaultTransform----->', defaultTransform, overlayRef.current, mapJson);
9156
9448
  overlayRef.current?.setTransform(defaultTransform);
9157
- const validBounds = getValidGpsBounds(mapJson);
9449
+ const validBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
9158
9450
  // 地图数据中的坐标格式是 [longitude, latitude]
9159
9451
  const swLat = validBounds.sw[1] + defaultTransform.y;
9160
9452
  const swLng = validBounds.sw[0] + defaultTransform.x;
@@ -9200,12 +9492,32 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
9200
9492
  setTransform: (t) => overlayRef.current?.setTransform(t),
9201
9493
  resetToDefaultTransform: () => overlayRef.current?.resetToDefaultTransform(),
9202
9494
  }));
9495
+ // Debug信息组件
9496
+ const DebugInfo = () => {
9497
+ if (!debug)
9498
+ return null;
9499
+ return (jsxRuntime.jsxs("div", { style: {
9500
+ position: 'fixed',
9501
+ bottom: '10px',
9502
+ left: '10px',
9503
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
9504
+ color: 'white',
9505
+ padding: '10px',
9506
+ borderRadius: '5px',
9507
+ fontSize: '12px',
9508
+ fontFamily: 'monospace',
9509
+ zIndex: 10000,
9510
+ maxWidth: '300px',
9511
+ lineHeight: '1.4',
9512
+ }, children: [jsxRuntime.jsx("div", { style: { fontWeight: 'bold', marginBottom: '8px' }, children: "\uD83D\uDC1B Debug Info" }), debugInfo.mapBounds && (jsxRuntime.jsxs("div", { style: { marginBottom: '6px' }, children: [jsxRuntime.jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDCCD Map GPS Bounds:" }), jsxRuntime.jsxs("div", { children: ["SW: [", debugInfo.mapBounds.sw[0].toFixed(6), ", ", debugInfo.mapBounds.sw[1].toFixed(6), "]"] }), jsxRuntime.jsxs("div", { children: ["NE: [", debugInfo.mapBounds.ne[0].toFixed(6), ", ", debugInfo.mapBounds.ne[1].toFixed(6), "]"] })] })), debugInfo.viewBox && (jsxRuntime.jsxs("div", { style: { marginBottom: '6px' }, children: [jsxRuntime.jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDCD0 SVG ViewBox (meters):" }), jsxRuntime.jsxs("div", { children: ["SW: [", debugInfo.viewBox.sw.x.toFixed(2), ", ", debugInfo.viewBox.sw.y.toFixed(2), "]"] }), jsxRuntime.jsxs("div", { children: ["NE: [", debugInfo.viewBox.ne.x.toFixed(2), ", ", debugInfo.viewBox.ne.y.toFixed(2), "]"] }), jsxRuntime.jsxs("div", { children: ["Size: ", debugInfo.viewBox.width.toFixed(2), "m \u00D7 ", debugInfo.viewBox.height.toFixed(2), "m"] }), jsxRuntime.jsxs("div", { children: ["Scale: 1:", debugInfo.viewBox.scale] })] })), debugInfo.mowerPosition && (jsxRuntime.jsxs("div", { style: { marginBottom: '6px' }, children: [jsxRuntime.jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDE9C Mower Position:" }), jsxRuntime.jsxs("div", { children: ["Current: X=", debugInfo.mowerPosition.x.toFixed(2), ", Y=", debugInfo.mowerPosition.y.toFixed(2)] }), jsxRuntime.jsxs("div", { children: ["Theta: ", ((debugInfo.mowerPosition.theta * 180) / Math.PI).toFixed(1), "\u00B0"] }), jsxRuntime.jsxs("div", { children: ["Last: X=", debugInfo.mowerPosition.lastX.toFixed(2), ", Y=", debugInfo.mowerPosition.lastY.toFixed(2)] }), jsxRuntime.jsxs("div", { children: ["Last Theta: ", ((debugInfo.mowerPosition.lastTheta * 180) / Math.PI).toFixed(1), "\u00B0"] }), jsxRuntime.jsxs("div", { children: ["Status: ", debugInfo.mowerPosition.vehicleStateText, " (", debugInfo.mowerPosition.vehicleState, ")"] })] })), debugInfo.partitionData && (jsxRuntime.jsxs("div", { style: { marginBottom: '6px' }, children: [jsxRuntime.jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDD32 Mow Partition Data:" }), jsxRuntime.jsxs("div", { children: ["Type: ", debugInfo.partitionData.type || 'N/A'] }), debugInfo.partitionData.partitionIds &&
9513
+ debugInfo.partitionData.partitionIds.length > 0 ? (jsxRuntime.jsxs("div", { children: ["Active IDs: [", debugInfo.partitionData.partitionIds.join(', '), "]"] })) : (jsxRuntime.jsx("div", { children: "No active partitions" })), debugInfo.partitionData.time && (jsxRuntime.jsxs("div", { children: ["Updated: ", new Date(debugInfo.partitionData.time).toLocaleTimeString()] }))] })), !debugInfo.partitionData && (jsxRuntime.jsxs("div", { style: { marginBottom: '6px' }, children: [jsxRuntime.jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDD32 Mow Partition Data:" }), jsxRuntime.jsx("div", { style: { color: '#888' }, children: "No partition data available" })] }))] }));
9514
+ };
9203
9515
  // 错误显示
9204
9516
  if (currentError) {
9205
- return (jsxRuntime.jsx("div", { className: className, style: style, children: jsxRuntime.jsxs("div", { style: { color: 'red', padding: '10px' }, children: ["\u9519\u8BEF: ", currentError] }) }));
9517
+ return (jsxRuntime.jsxs("div", { className: className, style: style, children: [jsxRuntime.jsxs("div", { style: { color: 'red', padding: '10px' }, children: ["\u9519\u8BEF: ", currentError] }), jsxRuntime.jsx(DebugInfo, {})] }));
9206
9518
  }
9207
- // 使用goole maps自定义叠加层,不需要实际返回组件内容
9208
- return null;
9519
+ // 使用goole maps自定义叠加层,返回debug信息(如果启用)
9520
+ return debug ? jsxRuntime.jsx(DebugInfo, {}) : null;
9209
9521
  });
9210
9522
  MowerMapRenderer.displayName = 'MowerMapRenderer';
9211
9523