Android使用dom4j-创建大疆MSDK-V5 航点飞行kml和wpml文件

概要

前期使用MSDK-V4实现了航点飞行功能,最近适配了MSDK-V5的航点飞行功能,和V4不同的是V5执行任务需要KMZ文件,包含kml和wpml,本文介绍如何生成kml和wpml。

技术细节

1.首先下载dom4j并导入项目

2.根据功能创建任务信息类

3.根据任务信息类创建文件

任务信息类主要包含下面信息:

private String id;

    private Integer missionId;


    private String title;

    private Double height;

    private Double speed;

    private Date createTime;

    private String createUser;

    private String path;

    private Date updateTime;

    private String updateUser;

    private String address;

    private String screenShotPath;

    private Date startTime;

    private Date endTime;

    /**
     * 飞行状态(0:未飞行,1:飞行完成)
     */
    private Integer status;

    /**
     * 0:沿航线防线  1:手动控制  2:依照每个航点设置
     */
    private Integer headingMode;

    /**
     * 0:悬停  1:自动返航  2:原地降落 3:返回航线起始点
     */
    private Integer finishedAction;

    /**
     * 暂停点的索引(从1开始,索引前的点表示已经执行)
     */
    private Integer stopMissionIndex;

    /**
     * 云台仰角[0-90]
     */
    private Integer gimbalPitch;

    /**
     * 暂停点的位置
     */
    private String stopMissionLocation;

    /**
     * 最后一次中断的时间(用来过滤多媒体文件)
     */
    private Date lastInterruptTime;

    private String deviceId;

    /**
     * 本地选择使用
     */
    private boolean selectState;
    /**
     * 失控是否继续执行航线
     */
    private boolean exitOnRCLost;
    /**
     * 失控动作类型
     * 0:悬停", 1:原地降落, 2:自动返航
     */
    private Integer executeRCLostAction;
    /**
     * 安全起飞高度
     */
    private Float takeOffSecurityHeight;

其中path属性存放的是点位及动作:

    private double lng;
    private double lat;
    private double speed;
    // 录像:0,
    // 拍照:1,
    // 悬停:2
    // 停止录像:3
    private String action;

    //执行时间
    private Date executeTime;

下面是创建KML的DomParserKML 类:

package woyi.com.djilive.utils;

import android.util.Log;

import com.alibaba.fastjson.JSON;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.XMLWriter;

import java.io.File;
import java.io.FileWriter;
import java.util.List;

import woyi.com.djilive.MyApplication;
import woyi.com.djilive.bean.MissionPoint;
import woyi.com.djilive.bean.MyFlightMission;


public class DomParserKML {
    private static final String TAG = "DomParserKML";
    private String kmlFilePath = "";
    private String kmlFileName = "";

    private Element docElement;

    private List<MissionPoint> pointList;
    private MyFlightMission flightMission;


    /**
     * 构造
     *
     * @param filePath
     * @param fileName
     */
    public DomParserKML(String filePath, String fileName) {
        this.kmlFilePath = filePath;
        this.kmlFileName = fileName;
    }

