@fleet-frontend/mower-maps 0.0.2 → 0.0.3

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/index.esm.js CHANGED
@@ -1,350 +1,5 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import React, { forwardRef, useMemo, useImperativeHandle, useState, Children, useEffect, useContext, useRef } from 'react';
3
- import { createPortal } from 'react-dom';
4
-
5
- function _objectWithoutPropertiesLoose(r, e) {
6
- if (null == r) return {};
7
- var t = {};
8
- for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
9
- if (-1 !== e.indexOf(n)) continue;
10
- t[n] = r[n];
11
- }
12
- return t;
13
- }
14
-
15
- const APILoadingStatus = {
16
- NOT_LOADED: 'NOT_LOADED',
17
- LOADED: 'LOADED'};
18
- const APIProviderContext = React.createContext(null);
19
-
20
- function useApiLoadingStatus() {
21
- var _useContext;
22
- return ((_useContext = useContext(APIProviderContext)) == null ? void 0 : _useContext.status) || APILoadingStatus.NOT_LOADED;
23
- }
24
-
25
- /**
26
- * Hook to check if the Maps JavaScript API is loaded
27
- */
28
- function useApiIsLoaded() {
29
- const status = useApiLoadingStatus();
30
- return status === APILoadingStatus.LOADED;
31
- }
32
-
33
- const GoogleMapsContext = React.createContext(null);
34
-
35
- const shownMessages = new Set();
36
- function logErrorOnce(...args) {
37
- const key = JSON.stringify(args);
38
- if (!shownMessages.has(key)) {
39
- shownMessages.add(key);
40
- console.error(...args);
41
- }
42
- }
43
-
44
- /**
45
- * Retrieves a map-instance from the context. This is either an instance
46
- * identified by id or the parent map instance if no id is specified.
47
- * Returns null if neither can be found.
48
- */
49
- const useMap = (id = null) => {
50
- const ctx = useContext(APIProviderContext);
51
- const {
52
- map
53
- } = useContext(GoogleMapsContext) || {};
54
- if (ctx === null) {
55
- logErrorOnce('useMap(): failed to retrieve APIProviderContext. ' + 'Make sure that the <APIProvider> component exists and that the ' + 'component you are calling `useMap()` from is a sibling of the ' + '<APIProvider>.');
56
- return null;
57
- }
58
- const {
59
- mapInstances
60
- } = ctx;
61
- // if an id is specified, the corresponding map or null is returned
62
- if (id !== null) return mapInstances[id] || null;
63
- // otherwise, return the closest ancestor
64
- if (map) return map;
65
- // finally, return the default map instance
66
- return mapInstances['default'] || null;
67
- };
68
-
69
- function useMapsLibrary(name) {
70
- const apiIsLoaded = useApiIsLoaded();
71
- const ctx = useContext(APIProviderContext);
72
- useEffect(() => {
73
- if (!apiIsLoaded || !ctx) return;
74
- // Trigger loading the libraries via our proxy-method.
75
- // The returned promise is ignored, since importLibrary will update loadedLibraries
76
- // list in the context, triggering a re-render.
77
- void ctx.importLibrary(name);
78
- }, [apiIsLoaded, ctx, name]);
79
- return (ctx == null ? void 0 : ctx.loadedLibraries[name]) || null;
80
- }
81
-
82
- /* eslint-disable @typescript-eslint/no-explicit-any */
83
- /**
84
- * Internally used to bind events to Maps JavaScript API objects.
85
- * @internal
86
- */
87
- function useMapsEventListener(target, name, callback) {
88
- useEffect(() => {
89
- if (!target || !name || !callback) return;
90
- const listener = google.maps.event.addListener(target, name, callback);
91
- return () => listener.remove();
92
- }, [target, name, callback]);
93
- }
94
-
95
- /**
96
- * Internally used to copy values from props into API-Objects
97
- * whenever they change.
98
- *
99
- * @example
100
- * usePropBinding(marker, 'position', position);
101
- *
102
- * @internal
103
- */
104
- function usePropBinding(object, prop, value) {
105
- useEffect(() => {
106
- if (!object) return;
107
- object[prop] = value;
108
- }, [object, prop, value]);
109
- }
110
-
111
- /* eslint-disable @typescript-eslint/no-explicit-any */
112
- /**
113
- * Internally used to bind events to DOM nodes.
114
- * @internal
115
- */
116
- function useDomEventListener(target, name, callback) {
117
- useEffect(() => {
118
- if (!target || !name || !callback) return;
119
- target.addEventListener(name, callback);
120
- return () => target.removeEventListener(name, callback);
121
- }, [target, name, callback]);
122
- }
123
- function isElementNode(node) {
124
- return node.nodeType === Node.ELEMENT_NODE;
125
- }
126
- const AdvancedMarkerContext = React.createContext(null);
127
- // [xPosition, yPosition] when the top left corner is [0, 0]
128
- const AdvancedMarkerAnchorPoint = {
129
- BOTTOM: ['50%', '100%']};
130
- const MarkerContent = ({
131
- children,
132
- styles,
133
- className,
134
- anchorPoint
135
- }) => {
136
- const [xTranslation, yTranslation] = anchorPoint != null ? anchorPoint : AdvancedMarkerAnchorPoint['BOTTOM'];
137
- let xTranslationFlipped = `-${xTranslation}`;
138
- let yTranslationFlipped = `-${yTranslation}`;
139
- if (xTranslation.trimStart().startsWith('-')) {
140
- xTranslationFlipped = xTranslation.substring(1);
141
- }
142
- if (yTranslation.trimStart().startsWith('-')) {
143
- yTranslationFlipped = yTranslation.substring(1);
144
- }
145
- // The "translate(50%, 100%)" is here to counter and reset the default anchoring of the advanced marker element
146
- // that comes from the api
147
- const transformStyle = `translate(50%, 100%) translate(${xTranslationFlipped}, ${yTranslationFlipped})`;
148
- return (
149
- /*#__PURE__*/
150
- // anchoring container
151
- React.createElement("div", {
152
- style: {
153
- transform: transformStyle
154
- }
155
- }, /*#__PURE__*/React.createElement("div", {
156
- className: className,
157
- style: styles
158
- }, children))
159
- );
160
- };
161
- function useAdvancedMarker(props) {
162
- const [marker, setMarker] = useState(null);
163
- const [contentContainer, setContentContainer] = useState(null);
164
- const map = useMap();
165
- const markerLibrary = useMapsLibrary('marker');
166
- const {
167
- children,
168
- onClick,
169
- className,
170
- onMouseEnter,
171
- onMouseLeave,
172
- onDrag,
173
- onDragStart,
174
- onDragEnd,
175
- collisionBehavior,
176
- clickable,
177
- draggable,
178
- position,
179
- title,
180
- zIndex
181
- } = props;
182
- const numChildren = Children.count(children);
183
- // create an AdvancedMarkerElement instance and add it to the map once available
184
- useEffect(() => {
185
- if (!map || !markerLibrary) return;
186
- const newMarker = new markerLibrary.AdvancedMarkerElement();
187
- newMarker.map = map;
188
- setMarker(newMarker);
189
- // create the container for marker content if there are children
190
- let contentElement = null;
191
- if (numChildren > 0) {
192
- contentElement = document.createElement('div');
193
- // We need some kind of flag to identify the custom marker content
194
- // in the infowindow component. Choosing a custom property instead of a className
195
- // to not encourage users to style the marker content directly.
196
- contentElement.isCustomMarker = true;
197
- newMarker.content = contentElement;
198
- setContentContainer(contentElement);
199
- }
200
- return () => {
201
- var _contentElement;
202
- newMarker.map = null;
203
- (_contentElement = contentElement) == null || _contentElement.remove();
204
- setMarker(null);
205
- setContentContainer(null);
206
- };
207
- }, [map, markerLibrary, numChildren]);
208
- // When no children are present we don't have our own wrapper div
209
- // which usually gets the user provided className. In this case
210
- // we set the className directly on the marker.content element that comes
211
- // with the AdvancedMarker.
212
- useEffect(() => {
213
- if (!(marker != null && marker.content) || !isElementNode(marker.content) || numChildren > 0) return;
214
- marker.content.className = className != null ? className : '';
215
- }, [marker, className, numChildren]);
216
- // copy other props
217
- usePropBinding(marker, 'position', position);
218
- usePropBinding(marker, 'title', title != null ? title : '');
219
- usePropBinding(marker, 'zIndex', zIndex);
220
- usePropBinding(marker, 'collisionBehavior', collisionBehavior);
221
- // set gmpDraggable from props (when unspecified, it's true if any drag-event
222
- // callbacks are specified)
223
- useEffect(() => {
224
- if (!marker) return;
225
- if (draggable !== undefined) marker.gmpDraggable = draggable;else if (onDrag || onDragStart || onDragEnd) marker.gmpDraggable = true;else marker.gmpDraggable = false;
226
- }, [marker, draggable, onDrag, onDragEnd, onDragStart]);
227
- // set gmpClickable from props (when unspecified, it's true if the onClick or one of
228
- // the hover events callbacks are specified)
229
- useEffect(() => {
230
- if (!marker) return;
231
- const gmpClickable = clickable !== undefined || Boolean(onClick) || Boolean(onMouseEnter) || Boolean(onMouseLeave);
232
- // gmpClickable is only available in beta version of the
233
- // maps api (as of 2024-10-10)
234
- marker.gmpClickable = gmpClickable;
235
- // enable pointer events for the markers with custom content
236
- if (gmpClickable && marker != null && marker.content && isElementNode(marker.content)) {
237
- marker.content.style.pointerEvents = 'none';
238
- if (marker.content.firstElementChild) {
239
- marker.content.firstElementChild.style.pointerEvents = 'all';
240
- }
241
- }
242
- }, [marker, clickable, onClick, onMouseEnter, onMouseLeave]);
243
- useMapsEventListener(marker, 'click', onClick);
244
- useMapsEventListener(marker, 'drag', onDrag);
245
- useMapsEventListener(marker, 'dragstart', onDragStart);
246
- useMapsEventListener(marker, 'dragend', onDragEnd);
247
- useDomEventListener(marker == null ? void 0 : marker.element, 'mouseenter', onMouseEnter);
248
- useDomEventListener(marker == null ? void 0 : marker.element, 'mouseleave', onMouseLeave);
249
- return [marker, contentContainer];
250
- }
251
- forwardRef((props, ref) => {
252
- const {
253
- children,
254
- style,
255
- className,
256
- anchorPoint
257
- } = props;
258
- const [marker, contentContainer] = useAdvancedMarker(props);
259
- const advancedMarkerContextValue = useMemo(() => marker ? {
260
- marker
261
- } : null, [marker]);
262
- useImperativeHandle(ref, () => marker, [marker]);
263
- if (!contentContainer) return null;
264
- return /*#__PURE__*/React.createElement(AdvancedMarkerContext.Provider, {
265
- value: advancedMarkerContextValue
266
- }, createPortal(/*#__PURE__*/React.createElement(MarkerContent, {
267
- anchorPoint: anchorPoint,
268
- styles: style,
269
- className: className
270
- }, children), contentContainer));
271
- });
272
-
273
- const _excluded = ["onClick", "onDrag", "onDragStart", "onDragEnd", "onMouseOver", "onMouseOut"];
274
- function useMarker(props) {
275
- const [marker, setMarker] = useState(null);
276
- const map = useMap();
277
- const {
278
- onClick,
279
- onDrag,
280
- onDragStart,
281
- onDragEnd,
282
- onMouseOver,
283
- onMouseOut
284
- } = props,
285
- markerOptions = _objectWithoutPropertiesLoose(props, _excluded);
286
- const {
287
- position,
288
- draggable
289
- } = markerOptions;
290
- // create marker instance and add to the map once the map is available
291
- useEffect(() => {
292
- if (!map) {
293
- if (map === undefined) console.error('<Marker> has to be inside a Map component.');
294
- return;
295
- }
296
- const newMarker = new google.maps.Marker(markerOptions);
297
- newMarker.setMap(map);
298
- setMarker(newMarker);
299
- return () => {
300
- newMarker.setMap(null);
301
- setMarker(null);
302
- };
303
- // We do not want to re-render the whole marker when the options change.
304
- // Marker options update is handled in a useEffect below.
305
- // Excluding markerOptions from dependency array on purpose here.
306
- // eslint-disable-next-line react-hooks/exhaustive-deps
307
- }, [map]);
308
- // attach and re-attach event-handlers when any of the properties change
309
- useEffect(() => {
310
- if (!marker) return;
311
- const m = marker;
312
- // Add event listeners
313
- const gme = google.maps.event;
314
- if (onClick) gme.addListener(m, 'click', onClick);
315
- if (onDrag) gme.addListener(m, 'drag', onDrag);
316
- if (onDragStart) gme.addListener(m, 'dragstart', onDragStart);
317
- if (onDragEnd) gme.addListener(m, 'dragend', onDragEnd);
318
- if (onMouseOver) gme.addListener(m, 'mouseover', onMouseOver);
319
- if (onMouseOut) gme.addListener(m, 'mouseout', onMouseOut);
320
- marker.setDraggable(Boolean(draggable));
321
- return () => {
322
- gme.clearInstanceListeners(m);
323
- };
324
- }, [marker, draggable, onClick, onDrag, onDragStart, onDragEnd, onMouseOver, onMouseOut]);
325
- // update markerOptions (note the dependencies aren't properly checked
326
- // here, we just assume that setOptions is smart enough to not waste a
327
- // lot of time updating values that didn't change)
328
- useEffect(() => {
329
- if (!marker) return;
330
- if (markerOptions) marker.setOptions(markerOptions);
331
- }, [marker, markerOptions]);
332
- // update position when changed
333
- useEffect(() => {
334
- // Should not update position when draggable
335
- if (draggable || !position || !marker) return;
336
- marker.setPosition(position);
337
- }, [draggable, position, marker]);
338
- return marker;
339
- }
340
- /**
341
- * Component to render a marker on a map
342
- */
343
- forwardRef((props, ref) => {
344
- const marker = useMarker(props);
345
- useImperativeHandle(ref, () => marker, [marker]);
346
- return /*#__PURE__*/React.createElement(React.Fragment, null);
347
- });
2
+ import React, { forwardRef, useRef, useState, useMemo, useEffect, useImperativeHandle } from 'react';
348
3
 
