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