What is a DJI WPML file?
WPML (WayPoint Markup Language) is DJI’s extension of KML for waypoint routes. A route file is a ZIP archive with a .kmz suffix. Inside a wpmz/ folder are template.kml (the “template file”: business parameters a planner can edit) and waylines.wpml (the “execution file”: explicit flight and payload instructions), plus an optional res/ folder for resources. DJI Pilot 2 and FlightHub 2 generate waylines.wpml from the template’s parameters.
Elements in both files use the namespace xmlns:wpml="http://www.dji.com/wpmz/1.0.2" in DJI’s examples. The route’s name is the .kmz file name, and DJI warns that files and folders must follow the documented names or the route may fail to load.
Elements OpenDroneKit writes
| Element | Per DJI's spec | What OpenDroneKit writes |
|---|---|---|
wpml:missionConfig | Mission-wide behaviour: fly-to-route mode, finish action, RC-lost action, take-off security height, transit speed | safely · goHome (autoLand when the plan lands) · executeLostAction / goBack · RTH altitude capped at 100 m · cruise speed within 1 to 15 m/s |
wpml:droneInfo | droneEnumValue: 77 = M3E/M3T/M3M (sub 0/1/2), 67 = M30/M30T, 60 = M300 RTK, 89 = M350 RTK, 91 = M3D/M3TD | 77 / 0 (Mavic 3 Enterprise). Public main writes 68, which is not an assigned value |
wpml:payloadInfo | payloadEnumValue: 66 = Mavic 3E camera, 67 = 3T, 68 = 3M | 66, position 0 |
wpml:waylineCoordinateSysParam | coordinateMode and heightMode (template): EGM96, relativeToStartPoint, aboveGroundLevel, realTimeFollowSurface | WGS84 · relativeToStartPoint, or EGM96 for plans with absolute (AMSL) altitudes |
wpml:executeHeightMode | Required in waylines.wpml: WGS84, relativeToStartPoint or realTimeFollowSurface | Not written (known issue) |
Placemark / wpml:index / wpml:executeHeight | One per waypoint: lon,lat point, 0-based index, height in the declared mode | Every planned viewpoint |
wpml:waypointHeadingParam | Heading mode, angle, POI, path mode (followBadArc = shortest yaw) | smoothTransition when the heading is locked, else followWayline |
wpml:waypointTurnParam | coordinateTurn, toPointAndStop…, toPointAndPassWithContinuityCurvature | Stop at hold points, pass elsewhere; damping 0.2 m |
wpml:actionGroup | Actions on a trigger: reachPoint, betweenAdjacentPoints, multipleTiming, multipleDistance | Per waypoint, reachPoint: gimbalRotate (absolute pitch), hover at holds, takePhoto at captures |
wpml:waylineActionGroup | Route-level action group | Only for distance-triggered capture (multipleDistance) |
Example file
Written by OpenDroneKit’s exporter for a 90 x 60 m rectangle centred on 41.30420 N, 81.75200 W (Aukerman Park, Ohio): grid template, 55 m above take-off, 8 m/s, 27 waypoints. The first lines:
wpmz/template.kml
<?xml version="1.0" ?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.2">
<Document>
<wpml:author>OpenDroneKit</wpml:author>
<wpml:createTime>0</wpml:createTime>
<wpml:updateTime>0</wpml:updateTime>
<wpml:missionConfig>
<wpml:flyToWaylineMode>safely</wpml:flyToWaylineMode>
<wpml:finishAction>goHome</wpml:finishAction>
<wpml:exitOnRCLost>executeLostAction</wpml:exitOnRCLost>
<wpml:executeRCLostAction>goBack</wpml:executeRCLostAction>
<wpml:takeOffSecurityHeight>80.0</wpml:takeOffSecurityHeight>
<wpml:globalTransitionalSpeed>8.0</wpml:globalTransitionalSpeed>
<wpml:droneInfo>
<wpml:droneEnumValue>77</wpml:droneEnumValue>
<wpml:droneSubEnumValue>0</wpml:droneSubEnumValue>
</wpml:droneInfo>
<wpml:payloadInfo>
<wpml:payloadEnumValue>66</wpml:payloadEnumValue>
<wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
</wpml:payloadInfo>
</wpml:missionConfig>
<Folder>
<wpml:templateType>waypoint</wpml:templateType>
<wpml:templateId>0</wpml:templateId>
<wpml:waylineCoordinateSysParam>
<wpml:coordinateMode>WGS84</wpml:coordinateMode>
<wpml:heightMode>relativeToStartPoint</wpml:heightMode>
</wpml:waylineCoordinateSysParam>
<wpml:autoFlightSpeed>8.0</wpml:autoFlightSpeed>
<Placemark>
<Point>
<coordinates>-81.75253872,41.30393020</coordinates>
</Point>
<wpml:index>0</wpml:index>
<wpml:executeHeight>55.00</wpml:executeHeight>
<wpml:waypointSpeed>8.0</wpml:waypointSpeed>
<wpml:waypointHeadingParam>
<wpml:waypointHeadingMode>followWayline</wpml:waypointHeadingMode>
<wpml:waypointHeadingAngle>90.0</wpml:waypointHeadingAngle>wpmz/waylines.wpml
<?xml version="1.0" ?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.2">
<Document>
<wpml:missionConfig>
<wpml:flyToWaylineMode>safely</wpml:flyToWaylineMode>
<wpml:finishAction>goHome</wpml:finishAction>
<wpml:exitOnRCLost>executeLostAction</wpml:exitOnRCLost>
<wpml:executeRCLostAction>goBack</wpml:executeRCLostAction>
<wpml:takeOffSecurityHeight>80.0</wpml:takeOffSecurityHeight>
<wpml:globalTransitionalSpeed>8.0</wpml:globalTransitionalSpeed>
<wpml:droneInfo>
<wpml:droneEnumValue>77</wpml:droneEnumValue>
<wpml:droneSubEnumValue>0</wpml:droneSubEnumValue>
</wpml:droneInfo>
<wpml:payloadInfo>
<wpml:payloadEnumValue>66</wpml:payloadEnumValue>
<wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
</wpml:payloadInfo>
</wpml:missionConfig>
<Folder>
<wpml:templateType>waypoint</wpml:templateType>
<wpml:templateId>0</wpml:templateId>
<wpml:waylineId>0</wpml:waylineId>
<wpml:waylineCoordinateSysParam>
<wpml:coordinateMode>WGS84</wpml:coordinateMode>
<wpml:heightMode>relativeToStartPoint</wpml:heightMode>
</wpml:waylineCoordinateSysParam>
<wpml:autoFlightSpeed>8.0</wpml:autoFlightSpeed>
<Placemark>
<Point>
<coordinates>-81.75253872,41.30393020</coordinates>
</Point>
<wpml:index>0</wpml:index>
<wpml:executeHeight>55.00</wpml:executeHeight>
<wpml:waypointSpeed>8.0</wpml:waypointSpeed>
<wpml:waypointHeadingParam>
<wpml:waypointHeadingMode>followWayline</wpml:waypointHeadingMode>
<wpml:waypointHeadingAngle>90.0</wpml:waypointHeadingAngle>
<wpml:waypointPoiPoint>0.000000,0.000000,0.000000</wpml:waypointPoiPoint>
<wpml:waypointHeadingPathMode>followBadArc</wpml:waypointHeadingPathMode>
</wpml:waypointHeadingParam>
<wpml:waypointTurnParam>
<wpml:waypointTurnMode>toPointAndPassWithContinuityCurvature</wpml:waypointTurnMode>
<wpml:waypointTurnDampingDist>0.2</wpml:waypointTurnDampingDist>
</wpml:waypointTurnParam>
<wpml:useStraightLine>1</wpml:useStraightLine>
<wpml:actionGroup>
<wpml:actionGroupId>0</wpml:actionGroupId>
<wpml:actionGroupStartIndex>0</wpml:actionGroupStartIndex>
<wpml:actionGroupEndIndex>0</wpml:actionGroupEndIndex>
<wpml:actionGroupMode>sequence</wpml:actionGroupMode>
<wpml:actionTrigger>
<wpml:actionTriggerType>reachPoint</wpml:actionTriggerType>
</wpml:actionTrigger>
<wpml:action>
<wpml:actionId>0</wpml:actionId>
<wpml:actionActuatorFunc>gimbalRotate</wpml:actionActuatorFunc>
<wpml:actionActuatorFuncParam>
<wpml:gimbalHeadingYawBase>aircraft</wpml:gimbalHeadingYawBase>
<wpml:gimbalRotateMode>absoluteAngle</wpml:gimbalRotateMode>The full Aukerman Park files in the table above come from the run shown on the homepage: the public OpenDroneMap survey, 152 waypoints at 55 m, generated 2026-09-29. Every file’s SHA-256 is listed so a copy can be checked.
What OpenDroneKit writes
OpenDroneKit (export_dji_wpml) writes both files with the same waypoints. template.kml adds author and time fields, waylines.wpml adds the wayline id. Every waypoint gets an action group, because the gimbal angle is always commanded; hover and photo actions are added where the plan holds or captures.
The default aircraft is a Mavic 3 Enterprise (drone 77, sub-enum 0, payload 66). For another aircraft the enums must change: Pilot 2 checks them against the connected drone.
Known issues
Checked against the format’s reference and the exporter code on 2026-10-02. Listed here rather than hidden, so you can correct a file before you fly it.
wpml:executeHeightModeis missing fromwaylines.wpml. DJI’s spec marks it required. The exporter declares the height reference only throughwaylineCoordinateSysParam/heightMode. Present on both branches. Pilot 2 regenerates waylines from the template, which may mask it; it has not been tested on an aircraft here.- Not flight-tested. The files parse as XML and contain both members, but no exported .kmz has been flown on a DJI aircraft as part of this project’s tests.
Public main versus the development branch
The example files were written by the development branch (0ae10e4), which is ahead of the public main you get from a source install (1ef6351). On main:
- Public main defaults droneEnumValue to 68, which is not an assigned DJI drone enum; the integration branch defaults to 77 (Mavic 3 Enterprise), sub-enum 0 on both.
- Public main does not cap waypoint speed at 15 m/s and takes the speed from a key the planner does not set (falling back to 8 m/s); it always uses heightMode relativeToStartPoint, where the integration branch uses EGM96 for AMSL plans.
How do I import it into DJI Pilot 2?
- Copy the .kmz to the controller (USB, SD card or cloud drive) running DJI Pilot 2.
- In Pilot 2’s route library, use the import button and select the .kmz file.
- Open the route, confirm the aircraft and camera match, review heights and actions, and save.
- DJI Fly has no import button. Third-party tools replace the .kmz of a mission created on the aircraft (renamed to its GUID) with the new file; DJI does not document that route.
Pitfalls
- A droneEnumValue or payloadEnumValue that does not match the connected aircraft makes Pilot 2 reject or alter the route.
- relativeToStartPoint heights are measured from where the aircraft takes off. Take off somewhere else and the whole route shifts vertically.
- realTimeFollowSurface is supported only on some aircraft (DJI lists the M3E/M3T/M3M family for waylines).
- takeOffSecurityHeight (1.2 to 1500 m with a remote controller) is the climb before the route starts; set it above nearby obstacles.
References
- DJI WPML overview (Cloud API docs)
- DJI WPML: template.kml
- DJI WPML: waylines.wpml
- DJI WPML: common elements (enums)
- Same pages on GitHub (dji-sdk/Cloud-API-Doc)
- OpenDroneKit exporter source (mission/exporters.py at 1ef6351)
Other formats: QGC .plan · .waypoints · Litchi CSV. Missions come from the mission templates; see mission planning.