    /**
     * 创建kml文件
     */
    public void createKml(MyFlightMission mission) {
        flightMission = mission;
        pointList = JSON.parseArray(flightMission.getPathCopy(), MissionPoint.class);
        String realPath = createFileDir();

        String fileName = realPath + kmlFileName;

        Document document = DocumentHelper.createDocument();// 建立document对象,用来操作xml文件
        Element kmlElement = document.addElement("kml", "http://www.opengis.net/kml/2.2");// 建立根节点
        kmlElement.addAttribute("xmlns:wpml", "http://www.dji.com/wpmz/1.0.2");
        kmlElement.addNamespace("wpml", "http://www.dji.com/wpmz/1.0.2");

        docElement = kmlElement.addElement("Document");// 添加一个Document节点

        createBaseInfo();
        createMissionConfig();
        createFolder();
        try {
            XMLWriter writer = new XMLWriter(new FileWriter(fileName));
            writer.write(document); //写入
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建打包需要的文件目录
     */
    private String createFileDir() {
        String missionFilePath = kmlFilePath + "/" + flightMission.getMissionId();
        File missionFile = new File(missionFilePath);
        if (!missionFile.exists()) {
            missionFile.mkdir();
        }
        String wpmzPath = kmlFilePath + "/" + flightMission.getMissionId() + "/wpmz/";
        File dir = new File(wpmzPath);
        if (!dir.exists()) {
            dir.mkdir();
        }
        return wpmzPath;
    }

    /**
     * 创建基本信息节点
     *
     * @return 0:添加节点成功  -1:添加节点失败
     */
    private int createBaseInfo() {
        if (docElement != null) {
            docElement.addElement("wpml:author").setText(flightMission.getCreateUser() != null ?
                    flightMission.getCreateUser() : "admin");

            docElement.addElement("wpml:createTime").setText(flightMission.getCreateTime() != null ?
                    String.valueOf(flightMission.getCreateTime().getTime()) : "0");

            docElement.addElement("wpml:updateTime").setText(flightMission.getUpdateTime() != null ?
                    String.valueOf(flightMission.getUpdateTime().getTime()) : "0");
            return 0;
        } else {
            return -1;
        }
    }

    /**
     * 创建任务信息
     *
     * @return
     */
    private int createMissionConfig() {
        if (docElement != null) {
            try {
                Element missionConfigElement = docElement.addElement("wpml:missionConfig");
                //飞向起始点模式
                missionConfigElement.addElement("wpml:flyToWaylineMode").setText("safely");

                //航线结束动作
                missionConfigElement.addElement("wpml:finishAction").setText(getFinishAction());

                //失控是否继续执行航线
                missionConfigElement.addElement("wpml:exitOnRCLost").setText(flightMission.isExitOnRCLost() ?
                        "goContinue" : "executeLostAction");

                if (!flightMission.isExitOnRCLost()) {
                    //失控执行失控动作时下面元素是必须的的
                    missionConfigElement.addElement("wpml:executeRCLostAction").setText(getExecuteRCLostAction());
                }

                //安全起飞高度
                if (flightMission.getTakeOffSecurityHeight() != null) {
                    missionConfigElement.addElement("wpml:takeOffSecurityHeight").setText(String.valueOf(flightMission.getTakeOffSecurityHeight()));
                } else {
                    missionConfigElement.addElement("wpml:takeOffSecurityHeight").setText("100");
                }

                //全局航线过渡速度(飞向航线起点的速度)
                missionConfigElement.addElement("wpml:globalTransitionalSpeed").setText(String.valueOf(flightMission.getSpeed()));

                Element droneInfoElement = missionConfigElement.addElement("wpml:droneInfo");
                droneInfoElement.addElement("wpml:droneEnumValue").setText(getDroneEnumValue(MyApplication.getInstance().getProductType()));
                droneInfoElement.addElement("wpml:droneSubEnumValue").setText(getDroneSubEnumValue(MyApplication.getInstance().getProductType(),
                        MyApplication.getInstance().getCameraType()));

                Element payloadInfoElement = missionConfigElement.addElement("wpml:payloadInfo");
                payloadInfoElement.addElement("wpml:payloadEnumValue").setText(getPayloadEnumValue(MyApplication.getInstance().getCameraType()));
                payloadInfoElement.addElement("wpml:payloadSubEnumValue").setText("0");
                payloadInfoElement.addElement("wpml:payloadPositionIndex").setText("0");
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
                return -1;
            }
            return 0;
        } else {
            return -1;
        }

    }


    /**
     * 获取飞行器主类型
     *
     * @param productType
     * @return
     */
    private String getDroneEnumValue(String productType) {
        /**
         DJI_MAVIC_3_ENTERPRISE_SERIES	M3行业版系列。
         M30_SERIES	M30系列。
         M300_RTK	M300 RTK。
         M350_RTK	M350 RTK。
         DJI_MINI_3	DJI Mini 3。 ---不支持航线飞行
         DJI_MINI_3_PRO	DJI Mini 3 Pro。---不支持航线飞行
         **/
        String droneEnumValue = "66";
        if (productType.equals("DJI_MAVIC_3_ENTERPRISE_SERIES")) {
            droneEnumValue = "77";
        } else if (productType.equals("M30_SERIES")) {
            droneEnumValue = "67";
        } else if (productType.equals("M300_RTK")) {
            droneEnumValue = "60";
        } else if (productType.equals("M350_RTK")) {
            droneEnumValue = "89";
        }
        return droneEnumValue;
    }

    /**
     * 飞行器机型子类型
     *
     * @param productType
     * @param cameraType
     * @return
     */
    private String getDroneSubEnumValue(String productType, String cameraType) {
        String droneEnumValue = getDroneEnumValue(productType);
        if (droneEnumValue.equals("67")) {
            if (cameraType.equals("M30")) {
                return "0";
            } else if (cameraType.equals("M30T")) {
                return "1";
            } else {
                return "0";
            }
        } else if (droneEnumValue.equals("77")) {
            if (cameraType.equals("M3E")) {
                return "0";
            } else if (cameraType.equals("M3T")) {
                return "1";
            } else {
                //M3M
                return "2";
            }
        } else {
            return "0";
        }
    }

    /**
     * 负载机型主类型
     *
     * @param cameraType
     * @return
     */
    private String getPayloadEnumValue(String cameraType) {
        /**
         M3E	M3E行业版双光相机。
         M3T	M3T行业版三光相机。
         M3M	M3M多光谱相机。
         M30	M30双光相机。
         M30T	M30T三光相机。
         ZENMUSE_H20	Zenmuse H20双光相机。
         ZENMUSE_H20T	Zenmuse H20T三光相机。
         ZENMUSE_P1	Zenmuse P1相机。
         ZENMUSE_L1	Zenmuse L1相机。
         ZENMUSE_L2	Zenmuse L2相机。
         **/
        String type = "";
        if (cameraType.equals("M3E")) {
            type = "66";
        } else if (cameraType.equals("M3T")) {
            type = "67";
        } else if (cameraType.equals("M3M")) {
            type = "68";
        } else if (cameraType.equals("M30")) {
            type = "52";
        } else if (cameraType.equals("M30T")) {
            type = "53";
        } else if (cameraType.equals("ZENMUSE_H20")) {
            type = "42";
        } else if (cameraType.equals("ZENMUSE_H20T")) {
            type = "43";
        } else if (cameraType.equals("ZENMUSE_P1")) {
            type = "65534";
        } else if (cameraType.equals("ZENMUSE_L1")) {
            type = "65534";
        } else if (cameraType.equals("ZENMUSE_L2")) {
            type = "65534";
        }
        return type;
    }

    /**
     * 获取航线结束动作
     *
     * @return
     */
    private String getFinishAction() {
        if (flightMission.getFinishedAction() != null) {
            if (flightMission.getFinishedAction() == 0) {
                return "noAction";//悬停
            } else if (flightMission.getFinishedAction() == 1) {
                return "goHome";//返航
            } else if (flightMission.getFinishedAction() == 2) {
                return "autoLand";//原地降落
            } else if (flightMission.getFinishedAction() == 3) {
                return "gotoFirstWaypoint";//返回航线起始点
            } else {
                return "goHome";//返航
            }
        } else {
            return "goHome";//返航
        }
    }

    /**
     * 获取失控动作类型
     *
     * @return
     */
    private String getExecuteRCLostAction() {
        if (flightMission.getExecuteRCLostAction() != null) {
            if (flightMission.getExecuteRCLostAction() == 0) {
                //悬停
                return "hover";
            } else if (flightMission.getExecuteRCLostAction() == 1) {
                //原地降落
                return "landing";
            } else if (flightMission.getExecuteRCLostAction() == 1) {
                //返航
                return "goBack";
            } else {
                //返航
                return "goBack";
            }
        } else {
            //返航
            return "goBack";
        }
    }

    /**
     * 创建模板信息
     *
     * @return
     */
    public int createFolder() {
        if (docElement != null) {
            Element folderElement = docElement.addElement("Folder");
            //预定义模板类型
            // waypoint:航点飞行
            //mapping2d:建图航拍
            //mapping3d:倾斜摄影
            //mappingStrip:航带飞行
            folderElement.addElement("wpml:templateType").setText("waypoint");

            //模板ID,范围:[0, 65535]
            folderElement.addElement("wpml:templateId").setText(String.valueOf(flightMission.getMissionId()));

            //全局航线飞行速度
            folderElement.addElement("wpml:autoFlightSpeed").setText(String.valueOf(flightMission.getSpeed()));

            //全局航点类型(全局航点转弯模式)
            //coordinateTurn:协调转弯,不过点,提前转弯
            //toPointAndStopWithDiscontinuityCurvature:直线飞行,飞行器到点停
            //toPointAndStopWithContinuityCurvature:曲线飞行,飞行器到点停
            //toPointAndPassWithContinuityCurvature:曲线飞行,飞行器过点不停
            folderElement.addElement("wpml:globalWaypointTurnMode").setText("toPointAndStopWithDiscontinuityCurvature");

            //全局航段轨迹是否尽量贴合直线
            //0:航段轨迹全程为曲线
            //1:航段轨迹尽量贴合两点连线
            //当且仅当“wpml:globalWaypointTurnMode”被设置为“toPointAndStopWithContinuityCurvature
            // ”或“toPointAndPassWithContinuityCurvature”时必需。如果额外定义了某航点的该元素,则局部定义会覆盖全局定义。
            folderElement.addElement("wpml:globalUseStraightLine").setText("1");

            //云台俯仰角控制模式
            //manual:手动控制。飞行器从一个航点飞向下一个航点的过程中,支持用户手动控制云台的俯仰角度。若无用户控制,则保持飞离航点时的云台俯仰角度。
            //usePointSetting:依照每个航点设置。飞行器从一个航点飞向下一个航点的过程中,云台俯仰角均匀过渡至下一个航点的俯仰角。
            folderElement.addElement("wpml:gimbalPitchMode").setText("usePointSetting");

            //全局航线高度(相对起飞点高度)
            folderElement.addElement("wpml:globalHeight").setText(String.valueOf(flightMission.getHeight()));

            //全局偏航角模式参数
            Element globalWaypointHeadingParamElement = folderElement.addElement("wpml:globalWaypointHeadingParam");
            //云台模式
            // followWayline:沿航线方向。飞行器机头沿着航线方向飞至下一航点
            //manually:手动控制。飞行器在飞至下一航点的过程中,用户可以手动控制飞行器机头朝向
            //fixed:锁定当前偏航角。飞行器机头保持执行完航点动作后的飞行器偏航角飞至下一航点
            //smoothTransition:自定义。通过“wpml:waypointHeadingAngle”给定某航点的目标偏航角,并在航段飞行过程中均匀过渡至下一航点的目标偏航角。
            //towardPOI:朝向兴趣点
            globalWaypointHeadingParamElement.addElement("wpml:waypointHeadingMode").setText(getHeadMode(flightMission.getHeadingMode()));
            //飞行器偏航角度
            globalWaypointHeadingParamElement.addElement("wpml:waypointHeadingAngle").setText("0");
            //飞行器偏航角转动方向
            // clockwise:顺时针旋转飞行器偏航角
            //counterClockwise:逆时针旋转飞行器偏航角
            //followBadArc:沿最短路径旋转飞行器偏航角
            globalWaypointHeadingParamElement.addElement("wpml:waypointHeadingPathMode").setText("followBadArc");


            if (pointList != null && pointList.size() > 0) {
                //航点信息(包括航点经纬度和高度等)
                for (int i = 0; i < pointList.size(); i++) {
                    addPointToPlacemark(folderElement, pointList.get(i), pointList.size(), i);
                }
            }

            return 0;
        } else {
            return -1;
        }
    }

    /**
     * 向Placemark添加航点信息
     *
     * @param parentElement
     */
    private boolean addPointToPlacemark(Element parentElement, MissionPoint missionPoint, int size, int index) {
        if (missionPoint == null || missionPoint.getLat() == 0 || missionPoint.getLng() == 0) {
            return false;
        } else {
            Element placemarkElement = parentElement.addElement("Placemark");
            Element point = placemarkElement.addElement("Point");

            //航点经纬度<经度,纬度>
            point.addElement("coordinates")
                    .setText(missionPoint.getLng() + "," + missionPoint.getLat());

            //航点序号
            placemarkElement.addElement("wpml:index")
                    .setText(String.valueOf(index));

            //是否使用全局高度
            placemarkElement.addElement("wpml:useGlobalHeight")
                    .setText("1");

            //航点高度(WGS84椭球高度)useGlobalHeight 为0是必需
            placemarkElement.addElement("wpml:ellipsoidHeight")
                    .setText(String.valueOf(flightMission.getHeight()));

            //航点高度(EGM96海拔高度/相对起飞点高度/AGL相对地面高度)useGlobalHeight 为0是必需
            placemarkElement.addElement("wpml:height")
                    .setText(String.valueOf(flightMission.getHeight()));

            //是否使用全局速度
            placemarkElement.addElement("wpml:useGlobalSpeed")
                    .setText(missionPoint.getSpeed() > 0 ? "0" : "1");

            //航点飞行速度 当且仅当“wpml:useGlobalSpeed”为“0”时必需
            if (missionPoint.getSpeed() > 0) {
                placemarkElement.addElement("wpml:waypointSpeed")
                        .setText(String.valueOf(missionPoint.getSpeed()));
            }

            //是否使用全局偏航角模式参数
            placemarkElement.addElement("wpml:useGlobalHeadingParam")
                    .setText("1");
            //当且仅当“wpml:useGlobalHeadingParam”为“0”时必需
//            parentElement.addElement("wpml:waypointHeadingParam")
//                    .setText("0");

            //是否使用全局航点类型(全局航点转弯模式)
            placemarkElement.addElement("wpml:useGlobalTurnParam")
                    .setText("1");
            //当且仅当“wpml:useGlobalTurnParam”为“0”时必需
//            parentElement.addElement("wpml:waypointTurnParam")
//                    .setText("toPointAndStopWithDiscontinuityCurvature");

            //是该航段是否贴合直线
            //0:航段轨迹全程为曲线
            //1:航段轨迹尽量贴合两点连线
            //当且仅当“wpml:waypointTurnParam”内"waypointTurnMode"被设置为“toPointAndStopWithContinuityCurvature
            // ”或“toPointAndPassWithContinuityCurvature”时必需。如果此元素被设置,则局部定义会覆盖全局定义。
//            parentElement.addElement("wpml:useStraightLine")
//                    .setText("1");

            //航点云台俯仰角
            //当且仅当“wpml:gimbalPitchMode”为“usePointSetting”时必需。
            placemarkElement.addElement("wpml:gimbalPitchAngle")
                    .setText(String.valueOf(flightMission.getGimbalPitch()));

            //航线初始动作
            //*注:该元素用于规划一系列初始动作,在航线开始前执行。航线中断恢复时,先执行初始动作,再执行航点动作
            Element actionGroupElement = placemarkElement.addElement("wpml:actionGroup");
            //动作组id
            //* 注:在一个kmz文件内该ID唯一。建议从0开始单调连续递增。[0, 65535]
            actionGroupElement.addElement("wpml:actionGroupId").setText(String.valueOf(index));
            //动作组开始生效的航点 [0, 65535]
            actionGroupElement.addElement("wpml:actionGroupStartIndex").setText(String.valueOf(index));
            //动作组结束生效的航点
            //* 注:该元素必须大于等于“actionGroupStartIndex” [0, 65535]
            //* 注:当“动作组结束生效的航点”与“动作组开始生效的航点”一致,则代表该动作组仅在该航点处生效
            actionGroupElement.addElement("wpml:actionGroupEndIndex").setText(String.valueOf(index));
            //动作执行模式 sequence:串行执行。即动作组内的动作依次按顺序执行。
            actionGroupElement.addElement("wpml:actionGroupMode").setText("sequence");
            //动作组触发器
            Element actionTriggerElement = actionGroupElement.addElement("wpml:actionTrigger");
            //reachPoint:到达航点时执行
            //betweenAdjacentPoints:航段触发,均匀转云台
            //multipleTiming:等时触发
            //multipleDistance:等距触发
            //* 注:“betweenAdjacentPoints”需配合动作"gimbalEvenlyRotate"使用
            actionTriggerElement.addElement("wpml:actionTriggerType").setText("reachPoint");
            //动作列表,actiongroup中的第一个动作
            Element firstActionElement = actionGroupElement.addElement("wpml:action");
            //动作id [0, 65535]
            //* 注:在一个动作组内该ID唯一。建议从0开始单调连续递增。
            firstActionElement.addElement("wpml:actionId").setText("0");
            //动作类型
            //	takePhoto:单拍
            //startRecord:开始录像
            //stopRecord:结束录像
            //focus:对焦
            //zoom:变焦
            //customDirName:创建新文件夹
            //gimbalRotate:旋转云台
            //rotateYaw:飞行器偏航
            //hover:悬停等待
            //gimbalEvenlyRotate:航段间均匀转动云台pitch角
            //accurateShoot:精准复拍动作(已暂停维护,建议使用orientedShoot)
            //orientedShoot:精准复拍动作
            String firstAction = getActionActuatorFunc(missionPoint.getAction());
            firstActionElement.addElement("wpml:actionActuatorFunc").setText(firstAction);

            Element firstActionActuatorFuncParamElement = firstActionElement.addElement("wpml:actionActuatorFuncParam");
            if (firstAction.equals("takePhoto")) {
                //负载挂载位置
                //0:飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。
                //1:飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。
                //2:飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。
                firstActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");
                //拍摄照片文件后缀
                firstActionActuatorFuncParamElement.addElement("wpml:fileSuffix").setText("point" + index);
            } else if (firstAction.equals("startRecord")) {
                firstActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");
                //拍摄照片文件后缀
                firstActionActuatorFuncParamElement.addElement("wpml:fileSuffix").setText("point" + index);
                //拍摄照片存储类型
                // zoom: 存储变焦镜头拍摄照片
                //wide: 存储广角镜头拍摄照片
                //ir: 存储红外镜头拍摄照片
                //narrow_band: 存储窄带镜头拍摄照片
                //注:存储多个镜头照片,格式如“<wpml:payloadLensIndex>wide,ir,narrow_band</wpml:payloadLensIndex>”表示同时使用广角、红外和窄带镜头
                firstActionActuatorFuncParamElement.addElement("wpml:payloadLensIndex").setText("wide,ir,narrow_band");
                //是否使用全局存储类型
                //0:不使用全局设置
                //1:使用全局设置
                firstActionActuatorFuncParamElement.addElement("wpml:useGlobalPayloadLensIndex").setText("0");
            } else if (firstAction.equals("stopRecord")) {
                firstActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");
                firstActionActuatorFuncParamElement.addElement("wpml:payloadLensIndex").setText("wide,ir,narrow_band");
            } else if (firstAction.equals("hover")) {
                //飞行器悬停等待时间 单位s
                firstActionActuatorFuncParamElement.addElement("wpml:hoverTime").setText("5");
            } else if (firstAction.equals("rotateYaw")) {
                firstActionActuatorFuncParamElement.addElement("wpml:aircraftHeading").setText("180");
                firstActionActuatorFuncParamElement.addElement("wpml:aircraftPathMode").setText("clockwise");
            }

            //动作列表,actiongroup中的第二个动作
            Element seconAactionElement = actionGroupElement.addElement("wpml:action");
            //动作id [0, 65535]
            //* 注:在一个动作组内该ID唯一。建议从0开始单调连续递增。
            seconAactionElement.addElement("wpml:actionId").setText("1");
            seconAactionElement.addElement("wpml:actionActuatorFunc").setText("gimbalRotate");
            Element secondActionActuatorFuncParamElement = seconAactionElement.addElement("wpml" +
                    ":actionActuatorFuncParam");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalHeadingYawBase").setText("aircraft");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRotateMode").setText("absoluteAngle");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalPitchRotateEnable").setText("1");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalPitchRotateAngle").setText(String.valueOf(-flightMission.getGimbalPitch()));
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRollRotateEnable").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRollRotateAngle").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalYawRotateEnable").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalYawRotateAngle").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRotateTimeEnable").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRotateTime").setText("10");
            secondActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");

            if (size - 1 == index) {
                //最后一个航点设置停止录像

                //动作列表,actiongroup中的第二个动作
                Element thirdAactionElement = actionGroupElement.addElement("wpml:action");
                //动作id [0, 65535]
                //* 注:在一个动作组内该ID唯一。建议从0开始单调连续递增。
                thirdAactionElement.addElement("wpml:actionId").setText("2");
                thirdAactionElement.addElement("wpml:actionActuatorFunc").setText("stopRecord");
                Element thirdActionActuatorFuncParamElement =
                        seconAactionElement.addElement("wpml:actionActuatorFuncParam");
                thirdActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");
                thirdActionActuatorFuncParamElement.addElement("wpml:payloadLensIndex").setText("wide,ir,narrow_band");
            }
            return true;
        }
    }

    /**
     * 获取航点任务
     *
     * @return
     */
    private String getActionActuatorFunc(String action) {
        if (action == null) {
            return "takePhoto";
        } else {
            if (action.equals("0")) {
                return "startRecord";
            } else if (action.equals("2")) {
                return "hover";
            } else if (action.equals("3")) {
                return "stopRecord";
            } else if (action.equals("4")) {
                return "gimbalRotate";
            } else {
                return "takePhoto";
            }
        }

    }

    private String getHeadMode(int index) {
        if (index == 1) {
            //手动控制。飞行器在飞至下一航点的过程中,用户可以手动控制飞行器机头朝向
            return "manually";
        } else if (index == 2) {
            //锁定当前偏航角。飞行器机头保持执行完航点动作后的飞行器偏航角飞至下一航点
            return "fixed";
        } else {
            //沿航线方向。飞行器机头沿着航线方向飞至下一航点
            return "followWayline";
        }
    }
}

