@fleet-frontend/mower-maps 0.0.8 → 0.0.9-beta.10
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 +2 -25
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/styles.d.ts +3 -2
- package/dist/config/styles.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +1548 -1075
- package/dist/index.js +1546 -1073
- package/dist/processor/MapDataProcessor.d.ts +1 -1
- package/dist/processor/MapDataProcessor.d.ts.map +1 -1
- package/dist/processor/PathDataProcessor.d.ts +3 -18
- package/dist/processor/PathDataProcessor.d.ts.map +1 -1
- package/dist/processor/builder/AntennaDataBuilder.d.ts +2 -1
- package/dist/processor/builder/AntennaDataBuilder.d.ts.map +1 -1
- package/dist/processor/builder/BoundaryDataBuilder.d.ts +1 -1
- package/dist/processor/builder/BoundaryDataBuilder.d.ts.map +1 -1
- package/dist/processor/builder/ChannelDataBuilder.d.ts +1 -1
- package/dist/processor/builder/ChannelDataBuilder.d.ts.map +1 -1
- package/dist/processor/builder/ChargingPileDataBuilder.d.ts +1 -1
- package/dist/processor/builder/ChargingPileDataBuilder.d.ts.map +1 -1
- package/dist/processor/builder/ObstacleDataBuilder.d.ts +2 -1
- package/dist/processor/builder/ObstacleDataBuilder.d.ts.map +1 -1
- package/dist/processor/builder/PathDataBuilder.d.ts +6 -5
- package/dist/processor/builder/PathDataBuilder.d.ts.map +1 -1
- package/dist/processor/builder/PointDataBuilder.d.ts +2 -1
- package/dist/processor/builder/PointDataBuilder.d.ts.map +1 -1
- package/dist/processor/builder/SvgElementDataBuilder.d.ts +1 -1
- package/dist/processor/builder/SvgElementDataBuilder.d.ts.map +1 -1
- package/dist/processor/builder/VisionOffDataBuilder.d.ts +2 -1
- package/dist/processor/builder/VisionOffDataBuilder.d.ts.map +1 -1
- package/dist/processor/index.d.ts +2 -1
- package/dist/processor/index.d.ts.map +1 -1
- package/dist/render/AntennaManager.d.ts +7 -24
- package/dist/render/AntennaManager.d.ts.map +1 -1
- package/dist/render/BoundaryLabelsManager.d.ts +12 -20
- package/dist/render/BoundaryLabelsManager.d.ts.map +1 -1
- package/dist/render/ChargingPileManager.d.ts +10 -20
- package/dist/render/ChargingPileManager.d.ts.map +1 -1
- package/dist/render/MowerMapOverlay.d.ts +51 -8
- package/dist/render/MowerMapOverlay.d.ts.map +1 -1
- package/dist/render/MowerMapRenderer.d.ts.map +1 -1
- package/dist/render/{MowerPostionManager.d.ts → MowerPositionManager.d.ts} +14 -11
- package/dist/render/MowerPositionManager.d.ts.map +1 -0
- package/dist/render/SvgMapView.d.ts +15 -26
- package/dist/render/SvgMapView.d.ts.map +1 -1
- package/dist/render/layers/BoundaryBorderLayer.d.ts +7 -0
- package/dist/render/layers/BoundaryBorderLayer.d.ts.map +1 -1
- package/dist/render/layers/ChannelLayer.d.ts.map +1 -1
- package/dist/render/layers/DrawLayer.d.ts +1 -1
- package/dist/render/layers/DrawLayer.d.ts.map +1 -1
- package/dist/render/layers/PathLayer.d.ts +1 -0
- package/dist/render/layers/PathLayer.d.ts.map +1 -1
- package/dist/render/layers/SvgElementLayer.d.ts.map +1 -1
- package/dist/render/layers/index.d.ts +0 -1
- package/dist/render/layers/index.d.ts.map +1 -1
- package/dist/render/layers/types.d.ts +1 -36
- package/dist/render/layers/types.d.ts.map +1 -1
- package/dist/store/processMowingState.d.ts +4 -0
- package/dist/store/processMowingState.d.ts.map +1 -0
- package/dist/store/useSubBoundaryBorderStore.d.ts +9 -5
- package/dist/store/useSubBoundaryBorderStore.d.ts.map +1 -1
- package/dist/types/constants.d.ts +38 -0
- package/dist/types/constants.d.ts.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/layers.d.ts +50 -0
- package/dist/types/layers.d.ts.map +1 -0
- package/dist/types/processor.d.ts +26 -0
- package/dist/types/processor.d.ts.map +1 -0
- package/dist/types/realTime.d.ts +27 -2
- package/dist/types/realTime.d.ts.map +1 -1
- package/dist/types/renderer.d.ts +27 -10
- package/dist/types/renderer.d.ts.map +1 -1
- package/dist/types/store.d.ts +22 -0
- package/dist/types/store.d.ts.map +1 -0
- package/dist/types/utils.d.ts +102 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/utils/boundaryUtils.d.ts +1 -29
- package/dist/utils/boundaryUtils.d.ts.map +1 -1
- package/dist/utils/common.d.ts +20 -0
- package/dist/utils/common.d.ts.map +1 -0
- package/dist/utils/coordinates.d.ts +1 -7
- package/dist/utils/coordinates.d.ts.map +1 -1
- package/dist/utils/handleRealTime.d.ts +20 -10
- package/dist/utils/handleRealTime.d.ts.map +1 -1
- package/dist/utils/mapBounds.d.ts +1 -10
- package/dist/utils/mapBounds.d.ts.map +1 -1
- package/dist/utils/math.d.ts +3 -10
- package/dist/utils/math.d.ts.map +1 -1
- package/dist/utils/mower.d.ts +4 -4
- package/dist/utils/mower.d.ts.map +1 -1
- package/dist/utils/pathSegments.d.ts +1 -15
- package/dist/utils/pathSegments.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/render/MowerPostionManager.d.ts.map +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,134 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import React, { forwardRef,
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import React, { forwardRef, useState, useRef, useMemo, useCallback, useEffect, useImperativeHandle } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 常量和枚举类型定义
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 机器人状态枚举
|
|
9
|
+
*/
|
|
10
|
+
var RobotStatus;
|
|
11
|
+
(function (RobotStatus) {
|
|
12
|
+
RobotStatus[RobotStatus["PARKED"] = 1] = "PARKED";
|
|
13
|
+
RobotStatus[RobotStatus["CHARGING"] = 2] = "CHARGING";
|
|
14
|
+
RobotStatus[RobotStatus["STANDBY"] = 3] = "STANDBY";
|
|
15
|
+
RobotStatus[RobotStatus["MOWING"] = 4] = "MOWING";
|
|
16
|
+
RobotStatus[RobotStatus["WORKING"] = 5] = "WORKING";
|
|
17
|
+
RobotStatus[RobotStatus["MAPPING"] = 6] = "MAPPING";
|
|
18
|
+
RobotStatus[RobotStatus["ERROR"] = 7] = "ERROR";
|
|
19
|
+
RobotStatus[RobotStatus["UPGRADING"] = 8] = "UPGRADING";
|
|
20
|
+
RobotStatus[RobotStatus["DISCONNECTED"] = 9] = "DISCONNECTED";
|
|
21
|
+
RobotStatus[RobotStatus["UNKNOWN"] = -1] = "UNKNOWN";
|
|
22
|
+
RobotStatus[RobotStatus["TASK_DELAY"] = 10] = "TASK_DELAY";
|
|
23
|
+
})(RobotStatus || (RobotStatus = {}));
|
|
24
|
+
/**
|
|
25
|
+
* RTK状态枚举
|
|
26
|
+
*/
|
|
27
|
+
var RTK_STATE;
|
|
28
|
+
(function (RTK_STATE) {
|
|
29
|
+
RTK_STATE[RTK_STATE["LOW_RTK"] = 1] = "LOW_RTK";
|
|
30
|
+
RTK_STATE[RTK_STATE["MIDDLE_RTK"] = 2] = "MIDDLE_RTK";
|
|
31
|
+
RTK_STATE[RTK_STATE["HIGH_RTK"] = 3] = "HIGH_RTK";
|
|
32
|
+
RTK_STATE[RTK_STATE["NO_POSTURE"] = 10] = "NO_POSTURE";
|
|
33
|
+
RTK_STATE[RTK_STATE["OUT_OF_RANGE"] = 11] = "OUT_OF_RANGE";
|
|
34
|
+
RTK_STATE[RTK_STATE["OFF_LINE"] = 19] = "OFF_LINE";
|
|
35
|
+
})(RTK_STATE || (RTK_STATE = {}));
|
|
36
|
+
/**
|
|
37
|
+
* 实时数据类型枚举
|
|
38
|
+
*/
|
|
39
|
+
var REAL_TIME_DATA_TYPE;
|
|
40
|
+
(function (REAL_TIME_DATA_TYPE) {
|
|
41
|
+
REAL_TIME_DATA_TYPE[REAL_TIME_DATA_TYPE["LOCATION"] = 1] = "LOCATION";
|
|
42
|
+
REAL_TIME_DATA_TYPE[REAL_TIME_DATA_TYPE["PROCESS"] = 2] = "PROCESS";
|
|
43
|
+
})(REAL_TIME_DATA_TYPE || (REAL_TIME_DATA_TYPE = {}));
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 地图渲染相关常量配置
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* 缩放因子 - 将米转换为像素
|
|
50
|
+
* 与Python代码中的SVG比例一致
|
|
51
|
+
*/
|
|
52
|
+
const SCALE_FACTOR = 50; // 50像素/米
|
|
53
|
+
/**
|
|
54
|
+
* 默认线宽设置
|
|
55
|
+
*/
|
|
56
|
+
const DEFAULT_LINE_WIDTHS = {
|
|
57
|
+
OBSTACLE: 2,
|
|
58
|
+
CHARGING_PILE: 2,
|
|
59
|
+
CHANNEL: 2,
|
|
60
|
+
PATH: 20,
|
|
61
|
+
VISION_OFF_AREA: 2,
|
|
62
|
+
TIME_LIMIT_OBSTACLE: 1,
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* 默认透明度设置
|
|
66
|
+
*/
|
|
67
|
+
const DEFAULT_OPACITIES = {
|
|
68
|
+
FULL: 1.0,
|
|
69
|
+
HIGH: 0.7,
|
|
70
|
+
MEDIUM: 0.6};
|
|
71
|
+
/**
|
|
72
|
+
* 默认半径设置
|
|
73
|
+
*/
|
|
74
|
+
const DEFAULT_RADII = {
|
|
75
|
+
CHARGING_PILE: 12};
|
|
76
|
+
/**
|
|
77
|
+
* 图层等级
|
|
78
|
+
*/
|
|
79
|
+
const LAYER_LEVELS = {
|
|
80
|
+
BOUNDARY: 2,
|
|
81
|
+
BOUNDARY_BORDER: 4};
|
|
82
|
+
/**
|
|
83
|
+
* 图层默认id
|
|
84
|
+
*/
|
|
85
|
+
const LAYER_DEFAULT_TYPE = {
|
|
86
|
+
CHANNEL: 'channel',
|
|
87
|
+
BOUNDARY: 'boundary',
|
|
88
|
+
PATH: 'path',
|
|
89
|
+
BOUNDARY_BORDER: 'boundary_border',
|
|
90
|
+
OBSTACLE: 'obstacle',
|
|
91
|
+
CHARGING_PILE: 'charging_pile',
|
|
92
|
+
POINT: 'point',
|
|
93
|
+
SVG: 'svg',
|
|
94
|
+
VISION_OFF_AREA: 'vision_off_area',
|
|
95
|
+
ANTENNA: 'antenna',
|
|
96
|
+
};
|
|
97
|
+
const ISOLATED_BOUNDARY_SVG = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
98
|
+
<g opacity="0.6">
|
|
99
|
+
<rect width="24" height="24" rx="12" fill="#1E1E1F" fill-opacity="0.5"/>
|
|
100
|
+
<path d="M8.20573 12.4961C6.739 11.1707 4.85775 11.2284 3.60938 11.1707C6.17744 13.0156 6.05584 15.7887 5.67404 16.9446H14.327C13.837 13.1673 15.5321 10.1289 16.4408 9.08187C15.0342 9.46118 13.5794 10.8303 13.0278 11.4674C12.8102 8.94572 13.992 5.97489 14.61 4.80469C12.5235 5.92214 11.0501 8.26056 10.4938 9.56261C9.22803 7.5947 6.83894 7.17806 5.883 6.9432C8.20573 10.2373 8.00039 11.1707 8.20573 12.4961Z" fill="white"/>
|
|
101
|
+
<mask id="path-3-outside-1_9822_43516" maskUnits="userSpaceOnUse" x="13.2344" y="7.20545" width="12.0208" height="12.0208" fill="black">
|
|
102
|
+
<rect fill="white" x="13.2344" y="7.20545" width="12.0208" height="12.0208"/>
|
|
103
|
+
<path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z"/>
|
|
104
|
+
</mask>
|
|
105
|
+
<path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z" fill="white"/>
|
|
106
|
+
<path d="M20.6852 11.1208L21.7458 10.0601L21.7459 10.0601L20.6852 11.1208ZM17.8568 11.1208L16.7961 10.0601L16.7962 10.0601L17.8568 11.1208ZM17.2733 11.7043L16.2126 10.6437L16.2126 10.6436L17.2733 11.7043ZM16.8631 13.9319L17.9238 14.9926L16.381 16.5354L15.493 14.5425L16.8631 13.9319ZM17.5916 13.2034L19.0855 13.0678L19.149 13.7674L18.6523 14.2641L17.5916 13.2034ZM20.4056 12.1421L21.4662 11.0814L21.4665 11.0817L20.4056 12.1421ZM18.9699 14.5348L17.9093 13.4741L18.3741 13.0093L19.0309 13.036L18.9699 14.5348ZM18.2235 15.2812L17.6298 16.6587L15.5997 15.7837L17.1628 14.2206L18.2235 15.2812ZM20.429 14.86L21.4897 15.9207L21.4897 15.9207L20.429 14.86ZM21.0125 14.2765L22.0732 15.3371L22.0732 15.3372L21.0125 14.2765ZM20.6852 11.1208L19.6246 12.1815C19.4294 11.9862 19.1127 11.9863 18.9174 12.1815L17.8568 11.1208L16.7962 10.0601C18.163 8.69337 20.379 8.6934 21.7458 10.0601L20.6852 11.1208ZM17.8568 11.1208L18.9175 12.1814L18.334 12.7649L17.2733 11.7043L16.2126 10.6436L16.7961 10.0601L17.8568 11.1208ZM17.2733 11.7043L18.334 12.7649C18.1877 12.9112 18.1488 13.1318 18.2333 13.3214L16.8631 13.9319L15.493 14.5425C14.9228 13.2629 15.1537 11.7026 16.2126 10.6437L17.2733 11.7043ZM16.8631 13.9319L15.8025 12.8713L16.531 12.1428L17.5916 13.2034L18.6523 14.2641L17.9238 14.9926L16.8631 13.9319ZM17.5916 13.2034L16.0978 13.339C16.0334 12.6302 16.2727 11.8914 16.8196 11.3445L17.8803 12.4052L18.9409 13.4658C19.051 13.3558 19.098 13.206 19.0855 13.0678L17.5916 13.2034ZM17.8803 12.4052L16.8196 11.3445L17.5067 10.6574L18.5674 11.7181L19.628 12.7788L18.9409 13.4658L17.8803 12.4052ZM18.5674 11.7181L17.5067 10.6574C18.483 9.68112 20.0659 9.68112 21.0422 10.6574L19.9816 11.7181L18.9209 12.7788C19.1162 12.974 19.4328 12.974 19.628 12.7788L18.5674 11.7181ZM19.9816 11.7181L21.0422 10.6574L21.4662 11.0814L20.4056 12.1421L19.3449 13.2027L18.9209 12.7788L19.9816 11.7181ZM20.4056 12.1421L21.4665 11.0817C22.4419 12.0577 22.4428 13.6404 21.4662 14.617L20.4056 13.5563L19.3449 12.4956C19.1493 12.6913 19.1498 13.0076 19.3446 13.2024L20.4056 12.1421ZM20.4056 13.5563L21.4662 14.617L20.7791 15.304L19.7185 14.2434L18.6578 13.1827L19.3449 12.4956L20.4056 13.5563ZM19.7185 14.2434L20.7791 15.304C20.2628 15.8204 19.5762 16.0607 18.909 16.0335L18.9699 14.5348L19.0309 13.036C18.9025 13.0308 18.7629 13.0777 18.6578 13.1827L19.7185 14.2434ZM18.9699 14.5348L20.0306 15.5954L19.2841 16.3419L18.2235 15.2812L17.1628 14.2206L17.9093 13.4741L18.9699 14.5348ZM18.2235 15.2812L18.8172 13.9037C19.0042 13.9843 19.2223 13.9455 19.3684 13.7993L20.429 14.86L21.4897 15.9207C20.4427 16.9676 18.9034 17.2077 17.6298 16.6587L18.2235 15.2812ZM20.429 14.86L19.3684 13.7994L19.9519 13.2159L21.0125 14.2765L22.0732 15.3372L21.4897 15.9207L20.429 14.86ZM21.0125 14.2765L19.9518 13.2159C20.1471 13.0206 20.1471 12.704 19.9519 12.5088L21.0125 11.4481L22.0732 10.3874C23.4401 11.7543 23.4399 13.9703 22.0732 15.3371L21.0125 14.2765ZM21.0125 11.4481L19.9519 12.5088L19.6246 12.1814L20.6852 11.1208L21.7459 10.0601L22.0732 10.3874L21.0125 11.4481Z" fill="#8E8E8F" mask="url(#path-3-outside-1_9822_43516)"/>
|
|
107
|
+
<mask id="path-5-outside-2_9822_43516" maskUnits="userSpaceOnUse" x="9.22265" y="10.388" width="12.7279" height="12.7279" fill="black">
|
|
108
|
+
<rect fill="white" x="9.22265" y="10.388" width="12.7279" height="12.7279"/>
|
|
109
|
+
<path d="M16.1708 14.5077C15.4353 14.1778 14.5422 14.3133 13.9383 14.9172L13.3548 15.5007C12.574 16.2818 12.5738 17.5481 13.3548 18.3291L13.6821 18.6564C14.4631 19.4374 15.7295 19.4373 16.5105 18.6564L17.094 18.0729C17.6895 17.4775 17.8299 16.6003 17.5173 15.8708L16.7743 16.6138C16.7828 16.8803 16.6863 17.1494 16.4829 17.3527L15.7952 18.0405C15.4046 18.4309 14.7714 18.431 14.3809 18.0405L13.957 17.6165C13.5667 17.226 13.5666 16.5927 13.957 16.2023L14.6447 15.5145C14.8654 15.2939 15.1639 15.1979 15.452 15.2266L16.1708 14.5077Z"/>
|
|
110
|
+
</mask>
|
|
111
|
+
<path d="M16.1708 14.5077C15.4353 14.1778 14.5422 14.3133 13.9383 14.9172L13.3548 15.5007C12.574 16.2818 12.5738 17.5481 13.3548 18.3291L13.6821 18.6564C14.4631 19.4374 15.7295 19.4373 16.5105 18.6564L17.094 18.0729C17.6895 17.4775 17.8299 16.6003 17.5173 15.8708L16.7743 16.6138C16.7828 16.8803 16.6863 17.1494 16.4829 17.3527L15.7952 18.0405C15.4046 18.4309 14.7714 18.431 14.3809 18.0405L13.957 17.6165C13.5667 17.226 13.5666 16.5927 13.957 16.2023L14.6447 15.5145C14.8654 15.2939 15.1639 15.1979 15.452 15.2266L16.1708 14.5077Z" fill="white"/>
|
|
112
|
+
<path d="M16.1708 14.5077L16.7847 13.1391L18.7701 14.0297L17.2315 15.5684L16.1708 14.5077ZM13.3548 15.5007L12.294 14.4402L12.2941 14.44L13.3548 15.5007ZM16.5105 18.6564L17.5712 19.7171L17.5711 19.7172L16.5105 18.6564ZM17.5173 15.8708L16.4567 14.8102L18.0236 13.2433L18.8962 15.2802L17.5173 15.8708ZM16.7743 16.6138L15.2751 16.6613L15.2545 16.0123L15.7137 15.5532L16.7743 16.6138ZM15.7952 18.0405L16.8558 19.1011L16.8556 19.1013L15.7952 18.0405ZM13.957 17.6165L12.8963 18.6772L12.8959 18.6767L13.957 17.6165ZM13.957 16.2023L12.8961 15.1418L12.8963 15.1416L13.957 16.2023ZM15.452 15.2266L16.5126 16.2872L16.0102 16.7896L15.3032 16.7192L15.452 15.2266ZM16.1708 14.5077L15.5569 15.8763C15.3688 15.792 15.1469 15.8299 14.999 15.9779L13.9383 14.9172L12.8776 13.8565C13.9375 12.7967 15.5018 12.5636 16.7847 13.1391L16.1708 14.5077ZM13.9383 14.9172L14.999 15.9779L14.4155 16.5614L13.3548 15.5007L12.2941 14.44L12.8776 13.8565L13.9383 14.9172ZM13.3548 15.5007L14.4156 16.5612C14.2202 16.7567 14.2204 17.0734 14.4155 17.2685L13.3548 18.3291L12.2941 19.3898C10.9272 18.0229 10.9277 15.8069 12.294 14.4402L13.3548 15.5007ZM13.3548 18.3291L14.4155 17.2685L14.7428 17.5958L13.6821 18.6564L12.6215 19.7171L12.2941 19.3898L13.3548 18.3291ZM13.6821 18.6564L14.7428 17.5958C14.9378 17.7908 15.2546 17.791 15.45 17.5956L16.5105 18.6564L17.5711 19.7172C16.2044 21.0836 13.9884 21.084 12.6215 19.7171L13.6821 18.6564ZM16.5105 18.6564L15.4499 17.5958L16.0334 17.0123L17.094 18.0729L18.1547 19.1336L17.5712 19.7171L16.5105 18.6564ZM17.094 18.0729L16.0334 17.0123C16.1795 16.8662 16.2185 16.6481 16.1385 16.4615L17.5173 15.8708L18.8962 15.2802C19.4413 16.5526 19.1996 18.0887 18.1547 19.1336L17.094 18.0729ZM17.5173 15.8708L18.578 16.9315L17.835 17.6745L16.7743 16.6138L15.7137 15.5532L16.4567 14.8102L17.5173 15.8708ZM16.7743 16.6138L18.2736 16.5664C18.2945 17.226 18.0544 17.9026 17.5436 18.4134L16.4829 17.3527L15.4223 16.2921C15.3182 16.3961 15.2711 16.5346 15.2751 16.6613L16.7743 16.6138ZM16.4829 17.3527L17.5436 18.4134L16.8558 19.1011L15.7952 18.0405L14.7345 16.9798L15.4223 16.2921L16.4829 17.3527ZM15.7952 18.0405L16.8556 19.1013C15.8795 20.0771 14.2967 20.0776 13.3203 19.1011L14.3809 18.0405L15.4416 16.9798C15.2461 16.7844 14.9297 16.7847 14.7347 16.9797L15.7952 18.0405ZM14.3809 18.0405L13.3203 19.1011L12.8963 18.6772L13.957 17.6165L15.0176 16.5558L15.4416 16.9798L14.3809 18.0405ZM13.957 17.6165L12.8959 18.6767C11.9207 17.7008 11.92 16.1182 12.8961 15.1418L13.957 16.2023L15.0178 17.2628C15.2133 17.0672 15.2128 16.7512 15.018 16.5563L13.957 17.6165ZM13.957 16.2023L12.8963 15.1416L13.5841 14.4539L14.6447 15.5145L15.7054 16.5752L15.0176 17.2629L13.957 16.2023ZM14.6447 15.5145L13.5841 14.4539C14.1364 13.9015 14.8848 13.6626 15.6007 13.734L15.452 15.2266L15.3032 16.7192C15.443 16.7331 15.5943 16.6862 15.7054 16.5752L14.6447 15.5145ZM15.452 15.2266L14.3913 14.1659L15.1101 13.4471L16.1708 14.5077L17.2315 15.5684L16.5126 16.2872L15.452 15.2266Z" fill="#8E8E8F" mask="url(#path-5-outside-2_9822_43516)"/>
|
|
113
|
+
<path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z" fill="white"/>
|
|
114
|
+
<rect width="2.59942" height="2.97264" rx="1" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 21.1133 12.8486)" fill="#8E8E8F"/>
|
|
115
|
+
<rect width="1.91578" height="4.24403" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 19.6992 13.8066)" fill="#8E8E8F"/>
|
|
116
|
+
<rect width="2.59942" height="2.97264" rx="1" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 17.3125 16.6455)" fill="#8E8E8F"/>
|
|
117
|
+
</g>
|
|
118
|
+
</svg>`;
|
|
119
|
+
/**
|
|
120
|
+
* 遍历割草任务,下述四个字段可以在路径中唯一确定遍历的位置(当前区域、当前块、当前行、在当前行上的路程)
|
|
121
|
+
*/
|
|
122
|
+
const ACTION_BLOCK_COVER = 5;
|
|
123
|
+
/**
|
|
124
|
+
* 遍历割草块转移任务(在同一区域中的一个块转移到下一个块),下述四个字段可以在路径中唯一确定转移的位置(当前区域、前置块、当前转移路径线序号、在当前线上的路程)
|
|
125
|
+
*/
|
|
126
|
+
const ACTION_BLOCK_TRANSFER = 6;
|
|
127
|
+
/**
|
|
128
|
+
* 边界割草任务(割草任务内部的巡边任务)
|
|
129
|
+
*/
|
|
130
|
+
const ACTION_BOUNDARY_TASK = 8;
|
|
131
|
+
const SVG_MAP_VIEW_ID = 'fleet-maps-svg-map-view';
|
|
3
132
|
|
|
4
133
|
/**
|
|
5
134
|
* SVG基础MapView
|
|
@@ -14,7 +143,6 @@ class SvgMapView {
|
|
|
14
143
|
this.lineScale = 1; // 线条缩放系数
|
|
15
144
|
// 状态标志
|
|
16
145
|
this.destroyed = false;
|
|
17
|
-
this.showScale = false;
|
|
18
146
|
// 渲染系统 - 移除节流以确保用户操作的实时响应
|
|
19
147
|
// 拖动功能
|
|
20
148
|
this.isDragging = false;
|
|
@@ -57,6 +185,7 @@ class SvgMapView {
|
|
|
57
185
|
this.svg.setAttribute('shape-rendering', 'geometricPrecision');
|
|
58
186
|
this.svg.setAttribute('text-rendering', 'geometricPrecision');
|
|
59
187
|
this.svg.setAttribute('image-rendering', 'optimizeQuality');
|
|
188
|
+
this.svg.setAttribute('id', SVG_MAP_VIEW_ID);
|
|
60
189
|
}
|
|
61
190
|
/**
|
|
62
191
|
* 创建SVG组元素
|
|
@@ -79,7 +208,9 @@ class SvgMapView {
|
|
|
79
208
|
return;
|
|
80
209
|
this.layers.push(...layers);
|
|
81
210
|
this.layers.sort((a, b) => a.getLevel() - b.getLevel());
|
|
82
|
-
|
|
211
|
+
}
|
|
212
|
+
getLayer(type) {
|
|
213
|
+
return this.layers.find((layer) => layer.getType() === type) || null;
|
|
83
214
|
}
|
|
84
215
|
/**
|
|
85
216
|
* 添加图层
|
|
@@ -89,14 +220,12 @@ class SvgMapView {
|
|
|
89
220
|
return;
|
|
90
221
|
this.layers.push(layer);
|
|
91
222
|
this.layers.sort((a, b) => a.getLevel() - b.getLevel());
|
|
92
|
-
this.refresh();
|
|
93
223
|
}
|
|
94
224
|
/**
|
|
95
225
|
* 移除图层
|
|
96
226
|
*/
|
|
97
227
|
removeLayer(layer) {
|
|
98
228
|
const index = this.layers.indexOf(layer);
|
|
99
|
-
console.log('removeLayer----->', index);
|
|
100
229
|
if (index !== -1) {
|
|
101
230
|
this.layers.splice(index, 1);
|
|
102
231
|
this.refresh();
|
|
@@ -107,15 +236,18 @@ class SvgMapView {
|
|
|
107
236
|
* @param type 图层类型
|
|
108
237
|
*/
|
|
109
238
|
removeLayerByType(type) {
|
|
110
|
-
|
|
111
|
-
|
|
239
|
+
const layer = this.layers.find((layer) => layer.getType() === type);
|
|
240
|
+
if (layer) {
|
|
241
|
+
this.clearLayersGroup(layer);
|
|
242
|
+
this.layers = this.layers.filter((cLayer) => cLayer.getType() !== type);
|
|
243
|
+
}
|
|
112
244
|
}
|
|
113
245
|
// ==================== 变换系统 ====================
|
|
114
246
|
/**
|
|
115
247
|
* 设置自适应视图变换 - 让SVG刚好包裹住图形
|
|
116
248
|
*/
|
|
117
249
|
fitToView(bounds) {
|
|
118
|
-
const padding =
|
|
250
|
+
const padding = 20; // 添加一些边距以避免内容贴边
|
|
119
251
|
const boundWidth = bounds.maxX - bounds.minX;
|
|
120
252
|
const boundHeight = bounds.maxY - bounds.minY;
|
|
121
253
|
// 防止宽高为0的情况
|
|
@@ -138,8 +270,8 @@ class SvgMapView {
|
|
|
138
270
|
this.viewBox = {
|
|
139
271
|
x: bounds.minX - padding,
|
|
140
272
|
y: bounds.minY - padding,
|
|
141
|
-
width: boundWidth + padding,
|
|
142
|
-
height: boundHeight + padding,
|
|
273
|
+
width: boundWidth + padding * 2,
|
|
274
|
+
height: boundHeight + padding * 2,
|
|
143
275
|
};
|
|
144
276
|
// 根据宽高比选择合适的preserveAspectRatio设置
|
|
145
277
|
if (Math.abs(contentAspectRatio - containerAspectRatio) < 0.01) {
|
|
@@ -151,42 +283,6 @@ class SvgMapView {
|
|
|
151
283
|
this.svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
152
284
|
}
|
|
153
285
|
this.updateViewBox();
|
|
154
|
-
this.refresh();
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* 重置变换
|
|
158
|
-
*/
|
|
159
|
-
resetTransform() {
|
|
160
|
-
this.scale = 1;
|
|
161
|
-
// 重置viewBox到默认状态
|
|
162
|
-
const containerRect = this.container.getBoundingClientRect();
|
|
163
|
-
this.viewBox = {
|
|
164
|
-
x: 0,
|
|
165
|
-
y: 0,
|
|
166
|
-
width: containerRect.width,
|
|
167
|
-
height: containerRect.height,
|
|
168
|
-
};
|
|
169
|
-
this.updateViewBox();
|
|
170
|
-
this.refresh();
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* 设置缩放级别
|
|
174
|
-
*/
|
|
175
|
-
setZoom(zoomLevel) {
|
|
176
|
-
if (zoomLevel <= 0)
|
|
177
|
-
return;
|
|
178
|
-
const oldScale = this.scale;
|
|
179
|
-
this.scale = zoomLevel;
|
|
180
|
-
// 调整viewBox以实现缩放
|
|
181
|
-
const scaleFactor = oldScale / this.scale;
|
|
182
|
-
const centerX = this.viewBox.x + this.viewBox.width / 2;
|
|
183
|
-
const centerY = this.viewBox.y + this.viewBox.height / 2;
|
|
184
|
-
this.viewBox.width *= scaleFactor;
|
|
185
|
-
this.viewBox.height *= scaleFactor;
|
|
186
|
-
this.viewBox.x = centerX - this.viewBox.width / 2;
|
|
187
|
-
this.viewBox.y = centerY - this.viewBox.height / 2;
|
|
188
|
-
this.updateViewBox();
|
|
189
|
-
this.refresh();
|
|
190
286
|
}
|
|
191
287
|
/**
|
|
192
288
|
* 获取当前缩放级别
|
|
@@ -194,21 +290,21 @@ class SvgMapView {
|
|
|
194
290
|
getZoom() {
|
|
195
291
|
return this.scale;
|
|
196
292
|
}
|
|
197
|
-
/**
|
|
198
|
-
* 设置线条缩放系数
|
|
199
|
-
*/
|
|
200
|
-
setLineScale(lineScale) {
|
|
201
|
-
if (lineScale <= 0)
|
|
202
|
-
return;
|
|
203
|
-
this.lineScale = lineScale;
|
|
204
|
-
this.refresh();
|
|
205
|
-
}
|
|
206
293
|
/**
|
|
207
294
|
* 获取当前线条缩放系数
|
|
208
295
|
*/
|
|
209
296
|
getLineScale() {
|
|
210
297
|
return this.lineScale;
|
|
211
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* 绘制特定的图层
|
|
301
|
+
*/
|
|
302
|
+
renderLayer(type) {
|
|
303
|
+
const layer = this.layers.find((layer) => layer.getType() === type);
|
|
304
|
+
// 清空图层组
|
|
305
|
+
this.clearLayersGroup(layer);
|
|
306
|
+
this.onDrawLayers(type);
|
|
307
|
+
}
|
|
212
308
|
// ==================== 渲染系统 ====================
|
|
213
309
|
/**
|
|
214
310
|
* 主渲染方法
|
|
@@ -219,25 +315,112 @@ class SvgMapView {
|
|
|
219
315
|
// 绘制所有图层
|
|
220
316
|
this.onDrawLayers();
|
|
221
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* 获取图层id
|
|
320
|
+
*/
|
|
321
|
+
getLayerId(layer) {
|
|
322
|
+
return `layer-${layer.getType()}-${layer.getLevel()}`;
|
|
323
|
+
}
|
|
222
324
|
/**
|
|
223
325
|
* 清空图层组
|
|
224
326
|
*/
|
|
225
|
-
clearLayersGroup() {
|
|
226
|
-
|
|
227
|
-
this.
|
|
327
|
+
clearLayersGroup(layer) {
|
|
328
|
+
if (layer) {
|
|
329
|
+
const layerId = this.getLayerId(layer);
|
|
330
|
+
const layerGroup = this.layersGroup.querySelector(`#${layerId}`);
|
|
331
|
+
if (layerGroup) {
|
|
332
|
+
this.layersGroup.removeChild(layerGroup);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
while (this.layersGroup.firstChild) {
|
|
337
|
+
this.layersGroup.removeChild(this.layersGroup.firstChild);
|
|
338
|
+
}
|
|
228
339
|
}
|
|
229
340
|
}
|
|
230
341
|
/**
|
|
231
|
-
*
|
|
342
|
+
* 获取图层的下一个兄弟元素
|
|
343
|
+
* 根据图层的level来获取
|
|
232
344
|
*/
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
345
|
+
getNextSibling(layer) {
|
|
346
|
+
const nextLayer = this.layers.find((cLayer) => cLayer.getLevel() > layer.getLevel());
|
|
347
|
+
const id = `layer-${nextLayer?.getType()}-${nextLayer?.getLevel()}`;
|
|
348
|
+
const nextSibling = this.layersGroup.querySelector(`#${id}`);
|
|
349
|
+
return nextSibling;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* 绘制图层,不传参数则默认绘制所有图层
|
|
353
|
+
*/
|
|
354
|
+
onDrawLayers(type) {
|
|
355
|
+
if (type) {
|
|
356
|
+
const layer = this.layers.find((layer) => layer.getType() === type);
|
|
357
|
+
if (layer) {
|
|
358
|
+
const layerGroup = this.createSVGGroup(`layer-${layer.getType()}-${layer.getLevel()}`);
|
|
237
359
|
layer.drawSVG(layerGroup, this.scale, this.lineScale);
|
|
238
|
-
this.
|
|
360
|
+
const nextSibling = this.getNextSibling(layer);
|
|
361
|
+
if (nextSibling) {
|
|
362
|
+
this.layersGroup.insertBefore(layerGroup, nextSibling);
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
this.layersGroup.appendChild(layerGroup);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
for (const layer of this.layers) {
|
|
371
|
+
if (layer.isVisible()) {
|
|
372
|
+
const layerGroup = this.createSVGGroup(`layer-${layer.getType()}-${layer.getLevel()}`);
|
|
373
|
+
layer.drawSVG(layerGroup, this.scale, this.lineScale);
|
|
374
|
+
this.layersGroup.appendChild(layerGroup);
|
|
375
|
+
}
|
|
239
376
|
}
|
|
240
377
|
}
|
|
378
|
+
// if (type) {
|
|
379
|
+
// const layer = this.layers.find((layer) => layer.getType() === type);
|
|
380
|
+
// const layerId = this.getLayerId(layer);
|
|
381
|
+
// // 记录原始位置信息
|
|
382
|
+
// const layerGroup = this.layersGroup.querySelector(`#${layerId}`);
|
|
383
|
+
// let nextSibling: Node | null = null;
|
|
384
|
+
// if (layerGroup) {
|
|
385
|
+
// // 记录被删除元素的下一个兄弟元素,用于确定插入位置
|
|
386
|
+
// nextSibling = layerGroup.nextSibling;
|
|
387
|
+
// // 删除旧的图层组
|
|
388
|
+
// this.layersGroup.removeChild(layerGroup);
|
|
389
|
+
// // 从layerId解析出图层类型和层级
|
|
390
|
+
// // layerId格式: layer-${type}-${level}
|
|
391
|
+
// const layerIdParts = layerId.split('-');
|
|
392
|
+
// if (layerIdParts.length >= 3) {
|
|
393
|
+
// const layerType = layerIdParts.slice(1, -1).join('-'); // 处理类型名中可能包含连字符的情况
|
|
394
|
+
// const layerLevel = parseInt(layerIdParts[layerIdParts.length - 1]);
|
|
395
|
+
// // 查找对应的图层对象
|
|
396
|
+
// const targetLayer = this.layers.find(layer =>
|
|
397
|
+
// layer.getType() === layerType && layer.getLevel() === layerLevel
|
|
398
|
+
// );
|
|
399
|
+
// if (targetLayer && targetLayer.isVisible()) {
|
|
400
|
+
// // 创建新的图层组
|
|
401
|
+
// const newLayerGroup = this.createSVGGroup(layerId);
|
|
402
|
+
// // 重新绘制图层
|
|
403
|
+
// targetLayer.drawSVG(newLayerGroup, this.scale, this.lineScale);
|
|
404
|
+
// // 在原始位置插入新元素
|
|
405
|
+
// if (nextSibling) {
|
|
406
|
+
// this.layersGroup.insertBefore(newLayerGroup, nextSibling);
|
|
407
|
+
// } else {
|
|
408
|
+
// // 如果没有下一个兄弟元素,说明原来是最后一个,直接appendChild
|
|
409
|
+
// this.layersGroup.appendChild(newLayerGroup);
|
|
410
|
+
// }
|
|
411
|
+
// }
|
|
412
|
+
// }
|
|
413
|
+
// }
|
|
414
|
+
// } else {
|
|
415
|
+
// // 重绘所有图层
|
|
416
|
+
// for (const layer of this.layers) {
|
|
417
|
+
// if (layer.isVisible()) {
|
|
418
|
+
// const layerGroup = this.createSVGGroup(`layer-${layer.getType()}-${layer.getLevel()}`);
|
|
419
|
+
// layer.drawSVG(layerGroup, this.scale, this.lineScale);
|
|
420
|
+
// this.layersGroup.appendChild(layerGroup);
|
|
421
|
+
// }
|
|
422
|
+
// }
|
|
423
|
+
// }
|
|
241
424
|
}
|
|
242
425
|
/**
|
|
243
426
|
* 刷新渲染
|
|
@@ -247,12 +430,6 @@ class SvgMapView {
|
|
|
247
430
|
return;
|
|
248
431
|
this.render();
|
|
249
432
|
}
|
|
250
|
-
/**
|
|
251
|
-
* 重新初始化SVG(用于容器大小变化)
|
|
252
|
-
*/
|
|
253
|
-
reinitializeSVG() {
|
|
254
|
-
this.refresh();
|
|
255
|
-
}
|
|
256
433
|
// ==================== 拖拽功能 ====================
|
|
257
434
|
/**
|
|
258
435
|
* 设置拖拽事件处理器
|
|
@@ -377,22 +554,6 @@ class SvgMapView {
|
|
|
377
554
|
return null;
|
|
378
555
|
}
|
|
379
556
|
}
|
|
380
|
-
/**
|
|
381
|
-
* 自动适配viewBox到实际内容
|
|
382
|
-
*/
|
|
383
|
-
autoFitToContent() {
|
|
384
|
-
if (this.destroyed || this.layers.length === 0)
|
|
385
|
-
return;
|
|
386
|
-
const bounds = this.getLayersGroupBounds();
|
|
387
|
-
if (bounds) {
|
|
388
|
-
this.fitToView({
|
|
389
|
-
minX: bounds.x,
|
|
390
|
-
minY: bounds.y,
|
|
391
|
-
maxX: bounds.x + bounds.width,
|
|
392
|
-
maxY: bounds.y + bounds.height,
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
557
|
/**
|
|
397
558
|
* 获取ViewBox信息
|
|
398
559
|
*/
|
|
@@ -413,13 +574,6 @@ class SvgMapView {
|
|
|
413
574
|
* 诊断SVG尺寸信息
|
|
414
575
|
*/
|
|
415
576
|
diagnosticSizeInfo() { }
|
|
416
|
-
/**
|
|
417
|
-
* 设置是否显示比例尺
|
|
418
|
-
*/
|
|
419
|
-
setShowScale(show) {
|
|
420
|
-
this.showScale = show;
|
|
421
|
-
this.refresh();
|
|
422
|
-
}
|
|
423
577
|
/**
|
|
424
578
|
* 获取SVG元素
|
|
425
579
|
*/
|
|
@@ -599,164 +753,56 @@ const createStoreImpl = (createState) => {
|
|
|
599
753
|
const initialState = state = createState(setState, getState, api);
|
|
600
754
|
return api;
|
|
601
755
|
};
|
|
602
|
-
const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
603
|
-
|
|
604
|
-
const identity$1 = (arg) => arg;
|
|
605
|
-
function useStore(api, selector = identity$1) {
|
|
606
|
-
const slice = React.useSyncExternalStore(
|
|
607
|
-
api.subscribe,
|
|
608
|
-
() => selector(api.getState()),
|
|
609
|
-
() => selector(api.getInitialState())
|
|
610
|
-
);
|
|
611
|
-
React.useDebugValue(slice);
|
|
612
|
-
return slice;
|
|
613
|
-
}
|
|
614
|
-
const createImpl = (createState) => {
|
|
615
|
-
const api = createStore(createState);
|
|
616
|
-
const useBoundStore = (selector) => useStore(api, selector);
|
|
617
|
-
Object.assign(useBoundStore, api);
|
|
618
|
-
return useBoundStore;
|
|
619
|
-
};
|
|
620
|
-
const create = (createState) => createState ? createImpl(createState) : createImpl;
|
|
621
|
-
|
|
622
|
-
const useSubBoundaryBorderStore = create((set, get) => ({
|
|
623
|
-
subBoundaryBorder: {},
|
|
624
|
-
//
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
})
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
...
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
/**
|
|
653
|
-
* 默认线宽设置
|
|
654
|
-
*/
|
|
655
|
-
const DEFAULT_LINE_WIDTHS = {
|
|
656
|
-
OBSTACLE: 2,
|
|
657
|
-
CHARGING_PILE: 2,
|
|
658
|
-
CHANNEL: 2,
|
|
659
|
-
PATH: 20,
|
|
660
|
-
VISION_OFF_AREA: 2,
|
|
661
|
-
TIME_LIMIT_OBSTACLE: 1,
|
|
662
|
-
};
|
|
663
|
-
/**
|
|
664
|
-
* 默认透明度设置
|
|
665
|
-
*/
|
|
666
|
-
const DEFAULT_OPACITIES = {
|
|
667
|
-
FULL: 1.0,
|
|
668
|
-
HIGH: 0.7,
|
|
669
|
-
LOW: 0.4};
|
|
670
|
-
/**
|
|
671
|
-
* 默认半径设置
|
|
672
|
-
*/
|
|
673
|
-
const DEFAULT_RADII = {
|
|
674
|
-
CHARGING_PILE: 12};
|
|
675
|
-
/**
|
|
676
|
-
* 图层等级
|
|
677
|
-
*/
|
|
678
|
-
const LAYER_LEVELS = {
|
|
679
|
-
BOUNDARY: 2,
|
|
680
|
-
BOUNDARY_BORDER: 4};
|
|
681
|
-
/**
|
|
682
|
-
* 图层默认id
|
|
683
|
-
*/
|
|
684
|
-
const LAYER_DEFAULT_TYPE = {
|
|
685
|
-
CHANNEL: 'channel',
|
|
686
|
-
BOUNDARY: 'boundary',
|
|
687
|
-
PATH: 'path',
|
|
688
|
-
BOUNDARY_BORDER: 'boundary_border',
|
|
689
|
-
OBSTACLE: 'obstacle',
|
|
690
|
-
CHARGING_PILE: 'charging_pile',
|
|
691
|
-
POINT: 'point',
|
|
692
|
-
SVG: 'svg',
|
|
693
|
-
VISION_OFF_AREA: 'vision_off_area',
|
|
694
|
-
ANTENNA: 'antenna',
|
|
695
|
-
};
|
|
696
|
-
var RobotStatus;
|
|
697
|
-
(function (RobotStatus) {
|
|
698
|
-
RobotStatus[RobotStatus["PARKED"] = 1] = "PARKED";
|
|
699
|
-
RobotStatus[RobotStatus["CHARGING"] = 2] = "CHARGING";
|
|
700
|
-
RobotStatus[RobotStatus["STANDBY"] = 3] = "STANDBY";
|
|
701
|
-
RobotStatus[RobotStatus["MOWING"] = 4] = "MOWING";
|
|
702
|
-
RobotStatus[RobotStatus["WORKING"] = 5] = "WORKING";
|
|
703
|
-
RobotStatus[RobotStatus["MAPPING"] = 6] = "MAPPING";
|
|
704
|
-
RobotStatus[RobotStatus["ERROR"] = 7] = "ERROR";
|
|
705
|
-
RobotStatus[RobotStatus["UPGRADING"] = 8] = "UPGRADING";
|
|
706
|
-
RobotStatus[RobotStatus["DISCONNECTED"] = 9] = "DISCONNECTED";
|
|
707
|
-
RobotStatus[RobotStatus["UNKNOWN"] = -1] = "UNKNOWN";
|
|
708
|
-
RobotStatus[RobotStatus["TASK_DELAY"] = 10] = "TASK_DELAY";
|
|
709
|
-
// WAITING = 'Waiting',
|
|
710
|
-
})(RobotStatus || (RobotStatus = {}));
|
|
711
|
-
// RTK状态
|
|
712
|
-
var RTK_STATE;
|
|
713
|
-
(function (RTK_STATE) {
|
|
714
|
-
RTK_STATE[RTK_STATE["LOW_RTK"] = 1] = "LOW_RTK";
|
|
715
|
-
RTK_STATE[RTK_STATE["MIDDLE_RTK"] = 2] = "MIDDLE_RTK";
|
|
716
|
-
RTK_STATE[RTK_STATE["HIGH_RTK"] = 3] = "HIGH_RTK";
|
|
717
|
-
RTK_STATE[RTK_STATE["NO_POSTURE"] = 10] = "NO_POSTURE";
|
|
718
|
-
RTK_STATE[RTK_STATE["OUT_OF_RANGE"] = 11] = "OUT_OF_RANGE";
|
|
719
|
-
RTK_STATE[RTK_STATE["OFF_LINE"] = 19] = "OFF_LINE";
|
|
720
|
-
})(RTK_STATE || (RTK_STATE = {}));
|
|
721
|
-
var REAL_TIME_DATA_TYPE;
|
|
722
|
-
(function (REAL_TIME_DATA_TYPE) {
|
|
723
|
-
REAL_TIME_DATA_TYPE[REAL_TIME_DATA_TYPE["LOCATION"] = 1] = "LOCATION";
|
|
724
|
-
REAL_TIME_DATA_TYPE[REAL_TIME_DATA_TYPE["PROCESS"] = 2] = "PROCESS";
|
|
725
|
-
})(REAL_TIME_DATA_TYPE || (REAL_TIME_DATA_TYPE = {}));
|
|
726
|
-
const ISOLATED_BOUNDARY_SVG = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
727
|
-
<g opacity="0.6">
|
|
728
|
-
<rect width="24" height="24" rx="12" fill="#1E1E1F" fill-opacity="0.5"/>
|
|
729
|
-
<path d="M8.20573 12.4961C6.739 11.1707 4.85775 11.2284 3.60938 11.1707C6.17744 13.0156 6.05584 15.7887 5.67404 16.9446H14.327C13.837 13.1673 15.5321 10.1289 16.4408 9.08187C15.0342 9.46118 13.5794 10.8303 13.0278 11.4674C12.8102 8.94572 13.992 5.97489 14.61 4.80469C12.5235 5.92214 11.0501 8.26056 10.4938 9.56261C9.22803 7.5947 6.83894 7.17806 5.883 6.9432C8.20573 10.2373 8.00039 11.1707 8.20573 12.4961Z" fill="white"/>
|
|
730
|
-
<mask id="path-3-outside-1_9822_43516" maskUnits="userSpaceOnUse" x="13.2344" y="7.20545" width="12.0208" height="12.0208" fill="black">
|
|
731
|
-
<rect fill="white" x="13.2344" y="7.20545" width="12.0208" height="12.0208"/>
|
|
732
|
-
<path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z"/>
|
|
733
|
-
</mask>
|
|
734
|
-
<path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z" fill="white"/>
|
|
735
|
-
<path d="M20.6852 11.1208L21.7458 10.0601L21.7459 10.0601L20.6852 11.1208ZM17.8568 11.1208L16.7961 10.0601L16.7962 10.0601L17.8568 11.1208ZM17.2733 11.7043L16.2126 10.6437L16.2126 10.6436L17.2733 11.7043ZM16.8631 13.9319L17.9238 14.9926L16.381 16.5354L15.493 14.5425L16.8631 13.9319ZM17.5916 13.2034L19.0855 13.0678L19.149 13.7674L18.6523 14.2641L17.5916 13.2034ZM20.4056 12.1421L21.4662 11.0814L21.4665 11.0817L20.4056 12.1421ZM18.9699 14.5348L17.9093 13.4741L18.3741 13.0093L19.0309 13.036L18.9699 14.5348ZM18.2235 15.2812L17.6298 16.6587L15.5997 15.7837L17.1628 14.2206L18.2235 15.2812ZM20.429 14.86L21.4897 15.9207L21.4897 15.9207L20.429 14.86ZM21.0125 14.2765L22.0732 15.3371L22.0732 15.3372L21.0125 14.2765ZM20.6852 11.1208L19.6246 12.1815C19.4294 11.9862 19.1127 11.9863 18.9174 12.1815L17.8568 11.1208L16.7962 10.0601C18.163 8.69337 20.379 8.6934 21.7458 10.0601L20.6852 11.1208ZM17.8568 11.1208L18.9175 12.1814L18.334 12.7649L17.2733 11.7043L16.2126 10.6436L16.7961 10.0601L17.8568 11.1208ZM17.2733 11.7043L18.334 12.7649C18.1877 12.9112 18.1488 13.1318 18.2333 13.3214L16.8631 13.9319L15.493 14.5425C14.9228 13.2629 15.1537 11.7026 16.2126 10.6437L17.2733 11.7043ZM16.8631 13.9319L15.8025 12.8713L16.531 12.1428L17.5916 13.2034L18.6523 14.2641L17.9238 14.9926L16.8631 13.9319ZM17.5916 13.2034L16.0978 13.339C16.0334 12.6302 16.2727 11.8914 16.8196 11.3445L17.8803 12.4052L18.9409 13.4658C19.051 13.3558 19.098 13.206 19.0855 13.0678L17.5916 13.2034ZM17.8803 12.4052L16.8196 11.3445L17.5067 10.6574L18.5674 11.7181L19.628 12.7788L18.9409 13.4658L17.8803 12.4052ZM18.5674 11.7181L17.5067 10.6574C18.483 9.68112 20.0659 9.68112 21.0422 10.6574L19.9816 11.7181L18.9209 12.7788C19.1162 12.974 19.4328 12.974 19.628 12.7788L18.5674 11.7181ZM19.9816 11.7181L21.0422 10.6574L21.4662 11.0814L20.4056 12.1421L19.3449 13.2027L18.9209 12.7788L19.9816 11.7181ZM20.4056 12.1421L21.4665 11.0817C22.4419 12.0577 22.4428 13.6404 21.4662 14.617L20.4056 13.5563L19.3449 12.4956C19.1493 12.6913 19.1498 13.0076 19.3446 13.2024L20.4056 12.1421ZM20.4056 13.5563L21.4662 14.617L20.7791 15.304L19.7185 14.2434L18.6578 13.1827L19.3449 12.4956L20.4056 13.5563ZM19.7185 14.2434L20.7791 15.304C20.2628 15.8204 19.5762 16.0607 18.909 16.0335L18.9699 14.5348L19.0309 13.036C18.9025 13.0308 18.7629 13.0777 18.6578 13.1827L19.7185 14.2434ZM18.9699 14.5348L20.0306 15.5954L19.2841 16.3419L18.2235 15.2812L17.1628 14.2206L17.9093 13.4741L18.9699 14.5348ZM18.2235 15.2812L18.8172 13.9037C19.0042 13.9843 19.2223 13.9455 19.3684 13.7993L20.429 14.86L21.4897 15.9207C20.4427 16.9676 18.9034 17.2077 17.6298 16.6587L18.2235 15.2812ZM20.429 14.86L19.3684 13.7994L19.9519 13.2159L21.0125 14.2765L22.0732 15.3372L21.4897 15.9207L20.429 14.86ZM21.0125 14.2765L19.9518 13.2159C20.1471 13.0206 20.1471 12.704 19.9519 12.5088L21.0125 11.4481L22.0732 10.3874C23.4401 11.7543 23.4399 13.9703 22.0732 15.3371L21.0125 14.2765ZM21.0125 11.4481L19.9519 12.5088L19.6246 12.1814L20.6852 11.1208L21.7459 10.0601L22.0732 10.3874L21.0125 11.4481Z" fill="#8E8E8F" mask="url(#path-3-outside-1_9822_43516)"/>
|
|
736
|
-
<mask id="path-5-outside-2_9822_43516" maskUnits="userSpaceOnUse" x="9.22265" y="10.388" width="12.7279" height="12.7279" fill="black">
|
|
737
|
-
<rect fill="white" x="9.22265" y="10.388" width="12.7279" height="12.7279"/>
|
|
738
|
-
<path d="M16.1708 14.5077C15.4353 14.1778 14.5422 14.3133 13.9383 14.9172L13.3548 15.5007C12.574 16.2818 12.5738 17.5481 13.3548 18.3291L13.6821 18.6564C14.4631 19.4374 15.7295 19.4373 16.5105 18.6564L17.094 18.0729C17.6895 17.4775 17.8299 16.6003 17.5173 15.8708L16.7743 16.6138C16.7828 16.8803 16.6863 17.1494 16.4829 17.3527L15.7952 18.0405C15.4046 18.4309 14.7714 18.431 14.3809 18.0405L13.957 17.6165C13.5667 17.226 13.5666 16.5927 13.957 16.2023L14.6447 15.5145C14.8654 15.2939 15.1639 15.1979 15.452 15.2266L16.1708 14.5077Z"/>
|
|
739
|
-
</mask>
|
|
740
|
-
<path d="M16.1708 14.5077C15.4353 14.1778 14.5422 14.3133 13.9383 14.9172L13.3548 15.5007C12.574 16.2818 12.5738 17.5481 13.3548 18.3291L13.6821 18.6564C14.4631 19.4374 15.7295 19.4373 16.5105 18.6564L17.094 18.0729C17.6895 17.4775 17.8299 16.6003 17.5173 15.8708L16.7743 16.6138C16.7828 16.8803 16.6863 17.1494 16.4829 17.3527L15.7952 18.0405C15.4046 18.4309 14.7714 18.431 14.3809 18.0405L13.957 17.6165C13.5667 17.226 13.5666 16.5927 13.957 16.2023L14.6447 15.5145C14.8654 15.2939 15.1639 15.1979 15.452 15.2266L16.1708 14.5077Z" fill="white"/>
|
|
741
|
-
<path d="M16.1708 14.5077L16.7847 13.1391L18.7701 14.0297L17.2315 15.5684L16.1708 14.5077ZM13.3548 15.5007L12.294 14.4402L12.2941 14.44L13.3548 15.5007ZM16.5105 18.6564L17.5712 19.7171L17.5711 19.7172L16.5105 18.6564ZM17.5173 15.8708L16.4567 14.8102L18.0236 13.2433L18.8962 15.2802L17.5173 15.8708ZM16.7743 16.6138L15.2751 16.6613L15.2545 16.0123L15.7137 15.5532L16.7743 16.6138ZM15.7952 18.0405L16.8558 19.1011L16.8556 19.1013L15.7952 18.0405ZM13.957 17.6165L12.8963 18.6772L12.8959 18.6767L13.957 17.6165ZM13.957 16.2023L12.8961 15.1418L12.8963 15.1416L13.957 16.2023ZM15.452 15.2266L16.5126 16.2872L16.0102 16.7896L15.3032 16.7192L15.452 15.2266ZM16.1708 14.5077L15.5569 15.8763C15.3688 15.792 15.1469 15.8299 14.999 15.9779L13.9383 14.9172L12.8776 13.8565C13.9375 12.7967 15.5018 12.5636 16.7847 13.1391L16.1708 14.5077ZM13.9383 14.9172L14.999 15.9779L14.4155 16.5614L13.3548 15.5007L12.2941 14.44L12.8776 13.8565L13.9383 14.9172ZM13.3548 15.5007L14.4156 16.5612C14.2202 16.7567 14.2204 17.0734 14.4155 17.2685L13.3548 18.3291L12.2941 19.3898C10.9272 18.0229 10.9277 15.8069 12.294 14.4402L13.3548 15.5007ZM13.3548 18.3291L14.4155 17.2685L14.7428 17.5958L13.6821 18.6564L12.6215 19.7171L12.2941 19.3898L13.3548 18.3291ZM13.6821 18.6564L14.7428 17.5958C14.9378 17.7908 15.2546 17.791 15.45 17.5956L16.5105 18.6564L17.5711 19.7172C16.2044 21.0836 13.9884 21.084 12.6215 19.7171L13.6821 18.6564ZM16.5105 18.6564L15.4499 17.5958L16.0334 17.0123L17.094 18.0729L18.1547 19.1336L17.5712 19.7171L16.5105 18.6564ZM17.094 18.0729L16.0334 17.0123C16.1795 16.8662 16.2185 16.6481 16.1385 16.4615L17.5173 15.8708L18.8962 15.2802C19.4413 16.5526 19.1996 18.0887 18.1547 19.1336L17.094 18.0729ZM17.5173 15.8708L18.578 16.9315L17.835 17.6745L16.7743 16.6138L15.7137 15.5532L16.4567 14.8102L17.5173 15.8708ZM16.7743 16.6138L18.2736 16.5664C18.2945 17.226 18.0544 17.9026 17.5436 18.4134L16.4829 17.3527L15.4223 16.2921C15.3182 16.3961 15.2711 16.5346 15.2751 16.6613L16.7743 16.6138ZM16.4829 17.3527L17.5436 18.4134L16.8558 19.1011L15.7952 18.0405L14.7345 16.9798L15.4223 16.2921L16.4829 17.3527ZM15.7952 18.0405L16.8556 19.1013C15.8795 20.0771 14.2967 20.0776 13.3203 19.1011L14.3809 18.0405L15.4416 16.9798C15.2461 16.7844 14.9297 16.7847 14.7347 16.9797L15.7952 18.0405ZM14.3809 18.0405L13.3203 19.1011L12.8963 18.6772L13.957 17.6165L15.0176 16.5558L15.4416 16.9798L14.3809 18.0405ZM13.957 17.6165L12.8959 18.6767C11.9207 17.7008 11.92 16.1182 12.8961 15.1418L13.957 16.2023L15.0178 17.2628C15.2133 17.0672 15.2128 16.7512 15.018 16.5563L13.957 17.6165ZM13.957 16.2023L12.8963 15.1416L13.5841 14.4539L14.6447 15.5145L15.7054 16.5752L15.0176 17.2629L13.957 16.2023ZM14.6447 15.5145L13.5841 14.4539C14.1364 13.9015 14.8848 13.6626 15.6007 13.734L15.452 15.2266L15.3032 16.7192C15.443 16.7331 15.5943 16.6862 15.7054 16.5752L14.6447 15.5145ZM15.452 15.2266L14.3913 14.1659L15.1101 13.4471L16.1708 14.5077L17.2315 15.5684L16.5126 16.2872L15.452 15.2266Z" fill="#8E8E8F" mask="url(#path-5-outside-2_9822_43516)"/>
|
|
742
|
-
<path d="M20.6852 11.1208C19.9042 10.3398 18.6378 10.3398 17.8568 11.1208L17.2733 11.7043C16.6707 12.3069 16.5358 13.1973 16.8631 13.9319L17.5916 13.2034C17.5657 12.9181 17.6619 12.6236 17.8803 12.4052L18.5674 11.7181C18.9579 11.3276 19.5911 11.3276 19.9816 11.7181L20.4056 12.1421C20.7959 12.5326 20.796 13.1658 20.4056 13.5563L19.7185 14.2434C19.5128 14.449 19.2393 14.5457 18.9699 14.5348L18.2235 15.2812C18.9538 15.596 19.8325 15.4565 20.429 14.86L21.0125 14.2765C21.7935 13.4955 21.7936 12.2291 21.0125 11.4481L20.6852 11.1208Z" fill="white"/>
|
|
743
|
-
<rect width="2.59942" height="2.97264" rx="1" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 21.1133 12.8486)" fill="#8E8E8F"/>
|
|
744
|
-
<rect width="1.91578" height="4.24403" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 19.6992 13.8066)" fill="#8E8E8F"/>
|
|
745
|
-
<rect width="2.59942" height="2.97264" rx="1" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 17.3125 16.6455)" fill="#8E8E8F"/>
|
|
746
|
-
</g>
|
|
747
|
-
</svg>`;
|
|
748
|
-
/**
|
|
749
|
-
* 遍历割草任务,下述四个字段可以在路径中唯一确定遍历的位置(当前区域、当前块、当前行、在当前行上的路程)
|
|
750
|
-
*/
|
|
751
|
-
const ACTION_BLOCK_COVER = 5;
|
|
752
|
-
/**
|
|
753
|
-
* 遍历割草块转移任务(在同一区域中的一个块转移到下一个块),下述四个字段可以在路径中唯一确定转移的位置(当前区域、前置块、当前转移路径线序号、在当前线上的路程)
|
|
754
|
-
*/
|
|
755
|
-
const ACTION_BLOCK_TRANSFER = 6;
|
|
756
|
-
/**
|
|
757
|
-
* 边界割草任务(割草任务内部的巡边任务)
|
|
758
|
-
*/
|
|
759
|
-
const ACTION_BOUNDARY_TASK = 8;
|
|
756
|
+
const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
757
|
+
|
|
758
|
+
const identity$1 = (arg) => arg;
|
|
759
|
+
function useStore(api, selector = identity$1) {
|
|
760
|
+
const slice = React.useSyncExternalStore(
|
|
761
|
+
api.subscribe,
|
|
762
|
+
() => selector(api.getState()),
|
|
763
|
+
() => selector(api.getInitialState())
|
|
764
|
+
);
|
|
765
|
+
React.useDebugValue(slice);
|
|
766
|
+
return slice;
|
|
767
|
+
}
|
|
768
|
+
const createImpl = (createState) => {
|
|
769
|
+
const api = createStore(createState);
|
|
770
|
+
const useBoundStore = (selector) => useStore(api, selector);
|
|
771
|
+
Object.assign(useBoundStore, api);
|
|
772
|
+
return useBoundStore;
|
|
773
|
+
};
|
|
774
|
+
const create = (createState) => createState ? createImpl(createState) : createImpl;
|
|
775
|
+
|
|
776
|
+
const useSubBoundaryBorderStore = create((set, get) => ({
|
|
777
|
+
subBoundaryBorder: {},
|
|
778
|
+
// 追加单个数据
|
|
779
|
+
addSubBoundaryBorder: (key, element) => set((state) => ({
|
|
780
|
+
subBoundaryBorder: {
|
|
781
|
+
...state.subBoundaryBorder,
|
|
782
|
+
[key]: element,
|
|
783
|
+
},
|
|
784
|
+
})),
|
|
785
|
+
// 清空所有数据
|
|
786
|
+
clearSubBoundaryBorder: () => set({ subBoundaryBorder: {} }),
|
|
787
|
+
// 障碍物
|
|
788
|
+
obstacles: {},
|
|
789
|
+
addObstacles: (key, element) => set((state) => ({
|
|
790
|
+
obstacles: {
|
|
791
|
+
...state.obstacles,
|
|
792
|
+
[key]: element,
|
|
793
|
+
},
|
|
794
|
+
})),
|
|
795
|
+
clearObstacles: () => set({ obstacles: {} }),
|
|
796
|
+
// svg数据
|
|
797
|
+
svgElements: {},
|
|
798
|
+
addSvgElements: (key, element) => set((state) => ({
|
|
799
|
+
svgElements: {
|
|
800
|
+
...state.svgElements,
|
|
801
|
+
[key]: element,
|
|
802
|
+
},
|
|
803
|
+
})),
|
|
804
|
+
clearSvgElements: () => set({ svgElements: {} }),
|
|
805
|
+
}));
|
|
760
806
|
|
|
761
807
|
/**
|
|
762
808
|
* 路径图层
|
|
@@ -808,51 +854,90 @@ class ChannelLayer extends BaseLayer {
|
|
|
808
854
|
}
|
|
809
855
|
// �� 修改:计算包含所有分区和通道的边界框
|
|
810
856
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
811
|
-
// 1.
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
857
|
+
// 1. 先计算所有分区的边界,如果能拿到边界的svg的大小,就使用这个如果拿不到,就根据分区去计算
|
|
858
|
+
const svg = document.getElementById(SVG_MAP_VIEW_ID);
|
|
859
|
+
if (svg && svg instanceof SVGSVGElement && svg.viewBox) {
|
|
860
|
+
const viewBox = svg.viewBox.baseVal;
|
|
861
|
+
minX = viewBox.x;
|
|
862
|
+
minY = viewBox.y;
|
|
863
|
+
maxX = viewBox.x + viewBox.width;
|
|
864
|
+
maxY = viewBox.y + viewBox.height;
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
for (const partitionId in subBoundaryBorder) {
|
|
868
|
+
const boundaryData = subBoundaryBorder[partitionId];
|
|
869
|
+
if (boundaryData && boundaryData.coordinates && boundaryData.coordinates.length > 0) {
|
|
870
|
+
for (const coord of boundaryData.coordinates) {
|
|
871
|
+
minX = Math.min(minX, coord[0]);
|
|
872
|
+
minY = Math.min(minY, coord[1]);
|
|
873
|
+
maxX = Math.max(maxX, coord[0]);
|
|
874
|
+
maxY = Math.max(maxY, coord[1]);
|
|
875
|
+
}
|
|
820
876
|
}
|
|
821
877
|
}
|
|
822
878
|
}
|
|
823
879
|
// 2. 再计算所有通道的边界
|
|
824
880
|
for (const element of this.elements) {
|
|
825
|
-
const tunnelConnection = element.originalData?.connection;
|
|
826
|
-
if (tunnelConnection && Array.isArray(tunnelConnection)) {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
881
|
+
// const tunnelConnection = element.originalData?.connection;
|
|
882
|
+
// if (tunnelConnection && Array.isArray(tunnelConnection)) {
|
|
883
|
+
// const clipPathId = `channel-exclude-${
|
|
884
|
+
// element.originalData?.id || Math.random().toString(36).substr(2, 9)
|
|
885
|
+
// }`;
|
|
886
|
+
// // 检查是否已存在该 clipPath
|
|
887
|
+
// const existingClipPath = defs.querySelector(`#${clipPathId}`);
|
|
888
|
+
// if (existingClipPath) continue;
|
|
889
|
+
// // 创建 clipPath
|
|
890
|
+
// const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
|
|
891
|
+
// clipPath.setAttribute('id', clipPathId);
|
|
892
|
+
// clipPath.setAttribute('clipPathUnits', 'userSpaceOnUse');
|
|
893
|
+
// // === 合成一个 path ===
|
|
894
|
+
// let d = `M ${minX} ${minY} L ${maxX} ${minY} L ${maxX} ${maxY} L ${minX} ${maxY} Z`;
|
|
895
|
+
// for (const partitionId of tunnelConnection) {
|
|
896
|
+
// const boundaryData = subBoundaryBorder[partitionId];
|
|
897
|
+
// if (boundaryData && boundaryData.coordinates.length >= 3) {
|
|
898
|
+
// d += ` M ${boundaryData.coordinates[0][0]} ${boundaryData.coordinates[0][1]}`;
|
|
899
|
+
// for (let i = 1; i < boundaryData.coordinates.length; i++) {
|
|
900
|
+
// d += ` L ${boundaryData.coordinates[i][0]} ${boundaryData.coordinates[i][1]}`;
|
|
901
|
+
// }
|
|
902
|
+
// d += ' Z';
|
|
903
|
+
// }
|
|
904
|
+
// }
|
|
905
|
+
// const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
906
|
+
// path.setAttribute('d', d);
|
|
907
|
+
// path.setAttribute('clip-rule', 'evenodd'); // 关键
|
|
908
|
+
// clipPath.appendChild(path);
|
|
909
|
+
// defs.appendChild(clipPath);
|
|
910
|
+
// clipPathIdsMap[element.originalData?.id.toString()] = clipPathId;
|
|
911
|
+
// } else {
|
|
912
|
+
const clipPathId = `channel-exclude-all-${element.originalData?.id || Math.random().toString(36).substr(2, 9)}`;
|
|
913
|
+
// 检查是否已存在该 clipPath
|
|
914
|
+
const existingClipPath = defs.querySelector(`#${clipPathId}`);
|
|
915
|
+
if (existingClipPath)
|
|
916
|
+
continue;
|
|
917
|
+
// 创建 clipPath
|
|
918
|
+
const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
|
|
919
|
+
clipPath.setAttribute('id', clipPathId);
|
|
920
|
+
clipPath.setAttribute('clipPathUnits', 'userSpaceOnUse');
|
|
921
|
+
// === 合成一个 path ===
|
|
922
|
+
let d = `M ${minX} ${minY} L ${maxX} ${minY} L ${maxX} ${maxY} L ${minX} ${maxY} Z`;
|
|
923
|
+
for (const partitionId in subBoundaryBorder) {
|
|
924
|
+
const boundaryData = subBoundaryBorder[partitionId];
|
|
925
|
+
if (boundaryData && boundaryData.coordinates.length >= 3) {
|
|
926
|
+
d += ` M ${boundaryData.coordinates[0][0]} ${boundaryData.coordinates[0][1]}`;
|
|
927
|
+
for (let i = 1; i < boundaryData.coordinates.length; i++) {
|
|
928
|
+
d += ` L ${boundaryData.coordinates[i][0]} ${boundaryData.coordinates[i][1]}`;
|
|
846
929
|
}
|
|
930
|
+
d += ' Z';
|
|
847
931
|
}
|
|
848
|
-
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
849
|
-
path.setAttribute('d', d);
|
|
850
|
-
path.setAttribute('clip-rule', 'evenodd'); // 关键
|
|
851
|
-
clipPath.appendChild(path);
|
|
852
|
-
defs.appendChild(clipPath);
|
|
853
|
-
clipPathIdsMap[element.originalData?.id.toString()] = clipPathId;
|
|
854
932
|
}
|
|
933
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
934
|
+
path.setAttribute('d', d);
|
|
935
|
+
path.setAttribute('clip-rule', 'evenodd'); // 关键
|
|
936
|
+
clipPath.appendChild(path);
|
|
937
|
+
defs.appendChild(clipPath);
|
|
938
|
+
clipPathIdsMap[element.originalData?.id.toString()] = clipPathId;
|
|
855
939
|
}
|
|
940
|
+
// }
|
|
856
941
|
return clipPathIdsMap;
|
|
857
942
|
}
|
|
858
943
|
/**
|
|
@@ -916,13 +1001,14 @@ class PathLayer extends BaseLayer {
|
|
|
916
1001
|
this.level = 3;
|
|
917
1002
|
this.scale = 1;
|
|
918
1003
|
this.lineScale = 1;
|
|
1004
|
+
this.boundaryPaths = {};
|
|
919
1005
|
this.type = LAYER_DEFAULT_TYPE.PATH;
|
|
920
1006
|
}
|
|
921
1007
|
/**
|
|
922
1008
|
* 创建所有分区并集的 clipPath
|
|
923
1009
|
*/
|
|
924
1010
|
createUnionClipPath(svgGroup) {
|
|
925
|
-
const { subBoundaryBorder } = useSubBoundaryBorderStore.getState();
|
|
1011
|
+
const { subBoundaryBorder, obstacles, svgElements } = useSubBoundaryBorderStore.getState();
|
|
926
1012
|
// 确保 defs 元素存在
|
|
927
1013
|
let defs = svgGroup.querySelector('defs');
|
|
928
1014
|
if (!defs) {
|
|
@@ -936,7 +1022,7 @@ class PathLayer extends BaseLayer {
|
|
|
936
1022
|
defs.removeChild(existing);
|
|
937
1023
|
// 合成所有分区的 path
|
|
938
1024
|
let d = '';
|
|
939
|
-
//
|
|
1025
|
+
// 1. 外圈(主边界,顺时针)
|
|
940
1026
|
Object.values(subBoundaryBorder).forEach((item) => {
|
|
941
1027
|
const bCoords = item.coordinates;
|
|
942
1028
|
if (bCoords.length >= 3) {
|
|
@@ -947,6 +1033,26 @@ class PathLayer extends BaseLayer {
|
|
|
947
1033
|
d += ' Z ';
|
|
948
1034
|
}
|
|
949
1035
|
});
|
|
1036
|
+
// 2. 内圈(禁区,逆时针)
|
|
1037
|
+
Object.values(obstacles).forEach((item) => {
|
|
1038
|
+
const bCoords = item.coordinates;
|
|
1039
|
+
if (bCoords.length >= 3) {
|
|
1040
|
+
d += `M ${bCoords[bCoords.length - 1][0]} ${bCoords[bCoords.length - 1][1]}`;
|
|
1041
|
+
for (let i = bCoords.length - 2; i >= 0; i--) {
|
|
1042
|
+
d += ` L ${bCoords[i][0]} ${bCoords[i][1]}`;
|
|
1043
|
+
}
|
|
1044
|
+
d += ' Z ';
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
// 3. svgElements(直接拼接path字符串,建议逆时针)
|
|
1048
|
+
if (Array.isArray(svgElements)) {
|
|
1049
|
+
svgElements.forEach((svgPath) => {
|
|
1050
|
+
const svgPathString = svgPath?.metadata?.svg;
|
|
1051
|
+
if (svgPathString && typeof svgPathString === 'string' && svgPathString.trim()) {
|
|
1052
|
+
d += svgPathString + ' ';
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
950
1056
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
951
1057
|
path.setAttribute('d', d);
|
|
952
1058
|
const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
|
|
@@ -971,17 +1077,21 @@ class PathLayer extends BaseLayer {
|
|
|
971
1077
|
// 2. 创建一个组,应用 clipPath
|
|
972
1078
|
const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
973
1079
|
group.setAttribute('clip-path', `url(#${clipPathId})`);
|
|
974
|
-
group.setAttribute('opacity', '0.
|
|
1080
|
+
group.setAttribute('opacity', '0.6'); // 统一透明度,防止叠加脏乱
|
|
975
1081
|
// 3. 渲染所有路径
|
|
976
1082
|
for (const element of this.elements) {
|
|
977
|
-
|
|
1083
|
+
const { id, elements } = element;
|
|
1084
|
+
this.boundaryPaths[id] = [];
|
|
1085
|
+
elements.forEach((element) => {
|
|
1086
|
+
this.renderPathToGroup(group, id, element);
|
|
1087
|
+
});
|
|
978
1088
|
}
|
|
979
1089
|
svgGroup.appendChild(group);
|
|
980
1090
|
}
|
|
981
1091
|
/**
|
|
982
1092
|
* 渲染单个路径到指定的组中
|
|
983
1093
|
*/
|
|
984
|
-
renderPathToGroup(group, element) {
|
|
1094
|
+
renderPathToGroup(group, id, element) {
|
|
985
1095
|
const { coordinates, style } = element;
|
|
986
1096
|
if (coordinates.length < 2)
|
|
987
1097
|
return;
|
|
@@ -997,7 +1107,7 @@ class PathLayer extends BaseLayer {
|
|
|
997
1107
|
// 直接给fill的颜色设置透明度会导致path重叠的部分颜色叠加,所以使用fill填充实色,通过fill-opacity设置透明度
|
|
998
1108
|
path.setAttribute('fill', 'none');
|
|
999
1109
|
// path.setAttribute('fill-opacity', '0.4');
|
|
1000
|
-
path.setAttribute('stroke', style.
|
|
1110
|
+
path.setAttribute('stroke', style.lineColor || '#000000');
|
|
1001
1111
|
path.setAttribute('mix-blend-mode', 'normal');
|
|
1002
1112
|
const lineWidth = Math.max(style.lineWidth || 1, 0.5);
|
|
1003
1113
|
path.setAttribute('stroke-width', lineWidth.toString());
|
|
@@ -1005,13 +1115,8 @@ class PathLayer extends BaseLayer {
|
|
|
1005
1115
|
path.setAttribute('stroke-linejoin', 'round');
|
|
1006
1116
|
// 注意:这里不设置 opacity,因为透明度由父组控制
|
|
1007
1117
|
// path.setAttribute('vector-effect', 'non-scaling-stroke');
|
|
1008
|
-
if (style.lineDash && style.lineDash.length > 0) {
|
|
1009
|
-
path.setAttribute('stroke-dasharray', style.lineDash.join(','));
|
|
1010
|
-
}
|
|
1011
|
-
else if (style.strokeDasharray) {
|
|
1012
|
-
path.setAttribute('stroke-dasharray', style.strokeDasharray);
|
|
1013
|
-
}
|
|
1014
1118
|
path.classList.add('vector-path');
|
|
1119
|
+
this.boundaryPaths[id].push(path);
|
|
1015
1120
|
group.appendChild(path);
|
|
1016
1121
|
}
|
|
1017
1122
|
}
|
|
@@ -1120,7 +1225,7 @@ class ObstacleLayer extends BaseLayer {
|
|
|
1120
1225
|
}
|
|
1121
1226
|
}
|
|
1122
1227
|
|
|
1123
|
-
var chargingPileImage = "data:image/png;base64,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";
|
|
1228
|
+
var chargingPileImage = "data:image/png;base64,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";
|
|
1124
1229
|
|
|
1125
1230
|
/**
|
|
1126
1231
|
* 充电桩图层
|
|
@@ -1310,7 +1415,7 @@ class SvgElementLayer extends BaseLayer {
|
|
|
1310
1415
|
// 在transformGroup上应用变换:平移到中心,旋转,缩放,然后居中SVG
|
|
1311
1416
|
const transform = [
|
|
1312
1417
|
`translate(${center[0]}, ${center[1]})`,
|
|
1313
|
-
`rotate(${(direction * 180) / Math.PI})`,
|
|
1418
|
+
`rotate(${-(direction * 180) / Math.PI})`,
|
|
1314
1419
|
`scale(${userScale})`,
|
|
1315
1420
|
`translate(${-originalWidth / 2}, ${-originalHeight / 2})`,
|
|
1316
1421
|
].join(' ');
|
|
@@ -1339,7 +1444,6 @@ class SvgElementLayer extends BaseLayer {
|
|
|
1339
1444
|
renderSvgPlaceholder(svgGroup, center, metadata, style) {
|
|
1340
1445
|
const size = (metadata?.scale || 1) * 20;
|
|
1341
1446
|
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
1342
|
-
console.log('style==', style);
|
|
1343
1447
|
rect.setAttribute('x', ((center[0] - size / 2) / 50).toString());
|
|
1344
1448
|
rect.setAttribute('y', ((center[1] - size / 2) / 50).toString());
|
|
1345
1449
|
rect.setAttribute('width', size.toString());
|
|
@@ -1409,9 +1513,10 @@ class VisionOffLayer extends BaseLayer {
|
|
|
1409
1513
|
*/
|
|
1410
1514
|
const BOUNDARY_STYLES = {
|
|
1411
1515
|
lineColor: '#ffffff',
|
|
1412
|
-
fillColor: 'rgba(239, 255, 237, 0.
|
|
1516
|
+
fillColor: 'rgba(239, 255, 237, 0.1)', // 更鲜艳的绿色半透明填充,增强可见性
|
|
1413
1517
|
lineWidth: 2,
|
|
1414
1518
|
opacity: DEFAULT_OPACITIES.FULL,
|
|
1519
|
+
mowingLineColor: 'rgba(99, 216, 174, 1)',
|
|
1415
1520
|
};
|
|
1416
1521
|
const VISION_OFF_AREA_STYLES = {
|
|
1417
1522
|
lineColor: 'rgba(108, 167, 255, 1)',
|
|
@@ -1440,10 +1545,11 @@ const DOODLE_STYLES = {
|
|
|
1440
1545
|
};
|
|
1441
1546
|
const PATH_EDGE_STYLES = {
|
|
1442
1547
|
lineWidth: DEFAULT_LINE_WIDTHS.PATH,
|
|
1443
|
-
opacity: DEFAULT_OPACITIES.
|
|
1444
|
-
edgeLineColor: 'rgba(
|
|
1548
|
+
opacity: DEFAULT_OPACITIES.MEDIUM,
|
|
1549
|
+
edgeLineColor: 'rgba(231, 238, 246)',
|
|
1445
1550
|
transLineColor: 'transparent',
|
|
1446
|
-
|
|
1551
|
+
mowedLineColor: 'rgba(231, 238, 246)',
|
|
1552
|
+
mowingLineColor: 'rgba(123, 200, 187)',
|
|
1447
1553
|
};
|
|
1448
1554
|
const CHANNEL_STYLES = {
|
|
1449
1555
|
lineColor: 'purple',
|
|
@@ -1527,11 +1633,11 @@ function createPathSegmentsByType(list) {
|
|
|
1527
1633
|
let currentSegmentType = null;
|
|
1528
1634
|
for (const currentPoint of list) {
|
|
1529
1635
|
const currentCoord = {
|
|
1530
|
-
x: currentPoint.postureX
|
|
1531
|
-
y:
|
|
1636
|
+
x: currentPoint.postureX,
|
|
1637
|
+
y: currentPoint.postureY
|
|
1532
1638
|
};
|
|
1533
1639
|
if (lastPoint !== null) {
|
|
1534
|
-
// 判断上一个点和当前点是否需要绘制 (
|
|
1640
|
+
// 判断上一个点和当前点是否需要绘制 (iso端逻辑)
|
|
1535
1641
|
const lastShouldDraw = lastPoint.pathType === '00' || lastPoint.pathType === '01' || lastPoint.knifeRotation === '01';
|
|
1536
1642
|
const currentShouldDraw = currentPoint.pathType === '00' || currentPoint.pathType === '01' || currentPoint.knifeRotation === '01';
|
|
1537
1643
|
let segmentType;
|
|
@@ -1551,8 +1657,8 @@ function createPathSegmentsByType(list) {
|
|
|
1551
1657
|
// 开始新段
|
|
1552
1658
|
currentSegment = [
|
|
1553
1659
|
{
|
|
1554
|
-
x: lastPoint.postureX
|
|
1555
|
-
y:
|
|
1660
|
+
x: lastPoint.postureX,
|
|
1661
|
+
y: lastPoint.postureY
|
|
1556
1662
|
},
|
|
1557
1663
|
currentCoord
|
|
1558
1664
|
];
|
|
@@ -1785,26 +1891,7 @@ function generateBoundaryData(mapData, pathData) {
|
|
|
1785
1891
|
}
|
|
1786
1892
|
// 第一步:收集所有TUNNEL数据的connection信息
|
|
1787
1893
|
const connectedBoundaryIds = new Set();
|
|
1788
|
-
//
|
|
1789
|
-
for (const subMap of mapData.sub_maps) {
|
|
1790
|
-
if (!subMap.elements)
|
|
1791
|
-
continue;
|
|
1792
|
-
// 找到该子地图中所有 type 为 TUNNEL 的元素
|
|
1793
|
-
const tunnelElements = subMap.elements.filter(element => element.type === 'TUNNEL');
|
|
1794
|
-
for (const tunnelElement of tunnelElements) {
|
|
1795
|
-
const connection = tunnelElement.connection;
|
|
1796
|
-
if (connection) {
|
|
1797
|
-
// connection可能是单个数字或数组
|
|
1798
|
-
if (Array.isArray(connection)) {
|
|
1799
|
-
connection.forEach(id => connectedBoundaryIds.add(id));
|
|
1800
|
-
}
|
|
1801
|
-
else if (typeof connection === 'number') {
|
|
1802
|
-
connectedBoundaryIds.add(connection);
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
// 1.2 遍历mapData中的tunnels字段
|
|
1894
|
+
// 遍历mapData中的tunnels字段
|
|
1808
1895
|
if (mapData.tunnels && Array.isArray(mapData.tunnels)) {
|
|
1809
1896
|
for (const tunnel of mapData.tunnels) {
|
|
1810
1897
|
const connection = tunnel.connection;
|
|
@@ -1826,6 +1913,8 @@ function generateBoundaryData(mapData, pathData) {
|
|
|
1826
1913
|
continue;
|
|
1827
1914
|
// 每个sub_map的elements是边界坐标,没有sub_map只有一个boundary数据
|
|
1828
1915
|
const boundaryElement = subMap.elements.find(element => element.type === 'BOUNDARY');
|
|
1916
|
+
// 如果当前subMap存在充电桩且充电桩存在tunnel,说明当前subMap中的boundary是初始boundary,这个boundary不为孤立区域
|
|
1917
|
+
const hasTunnelToChargingPile = subMap.elements.some(element => element.type === 'CHARGING_PILE' && element.tunnel);
|
|
1829
1918
|
// 创建基础的 boundary 数据(来自 mapData)
|
|
1830
1919
|
const boundary = {
|
|
1831
1920
|
// 从 BOUNDARY 元素复制属性
|
|
@@ -1835,7 +1924,7 @@ function generateBoundaryData(mapData, pathData) {
|
|
|
1835
1924
|
points: convertPointsFormat(boundaryElement?.points) || [],
|
|
1836
1925
|
type: boundaryElement.type,
|
|
1837
1926
|
// 判断是否为孤立子区域
|
|
1838
|
-
isIsolated: !connectedBoundaryIds.has(boundaryElement.id)
|
|
1927
|
+
isIsolated: hasTunnelToChargingPile ? false : !connectedBoundaryIds.has(boundaryElement.id)
|
|
1839
1928
|
};
|
|
1840
1929
|
// 如果有 pathData,尝试匹配对应的分区数据
|
|
1841
1930
|
if (pathData) {
|
|
@@ -1856,180 +1945,13 @@ function generateBoundaryData(mapData, pathData) {
|
|
|
1856
1945
|
return boundaryData;
|
|
1857
1946
|
}
|
|
1858
1947
|
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
* 对应Java: public static int bytes2Int(byte[] data)
|
|
1867
|
-
*
|
|
1868
|
-
* @param data 源字节数组
|
|
1869
|
-
* @returns 返回int型数
|
|
1870
|
-
*/
|
|
1871
|
-
// public static bytes2Int(data: Uint8Array): number {
|
|
1872
|
-
// return FormatUtils.bytes2IntWithOffset(data, 0, false);
|
|
1873
|
-
// }
|
|
1874
|
-
/**
|
|
1875
|
-
* 从data数组的第position位置开始取4个字节组成int型数
|
|
1876
|
-
* 对应Java: public static int bytes2Int(byte[] data, int offset, boolean bigEndian)
|
|
1877
|
-
*
|
|
1878
|
-
* @param data 源字节数组
|
|
1879
|
-
* @param offset 要组成int型数据的起始位置
|
|
1880
|
-
* @param bigEndian 是否为大端
|
|
1881
|
-
* @returns 返回int型数
|
|
1882
|
-
*/
|
|
1883
|
-
static bytes2Int(data, offset, bigEndian) {
|
|
1884
|
-
return FormatUtils.bytes2IntWidthLength(data, offset, bigEndian, 4);
|
|
1885
|
-
}
|
|
1886
|
-
/**
|
|
1887
|
-
* 从data数组的第position位置开始取几个字节组成int型数
|
|
1888
|
-
* 对应Java: public static int bytes2Int(byte[] data, int offset, boolean bigEndian, int length)
|
|
1889
|
-
*
|
|
1890
|
-
* @param data 源字节数组
|
|
1891
|
-
* @param offset 要组成int型数据的起始位置
|
|
1892
|
-
* @param bigEndian 是否为大端
|
|
1893
|
-
* @param length 取的字节长度
|
|
1894
|
-
* @returns 返回int型数
|
|
1895
|
-
*/
|
|
1896
|
-
static bytes2IntWidthLength(data, offset, bigEndian, length) {
|
|
1897
|
-
if (!data || offset < 0 || offset > data.length) {
|
|
1898
|
-
return 0;
|
|
1899
|
-
}
|
|
1900
|
-
let result = 0;
|
|
1901
|
-
try {
|
|
1902
|
-
// 创建DataView来处理字节序
|
|
1903
|
-
const buffer = new ArrayBuffer(length);
|
|
1904
|
-
const view = new DataView(buffer);
|
|
1905
|
-
// 将指定长度的数据复制到buffer中
|
|
1906
|
-
const tempArray = new Uint8Array(buffer);
|
|
1907
|
-
for (let i = 0; i < length && offset + i < data.length; i++) {
|
|
1908
|
-
tempArray[i] = data[offset + i];
|
|
1909
|
-
}
|
|
1910
|
-
// 根据字节序读取数据(使用有符号整数,与Java保持一致)
|
|
1911
|
-
if (length === 1) {
|
|
1912
|
-
result = view.getInt8(0);
|
|
1913
|
-
}
|
|
1914
|
-
else if (length === 2) {
|
|
1915
|
-
result = view.getInt16(0, !bigEndian); // DataView的littleEndian参数与bigEndian相反
|
|
1916
|
-
}
|
|
1917
|
-
else if (length === 4) {
|
|
1918
|
-
result = view.getInt32(0, !bigEndian); // 改为 getInt32,返回有符号整数
|
|
1919
|
-
}
|
|
1920
|
-
else {
|
|
1921
|
-
// 对于其他长度,手动处理
|
|
1922
|
-
result = FormatUtils.manualBytes2Int(data, offset, bigEndian, length);
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
catch (e) {
|
|
1926
|
-
// console.error(
|
|
1927
|
-
// `${FormatUtils.TAG}: bytes2Int: Exception : data = ${data}, offset = ${offset}`,
|
|
1928
|
-
// e
|
|
1929
|
-
// );
|
|
1930
|
-
// console.log(
|
|
1931
|
-
// `${FormatUtils.TAG}: bytes2Int: Exception = ${e instanceof Error ? e.message : e}`
|
|
1932
|
-
// );
|
|
1933
|
-
}
|
|
1934
|
-
return result;
|
|
1935
|
-
}
|
|
1936
|
-
/**
|
|
1937
|
-
* 手动处理字节到整数的转换(用于非标准长度)
|
|
1938
|
-
*
|
|
1939
|
-
* @param data 源字节数组
|
|
1940
|
-
* @param offset 起始位置
|
|
1941
|
-
* @param bigEndian 是否为大端
|
|
1942
|
-
* @param length 字节长度
|
|
1943
|
-
* @returns 转换后的整数
|
|
1944
|
-
*/
|
|
1945
|
-
static manualBytes2Int(data, offset, bigEndian, length) {
|
|
1946
|
-
let result = 0;
|
|
1947
|
-
if (bigEndian) {
|
|
1948
|
-
// 大端序:最高位字节在最低地址
|
|
1949
|
-
for (let i = 0; i < length && offset + i < data.length; i++) {
|
|
1950
|
-
result = (result << 8) | (data[offset + i] & 0xff);
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
else {
|
|
1954
|
-
// 小端序:最低位字节在最低地址
|
|
1955
|
-
for (let i = length - 1; i >= 0; i--) {
|
|
1956
|
-
if (offset + i < data.length) {
|
|
1957
|
-
result = (result << 8) | (data[offset + i] & 0xff);
|
|
1958
|
-
}
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
return result;
|
|
1962
|
-
}
|
|
1963
|
-
/**
|
|
1964
|
-
* 将字节数组转换为十六进制字符串
|
|
1965
|
-
* 对应Java: public static String bytesToHex(byte[] bytes)
|
|
1966
|
-
*
|
|
1967
|
-
* @param bytes 字节数组
|
|
1968
|
-
* @returns 十六进制字符串
|
|
1969
|
-
*/
|
|
1970
|
-
static bytesToHex(bytes) {
|
|
1971
|
-
if (!bytes) {
|
|
1972
|
-
return 'null';
|
|
1973
|
-
}
|
|
1974
|
-
const hexArray = '0123456789ABCDEF'.split('');
|
|
1975
|
-
const hexChars = [];
|
|
1976
|
-
for (let j = 0; j < bytes.length; j++) {
|
|
1977
|
-
const v = bytes[j] & 0xff;
|
|
1978
|
-
hexChars.push(hexArray[v >>> 4]);
|
|
1979
|
-
hexChars.push(hexArray[v & 0x0f]);
|
|
1980
|
-
}
|
|
1981
|
-
return hexChars.join('');
|
|
1982
|
-
}
|
|
1983
|
-
/**
|
|
1984
|
-
* 将十六进制字符串转换为字节数组
|
|
1985
|
-
* 对应Java: public static byte[] hexStringToByteArray(String s)
|
|
1986
|
-
*
|
|
1987
|
-
* @param s 十六进制字符串
|
|
1988
|
-
* @returns 字节数组
|
|
1989
|
-
*/
|
|
1990
|
-
static hexStringToByteArray(s) {
|
|
1991
|
-
if (!s || s.length <= 0) {
|
|
1992
|
-
return null;
|
|
1993
|
-
}
|
|
1994
|
-
s = s.trim();
|
|
1995
|
-
const len = s.length;
|
|
1996
|
-
const data = new Uint8Array(len / 2);
|
|
1997
|
-
for (let i = 0; i < len - 1; i += 2) {
|
|
1998
|
-
try {
|
|
1999
|
-
const high = parseInt(s.charAt(i), 16);
|
|
2000
|
-
const low = parseInt(s.charAt(i + 1), 16);
|
|
2001
|
-
data[i / 2] = (high << 4) + low;
|
|
2002
|
-
}
|
|
2003
|
-
catch (e) {
|
|
2004
|
-
console.log('hexStringToByteArray: ' + e);
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
return data;
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
2010
|
-
FormatUtils.TAG = 'FormatUtils';
|
|
2011
|
-
// 使用示例
|
|
2012
|
-
// export function example() {
|
|
2013
|
-
// // 示例1:从字节数组开头读取4字节整数(小端序)
|
|
2014
|
-
// const data1 = new Uint8Array([0x12, 0x34, 0x56, 0x78]);
|
|
2015
|
-
// const result1 = FormatUtils.bytes2Int(data1); // 应该返回 0x78563412
|
|
2016
|
-
// // 示例2:从指定位置读取4字节整数(大端序)
|
|
2017
|
-
// const data2 = new Uint8Array([0x00, 0x00, 0x12, 0x34, 0x56, 0x78]);
|
|
2018
|
-
// const result2 = FormatUtils.bytes2Int(data2, 2, true); // 应该返回 0x12345678
|
|
2019
|
-
// // 示例3:读取2字节整数
|
|
2020
|
-
// const data3 = new Uint8Array([0x12, 0x34]);
|
|
2021
|
-
// const result3 = FormatUtils.bytes2Int(data3, 0, false, 2); // 应该返回 0x3412
|
|
2022
|
-
// console.log(`Result1: 0x${result1.toString(16)}`);
|
|
2023
|
-
// console.log(`Result2: 0x${result2.toString(16)}`);
|
|
2024
|
-
// console.log(`Result3: 0x${result3.toString(16)}`);
|
|
2025
|
-
// // 示例4:十六进制字符串转换
|
|
2026
|
-
// const hexString = "12345678";
|
|
2027
|
-
// const byteArray = FormatUtils.hexStringToByteArray(hexString);
|
|
2028
|
-
// const backToHex = FormatUtils.bytesToHex(byteArray);
|
|
2029
|
-
// console.log(`Original: ${hexString}`);
|
|
2030
|
-
// console.log(`To bytes: ${byteArray}`);
|
|
2031
|
-
// console.log(`Back to hex: ${backToHex}`);
|
|
2032
|
-
// }
|
|
1948
|
+
var RealTimeDataType;
|
|
1949
|
+
(function (RealTimeDataType) {
|
|
1950
|
+
RealTimeDataType[RealTimeDataType["LOCATION"] = 1] = "LOCATION";
|
|
1951
|
+
RealTimeDataType[RealTimeDataType["PROCESS"] = 2] = "PROCESS";
|
|
1952
|
+
RealTimeDataType[RealTimeDataType["PARTITION"] = 3] = "PARTITION";
|
|
1953
|
+
RealTimeDataType[RealTimeDataType["STATUS"] = 4] = "STATUS";
|
|
1954
|
+
})(RealTimeDataType || (RealTimeDataType = {}));
|
|
2033
1955
|
|
|
2034
1956
|
/**
|
|
2035
1957
|
* 射线法判断点是否在多边形内部
|
|
@@ -2125,50 +2047,6 @@ const getPartitionId = (partitionBoundary, postureX, postureY) => {
|
|
|
2125
2047
|
})?.id;
|
|
2126
2048
|
return partitionId;
|
|
2127
2049
|
};
|
|
2128
|
-
/**
|
|
2129
|
-
*
|
|
2130
|
-
* 支持分区割草后的解析,加入了分区数量、分区列表
|
|
2131
|
-
* 8 割草路径类型
|
|
2132
|
-
* 4 割草启动类型 0:自动 1:手动
|
|
2133
|
-
* 4 当前割草边界id
|
|
2134
|
-
* 4 当前边界割草百分比 比如9800----98%
|
|
2135
|
-
* @returns
|
|
2136
|
-
*/
|
|
2137
|
-
const parseMapWorkPosition = (mapWorkPosition) => {
|
|
2138
|
-
let isMowing = false;
|
|
2139
|
-
let mowStartType = null;
|
|
2140
|
-
let currentMowBoundaryId = null;
|
|
2141
|
-
let currentMowProgress = null;
|
|
2142
|
-
const bytes = new Uint8Array(mapWorkPosition.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) || []);
|
|
2143
|
-
if (mapWorkPosition.length >= 8) {
|
|
2144
|
-
// 以下两种状态认为是割草中
|
|
2145
|
-
// 1. action为8,且subAction为6
|
|
2146
|
-
// 2. action为5
|
|
2147
|
-
const action = FormatUtils.bytes2Int(bytes, 0, true);
|
|
2148
|
-
if (action === ACTION_BOUNDARY_TASK) {
|
|
2149
|
-
const subAction = FormatUtils.bytes2Int(bytes, 4, true);
|
|
2150
|
-
if (subAction === ACTION_BLOCK_TRANSFER) {
|
|
2151
|
-
// action=8且subAction=6:边界任务中的块转移
|
|
2152
|
-
isMowing = true;
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
else if (action === ACTION_BLOCK_COVER) {
|
|
2156
|
-
// action=5:块覆盖割草
|
|
2157
|
-
isMowing = true;
|
|
2158
|
-
}
|
|
2159
|
-
if (mapWorkPosition.length >= 16) {
|
|
2160
|
-
mowStartType = FormatUtils.bytes2Int(bytes, 8, true);
|
|
2161
|
-
currentMowBoundaryId = FormatUtils.bytes2Int(bytes, 12, true);
|
|
2162
|
-
currentMowProgress = FormatUtils.bytes2Int(bytes, 16, true);
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
return {
|
|
2166
|
-
isMowing,
|
|
2167
|
-
mowStartType,
|
|
2168
|
-
currentMowBoundaryId,
|
|
2169
|
-
currentMowProgress,
|
|
2170
|
-
};
|
|
2171
|
-
};
|
|
2172
2050
|
/**
|
|
2173
2051
|
* 处理实时数据的消息,这里的实时数据消息有两种,一种是实时轨迹,一种是割草进度,其中这两种下发的时间频次不一样
|
|
2174
2052
|
* 实时轨迹的路径需要依靠割草进度时候的割草状态判断,目前只能根据上一次获取到的割草进度的状态来处理,如果一开始没有割草的状态,则默认为不割草,后续会根据割草进度来更新
|
|
@@ -2176,7 +2054,7 @@ const parseMapWorkPosition = (mapWorkPosition) => {
|
|
|
2176
2054
|
* @param param0
|
|
2177
2055
|
* @returns
|
|
2178
2056
|
*/
|
|
2179
|
-
const
|
|
2057
|
+
const handleMultipleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundary, }) => {
|
|
2180
2058
|
// 先将数据进行倒排,这样好插入数据
|
|
2181
2059
|
if (realTimeData.length > 0) {
|
|
2182
2060
|
realTimeData.reverse();
|
|
@@ -2192,7 +2070,6 @@ const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundar
|
|
|
2192
2070
|
// 割草轨迹
|
|
2193
2071
|
const { postureX, postureY, vehicleState } = item;
|
|
2194
2072
|
const currentPartitionId = getPartitionId(partitionBoundary, Number(postureX), Number(postureY));
|
|
2195
|
-
console.log('currentPartitionId===', currentPartitionId);
|
|
2196
2073
|
if (currentPartitionId && newPathData?.[currentPartitionId]) {
|
|
2197
2074
|
const currentPathData = newPathData[currentPartitionId];
|
|
2198
2075
|
newPathData[currentPartitionId] = {
|
|
@@ -2204,7 +2081,7 @@ const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundar
|
|
|
2204
2081
|
postureY: Number(postureY),
|
|
2205
2082
|
knifeRotation: mowingStatus && vehicleState === RobotStatus.MOWING ? '01' : '00', // "knifeRotation": "01",//刀盘是否转动 00-否 01-是
|
|
2206
2083
|
// knifeRotation: '01', // "knifeRotation": "01",//刀盘是否转动 00-否 01-是
|
|
2207
|
-
pathType: '
|
|
2084
|
+
pathType: '', //"pathType": "01",//路径类型 : 00-巡边 01-弓字型割草 02-地图测试 03-转移路径 04-避障路径 05-恢复/脱困路径
|
|
2208
2085
|
partitionId: currentPartitionId.toString(), // TODO:不知道为什么这里的id需要是字符串类型?
|
|
2209
2086
|
},
|
|
2210
2087
|
],
|
|
@@ -2213,21 +2090,20 @@ const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundar
|
|
|
2213
2090
|
}
|
|
2214
2091
|
else if (item.type === REAL_TIME_DATA_TYPE.PROCESS) {
|
|
2215
2092
|
// 割草进度
|
|
2216
|
-
const {
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
}
|
|
2093
|
+
const { action, subAction, currentMowBoundary, currentMowProgress } = item;
|
|
2094
|
+
// 设置状态
|
|
2095
|
+
if ((action === ACTION_BOUNDARY_TASK && subAction && subAction === ACTION_BLOCK_TRANSFER) ||
|
|
2096
|
+
action === ACTION_BLOCK_COVER) {
|
|
2097
|
+
mowingStatus = true;
|
|
2098
|
+
}
|
|
2099
|
+
else {
|
|
2100
|
+
mowingStatus = false;
|
|
2101
|
+
}
|
|
2102
|
+
const currentPartitionId = currentMowBoundary ? currentMowBoundary.toString() : null;
|
|
2103
|
+
if (currentMowProgress && currentPartitionId && newPathData?.[currentPartitionId]) {
|
|
2104
|
+
newPathData[currentPartitionId].partitionPercentage = currentMowProgress / 100;
|
|
2105
|
+
newPathData[currentPartitionId].finishedArea =
|
|
2106
|
+
(newPathData[currentPartitionId].area * currentMowProgress) / 10000;
|
|
2231
2107
|
}
|
|
2232
2108
|
}
|
|
2233
2109
|
});
|
|
@@ -2236,6 +2112,39 @@ const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionBoundar
|
|
|
2236
2112
|
isMowing: mowingStatus,
|
|
2237
2113
|
};
|
|
2238
2114
|
};
|
|
2115
|
+
/**
|
|
2116
|
+
* 根据实时数据,获取到割草状态
|
|
2117
|
+
* @param realTimeData 实时数据
|
|
2118
|
+
* @param isMowing 上一次的割草状态
|
|
2119
|
+
* @returns 新的割草状态
|
|
2120
|
+
*/
|
|
2121
|
+
const getProcessMowingDataFromRealTimeData = ({ realTimeData, isMowing, pathData, }) => {
|
|
2122
|
+
let newMowingStatus = isMowing;
|
|
2123
|
+
let newPathData = pathData || {};
|
|
2124
|
+
// 找到返回的第一个实时进度的点
|
|
2125
|
+
const firstProcessData = realTimeData.find((item) => item.type === RealTimeDataType.PROCESS);
|
|
2126
|
+
if (firstProcessData) {
|
|
2127
|
+
// console.log('firstProcessData==', firstProcessData);
|
|
2128
|
+
const { action, subAction, currentMowBoundary, currentMowProgress } = firstProcessData;
|
|
2129
|
+
// 设置状态
|
|
2130
|
+
if ((action === ACTION_BOUNDARY_TASK && subAction && subAction === ACTION_BLOCK_TRANSFER) ||
|
|
2131
|
+
action === ACTION_BLOCK_COVER) {
|
|
2132
|
+
newMowingStatus = true;
|
|
2133
|
+
}
|
|
2134
|
+
else {
|
|
2135
|
+
newMowingStatus = false;
|
|
2136
|
+
}
|
|
2137
|
+
if (currentMowBoundary && newPathData?.[currentMowBoundary]) {
|
|
2138
|
+
newPathData[currentMowBoundary].partitionPercentage = currentMowProgress / 100;
|
|
2139
|
+
newPathData[currentMowBoundary].finishedArea =
|
|
2140
|
+
(newPathData[currentMowBoundary].area * currentMowProgress) / 10000;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
return {
|
|
2144
|
+
isMowing: newMowingStatus,
|
|
2145
|
+
pathData: newPathData,
|
|
2146
|
+
};
|
|
2147
|
+
};
|
|
2239
2148
|
|
|
2240
2149
|
var iMower = "data:image/png;base64,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";
|
|
2241
2150
|
|
|
@@ -2291,8 +2200,10 @@ function getNoPositionMowerImageByModal(mowerModal) {
|
|
|
2291
2200
|
}
|
|
2292
2201
|
return iNoPosition;
|
|
2293
2202
|
}
|
|
2294
|
-
function getMowerImage(positonConfig) {
|
|
2295
|
-
|
|
2203
|
+
function getMowerImage(positonConfig, modelType) {
|
|
2204
|
+
if (!positonConfig)
|
|
2205
|
+
return '';
|
|
2206
|
+
const model = modelType?.toLowerCase() || 'i';
|
|
2296
2207
|
const state = positonConfig.vehicleState;
|
|
2297
2208
|
const mowerImage = getMowerImageByModal(model);
|
|
2298
2209
|
const disabledImage = getDisabledMowerImageByModal(model);
|
|
@@ -2329,9 +2240,9 @@ function isInvalidPosition(positonConfig) {
|
|
|
2329
2240
|
}
|
|
2330
2241
|
function isOutOfRange(positonConfig) {
|
|
2331
2242
|
return (positonConfig.postureX != null &&
|
|
2332
|
-
Math.abs(positonConfig.postureX) > 1000
|
|
2243
|
+
Math.abs(positonConfig.postureX) > 1000 &&
|
|
2333
2244
|
positonConfig.postureY != null &&
|
|
2334
|
-
Math.abs(positonConfig.postureY) > 1000
|
|
2245
|
+
Math.abs(positonConfig.postureY) > 1000);
|
|
2335
2246
|
}
|
|
2336
2247
|
|
|
2337
2248
|
/** Detect free variable `global` from Node.js. */
|
|
@@ -4397,7 +4308,7 @@ function cloneBuffer(buffer, isDeep) {
|
|
|
4397
4308
|
}
|
|
4398
4309
|
|
|
4399
4310
|
/** Built-in value references. */
|
|
4400
|
-
var Uint8Array
|
|
4311
|
+
var Uint8Array = root.Uint8Array;
|
|
4401
4312
|
|
|
4402
4313
|
/**
|
|
4403
4314
|
* Creates a clone of `arrayBuffer`.
|
|
@@ -4408,7 +4319,7 @@ var Uint8Array$1 = root.Uint8Array;
|
|
|
4408
4319
|
*/
|
|
4409
4320
|
function cloneArrayBuffer(arrayBuffer) {
|
|
4410
4321
|
var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
|
|
4411
|
-
new Uint8Array
|
|
4322
|
+
new Uint8Array(result).set(new Uint8Array(arrayBuffer));
|
|
4412
4323
|
return result;
|
|
4413
4324
|
}
|
|
4414
4325
|
|
|
@@ -4759,6 +4670,36 @@ var merge = createAssigner(function(object, source, srcIndex) {
|
|
|
4759
4670
|
*/
|
|
4760
4671
|
var round = createRound('round');
|
|
4761
4672
|
|
|
4673
|
+
/**
|
|
4674
|
+
* 工具模块类型定义
|
|
4675
|
+
*/
|
|
4676
|
+
/**
|
|
4677
|
+
* 路径段类型枚举
|
|
4678
|
+
*/
|
|
4679
|
+
var PathSegmentType;
|
|
4680
|
+
(function (PathSegmentType) {
|
|
4681
|
+
PathSegmentType["EDGE"] = "edge";
|
|
4682
|
+
PathSegmentType["MOWING"] = "mowing";
|
|
4683
|
+
PathSegmentType["TRANS"] = "trans";
|
|
4684
|
+
})(PathSegmentType || (PathSegmentType = {}));
|
|
4685
|
+
/**
|
|
4686
|
+
* 单位类型枚举
|
|
4687
|
+
*/
|
|
4688
|
+
var UnitsType;
|
|
4689
|
+
(function (UnitsType) {
|
|
4690
|
+
UnitsType["Metric"] = "Metric";
|
|
4691
|
+
UnitsType["Imperial"] = "Imperial";
|
|
4692
|
+
})(UnitsType || (UnitsType = {}));
|
|
4693
|
+
/**
|
|
4694
|
+
* 面积单位类型枚举
|
|
4695
|
+
*/
|
|
4696
|
+
var UnitsAreaType;
|
|
4697
|
+
(function (UnitsAreaType) {
|
|
4698
|
+
UnitsAreaType["SQUARE_METER"] = "m\u00B2";
|
|
4699
|
+
UnitsAreaType["SQUARE_FOOT"] = "ft\u00B2";
|
|
4700
|
+
UnitsAreaType["ACRE"] = "ac";
|
|
4701
|
+
})(UnitsAreaType || (UnitsAreaType = {}));
|
|
4702
|
+
|
|
4762
4703
|
/**
|
|
4763
4704
|
* 默认航向相对于canvas的偏移角度: 航向默认是东
|
|
4764
4705
|
*/
|
|
@@ -4786,10 +4727,6 @@ function radToDegree(radian) {
|
|
|
4786
4727
|
function distance(x1, y1, x2, y2) {
|
|
4787
4728
|
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
|
|
4788
4729
|
}
|
|
4789
|
-
// 计算前进方向和车头的夹角
|
|
4790
|
-
function calAngle(x1, y1, x2, y2) {
|
|
4791
|
-
return Math.atan2((y2 - y1), (x2 - x1));
|
|
4792
|
-
}
|
|
4793
4730
|
const mathRound = (value, decimals = 2) => {
|
|
4794
4731
|
return Number.isInteger(value) ? value : round(value, decimals);
|
|
4795
4732
|
};
|
|
@@ -4836,17 +4773,6 @@ function formatNumberWithMetricPrefix(value, round = true, decimals = 2) {
|
|
|
4836
4773
|
return `${mathFn(value / 1000000000, decimals)}B`;
|
|
4837
4774
|
}
|
|
4838
4775
|
}
|
|
4839
|
-
var UnitsType;
|
|
4840
|
-
(function (UnitsType) {
|
|
4841
|
-
UnitsType["Metric"] = "metric";
|
|
4842
|
-
UnitsType["Imperial"] = "imperial";
|
|
4843
|
-
})(UnitsType || (UnitsType = {}));
|
|
4844
|
-
var UnitsAreaType;
|
|
4845
|
-
(function (UnitsAreaType) {
|
|
4846
|
-
UnitsAreaType["SQUARE_METER"] = "m\u00B2";
|
|
4847
|
-
UnitsAreaType["SQUARE_FOOT"] = "ft\u00B2";
|
|
4848
|
-
UnitsAreaType["ACRE"] = "ac";
|
|
4849
|
-
})(UnitsAreaType || (UnitsAreaType = {}));
|
|
4850
4776
|
/**
|
|
4851
4777
|
* 转换割草面积的方法
|
|
4852
4778
|
* @param area 面积数值(单位:m²)
|
|
@@ -5035,6 +4961,19 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
5035
4961
|
this.scale = 1;
|
|
5036
4962
|
this.level = LAYER_LEVELS.BOUNDARY_BORDER; // 中等层级
|
|
5037
4963
|
this.type = LAYER_DEFAULT_TYPE.BOUNDARY_BORDER;
|
|
4964
|
+
this.boudaryBorderPaths = {};
|
|
4965
|
+
this.mowingBoundarys = [];
|
|
4966
|
+
}
|
|
4967
|
+
/**
|
|
4968
|
+
* 设置当前割草任务的边界
|
|
4969
|
+
*/
|
|
4970
|
+
setMowingBoundarys(mowingBoundarys) {
|
|
4971
|
+
if (!mowingBoundarys) {
|
|
4972
|
+
this.mowingBoundarys = this.elements?.map(item => item?.originalData?.id);
|
|
4973
|
+
}
|
|
4974
|
+
else {
|
|
4975
|
+
this.mowingBoundarys = mowingBoundarys;
|
|
4976
|
+
}
|
|
5038
4977
|
}
|
|
5039
4978
|
/**
|
|
5040
4979
|
* SVG渲染方法
|
|
@@ -5055,7 +4994,8 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
5055
4994
|
* 渲染边界边框
|
|
5056
4995
|
*/
|
|
5057
4996
|
renderBoundaryBorder(svgGroup, element) {
|
|
5058
|
-
const { coordinates, style } = element;
|
|
4997
|
+
const { coordinates, style, originalData } = element;
|
|
4998
|
+
const { id } = originalData || {};
|
|
5059
4999
|
if (coordinates.length < 2)
|
|
5060
5000
|
return;
|
|
5061
5001
|
// 1. 先遍历所有的coordinates,把所有点分为若干段的path
|
|
@@ -5066,20 +5006,21 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
5066
5006
|
return;
|
|
5067
5007
|
if (segment.type === 2) {
|
|
5068
5008
|
// type=2: 直接添加到svgGroup中
|
|
5069
|
-
this.createDirectPath(svgGroup, segment.points, style);
|
|
5009
|
+
this.createDirectPath(svgGroup, segment.points, style, id);
|
|
5070
5010
|
}
|
|
5071
5011
|
else if (segment.type === 1) {
|
|
5072
5012
|
// type=1: 使用PathMeasure逻辑生成平行路径
|
|
5073
5013
|
// this.createDirectPath(svgGroup, segment.points, style);
|
|
5074
|
-
this.createParallelPathsWithMeasure(svgGroup, segment.points, style);
|
|
5014
|
+
this.createParallelPathsWithMeasure(svgGroup, segment.points, style, id);
|
|
5075
5015
|
}
|
|
5076
5016
|
});
|
|
5077
5017
|
}
|
|
5078
5018
|
/**
|
|
5079
5019
|
* 创建直接路径(type=2)
|
|
5080
5020
|
*/
|
|
5081
|
-
createDirectPath(svgGroup, points, style) {
|
|
5082
|
-
const
|
|
5021
|
+
createDirectPath(svgGroup, points, style, id) {
|
|
5022
|
+
const isMowing = this.mowingBoundarys.includes(Number(id));
|
|
5023
|
+
const strokeColor = isMowing ? style.mowingLineColor : style.lineColor;
|
|
5083
5024
|
const lineWidth = dp2px(style.lineWidth || 3);
|
|
5084
5025
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
5085
5026
|
// 构建路径数据
|
|
@@ -5102,13 +5043,18 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
5102
5043
|
path.setAttribute('opacity', (style.opacity || 1).toString());
|
|
5103
5044
|
path.setAttribute('vector-effect', 'non-scaling-stroke');
|
|
5104
5045
|
path.classList.add('vector-boundary-solid');
|
|
5046
|
+
if (!this.boudaryBorderPaths[id]) {
|
|
5047
|
+
this.boudaryBorderPaths[id] = [];
|
|
5048
|
+
}
|
|
5049
|
+
this.boudaryBorderPaths[id].push(path);
|
|
5105
5050
|
svgGroup.appendChild(path);
|
|
5106
5051
|
}
|
|
5107
5052
|
/**
|
|
5108
5053
|
* 使用PathMeasure逻辑创建平行路径(type=1)
|
|
5109
5054
|
*/
|
|
5110
|
-
createParallelPathsWithMeasure(svgGroup, points, style) {
|
|
5111
|
-
const
|
|
5055
|
+
createParallelPathsWithMeasure(svgGroup, points, style, id) {
|
|
5056
|
+
const isMowing = this.mowingBoundarys.includes(Number(id));
|
|
5057
|
+
const strokeColor = isMowing ? style.mowingLineColor : style.lineColor;
|
|
5112
5058
|
const lineWidth = dp2px(style.lineWidth || 3);
|
|
5113
5059
|
// 获取当前SVG的缩放级别,计算固定屏幕像素间距
|
|
5114
5060
|
const fixedScreenDistance = lineWidth; // 固定的屏幕像素距离
|
|
@@ -5131,6 +5077,10 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
5131
5077
|
// 或者可以根据当前缩放级别动态计算dash array
|
|
5132
5078
|
path.style.strokeDasharray = `${lineWidth}px ${lineWidth * 2}px`;
|
|
5133
5079
|
path.classList.add(`vector-boundary-parallel-${index + 1}`);
|
|
5080
|
+
if (!this.boudaryBorderPaths[id]) {
|
|
5081
|
+
this.boudaryBorderPaths[id] = [];
|
|
5082
|
+
}
|
|
5083
|
+
this.boudaryBorderPaths[id].push(path);
|
|
5134
5084
|
svgGroup.appendChild(path);
|
|
5135
5085
|
});
|
|
5136
5086
|
}
|
|
@@ -5220,6 +5170,9 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
5220
5170
|
}
|
|
5221
5171
|
return segments;
|
|
5222
5172
|
}
|
|
5173
|
+
resetPaths() {
|
|
5174
|
+
this.boudaryBorderPaths = {};
|
|
5175
|
+
}
|
|
5223
5176
|
}
|
|
5224
5177
|
|
|
5225
5178
|
var antennaOneOnline = "data:image/png;base64,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";
|
|
@@ -5432,7 +5385,7 @@ class DrawLayer extends BaseLayer {
|
|
|
5432
5385
|
this.pointLayer,
|
|
5433
5386
|
this.svgElementLayer,
|
|
5434
5387
|
this.visionOffLayer,
|
|
5435
|
-
];
|
|
5388
|
+
]?.filter((layer) => layer.getElements().length > 0);
|
|
5436
5389
|
}
|
|
5437
5390
|
getPathLayers() {
|
|
5438
5391
|
return this.pathLayer;
|
|
@@ -5447,6 +5400,14 @@ class BoundaryDataBuilder {
|
|
|
5447
5400
|
* 创建边界元素数据
|
|
5448
5401
|
*/
|
|
5449
5402
|
static create(type, coordinates, style) {
|
|
5403
|
+
const len = coordinates?.length || 0;
|
|
5404
|
+
const firstPoint = coordinates?.[0];
|
|
5405
|
+
const lastPoint = coordinates?.[len - 1];
|
|
5406
|
+
const isClosed = firstPoint?.[0] === lastPoint?.[0] && firstPoint?.[1] === lastPoint?.[1];
|
|
5407
|
+
// 如果地图没有闭合,则手动新增闭合点,避免border最后一部分没有闭合的情况
|
|
5408
|
+
if (!isClosed) {
|
|
5409
|
+
coordinates.push([firstPoint?.[0], firstPoint?.[1], lastPoint?.[2]]);
|
|
5410
|
+
}
|
|
5450
5411
|
return {
|
|
5451
5412
|
type,
|
|
5452
5413
|
coordinates,
|
|
@@ -5761,7 +5722,6 @@ class MapDataProcessor {
|
|
|
5761
5722
|
if (mapData.vision_off_areas && mapData.vision_off_areas.length > 0) {
|
|
5762
5723
|
allElements.push(...mapData.vision_off_areas);
|
|
5763
5724
|
}
|
|
5764
|
-
console.log('allElements', allElements);
|
|
5765
5725
|
// 按照元素类型分组并设置不同的层级
|
|
5766
5726
|
return this.createLayeredMapData(allElements);
|
|
5767
5727
|
}
|
|
@@ -5850,8 +5810,13 @@ class MapDataProcessor {
|
|
|
5850
5810
|
// 为ObstacleData创建兼容的MapElement接口
|
|
5851
5811
|
const mapElement = element;
|
|
5852
5812
|
const obstacleElement = ObstacleDataBuilder.fromMapElement(mapElement, this.mapConfig.obstacle);
|
|
5853
|
-
if (obstacleElement)
|
|
5813
|
+
if (obstacleElement) {
|
|
5854
5814
|
result.push(obstacleElement);
|
|
5815
|
+
const { addObstacles } = useSubBoundaryBorderStore.getState();
|
|
5816
|
+
addObstacles(`obstacle-${obstacleElement.originalData.id}`, {
|
|
5817
|
+
...obstacleElement,
|
|
5818
|
+
});
|
|
5819
|
+
}
|
|
5855
5820
|
}
|
|
5856
5821
|
catch (error) {
|
|
5857
5822
|
console.warn(`Error processing OBSTACLE element:`, element, error);
|
|
@@ -5912,9 +5877,13 @@ class MapDataProcessor {
|
|
|
5912
5877
|
element.direction !== undefined) {
|
|
5913
5878
|
const mapElement = element;
|
|
5914
5879
|
const svgElement = SvgElementDataBuilder.fromMapElement(mapElement, this.mapConfig.doodle);
|
|
5915
|
-
|
|
5916
|
-
if (svgElement)
|
|
5880
|
+
if (svgElement) {
|
|
5917
5881
|
result.push(svgElement);
|
|
5882
|
+
const { addSvgElements } = useSubBoundaryBorderStore.getState();
|
|
5883
|
+
addSvgElements(`time-limit-obstacle-${svgElement.originalData.id}`, {
|
|
5884
|
+
...svgElement,
|
|
5885
|
+
});
|
|
5886
|
+
}
|
|
5918
5887
|
}
|
|
5919
5888
|
// 如果有points数据,按传统方式绘制
|
|
5920
5889
|
else if ('points' in element &&
|
|
@@ -5923,8 +5892,13 @@ class MapDataProcessor {
|
|
|
5923
5892
|
element.points.length >= 3) {
|
|
5924
5893
|
const mapElement = element;
|
|
5925
5894
|
const polygonElement = ObstacleDataBuilder.createTimeLimitObstacle(mapElement, this.mapConfig.obstacle);
|
|
5926
|
-
if (polygonElement)
|
|
5895
|
+
if (polygonElement) {
|
|
5927
5896
|
result.push(polygonElement);
|
|
5897
|
+
const { addObstacles } = useSubBoundaryBorderStore.getState();
|
|
5898
|
+
addObstacles(`time-limit-obstacle-${polygonElement.originalData.id}`, {
|
|
5899
|
+
...polygonElement,
|
|
5900
|
+
});
|
|
5901
|
+
}
|
|
5928
5902
|
}
|
|
5929
5903
|
}
|
|
5930
5904
|
catch (error) {
|
|
@@ -5969,35 +5943,15 @@ class PathDataBuilder {
|
|
|
5969
5943
|
style,
|
|
5970
5944
|
};
|
|
5971
5945
|
}
|
|
5972
|
-
// /**
|
|
5973
|
-
// * 创建隧道元素数据
|
|
5974
|
-
// */
|
|
5975
|
-
// static createTunnel(element: MapElement): DrawElement | null {
|
|
5976
|
-
// const convertedPoints = convertPointsFormat(element.tunnel?.points || element.points);
|
|
5977
|
-
// if (!convertedPoints || convertedPoints.length < 2) return null;
|
|
5978
|
-
// return this.create(convertedPoints, DEFAULT_STYLES.PATH_EDGE);
|
|
5979
|
-
// }
|
|
5980
|
-
// /**
|
|
5981
|
-
// * 创建充电桩内部隧道路径(用于CHARGING_PILE元素中的tunnel.points)
|
|
5982
|
-
// */
|
|
5983
|
-
// static createChargingPileTunnelPath(element: MapElement): DrawElement | null {
|
|
5984
|
-
// if (!element.tunnel || !element.tunnel.points) return null;
|
|
5985
|
-
// const convertedTunnelPoints = convertPointsFormat(element.tunnel.points);
|
|
5986
|
-
// if (!convertedTunnelPoints || convertedTunnelPoints.length < 2) return null;
|
|
5987
|
-
// return this.create(convertedTunnelPoints, {
|
|
5988
|
-
// strokeColor: DEFAULT_STYLES.CHANNEL.strokeColor,
|
|
5989
|
-
// lineWidth: 2,
|
|
5990
|
-
// opacity: 1.0
|
|
5991
|
-
// });
|
|
5992
|
-
// }
|
|
5993
5946
|
/**
|
|
5994
5947
|
* 创建边缘路径
|
|
5995
5948
|
*/
|
|
5996
5949
|
static createEdgePath(points, config) {
|
|
5997
|
-
const
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
5950
|
+
const convertedPoints = convertPointsFormat(points);
|
|
5951
|
+
const drawElement = this.create(convertedPoints, {
|
|
5952
|
+
lineColor: config?.color,
|
|
5953
|
+
lineWidth: config?.lineWidth,
|
|
5954
|
+
opacity: config?.opacity,
|
|
6001
5955
|
});
|
|
6002
5956
|
drawElement.originalData = points;
|
|
6003
5957
|
return drawElement;
|
|
@@ -6006,10 +5960,11 @@ class PathDataBuilder {
|
|
|
6006
5960
|
* 创建割草路径
|
|
6007
5961
|
*/
|
|
6008
5962
|
static createMowingPath(points, config) {
|
|
6009
|
-
const
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
5963
|
+
const convertedPoints = convertPointsFormat(points);
|
|
5964
|
+
const drawElement = this.create(convertedPoints, {
|
|
5965
|
+
lineColor: config?.color,
|
|
5966
|
+
lineWidth: config?.lineWidth,
|
|
5967
|
+
opacity: config?.opacity,
|
|
6013
5968
|
});
|
|
6014
5969
|
drawElement.originalData = points;
|
|
6015
5970
|
return drawElement;
|
|
@@ -6018,11 +5973,11 @@ class PathDataBuilder {
|
|
|
6018
5973
|
* 创建传输路径
|
|
6019
5974
|
*/
|
|
6020
5975
|
static createTransPath(points, config) {
|
|
6021
|
-
const
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
opacity: config?.opacity
|
|
5976
|
+
const convertedPoints = convertPointsFormat(points);
|
|
5977
|
+
const drawElement = this.create(convertedPoints, {
|
|
5978
|
+
lineColor: config?.color,
|
|
5979
|
+
lineWidth: config?.lineWidth,
|
|
5980
|
+
opacity: config?.opacity,
|
|
6026
5981
|
});
|
|
6027
5982
|
drawElement.originalData = points;
|
|
6028
5983
|
return drawElement;
|
|
@@ -6041,55 +5996,59 @@ class PathDataProcessor {
|
|
|
6041
5996
|
if (!pathData || typeof pathData !== 'object') {
|
|
6042
5997
|
return [];
|
|
6043
5998
|
}
|
|
6044
|
-
// 获取所有分区的路径数据
|
|
6045
|
-
const allPathItems = Object.values(pathData).reduce((acc, partitionData) => {
|
|
6046
|
-
if (partitionData && partitionData.points && partitionData.points.length > 0) {
|
|
6047
|
-
acc.push(...partitionData.points);
|
|
6048
|
-
}
|
|
6049
|
-
return acc;
|
|
6050
|
-
}, []);
|
|
6051
5999
|
// 合并配置
|
|
6052
6000
|
const config = mapConfig.path;
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
const
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6001
|
+
const result = Object.keys(pathData).map((key) => {
|
|
6002
|
+
const id = Number(key);
|
|
6003
|
+
const points = pathData[key].points || [];
|
|
6004
|
+
// 使用Python相同的逻辑:按线段分组而不是按点分组
|
|
6005
|
+
const pathSegments = createPathSegmentsByType(points);
|
|
6006
|
+
const elements = [];
|
|
6007
|
+
// 处理边缘路径段
|
|
6008
|
+
for (const segment of pathSegments.edge) {
|
|
6009
|
+
// 转换 Point[] 为 number[][]
|
|
6010
|
+
const points = segment.points.map((point) => [point.x, point.y]);
|
|
6011
|
+
const element = PathDataBuilder.createEdgePath(points, {
|
|
6012
|
+
lineWidth: config.lineWidth,
|
|
6013
|
+
color: config.edgeLineColor,
|
|
6014
|
+
opacity: config.opacity,
|
|
6015
|
+
});
|
|
6016
|
+
element.originalData = points;
|
|
6017
|
+
element.pathType = PathSegmentType.EDGE;
|
|
6018
|
+
elements.push(element);
|
|
6019
|
+
}
|
|
6020
|
+
// 处理割草路径段
|
|
6021
|
+
for (const segment of pathSegments.mowing) {
|
|
6022
|
+
// 转换 Point[] 为 number[][]
|
|
6023
|
+
const points = segment.points.map((point) => [point.x, point.y]);
|
|
6024
|
+
const element = PathDataBuilder.createMowingPath(points, {
|
|
6025
|
+
lineWidth: config.lineWidth,
|
|
6026
|
+
color: config.mowedLineColor,
|
|
6027
|
+
opacity: config.opacity,
|
|
6028
|
+
});
|
|
6029
|
+
element.originalData = points;
|
|
6030
|
+
element.pathType = PathSegmentType.MOWING;
|
|
6031
|
+
elements.push(element);
|
|
6032
|
+
}
|
|
6033
|
+
// 处理传输路径段(只有在showTransPaths为true时才添加)
|
|
6034
|
+
for (const segment of pathSegments.trans) {
|
|
6035
|
+
// 转换 Point[] 为 number[][]
|
|
6036
|
+
const points = segment.points.map((point) => [point.x, point.y]);
|
|
6037
|
+
const element = PathDataBuilder.createTransPath(points, {
|
|
6038
|
+
lineWidth: config.lineWidth,
|
|
6039
|
+
color: config.transLineColor,
|
|
6040
|
+
opacity: config.opacity,
|
|
6041
|
+
});
|
|
6042
|
+
element.originalData = points;
|
|
6043
|
+
element.pathType = PathSegmentType.TRANS;
|
|
6044
|
+
elements.push(element);
|
|
6045
|
+
}
|
|
6046
|
+
return {
|
|
6047
|
+
id,
|
|
6048
|
+
elements,
|
|
6049
|
+
};
|
|
6050
|
+
});
|
|
6051
|
+
return result;
|
|
6093
6052
|
}
|
|
6094
6053
|
}
|
|
6095
6054
|
|
|
@@ -6098,15 +6057,19 @@ class PathDataProcessor {
|
|
|
6098
6057
|
* 专门处理边界标签的创建、定位和管理
|
|
6099
6058
|
*/
|
|
6100
6059
|
class BoundaryLabelsManager {
|
|
6101
|
-
constructor(svgView, boundaryData) {
|
|
6060
|
+
constructor(svgView, boundaryData, { unitType, language }) {
|
|
6102
6061
|
this.container = null;
|
|
6103
6062
|
this.overlayDiv = null;
|
|
6104
6063
|
this.globalClickHandler = null;
|
|
6105
6064
|
// 当前展开的边界id
|
|
6106
6065
|
this.currentExpandedBoundaryId = null;
|
|
6066
|
+
// 旋转角度
|
|
6067
|
+
this.rotation = 0;
|
|
6107
6068
|
this.svgView = svgView;
|
|
6108
6069
|
this.boundaryData = boundaryData;
|
|
6109
6070
|
this.initializeContainer();
|
|
6071
|
+
this.unitType = unitType;
|
|
6072
|
+
this.language = language;
|
|
6110
6073
|
}
|
|
6111
6074
|
/**
|
|
6112
6075
|
* 初始化容器
|
|
@@ -6180,17 +6143,17 @@ class BoundaryLabelsManager {
|
|
|
6180
6143
|
labelDiv.style.fontWeight = 'bold';
|
|
6181
6144
|
labelDiv.style.whiteSpace = 'nowrap';
|
|
6182
6145
|
labelDiv.style.maxWidth = '220px';
|
|
6183
|
-
labelDiv.style.transform =
|
|
6146
|
+
labelDiv.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
|
|
6184
6147
|
labelDiv.style.pointerEvents = 'auto';
|
|
6185
6148
|
labelDiv.style.boxShadow = '0 2px 8px rgba(0,0,0,0.4)';
|
|
6186
6149
|
labelDiv.style.cursor = 'pointer';
|
|
6187
|
-
labelDiv.style.transition = 'background-color 0.2s ease
|
|
6150
|
+
labelDiv.style.transition = 'background-color 0.2s ease';
|
|
6188
6151
|
labelDiv.style.userSelect = 'none';
|
|
6189
6152
|
labelDiv.style.zIndex = BoundaryLabelsManager.Z_INDEX.DEFAULT.toString();
|
|
6190
6153
|
// 计算进度
|
|
6191
|
-
const progress = boundary.finishedArea && boundary.area
|
|
6192
|
-
`${Math.round((boundary.finishedArea / boundary.area) * 100)}%`
|
|
6193
|
-
'0%';
|
|
6154
|
+
const progress = boundary.finishedArea && boundary.area
|
|
6155
|
+
? `${Math.round((boundary.finishedArea / boundary.area) * 100)}%`
|
|
6156
|
+
: '0%';
|
|
6194
6157
|
// 基础内容(始终显示)
|
|
6195
6158
|
const baseContent = document.createElement('div');
|
|
6196
6159
|
baseContent.className = 'boundary-label-base';
|
|
@@ -6202,12 +6165,14 @@ class BoundaryLabelsManager {
|
|
|
6202
6165
|
extendedContent.style.marginTop = '6px';
|
|
6203
6166
|
extendedContent.style.fontSize = '11px';
|
|
6204
6167
|
extendedContent.style.opacity = '0.9';
|
|
6205
|
-
extendedContent.style.display =
|
|
6168
|
+
extendedContent.style.display =
|
|
6169
|
+
this.currentExpandedBoundaryId === boundary.id ? 'block' : 'none';
|
|
6206
6170
|
extendedContent.style.borderTop = '1px solid rgba(255,255,255,0.2)';
|
|
6207
6171
|
extendedContent.style.paddingTop = '6px';
|
|
6172
|
+
console.log('this.unitType->', this.unitType);
|
|
6208
6173
|
// 面积信息
|
|
6209
|
-
const totalArea = convertAreaByUnits(boundary.area || 0,
|
|
6210
|
-
const finishedArea = convertAreaByUnits(boundary.finishedArea || 0,
|
|
6174
|
+
const totalArea = convertAreaByUnits(boundary.area || 0, this.unitType);
|
|
6175
|
+
const finishedArea = convertAreaByUnits(boundary.finishedArea || 0, this.unitType);
|
|
6211
6176
|
const coverageText = `Coverage: ${finishedArea.value}/${totalArea.value}`;
|
|
6212
6177
|
// 日期信息
|
|
6213
6178
|
const dateText = formatBoundaryDateText(boundary.endTime || 0);
|
|
@@ -6273,7 +6238,6 @@ class BoundaryLabelsManager {
|
|
|
6273
6238
|
this.collapseOtherLabels(boundaryId);
|
|
6274
6239
|
// 展开当前标签
|
|
6275
6240
|
extendedContent.style.display = 'block';
|
|
6276
|
-
labelDiv.style.whiteSpace = 'normal';
|
|
6277
6241
|
this.currentExpandedBoundaryId = boundaryId;
|
|
6278
6242
|
}
|
|
6279
6243
|
/**
|
|
@@ -6283,7 +6247,7 @@ class BoundaryLabelsManager {
|
|
|
6283
6247
|
if (!this.container)
|
|
6284
6248
|
return;
|
|
6285
6249
|
const allLabels = this.container.querySelectorAll('.boundary-label');
|
|
6286
|
-
allLabels.forEach(label => {
|
|
6250
|
+
allLabels.forEach((label) => {
|
|
6287
6251
|
const labelBoundaryId = label.getAttribute('data-boundary-id');
|
|
6288
6252
|
if (labelBoundaryId && labelBoundaryId !== boundaryId.toString()) {
|
|
6289
6253
|
const extendedContent = label.querySelector('.boundary-label-extended');
|
|
@@ -6386,7 +6350,7 @@ class BoundaryLabelsManager {
|
|
|
6386
6350
|
if (points.length < 3)
|
|
6387
6351
|
return null;
|
|
6388
6352
|
// 过滤有效点
|
|
6389
|
-
const validPoints = points.filter(point => point.length >= 2);
|
|
6353
|
+
const validPoints = points.filter((point) => point.length >= 2);
|
|
6390
6354
|
if (validPoints.length < 3)
|
|
6391
6355
|
return null;
|
|
6392
6356
|
// 确保多边形是封闭的(如果不是,自动闭合)
|
|
@@ -6435,7 +6399,7 @@ class BoundaryLabelsManager {
|
|
|
6435
6399
|
});
|
|
6436
6400
|
return {
|
|
6437
6401
|
x: sumX / points.length,
|
|
6438
|
-
y: sumY / points.length
|
|
6402
|
+
y: sumY / points.length,
|
|
6439
6403
|
};
|
|
6440
6404
|
}
|
|
6441
6405
|
/**
|
|
@@ -6509,12 +6473,6 @@ class BoundaryLabelsManager {
|
|
|
6509
6473
|
this.container.style.display = visible ? 'block' : 'none';
|
|
6510
6474
|
}
|
|
6511
6475
|
}
|
|
6512
|
-
/**
|
|
6513
|
-
* 获取边界数据
|
|
6514
|
-
*/
|
|
6515
|
-
getBoundaryData() {
|
|
6516
|
-
return this.boundaryData;
|
|
6517
|
-
}
|
|
6518
6476
|
/**
|
|
6519
6477
|
* 根据ID获取特定边界的标签元素
|
|
6520
6478
|
*/
|
|
@@ -6531,7 +6489,7 @@ class BoundaryLabelsManager {
|
|
|
6531
6489
|
return;
|
|
6532
6490
|
const allLabels = this.container.querySelectorAll('.boundary-label');
|
|
6533
6491
|
this.currentExpandedBoundaryId = null;
|
|
6534
|
-
allLabels.forEach(label => {
|
|
6492
|
+
allLabels.forEach((label) => {
|
|
6535
6493
|
const extendedContent = label.querySelector('.boundary-label-extended');
|
|
6536
6494
|
if (extendedContent) {
|
|
6537
6495
|
extendedContent.style.display = 'none';
|
|
@@ -6546,7 +6504,7 @@ class BoundaryLabelsManager {
|
|
|
6546
6504
|
if (!this.container)
|
|
6547
6505
|
return;
|
|
6548
6506
|
const allLabels = this.container.querySelectorAll('.boundary-label');
|
|
6549
|
-
allLabels.forEach(label => {
|
|
6507
|
+
allLabels.forEach((label) => {
|
|
6550
6508
|
const labelElement = label;
|
|
6551
6509
|
if (interactive) {
|
|
6552
6510
|
labelElement.style.pointerEvents = 'auto';
|
|
@@ -6565,34 +6523,26 @@ class BoundaryLabelsManager {
|
|
|
6565
6523
|
});
|
|
6566
6524
|
}
|
|
6567
6525
|
/**
|
|
6568
|
-
*
|
|
6569
|
-
|
|
6570
|
-
resetZIndex() {
|
|
6571
|
-
this.resetLabelZIndex();
|
|
6572
|
-
}
|
|
6573
|
-
/**
|
|
6574
|
-
* 获取层级常量(静态方法)
|
|
6575
|
-
*/
|
|
6576
|
-
static getZIndexConstants() {
|
|
6577
|
-
return BoundaryLabelsManager.Z_INDEX;
|
|
6578
|
-
}
|
|
6579
|
-
/**
|
|
6580
|
-
* 调试方法:检查当前所有边界标签的层级
|
|
6526
|
+
* 设置标签旋转角度,使其与地图旋转相反,保持水平状态
|
|
6527
|
+
* @param rotation 地图的旋转角度(度)
|
|
6581
6528
|
*/
|
|
6582
|
-
|
|
6529
|
+
setRotation(rotation) {
|
|
6583
6530
|
if (!this.container)
|
|
6584
6531
|
return;
|
|
6585
|
-
|
|
6586
|
-
|
|
6532
|
+
this.rotation = rotation;
|
|
6533
|
+
const labels = this.container.querySelectorAll('.boundary-label');
|
|
6534
|
+
labels.forEach((label) => {
|
|
6587
6535
|
const labelElement = label;
|
|
6588
|
-
|
|
6536
|
+
// 应用与地图旋转相反的旋转,保持标签水平
|
|
6537
|
+
const counterRotation = -rotation;
|
|
6538
|
+
labelElement.style.transform = `translate(-50%, -50%) rotate(${counterRotation}deg)`;
|
|
6589
6539
|
});
|
|
6590
6540
|
}
|
|
6591
6541
|
}
|
|
6592
6542
|
// 简化的层级定义
|
|
6593
6543
|
BoundaryLabelsManager.Z_INDEX = {
|
|
6594
6544
|
DEFAULT: 900, // 默认层级
|
|
6595
|
-
ACTIVE: 9999 // 点击激活时的高层级
|
|
6545
|
+
ACTIVE: 9999, // 点击激活时的高层级
|
|
6596
6546
|
};
|
|
6597
6547
|
|
|
6598
6548
|
/**
|
|
@@ -6605,6 +6555,10 @@ class ChargingPileManager {
|
|
|
6605
6555
|
this.container = null;
|
|
6606
6556
|
this.overlayDiv = null;
|
|
6607
6557
|
this.pileElements = new Map();
|
|
6558
|
+
// 原始旋转角度
|
|
6559
|
+
this.originalRotation = 0;
|
|
6560
|
+
// 旋转角度
|
|
6561
|
+
this.rotation = 0;
|
|
6608
6562
|
this.svgView = svgView;
|
|
6609
6563
|
this.initializeContainer();
|
|
6610
6564
|
}
|
|
@@ -6659,7 +6613,9 @@ class ChargingPileManager {
|
|
|
6659
6613
|
// 将弧度转换为角度
|
|
6660
6614
|
const angle = (direction * 180) / Math.PI;
|
|
6661
6615
|
const rotationDegree = 270 - angle; // 坐标系转换
|
|
6662
|
-
|
|
6616
|
+
this.originalRotation = rotationDegree;
|
|
6617
|
+
const actualRotation = rotationDegree - this.rotation;
|
|
6618
|
+
pileDiv.style.transform = `translate(-50%, -50%) rotate(${actualRotation}deg)`;
|
|
6663
6619
|
// 添加动画
|
|
6664
6620
|
// this.addChargingPileAnimation(pileDiv, imgElement, rotationDegree);
|
|
6665
6621
|
// 生成唯一ID
|
|
@@ -6708,30 +6664,15 @@ class ChargingPileManager {
|
|
|
6708
6664
|
updatePositions() {
|
|
6709
6665
|
if (!this.overlayDiv || !this.container)
|
|
6710
6666
|
return;
|
|
6711
|
-
|
|
6712
|
-
const divWidth = divRect.width;
|
|
6713
|
-
const divHeight = divRect.height;
|
|
6714
|
-
// 获取SVG的viewBox
|
|
6715
|
-
const svg = this.svgView.getSVG();
|
|
6716
|
-
if (!svg)
|
|
6717
|
-
return;
|
|
6718
|
-
const viewBox = svg.viewBox.baseVal;
|
|
6719
|
-
const viewBoxData = {
|
|
6720
|
-
x: viewBox.x,
|
|
6721
|
-
y: viewBox.y,
|
|
6722
|
-
width: viewBox.width,
|
|
6723
|
-
height: viewBox.height,
|
|
6724
|
-
};
|
|
6725
|
-
this.updatePositionsWithPrecomputedData(divWidth, divHeight, viewBoxData);
|
|
6667
|
+
this.updatePositionsWithPrecomputedData();
|
|
6726
6668
|
}
|
|
6727
6669
|
/**
|
|
6728
6670
|
* 使用预计算数据更新位置
|
|
6729
6671
|
*/
|
|
6730
|
-
updatePositionsWithPrecomputedData(
|
|
6672
|
+
updatePositionsWithPrecomputedData() {
|
|
6731
6673
|
this.chargingPileElements.forEach((element, _index) => {
|
|
6732
6674
|
const center = element.coordinates[0];
|
|
6733
|
-
const pixelPosition = this.convertMapCoordinateToPixelWithPrecomputedData(center[0], center[1]
|
|
6734
|
-
console.log('updatePositionsWithPrecomputedData----->', element, pixelPosition, divWidth, divHeight, viewBox);
|
|
6675
|
+
const pixelPosition = this.convertMapCoordinateToPixelWithPrecomputedData(center[0], center[1]);
|
|
6735
6676
|
if (pixelPosition) {
|
|
6736
6677
|
const pileId = `pile_${center[0]}_${center[1]}`;
|
|
6737
6678
|
const pileElement = this.pileElements.get(pileId);
|
|
@@ -6745,7 +6686,15 @@ class ChargingPileManager {
|
|
|
6745
6686
|
/**
|
|
6746
6687
|
* 使用预计算数据进行坐标转换
|
|
6747
6688
|
*/
|
|
6748
|
-
convertMapCoordinateToPixelWithPrecomputedData(mapX, mapY
|
|
6689
|
+
convertMapCoordinateToPixelWithPrecomputedData(mapX, mapY) {
|
|
6690
|
+
// 获取叠加层div的CSS尺寸
|
|
6691
|
+
const divWidth = parseFloat(this.overlayDiv.style.width) || this.overlayDiv.offsetWidth;
|
|
6692
|
+
const divHeight = parseFloat(this.overlayDiv.style.height) || this.overlayDiv.offsetHeight;
|
|
6693
|
+
// 获取SVG的viewBox
|
|
6694
|
+
const svg = this.svgView.getSVG();
|
|
6695
|
+
if (!svg)
|
|
6696
|
+
return { x: 0, y: 0 };
|
|
6697
|
+
const viewBox = svg.viewBox.baseVal;
|
|
6749
6698
|
// 计算地图坐标在viewBox中的相对位置
|
|
6750
6699
|
const relativeX = (mapX - viewBox.x) / viewBox.width;
|
|
6751
6700
|
const relativeY = (mapY - viewBox.y) / viewBox.height;
|
|
@@ -6760,29 +6709,9 @@ class ChargingPileManager {
|
|
|
6760
6709
|
clear() {
|
|
6761
6710
|
this.chargingPileElements = [];
|
|
6762
6711
|
this.pileElements.clear();
|
|
6763
|
-
if (this.container) {
|
|
6764
|
-
this.container.innerHTML = '';
|
|
6765
|
-
}
|
|
6766
|
-
}
|
|
6767
|
-
/**
|
|
6768
|
-
* 设置可见性
|
|
6769
|
-
*/
|
|
6770
|
-
setVisible(visible) {
|
|
6771
|
-
if (this.container) {
|
|
6772
|
-
this.container.style.display = visible ? 'block' : 'none';
|
|
6773
|
-
}
|
|
6774
|
-
}
|
|
6775
|
-
/**
|
|
6776
|
-
* 获取充电桩数量
|
|
6777
|
-
*/
|
|
6778
|
-
getElementCount() {
|
|
6779
|
-
return this.chargingPileElements.length;
|
|
6780
|
-
}
|
|
6781
|
-
/**
|
|
6782
|
-
* 充电桩不需要动态层级调整(为了接口统一而保留)
|
|
6783
|
-
*/
|
|
6784
|
-
resetZIndex() {
|
|
6785
|
-
// 充电桩层级始终保持固定,无需重置
|
|
6712
|
+
if (this.container) {
|
|
6713
|
+
this.container.innerHTML = '';
|
|
6714
|
+
}
|
|
6786
6715
|
}
|
|
6787
6716
|
/**
|
|
6788
6717
|
* 销毁管理器
|
|
@@ -6795,10 +6724,21 @@ class ChargingPileManager {
|
|
|
6795
6724
|
this.container = null;
|
|
6796
6725
|
this.overlayDiv = null;
|
|
6797
6726
|
}
|
|
6727
|
+
/**
|
|
6728
|
+
* 设置充电桩旋转角度
|
|
6729
|
+
*/
|
|
6730
|
+
setRotation(rotation) {
|
|
6731
|
+
this.rotation = rotation;
|
|
6732
|
+
const allContainers = this.container.querySelectorAll('.charging-pile');
|
|
6733
|
+
allContainers.forEach((container) => {
|
|
6734
|
+
const pileElement = container;
|
|
6735
|
+
pileElement.style.transform = `translate(-50%, -50%) rotate(${this.originalRotation - this.rotation}deg)`;
|
|
6736
|
+
});
|
|
6737
|
+
}
|
|
6798
6738
|
}
|
|
6799
6739
|
// 简化的层级定义 - 充电桩只需要一个固定层级
|
|
6800
6740
|
ChargingPileManager.Z_INDEX = {
|
|
6801
|
-
CHARGING_PILE:
|
|
6741
|
+
CHARGING_PILE: 750, // 充电桩图标固定层级
|
|
6802
6742
|
};
|
|
6803
6743
|
|
|
6804
6744
|
/**
|
|
@@ -6816,6 +6756,8 @@ class AntennaManager {
|
|
|
6816
6756
|
this.globalClickHandler = null;
|
|
6817
6757
|
this.antennaTooltipFlag = false;
|
|
6818
6758
|
this.singleAntennaTooltipFlag = false;
|
|
6759
|
+
// 旋转角度
|
|
6760
|
+
this.rotation = 0;
|
|
6819
6761
|
this.svgView = svgView;
|
|
6820
6762
|
this.initializeContainer();
|
|
6821
6763
|
this.setupGlobalClickHandler();
|
|
@@ -6888,7 +6830,7 @@ class AntennaManager {
|
|
|
6888
6830
|
const antennaContainer = document.createElement('div');
|
|
6889
6831
|
antennaContainer.className = 'antenna-container-item';
|
|
6890
6832
|
antennaContainer.style.position = 'absolute';
|
|
6891
|
-
antennaContainer.style.transform =
|
|
6833
|
+
antennaContainer.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
|
|
6892
6834
|
antennaContainer.style.pointerEvents = 'auto';
|
|
6893
6835
|
antennaContainer.style.zIndex = AntennaManager.Z_INDEX.DEFAULT.toString();
|
|
6894
6836
|
antennaContainer.setAttribute('data-antenna-id', antennaData.type.toString());
|
|
@@ -6929,8 +6871,6 @@ class AntennaManager {
|
|
|
6929
6871
|
this.singleAntennaTooltipFlag = true;
|
|
6930
6872
|
}
|
|
6931
6873
|
this.expandTooltip(antennaContainer);
|
|
6932
|
-
// 调试:检查层级变化
|
|
6933
|
-
this.debugCheckZIndex();
|
|
6934
6874
|
});
|
|
6935
6875
|
// 添加悬停效果
|
|
6936
6876
|
antennaDiv.addEventListener('mouseenter', () => {
|
|
@@ -7180,39 +7120,6 @@ class AntennaManager {
|
|
|
7180
7120
|
this.container.innerHTML = '';
|
|
7181
7121
|
}
|
|
7182
7122
|
}
|
|
7183
|
-
/**
|
|
7184
|
-
* 设置可见性
|
|
7185
|
-
*/
|
|
7186
|
-
setVisible(visible) {
|
|
7187
|
-
if (this.container) {
|
|
7188
|
-
this.container.style.display = visible ? 'block' : 'none';
|
|
7189
|
-
}
|
|
7190
|
-
}
|
|
7191
|
-
/**
|
|
7192
|
-
* 获取天线数量
|
|
7193
|
-
*/
|
|
7194
|
-
getElementCount() {
|
|
7195
|
-
return this.antennaElements.length;
|
|
7196
|
-
}
|
|
7197
|
-
/**
|
|
7198
|
-
* 重置天线层级(公共方法)
|
|
7199
|
-
*/
|
|
7200
|
-
resetZIndex() {
|
|
7201
|
-
this.resetAntennaZIndex();
|
|
7202
|
-
}
|
|
7203
|
-
/**
|
|
7204
|
-
* 获取层级常量(静态方法)
|
|
7205
|
-
*/
|
|
7206
|
-
static getZIndexConstants() {
|
|
7207
|
-
return AntennaManager.Z_INDEX;
|
|
7208
|
-
}
|
|
7209
|
-
/**
|
|
7210
|
-
* 调试方法:检查当前所有天线的层级
|
|
7211
|
-
*/
|
|
7212
|
-
debugCheckZIndex() {
|
|
7213
|
-
if (!this.container)
|
|
7214
|
-
return;
|
|
7215
|
-
}
|
|
7216
7123
|
/**
|
|
7217
7124
|
* 销毁管理器
|
|
7218
7125
|
*/
|
|
@@ -7229,10 +7136,21 @@ class AntennaManager {
|
|
|
7229
7136
|
this.container = null;
|
|
7230
7137
|
this.overlayDiv = null;
|
|
7231
7138
|
}
|
|
7139
|
+
/**
|
|
7140
|
+
* 设置天线旋转角度
|
|
7141
|
+
*/
|
|
7142
|
+
setRotation(rotation) {
|
|
7143
|
+
this.rotation = rotation;
|
|
7144
|
+
const allContainers = this.container.querySelectorAll('.antenna-container-item');
|
|
7145
|
+
allContainers.forEach((container) => {
|
|
7146
|
+
const antennaContainer = container;
|
|
7147
|
+
antennaContainer.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
|
|
7148
|
+
});
|
|
7149
|
+
}
|
|
7232
7150
|
}
|
|
7233
7151
|
// 简化的层级定义
|
|
7234
7152
|
AntennaManager.Z_INDEX = {
|
|
7235
|
-
DEFAULT:
|
|
7153
|
+
DEFAULT: 800, // 默认层级
|
|
7236
7154
|
ACTIVE: 9999, // 点击激活时的高层级
|
|
7237
7155
|
};
|
|
7238
7156
|
|
|
@@ -7283,8 +7201,8 @@ const EDIT_BEHAVIOR = {
|
|
|
7283
7201
|
SUCCESS_MESSAGE_DURATION_MS: 3000,
|
|
7284
7202
|
};
|
|
7285
7203
|
|
|
7286
|
-
class
|
|
7287
|
-
constructor(svgView,
|
|
7204
|
+
class MowerPositionManager {
|
|
7205
|
+
constructor(svgView, mowerPositionConfig, modelType, overlayDiv, onAnimationComplete, onMowingPositionChange) {
|
|
7288
7206
|
this.container = null;
|
|
7289
7207
|
this.overlayDiv = null;
|
|
7290
7208
|
this.mowerElement = null;
|
|
@@ -7301,9 +7219,11 @@ class MowerPostionManager {
|
|
|
7301
7219
|
this.deltaPosition = null;
|
|
7302
7220
|
this.onlyUpdateTheta = false;
|
|
7303
7221
|
this.svgView = svgView;
|
|
7304
|
-
this.
|
|
7222
|
+
this.mowerPositionConfig = mowerPositionConfig;
|
|
7223
|
+
this.modelType = modelType;
|
|
7305
7224
|
this.overlayDiv = overlayDiv;
|
|
7306
7225
|
this.onAnimationComplete = onAnimationComplete;
|
|
7226
|
+
this.onMowingPositionChange = onMowingPositionChange;
|
|
7307
7227
|
this.initializeContainer();
|
|
7308
7228
|
}
|
|
7309
7229
|
get animationFlag() {
|
|
@@ -7349,12 +7269,13 @@ class MowerPostionManager {
|
|
|
7349
7269
|
imgElement.style.height = '100%';
|
|
7350
7270
|
imgElement.style.objectFit = 'contain';
|
|
7351
7271
|
// 获取图片源
|
|
7352
|
-
const imageSrc = getMowerImage(this.
|
|
7272
|
+
const imageSrc = getMowerImage(this.mowerPositionConfig);
|
|
7353
7273
|
if (imageSrc) {
|
|
7354
7274
|
imgElement.src = imageSrc;
|
|
7355
7275
|
}
|
|
7356
7276
|
this.mowerElement.appendChild(imgElement);
|
|
7357
7277
|
this.container.appendChild(this.mowerElement);
|
|
7278
|
+
this.updatePosition(this.mowerPositionConfig);
|
|
7358
7279
|
}
|
|
7359
7280
|
/**
|
|
7360
7281
|
* 设置叠加层div引用(用于坐标转换)
|
|
@@ -7371,51 +7292,62 @@ class MowerPostionManager {
|
|
|
7371
7292
|
/**
|
|
7372
7293
|
* 根据最后一次有效的位置更新数据
|
|
7373
7294
|
*/
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7295
|
+
updatePositionByLastPosition(chargingPilesPositionConfig) {
|
|
7296
|
+
if (!chargingPilesPositionConfig)
|
|
7297
|
+
return;
|
|
7298
|
+
this.mowerPositionConfig = chargingPilesPositionConfig;
|
|
7299
|
+
const positonOutOfRange = isOutOfRange(chargingPilesPositionConfig);
|
|
7300
|
+
const positionInValid = isInvalidPosition(chargingPilesPositionConfig);
|
|
7301
|
+
let postureX = 0;
|
|
7302
|
+
let postureY = 0;
|
|
7303
|
+
let postureTheta = 0;
|
|
7377
7304
|
const lastPosition = this.lastPosition;
|
|
7378
|
-
if (
|
|
7379
|
-
|
|
7305
|
+
if (positonOutOfRange || positionInValid) {
|
|
7306
|
+
postureX = lastPosition?.x || 0;
|
|
7307
|
+
postureY = lastPosition?.y || 0;
|
|
7308
|
+
postureTheta = lastPosition?.rotation || 0;
|
|
7380
7309
|
}
|
|
7381
7310
|
else {
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
postureY: lastPosition.y,
|
|
7386
|
-
postureTheta: lastPosition.rotation,
|
|
7387
|
-
}, 0);
|
|
7311
|
+
postureX = chargingPilesPositionConfig.postureX || 0;
|
|
7312
|
+
postureY = chargingPilesPositionConfig.postureY || 0;
|
|
7313
|
+
postureTheta = chargingPilesPositionConfig.postureTheta || 0;
|
|
7388
7314
|
}
|
|
7315
|
+
// 检查是否需要更新图片
|
|
7316
|
+
this.updateMowerImage(chargingPilesPositionConfig);
|
|
7317
|
+
// 立即更新位置
|
|
7318
|
+
this.setElementPosition(postureX, postureY, postureTheta);
|
|
7389
7319
|
}
|
|
7390
7320
|
/**
|
|
7391
7321
|
* 更新割草机位置
|
|
7392
7322
|
*/
|
|
7393
|
-
updatePosition(
|
|
7394
|
-
console.log('updatePosition----->', positonConfig);
|
|
7323
|
+
updatePosition(positionConfig, animationTime = 0) {
|
|
7395
7324
|
// 检查是否需要更新图片
|
|
7396
|
-
this.updateMowerImage(
|
|
7325
|
+
this.updateMowerImage(positionConfig);
|
|
7326
|
+
// 更新配置
|
|
7327
|
+
this.mowerPositionConfig = positionConfig;
|
|
7328
|
+
const postureX = positionConfig?.postureX || this.lastPosition?.x || 0;
|
|
7329
|
+
const postureY = positionConfig?.postureY || this.lastPosition?.y || 0;
|
|
7330
|
+
const postureTheta = positionConfig?.postureTheta || this.lastPosition?.rotation || 0;
|
|
7397
7331
|
// 停止当前动画(如果有)
|
|
7398
7332
|
this.stopAnimation();
|
|
7399
7333
|
// 第一个点
|
|
7400
7334
|
if (!this.currentPosition) {
|
|
7401
7335
|
this.currentPosition = {
|
|
7402
|
-
x:
|
|
7403
|
-
y:
|
|
7404
|
-
rotation:
|
|
7336
|
+
x: postureX,
|
|
7337
|
+
y: postureY,
|
|
7338
|
+
rotation: postureTheta,
|
|
7405
7339
|
};
|
|
7406
7340
|
this.setElementPosition(this.currentPosition.x, this.currentPosition.y, this.currentPosition.rotation);
|
|
7407
7341
|
return;
|
|
7408
7342
|
}
|
|
7409
7343
|
// 根据动画时长决定更新方式
|
|
7410
7344
|
if (animationTime > 0) {
|
|
7411
|
-
this.startAnimationToPosition(
|
|
7345
|
+
this.startAnimationToPosition(positionConfig, animationTime);
|
|
7412
7346
|
}
|
|
7413
7347
|
else {
|
|
7414
7348
|
// 立即更新位置
|
|
7415
|
-
this.setElementPosition(
|
|
7349
|
+
this.setElementPosition(positionConfig.postureX, positionConfig.postureY, positionConfig.postureTheta);
|
|
7416
7350
|
}
|
|
7417
|
-
// 更新配置
|
|
7418
|
-
this.mowerPositonConfig = positonConfig;
|
|
7419
7351
|
}
|
|
7420
7352
|
/**
|
|
7421
7353
|
* 更新割草机图片
|
|
@@ -7426,9 +7358,14 @@ class MowerPostionManager {
|
|
|
7426
7358
|
const imgElement = this.mowerElement.querySelector('img');
|
|
7427
7359
|
if (!imgElement)
|
|
7428
7360
|
return;
|
|
7429
|
-
const imageSrc = getMowerImage(positonConfig);
|
|
7361
|
+
const imageSrc = getMowerImage(positonConfig, this.modelType);
|
|
7430
7362
|
if (imageSrc) {
|
|
7431
7363
|
imgElement.src = imageSrc;
|
|
7364
|
+
imgElement.style.display = 'block';
|
|
7365
|
+
}
|
|
7366
|
+
else {
|
|
7367
|
+
imgElement.style.display = 'none';
|
|
7368
|
+
return;
|
|
7432
7369
|
}
|
|
7433
7370
|
}
|
|
7434
7371
|
/**
|
|
@@ -7438,7 +7375,9 @@ class MowerPostionManager {
|
|
|
7438
7375
|
const { x: pointX, y: pointY } = convertCoordinate(x, y);
|
|
7439
7376
|
const targetPixelPosition = this.convertMapCoordinateToOverlayPixel(pointX, pointY);
|
|
7440
7377
|
const targetRotation = radToDegree(theta);
|
|
7378
|
+
this.currentPosition = { x: x, y: y, rotation: theta };
|
|
7441
7379
|
this.lastPosition = { x, y, rotation: theta };
|
|
7380
|
+
// console.log('setElementPosition', x, y, theta, targetRotation, positonOutOfRange, positionValid, targetPixelPosition);
|
|
7442
7381
|
if (!this.mowerElement)
|
|
7443
7382
|
return;
|
|
7444
7383
|
this.mowerElement.style.left = `${targetPixelPosition?.x}px`;
|
|
@@ -7464,29 +7403,48 @@ class MowerPostionManager {
|
|
|
7464
7403
|
startAnimationToPosition(positionConfig, duration) {
|
|
7465
7404
|
if (!this.mowerElement || !this.currentPosition)
|
|
7466
7405
|
return;
|
|
7467
|
-
console.log('before startAnimationToPosition----->', this.startPosition, this.currentPosition, this.targetPosition, positionConfig);
|
|
7468
|
-
this.startPosition = { ...this.currentPosition, rotation: radNormalize(this.currentPosition.rotation) };
|
|
7469
7406
|
this.targetPosition = {
|
|
7470
7407
|
x: positionConfig.postureX,
|
|
7471
7408
|
y: positionConfig.postureY,
|
|
7472
7409
|
rotation: positionConfig.postureTheta,
|
|
7473
7410
|
};
|
|
7411
|
+
const isTargetPositionInvalid = isInvalidPosition({
|
|
7412
|
+
postureX: this.targetPosition.x,
|
|
7413
|
+
postureY: this.targetPosition.y,
|
|
7414
|
+
postureTheta: this.targetPosition.rotation,
|
|
7415
|
+
});
|
|
7416
|
+
const isTargetPositionOutOfRange = isOutOfRange({
|
|
7417
|
+
postureX: this.targetPosition.x,
|
|
7418
|
+
postureY: this.targetPosition.y,
|
|
7419
|
+
postureTheta: this.targetPosition.rotation,
|
|
7420
|
+
});
|
|
7421
|
+
// 如果目标坐标点不合理,则舍弃不使用
|
|
7422
|
+
if (isTargetPositionInvalid || isTargetPositionOutOfRange) {
|
|
7423
|
+
return;
|
|
7424
|
+
}
|
|
7425
|
+
this.startPosition = {
|
|
7426
|
+
...this.currentPosition,
|
|
7427
|
+
rotation: radNormalize(this.currentPosition.rotation),
|
|
7428
|
+
};
|
|
7474
7429
|
this.animationDuration = duration;
|
|
7475
7430
|
this.startTime = window.performance.now();
|
|
7476
7431
|
this.isAnimating = true;
|
|
7477
7432
|
this.onlyUpdateTheta =
|
|
7478
7433
|
distance(this.currentPosition.x, this.currentPosition.y, positionConfig.postureX, positionConfig.postureY) < 0.2;
|
|
7479
7434
|
// 为了实现倒车时,割草机图标旋转方向正确,需要计算当前位置和目标位置的夹角
|
|
7480
|
-
const calTheta = calAngle(
|
|
7435
|
+
// const calTheta = calAngle(
|
|
7436
|
+
// this.startPosition.x,
|
|
7437
|
+
// this.startPosition.y,
|
|
7438
|
+
// this.targetPosition.x,
|
|
7439
|
+
// this.targetPosition.y
|
|
7440
|
+
// );
|
|
7481
7441
|
const startTheta = this.startPosition.rotation;
|
|
7482
|
-
const targetTheta = this.
|
|
7483
|
-
console.log('begain startAnimationToPosition----->', this.startPosition, this.targetPosition, this.onlyUpdateTheta);
|
|
7442
|
+
const targetTheta = this.targetPosition.rotation;
|
|
7484
7443
|
this.deltaPosition = {
|
|
7485
7444
|
x: this.onlyUpdateTheta ? 0 : this.targetPosition.x - this.startPosition.x,
|
|
7486
7445
|
y: this.onlyUpdateTheta ? 0 : this.targetPosition.y - this.startPosition.y,
|
|
7487
|
-
rotation: radNormalize(targetTheta - startTheta)
|
|
7446
|
+
rotation: radNormalize(targetTheta - startTheta),
|
|
7488
7447
|
};
|
|
7489
|
-
console.log('deltaPosition----->', targetTheta, startTheta, calTheta, this.deltaPosition);
|
|
7490
7448
|
// 开始动画循环
|
|
7491
7449
|
this.animateStep();
|
|
7492
7450
|
}
|
|
@@ -7497,7 +7455,7 @@ class MowerPostionManager {
|
|
|
7497
7455
|
* 动画步骤
|
|
7498
7456
|
*/
|
|
7499
7457
|
animateStep() {
|
|
7500
|
-
if (!this.isAnimating || !this.
|
|
7458
|
+
if (!this.isAnimating || !this.targetPosition || !this.startPosition)
|
|
7501
7459
|
return;
|
|
7502
7460
|
const currentTime = window.performance.now();
|
|
7503
7461
|
const elapsed = currentTime - this.startTime;
|
|
@@ -7508,20 +7466,24 @@ class MowerPostionManager {
|
|
|
7508
7466
|
const currentX = this.startPosition.x + this.deltaPosition.x * easedProgress;
|
|
7509
7467
|
const currentY = this.startPosition.y + this.deltaPosition.y * easedProgress;
|
|
7510
7468
|
const currentRotation = this.startPosition.rotation + this.deltaPosition.rotation * easedProgress;
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7469
|
+
// 假设在这里进行更新路径数据
|
|
7470
|
+
if (this.onMowingPositionChange) {
|
|
7471
|
+
this.onMowingPositionChange({
|
|
7472
|
+
x: currentX,
|
|
7473
|
+
y: currentY,
|
|
7474
|
+
vehicleState: this.mowerPositionConfig?.vehicleState,
|
|
7475
|
+
});
|
|
7476
|
+
}
|
|
7477
|
+
// console.log('animateStep-->', this.startPosition, this.deltaPosition, this.targetPosition, easedProgress)
|
|
7478
|
+
this.setElementPosition(currentX, currentY, currentRotation);
|
|
7514
7479
|
// 继续动画或结束
|
|
7515
7480
|
if (progress < 1) {
|
|
7516
7481
|
// 设置当前位置
|
|
7517
|
-
this.setElementPosition(currentX, currentY, currentRotation);
|
|
7518
7482
|
this.animationId = window.requestAnimationFrame(() => this.animateStep());
|
|
7519
7483
|
}
|
|
7520
7484
|
else {
|
|
7521
|
-
this.setElementPosition(currentX, currentY, currentRotation);
|
|
7522
7485
|
// 动画完成
|
|
7523
7486
|
this.stopAnimation();
|
|
7524
|
-
console.log('end animateStep----->', this.lastPosition, this.currentPosition, this.targetPosition);
|
|
7525
7487
|
// 通知动画完成
|
|
7526
7488
|
if (this.onAnimationComplete) {
|
|
7527
7489
|
this.onAnimationComplete();
|
|
@@ -7566,12 +7528,6 @@ class MowerPostionManager {
|
|
|
7566
7528
|
this.container.style.display = visible ? 'block' : 'none';
|
|
7567
7529
|
}
|
|
7568
7530
|
}
|
|
7569
|
-
/**
|
|
7570
|
-
* 检查是否正在动画中
|
|
7571
|
-
*/
|
|
7572
|
-
getIsAnimating() {
|
|
7573
|
-
return this.isAnimating;
|
|
7574
|
-
}
|
|
7575
7531
|
/**
|
|
7576
7532
|
* 销毁管理器
|
|
7577
7533
|
*/
|
|
@@ -7587,13 +7543,85 @@ class MowerPostionManager {
|
|
|
7587
7543
|
}
|
|
7588
7544
|
}
|
|
7589
7545
|
|
|
7546
|
+
// 记录割草状态,状态变更的时候,变量不触发重新渲染
|
|
7547
|
+
const useProcessMowingState = create((set) => ({
|
|
7548
|
+
processStateIsMowing: false,
|
|
7549
|
+
updateProcessStateIsMowing: (isMowing) => set({ processStateIsMowing: isMowing }),
|
|
7550
|
+
resetProcessStateIsMowing: () => set({ processStateIsMowing: false }),
|
|
7551
|
+
}));
|
|
7552
|
+
|
|
7553
|
+
/**
|
|
7554
|
+
* 高级节流函数
|
|
7555
|
+
* @param func 要节流的函数
|
|
7556
|
+
* @param delay 延迟时间(毫秒)
|
|
7557
|
+
* @param options 配置选项
|
|
7558
|
+
* @returns 节流后的函数
|
|
7559
|
+
*/
|
|
7560
|
+
function throttleAdvanced(func, delay, options = { leading: true, trailing: true }) {
|
|
7561
|
+
let lastExecTime = 0;
|
|
7562
|
+
let timeoutId = null;
|
|
7563
|
+
let lastArgs = null;
|
|
7564
|
+
return function throttled(...args) {
|
|
7565
|
+
const currentTime = Date.now();
|
|
7566
|
+
lastArgs = args;
|
|
7567
|
+
// 如果距离上次执行的时间小于延迟时间
|
|
7568
|
+
if (currentTime - lastExecTime < delay) {
|
|
7569
|
+
// 清除之前的定时器
|
|
7570
|
+
if (timeoutId) {
|
|
7571
|
+
clearTimeout(timeoutId);
|
|
7572
|
+
}
|
|
7573
|
+
// 设置新的定时器
|
|
7574
|
+
timeoutId = setTimeout(() => {
|
|
7575
|
+
if (options.trailing && lastArgs) {
|
|
7576
|
+
lastExecTime = Date.now();
|
|
7577
|
+
func.apply(this, lastArgs);
|
|
7578
|
+
lastArgs = null;
|
|
7579
|
+
}
|
|
7580
|
+
timeoutId = null;
|
|
7581
|
+
}, delay - (currentTime - lastExecTime));
|
|
7582
|
+
}
|
|
7583
|
+
else {
|
|
7584
|
+
// 如果距离上次执行的时间已经超过延迟时间
|
|
7585
|
+
if (options.leading) {
|
|
7586
|
+
lastExecTime = currentTime;
|
|
7587
|
+
func.apply(this, args);
|
|
7588
|
+
}
|
|
7589
|
+
}
|
|
7590
|
+
};
|
|
7591
|
+
}
|
|
7592
|
+
/**
|
|
7593
|
+
* 检测当前设备是否为移动设备
|
|
7594
|
+
* @returns {boolean} 如果是移动设备返回true,否则返回false
|
|
7595
|
+
*/
|
|
7596
|
+
function isMobileDevice() {
|
|
7597
|
+
// 确保在浏览器环境中运行
|
|
7598
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
7599
|
+
return false;
|
|
7600
|
+
}
|
|
7601
|
+
// 检查用户代理字符串
|
|
7602
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
7603
|
+
const mobileKeywords = [
|
|
7604
|
+
'android', 'webos', 'iphone', 'ipad', 'ipod',
|
|
7605
|
+
'blackberry', 'windows phone', 'mobile'
|
|
7606
|
+
];
|
|
7607
|
+
const isMobileUserAgent = mobileKeywords.some(keyword => userAgent.includes(keyword));
|
|
7608
|
+
// 检查触摸屏支持
|
|
7609
|
+
const hasTouchScreen = 'ontouchstart' in window ||
|
|
7610
|
+
(navigator.maxTouchPoints && navigator.maxTouchPoints > 0);
|
|
7611
|
+
// 检查屏幕尺寸(移动设备通常屏幕较小)
|
|
7612
|
+
const isSmallScreen = window.innerWidth <= 768;
|
|
7613
|
+
// 综合判断:用户代理包含移动设备关键词,或者有触摸屏且屏幕较小
|
|
7614
|
+
return isMobileUserAgent || (hasTouchScreen && isSmallScreen);
|
|
7615
|
+
}
|
|
7616
|
+
|
|
7590
7617
|
// Google Maps 叠加层类 - 带编辑功能
|
|
7591
7618
|
class MowerMapOverlay {
|
|
7592
|
-
constructor(bounds, mapData,
|
|
7619
|
+
constructor(bounds, mapData, partitionBoundary, mowerPositionConfig, modelType, pathData, isEditMode = false, unitType = UnitsType.Imperial, language = 'en', mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
|
|
7593
7620
|
this.div = null;
|
|
7594
7621
|
this.svgMapView = null;
|
|
7595
7622
|
this.offscreenContainer = null;
|
|
7596
7623
|
this.overlayView = null;
|
|
7624
|
+
this.defaultTransform = { x: 0, y: 0, rotation: 0 };
|
|
7597
7625
|
// boundary数据
|
|
7598
7626
|
this.boundaryData = [];
|
|
7599
7627
|
// 边界标签管理器
|
|
@@ -7603,7 +7631,7 @@ class MowerMapOverlay {
|
|
|
7603
7631
|
// 天线管理器
|
|
7604
7632
|
this.antennaManager = null;
|
|
7605
7633
|
// 割草机位置管理器
|
|
7606
|
-
this.
|
|
7634
|
+
this.mowerPositionManager = null;
|
|
7607
7635
|
// 当前动画时长
|
|
7608
7636
|
this.currentAnimationTime = 0;
|
|
7609
7637
|
// 是否正在用户动画中(区分用户主动触发的动画和地图重绘)
|
|
@@ -7626,16 +7654,38 @@ class MowerMapOverlay {
|
|
|
7626
7654
|
// 初始状态记录(用于计算相对于初始状态的偏移)
|
|
7627
7655
|
this.initialOffset = { x: 0, y: 0 };
|
|
7628
7656
|
this.initialRotation = 0;
|
|
7657
|
+
this.mowPartitionData = null;
|
|
7658
|
+
this.updatePathDataByMowingPositionThrottled = throttleAdvanced(this.updatePathDataByMowingPosition, 300, {
|
|
7659
|
+
leading: true,
|
|
7660
|
+
trailing: false,
|
|
7661
|
+
});
|
|
7629
7662
|
this.bounds = bounds;
|
|
7630
7663
|
this.mapData = mapData;
|
|
7664
|
+
this.partitionBoundary = partitionBoundary;
|
|
7631
7665
|
this.pathData = pathData;
|
|
7632
7666
|
this.isEditMode = isEditMode;
|
|
7667
|
+
this.unitType = unitType;
|
|
7668
|
+
this.language = language;
|
|
7633
7669
|
this.mapConfig = mapConfig;
|
|
7634
7670
|
this.antennaConfig = antennaConfig;
|
|
7635
7671
|
this.onMapLoad = onMapLoad;
|
|
7636
7672
|
this.onPathLoad = onPathLoad;
|
|
7637
7673
|
this.dragCallbacks = dragCallbacks;
|
|
7638
|
-
this.
|
|
7674
|
+
this.mowerPositionConfig = mowerPositionConfig;
|
|
7675
|
+
this.mowPartitionData = mowPartitionData;
|
|
7676
|
+
this.modelType = modelType;
|
|
7677
|
+
// 设置默认的transform
|
|
7678
|
+
if (defaultTransform) {
|
|
7679
|
+
this.defaultTransform = {
|
|
7680
|
+
x: defaultTransform.x ?? 0,
|
|
7681
|
+
y: defaultTransform.y ?? 0,
|
|
7682
|
+
rotation: defaultTransform.rotation ?? 0,
|
|
7683
|
+
};
|
|
7684
|
+
// defaultTransform的x对应经度偏移量,y对应纬度偏移量
|
|
7685
|
+
this.latLngOffset.lng = this.defaultTransform.x;
|
|
7686
|
+
this.latLngOffset.lat = this.defaultTransform.y;
|
|
7687
|
+
this.currentRotation = this.defaultTransform.rotation;
|
|
7688
|
+
}
|
|
7639
7689
|
// 创建 OverlayView 实例
|
|
7640
7690
|
if (window.google && window.google.maps) {
|
|
7641
7691
|
this.overlayView = new window.google.maps.OverlayView();
|
|
@@ -7650,26 +7700,28 @@ class MowerMapOverlay {
|
|
|
7650
7700
|
this.overlayView.handleSave = this.handleSave.bind(this);
|
|
7651
7701
|
this.overlayView.setCustomIcons = this.setCustomIcons.bind(this);
|
|
7652
7702
|
this.overlayView.getCurrentDragState = this.getCurrentDragState.bind(this);
|
|
7703
|
+
this.overlayView.setTransform = this.setTransform.bind(this);
|
|
7704
|
+
this.overlayView.resetToDefaultTransform = this.resetToDefaultTransform.bind(this);
|
|
7653
7705
|
}
|
|
7654
7706
|
this.boundaryData = generateBoundaryData(mapData, pathData);
|
|
7655
7707
|
}
|
|
7656
|
-
updatePosition(
|
|
7657
|
-
console.log('updatePosition==', positonConfig, animationTime);
|
|
7708
|
+
updatePosition(positionConfig, animationTime = 2200) {
|
|
7658
7709
|
// 保存当前动画时长
|
|
7659
7710
|
this.currentAnimationTime = animationTime;
|
|
7660
7711
|
// 标记是否为用户动画(大于0表示用户主动触发的动画)
|
|
7661
7712
|
this.isUserAnimation = animationTime > 0;
|
|
7662
7713
|
// 更新割草机位置配置
|
|
7663
|
-
this.
|
|
7714
|
+
this.mowerPositionConfig = positionConfig;
|
|
7664
7715
|
// 更新割草机位置管理器
|
|
7665
|
-
if (this.
|
|
7666
|
-
this.
|
|
7716
|
+
if (this.mowerPositionManager) {
|
|
7717
|
+
this.mowerPositionManager.updatePosition(positionConfig, animationTime);
|
|
7667
7718
|
}
|
|
7668
7719
|
}
|
|
7669
|
-
|
|
7720
|
+
updatePositionByLastPosition(chargingPilesPositionConfig) {
|
|
7721
|
+
this.mowerPositionConfig = chargingPilesPositionConfig;
|
|
7670
7722
|
// 更新配置
|
|
7671
|
-
if (this.
|
|
7672
|
-
this.
|
|
7723
|
+
if (this.mowerPositionManager) {
|
|
7724
|
+
this.mowerPositionManager.updatePositionByLastPosition(chargingPilesPositionConfig);
|
|
7673
7725
|
}
|
|
7674
7726
|
}
|
|
7675
7727
|
setMap(map) {
|
|
@@ -7686,7 +7738,25 @@ class MowerMapOverlay {
|
|
|
7686
7738
|
getPanes() {
|
|
7687
7739
|
return this.overlayView ? this.overlayView.getPanes() : null;
|
|
7688
7740
|
}
|
|
7741
|
+
resetBorderLayerHighlight() {
|
|
7742
|
+
this.mowPartitionData = null;
|
|
7743
|
+
const boundaryBorderLayer = this.svgMapView?.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
7744
|
+
if (!boundaryBorderLayer)
|
|
7745
|
+
return;
|
|
7746
|
+
boundaryBorderLayer.setMowingBoundarys([]);
|
|
7747
|
+
this.svgMapView?.renderLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
7748
|
+
}
|
|
7749
|
+
setBorderLayerHighlight(mowPartitionData) {
|
|
7750
|
+
this.mowPartitionData = mowPartitionData;
|
|
7751
|
+
const boundaryBorderLayer = this.svgMapView?.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
7752
|
+
const partitionIds = mowPartitionData?.partitionIds || [];
|
|
7753
|
+
if (!boundaryBorderLayer)
|
|
7754
|
+
return;
|
|
7755
|
+
boundaryBorderLayer.setMowingBoundarys(partitionIds);
|
|
7756
|
+
this.svgMapView?.renderLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
7757
|
+
}
|
|
7689
7758
|
onAdd() {
|
|
7759
|
+
console.log('onAdd');
|
|
7690
7760
|
// 创建包含SVG的div
|
|
7691
7761
|
this.div = document.createElement('div');
|
|
7692
7762
|
this.div.style.borderStyle = 'none';
|
|
@@ -7708,6 +7778,7 @@ class MowerMapOverlay {
|
|
|
7708
7778
|
this.createBoundaryLabelsManager();
|
|
7709
7779
|
// 创建割草机位置管理器
|
|
7710
7780
|
this.createMowerPositionManager();
|
|
7781
|
+
this.setManagerRotation(this.defaultTransform.rotation);
|
|
7711
7782
|
// 如果处于编辑模式,创建编辑界面
|
|
7712
7783
|
if (this.isEditMode) {
|
|
7713
7784
|
this.createEditInterface();
|
|
@@ -7746,23 +7817,17 @@ class MowerMapOverlay {
|
|
|
7746
7817
|
const map = this.getMap();
|
|
7747
7818
|
if (!map || !this.svgMapView)
|
|
7748
7819
|
return;
|
|
7749
|
-
|
|
7750
|
-
// const center = map.getCenter();
|
|
7751
|
-
// 基础公式:像素/米 = 156543.03392 * cos(latitude) / (2 ^ zoom)
|
|
7752
|
-
// const metersPerPixel =
|
|
7753
|
-
// (156543.03392 * Math.cos((center.lat() * Math.PI) / 180)) / Math.pow(2, currentZoom);
|
|
7754
|
-
// 缩放比例 = 1 / 米/像素
|
|
7755
|
-
// const scale = (1 / metersPerPixel) * 50;
|
|
7756
|
-
// 应用缩放到SVG
|
|
7757
|
-
// this.svgMapView.setZoom(scale);
|
|
7820
|
+
this.draw();
|
|
7758
7821
|
}
|
|
7759
7822
|
// 创建边界标签管理器
|
|
7760
7823
|
createBoundaryLabelsManager() {
|
|
7761
7824
|
if (!this.div || !this.svgMapView)
|
|
7762
7825
|
return;
|
|
7763
|
-
console.log('this.boundaryData----->', this.boundaryData);
|
|
7764
7826
|
// 创建边界标签管理器
|
|
7765
|
-
this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData
|
|
7827
|
+
this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData, {
|
|
7828
|
+
unitType: this.unitType,
|
|
7829
|
+
language: this.language,
|
|
7830
|
+
});
|
|
7766
7831
|
// 设置叠加层div引用
|
|
7767
7832
|
this.boundaryLabelsManager.setOverlayDiv(this.div);
|
|
7768
7833
|
// 添加所有边界标签
|
|
@@ -7806,11 +7871,11 @@ class MowerMapOverlay {
|
|
|
7806
7871
|
if (!this.div || !this.svgMapView)
|
|
7807
7872
|
return;
|
|
7808
7873
|
// 创建割草机位置管理器,传入动画完成回调
|
|
7809
|
-
this.
|
|
7874
|
+
this.mowerPositionManager = new MowerPositionManager(this.svgMapView, this.mowerPositionConfig, this.modelType, this.div, () => { }, this.updatePathDataByMowingPositionThrottled.bind(this));
|
|
7810
7875
|
// 设置叠加层div引用
|
|
7811
|
-
this.
|
|
7876
|
+
this.mowerPositionManager.setOverlayDiv(this.div);
|
|
7812
7877
|
// 获取容器并添加到主div
|
|
7813
|
-
const container = this.
|
|
7878
|
+
const container = this.mowerPositionManager.getElement();
|
|
7814
7879
|
if (container) {
|
|
7815
7880
|
this.div.appendChild(container);
|
|
7816
7881
|
}
|
|
@@ -7837,23 +7902,9 @@ class MowerMapOverlay {
|
|
|
7837
7902
|
this.boundaryLabelsManager.updatePositionsWithPrecomputedData(width, height, viewBoxInfo);
|
|
7838
7903
|
}
|
|
7839
7904
|
// 更新管理器位置
|
|
7840
|
-
updateManagerPositions(
|
|
7905
|
+
updateManagerPositions(_width, _height) {
|
|
7841
7906
|
if (!this.div)
|
|
7842
7907
|
return;
|
|
7843
|
-
// 获取SVG元素和其viewBox
|
|
7844
|
-
const svgElement = this.div.querySelector('svg');
|
|
7845
|
-
if (!svgElement)
|
|
7846
|
-
return;
|
|
7847
|
-
const viewBox = svgElement.viewBox.baseVal;
|
|
7848
|
-
if (!viewBox)
|
|
7849
|
-
return;
|
|
7850
|
-
// 构造viewBox信息对象
|
|
7851
|
-
({
|
|
7852
|
-
x: viewBox.x,
|
|
7853
|
-
y: viewBox.y,
|
|
7854
|
-
width: viewBox.width,
|
|
7855
|
-
height: viewBox.height,
|
|
7856
|
-
});
|
|
7857
7908
|
// 更新充电桩位置
|
|
7858
7909
|
if (this.chargingPileManager) {
|
|
7859
7910
|
this.chargingPileManager.updatePositions();
|
|
@@ -7902,7 +7953,7 @@ class MowerMapOverlay {
|
|
|
7902
7953
|
this.rotateHandle.style.pointerEvents = 'auto';
|
|
7903
7954
|
this.rotateHandle.innerHTML = DEFAULT_ROTATE_ICON;
|
|
7904
7955
|
this.editContainer.appendChild(this.rotateHandle);
|
|
7905
|
-
//
|
|
7956
|
+
// 创建拖拽手柄(左下角)- 仅在移动设备上显示
|
|
7906
7957
|
this.dragHandle = document.createElement('div');
|
|
7907
7958
|
this.dragHandle.style.position = 'absolute';
|
|
7908
7959
|
this.dragHandle.style.bottom = '-20px';
|
|
@@ -7913,6 +7964,10 @@ class MowerMapOverlay {
|
|
|
7913
7964
|
this.dragHandle.style.zIndex = EDIT_STYLES.Z_INDEX.HANDLE;
|
|
7914
7965
|
this.dragHandle.style.pointerEvents = 'auto';
|
|
7915
7966
|
this.dragHandle.innerHTML = DEFAULT_DRAG_ICON;
|
|
7967
|
+
// 在PC设备上隐藏拖拽手柄
|
|
7968
|
+
if (!isMobileDevice()) {
|
|
7969
|
+
this.dragHandle.style.display = 'none';
|
|
7970
|
+
}
|
|
7916
7971
|
this.editContainer.appendChild(this.dragHandle);
|
|
7917
7972
|
// 将编辑容器添加到主div
|
|
7918
7973
|
this.div.appendChild(this.editContainer);
|
|
@@ -7928,9 +7983,10 @@ class MowerMapOverlay {
|
|
|
7928
7983
|
}
|
|
7929
7984
|
// 获取当前拖拽状态
|
|
7930
7985
|
getCurrentDragState() {
|
|
7986
|
+
// 返回基于地图中心点的经纬度偏移量
|
|
7931
7987
|
return {
|
|
7932
|
-
|
|
7933
|
-
|
|
7988
|
+
x: this.latLngOffset.lng + this.tempPixelOffset.x - this.initialOffset.x,
|
|
7989
|
+
y: this.latLngOffset.lat + this.tempPixelOffset.y - this.initialOffset.y,
|
|
7934
7990
|
rotation: this.currentRotation - this.initialRotation,
|
|
7935
7991
|
isDragging: this.isDragging,
|
|
7936
7992
|
isRotating: this.isRotating,
|
|
@@ -7953,7 +8009,6 @@ class MowerMapOverlay {
|
|
|
7953
8009
|
this.boundaryLabelsManager.collapseAllLabels();
|
|
7954
8010
|
}
|
|
7955
8011
|
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
7956
|
-
console.log('开始旋转操作');
|
|
7957
8012
|
});
|
|
7958
8013
|
// 旋转手柄的触摸事件
|
|
7959
8014
|
this.rotateHandle.addEventListener('touchstart', (e) => {
|
|
@@ -7969,39 +8024,41 @@ class MowerMapOverlay {
|
|
|
7969
8024
|
this.boundaryLabelsManager.collapseAllLabels();
|
|
7970
8025
|
}
|
|
7971
8026
|
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
7972
|
-
console.log('开始旋转操作(触摸)');
|
|
7973
|
-
}, { passive: false });
|
|
7974
|
-
// 拖拽手柄的鼠标事件
|
|
7975
|
-
this.dragHandle.addEventListener('mousedown', (e) => {
|
|
7976
|
-
e.preventDefault();
|
|
7977
|
-
e.stopPropagation();
|
|
7978
|
-
e.stopImmediatePropagation();
|
|
7979
|
-
this.isDragging = true;
|
|
7980
|
-
this.startPos = { x: e.clientX, y: e.clientY };
|
|
7981
|
-
this.dragHandle.style.cursor = 'grabbing';
|
|
7982
|
-
// 开始编辑时关闭所有展开的边界标签
|
|
7983
|
-
if (this.boundaryLabelsManager) {
|
|
7984
|
-
this.boundaryLabelsManager.collapseAllLabels();
|
|
7985
|
-
}
|
|
7986
|
-
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
7987
|
-
console.log('开始拖动操作(通过手柄)');
|
|
7988
|
-
});
|
|
7989
|
-
// 拖拽手柄的触摸事件
|
|
7990
|
-
this.dragHandle.addEventListener('touchstart', (e) => {
|
|
7991
|
-
e.preventDefault();
|
|
7992
|
-
e.stopPropagation();
|
|
7993
|
-
e.stopImmediatePropagation();
|
|
7994
|
-
this.isDragging = true;
|
|
7995
|
-
const touch = e.touches[0];
|
|
7996
|
-
this.startPos = { x: touch.clientX, y: touch.clientY };
|
|
7997
|
-
this.dragHandle.style.cursor = 'grabbing';
|
|
7998
|
-
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
7999
|
-
console.log('开始拖动操作(通过手柄,触摸)');
|
|
8000
8027
|
}, { passive: false });
|
|
8028
|
+
// 拖拽手柄的鼠标事件 - 仅在移动设备上启用
|
|
8029
|
+
if (isMobileDevice()) {
|
|
8030
|
+
this.dragHandle.addEventListener('mousedown', (e) => {
|
|
8031
|
+
e.preventDefault();
|
|
8032
|
+
e.stopPropagation();
|
|
8033
|
+
e.stopImmediatePropagation();
|
|
8034
|
+
this.isDragging = true;
|
|
8035
|
+
this.startPos = { x: e.clientX, y: e.clientY };
|
|
8036
|
+
this.dragHandle.style.cursor = 'grabbing';
|
|
8037
|
+
// 开始编辑时关闭所有展开的边界标签
|
|
8038
|
+
if (this.boundaryLabelsManager) {
|
|
8039
|
+
this.boundaryLabelsManager.collapseAllLabels();
|
|
8040
|
+
}
|
|
8041
|
+
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
8042
|
+
});
|
|
8043
|
+
// 拖拽手柄的触摸事件
|
|
8044
|
+
this.dragHandle.addEventListener('touchstart', (e) => {
|
|
8045
|
+
e.preventDefault();
|
|
8046
|
+
e.stopPropagation();
|
|
8047
|
+
e.stopImmediatePropagation();
|
|
8048
|
+
this.isDragging = true;
|
|
8049
|
+
const touch = e.touches[0];
|
|
8050
|
+
this.startPos = { x: touch.clientX, y: touch.clientY };
|
|
8051
|
+
this.dragHandle.style.cursor = 'grabbing';
|
|
8052
|
+
this.dragCallbacks?.onDragStart?.(this.getCurrentDragState());
|
|
8053
|
+
}, { passive: false });
|
|
8054
|
+
}
|
|
8001
8055
|
// 编辑容器的鼠标事件(整个区域拖拽)
|
|
8002
8056
|
this.editContainer.addEventListener('mousedown', (e) => {
|
|
8003
|
-
|
|
8004
|
-
|
|
8057
|
+
// 在移动设备上,检查是否点击了拖拽手柄或旋转手柄
|
|
8058
|
+
// 在PC设备上,只检查旋转手柄(拖拽手柄已隐藏)
|
|
8059
|
+
const isDragHandleClick = isMobileDevice() && e.target === this.dragHandle;
|
|
8060
|
+
const isRotateHandleClick = e.target === this.rotateHandle;
|
|
8061
|
+
if (isDragHandleClick || isRotateHandleClick) {
|
|
8005
8062
|
return;
|
|
8006
8063
|
}
|
|
8007
8064
|
e.preventDefault();
|
|
@@ -8018,8 +8075,11 @@ class MowerMapOverlay {
|
|
|
8018
8075
|
});
|
|
8019
8076
|
// 编辑容器的触摸事件(整个区域拖拽)
|
|
8020
8077
|
this.editContainer.addEventListener('touchstart', (e) => {
|
|
8021
|
-
|
|
8022
|
-
|
|
8078
|
+
// 在移动设备上,检查是否点击了拖拽手柄或旋转手柄
|
|
8079
|
+
// 在PC设备上,只检查旋转手柄(拖拽手柄已隐藏)
|
|
8080
|
+
const isDragHandleClick = isMobileDevice() && e.target === this.dragHandle;
|
|
8081
|
+
const isRotateHandleClick = e.target === this.rotateHandle;
|
|
8082
|
+
if (isDragHandleClick || isRotateHandleClick) {
|
|
8023
8083
|
return;
|
|
8024
8084
|
}
|
|
8025
8085
|
e.preventDefault();
|
|
@@ -8079,7 +8139,6 @@ class MowerMapOverlay {
|
|
|
8079
8139
|
e.preventDefault();
|
|
8080
8140
|
e.stopPropagation();
|
|
8081
8141
|
e.stopImmediatePropagation();
|
|
8082
|
-
console.log('结束编辑操作');
|
|
8083
8142
|
}
|
|
8084
8143
|
// 如果是拖拽结束,将像素偏移量转换为地理坐标偏移量
|
|
8085
8144
|
if (this.isDragging) {
|
|
@@ -8101,7 +8160,6 @@ class MowerMapOverlay {
|
|
|
8101
8160
|
e.preventDefault();
|
|
8102
8161
|
e.stopPropagation();
|
|
8103
8162
|
e.stopImmediatePropagation();
|
|
8104
|
-
console.log('结束编辑操作(触摸)');
|
|
8105
8163
|
}
|
|
8106
8164
|
// 如果是拖拽结束,将像素偏移量转换为地理坐标偏移量
|
|
8107
8165
|
if (this.isDragging) {
|
|
@@ -8207,11 +8265,12 @@ class MowerMapOverlay {
|
|
|
8207
8265
|
// 确保旋转角度在0-360度范围内
|
|
8208
8266
|
this.currentRotation = ((this.currentRotation % 360) + 360) % 360;
|
|
8209
8267
|
// 应用旋转变换到DOM元素,同时保持位移
|
|
8268
|
+
// 更新边界标签的旋转角度,使其保持水平状态
|
|
8269
|
+
this.setManagerRotation(this.currentRotation);
|
|
8210
8270
|
const transform = `translate(${this.tempPixelOffset.x}px, ${this.tempPixelOffset.y}px) rotate(${this.currentRotation}deg)`;
|
|
8211
8271
|
this.div.style.transform = transform;
|
|
8212
8272
|
// 更新鼠标起始位置为当前位置,为下次计算做准备
|
|
8213
8273
|
this.startPos = { x: mouseCurrentX, y: mouseCurrentY };
|
|
8214
|
-
console.log('旋转角度:', this.currentRotation, '角度增量:', angleDifferenceDegrees);
|
|
8215
8274
|
}
|
|
8216
8275
|
// 将像素偏移量转换为地理坐标偏移量
|
|
8217
8276
|
convertPixelOffsetToLatLng() {
|
|
@@ -8241,17 +8300,8 @@ class MowerMapOverlay {
|
|
|
8241
8300
|
// 累积更新地理坐标偏移量(不是直接赋值!)
|
|
8242
8301
|
this.latLngOffset.lat += latOffset;
|
|
8243
8302
|
this.latLngOffset.lng += lngOffset;
|
|
8244
|
-
console.log('精确转换偏移量:', {
|
|
8245
|
-
pixelOffset: this.tempPixelOffset,
|
|
8246
|
-
centerLatLng: { lat: centerLatLng.lat(), lng: centerLatLng.lng() },
|
|
8247
|
-
offsetLatLng: { lat: offsetLatLng.lat(), lng: offsetLatLng.lng() },
|
|
8248
|
-
latOffset,
|
|
8249
|
-
lngOffset,
|
|
8250
|
-
newLatLngOffset: this.latLngOffset,
|
|
8251
|
-
});
|
|
8252
8303
|
// 重置临时像素偏移量
|
|
8253
8304
|
this.tempPixelOffset = { x: 0, y: 0 };
|
|
8254
|
-
// 重新绘制以应用新的地理坐标偏移量
|
|
8255
8305
|
this.draw();
|
|
8256
8306
|
}
|
|
8257
8307
|
// 获取编辑数据
|
|
@@ -8287,8 +8337,6 @@ class MowerMapOverlay {
|
|
|
8287
8337
|
editData: editData,
|
|
8288
8338
|
timestamp: new Date().toISOString(),
|
|
8289
8339
|
};
|
|
8290
|
-
// 在这里可以添加保存逻辑,比如发送到服务器
|
|
8291
|
-
console.log('保存编辑数据:', saveData);
|
|
8292
8340
|
// 显示保存成功提示
|
|
8293
8341
|
this.showSaveSuccess();
|
|
8294
8342
|
return saveData;
|
|
@@ -8354,6 +8402,63 @@ class MowerMapOverlay {
|
|
|
8354
8402
|
this.div.style.pointerEvents = 'none';
|
|
8355
8403
|
}
|
|
8356
8404
|
}
|
|
8405
|
+
/**
|
|
8406
|
+
* 设置旋转角度
|
|
8407
|
+
*/
|
|
8408
|
+
setManagerRotation(rotation) {
|
|
8409
|
+
if (this.boundaryLabelsManager) {
|
|
8410
|
+
this.boundaryLabelsManager.setRotation(rotation);
|
|
8411
|
+
}
|
|
8412
|
+
if (this.antennaManager) {
|
|
8413
|
+
this.antennaManager.setRotation(rotation);
|
|
8414
|
+
}
|
|
8415
|
+
if (this.chargingPileManager) {
|
|
8416
|
+
this.chargingPileManager.setRotation(rotation);
|
|
8417
|
+
}
|
|
8418
|
+
}
|
|
8419
|
+
// 设置transform
|
|
8420
|
+
setTransform(transform) {
|
|
8421
|
+
if (typeof transform.x === 'number')
|
|
8422
|
+
this.tempPixelOffset.x = transform.x;
|
|
8423
|
+
if (typeof transform.y === 'number')
|
|
8424
|
+
this.tempPixelOffset.y = transform.y;
|
|
8425
|
+
if (typeof transform.rotation === 'number')
|
|
8426
|
+
this.currentRotation = transform.rotation;
|
|
8427
|
+
this.defaultTransform = {
|
|
8428
|
+
x: transform.x,
|
|
8429
|
+
y: transform.y,
|
|
8430
|
+
rotation: transform.rotation,
|
|
8431
|
+
};
|
|
8432
|
+
// defaultTransform的x对应经度偏移量,y对应纬度偏移量
|
|
8433
|
+
this.latLngOffset.lng = this.defaultTransform.x;
|
|
8434
|
+
this.latLngOffset.lat = this.defaultTransform.y;
|
|
8435
|
+
this.setManagerRotation(this.currentRotation);
|
|
8436
|
+
this.draw();
|
|
8437
|
+
}
|
|
8438
|
+
// 重置到默认的transform
|
|
8439
|
+
resetToDefaultTransform() {
|
|
8440
|
+
// 重置所有偏移和旋转相关的状态,x对应经度偏移,y对应纬度偏移
|
|
8441
|
+
this.latLngOffset.lng = this.defaultTransform.x;
|
|
8442
|
+
this.latLngOffset.lat = this.defaultTransform.y;
|
|
8443
|
+
this.tempPixelOffset = { x: 0, y: 0 };
|
|
8444
|
+
this.currentRotation = this.defaultTransform.rotation;
|
|
8445
|
+
this.initialOffset = { x: 0, y: 0 };
|
|
8446
|
+
this.initialRotation = 0;
|
|
8447
|
+
// 重新绘制
|
|
8448
|
+
// 重置边界标签的旋转角度
|
|
8449
|
+
if (this.boundaryLabelsManager) {
|
|
8450
|
+
this.boundaryLabelsManager.setRotation(this.defaultTransform.rotation);
|
|
8451
|
+
}
|
|
8452
|
+
if (this.antennaManager) {
|
|
8453
|
+
this.antennaManager.setRotation(this.defaultTransform.rotation);
|
|
8454
|
+
}
|
|
8455
|
+
if (this.chargingPileManager) {
|
|
8456
|
+
this.chargingPileManager.setRotation(this.defaultTransform.rotation);
|
|
8457
|
+
}
|
|
8458
|
+
this.draw();
|
|
8459
|
+
// 触发拖拽回调,通知外部状态更新
|
|
8460
|
+
this.dragCallbacks?.onDragEnd?.(this.getCurrentDragState());
|
|
8461
|
+
}
|
|
8357
8462
|
initializeSvgMapView() {
|
|
8358
8463
|
if (!this.offscreenContainer)
|
|
8359
8464
|
return;
|
|
@@ -8364,8 +8469,10 @@ class MowerMapOverlay {
|
|
|
8364
8469
|
this.loadMapData();
|
|
8365
8470
|
// 加载路径数据
|
|
8366
8471
|
if (this.pathData && this.svgMapView) {
|
|
8367
|
-
this.loadPathData(this.pathData);
|
|
8472
|
+
this.loadPathData(this.pathData, this.mowPartitionData);
|
|
8368
8473
|
}
|
|
8474
|
+
// 刷新绘制图层
|
|
8475
|
+
this.svgMapView.refresh();
|
|
8369
8476
|
// 获取生成的SVG并添加到叠加层div中
|
|
8370
8477
|
const svgElement = this.svgMapView.getSVG();
|
|
8371
8478
|
if (svgElement) {
|
|
@@ -8388,9 +8495,6 @@ class MowerMapOverlay {
|
|
|
8388
8495
|
// 使用现有的MapDataProcessor处理地图数据
|
|
8389
8496
|
const elements = MapDataProcessor.processMapData(this.mapData, this.mapConfig);
|
|
8390
8497
|
// 分离充电桩和天线元素,其他元素添加到SVG图层
|
|
8391
|
-
// const svgElements = elements.filter(element =>
|
|
8392
|
-
// element.type !== 'charging_pile' && element.type !== 'antenna'
|
|
8393
|
-
// );
|
|
8394
8498
|
const svgElements = elements.filter((element) => element.type !== 'charging_pile' && element.type !== 'antenna');
|
|
8395
8499
|
const chargingPileElements = elements.filter((element) => element.type === 'charging_pile');
|
|
8396
8500
|
// 处理SVG图层元素
|
|
@@ -8399,14 +8503,16 @@ class MowerMapOverlay {
|
|
|
8399
8503
|
// 处理天线数据
|
|
8400
8504
|
let antennaElements = [];
|
|
8401
8505
|
if (this.antennaConfig.length > 0) {
|
|
8402
|
-
antennaElements = AntennaDataBuilder.fromAntennaData(this.antennaConfig);
|
|
8506
|
+
antennaElements = AntennaDataBuilder.fromAntennaData(this.antennaConfig, this.mapConfig);
|
|
8403
8507
|
}
|
|
8404
8508
|
// 添加图层到SvgMapView
|
|
8405
8509
|
const layers = drawLayer.getLayers();
|
|
8406
8510
|
this.svgMapView.clear();
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8511
|
+
this.svgMapView.addLayers(layers);
|
|
8512
|
+
const boundaryBorderLayer = this.svgMapView.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
8513
|
+
if (boundaryBorderLayer) {
|
|
8514
|
+
boundaryBorderLayer.setMowingBoundarys(this.mowPartitionData?.partitionIds || []);
|
|
8515
|
+
}
|
|
8410
8516
|
this.createChargingPileManager();
|
|
8411
8517
|
// 使用管理器处理充电桩和天线
|
|
8412
8518
|
if (this.chargingPileManager && chargingPileElements.length > 0) {
|
|
@@ -8432,40 +8538,95 @@ class MowerMapOverlay {
|
|
|
8432
8538
|
console.error('加载地图数据时出错:', error);
|
|
8433
8539
|
}
|
|
8434
8540
|
}
|
|
8435
|
-
loadPathData(pathData) {
|
|
8541
|
+
loadPathData(pathData, mowPartitionData) {
|
|
8436
8542
|
try {
|
|
8437
8543
|
// 使用现有的PathDataProcessor处理路径数据
|
|
8438
8544
|
const pathElements = PathDataProcessor.processPathData(pathData, this.mapConfig);
|
|
8545
|
+
const newPathElements = pathElements.map((pathElement) => {
|
|
8546
|
+
const { id, elements } = pathElement;
|
|
8547
|
+
const isMowBoundary = mowPartitionData && mowPartitionData?.partitionIds?.includes(id);
|
|
8548
|
+
if (isMowBoundary) {
|
|
8549
|
+
return {
|
|
8550
|
+
id: id,
|
|
8551
|
+
elements: elements.map((element) => {
|
|
8552
|
+
const isTransPath = element.pathType === PathSegmentType.TRANS;
|
|
8553
|
+
if (isTransPath) {
|
|
8554
|
+
return element;
|
|
8555
|
+
}
|
|
8556
|
+
return {
|
|
8557
|
+
...element,
|
|
8558
|
+
style: {
|
|
8559
|
+
...element.style,
|
|
8560
|
+
lineColor: DEFAULT_STYLES.path.mowingLineColor,
|
|
8561
|
+
},
|
|
8562
|
+
};
|
|
8563
|
+
}),
|
|
8564
|
+
};
|
|
8565
|
+
}
|
|
8566
|
+
else {
|
|
8567
|
+
return pathElement;
|
|
8568
|
+
}
|
|
8569
|
+
});
|
|
8439
8570
|
const pathLayer = new PathLayer();
|
|
8440
|
-
pathLayer.addElements(
|
|
8571
|
+
pathLayer.addElements(newPathElements);
|
|
8441
8572
|
// 添加图层到SvgMapView
|
|
8442
8573
|
this.svgMapView.removeLayerByType(LAYER_DEFAULT_TYPE.PATH);
|
|
8443
8574
|
this.svgMapView.addLayer(pathLayer);
|
|
8444
|
-
|
|
8575
|
+
this.svgMapView.renderLayer(LAYER_DEFAULT_TYPE.PATH);
|
|
8445
8576
|
// 调用回调
|
|
8446
|
-
const elementCount =
|
|
8577
|
+
const elementCount = newPathElements.length;
|
|
8447
8578
|
this.onPathLoad?.(elementCount);
|
|
8448
8579
|
}
|
|
8449
8580
|
catch (error) {
|
|
8450
8581
|
console.error('加载路径数据时出错:', error);
|
|
8451
8582
|
}
|
|
8452
8583
|
}
|
|
8584
|
+
/**
|
|
8585
|
+
* 根据割草机位置,更新路径数据
|
|
8586
|
+
* @param position 割草机位置
|
|
8587
|
+
*/
|
|
8588
|
+
updatePathDataByMowingPosition(position) {
|
|
8589
|
+
// 找到当前position所在的分区id,将该点更新到pathData中
|
|
8590
|
+
const currentPartitionId = getPartitionId(this.partitionBoundary, position.x, position.y);
|
|
8591
|
+
const processStateIsMowing = useProcessMowingState.getState().processStateIsMowing;
|
|
8592
|
+
if (currentPartitionId && this.pathData?.[currentPartitionId]) {
|
|
8593
|
+
const currentPathData = this.pathData[currentPartitionId];
|
|
8594
|
+
this.pathData[currentPartitionId] = {
|
|
8595
|
+
...currentPathData,
|
|
8596
|
+
points: [
|
|
8597
|
+
...(currentPathData?.points || []),
|
|
8598
|
+
{
|
|
8599
|
+
postureX: Number(position.x),
|
|
8600
|
+
postureY: Number(position.y),
|
|
8601
|
+
knifeRotation: processStateIsMowing && position.vehicleState === RobotStatus.MOWING ? '01' : '00', // "knifeRotation": "01",//刀盘是否转动 00-否 01-是
|
|
8602
|
+
pathType: '', //这里由于从实时路径是获取不到pathType的,所以这里暂时不传。让路径是否绘制取决于knifeRotation
|
|
8603
|
+
partitionId: currentPartitionId.toString(),
|
|
8604
|
+
},
|
|
8605
|
+
],
|
|
8606
|
+
};
|
|
8607
|
+
this.updatePathData(this.pathData, this.mowPartitionData);
|
|
8608
|
+
}
|
|
8609
|
+
}
|
|
8610
|
+
updateMowPartitionData(mowPartitionData) {
|
|
8611
|
+
this.mowPartitionData = mowPartitionData;
|
|
8612
|
+
}
|
|
8453
8613
|
/** 更新历史路径数据 */
|
|
8454
|
-
updatePathData(pathData) {
|
|
8614
|
+
updatePathData(pathData, mowPartitionData) {
|
|
8455
8615
|
if (!this.svgMapView || !pathData)
|
|
8456
8616
|
return;
|
|
8457
8617
|
// 找到pathLayer,将其删除,然后重新添加
|
|
8458
|
-
this.loadPathData(pathData);
|
|
8618
|
+
this.loadPathData(pathData, mowPartitionData);
|
|
8459
8619
|
}
|
|
8620
|
+
/** 更新边界标签信息 */
|
|
8460
8621
|
updateBoundaryLabelInfo(pathJson) {
|
|
8461
8622
|
if (!pathJson)
|
|
8462
8623
|
return;
|
|
8463
8624
|
const boundaryData = generateBoundaryData(this.mapData, pathJson);
|
|
8464
|
-
console.log('boundaryData==', boundaryData);
|
|
8625
|
+
// console.log('boundaryData==', boundaryData);
|
|
8465
8626
|
this.boundaryLabelsManager?.updateBoundaryData(boundaryData);
|
|
8466
8627
|
}
|
|
8467
8628
|
draw() {
|
|
8468
|
-
console.log('
|
|
8629
|
+
console.log('ondraw');
|
|
8469
8630
|
// 防御性检查:如果this.div为null,说明onAdd还没被调用,直接返回
|
|
8470
8631
|
if (!this.div) {
|
|
8471
8632
|
return;
|
|
@@ -8523,13 +8684,17 @@ class MowerMapOverlay {
|
|
|
8523
8684
|
this.div.style.transform = transform;
|
|
8524
8685
|
}
|
|
8525
8686
|
else {
|
|
8526
|
-
//
|
|
8527
|
-
|
|
8528
|
-
|
|
8687
|
+
// 非拖拽时:应用当前偏移和旋转(包括默认值)
|
|
8688
|
+
const transforms = [];
|
|
8689
|
+
// 应用像素偏移
|
|
8690
|
+
if (this.tempPixelOffset.x !== 0 || this.tempPixelOffset.y !== 0) {
|
|
8691
|
+
transforms.push(`translate(${this.tempPixelOffset.x}px, ${this.tempPixelOffset.y}px)`);
|
|
8529
8692
|
}
|
|
8530
|
-
|
|
8531
|
-
|
|
8693
|
+
// 应用旋转
|
|
8694
|
+
if (this.currentRotation !== 0) {
|
|
8695
|
+
transforms.push(`rotate(${this.currentRotation}deg)`);
|
|
8532
8696
|
}
|
|
8697
|
+
this.div.style.transform = transforms.join(' ');
|
|
8533
8698
|
}
|
|
8534
8699
|
// 更新SVG视图框以适应新的尺寸
|
|
8535
8700
|
if (this.svgMapView) {
|
|
@@ -8549,11 +8714,11 @@ class MowerMapOverlay {
|
|
|
8549
8714
|
this.updateManagerPositions(width, height);
|
|
8550
8715
|
// 重绘的时候可能在动画中,这时候需要强制更新一次数据,不然会出现抖动的效果
|
|
8551
8716
|
// 非动画的情况下,根据最新的数据实时同步就可以了
|
|
8552
|
-
if (!this.
|
|
8553
|
-
this.
|
|
8717
|
+
if (!this.mowerPositionManager?.animationFlag) {
|
|
8718
|
+
this.mowerPositionManager?.updatePositionByLastPosition(this.mowerPositionConfig);
|
|
8554
8719
|
}
|
|
8555
8720
|
else {
|
|
8556
|
-
this.
|
|
8721
|
+
this.mowerPositionManager?.forceUpdatePosition();
|
|
8557
8722
|
}
|
|
8558
8723
|
if (this.antennaManager) {
|
|
8559
8724
|
this.antennaManager.updateAntennaPosition();
|
|
@@ -8589,9 +8754,9 @@ class MowerMapOverlay {
|
|
|
8589
8754
|
this.antennaManager = null;
|
|
8590
8755
|
}
|
|
8591
8756
|
// 清理割草机位置管理器
|
|
8592
|
-
if (this.
|
|
8593
|
-
this.
|
|
8594
|
-
this.
|
|
8757
|
+
if (this.mowerPositionManager) {
|
|
8758
|
+
this.mowerPositionManager.destroy();
|
|
8759
|
+
this.mowerPositionManager = null;
|
|
8595
8760
|
}
|
|
8596
8761
|
// 清理编辑界面
|
|
8597
8762
|
this.removeEditInterface();
|
|
@@ -8608,12 +8773,45 @@ class MowerMapOverlay {
|
|
|
8608
8773
|
}
|
|
8609
8774
|
// 显示/隐藏割草机位置
|
|
8610
8775
|
setMowerPositionVisible(visible) {
|
|
8611
|
-
if (this.
|
|
8612
|
-
this.
|
|
8613
|
-
}
|
|
8776
|
+
if (this.mowerPositionManager) {
|
|
8777
|
+
this.mowerPositionManager.setVisible(visible);
|
|
8778
|
+
}
|
|
8779
|
+
}
|
|
8780
|
+
// 获取SvgMapView实例(用于debug)
|
|
8781
|
+
getSvgMapView() {
|
|
8782
|
+
return this.svgMapView;
|
|
8783
|
+
}
|
|
8784
|
+
}
|
|
8785
|
+
|
|
8786
|
+
// 获取车辆状态的中文文案
|
|
8787
|
+
const getVehicleStateText = (vehicleState) => {
|
|
8788
|
+
switch (vehicleState) {
|
|
8789
|
+
case RobotStatus.PARKED:
|
|
8790
|
+
return 'PARKED';
|
|
8791
|
+
case RobotStatus.CHARGING:
|
|
8792
|
+
return 'CHARGING';
|
|
8793
|
+
case RobotStatus.STANDBY:
|
|
8794
|
+
return 'STANDBY';
|
|
8795
|
+
case RobotStatus.MOWING:
|
|
8796
|
+
return 'MOWING';
|
|
8797
|
+
case RobotStatus.WORKING:
|
|
8798
|
+
return 'WORKING';
|
|
8799
|
+
case RobotStatus.MAPPING:
|
|
8800
|
+
return 'MAPPING';
|
|
8801
|
+
case RobotStatus.ERROR:
|
|
8802
|
+
return 'ERROR';
|
|
8803
|
+
case RobotStatus.UPGRADING:
|
|
8804
|
+
return 'UPGRADING';
|
|
8805
|
+
case RobotStatus.DISCONNECTED:
|
|
8806
|
+
return 'DISCONNECTED';
|
|
8807
|
+
case RobotStatus.TASK_DELAY:
|
|
8808
|
+
return 'TASK_DELAY';
|
|
8809
|
+
case RobotStatus.UNKNOWN:
|
|
8810
|
+
return '未知';
|
|
8811
|
+
default:
|
|
8812
|
+
return `未知状态(${vehicleState})`;
|
|
8614
8813
|
}
|
|
8615
|
-
}
|
|
8616
|
-
|
|
8814
|
+
};
|
|
8617
8815
|
// 验证GPS坐标是否有效
|
|
8618
8816
|
const isValidGpsCoordinate = (coordinate) => {
|
|
8619
8817
|
if (!coordinate || coordinate.length < 2)
|
|
@@ -8627,47 +8825,89 @@ const isValidGpsCoordinate = (coordinate) => {
|
|
|
8627
8825
|
!(Math.abs(lng) < 0.001 && Math.abs(lat) < 0.001) // 排除接近(0,0)的坐标
|
|
8628
8826
|
);
|
|
8629
8827
|
};
|
|
8828
|
+
// 旋转坐标点
|
|
8829
|
+
const rotateCoordinate = (point, center, angleRadians) => {
|
|
8830
|
+
const [x, y] = point;
|
|
8831
|
+
const [cx, cy] = center;
|
|
8832
|
+
// 将点移动到原点
|
|
8833
|
+
const dx = x - cx;
|
|
8834
|
+
const dy = y - cy;
|
|
8835
|
+
// 应用旋转矩阵
|
|
8836
|
+
const cos = Math.cos(angleRadians);
|
|
8837
|
+
const sin = Math.sin(angleRadians);
|
|
8838
|
+
const rotatedX = dx * cos - dy * sin;
|
|
8839
|
+
const rotatedY = dx * sin + dy * cos;
|
|
8840
|
+
// 移回原位置
|
|
8841
|
+
return [rotatedX + cx, rotatedY + cy];
|
|
8842
|
+
};
|
|
8630
8843
|
// 获取有效的GPS边界
|
|
8631
|
-
const getValidGpsBounds = (mapData) => {
|
|
8844
|
+
const getValidGpsBounds = (mapData, rotation = 0) => {
|
|
8845
|
+
let bounds;
|
|
8632
8846
|
// 首先尝试使用地图数据中的GPS坐标
|
|
8633
8847
|
if (isValidGpsCoordinate(mapData.sw_gps) && isValidGpsCoordinate(mapData.ne_gps)) {
|
|
8634
|
-
|
|
8848
|
+
bounds = {
|
|
8635
8849
|
sw: mapData.sw_gps,
|
|
8636
8850
|
ne: mapData.ne_gps,
|
|
8637
8851
|
};
|
|
8638
8852
|
}
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8853
|
+
else {
|
|
8854
|
+
// 如果GPS坐标无效,尝试从地图几何数据估算
|
|
8855
|
+
const { sw, ne } = estimateGpsFromMapBounds(mapData);
|
|
8856
|
+
if (sw && ne) {
|
|
8857
|
+
console.warn('GPS坐标无效,使用地图几何数据估算边界:', sw, ne);
|
|
8858
|
+
bounds = {
|
|
8859
|
+
sw: [sw[0], sw[1]],
|
|
8860
|
+
ne: [ne[0], ne[1]],
|
|
8861
|
+
};
|
|
8862
|
+
}
|
|
8863
|
+
else {
|
|
8864
|
+
// 最后的fallback:使用默认坐标
|
|
8865
|
+
console.warn('无法获取有效的GPS边界,使用默认坐标');
|
|
8866
|
+
bounds = {
|
|
8867
|
+
sw: [-9.1562, -37.7503],
|
|
8868
|
+
ne: [31.247, 5.797],
|
|
8869
|
+
};
|
|
8870
|
+
}
|
|
8871
|
+
}
|
|
8872
|
+
// 如果有旋转角度,计算旋转后的边界
|
|
8873
|
+
if (rotation !== 0) {
|
|
8874
|
+
const angleRadians = (rotation * Math.PI) / 180; // 转换为弧度
|
|
8875
|
+
// 计算边界中心点
|
|
8876
|
+
const centerLng = (bounds.sw[0] + bounds.ne[0]) / 2;
|
|
8877
|
+
const centerLat = (bounds.sw[1] + bounds.ne[1]) / 2;
|
|
8878
|
+
const center = [centerLng, centerLat];
|
|
8879
|
+
// 旋转四个角点
|
|
8880
|
+
const sw = rotateCoordinate(bounds.sw, center, angleRadians);
|
|
8881
|
+
const ne = rotateCoordinate(bounds.ne, center, angleRadians);
|
|
8882
|
+
const se = rotateCoordinate([bounds.ne[0], bounds.sw[1]], center, angleRadians);
|
|
8883
|
+
const nw = rotateCoordinate([bounds.sw[0], bounds.ne[1]], center, angleRadians);
|
|
8884
|
+
// 计算旋转后的边界框(包含所有旋转后的点)
|
|
8885
|
+
const lngs = [sw[0], ne[0], se[0], nw[0]];
|
|
8886
|
+
const lats = [sw[1], ne[1], se[1], nw[1]];
|
|
8887
|
+
bounds = {
|
|
8888
|
+
sw: [Math.min(...lngs), Math.min(...lats)],
|
|
8889
|
+
ne: [Math.max(...lngs), Math.max(...lats)],
|
|
8647
8890
|
};
|
|
8648
8891
|
}
|
|
8649
|
-
|
|
8650
|
-
console.warn('无法获取有效的GPS边界,使用默认坐标');
|
|
8651
|
-
return {
|
|
8652
|
-
sw: [-9.1562, -37.7503],
|
|
8653
|
-
ne: [31.247, 5.797],
|
|
8654
|
-
};
|
|
8892
|
+
return bounds;
|
|
8655
8893
|
};
|
|
8656
8894
|
// 默认配置
|
|
8657
8895
|
const defaultMapConfig = DEFAULT_STYLES;
|
|
8658
8896
|
// 地图渲染器组件
|
|
8659
|
-
const MowerMapRenderer = forwardRef(({ mapConfig, modelType, mapRef, mapJson, pathJson, realTimeData,
|
|
8660
|
-
const svgMapViewRef = useRef(null);
|
|
8897
|
+
const MowerMapRenderer = forwardRef(({ 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) => {
|
|
8661
8898
|
const [elementCount, setElementCount] = useState(0);
|
|
8662
8899
|
const [pathCount, setPathCount] = useState(0);
|
|
8663
|
-
const [zoom, setZoom] = useState(1);
|
|
8664
8900
|
const [currentError, setCurrentError] = useState(null);
|
|
8665
8901
|
const overlayRef = useRef(null);
|
|
8666
8902
|
// const mapRef = useMap();
|
|
8667
8903
|
const [isGoogleMapsReady, setIsGoogleMapsReady] = useState(false);
|
|
8668
8904
|
const [hasInitializedBounds, setHasInitializedBounds] = useState(false);
|
|
8669
|
-
const { clearSubBoundaryBorder } = useSubBoundaryBorderStore();
|
|
8905
|
+
const { clearSubBoundaryBorder, clearObstacles } = useSubBoundaryBorderStore();
|
|
8670
8906
|
const currentProcessMowingStatusRef = useRef(false);
|
|
8907
|
+
const { updateProcessStateIsMowing, processStateIsMowing } = useProcessMowingState();
|
|
8908
|
+
const [mowPartitionData, setMowPartitionData] = useState(null);
|
|
8909
|
+
// Debug相关状态
|
|
8910
|
+
const [debugInfo, setDebugInfo] = useState({});
|
|
8671
8911
|
// 处理地图分区边界
|
|
8672
8912
|
const partitionBoundary = useMemo(() => {
|
|
8673
8913
|
const allBoundaryElements = [];
|
|
@@ -8690,22 +8930,67 @@ const MowerMapRenderer = forwardRef(({ mapConfig, modelType, mapRef, mapJson, pa
|
|
|
8690
8930
|
}, [mapConfig]);
|
|
8691
8931
|
const mergedAntennaConfig = useMemo(() => antennaConfig, [antennaConfig]);
|
|
8692
8932
|
const mowerPositionData = useMemo(() => {
|
|
8933
|
+
// realTimeData 中包含三个种类的数据,之需要实时坐标的数据即可。
|
|
8934
|
+
if (!realTimeData || realTimeData.length === 0)
|
|
8935
|
+
return {
|
|
8936
|
+
postureX: 0,
|
|
8937
|
+
postureY: 0,
|
|
8938
|
+
postureTheta: 0,
|
|
8939
|
+
vehicleState: RobotStatus.DISCONNECTED,
|
|
8940
|
+
};
|
|
8941
|
+
let currentPositionData;
|
|
8942
|
+
if (realTimeData.length === 1 && realTimeData[0].type === RealTimeDataType.LOCATION) {
|
|
8943
|
+
currentPositionData = realTimeData[0];
|
|
8944
|
+
}
|
|
8945
|
+
else {
|
|
8946
|
+
currentPositionData = realTimeData?.find((item) => item.type === RealTimeDataType.LOCATION);
|
|
8947
|
+
}
|
|
8948
|
+
if (!currentPositionData)
|
|
8949
|
+
return undefined;
|
|
8693
8950
|
return {
|
|
8694
8951
|
postureTheta: currentPositionData?.postureTheta
|
|
8695
8952
|
? Number(currentPositionData.postureTheta)
|
|
8696
8953
|
: 0,
|
|
8697
8954
|
postureX: currentPositionData?.postureX ? Number(currentPositionData.postureX) : 0,
|
|
8698
8955
|
postureY: currentPositionData?.postureY ? Number(currentPositionData.postureY) : 0,
|
|
8699
|
-
|
|
8700
|
-
|
|
8956
|
+
lastPostureTheta: currentPositionData?.lastPostureTheta
|
|
8957
|
+
? Number(currentPositionData.lastPostureTheta)
|
|
8958
|
+
: 0,
|
|
8959
|
+
lastPostureX: currentPositionData?.lastPostureX
|
|
8960
|
+
? Number(currentPositionData.lastPostureX)
|
|
8961
|
+
: 0,
|
|
8962
|
+
lastPostureY: currentPositionData?.lastPostureY
|
|
8963
|
+
? Number(currentPositionData.lastPostureY)
|
|
8964
|
+
: 0,
|
|
8965
|
+
vehicleState: currentPositionData?.vehicleState || RobotStatus.DISCONNECTED,
|
|
8701
8966
|
};
|
|
8702
|
-
}, [
|
|
8703
|
-
console.log('mowerPositionData==', currentPositionData, mowerPositionData);
|
|
8967
|
+
}, [realTimeData, modelType]);
|
|
8704
8968
|
// 处理错误
|
|
8705
8969
|
const handleError = (error) => {
|
|
8706
8970
|
setCurrentError(error);
|
|
8707
8971
|
onError?.(error);
|
|
8708
8972
|
};
|
|
8973
|
+
const fitBounds = useCallback(() => {
|
|
8974
|
+
if (!mapJson || !mapRef)
|
|
8975
|
+
return null;
|
|
8976
|
+
// 计算边界
|
|
8977
|
+
const bounds = calculateMapBounds(mapJson);
|
|
8978
|
+
if (!bounds) {
|
|
8979
|
+
handleError('无法计算地图边界');
|
|
8980
|
+
return;
|
|
8981
|
+
}
|
|
8982
|
+
// 将自定义边界转换为Google Maps LatLngBounds(使用有效的GPS坐标)
|
|
8983
|
+
const validBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
|
|
8984
|
+
// 地图数据中的坐标格式是 [longitude, latitude]
|
|
8985
|
+
const swLat = validBounds.sw[1] + defaultTransform.y;
|
|
8986
|
+
const swLng = validBounds.sw[0] + defaultTransform.x;
|
|
8987
|
+
const neLat = validBounds.ne[1] + defaultTransform.y;
|
|
8988
|
+
const neLng = validBounds.ne[0] + defaultTransform.x;
|
|
8989
|
+
const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
|
|
8990
|
+
new window.google.maps.LatLng(neLat, neLng) // 东北角
|
|
8991
|
+
);
|
|
8992
|
+
mapRef.fitBounds(googleBounds);
|
|
8993
|
+
}, [mapJson, mapRef, defaultTransform]);
|
|
8709
8994
|
// 初始化Google Maps叠加层
|
|
8710
8995
|
const initializeGoogleMapsOverlay = async () => {
|
|
8711
8996
|
if (!mapJson)
|
|
@@ -8729,7 +9014,8 @@ const MowerMapRenderer = forwardRef(({ mapConfig, modelType, mapRef, mapJson, pa
|
|
|
8729
9014
|
return;
|
|
8730
9015
|
}
|
|
8731
9016
|
// 将自定义边界转换为Google Maps LatLngBounds(使用有效的GPS坐标)
|
|
8732
|
-
|
|
9017
|
+
// 这里需要使用0度,需要原始的坐标点去计算元素的实际位置,只有在fitbounds的时候才需要使用旋转后的坐标点
|
|
9018
|
+
const validBounds = getValidGpsBounds(mapJson, 0);
|
|
8733
9019
|
// 地图数据中的坐标格式是 [longitude, latitude]
|
|
8734
9020
|
const swLat = validBounds.sw[1];
|
|
8735
9021
|
const swLng = validBounds.sw[0];
|
|
@@ -8738,17 +9024,13 @@ const MowerMapRenderer = forwardRef(({ mapConfig, modelType, mapRef, mapJson, pa
|
|
|
8738
9024
|
const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
|
|
8739
9025
|
new window.google.maps.LatLng(neLat, neLng) // 东北角
|
|
8740
9026
|
);
|
|
8741
|
-
console.log('使用有效GPS坐标创建边界:', {
|
|
8742
|
-
sw: { lat: swLat, lng: swLng },
|
|
8743
|
-
ne: { lat: neLat, lng: neLng },
|
|
8744
|
-
});
|
|
8745
9027
|
// 如果已经存在叠加层,先移除它
|
|
8746
9028
|
if (overlayRef.current) {
|
|
8747
9029
|
overlayRef.current.setMap(null);
|
|
8748
9030
|
overlayRef.current = null;
|
|
8749
9031
|
}
|
|
8750
9032
|
// 创建叠加层
|
|
8751
|
-
const overlay = new MowerMapOverlay(googleBounds, mapJson, mowerPositionData, pathJson || {}, isEditMode, mergedMapConfig, mergedAntennaConfig, (count) => {
|
|
9033
|
+
const overlay = new MowerMapOverlay(googleBounds, mapJson, partitionBoundary, mowerPositionData, modelType, pathJson || {}, isEditMode, unitType, language, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
|
|
8752
9034
|
setElementCount(count);
|
|
8753
9035
|
onMapLoad?.(count);
|
|
8754
9036
|
}, (count) => {
|
|
@@ -8769,39 +9051,32 @@ const MowerMapRenderer = forwardRef(({ mapConfig, modelType, mapRef, mapJson, pa
|
|
|
8769
9051
|
handleError(`初始化Google Maps叠加层失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
8770
9052
|
}
|
|
8771
9053
|
};
|
|
8772
|
-
const
|
|
8773
|
-
|
|
8774
|
-
const
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
overlayRef.current.updatePathData(newRealTimeData.pathData);
|
|
8786
|
-
}
|
|
8787
|
-
overlayRef.current.updateBoundaryLabelInfo(newRealTimeData.pathData);
|
|
8788
|
-
};
|
|
9054
|
+
const resetInCharginPie = useCallback(() => {
|
|
9055
|
+
const elements = MapDataProcessor.processMapData(mapJson, mergedMapConfig);
|
|
9056
|
+
const chargingPiles = elements.find((element) => element.type === 'charging_pile');
|
|
9057
|
+
if (!overlayRef.current)
|
|
9058
|
+
return;
|
|
9059
|
+
// 如果在充电桩上,则直接更新位置到充电桩的位置
|
|
9060
|
+
overlayRef.current.updatePosition({
|
|
9061
|
+
...mowerPositionData,
|
|
9062
|
+
postureX: chargingPiles?.originalData.position[0],
|
|
9063
|
+
postureY: chargingPiles?.originalData.position[1],
|
|
9064
|
+
postureTheta: chargingPiles?.originalData.direction - Math.PI || 0,
|
|
9065
|
+
}, 0);
|
|
9066
|
+
}, [mapJson, mowerPositionData]);
|
|
8789
9067
|
// 初始化效果
|
|
8790
9068
|
useEffect(() => {
|
|
8791
9069
|
initializeGoogleMapsOverlay();
|
|
8792
|
-
console.log('init mow map');
|
|
8793
9070
|
// 清理函数
|
|
8794
9071
|
return () => {
|
|
8795
9072
|
clearSubBoundaryBorder();
|
|
9073
|
+
clearObstacles();
|
|
9074
|
+
updateProcessStateIsMowing(false);
|
|
8796
9075
|
currentProcessMowingStatusRef.current = false;
|
|
8797
9076
|
if (overlayRef.current) {
|
|
8798
9077
|
overlayRef.current.setMap(null);
|
|
8799
9078
|
overlayRef.current = null;
|
|
8800
9079
|
}
|
|
8801
|
-
if (svgMapViewRef.current) {
|
|
8802
|
-
svgMapViewRef.current.destroy();
|
|
8803
|
-
svgMapViewRef.current = null;
|
|
8804
|
-
}
|
|
8805
9080
|
};
|
|
8806
9081
|
}, [mapJson, pathJson, mergedMapConfig, mergedAntennaConfig]);
|
|
8807
9082
|
// 监听编辑模式变化
|
|
@@ -8821,13 +9096,13 @@ const MowerMapRenderer = forwardRef(({ mapConfig, modelType, mapRef, mapJson, pa
|
|
|
8821
9096
|
return;
|
|
8822
9097
|
const elements = MapDataProcessor.processMapData(mapJson, mergedMapConfig);
|
|
8823
9098
|
const chargingPiles = elements.find((element) => element.type === 'charging_pile');
|
|
8824
|
-
console.log('chargingPiles==', chargingPiles, currentPositionData);
|
|
8825
9099
|
if (!mowerPositionData || !overlayRef.current)
|
|
8826
9100
|
return;
|
|
8827
9101
|
const inChargingPiles = [RobotStatus.CHARGING, RobotStatus.PARKED];
|
|
8828
9102
|
const isOffLine = mowerPositionData.vehicleState === RobotStatus.DISCONNECTED;
|
|
8829
9103
|
const isInChargingPile = inChargingPiles.includes(mowerPositionData.vehicleState);
|
|
8830
|
-
//
|
|
9104
|
+
// 如果在充电桩上,则直接更新位置到充电桩的位置
|
|
9105
|
+
console.log('usefeect mowerPositionData----->', mowerPositionData, isInChargingPile);
|
|
8831
9106
|
if (isInChargingPile) {
|
|
8832
9107
|
overlayRef.current.updatePosition({
|
|
8833
9108
|
...mowerPositionData,
|
|
@@ -8837,68 +9112,244 @@ const MowerMapRenderer = forwardRef(({ mapConfig, modelType, mapRef, mapJson, pa
|
|
|
8837
9112
|
}, 0);
|
|
8838
9113
|
}
|
|
8839
9114
|
else {
|
|
9115
|
+
// 如果车辆是disabled状态或者超出边界(默认超过1000m),则更新位置到上一次的位置
|
|
8840
9116
|
const positonOutOfRange = isOutOfRange(mowerPositionData);
|
|
8841
9117
|
const positionValid = isInvalidPosition(mowerPositionData);
|
|
8842
|
-
const
|
|
9118
|
+
const isStandby = mowerPositionData.vehicleState === RobotStatus.STANDBY;
|
|
8843
9119
|
if (positonOutOfRange || positionValid || isOffLine) {
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
9120
|
+
// 初始信息是通过后端接口获取的,此时当前位置数据不可用的时候,可以取上一次的位置数据
|
|
9121
|
+
// mowerPositionData 中可能会包含上一次的位置数据,
|
|
9122
|
+
const lastPostureX = mowerPositionData.lastPostureX;
|
|
9123
|
+
const lastPostureY = mowerPositionData.lastPostureY;
|
|
9124
|
+
const lastPostureTheta = mowerPositionData.lastPostureTheta;
|
|
9125
|
+
if (lastPostureX && lastPostureY && lastPostureTheta) {
|
|
9126
|
+
overlayRef.current.updatePositionByLastPosition(mowerPositionData);
|
|
9127
|
+
}
|
|
9128
|
+
else {
|
|
9129
|
+
overlayRef.current.updatePositionByLastPosition({
|
|
9130
|
+
...mowerPositionData,
|
|
9131
|
+
postureX: chargingPiles?.originalData.position[0],
|
|
9132
|
+
postureY: chargingPiles?.originalData.position[1],
|
|
9133
|
+
postureTheta: chargingPiles?.originalData.direction - Math.PI || 0,
|
|
9134
|
+
});
|
|
9135
|
+
}
|
|
8850
9136
|
}
|
|
8851
9137
|
else {
|
|
8852
|
-
overlayRef.current.updatePosition(mowerPositionData,
|
|
9138
|
+
overlayRef.current.updatePosition(mowerPositionData, isStandby ? 0 : 2000);
|
|
8853
9139
|
}
|
|
8854
9140
|
}
|
|
8855
|
-
}, [
|
|
9141
|
+
}, [mowerPositionData]);
|
|
9142
|
+
// 更新debug信息
|
|
9143
|
+
useEffect(() => {
|
|
9144
|
+
if (!debug)
|
|
9145
|
+
return;
|
|
9146
|
+
const updateDebugInfo = () => {
|
|
9147
|
+
const newDebugInfo = {};
|
|
9148
|
+
// 获取地图GPS边界
|
|
9149
|
+
if (mapJson) {
|
|
9150
|
+
newDebugInfo.mapBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
|
|
9151
|
+
}
|
|
9152
|
+
// 获取SVG viewBox信息
|
|
9153
|
+
if (overlayRef.current) {
|
|
9154
|
+
const overlay = overlayRef.current;
|
|
9155
|
+
const svgMapView = overlay.getSvgMapView?.();
|
|
9156
|
+
if (svgMapView) {
|
|
9157
|
+
const viewBoxInfo = svgMapView.getViewBoxInfo();
|
|
9158
|
+
// 计算实际的米单位数据(除以缩放比例)
|
|
9159
|
+
const SCALE_FACTOR = 50; // 根据项目中的缩放因子
|
|
9160
|
+
newDebugInfo.viewBox = {
|
|
9161
|
+
x: viewBoxInfo.x / SCALE_FACTOR,
|
|
9162
|
+
y: viewBoxInfo.y / SCALE_FACTOR,
|
|
9163
|
+
width: viewBoxInfo.width / SCALE_FACTOR,
|
|
9164
|
+
height: viewBoxInfo.height / SCALE_FACTOR,
|
|
9165
|
+
scale: SCALE_FACTOR,
|
|
9166
|
+
// 计算左下角和右上角坐标
|
|
9167
|
+
sw: {
|
|
9168
|
+
x: viewBoxInfo.x / SCALE_FACTOR,
|
|
9169
|
+
y: viewBoxInfo.y / SCALE_FACTOR,
|
|
9170
|
+
},
|
|
9171
|
+
ne: {
|
|
9172
|
+
x: (viewBoxInfo.x + viewBoxInfo.width) / SCALE_FACTOR,
|
|
9173
|
+
y: (viewBoxInfo.y + viewBoxInfo.height) / SCALE_FACTOR,
|
|
9174
|
+
},
|
|
9175
|
+
};
|
|
9176
|
+
}
|
|
9177
|
+
}
|
|
9178
|
+
// 获取当前割草机位置
|
|
9179
|
+
if (mowerPositionData) {
|
|
9180
|
+
newDebugInfo.mowerPosition = {
|
|
9181
|
+
x: mowerPositionData.postureX || 0,
|
|
9182
|
+
y: mowerPositionData.postureY || 0,
|
|
9183
|
+
theta: mowerPositionData.postureTheta || 0,
|
|
9184
|
+
lastX: mowerPositionData.lastPostureX || 0,
|
|
9185
|
+
lastY: mowerPositionData.lastPostureY || 0,
|
|
9186
|
+
lastTheta: mowerPositionData.lastPostureTheta || 0,
|
|
9187
|
+
vehicleState: mowerPositionData.vehicleState || RobotStatus.UNKNOWN,
|
|
9188
|
+
vehicleStateText: getVehicleStateText(mowerPositionData.vehicleState || RobotStatus.UNKNOWN),
|
|
9189
|
+
};
|
|
9190
|
+
}
|
|
9191
|
+
// 获取当前割草地块数据
|
|
9192
|
+
newDebugInfo.partitionData = mowPartitionData;
|
|
9193
|
+
setDebugInfo(newDebugInfo);
|
|
9194
|
+
};
|
|
9195
|
+
updateDebugInfo();
|
|
9196
|
+
}, [debug, mapJson, mowerPositionData, mowPartitionData, defaultTransform]);
|
|
9197
|
+
// 当关键数据变化时立即更新debug信息
|
|
9198
|
+
useEffect(() => {
|
|
9199
|
+
if (!debug)
|
|
9200
|
+
return;
|
|
9201
|
+
const updateDebugInfo = () => {
|
|
9202
|
+
const newDebugInfo = {};
|
|
9203
|
+
// 获取地图GPS边界
|
|
9204
|
+
if (mapJson) {
|
|
9205
|
+
newDebugInfo.mapBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
|
|
9206
|
+
}
|
|
9207
|
+
// 获取SVG viewBox信息
|
|
9208
|
+
if (overlayRef.current) {
|
|
9209
|
+
const overlay = overlayRef.current;
|
|
9210
|
+
const svgMapView = overlay.getSvgMapView?.();
|
|
9211
|
+
if (svgMapView) {
|
|
9212
|
+
const viewBoxInfo = svgMapView.getViewBoxInfo();
|
|
9213
|
+
// 计算实际的米单位数据(除以缩放比例)
|
|
9214
|
+
const SCALE_FACTOR = 50; // 根据项目中的缩放因子
|
|
9215
|
+
newDebugInfo.viewBox = {
|
|
9216
|
+
x: viewBoxInfo.x / SCALE_FACTOR,
|
|
9217
|
+
y: viewBoxInfo.y / SCALE_FACTOR,
|
|
9218
|
+
width: viewBoxInfo.width / SCALE_FACTOR,
|
|
9219
|
+
height: viewBoxInfo.height / SCALE_FACTOR,
|
|
9220
|
+
scale: SCALE_FACTOR,
|
|
9221
|
+
// 计算左下角和右上角坐标
|
|
9222
|
+
sw: {
|
|
9223
|
+
x: viewBoxInfo.x / SCALE_FACTOR,
|
|
9224
|
+
y: viewBoxInfo.y / SCALE_FACTOR,
|
|
9225
|
+
},
|
|
9226
|
+
ne: {
|
|
9227
|
+
x: (viewBoxInfo.x + viewBoxInfo.width) / SCALE_FACTOR,
|
|
9228
|
+
y: (viewBoxInfo.y + viewBoxInfo.height) / SCALE_FACTOR,
|
|
9229
|
+
},
|
|
9230
|
+
};
|
|
9231
|
+
}
|
|
9232
|
+
}
|
|
9233
|
+
// 获取当前割草机位置
|
|
9234
|
+
if (mowerPositionData) {
|
|
9235
|
+
newDebugInfo.mowerPosition = {
|
|
9236
|
+
x: mowerPositionData.postureX || 0,
|
|
9237
|
+
y: mowerPositionData.postureY || 0,
|
|
9238
|
+
theta: mowerPositionData.postureTheta || 0,
|
|
9239
|
+
lastX: mowerPositionData.lastPostureX || 0,
|
|
9240
|
+
lastY: mowerPositionData.lastPostureY || 0,
|
|
9241
|
+
lastTheta: mowerPositionData.lastPostureTheta || 0,
|
|
9242
|
+
vehicleState: mowerPositionData.vehicleState || RobotStatus.UNKNOWN,
|
|
9243
|
+
vehicleStateText: getVehicleStateText(mowerPositionData.vehicleState || RobotStatus.UNKNOWN),
|
|
9244
|
+
};
|
|
9245
|
+
}
|
|
9246
|
+
// 获取当前割草地块数据
|
|
9247
|
+
newDebugInfo.partitionData = mowPartitionData;
|
|
9248
|
+
setDebugInfo(newDebugInfo);
|
|
9249
|
+
};
|
|
9250
|
+
updateDebugInfo();
|
|
9251
|
+
}, [debug, mowerPositionData, mowPartitionData]);
|
|
8856
9252
|
useEffect(() => {
|
|
9253
|
+
if (!realTimeData || realTimeData.length === 0 || !Array.isArray(realTimeData)) {
|
|
9254
|
+
return;
|
|
9255
|
+
}
|
|
9256
|
+
console.log('usefeect realTimeData----->', realTimeData, mapJson, pathJson, overlayRef.current);
|
|
9257
|
+
let curMowPartitionData = mowPartitionData;
|
|
9258
|
+
// realtime中包含当前割草任务的数据,根据数据进行path路径和边界的高亮操作,
|
|
9259
|
+
const mowingPartition = realTimeData.find((item) => item.type === RealTimeDataType.PARTITION);
|
|
9260
|
+
if (mowingPartition) {
|
|
9261
|
+
setMowPartitionData(mowingPartition);
|
|
9262
|
+
curMowPartitionData = mowingPartition;
|
|
9263
|
+
}
|
|
9264
|
+
const positionData = realTimeData?.find(item => item?.type === RealTimeDataType.LOCATION);
|
|
9265
|
+
const statusData = realTimeData?.find(item => item?.type === RealTimeDataType.STATUS);
|
|
9266
|
+
if (statusData || positionData) {
|
|
9267
|
+
const currentStatus = statusData?.vehicleState || positionData?.vehicleState;
|
|
9268
|
+
// 车辆回桩不会回传最后的park的位置,所以根据实时数据的状态数据判断车辆回到桩上
|
|
9269
|
+
if ([RobotStatus.CHARGING, RobotStatus.PARKED].includes(currentStatus || RobotStatus.UNKNOWN)) {
|
|
9270
|
+
resetInCharginPie();
|
|
9271
|
+
}
|
|
9272
|
+
else if (currentStatus === RobotStatus.WORKING) {
|
|
9273
|
+
// 兜底收不到割草地块的实时数据,使用状态来兜底
|
|
9274
|
+
overlayRef.current.resetBorderLayerHighlight();
|
|
9275
|
+
setMowPartitionData(null);
|
|
9276
|
+
curMowPartitionData = null;
|
|
9277
|
+
}
|
|
9278
|
+
else if (currentStatus === RobotStatus.MOWING && (curMowPartitionData && !curMowPartitionData?.partitionIds)) {
|
|
9279
|
+
// 如果当前是割草状态,但是地块数据初始化过且不存在则认为是全局割草,则把所有地块都高亮
|
|
9280
|
+
const allPartitionIds = mapJson?.sub_maps?.map(item => item?.id);
|
|
9281
|
+
setMowPartitionData({
|
|
9282
|
+
partitionIds: allPartitionIds
|
|
9283
|
+
});
|
|
9284
|
+
curMowPartitionData = {
|
|
9285
|
+
partitionIds: allPartitionIds
|
|
9286
|
+
};
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
8857
9289
|
if (!mapJson ||
|
|
8858
9290
|
!pathJson ||
|
|
8859
|
-
!
|
|
8860
|
-
realTimeData.length === 0 ||
|
|
8861
|
-
!Array.isArray(realTimeData))
|
|
9291
|
+
!overlayRef.current)
|
|
8862
9292
|
return;
|
|
8863
9293
|
// 根据后端推送的实时数据,进行不同处理
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
useImperativeHandle(ref, () => ({
|
|
8870
|
-
setZoom: (newZoom) => {
|
|
8871
|
-
setZoom(newZoom);
|
|
8872
|
-
if (svgMapViewRef.current) {
|
|
8873
|
-
svgMapViewRef.current.setZoom(newZoom);
|
|
9294
|
+
if (curMowPartitionData) {
|
|
9295
|
+
const isMowing = curMowPartitionData?.partitionIds && curMowPartitionData.partitionIds.length > 0;
|
|
9296
|
+
overlayRef.current.updateMowPartitionData(curMowPartitionData);
|
|
9297
|
+
if (!isMowing) {
|
|
9298
|
+
overlayRef.current.resetBorderLayerHighlight();
|
|
8874
9299
|
}
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
getZoom: () => zoom,
|
|
8878
|
-
resetView: () => {
|
|
8879
|
-
if (svgMapViewRef.current) {
|
|
8880
|
-
svgMapViewRef.current.resetTransform();
|
|
9300
|
+
else {
|
|
9301
|
+
overlayRef.current.setBorderLayerHighlight(curMowPartitionData);
|
|
8881
9302
|
}
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
9303
|
+
}
|
|
9304
|
+
// 如果一次性推送的是多条数据,则把多条数据处理后存入pathData,然后更新路径数据和边界标签信息
|
|
9305
|
+
// 如果一次只推送一条数据,则只解析里面的进度数据,然后更新边界标签信息,剩下的实时轨迹数据由车辆运动产生时存入pathData
|
|
9306
|
+
if (realTimeData.length > 1) {
|
|
9307
|
+
const { pathData, isMowing } = handleMultipleRealTimeData({
|
|
9308
|
+
realTimeData,
|
|
9309
|
+
isMowing: processStateIsMowing,
|
|
9310
|
+
pathData: pathJson,
|
|
9311
|
+
partitionBoundary,
|
|
9312
|
+
});
|
|
9313
|
+
updateProcessStateIsMowing(isMowing);
|
|
9314
|
+
if (pathData) {
|
|
9315
|
+
overlayRef.current.updatePathData(pathData, curMowPartitionData);
|
|
9316
|
+
overlayRef.current.updateBoundaryLabelInfo(pathData);
|
|
8888
9317
|
}
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
9318
|
+
}
|
|
9319
|
+
else {
|
|
9320
|
+
const { isMowing, pathData } = getProcessMowingDataFromRealTimeData({
|
|
9321
|
+
realTimeData,
|
|
9322
|
+
isMowing: processStateIsMowing,
|
|
9323
|
+
pathData: pathJson,
|
|
9324
|
+
});
|
|
9325
|
+
updateProcessStateIsMowing(isMowing);
|
|
9326
|
+
overlayRef.current.updatePathData(pathData, curMowPartitionData);
|
|
9327
|
+
// 更新进度数据
|
|
9328
|
+
if (pathData) {
|
|
9329
|
+
overlayRef.current.updateBoundaryLabelInfo(pathData);
|
|
8901
9330
|
}
|
|
9331
|
+
}
|
|
9332
|
+
}, [realTimeData, mapJson, pathJson]);
|
|
9333
|
+
useEffect(() => {
|
|
9334
|
+
if (!overlayRef.current || !defaultTransform)
|
|
9335
|
+
return;
|
|
9336
|
+
console.log('defaultTransform----->', defaultTransform, overlayRef.current, mapJson);
|
|
9337
|
+
overlayRef.current?.setTransform(defaultTransform);
|
|
9338
|
+
const validBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
|
|
9339
|
+
// 地图数据中的坐标格式是 [longitude, latitude]
|
|
9340
|
+
const swLat = validBounds.sw[1] + defaultTransform.y;
|
|
9341
|
+
const swLng = validBounds.sw[0] + defaultTransform.x;
|
|
9342
|
+
const neLat = validBounds.ne[1] + defaultTransform.y;
|
|
9343
|
+
const neLng = validBounds.ne[0] + defaultTransform.x;
|
|
9344
|
+
const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
|
|
9345
|
+
new window.google.maps.LatLng(neLat, neLng) // 东北角
|
|
9346
|
+
);
|
|
9347
|
+
mapRef.fitBounds(googleBounds);
|
|
9348
|
+
}, [defaultTransform]);
|
|
9349
|
+
// 提供ref方法
|
|
9350
|
+
useImperativeHandle(ref, () => ({
|
|
9351
|
+
fitToView: () => {
|
|
9352
|
+
fitBounds();
|
|
8902
9353
|
},
|
|
8903
9354
|
getOverlay: () => {
|
|
8904
9355
|
return overlayRef.current;
|
|
@@ -8927,13 +9378,35 @@ const MowerMapRenderer = forwardRef(({ mapConfig, modelType, mapRef, mapJson, pa
|
|
|
8927
9378
|
getElementCount: () => elementCount,
|
|
8928
9379
|
getPathCount: () => pathCount,
|
|
8929
9380
|
isGoogleMapsReady: () => isGoogleMapsReady,
|
|
9381
|
+
setTransform: (t) => overlayRef.current?.setTransform(t),
|
|
9382
|
+
resetToDefaultTransform: () => overlayRef.current?.resetToDefaultTransform(),
|
|
8930
9383
|
}));
|
|
9384
|
+
// Debug信息组件
|
|
9385
|
+
const DebugInfo = () => {
|
|
9386
|
+
if (!debug)
|
|
9387
|
+
return null;
|
|
9388
|
+
return (jsxs("div", { style: {
|
|
9389
|
+
position: 'fixed',
|
|
9390
|
+
bottom: '10px',
|
|
9391
|
+
left: '10px',
|
|
9392
|
+
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|
9393
|
+
color: 'white',
|
|
9394
|
+
padding: '10px',
|
|
9395
|
+
borderRadius: '5px',
|
|
9396
|
+
fontSize: '12px',
|
|
9397
|
+
fontFamily: 'monospace',
|
|
9398
|
+
zIndex: 10000,
|
|
9399
|
+
maxWidth: '300px',
|
|
9400
|
+
lineHeight: '1.4',
|
|
9401
|
+
}, children: [jsx("div", { style: { fontWeight: 'bold', marginBottom: '8px' }, children: "\uD83D\uDC1B Debug Info" }), debugInfo.mapBounds && (jsxs("div", { style: { marginBottom: '6px' }, children: [jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDCCD Map GPS Bounds:" }), jsxs("div", { children: ["SW: [", debugInfo.mapBounds.sw[0].toFixed(6), ", ", debugInfo.mapBounds.sw[1].toFixed(6), "]"] }), jsxs("div", { children: ["NE: [", debugInfo.mapBounds.ne[0].toFixed(6), ", ", debugInfo.mapBounds.ne[1].toFixed(6), "]"] })] })), debugInfo.viewBox && (jsxs("div", { style: { marginBottom: '6px' }, children: [jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDCD0 SVG ViewBox (meters):" }), jsxs("div", { children: ["SW: [", debugInfo.viewBox.sw.x.toFixed(2), ", ", debugInfo.viewBox.sw.y.toFixed(2), "]"] }), jsxs("div", { children: ["NE: [", debugInfo.viewBox.ne.x.toFixed(2), ", ", debugInfo.viewBox.ne.y.toFixed(2), "]"] }), jsxs("div", { children: ["Size: ", debugInfo.viewBox.width.toFixed(2), "m \u00D7 ", debugInfo.viewBox.height.toFixed(2), "m"] }), jsxs("div", { children: ["Scale: 1:", debugInfo.viewBox.scale] })] })), debugInfo.mowerPosition && (jsxs("div", { style: { marginBottom: '6px' }, children: [jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDE9C Mower Position:" }), jsxs("div", { children: ["Current: X=", debugInfo.mowerPosition.x.toFixed(2), ", Y=", debugInfo.mowerPosition.y.toFixed(2)] }), jsxs("div", { children: ["Theta: ", ((debugInfo.mowerPosition.theta * 180) / Math.PI).toFixed(1), "\u00B0"] }), jsxs("div", { children: ["Last: X=", debugInfo.mowerPosition.lastX.toFixed(2), ", Y=", debugInfo.mowerPosition.lastY.toFixed(2)] }), jsxs("div", { children: ["Last Theta: ", ((debugInfo.mowerPosition.lastTheta * 180) / Math.PI).toFixed(1), "\u00B0"] }), jsxs("div", { children: ["Status: ", debugInfo.mowerPosition.vehicleStateText, " (", debugInfo.mowerPosition.vehicleState, ")"] })] })), debugInfo.partitionData && (jsxs("div", { style: { marginBottom: '6px' }, children: [jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDD32 Mow Partition Data:" }), jsxs("div", { children: ["Type: ", debugInfo.partitionData.type || 'N/A'] }), debugInfo.partitionData.partitionIds &&
|
|
9402
|
+
debugInfo.partitionData.partitionIds.length > 0 ? (jsxs("div", { children: ["Active IDs: [", debugInfo.partitionData.partitionIds.join(', '), "]"] })) : (jsx("div", { children: "No active partitions" })), debugInfo.partitionData.time && (jsxs("div", { children: ["Updated: ", new Date(debugInfo.partitionData.time).toLocaleTimeString()] }))] })), !debugInfo.partitionData && (jsxs("div", { style: { marginBottom: '6px' }, children: [jsx("div", { style: { fontWeight: 'bold' }, children: "\uD83D\uDD32 Mow Partition Data:" }), jsx("div", { style: { color: '#888' }, children: "No partition data available" })] }))] }));
|
|
9403
|
+
};
|
|
8931
9404
|
// 错误显示
|
|
8932
9405
|
if (currentError) {
|
|
8933
|
-
return (
|
|
9406
|
+
return (jsxs("div", { className: className, style: style, children: [jsxs("div", { style: { color: 'red', padding: '10px' }, children: ["\u9519\u8BEF: ", currentError] }), jsx(DebugInfo, {})] }));
|
|
8934
9407
|
}
|
|
8935
|
-
// 使用goole maps
|
|
8936
|
-
return null;
|
|
9408
|
+
// 使用goole maps自定义叠加层,返回debug信息(如果启用)
|
|
9409
|
+
return debug ? jsx(DebugInfo, {}) : null;
|
|
8937
9410
|
});
|
|
8938
9411
|
MowerMapRenderer.displayName = 'MowerMapRenderer';
|
|
8939
9412
|
|