@wenle_2523097/agri-map 2.0.7 → 2.0.9

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/README.md CHANGED
@@ -4,6 +4,13 @@
4
4
 
5
5
  ## 更新日志
6
6
 
7
+ ### v2.0.8 (2026-06-01)
8
+ #### 新增
9
+ - **TrackPlayer 时间轴功能**:新增 `onTimelineUpdate` 回调,暴露完整时间轴数据
10
+ - **useTimeline Hook**:提供时间轴数据计算逻辑(progress、时间戳、格式化时间等)
11
+ - **时间轴 UI 交互**:Demo 支持滑块拖动跳转、实时进度显示、时间戳标签
12
+ - **TimelineProgressInfo 类型**:完整的时间轴进度信息接口定义
13
+
7
14
  ### v2.0.7 (2026-05-27)
8
15
  #### 新增
9
16
  - **Panel 面板组件**:新增地图面板组件,支持在地图上渲染 React 内容
@@ -52,11 +59,6 @@ yarn add @wenle_2523097/agri-map
52
59
  ```bash
53
60
  npm install leaflet@^1.9.4 react@>=18.0.0 react-dom@>=18.0.0
54
61
  ```
55
-
56
- > ⚠️ **重要提示:**
57
- >
58
- > - 本库已移除 `react-leaflet` 依赖,直接使用原生 Leaflet API,更轻量、更灵活
59
-
60
62
  ---
61
63
 
62
64
  ## 快速开始
@@ -26,5 +26,12 @@ interface UsePlotDataResult {
26
26
  /** 当前高亮分组 ref */
27
27
  highlightedGroupRef: React.MutableRefObject<string | null>;
28
28
  }
29
+ /**
30
+ * 将坐标转换为原始输入格式
31
+ * @param coords 内部标准格式坐标 [lat, lng]
32
+ * @param order 原始输入格式
33
+ * @returns 原始格式的坐标
34
+ */
35
+ export declare function convertToOriginalFormat(coords: Coordinate[], order: 'lat-lng' | 'lng-lat' | undefined): Coordinate[];
29
36
  export declare function usePlotData({ fieldNames, dataSource, groupKey, }: UsePlotDataOptions): UsePlotDataResult;
