@lumikmz/kmz 0.6.1 → 0.6.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.d.mts CHANGED
@@ -575,8 +575,20 @@ interface MegaphoneParam {
575
575
  }
576
576
  type MegaphoneAction = ActionBase<"megaphone", MegaphoneParam>;
577
577
  /** M4D/M4TD only — starts gimbal swing in smart-oblique mode. */
578
+ interface SmartObliquePoint {
579
+ /** seconds before this pose starts; DJI exports currently use 0. */
580
+ smartObliqueRunningTime: number;
581
+ /** seconds to hold this pose for smart-oblique shooting. */
582
+ smartObliqueStayTime: number;
583
+ smartObliqueEulerPitch: Degrees;
584
+ smartObliqueEulerRoll: Degrees;
585
+ smartObliqueEulerYaw: Degrees;
586
+ }
578
587
  interface StartSmartObliqueParam {
588
+ /** Observed in DJI exports; older generated files may omit it. */
589
+ smartObliqueCycleMode?: "unlimited";
579
590
  payloadPositionIndex: number;
591
+ smartObliquePoint?: SmartObliquePoint[];
580
592
  }
581
593
  type StartSmartObliqueAction = ActionBase<"startSmartOblique", StartSmartObliqueParam>;
582
594
  /** M4D/M4TD only — stops gimbal swing in smart-oblique mode. */
@@ -931,4 +943,4 @@ declare function validate(doc: Template | Waylines): Issue[];
931
943
  /** Great-circle distance between two WGS-84 coordinates, in meters. */
932
944
  declare function haversineMeters(a: LngLat, b: LngLat): number;
933
945
  //#endregion
