文章目录
-
- 概要
- 技术细节
- 小结
概要
前期使用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)文章来源:https://www.toymoban.com/news/detail-770346.html
<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生成方式相同,但是部分节点属性不一样,需要注意文章来源地址https://www.toymoban.com/news/detail-770346.html
到了这里,关于Android使用dom4j-创建大疆MSDK-V5 航点飞行kml和wpml文件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!