349
4
  /**
350
5
  * SVG基础MapView
@@ -452,7 +107,7 @@ class SvgMapView {
452
107
  */
453
108
  fitToView(bounds) {
454
109
  console.log('fitToView----->', bounds);
455
- const padding = 0; // 添加一些边距以避免内容贴边
110
+ const padding = 1; // 添加一些边距以避免内容贴边
456
111
  const boundWidth = bounds.maxX - bounds.minX;
457
112
  const boundHeight = bounds.maxY - bounds.minY;
458
113
  // 防止宽高为0的情况
@@ -475,8 +130,8 @@ class SvgMapView {
475
130
  this.viewBox = {
476
131
  x: bounds.minX - padding,
477
132
  y: bounds.minY - padding,
478
- width: boundWidth + padding * 2,
479
- height: boundHeight + padding * 2
133
+ width: boundWidth + padding,
134
+ height: boundHeight + padding
480
135
  };
481
136
  // 根据宽高比选择合适的preserveAspectRatio设置
482
137
  if (Math.abs(contentAspectRatio - containerAspectRatio) < 0.01) {
@@ -555,10 +210,6 @@ class SvgMapView {
555
210
  this.clearLayersGroup();
556
211
  // 绘制所有图层
557
212
  this.onDrawLayers();
558
- // 绘制比例尺
559
- if (this.showScale) {
560
- this.drawScale();
561
- }
562
213
  }
563
214
  /**
564
215
  * 清空图层组
@@ -594,35 +245,6 @@ class SvgMapView {
594
245
  reinitializeSVG() {
595
246
  this.refresh();
596
247
  }
597
- /**
598
- * 绘制比例尺
599
- */
600
- drawScale() {
601
- const padding = 10;
602
- const scaleX = this.viewBox.x + this.viewBox.width - 100 - padding;
603
- const scaleY = this.viewBox.y + this.viewBox.height - padding;
604
- // 创建比例尺线段
605
- const scaleLength = 10 * 50; // 50像素/米的基准比例
606
- // 线段
607
- const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
608
- line.setAttribute('x1', scaleX.toString());
609
- line.setAttribute('y1', scaleY.toString());
610
- line.setAttribute('x2', (scaleX + scaleLength).toString());
611
- line.setAttribute('y2', scaleY.toString());
612
- line.setAttribute('stroke', '#333');
613
- line.setAttribute('stroke-width', (2 / this.getZoom()).toString());
614
- // 文字标签
615
- const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
616
- text.setAttribute('x', (scaleX + scaleLength / 2).toString());
617
- text.setAttribute('y', (scaleY - 5 / this.getZoom()).toString()); // 文字位置适应缩放
618
- text.setAttribute('text-anchor', 'middle');
619
- text.setAttribute('font-family', 'Arial, sans-serif');
620
- text.setAttribute('font-size', (12 / this.getZoom()).toString()); // 字体大小适应缩放
621
- text.setAttribute('fill', '#333');
622
- text.textContent = '10m';
623
- this.layersGroup.appendChild(line);
624
- this.layersGroup.appendChild(text);
625
- }
626
248
  // ==================== 拖拽功能 ====================
627
249
  /**
628
250
  * 设置拖拽事件处理器
@@ -1487,7 +1109,7 @@ class ObstacleLayer extends BaseLayer {
1487
1109
  }
1488
1110
  }
1489
1111
 
1490
- var chargingPileImage = "3900c861790f0a9d.png";
1112
+ var chargingPileImage = "data:image/png;base64,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";
1491
1113
 
1492
1114
  /**
1493
1115
  * 充电桩图层
@@ -1936,7 +1558,7 @@ function createPathSegmentsByType(list) {
1936
1558
  /**
1937
1559
  * 计算地图边界
1938
1560
  */
1939
- function calculateMapBounds(mapData, pathData) {
1561
+ function calculateMapBounds(mapData) {
1940
1562
  let minX = Infinity;
1941
1563
  let minY = Infinity;
1942
1564
  let maxX = -Infinity;
@@ -1981,21 +1603,6 @@ function calculateMapBounds(mapData, pathData) {
1981
1603
  if (point)
1982
1604
  updateBounds([[point.x, point.y]]);
1983
1605
  }
1984
- // 处理SVG元素的边界(TIME_LIMIT_OBSTACLE)- 简化为使用中心点
1985
- if (element.svg &&
1986
- element.center &&
1987
- element.scale !== undefined &&
1988
- element.direction !== undefined) {
1989
- const centerPoint = convertPositionFormat(element.center);
1990
- if (centerPoint) {
1991
- // 简单估算SVG边界:使用中心点加上缩放后的估算半径
1992
- const estimatedRadius = 50 * (element.scale || 1); // 估算半径
1993
- updateBounds([
1994
- [centerPoint.x - estimatedRadius, centerPoint.y - estimatedRadius],
1995
- [centerPoint.x + estimatedRadius, centerPoint.y + estimatedRadius],
1996
- ]);
1997
- }
1998
- }
1999
1606
  }
2000
1607
  // 如果没有找到边界,返回默认值
2001
1608
  if (minX === Infinity) {
@@ -2017,37 +1624,97 @@ function isValidGpsCoordinate$1(lat, lng) {
2017
1624
  );
2018
1625
  }
2019
1626
  /**
2020
- * 从地图几何数据估算GPS坐标
2021
- * 当GPS坐标无效时,基于地图的几何中心和大小来估算一个合理的位置
1627
+ * 从地图几何数据估算GPS边界坐标
1628
+ * 当GPS坐标无效时,基于地图的几何边界来估算SW和NE的GPS坐标
1629
+ * calculateMapBounds返回的数据除以50后代表准确的物理单位(米)
2022
1630
  */
2023
1631
  function estimateGpsFromMapBounds(mapData) {
2024
1632
  try {
2025
1633
  const bounds = calculateMapBounds(mapData);
2026
1634
  if (!bounds || bounds.minX === Infinity) {
1635
+ return {
1636
+ sw: [0, 0],
1637
+ ne: [0, 0]
1638
+ };
1639
+ }
1640
+ // 将边界数据转换为物理单位(米)
1641
+ const minXMeters = bounds.minX / SCALE_FACTOR * 6; // 西边界
1642
+ const minYMeters = bounds.minY / SCALE_FACTOR * 6; // 南边界
1643
+ const maxXMeters = bounds.maxX / SCALE_FACTOR * 6; // 东边界
1644
+ const maxYMeters = bounds.maxY / SCALE_FACTOR * 6; // 北边界
1645
+ const mapWidthMeters = maxXMeters - minXMeters;
1646
+ const mapHeightMeters = maxYMeters - minYMeters;
1647
+ console.log('地图物理尺寸:', {
1648
+ minX: minXMeters,
1649
+ minY: minYMeters,
1650
+ maxX: maxXMeters,
1651
+ maxY: maxYMeters,
1652
+ width: mapWidthMeters,
1653
+ height: mapHeightMeters
1654
+ });
1655
+ // 根据地图大小选择合适的基准GPS坐标
1656
+ let baseLat;
1657
+ let baseLng;
1658
+ if (mapWidthMeters < 1000 && mapHeightMeters < 1000) {
1659
+ // 小型区域 - 使用更精确的基准点
1660
+ baseLat = 39.9042; // 北京天安门
1661
+ baseLng = 116.4074;
1662
+ }
1663
+ else if (mapWidthMeters < 10000 && mapHeightMeters < 10000) {
1664
+ // 中型区域 - 使用城市级别的基准点
1665
+ baseLat = 39.9000;
1666
+ baseLng = 116.4000;
1667
+ }
1668
+ else {
1669
+ // 大型区域 - 使用更大范围的基准点
1670
+ baseLat = 40.0000;
1671
+ baseLng = 116.0000;
1672
+ }
1673
+ // 精确的坐标转换常数
1674
+ // 1度纬度 = 约111,320米(在地球上任何地方都基本相同)
1675
+ // 1度经度 = 约111,320 * cos(纬度) 米(随纬度变化)
1676
+ const METERS_PER_DEGREE_LAT = 111320;
1677
+ const METERS_PER_DEGREE_LNG = 111320 * Math.cos(baseLat * Math.PI / 180);
1678
+ // 计算SW(西南角)GPS坐标
1679
+ const swLat = baseLat + minYMeters / METERS_PER_DEGREE_LAT; // 南边界纬度
1680
+ const swLng = baseLng + minXMeters / METERS_PER_DEGREE_LNG; // 西边界经度
1681
+ // 计算NE(东北角)GPS坐标
1682
+ const neLat = baseLat + maxYMeters / METERS_PER_DEGREE_LAT; // 北边界纬度
1683
+ const neLng = baseLng + maxXMeters / METERS_PER_DEGREE_LNG; // 东边界经度
1684
+ console.log('GPS边界坐标估算:', {
1685
+ sw: { lat: swLat, lng: swLng },
1686
+ ne: { lat: neLat, lng: neLng },
1687
+ offsets: {
1688
+ minYOffset: minYMeters / METERS_PER_DEGREE_LAT,
1689
+ minXOffset: minXMeters / METERS_PER_DEGREE_LNG,
1690
+ maxYOffset: maxYMeters / METERS_PER_DEGREE_LAT,
1691
+ maxXOffset: maxXMeters / METERS_PER_DEGREE_LNG
1692
+ }
1693
+ });
1694
+ // 验证估算的坐标是否在合理范围内
1695
+ if (swLat < -90 || swLat > 90 || swLng < -180 || swLng > 180 ||
1696
+ neLat < -90 || neLat > 90 || neLng < -180 || neLng > 180) {
1697
+ console.warn('估算的GPS坐标超出有效范围:', {
1698
+ sw: [swLng, swLat],
1699
+ ne: [neLng, neLat]
1700
+ });
1701
+ return null;
1702
+ }
1703
+ // 确保SW在NE的西南方
1704
+ if (swLat >= neLat || swLng >= neLng) {
1705
+ console.warn('GPS边界坐标逻辑错误:', {
1706
+ sw: [swLng, swLat],
1707
+ ne: [neLng, neLat]
1708
+ });
2027
1709
  return null;
2028
1710
  }
2029
- // 计算地图的几何中心
2030
- const centerX = (bounds.minX + bounds.maxX) / 2;
2031
- const centerY = (bounds.minY + bounds.maxY) / 2;
2032
- const mapWidth = bounds.maxX - bounds.minX;
2033
- const mapHeight = bounds.maxY - bounds.minY;
2034
- // 基于地图大小估算GPS坐标
2035
- // 这里使用一个简化的转换:假设这是一个小型区域地图
2036
- // 1米大约对应0.000009度纬度,0.000011度经度(在中纬度地区)
2037
- const metersToLatDegree = 1 / 110540; // 1米 ≈ 0.000009度纬度
2038
- const metersToLngDegree = 1 / 111320; // 1米 ≈ 0.000009度经度(赤道附近)
2039
- // 估算的GPS中心点(使用一个合理的默认位置作为基准)
2040
- const baseLat = 40.0; // 使用北纬40度作为基准(大致位于中国/美国中部)
2041
- const baseLng = 116.0; // 使用东经116度作为基准
2042
- const estimatedLat = baseLat + centerY * metersToLatDegree;
2043
- const estimatedLng = baseLng + centerX * metersToLngDegree;
2044
1711
  return {
2045
- lat: estimatedLat,
2046
- lng: estimatedLng,
1712
+ sw: [swLng, swLat], // [经度, 纬度]
1713
+ ne: [neLng, neLat] // [经度, 纬度]
2047
1714
  };
2048
1715
  }
2049
1716
  catch (error) {
2050
- console.warn('估算GPS坐标时出错:', error);
1717
+ console.warn('估算GPS边界坐标时出错:', error);
2051
1718
  return null;
2052
1719
  }
2053
1720
  }
@@ -2093,10 +1760,12 @@ function calculateMapGpsCenter(mapData) {
2093
1760
  }
2094
1761
  }
2095
1762
  // 尝试从地图几何边界估算GPS坐标
2096
- const estimatedGps = estimateGpsFromMapBounds(mapData);
2097
- console.log('estimatedGps----->', estimatedGps);
2098
- if (estimatedGps) {
2099
- return estimatedGps;
1763
+ const estimatedBounds = estimateGpsFromMapBounds(mapData);
1764
+ if (estimatedBounds) {
1765
+ // 从估算的边界计算中心点
1766
+ const centerLat = (estimatedBounds.sw[1] + estimatedBounds.ne[1]) / 2;
1767
+ const centerLng = (estimatedBounds.sw[0] + estimatedBounds.ne[0]) / 2;
1768
+ return { lat: centerLat, lng: centerLng };
2100
1769
  }
2101
1770
  return {
2102
1771
  lat: 39.9042, // 北京纬度
@@ -2453,7 +2122,7 @@ const parseMapWorkPosition = (mapWorkPosition) => {
2453
2122
  currentMowProgress,
2454
2123
  };
2455
2124
  };
2456
- const handleRealTimeData = ({ realTimeData, isMowing, mapData, pathData, partitionId, }) => {
2125
+ const handleRealTimeData = ({ realTimeData, isMowing, pathData, partitionId, }) => {
2457
2126
  // 先将数据进行倒排,这样好插入数据
2458
2127
  if (realTimeData.length > 0) {
2459
2128
  realTimeData.reverse();
@@ -2463,25 +2132,6 @@ const handleRealTimeData = ({ realTimeData, isMowing, mapData, pathData, partiti
2463
2132
  let mowingStatus = isMowing || false;
2464
2133
  let currentPartitionId = partitionId || null;
2465
2134
  realTimeData.forEach((item) => {
2466
- if (currentPartitionId) {
2467
- // 如果这个分区不在历史轨迹数据里面,需要去地图数据里面看看是否有这个分区,和相关数据
2468
- if (!newPathData?.[currentPartitionId]) {
2469
- const findMapData = mapData?.sub_maps?.find((item) => item.id === Number(currentPartitionId));
2470
- if (findMapData) {
2471
- newPathData[currentPartitionId] = {
2472
- area: findMapData?.elements?.[0]?.area || 0,
2473
- finishedArea: 0,
2474
- partitionPercentage: 0,
2475
- partitionId: Number(currentPartitionId),
2476
- endTimeAlias: -1,
2477
- startTime: 0,
2478
- endTime: 0,
2479
- // name: findMapData?.name || '',
2480
- list: [],
2481
- };
2482
- }
2483
- }
2484
- }
2485
2135
  // 这里需要区分,是割草进度还是割草轨迹
2486
2136
  if (item.type === REAL_TIME_DATA_TYPE.LOCATION) {
2487
2137
  // 割草轨迹
@@ -2491,8 +2141,8 @@ const handleRealTimeData = ({ realTimeData, isMowing, mapData, pathData, partiti
2491
2141
  const currentPathData = newPathData[currentPartitionId];
2492
2142
  newPathData[currentPartitionId] = {
2493
2143
  ...currentPathData,
2494
- list: [
2495
- ...(currentPathData?.list || []),
2144
+ points: [
2145
+ ...(currentPathData?.points || []),
2496
2146
  {
2497
2147
  postureX: Number(postureX),
2498
2148
  postureY: Number(postureY),
@@ -3761,8 +3411,8 @@ class PathDataProcessor {
3761
3411
  }
3762
3412
  // 获取所有分区的路径数据
3763
3413
  const allPathItems = Object.values(pathData).reduce((acc, partitionData) => {
3764
- if (partitionData && partitionData.list && partitionData.list.length > 0) {
3765
- acc.push(...partitionData.list);
3414
+ if (partitionData && partitionData.points && partitionData.points.length > 0) {
3415
+ acc.push(...partitionData.points);
3766
3416
  }
3767
3417
  return acc;
3768
3418
  }, []);
@@ -4479,21 +4129,24 @@ class ChargingPileManager {
4479
4129
  if (!svg)
4480
4130
  return;
4481
4131
  const viewBox = svg.viewBox.baseVal;
4482
- const viewBoxData = {
4132
+ ({
4483
4133
  x: viewBox.x,
4484
4134
  y: viewBox.y,
4485
4135
  width: viewBox.width,
4486
4136
  height: viewBox.height,
4487
- };
4488
- this.updatePositionsWithPrecomputedData(divWidth, divHeight, viewBoxData);
4137
+ });
4138
+ const g = svg.querySelector('g');
4139
+ const gBox = g?.getBBox();
4140
+ console.log('g==', g, gBox, viewBox);
4141
+ this.updatePositionsWithPrecomputedData(divWidth, divHeight, gBox);
4489
4142
  }
4490
4143
  /**
4491
4144
  * 使用预计算数据更新位置
4492
4145
  */
4493
4146
  updatePositionsWithPrecomputedData(divWidth, divHeight, viewBox) {
4494
4147
  this.chargingPileElements.forEach((element, _index) => {
4495
- console.log('updatePositionsWithPrecomputedData-------->', element);
4496
4148
  const center = element.coordinates[0];
4149
+ console.log('updatePositionsWithPrecomputedData-------->', center, divHeight, divWidth, viewBox, element);
4497
4150
  const pixelPosition = this.convertMapCoordinateToPixelWithPrecomputedData(center[0], center[1], divWidth, divHeight, viewBox);
4498
4151
  if (pixelPosition) {
4499
4152
  const pileId = `pile_${center[0]}_${center[1]}`;
@@ -4646,10 +4299,10 @@ class AntennaManager {
4646
4299
  */
4647
4300
  createAntennaElement(antennaData) {
4648
4301
  const size = 30; // 默认30px大小
4649
- const isOnline = antennaData.status === 1;
4302
+ const isOnline = antennaData?.status === 1;
4650
4303
  // 根据天线ID和在线状态选择图片
4651
4304
  let imageSrc;
4652
- if (antennaData.type === 1) {
4305
+ if (antennaData?.type === 1) {
4653
4306
  imageSrc = isOnline ? antennaOneOnline : antennaOneOffline;
4654
4307
  }
4655
4308
  else {
@@ -4793,6 +4446,8 @@ class AntennaManager {
4793
4446
  collapseAllTooltips() {
4794
4447
  if (!this.container)
4795
4448
  return;
4449
+ this.antennaTooltipFlag = false;
4450
+ this.singleAntennaTooltipFlag = false;
4796
4451
  const allTooltips = this.container.querySelectorAll('.antenna-tooltip');
4797
4452
  allTooltips.forEach((tooltip) => {
4798
4453
  const tooltipElement = tooltip;
@@ -4858,8 +4513,12 @@ class AntennaManager {
4858
4513
  */
4859
4514
  updateAntennaPosition() {
4860
4515
  this.clear();
4861
- this.addMainAntennaElement(this.mainAntennaData?.originalData);
4862
- this.addSingleAntennaElement(this.singleAntennaData?.originalData);
4516
+ if (this.mainAntennaData) {
4517
+ this.addMainAntennaElement(this.mainAntennaData?.originalData);
4518
+ }
4519
+ if (this.singleAntennaData) {
4520
+ this.addSingleAntennaElement(this.singleAntennaData?.originalData);
4521
+ }
4863
4522
  }
4864
4523
  /**
4865
4524
  * 获取容器元素
@@ -5658,15 +5317,15 @@ class MowerMapOverlay {
5658
5317
  if (!viewBox)
5659
5318
  return;
5660
5319
  // 构造viewBox信息对象
5661
- const viewBoxInfo = {
5320
+ ({
5662
5321
  x: viewBox.x,
5663
5322
  y: viewBox.y,
5664
5323
  width: viewBox.width,
5665
5324
  height: viewBox.height,
5666
- };
5325
+ });
5667
5326
  // 更新充电桩位置
5668
5327
  if (this.chargingPileManager) {
5669
- this.chargingPileManager.updatePositionsWithPrecomputedData(width, height, viewBoxInfo);
5328
+ this.chargingPileManager.updatePositions();
5670
5329
  }
5671
5330
  }
5672
5331
  // 创建编辑界面
@@ -6428,25 +6087,6 @@ const isValidGpsCoordinate = (coordinate) => {
6428
6087
  !(Math.abs(lng) < 0.001 && Math.abs(lat) < 0.001) // 排除接近(0,0)的坐标
6429
6088
  );
6430
6089
  };
6431
- // 从地图几何数据估算GPS边界
6432
- const estimateGpsBoundsFromMapData = (mapData) => {
6433
- if (!mapData.origin || !mapData.size)
6434
- return null;
6435
- // 使用地图的几何尺寸来估算一个合理的GPS边界
6436
- // 这是一个简化的估算,假设地图大致在某个区域
6437
- const defaultLat = 39.9042; // 北京纬度作为默认值
6438
- const defaultLng = 116.4074; // 北京经度作为默认值
6439
- // 根据地图尺寸估算边界(简化计算)
6440
- const mapWidth = mapData.size.width || 1000;
6441
- const mapHeight = mapData.size.height || 1000;
6442
- // 大致估算:1000像素 ≈ 0.01度
6443
- const latRange = (mapHeight / 1000) * 0.01;
6444
- const lngRange = (mapWidth / 1000) * 0.01;
6445
- return {
6446
- sw: [defaultLng - lngRange / 2, defaultLat - latRange / 2],
6447
- ne: [defaultLng + lngRange / 2, defaultLat + latRange / 2],
6448
- };
6449
- };
6450
6090
  // 获取有效的GPS边界
6451
6091
  const getValidGpsBounds = (mapData) => {
6452
6092
  // 首先尝试使用地图数据中的GPS坐标
@@ -6457,23 +6097,27 @@ const getValidGpsBounds = (mapData) => {
6457
6097
  };
6458
6098
  }
6459
6099
  // 如果GPS坐标无效,尝试从地图几何数据估算
6460
- const estimatedBounds = estimateGpsBoundsFromMapData(mapData);
6461
- if (estimatedBounds) {
6462
- console.warn('GPS坐标无效,使用地图几何数据估算边界:', estimatedBounds);
6463
- return estimatedBounds;
6100
+ const { sw, ne } = estimateGpsFromMapBounds(mapData);
6101
+ console.log('sw, ne==', sw, ne);
6102
+ if (sw && ne) {
6103
+ console.warn('GPS坐标无效,使用地图几何数据估算边界:', sw, ne);
6104
+ return {
6105
+ sw: [sw[0], sw[1]],
6106
+ ne: [ne[0], ne[1]],
6107
+ };
6464
6108
  }
6465
6109
  // 最后的fallback:使用默认坐标
6466
6110
  console.warn('无法获取有效的GPS边界,使用默认坐标');
6467
6111
  return {
6468
- sw: [137.1, -28.1],
6469
- ne: [137.3, -27.9],
6112
+ sw: [-9.1562, -37.7503],
6113
+ ne: [31.247, 5.797],
6470
6114
  };
6471
6115
  };
6472
6116
  // 默认配置
6473
6117
  const defaultMapConfig = DEFAULT_MAP_CONFIG;
6474
6118
  const defaultPathConfig = DEFAULT_PATH_CONFIG;
6475
6119
  // 地图渲染器组件
6476
- const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, mapConfig, pathConfig, antennaConfig = [], onMapLoad, onPathLoad, onZoomChange, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, }, ref) => {
6120
+ const MowerMapRenderer = forwardRef(({ mapRef, mapJson, pathJson, mowerPositonConfig, mapConfig, pathConfig, antennaConfig, onMapLoad, onPathLoad, onZoomChange, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, }, ref) => {
6477
6121
  const containerRef = useRef(null);
6478
6122
  const svgMapViewRef = useRef(null);
6479
6123
  const [elementCount, setElementCount] = useState(0);
@@ -6481,12 +6125,15 @@ const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, ma
6481
6125
  const [zoom, setZoom] = useState(1);
6482
6126
  const [currentError, setCurrentError] = useState(null);
6483
6127
  const overlayRef = useRef(null);
6484
- const mapRef = useMap();
6128
+ // const mapRef = useMap();
6485
6129
  const [isGoogleMapsReady, setIsGoogleMapsReady] = useState(false);
6486
6130
  const [hasInitializedBounds, setHasInitializedBounds] = useState(false);
6487
6131
  const { subBoundaryBorder } = useSubBoundaryBorderStore();
6488
6132
  // 合并配置
6489
6133
  const mergedMapConfig = useMemo(() => {
6134
+ // if (!mapConfig) {
6135
+ // return defaultMapConfig;
6136
+ // }
6490
6137
  return { ...defaultMapConfig, ...(mapConfig || {}) };
6491
6138
  }, [mapConfig]);
6492
6139
  const mergedPathConfig = useMemo(() => {
@@ -6514,6 +6161,7 @@ const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, ma
6514
6161
  setCurrentError(error);
6515
6162
  onError?.(error);
6516
6163
  };
6164
+ console.log('react version', React.version);
6517
6165
  // 初始化Google Maps叠加层
6518
6166
  const initializeGoogleMapsOverlay = async () => {
6519
6167
  if (!mapJson || !mergedMapConfig.useGoogleMaps)
@@ -6533,6 +6181,7 @@ const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, ma
6533
6181
  }
6534
6182
  // 计算边界
6535
6183
  const bounds = calculateMapBounds(mapJson);
6184
+ console.log('bounds==', bounds);
6536
6185
  if (!bounds) {
6537
6186
  handleError('无法计算地图边界');
6538
6187
  return;
@@ -6656,7 +6305,7 @@ const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, ma
6656
6305
  Object.values(pathJson).forEach((item) => {
6657
6306
  const pathDrawLayer = new DrawLayer();
6658
6307
  const elements = PathDataProcessor.processPathData({
6659
- pathData: item.list || [],
6308
+ pathData: item.points || [],
6660
6309
  pathConfig: mergedPathConfig,
6661
6310
  });
6662
6311
  if (elements.length === 0) {
@@ -6694,7 +6343,7 @@ const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, ma
6694
6343
  svgMapViewRef.current = null;
6695
6344
  }
6696
6345
  };
6697
- }, [mapJson, pathJson, mergedMapConfig, mergedPathConfig]);
6346
+ }, [mapJson, pathJson, mergedMapConfig, mergedPathConfig, mergedAntennaConfig]);
6698
6347
  // 监听编辑模式变化
6699
6348
  useEffect(() => {
6700
6349
  if (overlayRef.current) {
@@ -6795,6 +6444,9 @@ const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, ma
6795
6444
  overlayRef.current.setEditMode(enabled);
6796
6445
  }
6797
6446
  },
6447
+ getEditMode: () => {
6448
+ return isEditMode;
6449
+ },
6798
6450
  getCurrentDragState: () => {
6799
6451
  return overlayRef.current ? overlayRef.current.getCurrentDragState() : null;
6800
6452
  },
@@ -6802,11 +6454,9 @@ const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, ma
6802
6454
  getPathCount: () => pathCount,
6803
6455
  isGoogleMapsReady: () => isGoogleMapsReady,
6804
6456
  handleRealTimeData: ({ realTimeData, isMowing, mapData, pathData, partitionId, }) => {
6805
- console.log('handleRealTimeData----->', realTimeData, isMowing, mapData, pathData, partitionId);
6806
6457
  return handleRealTimeData({
6807
6458
  realTimeData,
6808
6459
  isMowing,
6809
- mapData,
6810
6460
  pathData,
6811
6461
  partitionId,
6812
6462
  });
@@ -6834,4 +6484,4 @@ const MowerMapRenderer = forwardRef(({ mapJson, pathJson, mowerPositonConfig, ma
6834
6484
  });
6835
6485
  MowerMapRenderer.displayName = 'MowerMapRenderer';
6836
6486
 
6837
- export { MapDataProcessor, MowerMapRenderer, PathDataProcessor, calculateMapGpsCenter };
6487
+ export { MapDataProcessor, MowerMapRenderer, PathDataProcessor, calculateMapGpsCenter, estimateGpsFromMapBounds };