@fleet-frontend/mower-maps 0.0.9-beta.9 → 0.1.0
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/constants.d.ts +1 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/styles.d.ts.map +1 -1
- package/dist/index.esm.js +613 -169
- package/dist/index.js +613 -169
- package/dist/render/BoundaryLabelsManager.d.ts +7 -2
- package/dist/render/BoundaryLabelsManager.d.ts.map +1 -1
- package/dist/render/MowerMapOverlay.d.ts +6 -1
- package/dist/render/MowerMapOverlay.d.ts.map +1 -1
- package/dist/render/MowerMapRenderer.d.ts.map +1 -1
- package/dist/render/MowerPositionManager.d.ts +2 -0
- package/dist/render/MowerPositionManager.d.ts.map +1 -1
- package/dist/render/SvgMapView.d.ts.map +1 -1
- package/dist/render/layers/BoundaryBorderLayer.d.ts +4 -0
- package/dist/render/layers/BoundaryBorderLayer.d.ts.map +1 -1
- package/dist/render/layers/PathLayer.d.ts +10 -2
- package/dist/render/layers/PathLayer.d.ts.map +1 -1
- package/dist/types/renderer.d.ts +4 -0
- package/dist/types/renderer.d.ts.map +1 -1
- package/dist/types/utils.d.ts +2 -2
- package/dist/utils/boundaryUtils.d.ts.map +1 -1
- package/dist/utils/common.d.ts +10 -0
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/coordinates.d.ts +27 -0
- package/dist/utils/coordinates.d.ts.map +1 -1
- package/dist/utils/math.d.ts +1 -1
- package/dist/utils/math.d.ts.map +1 -1
- package/dist/utils/mower.d.ts +2 -2
- package/dist/utils/mower.d.ts.map +1 -1
- package/dist/utils/unionFind.d.ts +49 -0
- package/dist/utils/unionFind.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69,7 +69,8 @@ const DEFAULT_LINE_WIDTHS = {
|
|
|
69
69
|
const DEFAULT_OPACITIES = {
|
|
70
70
|
FULL: 1.0,
|
|
71
71
|
HIGH: 0.7,
|
|
72
|
-
MEDIUM: 0.6
|
|
72
|
+
MEDIUM: 0.6,
|
|
73
|
+
DOODLE: 0.8};
|
|
73
74
|
/**
|
|
74
75
|
* 默认半径设置
|
|
75
76
|
*/
|
|
@@ -228,7 +229,6 @@ class SvgMapView {
|
|
|
228
229
|
*/
|
|
229
230
|
removeLayer(layer) {
|
|
230
231
|
const index = this.layers.indexOf(layer);
|
|
231
|
-
console.log('removeLayer----->', index);
|
|
232
232
|
if (index !== -1) {
|
|
233
233
|
this.layers.splice(index, 1);
|
|
234
234
|
this.refresh();
|
|
@@ -276,7 +276,6 @@ class SvgMapView {
|
|
|
276
276
|
width: boundWidth + padding * 2,
|
|
277
277
|
height: boundHeight + padding * 2,
|
|
278
278
|
};
|
|
279
|
-
console.log('viewbox->', this.viewBox);
|
|
280
279
|
// 根据宽高比选择合适的preserveAspectRatio设置
|
|
281
280
|
if (Math.abs(contentAspectRatio - containerAspectRatio) < 0.01) {
|
|
282
281
|
// 宽高比接近,使用slice填满容器
|
|
@@ -286,7 +285,6 @@ class SvgMapView {
|
|
|
286
285
|
// 宽高比差异较大,使用meet确保内容完全可见
|
|
287
286
|
this.svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
288
287
|
}
|
|
289
|
-
console.log('fitToView');
|
|
290
288
|
this.updateViewBox();
|
|
291
289
|
}
|
|
292
290
|
/**
|
|
@@ -357,7 +355,6 @@ class SvgMapView {
|
|
|
357
355
|
* 绘制图层,不传参数则默认绘制所有图层
|
|
358
356
|
*/
|
|
359
357
|
onDrawLayers(type) {
|
|
360
|
-
console.log('onDrawLayers----->', type);
|
|
361
358
|
if (type) {
|
|
362
359
|
const layer = this.layers.find((layer) => layer.getType() === type);
|
|
363
360
|
if (layer) {
|
|
@@ -434,7 +431,6 @@ class SvgMapView {
|
|
|
434
431
|
refresh() {
|
|
435
432
|
if (this.destroyed)
|
|
436
433
|
return;
|
|
437
|
-
console.log('refresh----->');
|
|
438
434
|
this.render();
|
|
439
435
|
}
|
|
440
436
|
// ==================== 拖拽功能 ====================
|
|
@@ -1051,15 +1047,37 @@ class PathLayer extends BaseLayer {
|
|
|
1051
1047
|
d += ' Z ';
|
|
1052
1048
|
}
|
|
1053
1049
|
});
|
|
1054
|
-
// 3. svgElements
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1050
|
+
// 3. svgElements(解析 SVG 字符串并提取 path 数据)
|
|
1051
|
+
Object.values(svgElements).forEach((svgPath) => {
|
|
1052
|
+
const svgPathString = svgPath?.metadata?.svg;
|
|
1053
|
+
if (svgPathString && typeof svgPathString === 'string' && svgPathString.trim()) {
|
|
1054
|
+
// 处理转义字符
|
|
1055
|
+
const processedSvgString = svgPathString.replace(/\\n/g, '\n').replace(/\\"/g, '"');
|
|
1056
|
+
// 解析 SVG 字符串
|
|
1057
|
+
const parser = new DOMParser();
|
|
1058
|
+
const svgDoc = parser.parseFromString(processedSvgString, 'image/svg+xml');
|
|
1059
|
+
const svgElement = svgDoc.documentElement;
|
|
1060
|
+
if (svgElement.tagName === 'svg') {
|
|
1061
|
+
// 查找 path 元素
|
|
1062
|
+
const pathElement = svgElement.querySelector('path');
|
|
1063
|
+
if (pathElement) {
|
|
1064
|
+
const pathData = pathElement.getAttribute('d');
|
|
1065
|
+
if (pathData) {
|
|
1066
|
+
// 获取 SVG 元素的变换参数
|
|
1067
|
+
const centerCoords = svgPath.coordinates?.[0] || [0, 0];
|
|
1068
|
+
const center = [centerCoords[0], centerCoords[1]];
|
|
1069
|
+
const userScale = svgPath.metadata.scale || 1;
|
|
1070
|
+
const direction = svgPath.metadata?.direction || 0;
|
|
1071
|
+
const originalWidth = parseFloat(svgElement.getAttribute('width') || '76');
|
|
1072
|
+
const originalHeight = parseFloat(svgElement.getAttribute('height') || '68');
|
|
1073
|
+
// 应用变换到路径数据
|
|
1074
|
+
const transformedPathData = this.transformSvgPath(pathData, center, userScale, direction, originalWidth, originalHeight);
|
|
1075
|
+
d += transformedPathData + ' ';
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1060
1078
|
}
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1063
1081
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1064
1082
|
path.setAttribute('d', d);
|
|
1065
1083
|
const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
|
|
@@ -1084,47 +1102,132 @@ class PathLayer extends BaseLayer {
|
|
|
1084
1102
|
// 2. 创建一个组,应用 clipPath
|
|
1085
1103
|
const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1086
1104
|
group.setAttribute('clip-path', `url(#${clipPathId})`);
|
|
1087
|
-
group.setAttribute('opacity', '0.
|
|
1088
|
-
// 3.
|
|
1105
|
+
group.setAttribute('opacity', '0.5'); // 统一透明度,防止叠加脏乱
|
|
1106
|
+
// 3. 优化渲染:按样式分组并合并路径
|
|
1107
|
+
this.renderOptimizedPaths(group);
|
|
1108
|
+
svgGroup.appendChild(group);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* 优化渲染:按样式分组并合并路径,减少 DOM 节点数量
|
|
1112
|
+
*/
|
|
1113
|
+
renderOptimizedPaths(group) {
|
|
1114
|
+
// 按样式分组存储路径数据
|
|
1115
|
+
const styleGroups = new Map();
|
|
1116
|
+
// 收集所有路径数据并按样式分组
|
|
1089
1117
|
for (const element of this.elements) {
|
|
1090
|
-
|
|
1118
|
+
// 类型断言:PathLayer 中的 elements 实际上是 PathElements 结构
|
|
1119
|
+
const pathElement = element;
|
|
1120
|
+
const { id, elements } = pathElement;
|
|
1091
1121
|
this.boundaryPaths[id] = [];
|
|
1122
|
+
elements.forEach((pathElement) => {
|
|
1123
|
+
const { coordinates, style } = pathElement;
|
|
1124
|
+
if (coordinates.length < 2)
|
|
1125
|
+
return;
|
|
1126
|
+
// 生成样式键(用于分组)
|
|
1127
|
+
const styleKey = this.generateStyleKey(style);
|
|
1128
|
+
// 构建路径数据
|
|
1129
|
+
let pathData = `M ${coordinates[0][0]} ${coordinates[0][1]}`;
|
|
1130
|
+
for (let i = 1; i < coordinates.length; i++) {
|
|
1131
|
+
pathData += ` L ${coordinates[i][0]} ${coordinates[i][1]}`;
|
|
1132
|
+
}
|
|
1133
|
+
// 按样式分组存储
|
|
1134
|
+
if (!styleGroups.has(styleKey)) {
|
|
1135
|
+
styleGroups.set(styleKey, { pathData: [], elements: [] });
|
|
1136
|
+
}
|
|
1137
|
+
styleGroups.get(styleKey).pathData.push(pathData);
|
|
1138
|
+
styleGroups.get(styleKey).elements.push(pathElement);
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
// 为每种样式创建一个合并的 path 元素
|
|
1142
|
+
styleGroups.forEach((groupData) => {
|
|
1143
|
+
const { pathData, elements } = groupData;
|
|
1144
|
+
if (pathData.length === 0)
|
|
1145
|
+
return;
|
|
1146
|
+
// 使用第一个元素的样式作为该组的样式
|
|
1147
|
+
const firstElement = elements[0];
|
|
1148
|
+
const style = firstElement.style;
|
|
1149
|
+
// 创建合并的 path 元素
|
|
1150
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1151
|
+
// 合并所有路径数据
|
|
1152
|
+
const mergedPathData = pathData.join(' ');
|
|
1153
|
+
path.setAttribute('d', mergedPathData);
|
|
1154
|
+
// 设置样式属性
|
|
1155
|
+
path.setAttribute('fill', 'none');
|
|
1156
|
+
path.setAttribute('stroke', style.lineColor || '#000000');
|
|
1157
|
+
path.setAttribute('mix-blend-mode', 'normal');
|
|
1158
|
+
const lineWidth = Math.max(style.lineWidth || 1, 0.5);
|
|
1159
|
+
path.setAttribute('stroke-width', lineWidth.toString());
|
|
1160
|
+
path.setAttribute('stroke-linecap', 'round');
|
|
1161
|
+
path.setAttribute('stroke-linejoin', 'round');
|
|
1162
|
+
path.classList.add('vector-path');
|
|
1163
|
+
// 将合并的 path 添加到组中
|
|
1164
|
+
group.appendChild(path);
|
|
1165
|
+
// 保存引用到 boundaryPaths 中(保持兼容性)
|
|
1092
1166
|
elements.forEach((element) => {
|
|
1093
|
-
|
|
1167
|
+
const { id } = element;
|
|
1168
|
+
if (!this.boundaryPaths[id]) {
|
|
1169
|
+
this.boundaryPaths[id] = [];
|
|
1170
|
+
}
|
|
1171
|
+
this.boundaryPaths[id].push(path);
|
|
1094
1172
|
});
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* 变换 SVG 路径数据
|
|
1177
|
+
*/
|
|
1178
|
+
transformSvgPath(pathData, center, scale, direction, originalWidth, originalHeight) {
|
|
1179
|
+
// 解析路径数据并应用变换
|
|
1180
|
+
const commands = pathData.match(/[MmLlHhVvCcSsQqTtAaZz][^MmLlHhVvCcSsQqTtAaZz]*/g) || [];
|
|
1181
|
+
let transformedCommands = [];
|
|
1182
|
+
for (const command of commands) {
|
|
1183
|
+
const type = command[0];
|
|
1184
|
+
const params = command
|
|
1185
|
+
.slice(1)
|
|
1186
|
+
.trim()
|
|
1187
|
+
.split(/[\s,]+/)
|
|
1188
|
+
.filter(Boolean)
|
|
1189
|
+
.map(Number);
|
|
1190
|
+
if (type === 'Z' || type === 'z') {
|
|
1191
|
+
// 闭合路径,不需要变换
|
|
1192
|
+
transformedCommands.push(command);
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
// 处理坐标参数
|
|
1196
|
+
let transformedParams = [];
|
|
1197
|
+
for (let i = 0; i < params.length; i += 2) {
|
|
1198
|
+
if (i + 1 < params.length) {
|
|
1199
|
+
let x = params[i];
|
|
1200
|
+
let y = params[i + 1];
|
|
1201
|
+
// 应用变换:先平移到中心,然后缩放、旋转,最后平移到目标位置
|
|
1202
|
+
// 1. 平移到原点(相对于原始尺寸的中心)
|
|
1203
|
+
x -= originalWidth / 2;
|
|
1204
|
+
y -= originalHeight / 2;
|
|
1205
|
+
// 2. 应用缩放
|
|
1206
|
+
x *= scale;
|
|
1207
|
+
y *= scale;
|
|
1208
|
+
// 3. 应用旋转
|
|
1209
|
+
const cos = Math.cos(-direction);
|
|
1210
|
+
const sin = Math.sin(-direction);
|
|
1211
|
+
const newX = x * cos - y * sin;
|
|
1212
|
+
const newY = x * sin + y * cos;
|
|
1213
|
+
// 4. 平移到目标位置
|
|
1214
|
+
x = newX + center[0];
|
|
1215
|
+
y = newY + center[1];
|
|
1216
|
+
transformedParams.push(x, y);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
// 重建命令
|
|
1220
|
+
if (transformedParams.length > 0) {
|
|
1221
|
+
transformedCommands.push(type + transformedParams.join(' '));
|
|
1222
|
+
}
|
|
1095
1223
|
}
|
|
1096
|
-
|
|
1224
|
+
return transformedCommands.join(' ');
|
|
1097
1225
|
}
|
|
1098
1226
|
/**
|
|
1099
|
-
*
|
|
1227
|
+
* 生成样式键,用于路径分组
|
|
1100
1228
|
*/
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
if (coordinates.length < 2)
|
|
1104
|
-
return;
|
|
1105
|
-
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1106
|
-
// 构建路径数据
|
|
1107
|
-
let pathData = `M ${coordinates[0][0]} ${coordinates[0][1]}`;
|
|
1108
|
-
for (let i = 1; i < coordinates.length; i++) {
|
|
1109
|
-
pathData += ` L ${coordinates[i][0]} ${coordinates[i][1]}`;
|
|
1110
|
-
}
|
|
1111
|
-
path.style.mixBlendMode = 'normal';
|
|
1112
|
-
// 设置路径属性
|
|
1113
|
-
path.setAttribute('d', pathData);
|
|
1114
|
-
// 直接给fill的颜色设置透明度会导致path重叠的部分颜色叠加,所以使用fill填充实色,通过fill-opacity设置透明度
|
|
1115
|
-
path.setAttribute('fill', 'none');
|
|
1116
|
-
// path.setAttribute('fill-opacity', '0.4');
|
|
1117
|
-
path.setAttribute('stroke', style.lineColor || '#000000');
|
|
1118
|
-
path.setAttribute('mix-blend-mode', 'normal');
|
|
1119
|
-
const lineWidth = Math.max(style.lineWidth || 1, 0.5);
|
|
1120
|
-
path.setAttribute('stroke-width', lineWidth.toString());
|
|
1121
|
-
path.setAttribute('stroke-linecap', 'round');
|
|
1122
|
-
path.setAttribute('stroke-linejoin', 'round');
|
|
1123
|
-
// 注意:这里不设置 opacity,因为透明度由父组控制
|
|
1124
|
-
// path.setAttribute('vector-effect', 'non-scaling-stroke');
|
|
1125
|
-
path.classList.add('vector-path');
|
|
1126
|
-
this.boundaryPaths[id].push(path);
|
|
1127
|
-
group.appendChild(path);
|
|
1229
|
+
generateStyleKey(style) {
|
|
1230
|
+
return `${style.lineColor || '#000000'}-${style.lineWidth || 1}-${style.opacity || 1}`;
|
|
1128
1231
|
}
|
|
1129
1232
|
}
|
|
1130
1233
|
|
|
@@ -1548,7 +1651,7 @@ const DOODLE_STYLES = {
|
|
|
1548
1651
|
lineColor: '#ff5722',
|
|
1549
1652
|
fillColor: '#ff9800', // 粉色半透明填充
|
|
1550
1653
|
lineWidth: DEFAULT_LINE_WIDTHS.TIME_LIMIT_OBSTACLE,
|
|
1551
|
-
opacity: DEFAULT_OPACITIES.
|
|
1654
|
+
opacity: DEFAULT_OPACITIES.DOODLE,
|
|
1552
1655
|
};
|
|
1553
1656
|
const PATH_EDGE_STYLES = {
|
|
1554
1657
|
lineWidth: DEFAULT_LINE_WIDTHS.PATH,
|
|
@@ -1590,13 +1693,13 @@ const DEFAULT_STYLES = {
|
|
|
1590
1693
|
function convertPointsFormat(points) {
|
|
1591
1694
|
if (!points || points.length === 0)
|
|
1592
1695
|
return null;
|
|
1593
|
-
return points.map(point => {
|
|
1696
|
+
return points.map((point) => {
|
|
1594
1697
|
if (point.length >= 2) {
|
|
1595
1698
|
// 对前两个元素应用缩放因子,保留其他元素
|
|
1596
1699
|
return [
|
|
1597
1700
|
point[0] * SCALE_FACTOR,
|
|
1598
1701
|
-point[1] * SCALE_FACTOR, // Y轴翻转,与Python代码一致
|
|
1599
|
-
...point.slice(2) // 保留第三个及以后的元素
|
|
1702
|
+
...point.slice(2), // 保留第三个及以后的元素
|
|
1600
1703
|
];
|
|
1601
1704
|
}
|
|
1602
1705
|
return point;
|
|
@@ -1611,7 +1714,7 @@ function convertPositionFormat(position) {
|
|
|
1611
1714
|
return null;
|
|
1612
1715
|
return {
|
|
1613
1716
|
x: position[0] * SCALE_FACTOR,
|
|
1614
|
-
y: -position[1] * SCALE_FACTOR // Y轴翻转
|
|
1717
|
+
y: -position[1] * SCALE_FACTOR, // Y轴翻转
|
|
1615
1718
|
};
|
|
1616
1719
|
}
|
|
1617
1720
|
/**
|
|
@@ -1620,9 +1723,146 @@ function convertPositionFormat(position) {
|
|
|
1620
1723
|
function convertCoordinate(x, y) {
|
|
1621
1724
|
return {
|
|
1622
1725
|
x: x * SCALE_FACTOR,
|
|
1623
|
-
y: -y * SCALE_FACTOR // Y轴翻转
|
|
1726
|
+
y: -y * SCALE_FACTOR, // Y轴翻转
|
|
1624
1727
|
};
|
|
1625
1728
|
}
|
|
1729
|
+
/**
|
|
1730
|
+
* @param x x坐标
|
|
1731
|
+
* @param y y坐标
|
|
1732
|
+
* @param isAllowInBoundary 是否允许点在边界上的判断
|
|
1733
|
+
* @return ture-点在边界上即可视为在边界内,false-严格判断点在边界内
|
|
1734
|
+
*/
|
|
1735
|
+
function isPointIn$1(x, y, pointList, isAllowInBoundary) {
|
|
1736
|
+
let count = 0;
|
|
1737
|
+
let size = pointList.length;
|
|
1738
|
+
let p1, p2, p3;
|
|
1739
|
+
for (let i = 0; i < size; i++) {
|
|
1740
|
+
p1 = pointList[i];
|
|
1741
|
+
p2 = pointList[(i + 1) % size];
|
|
1742
|
+
if (p1.y == null || p2.y == null || p1.x == null || p2.x == null) {
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
if (p1.y === p2.y) {
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
if (y > Math.min(p1.y, p2.y) && y < Math.max(p1.y, p2.y)) {
|
|
1749
|
+
const interX = ((y - p1.y) * (p2.x - p1.x)) / (p2.y - p1.y) + p1.x;
|
|
1750
|
+
if (interX >= x) {
|
|
1751
|
+
count++;
|
|
1752
|
+
}
|
|
1753
|
+
else if (interX == x) {
|
|
1754
|
+
return isAllowInBoundary;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
else {
|
|
1758
|
+
if (y == p2.y && x <= p2.x) {
|
|
1759
|
+
p3 = pointList[(i + 2) % size];
|
|
1760
|
+
if (y >= Math.min(p1.y, p3.y) && y <= Math.max(p1.y, p3.y)) {
|
|
1761
|
+
// 若当前点的y坐标位于 p1和p3组成的线段关于y轴的投影中,则记为该点的射线只穿过端点一次。
|
|
1762
|
+
++count;
|
|
1763
|
+
}
|
|
1764
|
+
else {
|
|
1765
|
+
// 若当前点的y坐标不能包含在p1和p3组成的线段关于y轴的投影中,则点射线通过的两条线段组成了一个弯折的部分,
|
|
1766
|
+
// 此时我们记射线穿过该端点两次
|
|
1767
|
+
count += 2;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
return count % 2 == 1;
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* 用于判断三个点的方向的辅助方法
|
|
1776
|
+
*/
|
|
1777
|
+
function orientation(p, q, r) {
|
|
1778
|
+
const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
1779
|
+
if (val == 0)
|
|
1780
|
+
return 0; // colinear
|
|
1781
|
+
return val > 0 ? 1 : 2; // clock or counterclock wise
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* 检查点q是否在线段pr上的辅助方法
|
|
1785
|
+
*/
|
|
1786
|
+
function onSegment(p, q, r) {
|
|
1787
|
+
if (q.x <= Math.max(p.x, r.x) &&
|
|
1788
|
+
q.x >= Math.min(p.x, r.x) &&
|
|
1789
|
+
q.y <= Math.max(p.y, r.y) &&
|
|
1790
|
+
q.y >= Math.min(p.y, r.y)) {
|
|
1791
|
+
return true;
|
|
1792
|
+
}
|
|
1793
|
+
return false;
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* 判断两条线段是否相交的方法
|
|
1797
|
+
*/
|
|
1798
|
+
function doTwoLinesIntersect(p1, q1, p2, q2) {
|
|
1799
|
+
//处理p1和q1两个点相同的情况
|
|
1800
|
+
if (p1.x - q1.x == 0 && p1.y - q1.y == 0) {
|
|
1801
|
+
return false;
|
|
1802
|
+
}
|
|
1803
|
+
if (p2.x - q2.x == 0 && p2.y - q2.y == 0) {
|
|
1804
|
+
return false;
|
|
1805
|
+
}
|
|
1806
|
+
// 计算四个点的方向
|
|
1807
|
+
const o1 = orientation(p1, q1, p2);
|
|
1808
|
+
const o2 = orientation(p1, q1, q2);
|
|
1809
|
+
const o3 = orientation(p2, q2, p1);
|
|
1810
|
+
const o4 = orientation(p2, q2, q1);
|
|
1811
|
+
// 一般情况,如果四个方向两两不同,则线段相交
|
|
1812
|
+
if (o1 != o2 && o3 != o4) {
|
|
1813
|
+
return true;
|
|
1814
|
+
}
|
|
1815
|
+
// 特殊情况,当线段的端点在另一条线段上时
|
|
1816
|
+
if (o1 == 0 && onSegment(p1, q1, p2))
|
|
1817
|
+
return true;
|
|
1818
|
+
if (o2 == 0 && onSegment(p1, q1, q2))
|
|
1819
|
+
return true;
|
|
1820
|
+
if (o3 == 0 && onSegment(p2, q2, p1))
|
|
1821
|
+
return true;
|
|
1822
|
+
if (o4 == 0 && onSegment(p2, q2, q1))
|
|
1823
|
+
return true;
|
|
1824
|
+
// 如果以上情况都不满足,则线段不相交
|
|
1825
|
+
return false;
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* 判断多点折线是否相交
|
|
1829
|
+
*/
|
|
1830
|
+
function doIntersect(points1, points2) {
|
|
1831
|
+
if (points1 == null || points2 == null || points1.length < 3 || points2.length < 3) {
|
|
1832
|
+
return false;
|
|
1833
|
+
}
|
|
1834
|
+
for (let i = 0; i < points1.length - 1; i++) {
|
|
1835
|
+
for (let j = 0; j < points2.length - 1; j++) {
|
|
1836
|
+
if (doTwoLinesIntersect(points1[i], points1[i + 1], points2[j], points2[j + 1])) {
|
|
1837
|
+
return true;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
return false;
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* 两个图形是否完全分离,互相不包含
|
|
1845
|
+
*/
|
|
1846
|
+
function isOutsideToEachOther(points1, points2) {
|
|
1847
|
+
// 相交关系
|
|
1848
|
+
if (doIntersect(points1, points2)) {
|
|
1849
|
+
return false;
|
|
1850
|
+
}
|
|
1851
|
+
// 点关系,判断每个图形的点都在另一个图形外部
|
|
1852
|
+
for (let point of points1) {
|
|
1853
|
+
if (isPointIn$1(point.x, point.y, points2, true)) {
|
|
1854
|
+
// Log.i("ycf", "isOutsideToEachOther: mapPoint1=" + mapPoint);
|
|
1855
|
+
return false;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
for (let point of points2) {
|
|
1859
|
+
if (isPointIn$1(point.x, point.y, points1, true)) {
|
|
1860
|
+
// Log.i("ycf", "isOutsideToEachOther: mapPoint2=" + mapPoint);
|
|
1861
|
+
return false;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
return true;
|
|
1865
|
+
}
|
|
1626
1866
|
|
|
1627
1867
|
/**
|
|
1628
1868
|
* 按Python逻辑创建路径段:根据连续的两点之间的关系确定线段类型
|
|
@@ -1885,6 +2125,136 @@ function calculateMapGpsCenter(mapData) {
|
|
|
1885
2125
|
};
|
|
1886
2126
|
}
|
|
1887
2127
|
|
|
2128
|
+
/**
|
|
2129
|
+
* 并查集(Union-Find)是一种非常高效的数据结构,用于处理动态连通性问题。
|
|
2130
|
+
* 它可以快速判断网络中任意两点是否连通,并能将不连通的集合合并。
|
|
2131
|
+
*/
|
|
2132
|
+
class UnionFind {
|
|
2133
|
+
/**
|
|
2134
|
+
* 构造函数,n为图的节点总数
|
|
2135
|
+
* @param {number} n - 节点总数
|
|
2136
|
+
*/
|
|
2137
|
+
constructor(n) {
|
|
2138
|
+
this.count = n; // 连通分量的数量
|
|
2139
|
+
this.parent = new Array(n); // parent[i]表示第i个元素所指向的父节点
|
|
2140
|
+
// 初始时,每个节点的父节点是自己
|
|
2141
|
+
for (let i = 0; i < n; i++) {
|
|
2142
|
+
this.parent[i] = i;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* 查找元素p所对应的集合编号(根节点)
|
|
2147
|
+
* @param {number} p - 要查找的元素
|
|
2148
|
+
* @returns {number} 根节点的编号
|
|
2149
|
+
*/
|
|
2150
|
+
find(p) {
|
|
2151
|
+
while (p !== this.parent[p]) {
|
|
2152
|
+
this.parent[p] = this.parent[this.parent[p]]; // 路径压缩
|
|
2153
|
+
p = this.parent[p];
|
|
2154
|
+
}
|
|
2155
|
+
return p;
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* 判断元素p和元素q是否属于同一集合
|
|
2159
|
+
* @param {number} p - 第一个元素
|
|
2160
|
+
* @param {number} q - 第二个元素
|
|
2161
|
+
* @returns {boolean} 是否连通
|
|
2162
|
+
*/
|
|
2163
|
+
isConnected(p, q) {
|
|
2164
|
+
return this.find(p) === this.find(q);
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* 合并元素p和元素q所属的集合
|
|
2168
|
+
* @param {number} p - 第一个元素
|
|
2169
|
+
* @param {number} q - 第二个元素
|
|
2170
|
+
*/
|
|
2171
|
+
union(p, q) {
|
|
2172
|
+
const rootP = this.find(p);
|
|
2173
|
+
const rootQ = this.find(q);
|
|
2174
|
+
if (rootP === rootQ) {
|
|
2175
|
+
return; // 已经在同一个集合中
|
|
2176
|
+
}
|
|
2177
|
+
// 将较小的根节点作为父节点(按秩合并的简化版本)
|
|
2178
|
+
if (rootP < rootQ) {
|
|
2179
|
+
this.parent[rootQ] = rootP;
|
|
2180
|
+
}
|
|
2181
|
+
else {
|
|
2182
|
+
this.parent[rootP] = rootQ;
|
|
2183
|
+
}
|
|
2184
|
+
// 两个集合合并成一个集合,连通分量减1
|
|
2185
|
+
this.count--;
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* 获取当前的连通分量个数
|
|
2189
|
+
* @returns {number} 连通分量数量
|
|
2190
|
+
*/
|
|
2191
|
+
getCount() {
|
|
2192
|
+
return this.count;
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* 获取联通的组
|
|
2196
|
+
* @param {Array} list - 原始元素列表
|
|
2197
|
+
* @returns {Array<Set>} 联通组列表
|
|
2198
|
+
*/
|
|
2199
|
+
getConnectedGroup(list) {
|
|
2200
|
+
if (!list || list.length === 0 || !this.parent || this.parent.length === 0) {
|
|
2201
|
+
return null;
|
|
2202
|
+
}
|
|
2203
|
+
if (list.length !== this.parent.length) {
|
|
2204
|
+
return null;
|
|
2205
|
+
}
|
|
2206
|
+
const map = new Map();
|
|
2207
|
+
// 遍历所有元素,按根节点分组
|
|
2208
|
+
for (let i = 0; i < this.parent.length; i++) {
|
|
2209
|
+
const root = this.parent[i];
|
|
2210
|
+
if (!map.has(root)) {
|
|
2211
|
+
map.set(root, new Set());
|
|
2212
|
+
}
|
|
2213
|
+
map.get(root).add(list[i]);
|
|
2214
|
+
}
|
|
2215
|
+
return Array.from(map.values());
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* 重置并查集
|
|
2219
|
+
* @param {number} n - 新的节点总数
|
|
2220
|
+
*/
|
|
2221
|
+
reset(n) {
|
|
2222
|
+
this.count = n;
|
|
2223
|
+
this.parent = new Array(n);
|
|
2224
|
+
for (let i = 0; i < n; i++) {
|
|
2225
|
+
this.parent[i] = i;
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
function isTunnelConnected(a, b, connectIds) {
|
|
2231
|
+
if (!a || !b)
|
|
2232
|
+
return false;
|
|
2233
|
+
if (!connectIds || connectIds?.length === 0)
|
|
2234
|
+
return false;
|
|
2235
|
+
const temp = [a?.id, b?.id];
|
|
2236
|
+
temp.sort();
|
|
2237
|
+
return connectIds?.includes(temp?.join('-'));
|
|
2238
|
+
}
|
|
2239
|
+
function isOverlayConnected(a, b) {
|
|
2240
|
+
if (!a || !b) {
|
|
2241
|
+
return false;
|
|
2242
|
+
}
|
|
2243
|
+
if (!a?.points?.length || !b?.points?.length) {
|
|
2244
|
+
return false;
|
|
2245
|
+
}
|
|
2246
|
+
const aPoints = a?.points?.map(item => ({ x: item[0], y: item[1] }));
|
|
2247
|
+
const bPoints = b?.points?.map(item => ({ x: item[0], y: item[1] }));
|
|
2248
|
+
try {
|
|
2249
|
+
if (isOutsideToEachOther(aPoints, bPoints)) {
|
|
2250
|
+
return false;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
catch (error) {
|
|
2254
|
+
console.log('error->', error);
|
|
2255
|
+
}
|
|
2256
|
+
return true;
|
|
2257
|
+
}
|
|
1888
2258
|
/**
|
|
1889
2259
|
* 通过 mapData 和 pathData 生成所有 boundary 的数据
|
|
1890
2260
|
* @param mapData 地图数据
|
|
@@ -1893,11 +2263,12 @@ function calculateMapGpsCenter(mapData) {
|
|
|
1893
2263
|
*/
|
|
1894
2264
|
function generateBoundaryData(mapData, pathData) {
|
|
1895
2265
|
const boundaryData = [];
|
|
2266
|
+
let chargingPileBoundary = undefined;
|
|
1896
2267
|
if (!mapData || !mapData.sub_maps) {
|
|
1897
2268
|
return boundaryData;
|
|
1898
2269
|
}
|
|
1899
2270
|
// 第一步:收集所有TUNNEL数据的connection信息
|
|
1900
|
-
const
|
|
2271
|
+
const connectIds = [];
|
|
1901
2272
|
// 遍历mapData中的tunnels字段
|
|
1902
2273
|
if (mapData.tunnels && Array.isArray(mapData.tunnels)) {
|
|
1903
2274
|
for (const tunnel of mapData.tunnels) {
|
|
@@ -1905,10 +2276,8 @@ function generateBoundaryData(mapData, pathData) {
|
|
|
1905
2276
|
if (connection) {
|
|
1906
2277
|
// connection可能是单个数字或数组
|
|
1907
2278
|
if (Array.isArray(connection)) {
|
|
1908
|
-
connection.
|
|
1909
|
-
|
|
1910
|
-
else if (typeof connection === 'number') {
|
|
1911
|
-
connectedBoundaryIds.add(connection);
|
|
2279
|
+
connection.sort();
|
|
2280
|
+
connectIds.push(connection.join('-'));
|
|
1912
2281
|
}
|
|
1913
2282
|
}
|
|
1914
2283
|
}
|
|
@@ -1919,9 +2288,9 @@ function generateBoundaryData(mapData, pathData) {
|
|
|
1919
2288
|
if (!subMap.elements)
|
|
1920
2289
|
continue;
|
|
1921
2290
|
// 每个sub_map的elements是边界坐标,没有sub_map只有一个boundary数据
|
|
1922
|
-
const boundaryElement = subMap.elements.find(element => element.type === 'BOUNDARY');
|
|
2291
|
+
const boundaryElement = subMap.elements.find((element) => element.type === 'BOUNDARY');
|
|
1923
2292
|
// 如果当前subMap存在充电桩且充电桩存在tunnel,说明当前subMap中的boundary是初始boundary,这个boundary不为孤立区域
|
|
1924
|
-
const hasTunnelToChargingPile = subMap.elements.some(element => element.type === 'CHARGING_PILE' && element.tunnel);
|
|
2293
|
+
const hasTunnelToChargingPile = subMap.elements.some((element) => element.type === 'CHARGING_PILE' && element.tunnel);
|
|
1925
2294
|
// 创建基础的 boundary 数据(来自 mapData)
|
|
1926
2295
|
const boundary = {
|
|
1927
2296
|
// 从 BOUNDARY 元素复制属性
|
|
@@ -1930,8 +2299,6 @@ function generateBoundaryData(mapData, pathData) {
|
|
|
1930
2299
|
area: subMap?.area,
|
|
1931
2300
|
points: convertPointsFormat(boundaryElement?.points) || [],
|
|
1932
2301
|
type: boundaryElement.type,
|
|
1933
|
-
// 判断是否为孤立子区域
|
|
1934
|
-
isIsolated: hasTunnelToChargingPile ? false : !connectedBoundaryIds.has(boundaryElement.id)
|
|
1935
2302
|
};
|
|
1936
2303
|
// 如果有 pathData,尝试匹配对应的分区数据
|
|
1937
2304
|
if (pathData) {
|
|
@@ -1947,8 +2314,33 @@ function generateBoundaryData(mapData, pathData) {
|
|
|
1947
2314
|
boundary.endTime = partitionData.endTime;
|
|
1948
2315
|
}
|
|
1949
2316
|
}
|
|
2317
|
+
if (hasTunnelToChargingPile) {
|
|
2318
|
+
chargingPileBoundary = boundary;
|
|
2319
|
+
}
|
|
1950
2320
|
boundaryData.push(boundary);
|
|
1951
2321
|
}
|
|
2322
|
+
const unionFind = new UnionFind(boundaryData?.length);
|
|
2323
|
+
for (let i = 0; i < boundaryData?.length - 1; i++) {
|
|
2324
|
+
for (let j = i + 1; j < boundaryData?.length; j++) {
|
|
2325
|
+
const boundary1 = boundaryData[i];
|
|
2326
|
+
const boundary2 = boundaryData[j];
|
|
2327
|
+
const isChannelConnect = isTunnelConnected(boundary1, boundary2, connectIds);
|
|
2328
|
+
const isOverlayConnect = isOverlayConnected(boundary1, boundary2);
|
|
2329
|
+
if (isChannelConnect || isOverlayConnect) {
|
|
2330
|
+
unionFind.union(i, j);
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
const tunnelAndOverlayList = unionFind.getConnectedGroup(boundaryData);
|
|
2335
|
+
const chargingPileConnectBoundarys = tunnelAndOverlayList?.find(item => item?.has(chargingPileBoundary));
|
|
2336
|
+
for (let boundary of boundaryData) {
|
|
2337
|
+
if (chargingPileConnectBoundarys?.has(boundary)) {
|
|
2338
|
+
boundary.isIsolated = false;
|
|
2339
|
+
}
|
|
2340
|
+
else {
|
|
2341
|
+
boundary.isIsolated = true;
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
1952
2344
|
return boundaryData;
|
|
1953
2345
|
}
|
|
1954
2346
|
|
|
@@ -2165,13 +2557,15 @@ var hNoPosition = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEoAAABKCAYAAAA
|
|
|
2165
2557
|
|
|
2166
2558
|
var hDisabled = "data:image/png;base64,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";
|
|
2167
2559
|
|
|
2560
|
+
var x3Edger = "data:image/png;base64,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";
|
|
2561
|
+
|
|
2168
2562
|
var x3Mower = "data:image/png;base64,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";
|
|
2169
2563
|
|
|
2170
2564
|
var x3NoPosition = "data:image/png;base64,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";
|
|
2171
2565
|
|
|
2172
2566
|
var x3Disabled = "data:image/png;base64,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";
|
|
2173
2567
|
|
|
2174
|
-
function getMowerImageByModal(mowerModal) {
|
|
2568
|
+
function getMowerImageByModal(mowerModal, hasEdger) {
|
|
2175
2569
|
if (mowerModal.includes('i')) {
|
|
2176
2570
|
return iMower;
|
|
2177
2571
|
}
|
|
@@ -2179,7 +2573,7 @@ function getMowerImageByModal(mowerModal) {
|
|
|
2179
2573
|
return hMower;
|
|
2180
2574
|
}
|
|
2181
2575
|
else if (mowerModal.includes('x3')) {
|
|
2182
|
-
return x3Mower;
|
|
2576
|
+
return hasEdger ? x3Edger : x3Mower;
|
|
2183
2577
|
}
|
|
2184
2578
|
return iMower;
|
|
2185
2579
|
}
|
|
@@ -2207,12 +2601,12 @@ function getNoPositionMowerImageByModal(mowerModal) {
|
|
|
2207
2601
|
}
|
|
2208
2602
|
return iNoPosition;
|
|
2209
2603
|
}
|
|
2210
|
-
function getMowerImage(positonConfig, modelType) {
|
|
2604
|
+
function getMowerImage(positonConfig, modelType, hasEdger) {
|
|
2211
2605
|
if (!positonConfig)
|
|
2212
2606
|
return '';
|
|
2213
2607
|
const model = modelType?.toLowerCase() || 'i';
|
|
2214
2608
|
const state = positonConfig.vehicleState;
|
|
2215
|
-
const mowerImage = getMowerImageByModal(model);
|
|
2609
|
+
const mowerImage = getMowerImageByModal(model, hasEdger);
|
|
2216
2610
|
const disabledImage = getDisabledMowerImageByModal(model);
|
|
2217
2611
|
const noPositionImage = getNoPositionMowerImageByModal(model);
|
|
2218
2612
|
const positonOutOfRange = isOutOfRange(positonConfig);
|
|
@@ -4694,8 +5088,8 @@ var PathSegmentType;
|
|
|
4694
5088
|
*/
|
|
4695
5089
|
var UnitsType;
|
|
4696
5090
|
(function (UnitsType) {
|
|
4697
|
-
UnitsType["Metric"] = "
|
|
4698
|
-
UnitsType["Imperial"] = "
|
|
5091
|
+
UnitsType["Metric"] = "Metric";
|
|
5092
|
+
UnitsType["Imperial"] = "Imperial";
|
|
4699
5093
|
})(UnitsType || (UnitsType = {}));
|
|
4700
5094
|
/**
|
|
4701
5095
|
* 面积单位类型枚举
|
|
@@ -4982,6 +5376,12 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
4982
5376
|
this.mowingBoundarys = mowingBoundarys;
|
|
4983
5377
|
}
|
|
4984
5378
|
}
|
|
5379
|
+
/**
|
|
5380
|
+
* 获取当前割草任务的边界
|
|
5381
|
+
*/
|
|
5382
|
+
getMowingBoundarys() {
|
|
5383
|
+
return this.mowingBoundarys;
|
|
5384
|
+
}
|
|
4985
5385
|
/**
|
|
4986
5386
|
* SVG渲染方法
|
|
4987
5387
|
*/
|
|
@@ -4990,13 +5390,31 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
4990
5390
|
return;
|
|
4991
5391
|
}
|
|
4992
5392
|
this.scale = scale;
|
|
4993
|
-
|
|
4994
|
-
|
|
5393
|
+
// 将元素分为两组:非割草边界和割草边界
|
|
5394
|
+
const nonMowingElements = [];
|
|
5395
|
+
const mowingElements = [];
|
|
5396
|
+
// 只处理边界边框类型的元素
|
|
4995
5397
|
for (const element of this.elements) {
|
|
4996
5398
|
if (element.type === 'boundary_border') {
|
|
4997
|
-
|
|
5399
|
+
const { originalData } = element;
|
|
5400
|
+
const { id } = originalData || {};
|
|
5401
|
+
// 检查是否为割草边界
|
|
5402
|
+
if (this.mowingBoundarys.includes(Number(id))) {
|
|
5403
|
+
mowingElements.push(element);
|
|
5404
|
+
}
|
|
5405
|
+
else {
|
|
5406
|
+
nonMowingElements.push(element);
|
|
5407
|
+
}
|
|
4998
5408
|
}
|
|
4999
5409
|
}
|
|
5410
|
+
// 先渲染非割草边界
|
|
5411
|
+
for (const element of nonMowingElements) {
|
|
5412
|
+
this.renderBoundaryBorder(svgGroup, element);
|
|
5413
|
+
}
|
|
5414
|
+
// 再渲染割草边界(放在最后)
|
|
5415
|
+
for (const element of mowingElements) {
|
|
5416
|
+
this.renderBoundaryBorder(svgGroup, element);
|
|
5417
|
+
}
|
|
5000
5418
|
}
|
|
5001
5419
|
/**
|
|
5002
5420
|
* 渲染边界边框
|
|
@@ -6065,7 +6483,7 @@ class PathDataProcessor {
|
|
|
6065
6483
|
* 专门处理边界标签的创建、定位和管理
|
|
6066
6484
|
*/
|
|
6067
6485
|
class BoundaryLabelsManager {
|
|
6068
|
-
constructor(svgView, boundaryData) {
|
|
6486
|
+
constructor(svgView, boundaryData, { unitType, language }) {
|
|
6069
6487
|
this.container = null;
|
|
6070
6488
|
this.overlayDiv = null;
|
|
6071
6489
|
this.globalClickHandler = null;
|
|
@@ -6076,6 +6494,8 @@ class BoundaryLabelsManager {
|
|
|
6076
6494
|
this.svgView = svgView;
|
|
6077
6495
|
this.boundaryData = boundaryData;
|
|
6078
6496
|
this.initializeContainer();
|
|
6497
|
+
this.unitType = unitType;
|
|
6498
|
+
this.language = language;
|
|
6079
6499
|
}
|
|
6080
6500
|
/**
|
|
6081
6501
|
* 初始化容器
|
|
@@ -6141,7 +6561,7 @@ class BoundaryLabelsManager {
|
|
|
6141
6561
|
labelDiv.setAttribute('data-boundary-id', boundary.id.toString());
|
|
6142
6562
|
// 样式设置
|
|
6143
6563
|
labelDiv.style.position = 'absolute';
|
|
6144
|
-
labelDiv.style.backgroundColor = 'rgba(30, 30, 31, 0.
|
|
6564
|
+
labelDiv.style.backgroundColor = 'rgba(30, 30, 31, 0.6)';
|
|
6145
6565
|
labelDiv.style.color = 'rgba(255, 255, 255, 1)';
|
|
6146
6566
|
labelDiv.style.padding = '6px';
|
|
6147
6567
|
labelDiv.style.borderRadius = '12px';
|
|
@@ -6158,7 +6578,7 @@ class BoundaryLabelsManager {
|
|
|
6158
6578
|
labelDiv.style.zIndex = BoundaryLabelsManager.Z_INDEX.DEFAULT.toString();
|
|
6159
6579
|
// 计算进度
|
|
6160
6580
|
const progress = boundary.finishedArea && boundary.area
|
|
6161
|
-
? `${Math.
|
|
6581
|
+
? `${Math.floor((boundary.finishedArea / boundary.area) * 100)}%`
|
|
6162
6582
|
: '0%';
|
|
6163
6583
|
// 基础内容(始终显示)
|
|
6164
6584
|
const baseContent = document.createElement('div');
|
|
@@ -6175,12 +6595,15 @@ class BoundaryLabelsManager {
|
|
|
6175
6595
|
this.currentExpandedBoundaryId === boundary.id ? 'block' : 'none';
|
|
6176
6596
|
extendedContent.style.borderTop = '1px solid rgba(255,255,255,0.2)';
|
|
6177
6597
|
extendedContent.style.paddingTop = '6px';
|
|
6598
|
+
const boundaryLayer = this.svgView.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
6599
|
+
const mowingBoundarys = boundaryLayer.getMowingBoundarys();
|
|
6178
6600
|
// 面积信息
|
|
6179
|
-
const totalArea = convertAreaByUnits(boundary.area || 0,
|
|
6180
|
-
const finishedArea = convertAreaByUnits(boundary.finishedArea || 0,
|
|
6601
|
+
const totalArea = convertAreaByUnits(boundary.area || 0, this.unitType);
|
|
6602
|
+
const finishedArea = convertAreaByUnits(boundary.finishedArea || 0, this.unitType);
|
|
6181
6603
|
const coverageText = `Coverage: ${finishedArea.value}/${totalArea.value}`;
|
|
6604
|
+
const isMowing = mowingBoundarys.includes(boundary.id);
|
|
6182
6605
|
// 日期信息
|
|
6183
|
-
const dateText = formatBoundaryDateText(boundary.endTime || 0);
|
|
6606
|
+
const dateText = formatBoundaryDateText(isMowing ? Date.now() / 1000 : boundary.endTime || 0);
|
|
6184
6607
|
const covertHtml = `<div style="margin-bottom: 3px; font-weight: bold;">${coverageText}</div>`;
|
|
6185
6608
|
const dateHtml = `<div>${dateText}</div>`;
|
|
6186
6609
|
extendedContent.innerHTML = boundary.finishedArea > 0 ? `${covertHtml}${dateHtml}` : covertHtml;
|
|
@@ -6298,7 +6721,6 @@ class BoundaryLabelsManager {
|
|
|
6298
6721
|
// 计算边界中心点的地图坐标
|
|
6299
6722
|
const mapCenter = this.calculatePolygonCentroid(boundary.points);
|
|
6300
6723
|
if (!mapCenter) {
|
|
6301
|
-
console.warn(`BoundaryLabelsManager: 无法计算边界 ${boundary.name} (ID: ${boundary.id}) 的中心点`);
|
|
6302
6724
|
return;
|
|
6303
6725
|
}
|
|
6304
6726
|
// 直接使用预计算的数据进行坐标转换
|
|
@@ -6383,7 +6805,6 @@ class BoundaryLabelsManager {
|
|
|
6383
6805
|
area = area / 2;
|
|
6384
6806
|
// 如果面积为0,回退到简单的平均值计算
|
|
6385
6807
|
if (Math.abs(area) < 1e-10) {
|
|
6386
|
-
console.warn('BoundaryLabelsManager: 多边形面积为0,使用平均值计算重心');
|
|
6387
6808
|
return this.calculateAverageCenter(validPoints);
|
|
6388
6809
|
}
|
|
6389
6810
|
centroidX = centroidX / (6 * area);
|
|
@@ -7294,6 +7715,10 @@ class MowerPositionManager {
|
|
|
7294
7715
|
getElement() {
|
|
7295
7716
|
return this.container;
|
|
7296
7717
|
}
|
|
7718
|
+
//
|
|
7719
|
+
setEdger(edger) {
|
|
7720
|
+
this.hasEdger = edger;
|
|
7721
|
+
}
|
|
7297
7722
|
/**
|
|
7298
7723
|
* 根据最后一次有效的位置更新数据
|
|
7299
7724
|
*/
|
|
@@ -7317,7 +7742,6 @@ class MowerPositionManager {
|
|
|
7317
7742
|
postureY = chargingPilesPositionConfig.postureY || 0;
|
|
7318
7743
|
postureTheta = chargingPilesPositionConfig.postureTheta || 0;
|
|
7319
7744
|
}
|
|
7320
|
-
console.log('updatePositionByLastPosition->', postureX, postureY, postureTheta, chargingPilesPositionConfig);
|
|
7321
7745
|
// 检查是否需要更新图片
|
|
7322
7746
|
this.updateMowerImage(chargingPilesPositionConfig);
|
|
7323
7747
|
// 立即更新位置
|
|
@@ -7334,7 +7758,6 @@ class MowerPositionManager {
|
|
|
7334
7758
|
const postureX = positionConfig?.postureX || this.lastPosition?.x || 0;
|
|
7335
7759
|
const postureY = positionConfig?.postureY || this.lastPosition?.y || 0;
|
|
7336
7760
|
const postureTheta = positionConfig?.postureTheta || this.lastPosition?.rotation || 0;
|
|
7337
|
-
console.log('updatePosition manager', JSON.stringify(this.currentPosition), this.currentPosition, !this.currentPosition, positionConfig, this.lastPosition, animationTime);
|
|
7338
7761
|
// 停止当前动画(如果有)
|
|
7339
7762
|
this.stopAnimation();
|
|
7340
7763
|
// 第一个点
|
|
@@ -7344,7 +7767,6 @@ class MowerPositionManager {
|
|
|
7344
7767
|
y: postureY,
|
|
7345
7768
|
rotation: postureTheta,
|
|
7346
7769
|
};
|
|
7347
|
-
console.log('updatePosition first->', this.currentPosition);
|
|
7348
7770
|
this.setElementPosition(this.currentPosition.x, this.currentPosition.y, this.currentPosition.rotation);
|
|
7349
7771
|
return;
|
|
7350
7772
|
}
|
|
@@ -7366,7 +7788,7 @@ class MowerPositionManager {
|
|
|
7366
7788
|
const imgElement = this.mowerElement.querySelector('img');
|
|
7367
7789
|
if (!imgElement)
|
|
7368
7790
|
return;
|
|
7369
|
-
const imageSrc = getMowerImage(positonConfig, this.modelType);
|
|
7791
|
+
const imageSrc = getMowerImage(positonConfig, this.modelType, this.hasEdger);
|
|
7370
7792
|
if (imageSrc) {
|
|
7371
7793
|
imgElement.src = imageSrc;
|
|
7372
7794
|
imgElement.style.display = 'block';
|
|
@@ -7453,12 +7875,10 @@ class MowerPositionManager {
|
|
|
7453
7875
|
y: this.onlyUpdateTheta ? 0 : this.targetPosition.y - this.startPosition.y,
|
|
7454
7876
|
rotation: radNormalize(targetTheta - startTheta),
|
|
7455
7877
|
};
|
|
7456
|
-
console.log('startAnimationToPosition-->', this.deltaPosition, this.onlyUpdateTheta, this.targetPosition, this.startPosition);
|
|
7457
7878
|
// 开始动画循环
|
|
7458
7879
|
this.animateStep();
|
|
7459
7880
|
}
|
|
7460
7881
|
forceUpdatePosition() {
|
|
7461
|
-
console.log('forceUpdatePosition-->', this.currentPosition, this.targetPosition, this.startPosition);
|
|
7462
7882
|
this.animateStep();
|
|
7463
7883
|
}
|
|
7464
7884
|
/**
|
|
@@ -7599,15 +8019,40 @@ function throttleAdvanced(func, delay, options = { leading: true, trailing: true
|
|
|
7599
8019
|
}
|
|
7600
8020
|
};
|
|
7601
8021
|
}
|
|
8022
|
+
/**
|
|
8023
|
+
* 检测当前设备是否为移动设备
|
|
8024
|
+
* @returns {boolean} 如果是移动设备返回true,否则返回false
|
|
8025
|
+
*/
|
|
8026
|
+
function isMobileDevice() {
|
|
8027
|
+
// 确保在浏览器环境中运行
|
|
8028
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
8029
|
+
return false;
|
|
8030
|
+
}
|
|
8031
|
+
// 检查用户代理字符串
|
|
8032
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
8033
|
+
const mobileKeywords = [
|
|
8034
|
+
'android', 'webos', 'iphone', 'ipad', 'ipod',
|
|
8035
|
+
'blackberry', 'windows phone', 'mobile'
|
|
8036
|
+
];
|
|
8037
|
+
const isMobileUserAgent = mobileKeywords.some(keyword => userAgent.includes(keyword));
|
|
8038
|
+
// 检查触摸屏支持
|
|
8039
|
+
const hasTouchScreen = 'ontouchstart' in window ||
|
|
8040
|
+
(navigator.maxTouchPoints && navigator.maxTouchPoints > 0);
|
|
8041
|
+
// 检查屏幕尺寸(移动设备通常屏幕较小)
|
|
8042
|
+
const isSmallScreen = window.innerWidth <= 768;
|
|
8043
|
+
// 综合判断:用户代理包含移动设备关键词,或者有触摸屏且屏幕较小
|
|
8044
|
+
return isMobileUserAgent || (hasTouchScreen && isSmallScreen);
|
|
8045
|
+
}
|
|
7602
8046
|
|
|
7603
8047
|
// Google Maps 叠加层类 - 带编辑功能
|
|
7604
8048
|
class MowerMapOverlay {
|
|
7605
|
-
constructor(bounds, mapData, partitionBoundary, mowerPositionConfig, modelType, pathData, isEditMode = false, mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
|
|
8049
|
+
constructor(bounds, mapData, partitionBoundary, mowerPositionConfig, modelType, pathData, isEditMode = false, unitType = UnitsType.Imperial, language = 'en', mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
|
|
7606
8050
|
this.div = null;
|
|
7607
8051
|
this.svgMapView = null;
|
|
7608
8052
|
this.offscreenContainer = null;
|
|
7609
8053
|
this.overlayView = null;
|
|
7610
8054
|
this.defaultTransform = { x: 0, y: 0, rotation: 0 };
|
|
8055
|
+
this.hasEdger = false;
|
|
7611
8056
|
// boundary数据
|
|
7612
8057
|
this.boundaryData = [];
|
|
7613
8058
|
// 边界标签管理器
|
|
@@ -7650,6 +8095,8 @@ class MowerMapOverlay {
|
|
|
7650
8095
|
this.partitionBoundary = partitionBoundary;
|
|
7651
8096
|
this.pathData = pathData;
|
|
7652
8097
|
this.isEditMode = isEditMode;
|
|
8098
|
+
this.unitType = unitType;
|
|
8099
|
+
this.language = language;
|
|
7653
8100
|
this.mapConfig = mapConfig;
|
|
7654
8101
|
this.antennaConfig = antennaConfig;
|
|
7655
8102
|
this.onMapLoad = onMapLoad;
|
|
@@ -7696,7 +8143,6 @@ class MowerMapOverlay {
|
|
|
7696
8143
|
this.isUserAnimation = animationTime > 0;
|
|
7697
8144
|
// 更新割草机位置配置
|
|
7698
8145
|
this.mowerPositionConfig = positionConfig;
|
|
7699
|
-
console.log('updatePosition overlay', positionConfig);
|
|
7700
8146
|
// 更新割草机位置管理器
|
|
7701
8147
|
if (this.mowerPositionManager) {
|
|
7702
8148
|
this.mowerPositionManager.updatePosition(positionConfig, animationTime);
|
|
@@ -7714,6 +8160,12 @@ class MowerMapOverlay {
|
|
|
7714
8160
|
this.overlayView.setMap(map);
|
|
7715
8161
|
}
|
|
7716
8162
|
}
|
|
8163
|
+
setEdger(edger) {
|
|
8164
|
+
this.hasEdger = edger;
|
|
8165
|
+
if (this.mowerPositionManager) {
|
|
8166
|
+
this.mowerPositionManager.setEdger(edger);
|
|
8167
|
+
}
|
|
8168
|
+
}
|
|
7717
8169
|
getMap() {
|
|
7718
8170
|
return this.overlayView ? this.overlayView.getMap() : null;
|
|
7719
8171
|
}
|
|
@@ -7741,7 +8193,6 @@ class MowerMapOverlay {
|
|
|
7741
8193
|
this.svgMapView?.renderLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
7742
8194
|
}
|
|
7743
8195
|
onAdd() {
|
|
7744
|
-
console.log('onAdd');
|
|
7745
8196
|
// 创建包含SVG的div
|
|
7746
8197
|
this.div = document.createElement('div');
|
|
7747
8198
|
this.div.style.borderStyle = 'none';
|
|
@@ -7809,7 +8260,10 @@ class MowerMapOverlay {
|
|
|
7809
8260
|
if (!this.div || !this.svgMapView)
|
|
7810
8261
|
return;
|
|
7811
8262
|
// 创建边界标签管理器
|
|
7812
|
-
this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData
|
|
8263
|
+
this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData, {
|
|
8264
|
+
unitType: this.unitType,
|
|
8265
|
+
language: this.language,
|
|
8266
|
+
});
|
|
7813
8267
|
// 设置叠加层div引用
|
|
7814
8268
|
this.boundaryLabelsManager.setOverlayDiv(this.div);
|
|
7815
8269
|
// 添加所有边界标签
|
|
@@ -7853,11 +8307,10 @@ class MowerMapOverlay {
|
|
|
7853
8307
|
if (!this.div || !this.svgMapView)
|
|
7854
8308
|
return;
|
|
7855
8309
|
// 创建割草机位置管理器,传入动画完成回调
|
|
7856
|
-
this.mowerPositionManager = new MowerPositionManager(this.svgMapView, this.mowerPositionConfig, this.modelType, this.div, () => {
|
|
7857
|
-
console.log('动画完成');
|
|
7858
|
-
}, this.updatePathDataByMowingPositionThrottled.bind(this));
|
|
8310
|
+
this.mowerPositionManager = new MowerPositionManager(this.svgMapView, this.mowerPositionConfig, this.modelType, this.div, () => { }, this.updatePathDataByMowingPositionThrottled.bind(this));
|
|
7859
8311
|
// 设置叠加层div引用
|
|
7860
8312
|
this.mowerPositionManager.setOverlayDiv(this.div);
|
|
8313
|
+
this.mowerPositionManager.setEdger(this.hasEdger);
|
|
7861
8314
|
// 获取容器并添加到主div
|
|
7862
8315
|
const container = this.mowerPositionManager.getElement();
|
|
7863
8316
|
if (container) {
|
|
@@ -7937,7 +8390,7 @@ class MowerMapOverlay {
|
|
|
7937
8390
|
this.rotateHandle.style.pointerEvents = 'auto';
|
|
7938
8391
|
this.rotateHandle.innerHTML = DEFAULT_ROTATE_ICON;
|
|
7939
8392
|
this.editContainer.appendChild(this.rotateHandle);
|
|
7940
|
-
//
|
|
8393
|
+
// 创建拖拽手柄(左下角)- 仅在移动设备上显示
|
|
7941
8394
|
this.dragHandle = document.createElement('div');
|
|
7942
8395
|
this.dragHandle.style.position = 'absolute';
|
|
7943
8396
|
this.dragHandle.style.bottom = '-20px';
|
|
@@ -7948,6 +8401,10 @@ class MowerMapOverlay {
|
|
|
7948
8401
|
this.dragHandle.style.zIndex = EDIT_STYLES.Z_INDEX.HANDLE;
|
|
7949
8402
|
this.dragHandle.style.pointerEvents = 'auto';
|
|
7950
8403
|
this.dragHandle.innerHTML = DEFAULT_DRAG_ICON;
|
|
8404
|
+
// 在PC设备上隐藏拖拽手柄
|
|
8405
|
+
if (!isMobileDevice()) {
|
|
8406
|
+
this.dragHandle.style.display = 'none';
|
|
8407
|
+
}
|
|
7951
8408
|
this.editContainer.appendChild(this.dragHandle);
|
|
7952
8409
|
// 将编辑容器添加到主div
|
|
7953
8410
|
this.div.appendChild(this.editContainer);
|
|
@@ -7989,7 +8446,6 @@ class MowerMapOverlay {
|
|
|
7989
8446
|
this.boundaryLabelsManager.collapseAllLabels();
|
|
7990
8447
|
}
|
|
7991
8448
|
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
7992
|
-
console.log('开始旋转操作');
|
|
7993
8449
|
});
|
|
7994
8450
|
// 旋转手柄的触摸事件
|
|
7995
8451
|
this.rotateHandle.addEventListener('touchstart', (e) => {
|
|
@@ -8005,39 +8461,41 @@ class MowerMapOverlay {
|
|
|
8005
8461
|
this.boundaryLabelsManager.collapseAllLabels();
|
|
8006
8462
|
}
|
|
8007
8463
|
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
8008
|
-
console.log('开始旋转操作(触摸)');
|
|
8009
|
-
}, { passive: false });
|
|
8010
|
-
// 拖拽手柄的鼠标事件
|
|
8011
|
-
this.dragHandle.addEventListener('mousedown', (e) => {
|
|
8012
|
-
e.preventDefault();
|
|
8013
|
-
e.stopPropagation();
|
|
8014
|
-
e.stopImmediatePropagation();
|
|
8015
|
-
this.isDragging = true;
|
|
8016
|
-
this.startPos = { x: e.clientX, y: e.clientY };
|
|
8017
|
-
this.dragHandle.style.cursor = 'grabbing';
|
|
8018
|
-
// 开始编辑时关闭所有展开的边界标签
|
|
8019
|
-
if (this.boundaryLabelsManager) {
|
|
8020
|
-
this.boundaryLabelsManager.collapseAllLabels();
|
|
8021
|
-
}
|
|
8022
|
-
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
8023
|
-
console.log('开始拖动操作(通过手柄)');
|
|
8024
|
-
});
|
|
8025
|
-
// 拖拽手柄的触摸事件
|
|
8026
|
-
this.dragHandle.addEventListener('touchstart', (e) => {
|
|
8027
|
-
e.preventDefault();
|
|
8028
|
-
e.stopPropagation();
|
|
8029
|
-
e.stopImmediatePropagation();
|
|
8030
|
-
this.isDragging = true;
|
|
8031
|
-
const touch = e.touches[0];
|
|
8032
|
-
this.startPos = { x: touch.clientX, y: touch.clientY };
|
|
8033
|
-
this.dragHandle.style.cursor = 'grabbing';
|
|
8034
|
-
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
8035
|
-
console.log('开始拖动操作(通过手柄,触摸)');
|
|
8036
8464
|
}, { passive: false });
|
|
8465
|
+
// 拖拽手柄的鼠标事件 - 仅在移动设备上启用
|
|
8466
|
+
if (isMobileDevice()) {
|
|
8467
|
+
this.dragHandle.addEventListener('mousedown', (e) => {
|
|
8468
|
+
e.preventDefault();
|
|
8469
|
+
e.stopPropagation();
|
|
8470
|
+
e.stopImmediatePropagation();
|
|
8471
|
+
this.isDragging = true;
|
|
8472
|
+
this.startPos = { x: e.clientX, y: e.clientY };
|
|
8473
|
+
this.dragHandle.style.cursor = 'grabbing';
|
|
8474
|
+
// 开始编辑时关闭所有展开的边界标签
|
|
8475
|
+
if (this.boundaryLabelsManager) {
|
|
8476
|
+
this.boundaryLabelsManager.collapseAllLabels();
|
|
8477
|
+
}
|
|
8478
|
+
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
8479
|
+
});
|
|
8480
|
+
// 拖拽手柄的触摸事件
|
|
8481
|
+
this.dragHandle.addEventListener('touchstart', (e) => {
|
|
8482
|
+
e.preventDefault();
|
|
8483
|
+
e.stopPropagation();
|
|
8484
|
+
e.stopImmediatePropagation();
|
|
8485
|
+
this.isDragging = true;
|
|
8486
|
+
const touch = e.touches[0];
|
|
8487
|
+
this.startPos = { x: touch.clientX, y: touch.clientY };
|
|
8488
|
+
this.dragHandle.style.cursor = 'grabbing';
|
|
8489
|
+
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
8490
|
+
}, { passive: false });
|
|
8491
|
+
}
|
|
8037
8492
|
// 编辑容器的鼠标事件(整个区域拖拽)
|
|
8038
8493
|
this.editContainer.addEventListener('mousedown', (e) => {
|
|
8039
|
-
|
|
8040
|
-
|
|
8494
|
+
// 在移动设备上,检查是否点击了拖拽手柄或旋转手柄
|
|
8495
|
+
// 在PC设备上,只检查旋转手柄(拖拽手柄已隐藏)
|
|
8496
|
+
const isDragHandleClick = isMobileDevice() && e.target === this.dragHandle;
|
|
8497
|
+
const isRotateHandleClick = e.target === this.rotateHandle;
|
|
8498
|
+
if (isDragHandleClick || isRotateHandleClick) {
|
|
8041
8499
|
return;
|
|
8042
8500
|
}
|
|
8043
8501
|
e.preventDefault();
|
|
@@ -8054,8 +8512,11 @@ class MowerMapOverlay {
|
|
|
8054
8512
|
});
|
|
8055
8513
|
// 编辑容器的触摸事件(整个区域拖拽)
|
|
8056
8514
|
this.editContainer.addEventListener('touchstart', (e) => {
|
|
8057
|
-
|
|
8058
|
-
|
|
8515
|
+
// 在移动设备上,检查是否点击了拖拽手柄或旋转手柄
|
|
8516
|
+
// 在PC设备上,只检查旋转手柄(拖拽手柄已隐藏)
|
|
8517
|
+
const isDragHandleClick = isMobileDevice() && e.target === this.dragHandle;
|
|
8518
|
+
const isRotateHandleClick = e.target === this.rotateHandle;
|
|
8519
|
+
if (isDragHandleClick || isRotateHandleClick) {
|
|
8059
8520
|
return;
|
|
8060
8521
|
}
|
|
8061
8522
|
e.preventDefault();
|
|
@@ -8115,7 +8576,6 @@ class MowerMapOverlay {
|
|
|
8115
8576
|
e.preventDefault();
|
|
8116
8577
|
e.stopPropagation();
|
|
8117
8578
|
e.stopImmediatePropagation();
|
|
8118
|
-
console.log('结束编辑操作');
|
|
8119
8579
|
}
|
|
8120
8580
|
// 如果是拖拽结束,将像素偏移量转换为地理坐标偏移量
|
|
8121
8581
|
if (this.isDragging) {
|
|
@@ -8137,7 +8597,6 @@ class MowerMapOverlay {
|
|
|
8137
8597
|
e.preventDefault();
|
|
8138
8598
|
e.stopPropagation();
|
|
8139
8599
|
e.stopImmediatePropagation();
|
|
8140
|
-
console.log('结束编辑操作(触摸)');
|
|
8141
8600
|
}
|
|
8142
8601
|
// 如果是拖拽结束,将像素偏移量转换为地理坐标偏移量
|
|
8143
8602
|
if (this.isDragging) {
|
|
@@ -8249,7 +8708,6 @@ class MowerMapOverlay {
|
|
|
8249
8708
|
this.div.style.transform = transform;
|
|
8250
8709
|
// 更新鼠标起始位置为当前位置,为下次计算做准备
|
|
8251
8710
|
this.startPos = { x: mouseCurrentX, y: mouseCurrentY };
|
|
8252
|
-
console.log('旋转角度:', this.currentRotation, '角度增量:', angleDifferenceDegrees);
|
|
8253
8711
|
}
|
|
8254
8712
|
// 将像素偏移量转换为地理坐标偏移量
|
|
8255
8713
|
convertPixelOffsetToLatLng() {
|
|
@@ -8279,14 +8737,6 @@ class MowerMapOverlay {
|
|
|
8279
8737
|
// 累积更新地理坐标偏移量(不是直接赋值!)
|
|
8280
8738
|
this.latLngOffset.lat += latOffset;
|
|
8281
8739
|
this.latLngOffset.lng += lngOffset;
|
|
8282
|
-
console.log('精确转换偏移量:', {
|
|
8283
|
-
pixelOffset: this.tempPixelOffset,
|
|
8284
|
-
centerLatLng: { lat: centerLatLng.lat(), lng: centerLatLng.lng() },
|
|
8285
|
-
offsetLatLng: { lat: offsetLatLng.lat(), lng: offsetLatLng.lng() },
|
|
8286
|
-
latOffset,
|
|
8287
|
-
lngOffset,
|
|
8288
|
-
newLatLngOffset: this.latLngOffset,
|
|
8289
|
-
});
|
|
8290
8740
|
// 重置临时像素偏移量
|
|
8291
8741
|
this.tempPixelOffset = { x: 0, y: 0 };
|
|
8292
8742
|
this.draw();
|
|
@@ -8324,8 +8774,6 @@ class MowerMapOverlay {
|
|
|
8324
8774
|
editData: editData,
|
|
8325
8775
|
timestamp: new Date().toISOString(),
|
|
8326
8776
|
};
|
|
8327
|
-
// 在这里可以添加保存逻辑,比如发送到服务器
|
|
8328
|
-
console.log('保存编辑数据:', saveData);
|
|
8329
8777
|
// 显示保存成功提示
|
|
8330
8778
|
this.showSaveSuccess();
|
|
8331
8779
|
return saveData;
|
|
@@ -8418,6 +8866,9 @@ class MowerMapOverlay {
|
|
|
8418
8866
|
y: transform.y,
|
|
8419
8867
|
rotation: transform.rotation,
|
|
8420
8868
|
};
|
|
8869
|
+
// defaultTransform的x对应经度偏移量,y对应纬度偏移量
|
|
8870
|
+
this.latLngOffset.lng = this.defaultTransform.x;
|
|
8871
|
+
this.latLngOffset.lat = this.defaultTransform.y;
|
|
8421
8872
|
this.setManagerRotation(this.currentRotation);
|
|
8422
8873
|
this.draw();
|
|
8423
8874
|
}
|
|
@@ -8457,7 +8908,6 @@ class MowerMapOverlay {
|
|
|
8457
8908
|
if (this.pathData && this.svgMapView) {
|
|
8458
8909
|
this.loadPathData(this.pathData, this.mowPartitionData);
|
|
8459
8910
|
}
|
|
8460
|
-
console.log('initializeSvgMapView');
|
|
8461
8911
|
// 刷新绘制图层
|
|
8462
8912
|
this.svgMapView.refresh();
|
|
8463
8913
|
// 获取生成的SVG并添加到叠加层div中
|
|
@@ -8613,7 +9063,6 @@ class MowerMapOverlay {
|
|
|
8613
9063
|
this.boundaryLabelsManager?.updateBoundaryData(boundaryData);
|
|
8614
9064
|
}
|
|
8615
9065
|
draw() {
|
|
8616
|
-
console.log('ondraw');
|
|
8617
9066
|
// 防御性检查:如果this.div为null,说明onAdd还没被调用,直接返回
|
|
8618
9067
|
if (!this.div) {
|
|
8619
9068
|
return;
|
|
@@ -8881,7 +9330,7 @@ const getValidGpsBounds = (mapData, rotation = 0) => {
|
|
|
8881
9330
|
// 默认配置
|
|
8882
9331
|
const defaultMapConfig = DEFAULT_STYLES;
|
|
8883
9332
|
// 地图渲染器组件
|
|
8884
|
-
const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJson, pathJson, realTimeData, antennaConfig, onMapLoad, onPathLoad, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, defaultTransform, debug = false, }, ref) => {
|
|
9333
|
+
const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType.Imperial, language = 'en', mapConfig, modelType, mapRef, mapJson, pathJson, realTimeData, antennaConfig, onMapLoad, onPathLoad, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, defaultTransform, debug = false, }, ref) => {
|
|
8885
9334
|
const [elementCount, setElementCount] = React.useState(0);
|
|
8886
9335
|
const [pathCount, setPathCount] = React.useState(0);
|
|
8887
9336
|
const [currentError, setCurrentError] = React.useState(null);
|
|
@@ -8889,7 +9338,7 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
8889
9338
|
// const mapRef = useMap();
|
|
8890
9339
|
const [isGoogleMapsReady, setIsGoogleMapsReady] = React.useState(false);
|
|
8891
9340
|
const [hasInitializedBounds, setHasInitializedBounds] = React.useState(false);
|
|
8892
|
-
const { clearSubBoundaryBorder, clearObstacles } = useSubBoundaryBorderStore();
|
|
9341
|
+
const { clearSubBoundaryBorder, clearObstacles, clearSvgElements } = useSubBoundaryBorderStore();
|
|
8893
9342
|
const currentProcessMowingStatusRef = React.useRef(false);
|
|
8894
9343
|
const { updateProcessStateIsMowing, processStateIsMowing } = useProcessMowingState();
|
|
8895
9344
|
const [mowPartitionData, setMowPartitionData] = React.useState(null);
|
|
@@ -8923,7 +9372,7 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
8923
9372
|
postureX: 0,
|
|
8924
9373
|
postureY: 0,
|
|
8925
9374
|
postureTheta: 0,
|
|
8926
|
-
vehicleState: RobotStatus.
|
|
9375
|
+
vehicleState: RobotStatus.DISCONNECTED,
|
|
8927
9376
|
};
|
|
8928
9377
|
let currentPositionData;
|
|
8929
9378
|
if (realTimeData.length === 1 && realTimeData[0].type === RealTimeDataType.LOCATION) {
|
|
@@ -8949,10 +9398,9 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
8949
9398
|
lastPostureY: currentPositionData?.lastPostureY
|
|
8950
9399
|
? Number(currentPositionData.lastPostureY)
|
|
8951
9400
|
: 0,
|
|
8952
|
-
vehicleState: currentPositionData?.vehicleState || RobotStatus.
|
|
9401
|
+
vehicleState: currentPositionData?.vehicleState || RobotStatus.DISCONNECTED,
|
|
8953
9402
|
};
|
|
8954
9403
|
}, [realTimeData, modelType]);
|
|
8955
|
-
console.log('mowerPositionData', mowerPositionData);
|
|
8956
9404
|
// 处理错误
|
|
8957
9405
|
const handleError = (error) => {
|
|
8958
9406
|
setCurrentError(error);
|
|
@@ -8977,7 +9425,6 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
8977
9425
|
const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
|
|
8978
9426
|
new window.google.maps.LatLng(neLat, neLng) // 东北角
|
|
8979
9427
|
);
|
|
8980
|
-
console.log('fitBounds----->', googleBounds);
|
|
8981
9428
|
mapRef.fitBounds(googleBounds);
|
|
8982
9429
|
}, [mapJson, mapRef, defaultTransform]);
|
|
8983
9430
|
// 初始化Google Maps叠加层
|
|
@@ -9018,9 +9465,8 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9018
9465
|
overlayRef.current.setMap(null);
|
|
9019
9466
|
overlayRef.current = null;
|
|
9020
9467
|
}
|
|
9021
|
-
console.log('initializeGoogleMapsOverlay', mowPartitionData);
|
|
9022
9468
|
// 创建叠加层
|
|
9023
|
-
const overlay = new MowerMapOverlay(googleBounds, mapJson, partitionBoundary, mowerPositionData, modelType, pathJson || {}, isEditMode, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
|
|
9469
|
+
const overlay = new MowerMapOverlay(googleBounds, mapJson, partitionBoundary, mowerPositionData, modelType, pathJson || {}, isEditMode, unitType, language, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
|
|
9024
9470
|
setElementCount(count);
|
|
9025
9471
|
onMapLoad?.(count);
|
|
9026
9472
|
}, (count) => {
|
|
@@ -9030,6 +9476,7 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9030
9476
|
// 设置地图
|
|
9031
9477
|
overlay.setMap(mapInstance);
|
|
9032
9478
|
overlayRef.current = overlay;
|
|
9479
|
+
overlay.setEdger(edger);
|
|
9033
9480
|
// 只在首次初始化时自适应视图
|
|
9034
9481
|
if (!hasInitializedBounds) {
|
|
9035
9482
|
mapInstance.fitBounds(googleBounds);
|
|
@@ -9053,7 +9500,7 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9053
9500
|
postureY: chargingPiles?.originalData.position[1],
|
|
9054
9501
|
postureTheta: chargingPiles?.originalData.direction - Math.PI || 0,
|
|
9055
9502
|
}, 0);
|
|
9056
|
-
}, [mapJson]);
|
|
9503
|
+
}, [mapJson, mowerPositionData]);
|
|
9057
9504
|
// 初始化效果
|
|
9058
9505
|
React.useEffect(() => {
|
|
9059
9506
|
initializeGoogleMapsOverlay();
|
|
@@ -9061,6 +9508,7 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9061
9508
|
return () => {
|
|
9062
9509
|
clearSubBoundaryBorder();
|
|
9063
9510
|
clearObstacles();
|
|
9511
|
+
clearSvgElements();
|
|
9064
9512
|
updateProcessStateIsMowing(false);
|
|
9065
9513
|
currentProcessMowingStatusRef.current = false;
|
|
9066
9514
|
if (overlayRef.current) {
|
|
@@ -9092,7 +9540,6 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9092
9540
|
const isOffLine = mowerPositionData.vehicleState === RobotStatus.DISCONNECTED;
|
|
9093
9541
|
const isInChargingPile = inChargingPiles.includes(mowerPositionData.vehicleState);
|
|
9094
9542
|
// 如果在充电桩上,则直接更新位置到充电桩的位置
|
|
9095
|
-
console.log('usefeect mowerPositionData----->', mowerPositionData);
|
|
9096
9543
|
if (isInChargingPile) {
|
|
9097
9544
|
overlayRef.current.updatePosition({
|
|
9098
9545
|
...mowerPositionData,
|
|
@@ -9125,7 +9572,6 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9125
9572
|
}
|
|
9126
9573
|
}
|
|
9127
9574
|
else {
|
|
9128
|
-
console.log('hook updatePosition----->', mowerPositionData);
|
|
9129
9575
|
overlayRef.current.updatePosition(mowerPositionData, isStandby ? 0 : 2000);
|
|
9130
9576
|
}
|
|
9131
9577
|
}
|
|
@@ -9244,7 +9690,6 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9244
9690
|
if (!realTimeData || realTimeData.length === 0 || !Array.isArray(realTimeData)) {
|
|
9245
9691
|
return;
|
|
9246
9692
|
}
|
|
9247
|
-
console.log('usefeect realTimeData----->', realTimeData, mapJson, pathJson, overlayRef.current);
|
|
9248
9693
|
let curMowPartitionData = mowPartitionData;
|
|
9249
9694
|
// realtime中包含当前割草任务的数据,根据数据进行path路径和边界的高亮操作,
|
|
9250
9695
|
const mowingPartition = realTimeData.find((item) => item.type === RealTimeDataType.PARTITION);
|
|
@@ -9252,9 +9697,8 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9252
9697
|
setMowPartitionData(mowingPartition);
|
|
9253
9698
|
curMowPartitionData = mowingPartition;
|
|
9254
9699
|
}
|
|
9255
|
-
const positionData = realTimeData?.find(item => item?.type === RealTimeDataType.LOCATION);
|
|
9256
|
-
const statusData = realTimeData?.find(item => item?.type === RealTimeDataType.STATUS);
|
|
9257
|
-
console.log('current->1', positionData, statusData);
|
|
9700
|
+
const positionData = realTimeData?.find((item) => item?.type === RealTimeDataType.LOCATION);
|
|
9701
|
+
const statusData = realTimeData?.find((item) => item?.type === RealTimeDataType.STATUS);
|
|
9258
9702
|
if (statusData || positionData) {
|
|
9259
9703
|
const currentStatus = statusData?.vehicleState || positionData?.vehicleState;
|
|
9260
9704
|
// 车辆回桩不会回传最后的park的位置,所以根据实时数据的状态数据判断车辆回到桩上
|
|
@@ -9264,32 +9708,28 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9264
9708
|
else if (currentStatus === RobotStatus.WORKING) {
|
|
9265
9709
|
// 兜底收不到割草地块的实时数据,使用状态来兜底
|
|
9266
9710
|
overlayRef.current.resetBorderLayerHighlight();
|
|
9267
|
-
setMowPartitionData(
|
|
9268
|
-
curMowPartitionData =
|
|
9711
|
+
setMowPartitionData({});
|
|
9712
|
+
curMowPartitionData = {};
|
|
9269
9713
|
}
|
|
9270
|
-
else if (currentStatus === RobotStatus.MOWING &&
|
|
9714
|
+
else if (currentStatus === RobotStatus.MOWING &&
|
|
9715
|
+
curMowPartitionData &&
|
|
9716
|
+
!curMowPartitionData?.partitionIds) {
|
|
9271
9717
|
// 如果当前是割草状态,但是地块数据初始化过且不存在则认为是全局割草,则把所有地块都高亮
|
|
9272
|
-
const allPartitionIds = mapJson?.sub_maps?.map(item => item?.id);
|
|
9273
|
-
console.log('allPartitionIds->', allPartitionIds, mapJson);
|
|
9718
|
+
const allPartitionIds = mapJson?.sub_maps?.map((item) => item?.id);
|
|
9274
9719
|
setMowPartitionData({
|
|
9275
|
-
partitionIds: allPartitionIds
|
|
9720
|
+
partitionIds: allPartitionIds,
|
|
9276
9721
|
});
|
|
9277
9722
|
curMowPartitionData = {
|
|
9278
|
-
partitionIds: allPartitionIds
|
|
9723
|
+
partitionIds: allPartitionIds,
|
|
9279
9724
|
};
|
|
9280
9725
|
}
|
|
9281
9726
|
}
|
|
9282
|
-
if (!mapJson ||
|
|
9283
|
-
!pathJson ||
|
|
9284
|
-
!overlayRef.current)
|
|
9727
|
+
if (!mapJson || !pathJson || !overlayRef.current)
|
|
9285
9728
|
return;
|
|
9286
9729
|
// 根据后端推送的实时数据,进行不同处理
|
|
9287
|
-
// TODO:需要根据返回的数据,处理车辆的移动位置
|
|
9288
|
-
console.log('realTimeData----->', realTimeData, curMowPartitionData);
|
|
9289
9730
|
if (curMowPartitionData) {
|
|
9290
9731
|
const isMowing = curMowPartitionData?.partitionIds && curMowPartitionData.partitionIds.length > 0;
|
|
9291
9732
|
overlayRef.current.updateMowPartitionData(curMowPartitionData);
|
|
9292
|
-
console.log('isMowing', isMowing, curMowPartitionData);
|
|
9293
9733
|
if (!isMowing) {
|
|
9294
9734
|
overlayRef.current.resetBorderLayerHighlight();
|
|
9295
9735
|
}
|
|
@@ -9327,7 +9767,6 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9327
9767
|
}
|
|
9328
9768
|
}, [realTimeData, mapJson, pathJson]);
|
|
9329
9769
|
React.useEffect(() => {
|
|
9330
|
-
console.log('defaultTransform----->', defaultTransform, overlayRef.current, mapJson);
|
|
9331
9770
|
if (!overlayRef.current || !defaultTransform)
|
|
9332
9771
|
return;
|
|
9333
9772
|
overlayRef.current?.setTransform(defaultTransform);
|
|
@@ -9342,6 +9781,11 @@ const MowerMapRenderer = React.forwardRef(({ mapConfig, modelType, mapRef, mapJs
|
|
|
9342
9781
|
);
|
|
9343
9782
|
mapRef.fitBounds(googleBounds);
|
|
9344
9783
|
}, [defaultTransform]);
|
|
9784
|
+
React.useEffect(() => {
|
|
9785
|
+
if (!overlayRef || !overlayRef.current)
|
|
9786
|
+
return;
|
|
9787
|
+
overlayRef.current.setEdger(edger);
|
|
9788
|
+
}, [edger]);
|
|
9345
9789
|
// 提供ref方法
|
|
9346
9790
|
React.useImperativeHandle(ref, () => ({
|
|
9347
9791
|
fitToView: () => {
|