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

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 (44) hide show
  1. package/dist/config/styles.d.ts +1 -0
  2. package/dist/config/styles.d.ts.map +1 -1
  3. package/dist/index.esm.js +857 -587
  4. package/dist/index.js +856 -586
  5. package/dist/processor/MapDataProcessor.d.ts.map +1 -1
  6. package/dist/processor/PathDataProcessor.d.ts +7 -3
  7. package/dist/processor/PathDataProcessor.d.ts.map +1 -1
  8. package/dist/processor/builder/PathDataBuilder.d.ts +6 -5
  9. package/dist/processor/builder/PathDataBuilder.d.ts.map +1 -1
  10. package/dist/render/AntennaManager.d.ts +9 -4
  11. package/dist/render/AntennaManager.d.ts.map +1 -1
  12. package/dist/render/BoundaryLabelsManager.d.ts +8 -2
  13. package/dist/render/BoundaryLabelsManager.d.ts.map +1 -1
  14. package/dist/render/ChargingPileManager.d.ts +11 -6
  15. package/dist/render/ChargingPileManager.d.ts.map +1 -1
  16. package/dist/render/MowerMapOverlay.d.ts +45 -8
  17. package/dist/render/MowerMapOverlay.d.ts.map +1 -1
  18. package/dist/render/MowerMapRenderer.d.ts.map +1 -1
  19. package/dist/render/{MowerPostionManager.d.ts → MowerPositionManager.d.ts} +13 -7
  20. package/dist/render/MowerPositionManager.d.ts.map +1 -0
  21. package/dist/render/SvgMapView.d.ts +15 -1
  22. package/dist/render/SvgMapView.d.ts.map +1 -1
  23. package/dist/render/layers/BoundaryBorderLayer.d.ts +2 -0
  24. package/dist/render/layers/BoundaryBorderLayer.d.ts.map +1 -1
  25. package/dist/render/layers/PathLayer.d.ts +1 -0
  26. package/dist/render/layers/PathLayer.d.ts.map +1 -1
  27. package/dist/render/layers/SvgElementLayer.d.ts.map +1 -1
  28. package/dist/render/layers/types.d.ts +3 -1
  29. package/dist/render/layers/types.d.ts.map +1 -1
  30. package/dist/store/processMowingState.d.ts +8 -0
  31. package/dist/store/processMowingState.d.ts.map +1 -0
  32. package/dist/types/realTime.d.ts +17 -2
  33. package/dist/types/realTime.d.ts.map +1 -1
  34. package/dist/types/renderer.d.ts +21 -8
  35. package/dist/types/renderer.d.ts.map +1 -1
  36. package/dist/utils/common.d.ts +14 -0
  37. package/dist/utils/common.d.ts.map +1 -0
  38. package/dist/utils/handleRealTime.d.ts +19 -4
  39. package/dist/utils/handleRealTime.d.ts.map +1 -1
  40. package/dist/utils/mower.d.ts.map +1 -1
  41. package/dist/utils/pathSegments.d.ts +8 -0
  42. package/dist/utils/pathSegments.d.ts.map +1 -1
  43. package/package.json +1 -1
  44. package/dist/render/MowerPostionManager.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -81,7 +81,10 @@ class SvgMapView {
81
81
  return;
82
82
  this.layers.push(...layers);
83
83
  this.layers.sort((a, b) => a.getLevel() - b.getLevel());
84
- this.refresh();
84
+ }
85
+ getLayer(type) {
86
+ console.log('getLayer----->', this.layers, type);
87
+ return this.layers.find((layer) => layer.getType() === type) || null;
85
88
  }
86
89
  /**
87
90
  * 添加图层
@@ -91,14 +94,12 @@ class SvgMapView {
91
94
  return;
92
95
  this.layers.push(layer);
93
96
  this.layers.sort((a, b) => a.getLevel() - b.getLevel());
94
- this.refresh();
95
97
  }
96
98
  /**
97
99
  * 移除图层
98
100
  */