使用如下:

 domParserKML = new DomParserKML(getExternalFilesDir(Environment.DIRECTORY_DCIM).getAbsolutePath(), "/template" +
                ".kml");
         domParserKML.createKml(flightMission);

下面是创建WPML的DomParserWPML 类:

package woyi.com.djilive.utils;

import android.util.Log;

import com.alibaba.fastjson.JSON;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.XMLWriter;

import java.io.File;
import java.io.FileWriter;
import java.util.List;

import woyi.com.djilive.MyApplication;
import woyi.com.djilive.bean.MissionPoint;
import woyi.com.djilive.bean.MyFlightMission;

/**
 * Created by kklu
 * on 2023/6/16
 * des:
 */
public class DomParserWPML {
    private static final String TAG = "DomParserWPML";
    private String wpmlFilePath = "";
    private String wpmlFileName = "";

    private Element docElement;

    private List<MissionPoint> pointList;
    private MyFlightMission flightMission;


    /**
     * 构造
     *
     * @param filePath
     * @param fileName
     */
    public DomParserWPML(String filePath, String fileName) {
        this.wpmlFilePath = filePath;
        this.wpmlFileName = fileName;
    }

    /**
     * 创建wpml文件
     */
    public void createWpml(MyFlightMission mission) {
        flightMission = mission;
        pointList = JSON.parseArray(flightMission.getPathCopy(), MissionPoint.class);
        String realPath = createFileDir();

        String fileName = realPath + wpmlFileName;

        Document document = DocumentHelper.createDocument();// 建立document对象,用来操作xml文件
        Element wpmlElement = document.addElement("kml", "http://www.opengis.net/kml/2.2");// 建立根节点
        wpmlElement.addAttribute("xmlns:wpml", "http://www.dji.com/wpmz/1.0.2");
        wpmlElement.addNamespace("wpml", "http://www.dji.com/wpmz/1.0.2");

        docElement = wpmlElement.addElement("Document");// 添加一个Document节点

        createMissionConfig();
        createFolder();
        try {
            XMLWriter writer = new XMLWriter(new FileWriter(fileName));
            writer.write(document); //写入
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建打包需要的文件目录
     */
    private String createFileDir() {
        String missionFilePath = wpmlFilePath + "/" + flightMission.getMissionId();
        File missionFile = new File(missionFilePath);
        if (!missionFile.exists()) {
            missionFile.mkdir();
        }
        String wpmzPath = wpmlFilePath + "/" + flightMission.getMissionId() + "/wpmz/";
        File dir = new File(wpmzPath);
        if (!dir.exists()) {
            dir.mkdir();
        }
        return wpmzPath;
    }

    private int createMissionConfig() {
        if (docElement != null) {
            try {
                Element missionConfigElement = docElement.addElement("wpml:missionConfig");
                //飞向起始点模式
                missionConfigElement.addElement("wpml:flyToWaylineMode").setText("safely");

                //航线结束动作
                missionConfigElement.addElement("wpml:finishAction").setText(getFinishAction());

                //失控是否继续执行航线
                missionConfigElement.addElement("wpml:exitOnRCLost").setText(flightMission.isExitOnRCLost() ?
                        "goContinue" : "executeLostAction");

                if (!flightMission.isExitOnRCLost()) {
                    //失控执行失控动作时下面元素是必须的的
                    missionConfigElement.addElement("wpml:executeRCLostAction").setText(getExecuteRCLostAction());
                }

                //安全起飞高度
                if (flightMission.getTakeOffSecurityHeight() != null) {
                    missionConfigElement.addElement("wpml:takeOffSecurityHeight").setText(String.valueOf(flightMission.getTakeOffSecurityHeight()));
                } else {
                    missionConfigElement.addElement("wpml:takeOffSecurityHeight").setText("100");
                }

                //全局航线过渡速度(飞向航线起点的速度)
                missionConfigElement.addElement("wpml:globalTransitionalSpeed").setText(String.valueOf(flightMission.getSpeed()));

                Element droneInfoElement = missionConfigElement.addElement("wpml:droneInfo");
                droneInfoElement.addElement("wpml:droneEnumValue").setText(getDroneEnumValue(MyApplication.getInstance().getProductType()));
                droneInfoElement.addElement("wpml:droneSubEnumValue").setText(getDroneSubEnumValue(MyApplication.getInstance().getProductType(),
                        MyApplication.getInstance().getCameraType()));

                Element payloadInfoElement = missionConfigElement.addElement("wpml:payloadInfo");
                payloadInfoElement.addElement("wpml:payloadEnumValue").setText(getPayloadEnumValue(MyApplication.getInstance().getCameraType()));
                payloadInfoElement.addElement("wpml:payloadSubEnumValue").setText("0");
                payloadInfoElement.addElement("wpml:payloadPositionIndex").setText("0");
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
                return -1;
            }
            return 0;
        } else {
            return -1;
        }

    }


    /**
     * 获取飞行器主类型
     *
     * @param productType
     * @return
     */
    private String getDroneEnumValue(String productType) {
        /**
         DJI_MAVIC_3_ENTERPRISE_SERIES	M3行业版系列。
         M30_SERIES	M30系列。
         M300_RTK	M300 RTK。
         M350_RTK	M350 RTK。
         DJI_MINI_3	DJI Mini 3。 ---不支持航线飞行
         DJI_MINI_3_PRO	DJI Mini 3 Pro。---不支持航线飞行
         **/
        String droneEnumValue = "66";
        if (productType.equals("DJI_MAVIC_3_ENTERPRISE_SERIES")) {
            droneEnumValue = "77";
        } else if (productType.equals("M30_SERIES")) {
            droneEnumValue = "67";
        } else if (productType.equals("M300_RTK")) {
            droneEnumValue = "60";
        } else if (productType.equals("M350_RTK")) {
            droneEnumValue = "89";
        }
        return droneEnumValue;
    }

    /**
     * 飞行器机型子类型
     *
     * @param productType
     * @param cameraType
     * @return
     */
    private String getDroneSubEnumValue(String productType, String cameraType) {
        String droneEnumValue = getDroneEnumValue(productType);
        if (droneEnumValue.equals("67")) {
            if (cameraType.equals("M30")) {
                return "0";
            } else if (cameraType.equals("M30T")) {
                return "1";
            } else {
                return "0";
            }
        } else if (droneEnumValue.equals("77")) {
            if (cameraType.equals("M3E")) {
                return "0";
            } else if (cameraType.equals("M3T")) {
                return "1";
            } else {
                //M3M
                return "2";
            }
        } else {
            return "0";
        }
    }

    /**
     * 负载机型主类型
     *
     * @param cameraType
     * @return
     */
    private String getPayloadEnumValue(String cameraType) {
        /**
         M3E	M3E行业版双光相机。
         M3T	M3T行业版三光相机。
         M3M	M3M多光谱相机。
         M30	M30双光相机。
         M30T	M30T三光相机。
         ZENMUSE_H20	Zenmuse H20双光相机。
         ZENMUSE_H20T	Zenmuse H20T三光相机。
         ZENMUSE_P1	Zenmuse P1相机。
         ZENMUSE_L1	Zenmuse L1相机。
         ZENMUSE_L2	Zenmuse L2相机。
         **/
        String type = "";
        if (cameraType.equals("M3E")) {
            type = "66";
        } else if (cameraType.equals("M3T")) {
            type = "67";
        } else if (cameraType.equals("M3M")) {
            type = "68";
        } else if (cameraType.equals("M30")) {
            type = "52";
        } else if (cameraType.equals("M30T")) {
            type = "53";
        } else if (cameraType.equals("ZENMUSE_H20")) {
            type = "42";
        } else if (cameraType.equals("ZENMUSE_H20T")) {
            type = "43";
        } else if (cameraType.equals("ZENMUSE_P1")) {
            type = "65534";
        } else if (cameraType.equals("ZENMUSE_L1")) {
            type = "65534";
        } else if (cameraType.equals("ZENMUSE_L2")) {
            type = "65534";
        }
        return type;
    }

    /**
     * 获取航线结束动作
     *
     * @return
     */
    private String getFinishAction() {
        if (flightMission.getFinishedAction() != null) {
            if (flightMission.getFinishedAction() == 0) {
                return "noAction";//悬停
            } else if (flightMission.getFinishedAction() == 1) {
                return "goHome";//返航
            } else if (flightMission.getFinishedAction() == 2) {
                return "autoLand";//原地降落
            } else if (flightMission.getFinishedAction() == 3) {
                return "gotoFirstWaypoint";//返回航线起始点
            } else {
                return "goHome";//返航
            }
        } else {
            return "goHome";//返航
        }
    }

    /**
     * 获取失控动作类型
     *
     * @return
     */
    private String getExecuteRCLostAction() {
        if (flightMission.getExecuteRCLostAction() != null) {
            if (flightMission.getExecuteRCLostAction() == 0) {
                //悬停
                return "hover";
            } else if (flightMission.getExecuteRCLostAction() == 1) {
                //原地降落
                return "landing";
            } else if (flightMission.getExecuteRCLostAction() == 1) {
                //返航
                return "goBack";
            } else {
                //返航
                return "goBack";
            }
        } else {
            //返航
            return "goBack";
        }
    }

    /**
     * 创建模板信息
     *
     * @return
     */
    private int createFolder() {
        if (docElement != null) {
            Element folderElement = docElement.addElement("Folder");

            //模板ID,范围:[0, 65535]
            folderElement.addElement("wpml:templateId").setText(String.valueOf(flightMission.getMissionId()));

            //航线ID 在一条航线中该ID唯一  [0, 65535]
            folderElement.addElement("wpml:waylineId").setText(String.valueOf(flightMission.getMissionId()));

            //全局航线飞行速度
            folderElement.addElement("wpml:autoFlightSpeed").setText(String.valueOf(flightMission.getSpeed()));

            //执行高度模式 该元素仅在waylines.wpml中使用
            folderElement.addElement("wpml:executeHeightMode").setText("WGS84");

            if (pointList != null && pointList.size() > 0) {
                //航点信息(包括航点经纬度和高度等)
                for (int i = 0; i < pointList.size(); i++) {
                    addPointToPlacemark(folderElement, pointList.get(i), pointList.size(), i);
                }
            }

            return 0;
        } else {
            return -1;
        }
    }

    /**
     * 向Placemark添加航点信息
     *
     * @param parentElement
     */
    private boolean addPointToPlacemark(Element parentElement, MissionPoint missionPoint, int size, int index) {
        if (missionPoint == null || missionPoint.getLat() == 0 || missionPoint.getLng() == 0) {
            return false;
        } else {
            Element placemarkElement = parentElement.addElement("Placemark");
            Element point = placemarkElement.addElement("Point");

            //航点经纬度<经度,纬度>
            point.addElement("coordinates")
                    .setText(missionPoint.getLng() + "," + missionPoint.getLat());

            //航点序号
            placemarkElement.addElement("wpml:index")
                    .setText(String.valueOf(index));

            //航点执行高度
            placemarkElement.addElement("wpml:executeHeight")
                    .setText(String.valueOf(flightMission.getHeight()));

            //航点飞行速度,当前航点飞向下一个航点的速度
            placemarkElement.addElement("wpml:waypointSpeed")
                    .setText(String.valueOf(flightMission.getSpeed()));

            //偏航角模式参数
            Element waypointHeadingParamElememt = placemarkElement.addElement("wpml:waypointHeadingParam");
            // followWayline:沿航线方向。飞行器机头沿着航线方向飞至下一航点
            //manually:手动控制。飞行器在飞至下一航点的过程中,用户可以手动控制飞行器机头朝向
            //fixed:锁定当前偏航角。飞行器机头保持执行完航点动作后的飞行器偏航角飞至下一航点
            //smoothTransition:自定义。通过“wpml:waypointHeadingAngle”给定某航点的目标偏航角,并在航段飞行过程中均匀过渡至下一航点的目标偏航角。
            //towardPOI:朝向兴趣点
            waypointHeadingParamElememt.addElement("wpml:waypointHeadingMode").setText(getHeadMode(flightMission.getHeadingMode()));
            //飞行器偏航角度
            waypointHeadingParamElememt.addElement("wpml:waypointHeadingAngle").setText("0");
            //飞行器偏航角转动方向
            // clockwise:顺时针旋转飞行器偏航角
            //counterClockwise:逆时针旋转飞行器偏航角
            //followBadArc:沿最短路径旋转飞行器偏航角
            waypointHeadingParamElememt.addElement("wpml:waypointHeadingPathMode").setText("followBadArc");

            //航点类型(航点转弯模式
            Element waypointTurnParamElement = placemarkElement.addElement("wpml:waypointTurnParam");
            //coordinateTurn:协调转弯,不过点,提前转弯
            //toPointAndStopWithDiscontinuityCurvature:直线飞行,飞行器到点停
            //toPointAndStopWithContinuityCurvature:曲线飞行,飞行器到点停
            //toPointAndPassWithContinuityCurvature:曲线飞行,飞行器过点不停
            waypointTurnParamElement.addElement("wpml:waypointTurnMode").setText(
                    "toPointAndStopWithDiscontinuityCurvature");
            //(0, 航段最大长度] 注:两航点间航段长度必需大于两航点转弯截距之和。此元素定义了飞行器在距离该航点若干米前,提前多少距离转弯。
            //当且仅当以下两种情况下必需“wpml:waypointTurnMode”为“coordinateTurn”“wpml:waypointTurnMode
            // ”为“toPointAndPassWithContinuityCurvature”,且“wpml:useStraightLine”为“1”
            waypointTurnParamElement.addElement("wpml:waypointTurnDampingDist").setText("0");

            //该航段是否贴合直线
            //0:航段轨迹全程为曲线
            //1:航段轨迹尽量贴合两点连线
            //当且仅当“wpml:waypointTurnParam”内"waypointTurnMode"被设置为“toPointAndStopWithContinuityCurvature
            // ”或“toPointAndPassWithContinuityCurvature”时必需。如果此元素被设置,则局部定义会覆盖全局定义。
            placemarkElement.addElement("wpml:useStraightLine").setText("1");

            //航线初始动作
            //*注:该元素用于规划一系列初始动作,在航线开始前执行。航线中断恢复时,先执行初始动作,再执行航点动作
            Element actionGroupElement = placemarkElement.addElement("wpml:actionGroup");
            //动作组id
            //* 注:在一个kmz文件内该ID唯一。建议从0开始单调连续递增。[0, 65535]
            actionGroupElement.addElement("wpml:actionGroupId").setText(String.valueOf(index));
            //动作组开始生效的航点 [0, 65535]
            actionGroupElement.addElement("wpml:actionGroupStartIndex").setText(String.valueOf(index));
            //动作组结束生效的航点
            //* 注:该元素必须大于等于“actionGroupStartIndex” [0, 65535]
            //* 注:当“动作组结束生效的航点”与“动作组开始生效的航点”一致,则代表该动作组仅在该航点处生效
            actionGroupElement.addElement("wpml:actionGroupEndIndex").setText(String.valueOf(index));
            //动作执行模式 sequence:串行执行。即动作组内的动作依次按顺序执行。
            actionGroupElement.addElement("wpml:actionGroupMode").setText("sequence");
            //动作组触发器
            Element actionTriggerElement = actionGroupElement.addElement("wpml:actionTrigger");
            //reachPoint:到达航点时执行
            //betweenAdjacentPoints:航段触发,均匀转云台
            //multipleTiming:等时触发
            //multipleDistance:等距触发
            //* 注:“betweenAdjacentPoints”需配合动作"gimbalEvenlyRotate"使用
            actionTriggerElement.addElement("wpml:actionTriggerType").setText("reachPoint");
            //动作列表,actiongroup中的第一个动作
            Element firstActionElement = actionGroupElement.addElement("wpml:action");
            //动作id [0, 65535]
            //* 注:在一个动作组内该ID唯一。建议从0开始单调连续递增。
            firstActionElement.addElement("wpml:actionId").setText("0");
            //动作类型
            //	takePhoto:单拍
            //startRecord:开始录像
            //stopRecord:结束录像
            //focus:对焦
            //zoom:变焦
            //customDirName:创建新文件夹
            //gimbalRotate:旋转云台
            //rotateYaw:飞行器偏航
            //hover:悬停等待
            //gimbalEvenlyRotate:航段间均匀转动云台pitch角
            //accurateShoot:精准复拍动作(已暂停维护,建议使用orientedShoot)
            //orientedShoot:精准复拍动作
            String firstAction = getActionActuatorFunc(missionPoint.getAction());
            firstActionElement.addElement("wpml:actionActuatorFunc").setText(firstAction);

            Element firstActionActuatorFuncParamElement = firstActionElement.addElement("wpml:actionActuatorFuncParam");
            if (firstAction.equals("takePhoto")) {
                //负载挂载位置
                //0:飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。
                //1:飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。
                //2:飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。
                firstActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");
                //拍摄照片文件后缀
                firstActionActuatorFuncParamElement.addElement("wpml:fileSuffix").setText("point" + index);
            } else if (firstAction.equals("startRecord")) {
                firstActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");
                //拍摄照片文件后缀
                firstActionActuatorFuncParamElement.addElement("wpml:fileSuffix").setText("point" + index);
                //拍摄照片存储类型
                // zoom: 存储变焦镜头拍摄照片
                //wide: 存储广角镜头拍摄照片
                //ir: 存储红外镜头拍摄照片
                //narrow_band: 存储窄带镜头拍摄照片
                //注:存储多个镜头照片,格式如“<wpml:payloadLensIndex>wide,ir,narrow_band</wpml:payloadLensIndex>”表示同时使用广角、红外和窄带镜头
                firstActionActuatorFuncParamElement.addElement("wpml:payloadLensIndex").setText("wide,ir,narrow_band");
                //是否使用全局存储类型
                //0:不使用全局设置
                //1:使用全局设置
                firstActionActuatorFuncParamElement.addElement("wpml:useGlobalPayloadLensIndex").setText("0");
            } else if (firstAction.equals("stopRecord")) {
                firstActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");
                firstActionActuatorFuncParamElement.addElement("wpml:payloadLensIndex").setText("wide,ir,narrow_band");
            } else if (firstAction.equals("hover")) {
                //飞行器悬停等待时间 单位s
                firstActionActuatorFuncParamElement.addElement("wpml:hoverTime").setText("5");
            } else if (firstAction.equals("rotateYaw")) {
                firstActionActuatorFuncParamElement.addElement("wpml:aircraftHeading").setText("180");
                firstActionActuatorFuncParamElement.addElement("wpml:aircraftPathMode").setText("clockwise");
            }

            //动作列表,actiongroup中的第二个动作
            Element seconAactionElement = actionGroupElement.addElement("wpml:action");
            //动作id [0, 65535]
            //* 注:在一个动作组内该ID唯一。建议从0开始单调连续递增。
            seconAactionElement.addElement("wpml:actionId").setText("1");
            seconAactionElement.addElement("wpml:actionActuatorFunc").setText("gimbalRotate");
            Element secondActionActuatorFuncParamElement = seconAactionElement.addElement("wpml" +
                    ":actionActuatorFuncParam");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalHeadingYawBase").setText("aircraft");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRotateMode").setText("absoluteAngle");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalPitchRotateEnable").setText("1");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalPitchRotateAngle").setText(String.valueOf(-flightMission.getGimbalPitch()));
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRollRotateEnable").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRollRotateAngle").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalYawRotateEnable").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalYawRotateAngle").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRotateTimeEnable").setText("0");
            secondActionActuatorFuncParamElement.addElement("wpml:gimbalRotateTime").setText("10");
            secondActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");