30
37
  export {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TrackPlayer 组件单元测试
2
+ * @fileoverview TrackPlayer 组件单元测试
3
3
  * @description 测试轨迹播放组件的渲染、播放控制和状态管理
4
4
  */
5
5
  export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @fileoverview useTimeline hook 单元测试
3
+ * @module components/TrackPlayer/__tests__/useTimeline.test
4
+ */
5
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @fileoverview TrackPlayer 时间轴数据计算 Hook
3
+ * @module components/TrackPlayer/hooks/useTimeline
4
+ */
5
+ import type { TrackPointWithState } from '../types';
6
+ /** 时间轴信息 */
7
+ export interface TimelineInfo {
8
+ /** 起始时间戳 */
9
+ startTime: number | null;
10
+ /** 结束时间戳 */
11
+ endTime: number | null;
12
+ /** 当前时间戳 */
13
+ currentTime: number | null;
14
+ /** 进度百分比 0-1 */
15
+ progress: number;
16
+ /** 格式化起始时间 */
17
+ startTimeStr: string | null;
18
+ /** 格式化结束时间 */
19
+ endTimeStr: string | null;
20
+ /** 格式化当前时间 */
21
+ currentTimeStr: string | null;
22
+ }
23
+ interface UseTimelineOptions {
24
+ allPoints: TrackPointWithState[];
25
+ currentIndex: number;
26
+ }
27
+ /**
28
+ * 格式化时间戳为 HH:MM:SS 字符串
29
+ * @param timestamp 时间戳(毫秒),可为 null
30
+ */
31
+ export declare function formatTimestamp(timestamp: number | null): string | null;
32
+ /**
33
+ * 计算进度对应的索引
34
+ * @param allPointsLength 轨迹点总数
35
+ * @param progress 进度 0-1
36
+ * @returns 对应的索引
37
+ */
38
+ export declare function indexFromProgress(allPointsLength: number, progress: number): number;
39
+ /**
40
+ * 计算时间轴数据
41
+ * @param allPoints 轨迹点数组
42
+ * @param currentIndex 当前索引
43
+ * @returns 时间轴信息
44
+ */
45
+ export declare function useTimeline({ allPoints, currentIndex }: UseTimelineOptions): TimelineInfo;
46
+ /**
47
+ * 计算进度信息(用于 onProgress 回调兼容)
48
+ */
49
+ export declare function calcProgressInfo(allPoints: TrackPointWithState[], currentIndex: number, _distance: number): {
50
+ progress: number;
51
+ startTime: number | null;
52
+ endTime: number | null;
53
+ currentTime: number | null;
54
+ startTimeStr: string | null;
55
+ endTimeStr: string | null;
56
+ currentTimeStr: string | null;
57
+ };
58
+ export {};
@@ -52,6 +52,19 @@ export interface TrackPlayerProps {
52
52
  onStop?: () => void;
53
53
  onProgress?: (currentIndex: number, total: number, point: TrackPoint | null, distance: number, timestamp: number | null, time: string | null) => void;
54
54
  onComplete?: () => void;
55
+ /** 时间轴进度详细回调(提供完整时间轴数据) */
56
+ onTimelineUpdate?: (info: {
57
+ currentIndex: number;
58
+ totalPoints: number;
59
+ progress: number;
60
+ startTime: number | null;
61
+ endTime: number | null;
62
+ currentTime: number | null;
63
+ startTimeStr: string | null;
64
+ endTimeStr: string | null;
65
+ currentTimeStr: string | null;
66
+ distance: number;
67
+ }) => void;
55
68
  }
56
69
  declare const TrackPlayer: import("react").ForwardRefExoticComponent<TrackPlayerProps & import("react").RefAttributes<TrackPlayerHandle>>;
57
70
  export default TrackPlayer;
@@ -2,7 +2,7 @@
2
2
  * @fileoverview TrackPlayer 类型定义
3
3
  * @module components/TrackPlayer/types
4
4
  */
5
- import type { TrackData, TrackPoint, IconConfig } from '../../types';
5
+ import type { TrackData, TrackPoint, IconConfig, PathOptions } from '../../types';
6
6
  export type PlayStatus = 'idle' | 'playing' | 'paused' | 'stopped';
7
7
  /** 轨迹播放控制方法 */
8
8
  export interface TrackPlayerHandle {
@@ -32,6 +32,10 @@ export interface TrackPlayerProps {
32
32
  trailLength?: number;
33
33
  /** 显示轨迹线(完整路径) */
34
34
  showTrack?: boolean;
35
+ /** 播放前轨迹样式(未走过的路径),默认 TRACKPLAYER_STYLE.default */
36
+ path?: PathOptions | null | false;
37
+ /** 播放后轨迹样式(已走过的路径),默认 TRACKPLAYER_STYLE.played */
38
+ playPath?: PathOptions | null | false;
35
39
  /** 当前时刻图标配置,默认使用 TRACKPLAYER_DEFAULT_ICON */
36
40
  icon?: IconConfig | null | false;
37
41
  /** 图标默认朝向角度(度),默认 -90 */
@@ -44,6 +48,31 @@ export interface TrackPlayerProps {
44
48
  onStop?: () => void;
45
49
  onProgress?: (currentIndex: number, total: number, point: TrackPoint | null, distance: number, timestamp: number | null, time: string | null) => void;
46
50
  onComplete?: () => void;
51
+ /** 时间轴进度详细回调(提供完整时间轴数据) */
52
+ onTimelineUpdate?: (info: TimelineProgressInfo) => void;
53
+ }
54
+ /** 时间轴进度信息 */
55
+ export interface TimelineProgressInfo {
56
+ /** 当前索引 */
57
+ currentIndex: number;
58
+ /** 总点数 */
59
+ totalPoints: number;
60
+ /** 进度百分比 0-1 */
61
+ progress: number;
62
+ /** 起始时间戳 */
63
+ startTime: number | null;
64
+ /** 结束时间戳 */
65
+ endTime: number | null;
66
+ /** 当前时间戳 */
67
+ currentTime: number | null;
68
+ /** 格式化起始时间 */
69
+ startTimeStr: string | null;
70
+ /** 格式化结束时间 */
71
+ endTimeStr: string | null;
72
+ /** 格式化当前时间 */
73
+ currentTimeStr: string | null;
74
+ /** 已行进距离(米) */
75
+ distance: number;
47
76
  }
48
77
  /** 内部使用的轨迹点(含 state) */
49
78
  export interface TrackPointWithState extends TrackPoint {
@@ -7581,6 +7581,73 @@ function parseVirtualizationConfig(virtualization) {
7581
7581
  };
7582
7582
  }
7583
7583
 
7584
+ /**
7585
+ * 检测并转换坐标格式
7586
+ * @description 如果第一个坐标值 > 90 且第二个值 <= 90,认为是 [lng, lat] 格式,需要翻转
7587
+ * @param positions 原始坐标数组
7588
+ * @returns 包含坐标顺序和标准化后坐标的结果
7589
+ */
7590
+ function detectAndNormalizeCoordinates(positions) {
7591
+ if (!Array.isArray(positions) || positions.length === 0) {
7592
+ return {
7593
+ normalized: positions,
7594
+ order: 'lat-lng'
7595
+ };
7596
+ }
7597
+ // 获取第一个有效坐标进行格式检测
7598
+ var firstItem = positions[0];
7599
+ if (!Array.isArray(firstItem)) {
7600
+ return {
7601
+ normalized: positions,
7602
+ order: 'lat-lng'
7603
+ };
7604
+ }
7605
+ // 获取第一个坐标点
7606
+ var firstCoord = Array.isArray(firstItem[0]) ? firstItem[0] : firstItem;
7607
+ // 检查是否为有效坐标 [lat, lng] 或 [lng, lat]
7608
+ if (firstCoord.length >= 2) {
7609
+ var a = firstCoord[0];
7610
+ var b = firstCoord[1];
7611
+ // 如果 a > 90 && b <= 90,认为是经度值(经度范围 -180~180,纬度 -90~90)
7612
+ if (typeof a === 'number' && a > 90 && typeof b === 'number' && b <= 90) {
7613
+ var flip = function flip(coord) {
7614
+ return [coord[1], coord[0]];
7615
+ };
7616
+ if (Array.isArray(firstItem[0])) {
7617
+ // 三维数组:翻转所有环
7618
+ return {
7619
+ normalized: positions.map(function (inner) {
7620
+ return inner.map(flip);
7621
+ }),
7622
+ order: 'lng-lat'
7623
+ };
7624
+ }
7625
+ // 二维数组
7626
+ return {
7627
+ normalized: positions.map(flip),
7628
+ order: 'lng-lat'
7629
+ };
7630
+ }
7631
+ }
7632
+ return {
7633
+ normalized: positions,
7634
+ order: 'lat-lng'
7635
+ };
7636
+ }
7637
+ /**
7638
+ * 将坐标转换为原始输入格式
7639
+ * @param coords 内部标准格式坐标 [lat, lng]
7640
+ * @param order 原始输入格式
7641
+ * @returns 原始格式的坐标
7642
+ */
7643
+ function convertToOriginalFormat(coords, order) {
7644
+ if (order === 'lng-lat') {
7645
+ return coords.map(function (coord) {
7646
+ return [coord[1], coord[0]];
7647
+ });
7648
+ }
7649
+ return coords;
7650
+ }
7584
7651
  function usePlotData(_ref) {
7585
7652
  var fieldNames = _ref.fieldNames,
7586
7653
  dataSource = _ref.dataSource,
@@ -7597,9 +7664,13 @@ function usePlotData(_ref) {
7597
7664
  // 映射原始数据到标准 PlotData 格式
7598
7665
  var mapPlotData = useCallback(function (rawItem) {
7599
7666
  var id = rawItem[mergedFieldNames.id];
7600
- var positions = rawItem[mergedFieldNames.positions];
7667
+ var rawPositions = rawItem[mergedFieldNames.positions];
7601
7668
  var area = rawItem[mergedFieldNames.area];
7602
7669
  var possessor = rawItem[mergedFieldNames.possessor];
7670
+ // 检测并转换坐标格式
7671
+ var detectResult = detectAndNormalizeCoordinates(rawPositions);
7672
+ var positions = detectResult.normalized;
7673
+ var order = detectResult.order;
7603
7674
  // 二维 positions [[lat,lng],...] → 三维 [[[lat,lng],...]]
7604
7675
  if (Array.isArray(positions) && !is3DPositions(positions)) {
7605
7676
  positions = [positions];
@@ -7623,7 +7694,8 @@ function usePlotData(_ref) {
7623
7694
  positions: positions,
7624
7695
  area: area,
7625
7696
  possessor: possessor,
7626
- customData: Object.keys(customData).length > 0 ? customData : undefined
7697
+ customData: Object.keys(customData).length > 0 ? customData : undefined,
7698
+ _coordinateOrder: order
7627
7699
  }, extensionProps);
7628
7700
  }, [mergedFieldNames]);
7629
7701
  // 标准化数据源
@@ -8267,12 +8339,16 @@ function usePlotEditActions(_ref) {
8267
8339
  * 获取完整的编辑结果数据
8268
8340
  * @param coords - 编辑后的坐标
8269
8341
  * @param area - 编辑后的面积
8270
- * @returns 完整的地块数据(包含原始属性+新坐标)
8342
+ * @returns 完整的地块数据(包含原始属性+新坐标),坐标格式保持与输入一致
8271
8343
  */
8272
8344
  var getFullPlotData = useCallback(function (coords, area) {
8273
8345
  if (!localPlotDataRef.current) return null;
8346
+ // 获取输入坐标格式
8347
+ var order = localPlotDataRef.current._coordinateOrder;
8348
+ // 转换为原始格式
8349
+ var originalCoords = convertToOriginalFormat(coords, order);
8274
8350
  return _objectSpread2(_objectSpread2({}, localPlotDataRef.current), {}, {
8275
- positions: [coords],
8351
+ positions: [originalCoords],
8276
8352
  area: area
8277
8353
  });
8278
8354
  }, []);
@@ -8336,6 +8412,7 @@ function usePlotEditActions(_ref) {
8336
8412
  }, [cleanupEditEvents, cleanupFragmentLayers, setEditMode, editingLayerRef, drawingCoordinatesRef, preserveTempLayerRef, map]);
8337
8413
  // ========== 操作处理函数 ==========
8338
8414
  var handleSave = useCallback(function () {
8415
+ var _localPlotDataRef$cur;
8339
8416
  var currentEditMode = editModeStateRef.current;
8340
8417
  disableDrawMode(map);
8341
8418
  var coords = drawingCoordinatesRef.current;
@@ -8354,12 +8431,16 @@ function usePlotEditActions(_ref) {
8354
8431
  return;
8355
8432
  }
8356
8433
  var areaSquareMeters = calculatePolygonArea(coords);
8434
+ // 获取输入坐标格式并转换为原始格式
8435
+ var order = (_localPlotDataRef$cur = localPlotDataRef.current) === null || _localPlotDataRef$cur === void 0 ? void 0 : _localPlotDataRef$cur._coordinateOrder;
8436
+ var originalCoords = convertToOriginalFormat(coords, order);
8357
8437
  // 获取完整的地块数据(包含原始属性)
8358
8438
  var fullPlotData = getFullPlotData(coords, areaSquareMeters);
8359
8439
  var result = {
8360
8440
  plotId: selectedPlotId,
8361
8441
  mode: currentEditMode,
8362
- coordinates: coords,
8442
+ coordinates: originalCoords,
8443
+ // 保持与输入一致的坐标格式
8363
8444
  area: areaSquareMeters,
8364
8445
  areaUnit: areaUnit,
8365
8446
  plot: fullPlotData || undefined
@@ -8369,7 +8450,10 @@ function usePlotEditActions(_ref) {
8369
8450
  result.selectedFragment = selectedFragmentRef.current;
8370
8451
  }
8371
8452
  if (currentEditMode === 'clip' && clipHolesRef.current) {
8372
- result.positions = clipHolesRef.current;
8453
+ // 转换孔洞坐标为原始格式
8454
+ result.positions = clipHolesRef.current.map(function (hole) {
8455
+ return convertToOriginalFormat(hole, order);
8456
+ });
8373
8457
  }
8374
8458
  dispatchEditResult(result);
8375
8459
  cleanupAfterSave(currentEditMode);