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