99
101
  removeLayer(layer) {
100
102
  const index = this.layers.indexOf(layer);
101
- console.log('removeLayer----->', index);
102
103
  if (index !== -1) {
103
104
  this.layers.splice(index, 1);
104
105
  this.refresh();
@@ -109,8 +110,11 @@ class SvgMapView {
109
110
  * @param type 图层类型
110
111
  */
111
112
  removeLayerByType(type) {
112
- this.layers = this.layers.filter((layer) => layer.getType() !== type);
113
- this.refresh();
113
+ const layer = this.layers.find((layer) => layer.getType() === type);
114
+ if (layer) {
115
+ this.clearLayersGroup(layer);
116
+ this.layers = this.layers.filter((cLayer) => cLayer.getType() !== type);
117
+ }
114
118
  }
115
119
  // ==================== 变换系统 ====================
116
120
  /**
@@ -211,6 +215,15 @@ class SvgMapView {
211
215
  getLineScale() {
212
216
  return this.lineScale;
213
217
  }
218
+ /**
219
+ * 绘制特定的图层
220
+ */
221
+ renderLayer(type) {
222
+ const layer = this.layers.find((layer) => layer.getType() === type);
223
+ // 清空图层组
224
+ this.clearLayersGroup(layer);
225
+ this.onDrawLayers(type);
226
+ }
214
227
  // ==================== 渲染系统 ====================
215
228
  /**
216
229
  * 主渲染方法
@@ -221,25 +234,112 @@ class SvgMapView {
221
234
  // 绘制所有图层
222
235
  this.onDrawLayers();
223
236
  }
237
+ /**
238
+ * 获取图层id
239
+ */
240
+ getLayerId(layer) {
241
+ return `layer-${layer.getType()}-${layer.getLevel()}`;
242
+ }
224
243
  /**
225
244
  * 清空图层组
226
245
  */
227
- clearLayersGroup() {
228
- while (this.layersGroup.firstChild) {
229
- this.layersGroup.removeChild(this.layersGroup.firstChild);
246
+ clearLayersGroup(layer) {
247
+ if (layer) {
248
+ const layerId = this.getLayerId(layer);
249
+ const layerGroup = this.layersGroup.querySelector(`#${layerId}`);
250
+ if (layerGroup) {
251
+ this.layersGroup.removeChild(layerGroup);
252
+ }
253
+ }
254
+ else {
255
+ while (this.layersGroup.firstChild) {
256
+ this.layersGroup.removeChild(this.layersGroup.firstChild);
257
+ }
230
258
  }
231
259
  }
232
260
  /**
233
- * 绘制所有图层
261
+ * 获取图层的下一个兄弟元素
262
+ * 根据图层的level来获取
234
263
  */
235
- onDrawLayers() {
236
- for (const layer of this.layers) {
237
- if (layer.isVisible()) {
238
- const layerGroup = this.createSVGGroup(`layer-${layer.getLevel()}`);
264
+ getNextSibling(layer) {
265
+ const nextLayer = this.layers.find((cLayer) => cLayer.getLevel() > layer.getLevel());
266
+ const id = `layer-${nextLayer?.getType()}-${nextLayer?.getLevel()}`;
267
+ const nextSibling = this.layersGroup.querySelector(`#${id}`);
268
+ return nextSibling;
269
+ }
270
+ /**
271
+ * 绘制图层,不传参数则默认绘制所有图层
272
+ */
273
+ onDrawLayers(type) {
274
+ if (type) {
275
+ const layer = this.layers.find((layer) => layer.getType() === type);
276
+ if (layer) {
277
+ const layerGroup = this.createSVGGroup(`layer-${layer.getType()}-${layer.getLevel()}`);
239
278
  layer.drawSVG(layerGroup, this.scale, this.lineScale);
240
- this.layersGroup.appendChild(layerGroup);
279
+ const nextSibling = this.getNextSibling(layer);
280
+ if (nextSibling) {
281
+ this.layersGroup.insertBefore(layerGroup, nextSibling);
282
+ }
283
+ else {
284
+ this.layersGroup.appendChild(layerGroup);
285
+ }
286
+ }
287
+ }
288
+ else {
289
+ for (const layer of this.layers) {
290
+ if (layer.isVisible()) {
291
+ const layerGroup = this.createSVGGroup(`layer-${layer.getType()}-${layer.getLevel()}`);
292
+ layer.drawSVG(layerGroup, this.scale, this.lineScale);
293
+ this.layersGroup.appendChild(layerGroup);
294
+ }
241
295
  }
242
296
  }
297
+ // if (type) {
298
+ // const layer = this.layers.find((layer) => layer.getType() === type);
299
+ // const layerId = this.getLayerId(layer);
300
+ // // 记录原始位置信息
301
+ // const layerGroup = this.layersGroup.querySelector(`#${layerId}`);
302
+ // let nextSibling: Node | null = null;
303
+ // if (layerGroup) {
304
+ // // 记录被删除元素的下一个兄弟元素,用于确定插入位置
305
+ // nextSibling = layerGroup.nextSibling;
306
+ // // 删除旧的图层组
307
+ // this.layersGroup.removeChild(layerGroup);
308
+ // // 从layerId解析出图层类型和层级
309
+ // // layerId格式: layer-${type}-${level}
310
+ // const layerIdParts = layerId.split('-');
311
+ // if (layerIdParts.length >= 3) {
312
+ // const layerType = layerIdParts.slice(1, -1).join('-'); // 处理类型名中可能包含连字符的情况
313
+ // const layerLevel = parseInt(layerIdParts[layerIdParts.length - 1]);
314
+ // // 查找对应的图层对象
315
+ // const targetLayer = this.layers.find(layer =>
316
+ // layer.getType() === layerType && layer.getLevel() === layerLevel
317
+ // );
318
+ // if (targetLayer && targetLayer.isVisible()) {
319
+ // // 创建新的图层组
320
+ // const newLayerGroup = this.createSVGGroup(layerId);
321
+ // // 重新绘制图层
322
+ // targetLayer.drawSVG(newLayerGroup, this.scale, this.lineScale);
323
+ // // 在原始位置插入新元素
324
+ // if (nextSibling) {
325
+ // this.layersGroup.insertBefore(newLayerGroup, nextSibling);
326
+ // } else {
327
+ // // 如果没有下一个兄弟元素,说明原来是最后一个,直接appendChild
328
+ // this.layersGroup.appendChild(newLayerGroup);
329
+ // }
330
+ // }
331
+ // }
332
+ // }
333
+ // } else {
334
+ // // 重绘所有图层
335
+ // for (const layer of this.layers) {
336
+ // if (layer.isVisible()) {
337
+ // const layerGroup = this.createSVGGroup(`layer-${layer.getType()}-${layer.getLevel()}`);
338
+ // layer.drawSVG(layerGroup, this.scale, this.lineScale);
339
+ // this.layersGroup.appendChild(layerGroup);
340
+ // }
341
+ // }
342
+ // }
243
343
  }
244
344
  /**
245
345
  * 刷新渲染
@@ -918,6 +1018,7 @@ class PathLayer extends BaseLayer {
918
1018
  this.level = 3;
919
1019
  this.scale = 1;
920
1020
  this.lineScale = 1;
1021
+ this.boundaryPaths = {};
921
1022
  this.type = LAYER_DEFAULT_TYPE.PATH;
922
1023
  }
923
1024
  /**
@@ -976,14 +1077,18 @@ class PathLayer extends BaseLayer {
976
1077
  group.setAttribute('opacity', '0.35'); // 统一透明度,防止叠加脏乱
977
1078
  // 3. 渲染所有路径
978
1079
  for (const element of this.elements) {
979
- this.renderPathToGroup(group, element);
1080
+ const { id, elements } = element;
1081
+ this.boundaryPaths[id] = [];
1082
+ elements.forEach((element) => {
1083
+ this.renderPathToGroup(group, id, element);
1084
+ });
980
1085
  }
981
1086
  svgGroup.appendChild(group);
982
1087
  }
983
1088
  /**
984
1089
  * 渲染单个路径到指定的组中
985
1090
  */
986
- renderPathToGroup(group, element) {
1091
+ renderPathToGroup(group, id, element) {
987
1092
  const { coordinates, style } = element;
988
1093
  if (coordinates.length < 2)
989
1094
  return;
@@ -999,7 +1104,7 @@ class PathLayer extends BaseLayer {
999
1104
  // 直接给fill的颜色设置透明度会导致path重叠的部分颜色叠加,所以使用fill填充实色,通过fill-opacity设置透明度
1000
1105
  path.setAttribute('fill', 'none');
1001
1106
  // path.setAttribute('fill-opacity', '0.4');
1002
- path.setAttribute('stroke', style.strokeColor || '#000000');
1107
+ path.setAttribute('stroke', style.lineColor || '#000000');
1003
1108
  path.setAttribute('mix-blend-mode', 'normal');
1004
1109
  const lineWidth = Math.max(style.lineWidth || 1, 0.5);
1005
1110
  path.setAttribute('stroke-width', lineWidth.toString());
@@ -1007,13 +1112,8 @@ class PathLayer extends BaseLayer {
1007
1112
  path.setAttribute('stroke-linejoin', 'round');
1008
1113
  // 注意:这里不设置 opacity,因为透明度由父组控制
1009
1114
  // path.setAttribute('vector-effect', 'non-scaling-stroke');
1010
- if (style.lineDash && style.lineDash.length > 0) {
1011
- path.setAttribute('stroke-dasharray', style.lineDash.join(','));
1012
- }
1013
- else if (style.strokeDasharray) {
1014
- path.setAttribute('stroke-dasharray', style.strokeDasharray);
1015
- }
1016
1115
  path.classList.add('vector-path');
1116
+ this.boundaryPaths[id].push(path);
1017
1117
  group.appendChild(path);
1018
1118
  }
1019
1119
  }
@@ -1341,7 +1441,6 @@ class SvgElementLayer extends BaseLayer {
1341
1441
  renderSvgPlaceholder(svgGroup, center, metadata, style) {
1342
1442
  const size = (metadata?.scale || 1) * 20;
1343
1443
  const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
1344
- console.log('style==', style);
1345
1444
  rect.setAttribute('x', ((center[0] - size / 2) / 50).toString());
1346
1445
  rect.setAttribute('y', ((center[1] - size / 2) / 50).toString());
1347
1446
  rect.setAttribute('width', size.toString());
@@ -1414,6 +1513,7 @@ const BOUNDARY_STYLES = {
1414
1513
  fillColor: 'rgba(239, 255, 237, 0.15)', // 更鲜艳的绿色半透明填充,增强可见性
1415
1514
  lineWidth: 2,
1416
1515
  opacity: DEFAULT_OPACITIES.FULL,
1516
+ mowingLineColor: 'rgba(99, 216, 174, 1)',
1417
1517
  };
1418
1518
  const VISION_OFF_AREA_STYLES = {
1419
1519
  lineColor: 'rgba(108, 167, 255, 1)',
@@ -1445,7 +1545,8 @@ const PATH_EDGE_STYLES = {
1445
1545
  opacity: DEFAULT_OPACITIES.LOW,
1446
1546
  edgeLineColor: 'rgba(194, 203, 212)',
1447
1547
  transLineColor: 'transparent',
1448
- mowingLineColor: 'rgba(194, 203, 212)',
1548
+ mowedLineColor: 'rgba(194, 203, 212)',
1549
+ mowingLineColor: 'rgba(123, 200, 187)',
1449
1550
  };
1450
1551
  const CHANNEL_STYLES = {
1451
1552
  lineColor: 'purple',
@@ -1513,6 +1614,15 @@ function convertCoordinate(x, y) {
1513
1614
  };
1514
1615
  }
1515
1616
 
1617
+ /**
1618
+ * 路径段类型
1619
+ */
1620
+ var PathSegmentType;
1621
+ (function (PathSegmentType) {
1622
+ PathSegmentType["EDGE"] = "edge";
1623
+ PathSegmentType["MOWING"] = "mowing";
1624
+ PathSegmentType["TRANS"] = "trans";
1625
+ })(PathSegmentType || (PathSegmentType = {}));
1516
1626
  /**
1517
1627
  * 按Python逻辑创建路径段:根据连续的两点之间的关系确定线段类型
1518
1628
  */
@@ -1529,11 +1639,11 @@ function createPathSegmentsByType(list) {
1529
1639
  let currentSegmentType = null;
1530
1640
  for (const currentPoint of list) {
1531
1641
  const currentCoord = {
1532
- x: currentPoint.postureX * SCALE_FACTOR,
1533
- y: -currentPoint.postureY * SCALE_FACTOR // Y轴翻转
1642
+ x: currentPoint.postureX,
1643
+ y: currentPoint.postureY
1534
1644
  };
1535
1645
  if (lastPoint !== null) {
1536
- // 判断上一个点和当前点是否需要绘制 (Python逻辑)
1646
+ // 判断上一个点和当前点是否需要绘制 (iso端逻辑)
1537
1647
  const lastShouldDraw = lastPoint.pathType === '00' || lastPoint.pathType === '01' || lastPoint.knifeRotation === '01';
1538
1648
  const currentShouldDraw = currentPoint.pathType === '00' || currentPoint.pathType === '01' || currentPoint.knifeRotation === '01';
1539
1649
  let segmentType;
@@ -1553,8 +1663,8 @@ function createPathSegmentsByType(list) {
1553
1663
  // 开始新段
1554
1664
  currentSegment = [
1555
1665
  {
1556
- x: lastPoint.postureX * SCALE_FACTOR,
1557
- y: -lastPoint.postureY * SCALE_FACTOR
1666
+ x: lastPoint.postureX,
1667
+ y: lastPoint.postureY
1558
1668
  },
1559
1669
  currentCoord
1560
1670
  ];
@@ -1858,181 +1968,6 @@ function generateBoundaryData(mapData, pathData) {
1858
1968
  return boundaryData;
1859
1969
  }
1860
1970
 
1861
- /**
1862
- * 数据格式化转换类 - TypeScript版本
1863
- * 对应Java FormatUtils.java中的bytes2Int函数
1864
- */
1865
- class FormatUtils {
1866
- /**
1867
- * 从data数组的第0位开始取4个字节组成一个int型数
1868
- * 对应Java: public static int bytes2Int(byte[] data)
1869
- *
1870
- * @param data 源字节数组
1871
- * @returns 返回int型数
1872
- */
1873
- // public static bytes2Int(data: Uint8Array): number {
1874
- // return FormatUtils.bytes2IntWithOffset(data, 0, false);
1875
- // }
1876
- /**
1877
- * 从data数组的第position位置开始取4个字节组成int型数
1878
- * 对应Java: public static int bytes2Int(byte[] data, int offset, boolean bigEndian)
1879
- *
1880
- * @param data 源字节数组
1881
- * @param offset 要组成int型数据的起始位置
1882
- * @param bigEndian 是否为大端
1883
- * @returns 返回int型数
1884
- */
1885
- static bytes2Int(data, offset, bigEndian) {
1886
- return FormatUtils.bytes2IntWidthLength(data, offset, bigEndian, 4);
1887
- }
1888
- /**
1889
- * 从data数组的第position位置开始取几个字节组成int型数
1890
- * 对应Java: public static int bytes2Int(byte[] data, int offset, boolean bigEndian, int length)
1891
- *
1892
- * @param data 源字节数组
1893
- * @param offset 要组成int型数据的起始位置
1894
- * @param bigEndian 是否为大端
1895
- * @param length 取的字节长度
1896
- * @returns 返回int型数
1897
- */
1898
- static bytes2IntWidthLength(data, offset, bigEndian, length) {
1899
- if (!data || offset < 0 || offset > data.length) {
1900
- return 0;
1901
- }
1902
- let result = 0;
1903
- try {
1904
- // 创建DataView来处理字节序
1905
- const buffer = new ArrayBuffer(length);
1906
- const view = new DataView(buffer);
1907
- // 将指定长度的数据复制到buffer中
1908
- const tempArray = new Uint8Array(buffer);
1909
- for (let i = 0; i < length && offset + i < data.length; i++) {
1910
- tempArray[i] = data[offset + i];
1911
- }
1912
- // 根据字节序读取数据(使用有符号整数,与Java保持一致)
1913
- if (length === 1) {
1914
- result = view.getInt8(0);
1915
- }
1916
- else if (length === 2) {
1917
- result = view.getInt16(0, !bigEndian); // DataView的littleEndian参数与bigEndian相反
1918
- }
1919
- else if (length === 4) {
1920
- result = view.getInt32(0, !bigEndian); // 改为 getInt32,返回有符号整数
1921
- }
1922
- else {
1923
- // 对于其他长度,手动处理
1924
- result = FormatUtils.manualBytes2Int(data, offset, bigEndian, length);
1925
- }
1926
- }
1927
- catch (e) {
1928
- // console.error(
1929
- // `${FormatUtils.TAG}: bytes2Int: Exception : data = ${data}, offset = ${offset}`,
1930
- // e
1931
- // );
1932
- // console.log(
1933
- // `${FormatUtils.TAG}: bytes2Int: Exception = ${e instanceof Error ? e.message : e}`
1934
- // );
1935
- }
1936
- return result;
1937
- }
1938
- /**
1939
- * 手动处理字节到整数的转换(用于非标准长度)
1940
- *
1941
- * @param data 源字节数组
1942
- * @param offset 起始位置
1943
- * @param bigEndian 是否为大端
1944
- * @param length 字节长度
1945
- * @returns 转换后的整数
1946
- */
1947
- static manualBytes2Int(data, offset, bigEndian, length) {
1948
- let result = 0;
1949
- if (bigEndian) {
1950
- // 大端序:最高位字节在最低地址
1951
- for (let i = 0; i < length && offset + i < data.length; i++) {
1952
- result = (result << 8) | (data[offset + i] & 0xff);
1953
- }
1954
- }
1955
- else {
1956
- // 小端序:最低位字节在最低地址
1957
- for (let i = length - 1; i >= 0; i--) {
1958
- if (offset + i < data.length) {
1959
- result = (result << 8) | (data[offset + i] & 0xff);
1960
- }
1961
- }
1962
- }
1963
- return result;
1964
- }
1965
- /**
1966
- * 将字节数组转换为十六进制字符串
1967
- * 对应Java: public static String bytesToHex(byte[] bytes)
1968
- *
1969
- * @param bytes 字节数组
1970
- * @returns 十六进制字符串
1971
- */
1972
- static bytesToHex(bytes) {
1973
- if (!bytes) {
1974
- return 'null';
1975
- }
1976
- const hexArray = '0123456789ABCDEF'.split('');
1977
- const hexChars = [];
1978
- for (let j = 0; j < bytes.length; j++) {
1979
- const v = bytes[j] & 0xff;
1980
- hexChars.push(hexArray[v >>> 4]);
1981
- hexChars.push(hexArray[v & 0x0f]);
1982
- }
1983
- return hexChars.join('');
1984
- }
1985
- /**
1986
- * 将十六进制字符串转换为字节数组
1987
- * 对应Java: public static byte[] hexStringToByteArray(String s)
1988
- *
1989
- * @param s 十六进制字符串
1990
- * @returns 字节数组
1991
- */
1992
- static hexStringToByteArray(s) {
1993
- if (!s || s.length <= 0) {
1994
- return null;
1995
- }
1996
- s = s.trim();
1997
- const len = s.length;
1998
- const data = new Uint8Array(len / 2);
1999
- for (let i = 0; i < len - 1; i += 2) {
2000
- try {
2001
- const high = parseInt(s.charAt(i), 16);
2002
- const low = parseInt(s.charAt(i + 1), 16);
2003
- data[i / 2] = (high << 4) + low;
2004
- }
2005
- catch (e) {
2006
- console.log('hexStringToByteArray: ' + e);
2007
- }
2008
- }
2009
- return data;
2010
- }
2011
- }
2012
- FormatUtils.TAG = 'FormatUtils';
2013
- // 使用示例
2014
- // export function example() {
2015
- // // 示例1:从字节数组开头读取4字节整数(小端序)
2016
- // const data1 = new Uint8Array([0x12, 0x34, 0x56, 0x78]);
2017
- // const result1 = FormatUtils.bytes2Int(data1); // 应该返回 0x78563412
2018
- // // 示例2:从指定位置读取4字节整数(大端序)
2019
- // const data2 = new Uint8Array([0x00, 0x00, 0x12, 0x34, 0x56, 0x78]);
2020
- // const result2 = FormatUtils.bytes2Int(data2, 2, true); // 应该返回 0x12345678
2021
- // // 示例3:读取2字节整数
2022
- // const data3 = new Uint8Array([0x12, 0x34]);
2023
- // const result3 = FormatUtils.bytes2Int(data3, 0, false, 2); // 应该返回 0x3412
2024
- // console.log(`Result1: 0x${result1.toString(16)}`);
2025
- // console.log(`Result2: 0x${result2.toString(16)}`);
2026
- // console.log(`Result3: 0x${result3.toString(16)}`);
2027
- // // 示例4:十六进制字符串转换
2028
- // const hexString = "12345678";
2029
- // const byteArray = FormatUtils.hexStringToByteArray(hexString);
2030
- // const backToHex = FormatUtils.bytesToHex(byteArray);
2031
- // console.log(`Original: ${hexString}`);
2032
- // console.log(`To bytes: ${byteArray}`);
2033
- // console.log(`Back to hex: ${backToHex}`);
2034
- // }
2035
-
2036
1971
  /**
2037
1972
  * 射线法判断点是否在多边形内部
2038
1973
  * @param x 点的x坐标
@@ -2127,50 +2062,6 @@ const getPartitionId = (partitionBoundary, postureX, postureY) => {
2127
2062
  })?.id;
2128
2063
  return partitionId;
2129
2064
  };
2130
- /**
2131
- *
2132
- * 支持分区割草后的解析,加入了分区数量、分区列表
2133
- * 8 割草路径类型
2134
- * 4 割草启动类型 0:自动 1:手动
2135
- * 4 当前割草边界id
2136
- * 4 当前边界割草百分比 比如9800----98%
2137
- * @returns
2138
- */
2139
- const parseMapWorkPosition = (mapWorkPosition) => {
2140
- let isMowing = false;
2141
- let mowStartType = null;
2142
- let currentMowBoundaryId = null;
2143
- let currentMowProgress = null;
2144
- const bytes = new Uint8Array(mapWorkPosition.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) || []);
2145
- if (mapWorkPosition.length >= 8) {
2146
- // 以下两种状态认为是割草中
2147
- // 1. action为8,且subAction为6
2148
- // 2. action为5
2149
- const action = FormatUtils.bytes2Int(bytes, 0, true);
2150
- if (action === ACTION_BOUNDARY_TASK) {
2151
- const subAction = FormatUtils.bytes2Int(bytes, 4, true);
2152
- if (subAction === ACTION_BLOCK_TRANSFER) {
2153
- // action=8且subAction=6:边界任务中的块转移
2154
- isMowing = true;
2155
- }
2156
- }
2157
- else if (action === ACTION_BLOCK_COVER) {
2158
- // action=5:块覆盖割草
2159
- isMowing = true;
2160
- }
2161
- if (mapWorkPosition.length >= 16) {
2162
- mowStartType = FormatUtils.bytes2Int(bytes, 8, true);
2163
- currentMowBoundaryId = FormatUtils.bytes2Int(bytes, 12, true);
2164
- currentMowProgress = FormatUtils.bytes2Int(bytes, 16, true);
2165
- }
2166
- }
2167
- return {
2168
- isMowing,
2169
- mowStartType,
2170
- currentMowBoundaryId,
2171
- currentMowProgress,
2172
- };
2173
- };
2174
2065
  /**
2175
2066
  * 处理实时数据的消息,这里的实时数据消息有两种,一种是实时轨迹,一种是割草进度,其中这两种下发的时间频次不一样
2176
2067
  * 实时轨迹的路径需要依靠割草进度时候的割草状态判断,目前只能根据上一次获取到的割草进度的状态来处理,如果一开始没有割草的状态,则默认为不割草,后续会根据割草进度来更新
@@ -2178,7 +2069,7 @@ const parseMapWorkPosition = (mapWorkPosition) => {
2178
2069
  * @param param0
2179
2070
  * @returns
2180
2071
  */
2181
- const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundary, }) => {
2072
+ const handleMultipleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundary, }) => {
2182
2073
  // 先将数据进行倒排,这样好插入数据
2183
2074
  if (realTimeData.length > 0) {
2184
2075
  realTimeData.reverse();
@@ -2206,7 +2097,7 @@ const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundar
2206
2097
  postureY: Number(postureY),
2207
2098
  knifeRotation: mowingStatus && vehicleState === RobotStatus.MOWING ? '01' : '00', // "knifeRotation": "01",//刀盘是否转动 00-否 01-是
2208
2099
  // knifeRotation: '01', // "knifeRotation": "01",//刀盘是否转动 00-否 01-是
2209
- pathType: '01', //"pathType": "01",//路径类型 : 00-巡边 01-弓字型割草 02-地图测试 03-转移路径 04-避障路径 05-恢复/脱困路径
2100
+ pathType: '', //"pathType": "01",//路径类型 : 00-巡边 01-弓字型割草 02-地图测试 03-转移路径 04-避障路径 05-恢复/脱困路径
2210
2101
  partitionId: currentPartitionId.toString(), // TODO:不知道为什么这里的id需要是字符串类型?
2211
2102
  },
2212
2103
  ],
@@ -2215,21 +2106,20 @@ const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundar
2215
2106
  }
2216
2107
  else if (item.type === REAL_TIME_DATA_TYPE.PROCESS) {
2217
2108
  // 割草进度
2218
- const { mapWorkPosition } = item;
2219
- if (mapWorkPosition) {
2220
- // 解析mapWorkPosition数据,获取到当前的割草状态,割草进度,割草边界id
2221
- const { isMowing, currentMowBoundaryId, currentMowProgress } = parseMapWorkPosition(mapWorkPosition);
2222
- // const { isMowing, currentMowBoundaryId, currentMowProgress } = parseMapWorkPosition(
2223
- // '000000060000000000000001000000030000001BA5A5A5A5A5A5A5A5240000A024010D50FFFFFFED000000000000000000000000000000010000000000000000'
2224
- // );
2225
- console.log('isMowing===', isMowing, currentMowBoundaryId, currentMowProgress);
2226
- mowingStatus = isMowing;
2227
- const currentPartitionId = currentMowBoundaryId ? currentMowBoundaryId.toString() : null;
2228
- if (currentMowProgress && currentPartitionId && newPathData?.[currentPartitionId]) {
2229
- newPathData[currentPartitionId].partitionPercentage = currentMowProgress / 100;
2230
- newPathData[currentPartitionId].finishedArea =
2231
- (newPathData[currentPartitionId].area * currentMowProgress) / 10000;
2232
- }
2109
+ const { action, subAction, currentMowBoundary, currentMowProgress } = item;
2110
+ // 设置状态
2111
+ if ((action === ACTION_BOUNDARY_TASK && subAction && subAction === ACTION_BLOCK_TRANSFER) ||
2112
+ action === ACTION_BLOCK_COVER) {
2113
+ mowingStatus = true;
2114
+ }
2115
+ else {
2116
+ mowingStatus = false;
2117
+ }
2118
+ const currentPartitionId = currentMowBoundary ? currentMowBoundary.toString() : null;
2119
+ if (currentMowProgress && currentPartitionId && newPathData?.[currentPartitionId]) {
2120
+ newPathData[currentPartitionId].partitionPercentage = currentMowProgress / 100;
2121
+ newPathData[currentPartitionId].finishedArea =
2122
+ (newPathData[currentPartitionId].area * currentMowProgress) / 10000;
2233
2123
  }
2234
2124
  }
2235
2125
  });
@@ -2238,6 +2128,39 @@ const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundar
2238
2128
  isMowing: mowingStatus,
2239
2129
  };
2240
2130
  };
2131
+ /**
2132
+ * 根据实时数据,获取到割草状态
2133
+ * @param realTimeData 实时数据
2134
+ * @param isMowing 上一次的割草状态
2135
+ * @returns 新的割草状态
2136
+ */
2137
+ const getProcessMowingDataFromRealTimeData = ({ realTimeData, isMowing, pathData, }) => {
2138
+ let newMowingStatus = isMowing;
2139
+ let newPathData = pathData || {};
2140
+ // 找到返回的第一个实时进度的点
2141
+ const firstProcessData = realTimeData.find((item) => item.type === REAL_TIME_DATA_TYPE.PROCESS);
2142
+ if (firstProcessData) {
2143
+ // console.log('firstProcessData==', firstProcessData);
2144
+ const { action, subAction, currentMowBoundary, currentMowProgress } = firstProcessData;
2145
+ // 设置状态
2146
+ if ((action === ACTION_BOUNDARY_TASK && subAction && subAction === ACTION_BLOCK_TRANSFER) ||
2147
+ action === ACTION_BLOCK_COVER) {
2148
+ newMowingStatus = true;
2149
+ }
2150
+ else {
2151
+ newMowingStatus = false;
2152
+ }
2153
+ if (currentMowBoundary && newPathData?.[currentMowBoundary]) {
2154
+ newPathData[currentMowBoundary].partitionPercentage = currentMowProgress / 100;
2155
+ newPathData[currentMowBoundary].finishedArea =
2156
+ (newPathData[currentMowBoundary].area * currentMowProgress) / 10000;
2157
+ }
2158
+ }
2159
+ return {
2160
+ isMowing: newMowingStatus,
2161
+ pathData: newPathData,
2162
+ };
2163
+ };
2241
2164
 
2242
2165
  var iMower = "data:image/png;base64,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";
2243
2166
 
@@ -2294,6 +2217,8 @@ function getNoPositionMowerImageByModal(mowerModal) {
2294
2217
  return iNoPosition;
2295
2218
  }
2296
2219
  function getMowerImage(positonConfig) {
2220
+ if (!positonConfig)
2221
+ return '';
2297
2222
  const model = positonConfig.vehicleModel || '';
2298
2223
  const state = positonConfig.vehicleState;
2299
2224
  const mowerImage = getMowerImageByModal(model);
@@ -2331,9 +2256,9 @@ function isInvalidPosition(positonConfig) {
2331
2256
  }
2332
2257
  function isOutOfRange(positonConfig) {
2333
2258
  return (positonConfig.postureX != null &&
2334
- Math.abs(positonConfig.postureX) > 1000 * SCALE_FACTOR &&
2259
+ Math.abs(positonConfig.postureX) > 1000 &&
2335
2260
  positonConfig.postureY != null &&
2336
- Math.abs(positonConfig.postureY) > 1000 * SCALE_FACTOR);
2261
+ Math.abs(positonConfig.postureY) > 1000);
2337
2262
  }
2338
2263
 
2339
2264
  /** Detect free variable `global` from Node.js. */
@@ -4399,7 +4324,7 @@ function cloneBuffer(buffer, isDeep) {
4399
4324
  }
4400
4325
 
4401
4326
  /** Built-in value references. */
4402
- var Uint8Array$1 = root.Uint8Array;
4327
+ var Uint8Array = root.Uint8Array;
4403
4328
 
4404
4329
  /**
4405
4330
  * Creates a clone of `arrayBuffer`.
@@ -4410,7 +4335,7 @@ var Uint8Array$1 = root.Uint8Array;
4410
4335
  */
4411
4336
  function cloneArrayBuffer(arrayBuffer) {
4412
4337
  var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
4413
- new Uint8Array$1(result).set(new Uint8Array$1(arrayBuffer));
4338
+ new Uint8Array(result).set(new Uint8Array(arrayBuffer));
4414
4339
  return result;
4415
4340
  }
4416
4341
 
@@ -4788,10 +4713,6 @@ function radToDegree(radian) {
4788
4713
  function distance(x1, y1, x2, y2) {
4789
4714
  return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
4790
4715
  }
4791
- // 计算前进方向和车头的夹角
4792
- function calAngle(x1, y1, x2, y2) {
4793
- return Math.atan2((y2 - y1), (x2 - x1));
4794
- }
4795
4716
  const mathRound = (value, decimals = 2) => {
4796
4717
  return Number.isInteger(value) ? value : round(value, decimals);
4797
4718
  };
@@ -5037,6 +4958,7 @@ class BoundaryBorderLayer extends BaseLayer {
5037
4958
  this.scale = 1;
5038
4959
  this.level = LAYER_LEVELS.BOUNDARY_BORDER; // 中等层级
5039
4960
  this.type = LAYER_DEFAULT_TYPE.BOUNDARY_BORDER;
4961
+ this.boudaryBorderPaths = {};
5040
4962
  }
5041
4963
  /**
5042
4964
  * SVG渲染方法
@@ -5057,7 +4979,8 @@ class BoundaryBorderLayer extends BaseLayer {
5057
4979
  * 渲染边界边框
5058
4980
  */
5059
4981
  renderBoundaryBorder(svgGroup, element) {
5060
- const { coordinates, style } = element;
4982
+ const { coordinates, style, originalData } = element;
4983
+ const { id } = originalData || {};
5061
4984
  if (coordinates.length < 2)
5062
4985
  return;
5063
4986
  // 1. 先遍历所有的coordinates,把所有点分为若干段的path
@@ -5068,19 +4991,19 @@ class BoundaryBorderLayer extends BaseLayer {
5068
4991
  return;
5069
4992
  if (segment.type === 2) {
5070
4993
  // type=2: 直接添加到svgGroup中
5071
- this.createDirectPath(svgGroup, segment.points, style);
4994
+ this.createDirectPath(svgGroup, segment.points, style, id);
5072
4995
  }
5073
4996
  else if (segment.type === 1) {
5074
4997
  // type=1: 使用PathMeasure逻辑生成平行路径
5075
4998
  // this.createDirectPath(svgGroup, segment.points, style);
5076
- this.createParallelPathsWithMeasure(svgGroup, segment.points, style);
4999
+ this.createParallelPathsWithMeasure(svgGroup, segment.points, style, id);
5077
5000
  }
5078
5001
  });
5079
5002
  }
5080
5003
  /**
5081
5004
  * 创建直接路径(type=2)
5082
5005
  */
5083
- createDirectPath(svgGroup, points, style) {
5006
+ createDirectPath(svgGroup, points, style, id) {
5084
5007
  const strokeColor = style.lineColor;
5085
5008
  const lineWidth = dp2px(style.lineWidth || 3);
5086
5009
  const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
@@ -5104,12 +5027,16 @@ class BoundaryBorderLayer extends BaseLayer {
5104
5027
  path.setAttribute('opacity', (style.opacity || 1).toString());
5105
5028
  path.setAttribute('vector-effect', 'non-scaling-stroke');
5106
5029
  path.classList.add('vector-boundary-solid');
5030
+ if (!this.boudaryBorderPaths[id]) {
5031
+ this.boudaryBorderPaths[id] = [];
5032
+ }
5033
+ this.boudaryBorderPaths[id].push(path);
5107
5034
  svgGroup.appendChild(path);
5108
5035
  }
5109
5036
  /**
5110
5037
  * 使用PathMeasure逻辑创建平行路径(type=1)
5111
5038
  */
5112
- createParallelPathsWithMeasure(svgGroup, points, style) {
5039
+ createParallelPathsWithMeasure(svgGroup, points, style, id) {
5113
5040
  const strokeColor = style.lineColor;
5114
5041
  const lineWidth = dp2px(style.lineWidth || 3);
5115
5042
  // 获取当前SVG的缩放级别,计算固定屏幕像素间距
@@ -5133,6 +5060,10 @@ class BoundaryBorderLayer extends BaseLayer {
5133
5060
  // 或者可以根据当前缩放级别动态计算dash array
5134
5061
  path.style.strokeDasharray = `${lineWidth}px ${lineWidth * 2}px`;
5135
5062
  path.classList.add(`vector-boundary-parallel-${index + 1}`);
5063
+ if (!this.boudaryBorderPaths[id]) {
5064
+ this.boudaryBorderPaths[id] = [];
5065
+ }
5066
+ this.boudaryBorderPaths[id].push(path);
5136
5067
  svgGroup.appendChild(path);
5137
5068
  });
5138
5069
  }
@@ -5222,6 +5153,9 @@ class BoundaryBorderLayer extends BaseLayer {
5222
5153
  }
5223
5154
  return segments;
5224
5155
  }
5156
+ resetPaths() {
5157
+ this.boudaryBorderPaths = {};
5158
+ }
5225
5159
  }
5226
5160
 
5227
5161
  var antennaOneOnline = "data:image/png;base64,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";
@@ -5434,7 +5368,7 @@ class DrawLayer extends BaseLayer {
5434
5368
  this.pointLayer,
5435
5369
  this.svgElementLayer,
5436
5370
  this.visionOffLayer,
5437
- ];
5371
+ ]?.filter((layer) => layer.getElements().length > 0);
5438
5372
  }
5439
5373
  getPathLayers() {
5440
5374
  return this.pathLayer;
@@ -5763,7 +5697,6 @@ class MapDataProcessor {
5763
5697
  if (mapData.vision_off_areas && mapData.vision_off_areas.length > 0) {
5764
5698
  allElements.push(...mapData.vision_off_areas);
5765
5699
  }
5766
- console.log('allElements', allElements);
5767
5700
  // 按照元素类型分组并设置不同的层级
5768
5701
  return this.createLayeredMapData(allElements);
5769
5702
  }
@@ -5914,7 +5847,6 @@ class MapDataProcessor {
5914
5847
  element.direction !== undefined) {
5915
5848
  const mapElement = element;
5916
5849
  const svgElement = SvgElementDataBuilder.fromMapElement(mapElement, this.mapConfig.doodle);
5917
- console.log('svgElement==', svgElement);
5918
5850
  if (svgElement)
5919
5851
  result.push(svgElement);
5920
5852
  }
@@ -5971,35 +5903,15 @@ class PathDataBuilder {
5971
5903
  style,
5972
5904
  };
5973
5905
  }
5974
- // /**
5975
- // * 创建隧道元素数据
5976
- // */
5977
- // static createTunnel(element: MapElement): DrawElement | null {
5978
- // const convertedPoints = convertPointsFormat(element.tunnel?.points || element.points);
5979
- // if (!convertedPoints || convertedPoints.length < 2) return null;
5980
- // return this.create(convertedPoints, DEFAULT_STYLES.PATH_EDGE);
5981
- // }
5982
- // /**
5983
- // * 创建充电桩内部隧道路径(用于CHARGING_PILE元素中的tunnel.points)
5984
- // */
5985
- // static createChargingPileTunnelPath(element: MapElement): DrawElement | null {
5986
- // if (!element.tunnel || !element.tunnel.points) return null;
5987
- // const convertedTunnelPoints = convertPointsFormat(element.tunnel.points);
5988
- // if (!convertedTunnelPoints || convertedTunnelPoints.length < 2) return null;
5989
- // return this.create(convertedTunnelPoints, {
5990
- // strokeColor: DEFAULT_STYLES.CHANNEL.strokeColor,
5991
- // lineWidth: 2,
5992
- // opacity: 1.0
5993
- // });
5994
- // }
5995
5906
  /**
5996
5907
  * 创建边缘路径
5997
5908
  */
5998
5909
  static createEdgePath(points, config) {
5999
- const drawElement = this.create(points, {
6000
- strokeColor: config?.color || DEFAULT_STYLES.PATH_EDGE.strokeColor,
6001
- lineWidth: config?.lineWidth || 2,
6002
- opacity: config?.opacity || 1.0,
5910
+ const convertedPoints = convertPointsFormat(points);
5911
+ const drawElement = this.create(convertedPoints, {
5912
+ lineColor: config?.color,
5913
+ lineWidth: config?.lineWidth,
5914
+ opacity: config?.opacity,
6003
5915
  });
6004
5916
  drawElement.originalData = points;
6005
5917
  return drawElement;
@@ -6008,10 +5920,11 @@ class PathDataBuilder {
6008
5920
  * 创建割草路径
6009
5921
  */
6010
5922
  static createMowingPath(points, config) {
6011
- const drawElement = this.create(points, {
6012
- strokeColor: config?.color || DEFAULT_STYLES.PATH_EDGE.strokeColor,
6013
- lineWidth: config?.lineWidth || DEFAULT_STYLES.PATH_EDGE.lineWidth,
6014
- opacity: config?.opacity || 1.0,
5923
+ const convertedPoints = convertPointsFormat(points);
5924
+ const drawElement = this.create(convertedPoints, {
5925
+ lineColor: config?.color,
5926
+ lineWidth: config?.lineWidth,
5927
+ opacity: config?.opacity,
6015
5928
  });
6016
5929
  drawElement.originalData = points;
6017
5930
  return drawElement;
@@ -6020,11 +5933,11 @@ class PathDataBuilder {
6020
5933
  * 创建传输路径
6021
5934
  */
6022
5935
  static createTransPath(points, config) {
6023
- const drawElement = this.create(points, {
6024
- strokeColor: config?.color || DEFAULT_STYLES.PATH_EDGE.strokeColor,
6025
- lineWidth: config?.lineWidth || DEFAULT_STYLES.PATH_EDGE.lineWidth,
6026
- lineDash: [5, 5], // 虚线样式
6027
- opacity: config?.opacity || 1.0,
5936
+ const convertedPoints = convertPointsFormat(points);
5937
+ const drawElement = this.create(convertedPoints, {
5938
+ lineColor: config?.color,
5939
+ lineWidth: config?.lineWidth,
5940
+ opacity: config?.opacity,
6028
5941
  });
6029
5942
  drawElement.originalData = points;
6030
5943
  return drawElement;
@@ -6043,55 +5956,60 @@ class PathDataProcessor {
6043
5956
  if (!pathData || typeof pathData !== 'object') {
6044
5957
  return [];
6045
5958
  }
6046
- // 获取所有分区的路径数据
6047
- const allPathItems = Object.values(pathData).reduce((acc, partitionData) => {
6048
- if (partitionData && partitionData.points && partitionData.points.length > 0) {
6049
- acc.push(...partitionData.points);
6050
- }
6051
- return acc;
6052
- }, []);
6053
5959
  // 合并配置
6054
5960
  const config = mapConfig.path;
6055
- // 使用Python相同的逻辑:按线段分组而不是按点分组
6056
- const pathSegments = createPathSegmentsByType(allPathItems);
6057
- const elements = [];
6058
- // 处理边缘路径段
6059
- for (const segment of pathSegments.edge) {
6060
- // 转换 Point[] 为 number[][]
6061
- const points = segment.points.map((point) => [point.x, point.y]);
6062
- const element = PathDataBuilder.createEdgePath(points, {
6063
- lineWidth: config.lineWidth,
6064
- color: config.edgeLineColor,
6065
- opacity: config.opacity,
6066
- });
6067
- element.originalData = points;
6068
- elements.push(element);
6069
- }
6070
- // 处理割草路径段
6071
- for (const segment of pathSegments.mowing) {
6072
- // 转换 Point[] 为 number[][]
6073
- const points = segment.points.map((point) => [point.x, point.y]);
6074
- const element = PathDataBuilder.createMowingPath(points, {
6075
- lineWidth: config.lineWidth,
6076
- color: config.mowingLineColor,
6077
- opacity: config.opacity,
6078
- });
6079
- element.originalData = points;
6080
- elements.push(element);
6081
- }
6082
- // 处理传输路径段(只有在showTransPaths为true时才添加)
6083
- for (const segment of pathSegments.trans) {
6084
- // 转换 Point[] 为 number[][]
6085
- const points = segment.points.map((point) => [point.x, point.y]);
6086
- const element = PathDataBuilder.createTransPath(points, {
6087
- lineWidth: config.lineWidth,
6088
- color: config.transLineColor,
6089
- opacity: config.opacity,
6090
- });
6091
- element.originalData = points;
6092
- elements.push(element);
6093
- }
6094
- return elements;
5961
+ const result = Object.keys(pathData).map((key) => {
5962
+ const id = Number(key);
5963
+ const points = pathData[key].points || [];
5964
+ // 使用Python相同的逻辑:按线段分组而不是按点分组
5965
+ const pathSegments = createPathSegmentsByType(points);
5966
+ const elements = [];
5967
+ // 处理边缘路径段
5968
+ for (const segment of pathSegments.edge) {
5969
+ // 转换 Point[] 为 number[][]
5970
+ const points = segment.points.map((point) => [point.x, point.y]);
5971
+ const element = PathDataBuilder.createEdgePath(points, {
5972
+ lineWidth: config.lineWidth,
5973
+ color: config.edgeLineColor,
5974
+ opacity: config.opacity,
5975
+ });
5976
+ element.originalData = points;
5977
+ element.pathType = PathSegmentType.EDGE;
5978
+ elements.push(element);
5979
+ }
5980
+ // 处理割草路径段
5981
+ for (const segment of pathSegments.mowing) {
5982
+ // 转换 Point[] 为 number[][]
5983
+ const points = segment.points.map((point) => [point.x, point.y]);
5984
+ const element = PathDataBuilder.createMowingPath(points, {
5985
+ lineWidth: config.lineWidth,
5986
+ color: config.mowedLineColor,
5987
+ opacity: config.opacity,
5988
+ });
5989
+ element.originalData = points;
5990
+ element.pathType = PathSegmentType.MOWING;
5991
+ elements.push(element);
5992
+ }
5993
+ // 处理传输路径段(只有在showTransPaths为true时才添加)
5994
+ for (const segment of pathSegments.trans) {
5995
+ // 转换 Point[] 为 number[][]
5996
+ const points = segment.points.map((point) => [point.x, point.y]);
5997
+ const element = PathDataBuilder.createTransPath(points, {
5998
+ lineWidth: config.lineWidth,
5999
+ color: config.transLineColor,
6000
+ opacity: config.opacity,
6001
+ });
6002
+ element.originalData = points;
6003
+ element.pathType = PathSegmentType.TRANS;
6004
+ elements.push(element);
6005
+ }
6006
+ return {
6007
+ id,
6008
+ elements,
6009
+ };
6010
+ });
6011
+ console.log('result->', result);
6012
+ return result;
6095
6013
  }
6096
6014
  }
6097
6015
 
@@ -6106,6 +6024,8 @@ class BoundaryLabelsManager {
6106
6024
  this.globalClickHandler = null;
6107
6025
  // 当前展开的边界id
6108
6026
  this.currentExpandedBoundaryId = null;
6027
+ // 旋转角度
6028
+ this.rotation = 0;
6109
6029
  this.svgView = svgView;
6110
6030
  this.boundaryData = boundaryData;
6111
6031
  this.initializeContainer();
@@ -6182,17 +6102,17 @@ class BoundaryLabelsManager {
6182
6102
  labelDiv.style.fontWeight = 'bold';
6183
6103
  labelDiv.style.whiteSpace = 'nowrap';
6184
6104
  labelDiv.style.maxWidth = '220px';
6185
- labelDiv.style.transform = 'translate(-50%, -50%)';
6105
+ labelDiv.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
6186
6106
  labelDiv.style.pointerEvents = 'auto';
6187
6107
  labelDiv.style.boxShadow = '0 2px 8px rgba(0,0,0,0.4)';
6188
6108
  labelDiv.style.cursor = 'pointer';
6189
- labelDiv.style.transition = 'background-color 0.2s ease, transform 0.2s ease';
6109
+ labelDiv.style.transition = 'background-color 0.2s ease';
6190
6110
  labelDiv.style.userSelect = 'none';
6191
6111
  labelDiv.style.zIndex = BoundaryLabelsManager.Z_INDEX.DEFAULT.toString();
6192
6112
  // 计算进度
6193
- const progress = boundary.finishedArea && boundary.area ?
6194
- `${Math.round((boundary.finishedArea / boundary.area) * 100)}%` :
6195
- '0%';
6113
+ const progress = boundary.finishedArea && boundary.area
6114
+ ? `${Math.round((boundary.finishedArea / boundary.area) * 100)}%`
6115
+ : '0%';
6196
6116
  // 基础内容(始终显示)
6197
6117
  const baseContent = document.createElement('div');
6198
6118
  baseContent.className = 'boundary-label-base';
@@ -6204,7 +6124,8 @@ class BoundaryLabelsManager {
6204
6124
  extendedContent.style.marginTop = '6px';
6205
6125
  extendedContent.style.fontSize = '11px';
6206
6126
  extendedContent.style.opacity = '0.9';
6207
- extendedContent.style.display = this.currentExpandedBoundaryId === boundary.id ? 'block' : 'none';
6127
+ extendedContent.style.display =
6128
+ this.currentExpandedBoundaryId === boundary.id ? 'block' : 'none';
6208
6129
  extendedContent.style.borderTop = '1px solid rgba(255,255,255,0.2)';
6209
6130
  extendedContent.style.paddingTop = '6px';
6210
6131
  // 面积信息
@@ -6285,7 +6206,7 @@ class BoundaryLabelsManager {
6285
6206
  if (!this.container)
6286
6207
  return;
6287
6208
  const allLabels = this.container.querySelectorAll('.boundary-label');
6288
- allLabels.forEach(label => {
6209
+ allLabels.forEach((label) => {
6289
6210
  const labelBoundaryId = label.getAttribute('data-boundary-id');
6290
6211
  if (labelBoundaryId && labelBoundaryId !== boundaryId.toString()) {
6291
6212
  const extendedContent = label.querySelector('.boundary-label-extended');
@@ -6318,6 +6239,7 @@ class BoundaryLabelsManager {
6318
6239
  * @param viewBox SVG的viewBox信息
6319
6240
  */
6320
6241
  updatePositionsWithPrecomputedData(divWidth, divHeight, viewBox) {
6242
+ console.log('updatePositionsWithPrecomputedData----->', this.rotation);
6321
6243
  if (!this.container || !this.svgView)
6322
6244
  return;
6323
6245
  const dw = parseFloat(this.svgView.getSVG().getAttribute('width')) || divWidth;
@@ -6388,7 +6310,7 @@ class BoundaryLabelsManager {
6388
6310
  if (points.length < 3)
6389
6311
  return null;
6390
6312
  // 过滤有效点
6391
- const validPoints = points.filter(point => point.length >= 2);
6313
+ const validPoints = points.filter((point) => point.length >= 2);
6392
6314
  if (validPoints.length < 3)
6393
6315
  return null;
6394
6316
  // 确保多边形是封闭的(如果不是,自动闭合)
@@ -6437,7 +6359,7 @@ class BoundaryLabelsManager {
6437
6359
  });
6438
6360
  return {
6439
6361
  x: sumX / points.length,
6440
- y: sumY / points.length
6362
+ y: sumY / points.length,
6441
6363
  };
6442
6364
  }
6443
6365
  /**
@@ -6533,7 +6455,7 @@ class BoundaryLabelsManager {
6533
6455
  return;
6534
6456
  const allLabels = this.container.querySelectorAll('.boundary-label');
6535
6457
  this.currentExpandedBoundaryId = null;
6536
- allLabels.forEach(label => {
6458
+ allLabels.forEach((label) => {
6537
6459
  const extendedContent = label.querySelector('.boundary-label-extended');
6538
6460
  if (extendedContent) {
6539
6461
  extendedContent.style.display = 'none';
@@ -6548,7 +6470,7 @@ class BoundaryLabelsManager {
6548
6470
  if (!this.container)
6549
6471
  return;
6550
6472
  const allLabels = this.container.querySelectorAll('.boundary-label');
6551
- allLabels.forEach(label => {
6473
+ allLabels.forEach((label) => {
6552
6474
  const labelElement = label;
6553
6475
  if (interactive) {
6554
6476
  labelElement.style.pointerEvents = 'auto';
@@ -6579,22 +6501,39 @@ class BoundaryLabelsManager {
6579
6501
  return BoundaryLabelsManager.Z_INDEX;
6580
6502
  }
6581
6503
  /**
6582
- * 调试方法:检查当前所有边界标签的层级
6504
+ * 设置标签旋转角度,使其与地图旋转相反,保持水平状态
6505
+ * @param rotation 地图的旋转角度(度)
6583
6506
  */
6584
- debugCheckZIndex() {
6507
+ setRotation(rotation) {
6585
6508
  if (!this.container)
6586
6509
  return;
6587
- const allLabels = this.container.querySelectorAll('.boundary-label');
6588
- allLabels.forEach((label, index) => {
6510
+ this.rotation = rotation;
6511
+ const labels = this.container.querySelectorAll('.boundary-label');
6512
+ labels.forEach((label) => {
6513
+ const labelElement = label;
6514
+ // 应用与地图旋转相反的旋转,保持标签水平
6515
+ const counterRotation = -rotation;
6516
+ labelElement.style.transform = `translate(-50%, -50%) rotate(${counterRotation}deg)`;
6517
+ });
6518
+ }
6519
+ /**
6520
+ * 重置标签旋转角度
6521
+ */
6522
+ resetLabelsRotation() {
6523
+ if (!this.container)
6524
+ return;
6525
+ const labels = this.container.querySelectorAll('.boundary-label');
6526
+ labels.forEach((label) => {
6589
6527
  const labelElement = label;
6590
- labelElement.style.zIndex;
6528
+ // 重置为默认的居中变换
6529
+ labelElement.style.transform = 'translate(-50%, -50%)';
6591
6530
  });
6592
6531
  }
6593
6532
  }
6594
6533
  // 简化的层级定义
6595
6534
  BoundaryLabelsManager.Z_INDEX = {
6596
6535
  DEFAULT: 900, // 默认层级
6597
- ACTIVE: 9999 // 点击激活时的高层级
6536
+ ACTIVE: 9999, // 点击激活时的高层级
6598
6537
  };
6599
6538
 
6600
6539
  /**
@@ -6607,6 +6546,10 @@ class ChargingPileManager {
6607
6546
  this.container = null;
6608
6547
  this.overlayDiv = null;
6609
6548
  this.pileElements = new Map();
6549
+ // 原始旋转角度
6550
+ this.originalRotation = 0;
6551
+ // 旋转角度
6552
+ this.rotation = 0;
6610
6553
  this.svgView = svgView;
6611
6554
  this.initializeContainer();
6612
6555
  }
@@ -6661,7 +6604,9 @@ class ChargingPileManager {
6661
6604
  // 将弧度转换为角度
6662
6605
  const angle = (direction * 180) / Math.PI;
6663
6606
  const rotationDegree = 270 - angle; // 坐标系转换
6664
- pileDiv.style.transform = `translate(-50%, -50%) rotate(${rotationDegree}deg)`;
6607
+ this.originalRotation = rotationDegree;
6608
+ const actualRotation = rotationDegree - this.rotation;
6609
+ pileDiv.style.transform = `translate(-50%, -50%) rotate(${actualRotation}deg)`;
6665
6610
  // 添加动画
6666
6611
  // this.addChargingPileAnimation(pileDiv, imgElement, rotationDegree);
6667
6612
  // 生成唯一ID
@@ -6710,30 +6655,15 @@ class ChargingPileManager {
6710
6655
  updatePositions() {
6711
6656
  if (!this.overlayDiv || !this.container)
6712
6657
  return;
6713
- const divRect = this.overlayDiv.getBoundingClientRect();
6714
- const divWidth = divRect.width;
6715
- const divHeight = divRect.height;
6716
- // 获取SVG的viewBox
6717
- const svg = this.svgView.getSVG();
6718
- if (!svg)
6719
- return;
6720
- const viewBox = svg.viewBox.baseVal;
6721
- const viewBoxData = {
6722
- x: viewBox.x,
6723
- y: viewBox.y,
6724
- width: viewBox.width,
6725
- height: viewBox.height,
6726
- };
6727
- this.updatePositionsWithPrecomputedData(divWidth, divHeight, viewBoxData);
6658
+ this.updatePositionsWithPrecomputedData();
6728
6659
  }
6729
6660
  /**
6730
6661
  * 使用预计算数据更新位置
6731
6662
  */
6732
- updatePositionsWithPrecomputedData(divWidth, divHeight, viewBox) {
6663
+ updatePositionsWithPrecomputedData() {
6733
6664
  this.chargingPileElements.forEach((element, _index) => {
6734
6665
  const center = element.coordinates[0];
6735
- const pixelPosition = this.convertMapCoordinateToPixelWithPrecomputedData(center[0], center[1], divWidth, divHeight, viewBox);
6736
- console.log('updatePositionsWithPrecomputedData----->', element, pixelPosition, divWidth, divHeight, viewBox);
6666
+ const pixelPosition = this.convertMapCoordinateToPixelWithPrecomputedData(center[0], center[1]);
6737
6667
  if (pixelPosition) {
6738
6668
  const pileId = `pile_${center[0]}_${center[1]}`;
6739
6669
  const pileElement = this.pileElements.get(pileId);
@@ -6747,7 +6677,15 @@ class ChargingPileManager {
6747
6677
  /**
6748
6678
  * 使用预计算数据进行坐标转换
6749
6679
  */
6750
- convertMapCoordinateToPixelWithPrecomputedData(mapX, mapY, divWidth, divHeight, viewBox) {
6680
+ convertMapCoordinateToPixelWithPrecomputedData(mapX, mapY) {
6681
+ // 获取叠加层div的CSS尺寸
6682
+ const divWidth = parseFloat(this.overlayDiv.style.width) || this.overlayDiv.offsetWidth;
6683
+ const divHeight = parseFloat(this.overlayDiv.style.height) || this.overlayDiv.offsetHeight;
6684
+ // 获取SVG的viewBox
6685
+ const svg = this.svgView.getSVG();
6686
+ if (!svg)
6687
+ return { x: 0, y: 0 };
6688
+ const viewBox = svg.viewBox.baseVal;
6751
6689
  // 计算地图坐标在viewBox中的相对位置
6752
6690
  const relativeX = (mapX - viewBox.x) / viewBox.width;
6753
6691
  const relativeY = (mapY - viewBox.y) / viewBox.height;
@@ -6797,6 +6735,28 @@ class ChargingPileManager {
6797
6735
  this.container = null;
6798
6736
  this.overlayDiv = null;
6799
6737
  }
6738
+ /**
6739
+ * 设置充电桩旋转角度
6740
+ */
6741
+ setRotation(rotation) {
6742
+ this.rotation = rotation;
6743
+ const allContainers = this.container.querySelectorAll('.charging-pile');
6744
+ allContainers.forEach((container) => {
6745
+ const pileElement = container;
6746
+ pileElement.style.transform = `translate(-50%, -50%) rotate(${this.originalRotation - this.rotation}deg)`;
6747
+ });
6748
+ }
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
+ }
6800
6760
  }
6801
6761
  // 简化的层级定义 - 充电桩只需要一个固定层级
6802
6762
  ChargingPileManager.Z_INDEX = {
@@ -6818,6 +6778,8 @@ class AntennaManager {
6818
6778
  this.globalClickHandler = null;
6819
6779
  this.antennaTooltipFlag = false;
6820
6780
  this.singleAntennaTooltipFlag = false;
6781
+ // 旋转角度
6782
+ this.rotation = 0;
6821
6783
  this.svgView = svgView;
6822
6784
  this.initializeContainer();
6823
6785
  this.setupGlobalClickHandler();
@@ -6890,7 +6852,7 @@ class AntennaManager {
6890
6852
  const antennaContainer = document.createElement('div');
6891
6853
  antennaContainer.className = 'antenna-container-item';
6892
6854
  antennaContainer.style.position = 'absolute';
6893
- antennaContainer.style.transform = 'translate(-50%, -50%)';
6855
+ antennaContainer.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
6894
6856
  antennaContainer.style.pointerEvents = 'auto';
6895
6857
  antennaContainer.style.zIndex = AntennaManager.Z_INDEX.DEFAULT.toString();
6896
6858
  antennaContainer.setAttribute('data-antenna-id', antennaData.type.toString());
@@ -6931,8 +6893,6 @@ class AntennaManager {
6931
6893
  this.singleAntennaTooltipFlag = true;
6932
6894
  }
6933
6895
  this.expandTooltip(antennaContainer);
6934
- // 调试:检查层级变化
6935
- this.debugCheckZIndex();
6936
6896
  });
6937
6897
  // 添加悬停效果
6938
6898
  antennaDiv.addEventListener('mouseenter', () => {
@@ -7208,13 +7168,6 @@ class AntennaManager {
7208
7168
  static getZIndexConstants() {
7209
7169
  return AntennaManager.Z_INDEX;
7210
7170
  }
7211
- /**
7212
- * 调试方法:检查当前所有天线的层级
7213
- */
7214
- debugCheckZIndex() {
7215
- if (!this.container)
7216
- return;
7217
- }
7218
7171
  /**
7219
7172
  * 销毁管理器
7220
7173
  */
@@ -7231,6 +7184,28 @@ class AntennaManager {
7231
7184
  this.container = null;
7232
7185
  this.overlayDiv = null;
7233
7186
  }
7187
+ /**
7188
+ * 设置天线旋转角度
7189
+ */
7190
+ setRotation(rotation) {
7191
+ this.rotation = rotation;
7192
+ const allContainers = this.container.querySelectorAll('.antenna-container-item');
7193
+ allContainers.forEach((container) => {
7194
+ const antennaContainer = container;
7195
+ antennaContainer.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
7196
+ });
7197
+ }
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
+ }
7234
7209
  }
7235
7210
  // 简化的层级定义
7236
7211
  AntennaManager.Z_INDEX = {
@@ -7285,8 +7260,8 @@ const EDIT_BEHAVIOR = {
7285
7260
  SUCCESS_MESSAGE_DURATION_MS: 3000,
7286
7261
  };
7287
7262
 
7288
- class MowerPostionManager {
7289
- constructor(svgView, mowerPositonConfig, overlayDiv, onAnimationComplete) {
7263
+ class MowerPositionManager {
7264
+ constructor(svgView, mowerPositionConfig, overlayDiv, onAnimationComplete, onMowingPositionChange) {
7290
7265
  this.container = null;
7291
7266
  this.overlayDiv = null;
7292
7267
  this.mowerElement = null;
@@ -7303,9 +7278,10 @@ class MowerPostionManager {
7303
7278
  this.deltaPosition = null;
7304
7279
  this.onlyUpdateTheta = false;
7305
7280
  this.svgView = svgView;
7306
- this.mowerPositonConfig = mowerPositonConfig;
7281
+ this.mowerPositionConfig = mowerPositionConfig;
7307
7282
  this.overlayDiv = overlayDiv;
7308
7283
  this.onAnimationComplete = onAnimationComplete;
7284
+ this.onMowingPositionChange = onMowingPositionChange;
7309
7285
  this.initializeContainer();
7310
7286
  }
7311
7287
  get animationFlag() {
@@ -7351,7 +7327,7 @@ class MowerPostionManager {
7351
7327
  imgElement.style.height = '100%';
7352
7328
  imgElement.style.objectFit = 'contain';
7353
7329
  // 获取图片源
7354
- const imageSrc = getMowerImage(this.mowerPositonConfig);
7330
+ const imageSrc = getMowerImage(this.mowerPositionConfig);
7355
7331
  if (imageSrc) {
7356
7332
  imgElement.src = imageSrc;
7357
7333
  }
@@ -7373,51 +7349,52 @@ class MowerPostionManager {
7373
7349
  /**
7374
7350
  * 根据最后一次有效的位置更新数据
7375
7351
  */
7376
- updatePostionByLastPosition(chargingPilesPositonConfig, animationTime = 0) {
7377
- this.mowerPositonConfig = chargingPilesPositonConfig;
7378
- console.log('updatePostionByLastPosition----->', chargingPilesPositonConfig, this.lastPosition);
7352
+ updatePositionByLastPosition(chargingPilesPositionConfig) {
7353
+ if (!chargingPilesPositionConfig)
7354
+ return;
7355
+ this.mowerPositionConfig = chargingPilesPositionConfig;
7356
+ console.log('manager updatePositionByLastPosition----->', chargingPilesPositionConfig, this.lastPosition);
7379
7357
  const lastPosition = this.lastPosition;
7380
- if (!lastPosition) {
7381
- this.updatePosition(chargingPilesPositonConfig, animationTime);
7382
- }
7383
- else {
7384
- this.updatePosition({
7385
- ...chargingPilesPositonConfig,
7386
- postureX: lastPosition.x,
7387
- postureY: lastPosition.y,
7388
- postureTheta: lastPosition.rotation,
7389
- }, 0);
7390
- }
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;
7361
+ // 检查是否需要更新图片
7362
+ this.updateMowerImage(chargingPilesPositionConfig);
7363
+ // 立即更新位置
7364
+ this.setElementPosition(postureX, postureY, postureTheta);
7391
7365
  }
7392
7366
  /**
7393
7367
  * 更新割草机位置
7394
7368
  */
7395
- updatePosition(positonConfig, animationTime = 0) {
7396
- console.log('updatePosition----->', positonConfig);
7369
+ updatePosition(positionConfig, animationTime = 0) {
7370
+ console.log('manager updatePosition----->', positionConfig);
7397
7371
  // 检查是否需要更新图片
7398
- this.updateMowerImage(positonConfig);
7372
+ 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;
7399
7376
  // 停止当前动画(如果有)
7400
7377
  this.stopAnimation();
7401
7378
  // 第一个点
7402
7379
  if (!this.currentPosition) {
7403
7380
  this.currentPosition = {
7404
- x: positonConfig.postureX,
7405
- y: positonConfig.postureY,
7406
- rotation: positonConfig.postureTheta,
7381
+ x: postureX,
7382
+ y: postureY,
7383
+ rotation: postureTheta,
7407
7384
  };
7408
7385
  this.setElementPosition(this.currentPosition.x, this.currentPosition.y, this.currentPosition.rotation);
7409
7386
  return;
7410
7387
  }
7411
7388
  // 根据动画时长决定更新方式
7412
7389
  if (animationTime > 0) {
7413
- this.startAnimationToPosition(positonConfig, animationTime);
7390
+ this.startAnimationToPosition(positionConfig, animationTime);
7414
7391
  }
7415
7392
  else {
7416
7393
  // 立即更新位置
7417
- this.setElementPosition(positonConfig.postureX, positonConfig.postureY, positonConfig.postureTheta);
7394
+ this.setElementPosition(positionConfig.postureX, positionConfig.postureY, positionConfig.postureTheta);
7418
7395
  }
7419
7396
  // 更新配置
7420
- this.mowerPositonConfig = positonConfig;
7397
+ this.mowerPositionConfig = positionConfig;
7421
7398
  }
7422
7399
  /**
7423
7400
  * 更新割草机图片
@@ -7440,7 +7417,17 @@ class MowerPostionManager {
7440
7417
  const { x: pointX, y: pointY } = convertCoordinate(x, y);
7441
7418
  const targetPixelPosition = this.convertMapCoordinateToOverlayPixel(pointX, pointY);
7442
7419
  const targetRotation = radToDegree(theta);
7443
- this.lastPosition = { x, y, rotation: 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
+ }
7444
7431
  if (!this.mowerElement)
7445
7432
  return;
7446
7433
  this.mowerElement.style.left = `${targetPixelPosition?.x}px`;
@@ -7466,8 +7453,10 @@ class MowerPostionManager {
7466
7453
  startAnimationToPosition(positionConfig, duration) {
7467
7454
  if (!this.mowerElement || !this.currentPosition)
7468
7455
  return;
7469
- console.log('before startAnimationToPosition----->', this.startPosition, this.currentPosition, this.targetPosition, positionConfig);
7470
- this.startPosition = { ...this.currentPosition, rotation: radNormalize(this.currentPosition.rotation) };
7456
+ this.startPosition = {
7457
+ ...this.currentPosition,
7458
+ rotation: radNormalize(this.currentPosition.rotation),
7459
+ };
7471
7460
  this.targetPosition = {
7472
7461
  x: positionConfig.postureX,
7473
7462
  y: positionConfig.postureY,
@@ -7479,16 +7468,19 @@ class MowerPostionManager {
7479
7468
  this.onlyUpdateTheta =
7480
7469
  distance(this.currentPosition.x, this.currentPosition.y, positionConfig.postureX, positionConfig.postureY) < 0.2;
7481
7470
  // 为了实现倒车时,割草机图标旋转方向正确,需要计算当前位置和目标位置的夹角
7482
- const calTheta = calAngle(this.startPosition.x, this.startPosition.y, this.targetPosition.x, this.targetPosition.y);
7471
+ // const calTheta = calAngle(
7472
+ // this.startPosition.x,
7473
+ // this.startPosition.y,
7474
+ // this.targetPosition.x,
7475
+ // this.targetPosition.y
7476
+ // );
7483
7477
  const startTheta = this.startPosition.rotation;
7484
- const targetTheta = this.onlyUpdateTheta ? this.targetPosition.rotation : calTheta;
7485
- console.log('begain startAnimationToPosition----->', this.startPosition, this.targetPosition, this.onlyUpdateTheta);
7478
+ const targetTheta = this.targetPosition.rotation;
7486
7479
  this.deltaPosition = {
7487
7480
  x: this.onlyUpdateTheta ? 0 : this.targetPosition.x - this.startPosition.x,
7488
7481
  y: this.onlyUpdateTheta ? 0 : this.targetPosition.y - this.startPosition.y,
7489
- rotation: radNormalize(targetTheta - startTheta)
7482
+ rotation: radNormalize(targetTheta - startTheta),
7490
7483
  };
7491
- console.log('deltaPosition----->', targetTheta, startTheta, calTheta, this.deltaPosition);
7492
7484
  // 开始动画循环
7493
7485
  this.animateStep();
7494
7486
  }
@@ -7511,8 +7503,16 @@ class MowerPostionManager {
7511
7503
  const currentY = this.startPosition.y + this.deltaPosition.y * easedProgress;
7512
7504
  const currentRotation = this.startPosition.rotation + this.deltaPosition.rotation * easedProgress;
7513
7505
  this.currentPosition = { x: currentX, y: currentY, rotation: currentRotation };
7506
+ // 假设在这里进行更新路径数据
7507
+ if (this.onMowingPositionChange) {
7508
+ this.onMowingPositionChange({
7509
+ x: currentX,
7510
+ y: currentY,
7511
+ vehicleState: this.mowerPositionConfig.vehicleState,
7512
+ });
7513
+ }
7514
7514
  // console.log('animateStep----->', this.onlyUpdateTheta, currentX, currentY, currentRotation);
7515
- // console.log('animateStep1----->', onlyUpdateTheta, progress, calTheta, currentRotation);
7515
+ //
7516
7516
  // 继续动画或结束
7517
7517
  if (progress < 1) {
7518
7518
  // 设置当前位置
@@ -7523,7 +7523,6 @@ class MowerPostionManager {
7523
7523
  this.setElementPosition(currentX, currentY, currentRotation);
7524
7524
  // 动画完成
7525
7525
  this.stopAnimation();
7526
- console.log('end animateStep----->', this.lastPosition, this.currentPosition, this.targetPosition);
7527
7526
  // 通知动画完成
7528
7527
  if (this.onAnimationComplete) {
7529
7528
  this.onAnimationComplete();
@@ -7589,13 +7588,61 @@ class MowerPostionManager {
7589
7588
  }
7590
7589
  }
7591
7590
 
7591
+ // 记录割草状态,状态变更的时候,变量不触发重新渲染
7592
+ const useProcessMowingState = create((set) => ({
7593
+ processStateIsMowing: false,
7594
+ updateProcessStateIsMowing: (isMowing) => set({ processStateIsMowing: isMowing }),
7595
+ resetProcessStateIsMowing: () => set({ processStateIsMowing: false }),
7596
+ }));
7597
+
7598
+ /**
7599
+ * 高级节流函数
7600
+ * @param func 要节流的函数
7601
+ * @param delay 延迟时间(毫秒)
7602
+ * @param options 配置选项
7603
+ * @returns 节流后的函数
7604
+ */
7605
+ function throttleAdvanced(func, delay, options = { leading: true, trailing: true }) {
7606
+ let lastExecTime = 0;
7607
+ let timeoutId = null;
7608
+ let lastArgs = null;
7609
+ return function throttled(...args) {
7610
+ const currentTime = Date.now();
7611
+ lastArgs = args;
7612
+ // 如果距离上次执行的时间小于延迟时间
7613
+ if (currentTime - lastExecTime < delay) {
7614
+ // 清除之前的定时器
7615
+ if (timeoutId) {
7616
+ clearTimeout(timeoutId);
7617
+ }
7618
+ // 设置新的定时器
7619
+ timeoutId = setTimeout(() => {
7620
+ if (options.trailing && lastArgs) {
7621
+ lastExecTime = Date.now();
7622
+ func.apply(this, lastArgs);
7623
+ lastArgs = null;
7624
+ }
7625
+ timeoutId = null;
7626
+ }, delay - (currentTime - lastExecTime));
7627
+ }
7628
+ else {
7629
+ // 如果距离上次执行的时间已经超过延迟时间
7630
+ if (options.leading) {
7631
+ lastExecTime = currentTime;
7632
+ func.apply(this, args);
7633
+ }
7634
+ }
7635
+ };
7636
+ }
7637
+
7592
7638
  // Google Maps 叠加层类 - 带编辑功能
7593
7639
  class MowerMapOverlay {
7594
- constructor(bounds, mapData, mowerPositonConfig, pathData, isEditMode = false, mapConfig = {}, antennaConfig = {}, onMapLoad, onPathLoad, dragCallbacks) {
7640
+ constructor(bounds, mapData, partitionBoundary, mowerPositionConfig, pathData, isEditMode = false, mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
7595
7641
  this.div = null;
7596
7642
  this.svgMapView = null;
7597
7643
  this.offscreenContainer = null;
7598
7644
  this.overlayView = null;
7645
+ this.defaultTransform = { x: 0, y: 0, rotation: 0 };
7599
7646
  // boundary数据
7600
7647
  this.boundaryData = [];
7601
7648
  // 边界标签管理器
@@ -7605,7 +7652,7 @@ class MowerMapOverlay {
7605
7652
  // 天线管理器
7606
7653
  this.antennaManager = null;
7607
7654
  // 割草机位置管理器
7608
- this.mowerPostionManager = null;
7655
+ this.mowerPositionManager = null;
7609
7656
  // 当前动画时长
7610
7657
  this.currentAnimationTime = 0;
7611
7658
  // 是否正在用户动画中(区分用户主动触发的动画和地图重绘)
@@ -7628,8 +7675,14 @@ class MowerMapOverlay {
7628
7675
  // 初始状态记录(用于计算相对于初始状态的偏移)
7629
7676
  this.initialOffset = { x: 0, y: 0 };
7630
7677
  this.initialRotation = 0;
7678
+ this.mowPartitionData = null;
7679
+ this.updatePathDataByMowingPositionThrottled = throttleAdvanced(this.updatePathDataByMowingPosition, 300, {
7680
+ leading: true,
7681
+ trailing: false,
7682
+ });
7631
7683
  this.bounds = bounds;
7632
7684
  this.mapData = mapData;
7685
+ this.partitionBoundary = partitionBoundary;
7633
7686
  this.pathData = pathData;
7634
7687
  this.isEditMode = isEditMode;
7635
7688
  this.mapConfig = mapConfig;
@@ -7637,7 +7690,20 @@ class MowerMapOverlay {
7637
7690
  this.onMapLoad = onMapLoad;
7638
7691
  this.onPathLoad = onPathLoad;
7639
7692
  this.dragCallbacks = dragCallbacks;
7640
- this.mowerPositonConfig = mowerPositonConfig;
7693
+ this.mowerPositionConfig = mowerPositionConfig;
7694
+ this.mowPartitionData = mowPartitionData;
7695
+ // 设置默认的transform
7696
+ if (defaultTransform) {
7697
+ this.defaultTransform = {
7698
+ x: defaultTransform.x ?? 0,
7699
+ y: defaultTransform.y ?? 0,
7700
+ rotation: defaultTransform.rotation ?? 0,
7701
+ };
7702
+ // defaultTransform的x对应经度偏移量,y对应纬度偏移量
7703
+ this.latLngOffset.lng = this.defaultTransform.x;
7704
+ this.latLngOffset.lat = this.defaultTransform.y;
7705
+ this.currentRotation = this.defaultTransform.rotation;
7706
+ }
7641
7707
  // 创建 OverlayView 实例
7642
7708
  if (window.google && window.google.maps) {
7643
7709
  this.overlayView = new window.google.maps.OverlayView();
@@ -7652,26 +7718,29 @@ class MowerMapOverlay {
7652
7718
  this.overlayView.handleSave = this.handleSave.bind(this);
7653
7719
  this.overlayView.setCustomIcons = this.setCustomIcons.bind(this);
7654
7720
  this.overlayView.getCurrentDragState = this.getCurrentDragState.bind(this);
7721
+ this.overlayView.setTransform = this.setTransform.bind(this);
7722
+ this.overlayView.resetToDefaultTransform = this.resetToDefaultTransform.bind(this);
7655
7723
  }
7656
7724
  this.boundaryData = generateBoundaryData(mapData, pathData);
7657
7725
  }
7658
- updatePosition(positonConfig, animationTime = 2200) {
7659
- console.log('updatePosition==', positonConfig, animationTime);
7726
+ updatePosition(positionConfig, animationTime = 2200) {
7727
+ console.log('updatePosition==', positionConfig, animationTime);
7660
7728
  // 保存当前动画时长
7661
7729
  this.currentAnimationTime = animationTime;
7662
7730
  // 标记是否为用户动画(大于0表示用户主动触发的动画)
7663
7731
  this.isUserAnimation = animationTime > 0;
7664
7732
  // 更新割草机位置配置
7665
- this.mowerPositonConfig = positonConfig;
7733
+ this.mowerPositionConfig = positionConfig;
7666
7734
  // 更新割草机位置管理器
7667
- if (this.mowerPostionManager) {
7668
- this.mowerPostionManager.updatePosition(positonConfig, animationTime);
7735
+ if (this.mowerPositionManager) {
7736
+ this.mowerPositionManager.updatePosition(positionConfig, animationTime);
7669
7737
  }
7670
7738
  }
7671
- updatePostionByLastPosition(chargingPilesPositonConfig, animationTime = 2200) {
7739
+ updatePositionByLastPosition(chargingPilesPositionConfig) {
7740
+ console.log('overlay updatePositionByLastPosition----->', this.mowerPositionManager, chargingPilesPositionConfig);
7672
7741
  // 更新配置
7673
- if (this.mowerPostionManager) {
7674
- this.mowerPostionManager.updatePostionByLastPosition(chargingPilesPositonConfig, animationTime);
7742
+ if (this.mowerPositionManager) {
7743
+ this.mowerPositionManager.updatePositionByLastPosition(chargingPilesPositionConfig);
7675
7744
  }
7676
7745
  }
7677
7746
  setMap(map) {
@@ -7688,6 +7757,32 @@ class MowerMapOverlay {
7688
7757
  getPanes() {
7689
7758
  return this.overlayView ? this.overlayView.getPanes() : null;
7690
7759
  }
7760
+ resetBorderLayerHighlight() {
7761
+ 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
+ });
7770
+ }
7771
+ setBorderLayerHighlight(mowPartitionData) {
7772
+ this.mowPartitionData = mowPartitionData;
7773
+ const boundaryBorderLayer = this.svgMapView?.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
7774
+ 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
+ }
7785
+ }
7691
7786
  onAdd() {
7692
7787
  // 创建包含SVG的div
7693
7788
  this.div = document.createElement('div');
@@ -7748,7 +7843,7 @@ class MowerMapOverlay {
7748
7843
  const map = this.getMap();
7749
7844
  if (!map || !this.svgMapView)
7750
7845
  return;
7751
- map.getZoom();
7846
+ // const currentZoom = map.getZoom();
7752
7847
  // const center = map.getCenter();
7753
7848
  // 基础公式:像素/米 = 156543.03392 * cos(latitude) / (2 ^ zoom)
7754
7849
  // const metersPerPixel =
@@ -7757,12 +7852,13 @@ class MowerMapOverlay {
7757
7852
  // const scale = (1 / metersPerPixel) * 50;
7758
7853
  // 应用缩放到SVG
7759
7854
  // this.svgMapView.setZoom(scale);
7855
+ // 当缩放变化时,重新绘制以确保位置正确
7856
+ this.draw();
7760
7857
  }
7761
7858
  // 创建边界标签管理器
7762
7859
  createBoundaryLabelsManager() {
7763
7860
  if (!this.div || !this.svgMapView)
7764
7861
  return;
7765
- console.log('this.boundaryData----->', this.boundaryData);
7766
7862
  // 创建边界标签管理器
7767
7863
  this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData);
7768
7864
  // 设置叠加层div引用
@@ -7808,11 +7904,13 @@ class MowerMapOverlay {
7808
7904
  if (!this.div || !this.svgMapView)
7809
7905
  return;
7810
7906
  // 创建割草机位置管理器,传入动画完成回调
7811
- this.mowerPostionManager = new MowerPostionManager(this.svgMapView, this.mowerPositonConfig, this.div);
7907
+ this.mowerPositionManager = new MowerPositionManager(this.svgMapView, this.mowerPositionConfig, this.div, () => {
7908
+ console.log('动画完成');
7909
+ }, this.updatePathDataByMowingPositionThrottled.bind(this));
7812
7910
  // 设置叠加层div引用
7813
- this.mowerPostionManager.setOverlayDiv(this.div);
7911
+ this.mowerPositionManager.setOverlayDiv(this.div);
7814
7912
  // 获取容器并添加到主div
7815
- const container = this.mowerPostionManager.getElement();
7913
+ const container = this.mowerPositionManager.getElement();
7816
7914
  if (container) {
7817
7915
  this.div.appendChild(container);
7818
7916
  }
@@ -7842,20 +7940,6 @@ class MowerMapOverlay {
7842
7940
  updateManagerPositions(width, height) {
7843
7941
  if (!this.div)
7844
7942
  return;
7845
- // 获取SVG元素和其viewBox
7846
- const svgElement = this.div.querySelector('svg');
7847
- if (!svgElement)
7848
- return;
7849
- const viewBox = svgElement.viewBox.baseVal;
7850
- if (!viewBox)
7851
- return;
7852
- // 构造viewBox信息对象
7853
- ({
7854
- x: viewBox.x,
7855
- y: viewBox.y,
7856
- width: viewBox.width,
7857
- height: viewBox.height,
7858
- });
7859
7943
  // 更新充电桩位置
7860
7944
  if (this.chargingPileManager) {
7861
7945
  this.chargingPileManager.updatePositions();
@@ -7930,9 +8014,10 @@ class MowerMapOverlay {
7930
8014
  }
7931
8015
  // 获取当前拖拽状态
7932
8016
  getCurrentDragState() {
8017
+ // 返回基于地图中心点的经纬度偏移量
7933
8018
  return {
7934
- offsetX: this.latLngOffset.lng * 111320 + this.tempPixelOffset.x - this.initialOffset.x, // 简化的经度到米的转换
7935
- offsetY: this.latLngOffset.lat * 110540 + this.tempPixelOffset.y - this.initialOffset.y, // 简化的纬度到米的转换
8019
+ x: this.latLngOffset.lng + this.tempPixelOffset.x - this.initialOffset.x,
8020
+ y: this.latLngOffset.lat + this.tempPixelOffset.y - this.initialOffset.y,
7936
8021
  rotation: this.currentRotation - this.initialRotation,
7937
8022
  isDragging: this.isDragging,
7938
8023
  isRotating: this.isRotating,
@@ -8209,6 +8294,8 @@ class MowerMapOverlay {
8209
8294
  // 确保旋转角度在0-360度范围内
8210
8295
  this.currentRotation = ((this.currentRotation % 360) + 360) % 360;
8211
8296
  // 应用旋转变换到DOM元素,同时保持位移
8297
+ // 更新边界标签的旋转角度,使其保持水平状态
8298
+ this.setManagerRotation(this.currentRotation);
8212
8299
  const transform = `translate(${this.tempPixelOffset.x}px, ${this.tempPixelOffset.y}px) rotate(${this.currentRotation}deg)`;
8213
8300
  this.div.style.transform = transform;
8214
8301
  // 更新鼠标起始位置为当前位置,为下次计算做准备
@@ -8253,7 +8340,6 @@ class MowerMapOverlay {
8253
8340
  });
8254
8341
  // 重置临时像素偏移量
8255
8342
  this.tempPixelOffset = { x: 0, y: 0 };
8256
- // 重新绘制以应用新的地理坐标偏移量
8257
8343
  this.draw();
8258
8344
  }
8259
8345
  // 获取编辑数据
@@ -8356,18 +8442,76 @@ class MowerMapOverlay {
8356
8442
  this.div.style.pointerEvents = 'none';
8357
8443
  }
8358
8444
  }
8445
+ /**
8446
+ * 设置旋转角度
8447
+ */
8448
+ setManagerRotation(rotation) {
8449
+ if (this.boundaryLabelsManager) {
8450
+ this.boundaryLabelsManager.setRotation(rotation);
8451
+ }
8452
+ if (this.antennaManager) {
8453
+ this.antennaManager.setRotation(rotation);
8454
+ }
8455
+ if (this.chargingPileManager) {
8456
+ this.chargingPileManager.setRotation(rotation);
8457
+ }
8458
+ }
8459
+ // 设置transform
8460
+ setTransform(transform) {
8461
+ if (typeof transform.x === 'number')
8462
+ this.tempPixelOffset.x = transform.x;
8463
+ if (typeof transform.y === 'number')
8464
+ this.tempPixelOffset.y = transform.y;
8465
+ if (typeof transform.rotation === 'number')
8466
+ this.currentRotation = transform.rotation;
8467
+ this.defaultTransform = {
8468
+ x: transform.x,
8469
+ y: transform.y,
8470
+ rotation: transform.rotation,
8471
+ };
8472
+ this.setManagerRotation(this.currentRotation);
8473
+ this.draw();
8474
+ }
8475
+ // 重置到默认的transform
8476
+ resetToDefaultTransform() {
8477
+ // 重置所有偏移和旋转相关的状态,x对应经度偏移,y对应纬度偏移
8478
+ this.latLngOffset.lng = this.defaultTransform.x;
8479
+ this.latLngOffset.lat = this.defaultTransform.y;
8480
+ this.tempPixelOffset = { x: 0, y: 0 };
8481
+ this.currentRotation = this.defaultTransform.rotation;
8482
+ this.initialOffset = { x: 0, y: 0 };
8483
+ this.initialRotation = 0;
8484
+ // 重新绘制
8485
+ // 重置边界标签的旋转角度
8486
+ if (this.boundaryLabelsManager) {
8487
+ this.boundaryLabelsManager.setRotation(this.defaultTransform.rotation);
8488
+ }
8489
+ if (this.antennaManager) {
8490
+ this.antennaManager.setRotation(this.defaultTransform.rotation);
8491
+ }
8492
+ if (this.chargingPileManager) {
8493
+ this.chargingPileManager.setRotation(this.defaultTransform.rotation);
8494
+ }
8495
+ this.draw();
8496
+ // 触发拖拽回调,通知外部状态更新
8497
+ this.dragCallbacks?.onDragEnd?.(this.getCurrentDragState());
8498
+ }
8359
8499
  initializeSvgMapView() {
8360
8500
  if (!this.offscreenContainer)
8361
8501
  return;
8362
8502
  try {
8363
8503
  // 创建SvgMapView实例
8364
8504
  this.svgMapView = new SvgMapView(this.offscreenContainer, 800, 600);
8505
+ window.svgMapView = this.svgMapView;
8365
8506
  // 加载地图数据
8366
8507
  this.loadMapData();
8367
8508
  // 加载路径数据
8368
8509
  if (this.pathData && this.svgMapView) {
8369
- this.loadPathData(this.pathData);
8510
+ console.log('initializeSvgMapView->', this.pathData, this.mowPartitionData);
8511
+ this.loadPathData(this.pathData, this.mowPartitionData);
8370
8512
  }
8513
+ // 刷新绘制图层
8514
+ this.svgMapView.refresh();
8371
8515
  // 获取生成的SVG并添加到叠加层div中
8372
8516
  const svgElement = this.svgMapView.getSVG();
8373
8517
  if (svgElement) {
@@ -8401,14 +8545,12 @@ class MowerMapOverlay {
8401
8545
  // 处理天线数据
8402
8546
  let antennaElements = [];
8403
8547
  if (this.antennaConfig.length > 0) {
8404
- antennaElements = AntennaDataBuilder.fromAntennaData(this.antennaConfig);
8548
+ antennaElements = AntennaDataBuilder.fromAntennaData(this.antennaConfig, this.mapConfig);
8405
8549
  }
8406
8550
  // 添加图层到SvgMapView
8407
8551
  const layers = drawLayer.getLayers();
8408
8552
  this.svgMapView.clear();
8409
- layers.forEach((layer) => {
8410
- this.svgMapView.addLayer(layer);
8411
- });
8553
+ this.svgMapView.addLayers(layers);
8412
8554
  this.createChargingPileManager();
8413
8555
  // 使用管理器处理充电桩和天线
8414
8556
  if (this.chargingPileManager && chargingPileElements.length > 0) {
@@ -8434,40 +8576,97 @@ class MowerMapOverlay {
8434
8576
  console.error('加载地图数据时出错:', error);
8435
8577
  }
8436
8578
  }
8437
- loadPathData(pathData) {
8579
+ loadPathData(pathData, mowPartitionData) {
8438
8580
  try {
8439
8581
  // 使用现有的PathDataProcessor处理路径数据
8440
8582
  const pathElements = PathDataProcessor.processPathData(pathData, this.mapConfig);
8583
+ console.log('pathdaata->', pathElements, mowPartitionData);
8584
+ const newPathElements = pathElements.map((pathElement) => {
8585
+ const { id, elements } = pathElement;
8586
+ const isMowBoundary = mowPartitionData && mowPartitionData?.partitionIds?.includes(id);
8587
+ if (isMowBoundary) {
8588
+ return {
8589
+ id: id,
8590
+ elements: elements.map((element) => {
8591
+ const isTransPath = element.pathType === PathSegmentType.TRANS;
8592
+ if (isTransPath) {
8593
+ return element;
8594
+ }
8595
+ return {
8596
+ ...element,
8597
+ style: {
8598
+ ...element.style,
8599
+ lineColor: DEFAULT_STYLES.path.mowingLineColor,
8600
+ },
8601
+ };
8602
+ }),
8603
+ };
8604
+ }
8605
+ else {
8606
+ return pathElement;
8607
+ }
8608
+ });
8441
8609
  const pathLayer = new PathLayer();
8442
- pathLayer.addElements(pathElements);
8610
+ pathLayer.addElements(newPathElements);
8443
8611
  // 添加图层到SvgMapView
8444
8612
  this.svgMapView.removeLayerByType(LAYER_DEFAULT_TYPE.PATH);
8445
8613
  this.svgMapView.addLayer(pathLayer);
8446
- console.log('pathElements----->', this.svgMapView.getLayers());
8614
+ this.svgMapView.renderLayer(LAYER_DEFAULT_TYPE.PATH);
8447
8615
  // 调用回调
8448
- const elementCount = pathElements.length;
8616
+ const elementCount = newPathElements.length;
8449
8617
  this.onPathLoad?.(elementCount);
8450
8618
  }
8451
8619
  catch (error) {
8452
8620
  console.error('加载路径数据时出错:', error);
8453
8621
  }
8454
8622
  }
8623
+ /**
8624
+ * 根据割草机位置,更新路径数据
8625
+ * @param position 割草机位置
8626
+ */
8627
+ updatePathDataByMowingPosition(position) {
8628
+ // 找到当前position所在的分区id,将该点更新到pathData中
8629
+ const currentPartitionId = getPartitionId(this.partitionBoundary, position.x, position.y);
8630
+ const processStateIsMowing = useProcessMowingState.getState().processStateIsMowing;
8631
+ // console.log('processStateIsMowing==', processStateIsMowing);
8632
+ if (currentPartitionId && this.pathData?.[currentPartitionId]) {
8633
+ const currentPathData = this.pathData[currentPartitionId];
8634
+ this.pathData[currentPartitionId] = {
8635
+ ...currentPathData,
8636
+ points: [
8637
+ ...(currentPathData?.points || []),
8638
+ {
8639
+ postureX: Number(position.x),
8640
+ postureY: Number(position.y),
8641
+ knifeRotation: processStateIsMowing && position.vehicleState === RobotStatus.MOWING ? '01' : '00', // "knifeRotation": "01",//刀盘是否转动 00-否 01-是
8642
+ pathType: '', //这里由于从实时路径是获取不到pathType的,所以这里暂时不传。让路径是否绘制取决于knifeRotation
8643
+ partitionId: currentPartitionId.toString(),
8644
+ },
8645
+ ],
8646
+ };
8647
+ this.updatePathData(this.pathData, this.mowPartitionData);
8648
+ }
8649
+ }
8650
+ updateMowPartitionData(mowPartitionData) {
8651
+ this.mowPartitionData = mowPartitionData;
8652
+ }
8455
8653
  /** 更新历史路径数据 */
8456
- updatePathData(pathData) {
8654
+ updatePathData(pathData, mowPartitionData) {
8457
8655
  if (!this.svgMapView || !pathData)
8458
8656
  return;
8459
8657
  // 找到pathLayer,将其删除,然后重新添加
8460
- this.loadPathData(pathData);
8658
+ this.loadPathData(pathData, mowPartitionData);
8461
8659
  }
8660
+ /** 更新边界标签信息 */
8462
8661
  updateBoundaryLabelInfo(pathJson) {
8463
8662
  if (!pathJson)
8464
8663
  return;
8465
8664
  const boundaryData = generateBoundaryData(this.mapData, pathJson);
8466
- console.log('boundaryData==', boundaryData);
8665
+ // console.log('boundaryData==', boundaryData);
8467
8666
  this.boundaryLabelsManager?.updateBoundaryData(boundaryData);
8468
8667
  }
8469
8668
  draw() {
8470
- console.log('draw方法被调用');
8669
+ console.log('ondraw');
8471
8670
  // 防御性检查:如果this.div为null,说明onAdd还没被调用,直接返回
8472
8671
  if (!this.div) {
8473
8672
  return;
@@ -8525,13 +8724,17 @@ class MowerMapOverlay {
8525
8724
  this.div.style.transform = transform;
8526
8725
  }
8527
8726
  else {
8528
- // 非拖拽时:只应用旋转(如果有的话)
8529
- if (this.currentRotation !== 0) {
8530
- this.div.style.transform = `rotate(${this.currentRotation}deg)`;
8727
+ // 非拖拽时:应用当前偏移和旋转(包括默认值)
8728
+ const transforms = [];
8729
+ // 应用像素偏移
8730
+ if (this.tempPixelOffset.x !== 0 || this.tempPixelOffset.y !== 0) {
8731
+ transforms.push(`translate(${this.tempPixelOffset.x}px, ${this.tempPixelOffset.y}px)`);
8531
8732
  }
8532
- else {
8533
- this.div.style.transform = '';
8733
+ // 应用旋转
8734
+ if (this.currentRotation !== 0) {
8735
+ transforms.push(`rotate(${this.currentRotation}deg)`);
8534
8736
  }
8737
+ this.div.style.transform = transforms.join(' ');
8535
8738
  }
8536
8739
  // 更新SVG视图框以适应新的尺寸
8537
8740
  if (this.svgMapView) {
@@ -8551,11 +8754,11 @@ class MowerMapOverlay {
8551
8754
  this.updateManagerPositions(width, height);
8552
8755
  // 重绘的时候可能在动画中,这时候需要强制更新一次数据,不然会出现抖动的效果
8553
8756
  // 非动画的情况下,根据最新的数据实时同步就可以了
8554
- if (!this.mowerPostionManager?.animationFlag) {
8555
- this.mowerPostionManager?.updatePostionByLastPosition(this.mowerPositonConfig, 0);
8757
+ if (!this.mowerPositionManager?.animationFlag) {
8758
+ this.mowerPositionManager?.updatePositionByLastPosition(this.mowerPositionConfig);
8556
8759
  }
8557
8760
  else {
8558
- this.mowerPostionManager?.forceUpdatePosition();
8761
+ this.mowerPositionManager?.forceUpdatePosition();
8559
8762
  }
8560
8763
  if (this.antennaManager) {
8561
8764
  this.antennaManager.updateAntennaPosition();
@@ -8591,9 +8794,9 @@ class MowerMapOverlay {
8591
8794
  this.antennaManager = null;
8592
8795
  }
8593
8796
  // 清理割草机位置管理器
8594
- if (this.mowerPostionManager) {
8595
- this.mowerPostionManager.destroy();
8596
- this.mowerPostionManager = null;
8797
+ if (this.mowerPositionManager) {
8798
+ this.mowerPositionManager.destroy();
8799
+ this.mowerPositionManager = null;
8597
8800
  }
8598
8801
  // 清理编辑界面
8599
8802
  this.removeEditInterface();
@@ -8610,12 +8813,19 @@ class MowerMapOverlay {
8610
8813
  }
8611
8814
  // 显示/隐藏割草机位置
8612
8815
  setMowerPositionVisible(visible) {
8613
- if (this.mowerPostionManager) {
8614
- this.mowerPostionManager.setVisible(visible);
8816
+ if (this.mowerPositionManager) {
8817
+ this.mowerPositionManager.setVisible(visible);
8615
8818
  }
8616
8819
  }
8617
8820
  }
8618
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
+
8619
8829
  // 验证GPS坐标是否有效
8620
8830
  const isValidGpsCoordinate = (coordinate) => {
8621
8831
  if (!coordinate || coordinate.length < 2)
@@ -8640,7 +8850,6 @@ const getValidGpsBounds = (mapData) => {
8640
8850
  }
8641
8851
  // 如果GPS坐标无效,尝试从地图几何数据估算
8642
8852
  const { sw, ne } = estimateGpsFromMapBounds(mapData);
8643
- console.log('sw, ne==', sw, ne);
8644
8853
  if (sw && ne) {
8645
8854
  console.warn('GPS坐标无效,使用地图几何数据估算边界:', sw, ne);
8646
8855
  return {
@@ -8658,11 +8867,9 @@ const getValidGpsBounds = (mapData) => {
8658
8867
  // 默认配置
8659
8868
  const defaultMapConfig = DEFAULT_STYLES;
8660
8869
  // 地图渲染器组件
8661
- const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJson, pathJson, realTimeData, currentPositionData, antennaConfig, onMapLoad, onPathLoad, onZoomChange, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, }, ref) => {
8662
- const svgMapViewRef = React.useRef(null);
8870
+ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJson, pathJson, realTimeData, antennaConfig, onMapLoad, onPathLoad, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, defaultTransform, }, ref) => {
8663
8871
  const [elementCount, setElementCount] = React.useState(0);
8664
8872
  const [pathCount, setPathCount] = React.useState(0);
8665
- const [zoom, setZoom] = React.useState(1);
8666
8873
  const [currentError, setCurrentError] = React.useState(null);
8667
8874
  const overlayRef = React.useRef(null);
8668
8875
  // const mapRef = useMap();
@@ -8670,6 +8877,8 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8670
8877
  const [hasInitializedBounds, setHasInitializedBounds] = React.useState(false);
8671
8878
  const { clearSubBoundaryBorder } = useSubBoundaryBorderStore();
8672
8879
  const currentProcessMowingStatusRef = React.useRef(false);
8880
+ const { updateProcessStateIsMowing, processStateIsMowing } = useProcessMowingState();
8881
+ const [mowPartitionData, setMowPartitionData] = React.useState(null);
8673
8882
  // 处理地图分区边界
8674
8883
  const partitionBoundary = React.useMemo(() => {
8675
8884
  const allBoundaryElements = [];
@@ -8692,22 +8901,61 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8692
8901
  }, [mapConfig]);
8693
8902
  const mergedAntennaConfig = React.useMemo(() => antennaConfig, [antennaConfig]);
8694
8903
  const mowerPositionData = React.useMemo(() => {
8904
+ // realTimeData 中包含三个种类的数据,之需要实时坐标的数据即可。
8905
+ if (!realTimeData || realTimeData.length === 0)
8906
+ return undefined;
8907
+ let currentPositionData;
8908
+ if (realTimeData.length === 1 && realTimeData[0].type === RealTimeDataType.LOCATION) {
8909
+ currentPositionData = realTimeData[0];
8910
+ }
8911
+ else {
8912
+ currentPositionData = realTimeData?.find((item) => item.type === RealTimeDataType.LOCATION);
8913
+ }
8914
+ if (!currentPositionData)
8915
+ return undefined;
8695
8916
  return {
8696
8917
  postureTheta: currentPositionData?.postureTheta
8697
8918
  ? Number(currentPositionData.postureTheta)
8698
8919
  : 0,
8699
8920
  postureX: currentPositionData?.postureX ? Number(currentPositionData.postureX) : 0,
8700
8921
  postureY: currentPositionData?.postureY ? Number(currentPositionData.postureY) : 0,
8701
- vehicleState: currentPositionData?.vehicleState || RobotStatus.STANDBY,
8702
- vehicleModel: modelType,
8922
+ lastPostureTheta: currentPositionData?.lastPostureTheta
8923
+ ? Number(currentPositionData.lastPostureTheta)
8924
+ : 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() || '',
8703
8929
  };
8704
- }, [currentPositionData, modelType]);
8705
- console.log('mowerPositionData==', currentPositionData, mowerPositionData);
8930
+ }, [realTimeData, modelType]);
8931
+ console.log('mowerPositionData', mowerPositionData);
8706
8932
  // 处理错误
8707
8933
  const handleError = (error) => {
8708
8934
  setCurrentError(error);
8709
8935
  onError?.(error);
8710
8936
  };
8937
+ const fitBounds = React.useCallback(() => {
8938
+ if (!mapJson || !mapRef)
8939
+ return null;
8940
+ // 计算边界
8941
+ const bounds = calculateMapBounds(mapJson);
8942
+ if (!bounds) {
8943
+ handleError('无法计算地图边界');
8944
+ return;
8945
+ }
8946
+ // 将自定义边界转换为Google Maps LatLngBounds(使用有效的GPS坐标)
8947
+ const validBounds = getValidGpsBounds(mapJson);
8948
+ // 地图数据中的坐标格式是 [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];
8953
+ const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
8954
+ new window.google.maps.LatLng(neLat, neLng) // 东北角
8955
+ );
8956
+ console.log('fitBounds----->', googleBounds);
8957
+ mapRef.fitBounds(googleBounds);
8958
+ }, [mapJson, mapRef]);
8711
8959
  // 初始化Google Maps叠加层
8712
8960
  const initializeGoogleMapsOverlay = async () => {
8713
8961
  if (!mapJson)
@@ -8740,17 +8988,13 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8740
8988
  const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
8741
8989
  new window.google.maps.LatLng(neLat, neLng) // 东北角
8742
8990
  );
8743
- console.log('使用有效GPS坐标创建边界:', {
8744
- sw: { lat: swLat, lng: swLng },
8745
- ne: { lat: neLat, lng: neLng },
8746
- });
8747
8991
  // 如果已经存在叠加层,先移除它
8748
8992
  if (overlayRef.current) {
8749
8993
  overlayRef.current.setMap(null);
8750
8994
  overlayRef.current = null;
8751
8995
  }
8752
8996
  // 创建叠加层
8753
- const overlay = new MowerMapOverlay(googleBounds, mapJson, mowerPositionData, pathJson || {}, isEditMode, mergedMapConfig, mergedAntennaConfig, (count) => {
8997
+ const overlay = new MowerMapOverlay(googleBounds, mapJson, partitionBoundary, mowerPositionData, pathJson || {}, isEditMode, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
8754
8998
  setElementCount(count);
8755
8999
  onMapLoad?.(count);
8756
9000
  }, (count) => {
@@ -8771,39 +9015,18 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8771
9015
  handleError(`初始化Google Maps叠加层失败: ${error instanceof Error ? error.message : String(error)}`);
8772
9016
  }
8773
9017
  };
8774
- const formatRealTimeData = async (realTimeData) => {
8775
- // await sleep(1000);
8776
- const newRealTimeData = handleRealTimeData({
8777
- realTimeData,
8778
- isMowing: currentProcessMowingStatusRef.current,
8779
- pathData: pathJson,
8780
- partitionBoundary,
8781
- });
8782
- console.log('newRealTimeData==', newRealTimeData);
8783
- // currentProcessMowingStatusRef.current = newRealTimeData.isMowing;
8784
- currentProcessMowingStatusRef.current = true;
8785
- // 调用overlay的updatePathData方法,更新历史路径数据
8786
- if (overlayRef.current) {
8787
- overlayRef.current.updatePathData(newRealTimeData.pathData);
8788
- }
8789
- overlayRef.current.updateBoundaryLabelInfo(newRealTimeData.pathData);
8790
- };
8791
9018
  // 初始化效果
8792
9019
  React.useEffect(() => {
8793
9020
  initializeGoogleMapsOverlay();
8794
- console.log('init mow map');
8795
9021
  // 清理函数
8796
9022
  return () => {
8797
9023
  clearSubBoundaryBorder();
9024
+ updateProcessStateIsMowing(false);
8798
9025
  currentProcessMowingStatusRef.current = false;
8799
9026
  if (overlayRef.current) {
8800
9027
  overlayRef.current.setMap(null);
8801
9028
  overlayRef.current = null;
8802
9029
  }
8803
- if (svgMapViewRef.current) {
8804
- svgMapViewRef.current.destroy();
8805
- svgMapViewRef.current = null;
8806
- }
8807
9030
  };
8808
9031
  }, [mapJson, pathJson, mergedMapConfig, mergedAntennaConfig]);
8809
9032
  // 监听编辑模式变化
@@ -8823,14 +9046,15 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8823
9046
  return;
8824
9047
  const elements = MapDataProcessor.processMapData(mapJson, mergedMapConfig);
8825
9048
  const chargingPiles = elements.find((element) => element.type === 'charging_pile');
8826
- console.log('chargingPiles==', chargingPiles, currentPositionData);
8827
9049
  if (!mowerPositionData || !overlayRef.current)
8828
9050
  return;
8829
9051
  const inChargingPiles = [RobotStatus.CHARGING, RobotStatus.PARKED];
8830
9052
  const isOffLine = mowerPositionData.vehicleState === RobotStatus.DISCONNECTED;
8831
9053
  const isInChargingPile = inChargingPiles.includes(mowerPositionData.vehicleState);
8832
- // 如果在充电桩上,则更新位置到充电桩的位置
9054
+ console.log('isInChargingPile-----------》', isInChargingPile, mowerPositionData);
9055
+ // 如果在充电桩上,则直接更新位置到充电桩的位置
8833
9056
  if (isInChargingPile) {
9057
+ console.log('isInChargingPile', isInChargingPile, mowerPositionData);
8834
9058
  overlayRef.current.updatePosition({
8835
9059
  ...mowerPositionData,
8836
9060
  postureX: chargingPiles?.originalData.position[0],
@@ -8839,68 +9063,112 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8839
9063
  }, 0);
8840
9064
  }
8841
9065
  else {
9066
+ // 如果车辆是disabled状态或者超出边界(默认超过1000m),则更新位置到上一次的位置
8842
9067
  const positonOutOfRange = isOutOfRange(mowerPositionData);
8843
9068
  const positionValid = isInvalidPosition(mowerPositionData);
8844
- const isStandyBy = mowerPositionData.vehicleState === RobotStatus.STANDBY;
9069
+ const isStandby = mowerPositionData.vehicleState === RobotStatus.STANDBY;
9070
+ console.log('positonOutOfRange', positonOutOfRange, positionValid, isOffLine);
8845
9071
  if (positonOutOfRange || positionValid || isOffLine) {
8846
- overlayRef.current.updatePostionByLastPosition({
8847
- ...mowerPositionData,
8848
- postureX: chargingPiles?.originalData.position[0],
8849
- postureY: chargingPiles?.originalData.position[1],
8850
- postureTheta: chargingPiles?.originalData.direction - Math.PI || 0,
8851
- }, isStandyBy ? 0 : 2200);
9072
+ // 初始信息是通过后端接口获取的,此时当前位置数据不可用的时候,可以取上一次的位置数据
9073
+ // mowerPositionData 中可能会包含上一次的位置数据,
9074
+ const lastPostureX = mowerPositionData.lastPostureX;
9075
+ const lastPostureY = mowerPositionData.lastPostureY;
9076
+ const lastPostureTheta = mowerPositionData.lastPostureTheta;
9077
+ if (lastPostureX && lastPostureY && lastPostureTheta) {
9078
+ overlayRef.current.updatePositionByLastPosition(mowerPositionData);
9079
+ }
9080
+ else {
9081
+ overlayRef.current.updatePositionByLastPosition({
9082
+ ...mowerPositionData,
9083
+ postureX: chargingPiles?.originalData.position[0],
9084
+ postureY: chargingPiles?.originalData.position[1],
9085
+ postureTheta: chargingPiles?.originalData.direction - Math.PI || 0,
9086
+ });
9087
+ }
8852
9088
  }
8853
9089
  else {
8854
- overlayRef.current.updatePosition(mowerPositionData, isStandyBy ? 0 : 2200);
9090
+ console.log('hook updatePosition----->', mowerPositionData);
9091
+ overlayRef.current.updatePosition(mowerPositionData, isStandby ? 0 : 2000);
8855
9092
  }
8856
9093
  }
8857
- }, [currentPositionData]);
9094
+ }, [mowerPositionData]);
8858
9095
  React.useEffect(() => {
8859
9096
  if (!mapJson ||
8860
9097
  !pathJson ||
8861
9098
  !realTimeData ||
8862
9099
  realTimeData.length === 0 ||
8863
- !Array.isArray(realTimeData))
9100
+ !Array.isArray(realTimeData) ||
9101
+ !overlayRef.current)
8864
9102
  return;
8865
9103
  // 根据后端推送的实时数据,进行不同处理
8866
9104
  // TODO:需要根据返回的数据,处理车辆的移动位置
8867
- // 这里是根据返回的数据,处理实时轨迹
8868
- formatRealTimeData(realTimeData);
8869
- }, [realTimeData, mapJson, pathJson]);
8870
- // 提供ref方法
8871
- React.useImperativeHandle(ref, () => ({
8872
- setZoom: (newZoom) => {
8873
- setZoom(newZoom);
8874
- if (svgMapViewRef.current) {
8875
- svgMapViewRef.current.setZoom(newZoom);
9105
+ console.log('realTimeData----->', realTimeData);
9106
+ // realtime中包含当前割草任务的数据,根据数据进行path路径和边界的高亮操作
9107
+ const curMowPartitionData = realTimeData.find((item) => item.type === RealTimeDataType.PARTITION) || mowPartitionData;
9108
+ if (curMowPartitionData) {
9109
+ setMowPartitionData(curMowPartitionData);
9110
+ const isMowing = curMowPartitionData?.partitionIds && curMowPartitionData.partitionIds.length > 0;
9111
+ overlayRef.current.updateMowPartitionData(curMowPartitionData);
9112
+ console.log('isMowing', isMowing, curMowPartitionData);
9113
+ if (!isMowing) {
9114
+ overlayRef.current.resetBorderLayerHighlight();
8876
9115
  }
8877
- onZoomChange?.(newZoom);
8878
- },
8879
- getZoom: () => zoom,
8880
- resetView: () => {
8881
- if (svgMapViewRef.current) {
8882
- svgMapViewRef.current.resetTransform();
9116
+ else {
9117
+ overlayRef.current.setBorderLayerHighlight(curMowPartitionData);
8883
9118
  }
8884
- setZoom(1);
8885
- onZoomChange?.(1);
8886
- },
8887
- clear: () => {
8888
- if (svgMapViewRef.current) {
8889
- svgMapViewRef.current.clear();
9119
+ }
9120
+ // 如果一次性推送的是多条数据,则把多条数据处理后存入pathData,然后更新路径数据和边界标签信息
9121
+ // 如果一次只推送一条数据,则只解析里面的进度数据,然后更新边界标签信息,剩下的实时轨迹数据由车辆运动产生时存入pathData
9122
+ if (realTimeData.length > 1) {
9123
+ const { pathData, isMowing } = handleMultipleRealTimeData({
9124
+ realTimeData,
9125
+ isMowing: processStateIsMowing,
9126
+ pathData: pathJson,
9127
+ partitionBoundary,
9128
+ });
9129
+ updateProcessStateIsMowing(isMowing);
9130
+ if (pathData) {
9131
+ overlayRef.current.updatePathData(pathData, curMowPartitionData);
9132
+ overlayRef.current.updateBoundaryLabelInfo(pathData);
8890
9133
  }
8891
- setElementCount(0);
8892
- setPathCount(0);
8893
- },
8894
- getSvgElement: () => {
8895
- return svgMapViewRef.current ? svgMapViewRef.current.getSVG() : null;
8896
- },
8897
- fitToView: () => {
8898
- if (svgMapViewRef.current && mapJson) {
8899
- const bounds = calculateMapBounds(mapJson);
8900
- if (bounds) {
8901
- svgMapViewRef.current.fitToView(bounds);
8902
- }
9134
+ }
9135
+ else {
9136
+ const { isMowing, pathData } = getProcessMowingDataFromRealTimeData({
9137
+ realTimeData,
9138
+ isMowing: processStateIsMowing,
9139
+ pathData: pathJson,
9140
+ });
9141
+ updateProcessStateIsMowing(isMowing);
9142
+ overlayRef.current.updatePathData(pathData, curMowPartitionData);
9143
+ // 更新进度数据
9144
+ if (pathData) {
9145
+ overlayRef.current.updateBoundaryLabelInfo(pathData);
8903
9146
  }
9147
+ }
9148
+ }, [realTimeData, mapJson, pathJson]);
9149
+ React.useEffect(() => {
9150
+ console.log('defaultTransform----->', defaultTransform);
9151
+ if (!overlayRef.current || !defaultTransform)
9152
+ return;
9153
+ if (defaultTransform.x === 0 && defaultTransform.y === 0 && defaultTransform.rotation === 0) {
9154
+ return;
9155
+ }
9156
+ overlayRef.current?.setTransform(defaultTransform);
9157
+ const validBounds = getValidGpsBounds(mapJson);
9158
+ // 地图数据中的坐标格式是 [longitude, latitude]
9159
+ const swLat = validBounds.sw[1] + defaultTransform.y;
9160
+ const swLng = validBounds.sw[0] + defaultTransform.x;
9161
+ const neLat = validBounds.ne[1] + defaultTransform.y;
9162
+ const neLng = validBounds.ne[0] + defaultTransform.x;
9163
+ const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
9164
+ new window.google.maps.LatLng(neLat, neLng) // 东北角
9165
+ );
9166
+ mapRef.fitBounds(googleBounds);
9167
+ }, [defaultTransform]);
9168
+ // 提供ref方法
9169
+ React.useImperativeHandle(ref, () => ({
9170
+ fitToView: () => {
9171
+ fitBounds();
8904
9172
  },
8905
9173
  getOverlay: () => {
8906
9174
  return overlayRef.current;
@@ -8929,6 +9197,8 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
8929
9197
  getElementCount: () => elementCount,
8930
9198
  getPathCount: () => pathCount,
8931
9199
  isGoogleMapsReady: () => isGoogleMapsReady,
9200
+ setTransform: (t) => overlayRef.current?.setTransform(t),
9201
+ resetToDefaultTransform: () => overlayRef.current?.resetToDefaultTransform(),
8932
9202
  }));
8933
9203
  // 错误显示
8934
9204
  if (currentError) {