@lumikmz/kmz 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -59,7 +59,7 @@ type LidarReturnMode = "singleReturnStrongest" | "dualReturn" | "tripleReturn";
59
59
  type LidarScanningMode = "repetitive" | "nonRepetitive";
60
60
  type ActionGroupMode = "sequence";
61
61
  type ActionTriggerType = "reachPoint" | "betweenAdjacentPoints" | "betweenAdjacentPointsIncludeFirstPoint" | "multipleTiming" | "multipleDistance";
62
- type ActionActuatorFunc = "takePhoto" | "startRecord" | "stopRecord" | "focus" | "zoom" | "customDirName" | "gimbalRotate" | "rotateYaw" | "hover" | "gimbalEvenlyRotate" | "accurateShoot" | "orientedShoot" | "panoShot" | "recordPointCloud" | "megaphone" | "searchlight" /** ⚠️ NON-SPEC — observed in real DJI KMZ exports, absent from WPML 1.15 docs. */ | "startSmartOblique" /** ⚠️ NON-SPEC — observed in real DJI KMZ exports, absent from WPML 1.15 docs. */ | "stopSmartOblique" /** Starts time-lapse (interval) shooting for a mapping strip. */ | "startTimeLapse" /** Stops time-lapse shooting. */ | "stopTimeLapse" /** Locks gimbal angle during a mapping strip. */ | "gimbalAngleLock" /** Unlocks gimbal angle (no params in XML). */ | "gimbalAngleUnlock" /** Sets camera focus mode (manual / auto). */ | "setFocusType";
62
+ type ActionActuatorFunc = "targetDetection" | "takePhoto" | "startRecord" | "stopRecord" | "focus" | "zoom" | "customDirName" | "gimbalRotate" | "rotateYaw" | "hover" | "gimbalEvenlyRotate" | "accurateShoot" | "orientedShoot" | "panoShot" | "recordPointCloud" | "megaphone" | "searchlight" /** ⚠️ NON-SPEC — observed in real DJI KMZ exports, absent from WPML 1.15 docs. */ | "startSmartOblique" /** ⚠️ NON-SPEC — observed in real DJI KMZ exports, absent from WPML 1.15 docs. */ | "stopSmartOblique" /** Starts time-lapse (interval) shooting for a mapping strip. */ | "startTimeLapse" /** Stops time-lapse shooting. */ | "stopTimeLapse" /** Locks gimbal angle during a mapping strip. */ | "gimbalAngleLock" /** Unlocks gimbal angle (no params in XML). */ | "gimbalAngleUnlock" /** Sets camera focus mode (manual / auto). */ | "setFocusType";
63
63
  type GimbalHeadingYawBase = "north" | "aircraft";
64
64
  type GimbalRotateMode = "absoluteAngle";
65
65
  type AircraftPathMode = "clockwise" | "counterClockwise";
@@ -81,6 +81,8 @@ interface PayloadInfo {
81
81
  */
82
82
  payloadSubEnumValue?: number;
83
83
  payloadPositionIndex: number;
84
+ /** FlightHub 2 patrol-camera selection, in 35mm-equivalent millimeters. */
85
+ equivalentFocalLength?: number;
84
86
  }
85
87
  /** Aircraft `{ droneEnumValue, droneSubEnumValue }` tuples. */
