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