934
- 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, MappingStripFolder, MappingStripPlacemark, MappingStripTemplate, MegaphoneAction, MegaphoneParam, 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, StartActionGroup, StartRecordAction, StartRecordParam, StartSmartObliqueAction, StartSmartObliqueParam, StartTimeLapseAction, StartTimeLapseParam, StopRecordAction, StopRecordParam, StopSmartObliqueAction, StopSmartObliqueParam, StopTimeLapseAction, StopTimeLapseParam, TakePhotoAction, TakePhotoParam, Template, TemplateHeightMode, TemplatePolygon, TemplateType, TemplateWaypointPlacemark, WaylineCoordinateSysParam, Waylines, WaylinesFolder, WaylinesPlacemark, WaypointFolder, WaypointHeadingMode, WaypointHeadingParam, WaypointHeadingPathMode, WaypointTemplate, WaypointTurnMode, WaypointTurnParam, ZoomAction, ZoomParam, degrees, haversineMeters, meters, metersPerSecond, plan, validate };
946
+ 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, MappingStripFolder, MappingStripPlacemark, MappingStripTemplate, MegaphoneAction, MegaphoneParam, 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, Template, TemplateHeightMode, TemplatePolygon, TemplateType, TemplateWaypointPlacemark, WaylineCoordinateSysParam, Waylines, WaylinesFolder, WaylinesPlacemark, WaypointFolder, WaypointHeadingMode, WaypointHeadingParam, WaypointHeadingPathMode, WaypointTemplate, WaypointTurnMode, WaypointTurnParam, ZoomAction, ZoomParam, degrees, haversineMeters, meters, metersPerSecond, plan, validate };
package/dist/index.mjs CHANGED
@@ -734,9 +734,10 @@ function emitOrtho(folder, origin, enuWps, shootInterval, options) {
734
734
  * Single-pass smart-oblique / quick-ortho (smartObliqueEnable=true):
735
735
  * The aircraft flies one grid while the gimbal swings (startSmartOblique/stopSmartOblique).
736
736
  */
737
- function emitSmartOblique(folder, origin, enuWps, options) {
737
+ function emitSmartOblique(folder, origin, enuWps, shootInterval, options) {
738
738
  const lastIndex = enuWps.length - 1;
739
739
  const pitch = folder.Placemark.smartObliqueGimbalPitch ?? degrees(-45);
740
+ const smartObliquePoints = buildSmartObliquePoints(Number(pitch), shootInterval);
740
741
  const placemarks = enuWps.map((enu, index) => {
741
742
  const coord = enuToLngLat(enu, origin);
742
743
  const isLast = index === lastIndex;
@@ -761,8 +762,16 @@ function emitSmartOblique(folder, origin, enuWps, options) {
761
762
  actionTrigger: { actionTriggerType: "reachPoint" },
762
763
  action: [{
763
764
  actionId: 0,
764
- actionActuatorFunc: "startSmartOblique",
765
+ actionActuatorFunc: "gimbalAngleLock",
765
766
  actionActuatorFuncParam: { payloadPositionIndex: 0 }
767
+ }, {
768
+ actionId: 1,
769
+ actionActuatorFunc: "startSmartOblique",
770
+ actionActuatorFuncParam: {
771
+ smartObliqueCycleMode: "unlimited",
772
+ payloadPositionIndex: 0,
773
+ smartObliquePoint: smartObliquePoints
774
+ }
766
775
  }]
767
776
  }];
768
777
  else if (isLast) placemark.actionGroup = [{
@@ -775,6 +784,9 @@ function emitSmartOblique(folder, origin, enuWps, options) {
775
784
  actionId: 0,
776
785
  actionActuatorFunc: "stopSmartOblique",
777
786
  actionActuatorFuncParam: { payloadPositionIndex: 0 }
787
+ }, {
788
+ actionId: 1,
789
+ actionActuatorFunc: "gimbalAngleUnlock"
778
790
  }]
779
791
  }];
780
792
  return placemark;
@@ -788,6 +800,26 @@ function emitSmartOblique(folder, origin, enuWps, options) {
788
800
  Placemark: placemarks
789
801
  };
790
802
  }
803
+ function buildSmartObliquePoints(pitchDeg, shootInterval) {
804
+ const stayTime = Math.max(.1, shootInterval);
805
+ const obliquePitch = -Math.abs(pitchDeg || 45);
806
+ return [
807
+ smartObliquePoint(-90, 0, 0),
808
+ smartObliquePoint(obliquePitch, 45, stayTime),
809
+ smartObliquePoint(obliquePitch, -45, stayTime),
810
+ smartObliquePoint(-120, 50, stayTime),
811
+ smartObliquePoint(-120, -50, stayTime)
812
+ ];
813
+ }
814
+ function smartObliquePoint(pitchDeg, yawDeg, stayTime) {
815
+ return {
816
+ smartObliqueRunningTime: 0,
817
+ smartObliqueStayTime: stayTime,
818
+ smartObliqueEulerPitch: degrees(pitchDeg),
819
+ smartObliqueEulerRoll: degrees(0),
820
+ smartObliqueEulerYaw: degrees(yawDeg)
821
+ };
822
+ }
791
823
  function planMapping2d(template, options) {
792
824
  if (options?.lineSpacing == null) throw new PlannerError("mapping2d requires options.lineSpacing — calculate 2·h·tan(HFOV/2)·(1−sideOverlap) in the demo and pass it in.");
793
825
  const standoff = template.Folder.Placemark.smartObliqueEnable === true ? smartObliqueStandoff(template.Folder) : 0;
@@ -795,7 +827,7 @@ function planMapping2d(template, options) {
795
827
  const shootInterval = options.shootInterval ?? 2;
796
828
  if (folder.Placemark.smartObliqueEnable === true) return {
797
829
  missionConfig: template.missionConfig,
798
- Folder: [emitSmartOblique(folder, origin, enuWps, options)]
830
+ Folder: [emitSmartOblique(folder, origin, enuWps, shootInterval, options)]
799
831
  };
800
832
  return {
801
833
  missionConfig: template.missionConfig,
@@ -1061,7 +1093,7 @@ function planWaypoint(template) {
1061
1093
  const placemarks = folder.Placemark.map((p, i) => {
1062
1094
  const heading = p.useGlobalHeadingParam || p.waypointHeadingParam === void 0 ? folder.globalWaypointHeadingParam : p.waypointHeadingParam;
1063
1095
  const turn = p.useGlobalTurnParam || p.waypointTurnParam === void 0 ? { waypointTurnMode: folder.globalWaypointTurnMode } : p.waypointTurnParam;
1064
- const executeHeight = p.useGlobalHeight || p.height === void 0 ? folder.globalHeight : p.height;
1096
+ const executeHeight = folder.waylineCoordinateSysParam.heightMode === "aboveGroundLevel" && p.ellipsoidHeight !== void 0 ? p.ellipsoidHeight : p.useGlobalHeight || p.height === void 0 ? folder.globalHeight : p.height;
1065
1097
  const waypointSpeed = p.useGlobalSpeed || p.waypointSpeed === void 0 ? folder.autoFlightSpeed : p.waypointSpeed;
1066
1098
  return {
1067
1099
  Point: p.Point,
@@ -1090,13 +1122,13 @@ function planWaypoint(template) {
1090
1122
  /**
1091
1123
  * Map a template heightMode to a waylines executeHeightMode.
1092
1124
  * - `EGM96` → `WGS84` (waylines spec doesn't expose EGM96; runtime resolves it).
1093
- * - `aboveGroundLevel` → `realTimeFollowSurface` (closest executable analog).
1125
+ * - `aboveGroundLevel` → `WGS84` (waypoint waylines use per-point ellipsoid heights).
1094
1126
  */
1095
1127
  function heightModeToExecuteMode(mode) {
1096
1128
  switch (mode) {
1097
- case "EGM96": return "WGS84";
1129
+ case "EGM96":
1130
+ case "aboveGroundLevel": return "WGS84";
1098
1131
  case "relativeToStartPoint": return "relativeToStartPoint";
1099
- case "aboveGroundLevel":
1100
1132
  case "realTimeFollowSurface": return "realTimeFollowSurface";
1101
1133
  }
1102
1134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumikmz/kmz",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
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",
@@ -1,5 +1,5 @@
1
1
  import { degrees, meters, type Meters } from "../types/branded.js";
2
- import type { Action } from "../types/action.js";
2
+ import type { Action, SmartObliquePoint } from "../types/action.js";
3
3
  import type { ExecuteHeightMode } from "../types/enums.js";
4
4
  import type { Mapping2dFolder, Mapping2dTemplate } from "../types/template.js";
5
5
  import type { StartActionGroup } from "../types/waylines.js";
@@ -466,10 +466,12 @@ function emitSmartOblique(
466
466
  folder: Mapping2dFolder,
467
467
  origin: { lng: number; lat: number },
468
468
  enuWps: readonly EnuPoint[],
469
+ shootInterval: number,
469
470
  options?: PlanOptions,
470
471
  ): WaylinesFolder {
471
472
  const lastIndex = enuWps.length - 1;
472
473
  const pitch = folder.Placemark.smartObliqueGimbalPitch ?? degrees(-45);
474
+ const smartObliquePoints = buildSmartObliquePoints(Number(pitch), shootInterval);
473
475
 
474
476
  const placemarks: WaylinesPlacemark[] = enuWps.map((enu, index) => {
475
477
  const coord = enuToLngLat(enu, origin);
@@ -501,9 +503,18 @@ function emitSmartOblique(
501
503
  action: [
502
504
  {
503
505
  actionId: 0,
504
- actionActuatorFunc: "startSmartOblique",
506
+ actionActuatorFunc: "gimbalAngleLock",
505
507
  actionActuatorFuncParam: { payloadPositionIndex: 0 },
506
508
  },
509
+ {
510
+ actionId: 1,
511
+ actionActuatorFunc: "startSmartOblique",
512
+ actionActuatorFuncParam: {
513
+ smartObliqueCycleMode: "unlimited",
514
+ payloadPositionIndex: 0,
515
+ smartObliquePoint: smartObliquePoints,
516
+ },
517
+ },
507
518
  ],
508
519
  },
509
520
  ];
@@ -521,6 +532,7 @@ function emitSmartOblique(
521
532
  actionActuatorFunc: "stopSmartOblique",
522
533
  actionActuatorFuncParam: { payloadPositionIndex: 0 },
523
534
  },
535
+ { actionId: 1, actionActuatorFunc: "gimbalAngleUnlock" },
524
536
  ],
525
537
  },
526
538
  ];
@@ -538,6 +550,29 @@ function emitSmartOblique(
538
550
  };
539
551
  }
540
552
 
553
+ function buildSmartObliquePoints(pitchDeg: number, shootInterval: number): SmartObliquePoint[] {
554
+ const stayTime = Math.max(0.1, shootInterval);
555
+ const obliquePitch = -Math.abs(pitchDeg || 45);
556
+
557
+ return [
558
+ smartObliquePoint(-90, 0, 0),
559
+ smartObliquePoint(obliquePitch, 45, stayTime),
560
+ smartObliquePoint(obliquePitch, -45, stayTime),
561
+ smartObliquePoint(-120, 50, stayTime),
562
+ smartObliquePoint(-120, -50, stayTime),
563
+ ];
564
+ }
565
+
566
+ function smartObliquePoint(pitchDeg: number, yawDeg: number, stayTime: number): SmartObliquePoint {
567
+ return {
568
+ smartObliqueRunningTime: 0,
569
+ smartObliqueStayTime: stayTime,
570
+ smartObliqueEulerPitch: degrees(pitchDeg),
571
+ smartObliqueEulerRoll: degrees(0),
572
+ smartObliqueEulerYaw: degrees(yawDeg),
573
+ };
574
+ }
575
+
541
576
  export function planMapping2d(template: Mapping2dTemplate, options?: PlanOptions): Waylines {
542
577
  if (options?.lineSpacing == null) {
543
578
  throw new PlannerError(
@@ -556,7 +591,7 @@ export function planMapping2d(template: Mapping2dTemplate, options?: PlanOptions
556
591
  if (folder.Placemark.smartObliqueEnable === true) {
557
592
  return {
558
593
  missionConfig: template.missionConfig,
559
- Folder: [emitSmartOblique(folder, origin, enuWps, options)],
594
+ Folder: [emitSmartOblique(folder, origin, enuWps, shootInterval, options)],
560
595
  };
561
596
  }
562
597
  // 正射 and 正射+智能摆拍 share the nadir grid; the centroid detour is driven by
@@ -25,7 +25,12 @@ export function planWaypoint(template: WaypointTemplate): Waylines {
25
25
  : p.waypointTurnParam;
26
26
 
27
27
  const executeHeight =
28
- p.useGlobalHeight || p.height === undefined ? folder.globalHeight : p.height;
28
+ folder.waylineCoordinateSysParam.heightMode === "aboveGroundLevel" &&
29
+ p.ellipsoidHeight !== undefined
30
+ ? p.ellipsoidHeight
31
+ : p.useGlobalHeight || p.height === undefined
32
+ ? folder.globalHeight
33
+ : p.height;
29
34
 
30
35
  const waypointSpeed =
31
36
  p.useGlobalSpeed || p.waypointSpeed === undefined ? folder.autoFlightSpeed : p.waypointSpeed;
@@ -58,17 +63,17 @@ export function planWaypoint(template: WaypointTemplate): Waylines {
58
63
  /**
59
64
  * Map a template heightMode to a waylines executeHeightMode.
60
65
  * - `EGM96` → `WGS84` (waylines spec doesn't expose EGM96; runtime resolves it).
61
- * - `aboveGroundLevel` → `realTimeFollowSurface` (closest executable analog).
66
+ * - `aboveGroundLevel` → `WGS84` (waypoint waylines use per-point ellipsoid heights).
62
67
  */
63
68
  function heightModeToExecuteMode(
64
69
  mode: WaypointFolder["waylineCoordinateSysParam"]["heightMode"],
65
70
  ): ExecuteHeightMode {
66
71
  switch (mode) {
67
72
  case "EGM96":
73
+ case "aboveGroundLevel":
68
74
  return "WGS84";
69
75
  case "relativeToStartPoint":
70
76
  return "relativeToStartPoint";
71
- case "aboveGroundLevel":
72
77
  case "realTimeFollowSurface":
73
78
  return "realTimeFollowSurface";
74
79
  }
@@ -61,6 +61,52 @@ describe("plan", () => {
61
61
  expect(waylines.missionConfig).toBe(template.missionConfig);
62
62
  });
63
63
 
64
+ it("waypoint aboveGroundLevel uses per-point ellipsoid heights as WGS84 executeHeight", () => {
65
+ const template: WaypointTemplate = {
66
+ missionConfig,
67
+ Folder: {
68
+ templateType: "waypoint",
69
+ templateId: 0,
70
+ waylineCoordinateSysParam: { coordinateMode: "WGS84", heightMode: "aboveGroundLevel" },
71
+ autoFlightSpeed: metersPerSecond(10),
72
+ globalWaypointTurnMode: "toPointAndStopWithDiscontinuityCurvature",
73
+ gimbalPitchMode: "manual",
74
+ globalHeight: meters(50),
75
+ globalWaypointHeadingParam: {
76
+ waypointHeadingMode: "followWayline",
77
+ waypointHeadingPathMode: "followBadArc",
78
+ },
79
+ Placemark: [
80
+ {
81
+ Point: { coordinates: { lng: 113.938, lat: 22.5397 } },
82
+ index: 0,
83
+ useGlobalHeight: false,
84
+ height: meters(80),
85
+ ellipsoidHeight: meters(180),
86
+ useGlobalSpeed: true,
87
+ useGlobalHeadingParam: true,
88
+ useGlobalTurnParam: true,
89
+ },
90
+ {
91
+ Point: { coordinates: { lng: 113.939, lat: 22.5407 } },
92
+ index: 1,
93
+ useGlobalHeight: false,
94
+ height: meters(90),
95
+ ellipsoidHeight: meters(190),
96
+ useGlobalSpeed: true,
97
+ useGlobalHeadingParam: true,
98
+ useGlobalTurnParam: true,
99
+ },
100
+ ],
101
+ },
102
+ };
103
+
104
+ const waylines = plan(template);
105
+
106
+ expect(waylines.Folder[0]!.executeHeightMode).toBe("WGS84");
107
+ expect(waylines.Folder[0]!.Placemark.map((p) => Number(p.executeHeight))).toEqual([180, 190]);
108
+ });
109
+
64
110
  it("compiles a mapping2d template into a serpentine scan", () => {
65
111
  const template: Mapping2dTemplate = {
66
112
  missionConfig,
@@ -324,8 +370,26 @@ describe("plan", () => {
324
370
  const funcs = wl.Folder[0]!.Placemark.flatMap((p) =>
325
371
  (p.actionGroup ?? []).flatMap((g) => g.action.map((a) => a.actionActuatorFunc)),
326
372
  );
373
+ expect(funcs.filter((fn) => fn === "startSmartOblique")).toHaveLength(1);
374
+ expect(funcs.filter((fn) => fn === "stopSmartOblique")).toHaveLength(1);
375
+ expect(funcs).toContain("gimbalAngleLock");
327
376
  expect(funcs).toContain("startSmartOblique");
328
377
  expect(funcs).toContain("stopSmartOblique");
378
+ expect(funcs).toContain("gimbalAngleUnlock");
379
+
380
+ const start = wl.Folder[0]!.Placemark.flatMap((p) => p.actionGroup ?? [])
381
+ .flatMap((g) => g.action)
382
+ .find((a) => a.actionActuatorFunc === "startSmartOblique");
383
+ expect(start?.actionActuatorFunc).toBe("startSmartOblique");
384
+ if (start?.actionActuatorFunc === "startSmartOblique") {
385
+ expect(start.actionActuatorFuncParam.smartObliqueCycleMode).toBe("unlimited");
386
+ expect(start.actionActuatorFuncParam.smartObliquePoint).toHaveLength(5);
387
+ expect(
388
+ start.actionActuatorFuncParam.smartObliquePoint?.map((p) =>
389
+ Number(p.smartObliqueEulerPitch),
390
+ ),
391
+ ).toEqual([-90, -45, -45, -120, -120]);
392
+ }
329
393
 
330
394
  // The camera swings omnidirectionally, so the polygon is dilated by the
331
395
  // stand-off and the grid overflies the boundary on EVERY side. Polygon spans
@@ -253,8 +253,21 @@ export interface MegaphoneParam {
253
253
  export type MegaphoneAction = ActionBase<"megaphone", MegaphoneParam>;
254
254
 
255
255
  /** M4D/M4TD only — starts gimbal swing in smart-oblique mode. */
256
+ export interface SmartObliquePoint {
257
+ /** seconds before this pose starts; DJI exports currently use 0. */
258
+ smartObliqueRunningTime: number;
259
+ /** seconds to hold this pose for smart-oblique shooting. */
260
+ smartObliqueStayTime: number;
261
+ smartObliqueEulerPitch: Degrees;
262
+ smartObliqueEulerRoll: Degrees;
263
+ smartObliqueEulerYaw: Degrees;
264
+ }
265
+
256
266
  export interface StartSmartObliqueParam {
267
+ /** Observed in DJI exports; older generated files may omit it. */
268
+ smartObliqueCycleMode?: "unlimited";
257
269
  payloadPositionIndex: number;
270
+ smartObliquePoint?: SmartObliquePoint[];
258
271
  }
259
272
  export type StartSmartObliqueAction = ActionBase<"startSmartOblique", StartSmartObliqueParam>;
260
273