@lumikmz/kmz 0.6.2 → 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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumikmz/kmz",
3
- "version": "0.6.2",
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
@@ -370,8 +370,26 @@ describe("plan", () => {
370
370
  const funcs = wl.Folder[0]!.Placemark.flatMap((p) =>
371
371
  (p.actionGroup ?? []).flatMap((g) => g.action.map((a) => a.actionActuatorFunc)),
372
372
  );
373
+ expect(funcs.filter((fn) => fn === "startSmartOblique")).toHaveLength(1);
374
+ expect(funcs.filter((fn) => fn === "stopSmartOblique")).toHaveLength(1);
375
+ expect(funcs).toContain("gimbalAngleLock");
373
376
  expect(funcs).toContain("startSmartOblique");
374
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
+ }
375
393
 
376
394
  // The camera swings omnidirectionally, so the polygon is dilated by the
377
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