            if (size - 1 == index) {
                //最后一个航点设置停止录像

                //动作列表,actiongroup中的第二个动作
                Element thirdAactionElement = actionGroupElement.addElement("wpml:action");
                //动作id [0, 65535]
                //* 注:在一个动作组内该ID唯一。建议从0开始单调连续递增。
                thirdAactionElement.addElement("wpml:actionId").setText("2");
                thirdAactionElement.addElement("wpml:actionActuatorFunc").setText("stopRecord");
                Element thirdActionActuatorFuncParamElement =
                        seconAactionElement.addElement("wpml:actionActuatorFuncParam");
                thirdActionActuatorFuncParamElement.addElement("wpml:payloadPositionIndex").setText("0");
                thirdActionActuatorFuncParamElement.addElement("wpml:payloadLensIndex").setText("wide,ir,narrow_band");
            }
            return true;
        }
    }

    /**
     * 获取航点任务
     *
     * @return
     */
    private String getActionActuatorFunc(String action) {
        if (action == null) {
            return "takePhoto";
        } else {
            if (action.equals("0")) {
                return "startRecord";
            } else if (action.equals("2")) {
                return "hover";
            } else if (action.equals("3")) {
                return "stopRecord";
            } else if (action.equals("4")) {
                return "rotateYaw";
            } else {
                return "takePhoto";
            }
        }

    }

    private String getHeadMode(int index) {
        if (index == 1) {
            //手动控制。飞行器在飞至下一航点的过程中,用户可以手动控制飞行器机头朝向
            return "manually";
        } else if (index == 2) {
            //锁定当前偏航角。飞行器机头保持执行完航点动作后的飞行器偏航角飞至下一航点
            return "fixed";
        } else {
            //沿航线方向。飞行器机头沿着航线方向飞至下一航点
            return "followWayline";
        }
    }
}