86
88
  declare const DroneEnum: {
@@ -399,12 +401,36 @@ interface ActionTrigger {
399
401
  actionTriggerParam?: number;
400
402
  }
401
403
  /** Discriminated by `actionActuatorFunc`. */
402
- type Action = TakePhotoAction | StartRecordAction | StopRecordAction | FocusAction | ZoomAction | CustomDirNameAction | GimbalRotateAction | RotateYawAction | HoverAction | GimbalEvenlyRotateAction | AccurateShootAction | OrientedShootAction | PanoShotAction | RecordPointCloudAction | MegaphoneAction | SearchlightAction | StartSmartObliqueAction | StopSmartObliqueAction | StartTimeLapseAction | StopTimeLapseAction | GimbalAngleLockAction | GimbalAngleUnlockAction | SetFocusTypeAction;
404
+ type Action = TargetDetectionAction | TakePhotoAction | StartRecordAction | StopRecordAction | FocusAction | ZoomAction | CustomDirNameAction | GimbalRotateAction | RotateYawAction | HoverAction | GimbalEvenlyRotateAction | AccurateShootAction | OrientedShootAction | PanoShotAction | RecordPointCloudAction | MegaphoneAction | SearchlightAction | StartSmartObliqueAction | StopSmartObliqueAction | StartTimeLapseAction | StopTimeLapseAction | GimbalAngleLockAction | GimbalAngleUnlockAction | SetFocusTypeAction;
403
405
  interface ActionBase<F extends string, P> {
404
406
  actionId: number;
405
407
  actionActuatorFunc: F;
406
408
  actionActuatorFuncParam: P;
407
409
  }
410
+ interface TargetDetectionParam {
411
+ focalLength?: number;
412
+ payloadPositionIndex: number;
413
+ useGlobalPayloadLensIndex: boolean;
414
+ payloadLensIndex: LensType[];
415
+ actionUUID: string;
416
+ irGainMode?: string;
417
+ irColorPalette?: string;
418
+ customTitle: string;
419
+ customText: string;
420
+ modelIndex: number;
421
+ modelScore: number;
422
+ alarmInterval: number;
423
+ useThirdModel: boolean;
424
+ algorithmType: string;
425
+ targetParam: {
426
+ maxThreshold: number;
427
+ minThreshold: number;
428
+ useMaxThreshold: boolean;
429
+ useMinThreshold: boolean;
430
+ targetType: string;
431
+ };
432
+ }
433
+ type TargetDetectionAction = ActionBase<"targetDetection", TargetDetectionParam>;
408
434
  interface TakePhotoParam {
409
435
  payloadPositionIndex: number;
410
436
  fileSuffix?: string;
@@ -763,6 +789,9 @@ interface TargetDetectionPlacemark {
763
789
  mappingHeadingParam: MappingHeadingParam;
764
790
  gimbalPitchAngle: Degrees;
765
791
  recordEnable: boolean;
792
+ /** FlightHub 2 always emits this action for patrol-area routes. */
793
+ targetDetectionActionEnable?: boolean;
794
+ action?: TargetDetectionAction;
766
795
  }
767
796
  /** `Placemark` inside a `mappingStrip` Folder — uses a LineString; params live here. */
768
797
  interface MappingStripPlacemark {
@@ -1031,4 +1060,4 @@ declare function validate(doc: Template | Waylines): Issue[];
1031
1060
  /** Great-circle distance between two WGS-84 coordinates, in meters. */
1032
1061
  declare function haversineMeters(a: LngLat, b: LngLat): number;
1033
1062
  //#endregion
1034
- export { AccurateShootAction, AccurateShootParam, Action, ActionActuatorFunc, ActionGroup, ActionGroupMode, ActionTrigger, ActionTriggerType, AircraftPathMode, AutoRerouteInfo, CoordinateMode, CustomDirNameAction, CustomDirNameParam, Degrees, DroneEnum, DroneInfo, ExecuteHeightMode, ExecuteRCLostAction, ExitOnRCLost, FinishAction, FlyToWaylineMode, FocusAction, FocusMode, FocusParam, GimbalAngleLockAction, GimbalAngleLockParam, GimbalAngleUnlockAction, GimbalEvenlyRotateAction, GimbalEvenlyRotateParam, GimbalHeadingYawBase, GimbalPitchMode, GimbalRotateAction, GimbalRotateMode, GimbalRotateParam, HoverAction, HoverParam, Issue, KmzError, LensType, LidarReturnMode, LidarScanningMode, LngLat, LngLatHeight, Mapping2dFolder, Mapping2dPlacemark, Mapping2dTemplate, Mapping3dFolder, Mapping3dPlacemark, Mapping3dTemplate, MappingHeadingMode, MappingHeadingParam, type MappingStripDerived, MappingStripFolder, MappingStripPlacemark, type MappingStripRegion, type MappingStripRegionRoute, MappingStripTemplate, MegaphoneAction, MegaphoneParam, MegaphoneTtsParam, MeteringMode, Meters, MetersPerSecond, MissionConfig, OrientedPhotoMode, OrientedShootAction, OrientedShootParam, Overlap, PanoShotAction, PanoShotParam, PanoShotSubMode, PayloadEnum, PayloadInfo, PayloadParam, type PlanOptions, PlannerError, PositioningType, RecordPointCloudAction, RecordPointCloudOperate, RecordPointCloudParam, RotateYawAction, RotateYawParam, SearchlightAction, SearchlightParam, SetFocusTypeAction, SetFocusTypeParam, ShootType, SmartObliquePoint, StartActionGroup, StartRecordAction, StartRecordParam, StartSmartObliqueAction, StartSmartObliqueParam, StartTimeLapseAction, StartTimeLapseParam, StopRecordAction, StopRecordParam, StopSmartObliqueAction, StopSmartObliqueParam, StopTimeLapseAction, StopTimeLapseParam, TakePhotoAction, TakePhotoParam, TargetDetectionFolder, TargetDetectionPlacemark, TargetDetectionTemplate, Template, TemplateHeightMode, TemplatePolygon, TemplateType, TemplateWaypointPlacemark, WaylineCoordinateSysParam, Waylines, WaylinesFolder, WaylinesPlacemark, WaypointFolder, WaypointHeadingMode, WaypointHeadingParam, WaypointHeadingPathMode, WaypointTemplate, WaypointTurnMode, WaypointTurnParam, ZoomAction, ZoomParam, degrees, deriveMappingStrip, haversineMeters, meters, metersPerSecond, plan, validate };
1063
+ export { AccurateShootAction, AccurateShootParam, Action, ActionActuatorFunc, ActionGroup, ActionGroupMode, ActionTrigger, ActionTriggerType, AircraftPathMode, AutoRerouteInfo, CoordinateMode, CustomDirNameAction, CustomDirNameParam, Degrees, DroneEnum, DroneInfo, ExecuteHeightMode, ExecuteRCLostAction, ExitOnRCLost, FinishAction, FlyToWaylineMode, FocusAction, FocusMode, FocusParam, GimbalAngleLockAction, GimbalAngleLockParam, GimbalAngleUnlockAction, GimbalEvenlyRotateAction, GimbalEvenlyRotateParam, GimbalHeadingYawBase, GimbalPitchMode, GimbalRotateAction, GimbalRotateMode, GimbalRotateParam, HoverAction, HoverParam, Issue, KmzError, LensType, LidarReturnMode, LidarScanningMode, LngLat, LngLatHeight, Mapping2dFolder, Mapping2dPlacemark, Mapping2dTemplate, Mapping3dFolder, Mapping3dPlacemark, Mapping3dTemplate, MappingHeadingMode, MappingHeadingParam, type MappingStripDerived, MappingStripFolder, MappingStripPlacemark, type MappingStripRegion, type MappingStripRegionRoute, MappingStripTemplate, MegaphoneAction, MegaphoneParam, MegaphoneTtsParam, MeteringMode, Meters, MetersPerSecond, MissionConfig, OrientedPhotoMode, OrientedShootAction, OrientedShootParam, Overlap, PanoShotAction, PanoShotParam, PanoShotSubMode, PayloadEnum, PayloadInfo, PayloadParam, type PlanOptions, PlannerError, PositioningType, RecordPointCloudAction, RecordPointCloudOperate, RecordPointCloudParam, RotateYawAction, RotateYawParam, SearchlightAction, SearchlightParam, SetFocusTypeAction, SetFocusTypeParam, ShootType, SmartObliquePoint, StartActionGroup, StartRecordAction, StartRecordParam, StartSmartObliqueAction, StartSmartObliqueParam, StartTimeLapseAction, StartTimeLapseParam, StopRecordAction, StopRecordParam, StopSmartObliqueAction, StopSmartObliqueParam, StopTimeLapseAction, StopTimeLapseParam, TakePhotoAction, TakePhotoParam, TargetDetectionAction, TargetDetectionFolder, TargetDetectionParam, TargetDetectionPlacemark, TargetDetectionTemplate, Template, TemplateHeightMode, TemplatePolygon, TemplateType, TemplateWaypointPlacemark, WaylineCoordinateSysParam, Waylines, WaylinesFolder, WaylinesPlacemark, WaypointFolder, WaypointHeadingMode, WaypointHeadingParam, WaypointHeadingPathMode, WaypointTemplate, WaypointTurnMode, WaypointTurnParam, ZoomAction, ZoomParam, degrees, deriveMappingStrip, haversineMeters, meters, metersPerSecond, plan, validate };
package/dist/index.mjs CHANGED
@@ -1866,6 +1866,17 @@ function gimbalAction(pitch) {
1866
1866
  }
1867
1867
  };
1868
1868
  }
1869
+ function startRecordAction(payloadPositionIndex, actionId) {
1870
+ return {
1871
+ actionId,
1872
+ actionActuatorFunc: "startRecord",
1873
+ actionActuatorFuncParam: {
1874
+ payloadPositionIndex,
1875
+ useGlobalPayloadLensIndex: true,
1876
+ payloadLensIndex: ["visable"]
1877
+ }
1878
+ };
1879
+ }
1869
1880
  function pathPoints(template, lineSpacingM, preferredStartPoint) {
1870
1881
  const placemark = template.Folder.Placemark;
1871
1882
  const ring = parseCoordinatesString(placemark.Polygon.outerBoundaryIs.LinearRing.coordinates);
@@ -1920,6 +1931,13 @@ function planTargetDetection(template, options) {
1920
1931
  if (points.length < 2) throw new PlannerError("targetdetection polygon produced no flight path");
1921
1932
  const lastIndex = points.length - 1;
1922
1933
  const action = gimbalAction(pitch);
1934
+ const payloadPositionIndex = folder.payloadParam?.payloadPositionIndex ?? 0;
1935
+ const routeActions = [action];
1936
+ if (folder.Placemark.recordEnable) routeActions.push(startRecordAction(payloadPositionIndex, routeActions.length));
1937
+ if (folder.Placemark.action) routeActions.push({
1938
+ ...folder.Placemark.action,
1939
+ actionId: routeActions.length
1940
+ });
1923
1941
  const placemarks = points.map((point, index) => ({
1924
1942
  Point: { coordinates: point },
1925
1943
  index,
@@ -1955,7 +1973,7 @@ function planTargetDetection(template, options) {
1955
1973
  actionGroupEndIndex: lastIndex,
1956
1974
  actionGroupMode: "sequence",
1957
1975
  actionTrigger: { actionTriggerType: "betweenAdjacentPointsIncludeFirstPoint" },
1958
- action: [action]
1976
+ action: routeActions
1959
1977
  }] } : {}
1960
1978
  }));
1961
1979
  return {
@@ -1965,7 +1983,7 @@ function planTargetDetection(template, options) {
1965
1983
  waylineId: 0,
1966
1984
  executeHeightMode: executeHeightMode(folder),
1967
1985
  autoFlightSpeed: folder.autoFlightSpeed,
1968
- startActionGroup: { action: [gimbalAction(pitch)] },
1986
+ startActionGroup: { action: folder.Placemark.recordEnable ? [gimbalAction(pitch), startRecordAction(payloadPositionIndex, 1)] : [gimbalAction(pitch)] },
1969
1987
  Placemark: placemarks
1970
1988
  }]
1971
1989
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumikmz/kmz",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "DJI KMZ/WPML domain model + plan() algorithm + validate(), spec-aligned types mirroring docs/kmz/",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -54,6 +54,49 @@ function template(overrides: { pitch?: number; direction?: number } = {}): Targe
54
54
  }
