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