使用:

        domParserWPML =
                new DomParserWPML(context.getExternalFilesDir(Environment.DIRECTORY_DCIM).getAbsolutePath(),
                        "waylines.wpml");

 生成文件KML(上面代码已更新,但下面kml部分字段没更新,请参考官方文档):

上云API (dji.com)

<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.2">
<Document>
<wpml:author>admin</wpml:author>
<wpml:createTime>1687230797490</wpml:createTime>
<wpml:updateTime>1687230797490</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>200.0</wpml:takeOffSecurityHeight>
<wpml:globalTransitionalSpeed>10.0</wpml:globalTransitionalSpeed>
</wpml:missionConfig>
<Folder>
<wpml:templateType>waypoint</wpml:templateType>
<wpml:templateId>1</wpml:templateId>
<wpml:autoFlightSpeed>10.0</wpml:autoFlightSpeed>
<wpml:globalWaypointTurnMode>toPointAndStopWithDiscontinuityCurvature</wpml:globalWaypointTurnMode>
<wpml:globalUseStraightLine>1</wpml:globalUseStraightLine>
<wpml:gimbalPitchMode>usePointSetting</wpml:gimbalPitchMode>
<wpml:globalHeight>200.0</wpml:globalHeight>
<wpml:globalWaypointHeadingParam>
<wpml:waypointHeadingMode>followWayline</wpml:waypointHeadingMode>
<wpml:waypointHeadingAngle>45</wpml:waypointHeadingAngle>
</wpml:globalWaypointHeadingParam>
<Placemark>
<Point>
<coordinates>31.84681,117.14123</coordinates>
</Point>
<wpml:index>0</wpml:index>
<wpml:useGlobalHeight>1</wpml:useGlobalHeight>
<wpml:ellipsoidHeight>200.0</wpml:ellipsoidHeight>
<wpml:height>200.0</wpml:height>
<wpml:useGlobalSpeed>1</wpml:useGlobalSpeed>
<wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam>
<wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam>
<wpml:gimbalPitchAngle>90</wpml:gimbalPitchAngle>
</Placemark>
<Placemark>
<Point>
<coordinates>31.84682,117.14124</coordinates>
</Point>
<wpml:index>1</wpml:index>
<wpml:useGlobalHeight>1</wpml:useGlobalHeight>
<wpml:ellipsoidHeight>200.0</wpml:ellipsoidHeight>
<wpml:height>200.0</wpml:height>
<wpml:useGlobalSpeed>1</wpml:useGlobalSpeed>
<wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam>
<wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam>
<wpml:gimbalPitchAngle>90</wpml:gimbalPitchAngle>
</Placemark>
<Placemark>
<Point>
<coordinates>31.84682,117.14124</coordinates>
</Point>
<wpml:index>2</wpml:index>
<wpml:useGlobalHeight>1</wpml:useGlobalHeight>
<wpml:ellipsoidHeight>200.0</wpml:ellipsoidHeight>
<wpml:height>200.0</wpml:height>
<wpml:useGlobalSpeed>1</wpml:useGlobalSpeed>
<wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam>
<wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam>
<wpml:gimbalPitchAngle>90</wpml:gimbalPitchAngle>
</Placemark>
<Placemark>
<Point>
<coordinates>31.84683,117.14125</coordinates>
</Point>
<wpml:index>3</wpml:index>
<wpml:useGlobalHeight>1</wpml:useGlobalHeight>
<wpml:ellipsoidHeight>200.0</wpml:ellipsoidHeight>
<wpml:height>200.0</wpml:height>
<wpml:useGlobalSpeed>1</wpml:useGlobalSpeed>
<wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam>
<wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam>
<wpml:gimbalPitchAngle>90</wpml:gimbalPitchAngle>
</Placemark>
<Placemark>
<Point>
<coordinates>31.84684,117.14126</coordinates>
</Point>
<wpml:index>4</wpml:index>
<wpml:useGlobalHeight>1</wpml:useGlobalHeight>
<wpml:ellipsoidHeight>200.0</wpml:ellipsoidHeight>
<wpml:height>200.0</wpml:height>
<wpml:useGlobalSpeed>1</wpml:useGlobalSpeed>
<wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam>
<wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam>
<wpml:gimbalPitchAngle>90</wpml:gimbalPitchAngle>
</Placemark>
</Folder>
</Document>
</kml>

小结

wpmls生成方式相同,但是部分节点属性不一样,需要注意

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
线文件格式有两种,一种是KML文件,另一种是WPML文件KML文件是Keyhole Markup Language的缩写,是一种用于描述地理信息的标记语言。在大中,KML文件被用作线文件的模版文件,通常命名为template.kml。用户可以通过移动端的DJI Pilot 2应用或电脑端的大司空软件导出KML格式的线文件WPML文件是WayPoint Markup Language的缩写,是一种基于KML定义进行扩展的线文件格式标准。WPML线文件以.kmz为后缀,遵循KMZ归档要求。在线文件文件结构中,waylines.wpml是执行文件,res文件夹是资源文件(非必需)。 因此,大线文件可以是KML格式或WPML格式,用户可以通过DJI Pilot 2应用或大司空软件生成线文件。 #### 引用[.reference_title] - *1* [Android使用dom4j-创建MSDK-V5 飞行kmlwpml文件](https://blog.csdn.net/kervin521/article/details/131303501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [大SDK-线文件](https://blog.csdn.net/u011404423/article/details/125389394)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值