55
55
 
56
56
  describe("plan(targetdetection)", () => {
57
+ it("matches FlightHub recording and target-detection actions without stopRecord", () => {
58
+ const input = template();
59
+ Object.assign(input.Folder.Placemark, {
60
+ recordEnable: true,
61
+ targetDetectionActionEnable: true,
62
+ action: {
63
+ actionId: 0,
64
+ actionActuatorFunc: "targetDetection",
65
+ actionActuatorFuncParam: {
66
+ focalLength: 70,
67
+ payloadPositionIndex: 0,
68
+ useGlobalPayloadLensIndex: false,
69
+ payloadLensIndex: ["visable"],
70
+ actionUUID: "test-target-detection",
71
+ customTitle: "巡逻",
72
+ customText: "检测到异常目标",
73
+ modelIndex: 0,
74
+ modelScore: 55,
75
+ alarmInterval: 2,
76
+ useThirdModel: false,
77
+ algorithmType: "visable_ai",
78
+ targetParam: {
79
+ maxThreshold: 10,
80
+ minThreshold: 10,
81
+ useMaxThreshold: false,
82
+ useMinThreshold: true,
83
+ targetType: "person",
84
+ },
85
+ },
86
+ },
87
+ });
88
+
89
+ const folder = plan(input, { lineSpacing: meters(25) }).Folder[0]!;
90
+ const startFuncs = folder.startActionGroup!.action.map((a) => a.actionActuatorFunc);
91
+ const routeFuncs = folder.Placemark[0]!.actionGroup![0]!.action.map(
92
+ (a) => a.actionActuatorFunc,
93
+ );
94
+
95
+ expect(startFuncs).toEqual(["gimbalRotate", "startRecord"]);
96
+ expect(routeFuncs).toEqual(["gimbalRotate", "startRecord", "targetDetection"]);
97
+ expect(JSON.stringify(folder)).not.toContain("stopRecord");
98
+ });
99
+
57
100
  it("centers floor(width / spacing) scan lines", () => {
58
101
  const waylines = plan(template(), { lineSpacing: meters(25) });
59
102
 
@@ -1,4 +1,5 @@
1
1
  import { degrees, meters, type LngLat, type Meters } from "../types/branded.js";
2
+ import type { Action } from "../types/action.js";
2
3
  import { PlannerError } from "../types/errors.js";
3
4
  import type { ExecuteHeightMode } from "../types/enums.js";
4
5
  import type { WaylinesPlacemark } from "../types/placemark.js";
@@ -59,6 +60,18 @@ function gimbalAction(pitch: number) {
59
60
  };
60
61
  }
61
62
 
63
+ function startRecordAction(payloadPositionIndex: number, actionId: number) {
64
+ return {
65
+ actionId,
66
+ actionActuatorFunc: "startRecord" as const,
67
+ actionActuatorFuncParam: {
68
+ payloadPositionIndex,
69
+ useGlobalPayloadLensIndex: true,
70
+ payloadLensIndex: ["visable" as const],
71
+ },
72
+ };
73
+ }
74
+
62
75
  function pathPoints(
63
76
  template: TargetDetectionTemplate,
64
77
  lineSpacingM: number,
@@ -143,6 +156,14 @@ export function planTargetDetection(
143
156
  if (points.length < 2) throw new PlannerError("targetdetection polygon produced no flight path");
144
157
  const lastIndex = points.length - 1;
145
158
  const action = gimbalAction(pitch);
159
+ const payloadPositionIndex = folder.payloadParam?.payloadPositionIndex ?? 0;
160
+ const routeActions: Action[] = [action];
161
+ if (folder.Placemark.recordEnable) {
162
+ routeActions.push(startRecordAction(payloadPositionIndex, routeActions.length));
163
+ }
164
+ if (folder.Placemark.action) {
165
+ routeActions.push({ ...folder.Placemark.action, actionId: routeActions.length });
166
+ }
146
167
 
147
168
  const placemarks: WaylinesPlacemark[] = points.map((point, index) => ({
148
169
  Point: { coordinates: point },
@@ -185,7 +206,7 @@ export function planTargetDetection(
185
206
  actionGroupEndIndex: lastIndex,
186
207
  actionGroupMode: "sequence",
187
208
  actionTrigger: { actionTriggerType: "betweenAdjacentPointsIncludeFirstPoint" },
188
- action: [action],
209
+ action: routeActions,
189
210
  },
190
211
  ],
191
212
  }
@@ -200,7 +221,11 @@ export function planTargetDetection(
200
221
  waylineId: 0,
201
222
  executeHeightMode: executeHeightMode(folder),
202
223
  autoFlightSpeed: folder.autoFlightSpeed,
203
- startActionGroup: { action: [gimbalAction(pitch)] },
224
+ startActionGroup: {
225
+ action: folder.Placemark.recordEnable
226
+ ? [gimbalAction(pitch), startRecordAction(payloadPositionIndex, 1)]
227
+ : [gimbalAction(pitch)],
228
+ },
204
229
  Placemark: placemarks,
205
230
  },
206
231
  ],
@@ -34,6 +34,7 @@ export interface ActionTrigger {
34
34
 
35
35
  /** Discriminated by `actionActuatorFunc`. */
36
36
  export type Action =
37
+ | TargetDetectionAction
37
38
  | TakePhotoAction
38
39
  | StartRecordAction
39
40
  | StopRecordAction
@@ -64,6 +65,31 @@ interface ActionBase<F extends string, P> {
64
65
  actionActuatorFuncParam: P;
65
66
  }
66
67
 
68
+ export interface TargetDetectionParam {
69
+ focalLength?: number;
70
+ payloadPositionIndex: number;
71
+ useGlobalPayloadLensIndex: boolean;
72
+ payloadLensIndex: LensType[];
73
+ actionUUID: string;
74
+ irGainMode?: string;
75
+ irColorPalette?: string;
76
+ customTitle: string;
77
+ customText: string;
78
+ modelIndex: number;
79
+ modelScore: number;
80
+ alarmInterval: number;
81
+ useThirdModel: boolean;
82
+ algorithmType: string;
83
+ targetParam: {
84
+ maxThreshold: number;
85
+ minThreshold: number;
86
+ useMaxThreshold: boolean;
87
+ useMinThreshold: boolean;
88
+ targetType: string;
89
+ };
90
+ }
91
+ export type TargetDetectionAction = ActionBase<"targetDetection", TargetDetectionParam>;
92
+
67
93
  // ─── Action params (one type per actionActuatorFunc) ─────────────────────────
68
94
 
69
95
  export interface TakePhotoParam {
@@ -15,6 +15,8 @@ export interface PayloadInfo {
15
15
  */
16
16
  payloadSubEnumValue?: number;
17
17
  payloadPositionIndex: number;
18
+ /** FlightHub 2 patrol-camera selection, in 35mm-equivalent millimeters. */
19
+ equivalentFocalLength?: number;
18
20
  }
19
21
 
20
22
  /** Aircraft `{ droneEnumValue, droneSubEnumValue }` tuples. */
@@ -73,6 +73,7 @@ export type ActionTriggerType =
73
73
  | "multipleDistance";
74
74
 
75
75
  export type ActionActuatorFunc =
76
+ | "targetDetection"
76
77
  | "takePhoto"
77
78
  | "startRecord"
78
79
  | "stopRecord"
@@ -1,5 +1,5 @@
1
1
  import type { Degrees, LngLat, Meters, MetersPerSecond } from "./branded.js";
2
- import type { ActionGroup } from "./action.js";
2
+ import type { ActionGroup, TargetDetectionAction } from "./action.js";
3
3
  import type { ExecuteHeightMode, ShootType } from "./enums.js";
4
4
  import type {
5
5
  MappingHeadingParam,
@@ -127,6 +127,9 @@ export interface TargetDetectionPlacemark {
127
127
  mappingHeadingParam: MappingHeadingParam;
128
128
  gimbalPitchAngle: Degrees;
129
129
  recordEnable: boolean;
130
+ /** FlightHub 2 always emits this action for patrol-area routes. */
131
+ targetDetectionActionEnable?: boolean;
132
+ action?: TargetDetectionAction;
130
133
  }
131
134
 
132
135
  /** `Placemark` inside a `mappingStrip` Folder — uses a LineString; params live here. */