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