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