当前位置: 首页 > news >正文

Java生成与解析大疆无人机KMZ航线文件

目录

  • 一、概述
    • 1.template.kml 说明
    • 2.waylines.wpml 说明
  • 二、实现
    • 1.实现思路
  • 2.具体实现
    • 2.1 引入 maven 依赖
    • 2.2 使用 XStream 注解的 Java 类
    • 2.3 生成 KMZ 文件
    • 2.4 解析 KMZ 文件
    • 2.5 测试
      • 2.5.1 生成
      • 2.5.2 解析
  • 3.完整代码

一、概述

KMZ航线文件本质上是一个ZIP格式的压缩文件,一个标准的KMZ文件结构如下:

—— wpmz
|—— res
|—— template.kml
|—— waylines.wpml

  • res:资源文件夹,用来存储航线所需的辅助资源文件(如图片、数据等)。
  • template.kml:模板文件,定义了航线的业务属性,便于用户快速编辑和调整。
  • waylines.wpml:执行文件,包含了无人机执行航线任务时的具体执行细节。

template.kml、waylines.wpml 和 res 资源文件夹都是航线文件格式标准的一部分,我们可以根据使用场景灵活调整文件结构。

如果需要将 KMZ 航线文件导入到 Pilot2 中使用,可以只生成 template.kml 文件,Pilot2 可以根据 template 文件生成 waylines 文件。除特殊使用场景外(精准复拍前,准备参考目标照片等),可以不生成 res 文件夹。

1.template.kml 说明

template.kml是模板文件,可以被 DJI Pilot 2、DJI Flighthub 2 或者其它软件解析,生成最终提供给无人机执行的路径和动作,即waylines.wpml文件。

template.kml文件由三部分组成:

  • 创建信息:主要包含航线文件本身的信息,例如文件的创建、更新时间等。
  • 任务信息:主要包含 wpml:missionConfig元素,定义航线任务的全局参数等
  • 模板信息:主要包含Folder元素,定义航线的模板信息(如航点飞行、建图航拍、倾斜摄影、航带飞行等)。不同航线模板类型包含的元素不同。

示例文件
template.kml示例文件如下(以航点飞行模板为例):

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.6"><Document><wpml:author>Cleaner</wpml:author><wpml:createTime>1756429425069</wpml:createTime><wpml:updateTime>1756429425069</wpml:updateTime><wpml:missionConfig><wpml:finishAction>goHome</wpml:finishAction><wpml:exitOnRCLost>goContinue</wpml:exitOnRCLost><wpml:executeRCLostAction>goBack</wpml:executeRCLostAction><wpml:takeOffSecurityHeight>20</wpml:takeOffSecurityHeight><wpml:globalTransitionalSpeed>15</wpml:globalTransitionalSpeed><wpml:globalRTHHeight>100</wpml:globalRTHHeight><wpml:takeOffRefPoint>22.579831,113.937935,32.536774</wpml:takeOffRefPoint><wpml:takeOffRefPointAGLHeight>0</wpml:takeOffRefPointAGLHeight></wpml:missionConfig><Folder><wpml:templateType>waypoint</wpml:templateType><wpml:templateId>0</wpml:templateId><wpml:autoFlightSpeed>15</wpml:autoFlightSpeed><wpml:waylineCoordinateSysParam><wpml:coordinateMode>WGS84</wpml:coordinateMode><wpml:heightMode>EGM96</wpml:heightMode></wpml:waylineCoordinateSysParam><wpml:globalWaypointTurnMode>toPointAndStopWithDiscontinuityCurvature</wpml:globalWaypointTurnMode><wpml:globalUseStraightLine>1</wpml:globalUseStraightLine><wpml:gimbalPitchMode>manual</wpml:gimbalPitchMode><wpml:globalHeight>120</wpml:globalHeight><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.935612998,22.583152266</coordinates></Point><wpml:index>0</wpml:index><wpml:useGlobalHeight>1</wpml:useGlobalHeight><wpml:ellipsoidHeight>123.495649717576</wpml:ellipsoidHeight><wpml:height>127</wpml:height><wpml:useGlobalSpeed>1</wpml:useGlobalSpeed><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam><wpml:useStraightLine>1</wpml:useStraightLine><wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.935821421,22.582421727</coordinates></Point><wpml:index>1</wpml:index><wpml:useGlobalHeight>1</wpml:useGlobalHeight><wpml:ellipsoidHeight>123.497734208512</wpml:ellipsoidHeight><wpml:height>127</wpml:height><wpml:useGlobalSpeed>1</wpml:useGlobalSpeed><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam><wpml:useStraightLine>1</wpml:useStraightLine><wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.936045587,22.581940624</coordinates></Point><wpml:index>2</wpml:index><wpml:useGlobalHeight>1</wpml:useGlobalHeight><wpml:ellipsoidHeight>123.499437353844</wpml:ellipsoidHeight><wpml:height>127</wpml:height><wpml:useGlobalSpeed>1</wpml:useGlobalSpeed><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam><wpml:useStraightLine>1</wpml:useStraightLine><wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.936284025,22.581568969</coordinates></Point><wpml:index>3</wpml:index><wpml:useGlobalHeight>1</wpml:useGlobalHeight><wpml:ellipsoidHeight>123.501001190067</wpml:ellipsoidHeight><wpml:height>127</wpml:height><wpml:useGlobalSpeed>1</wpml:useGlobalSpeed><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam><wpml:useStraightLine>1</wpml:useStraightLine><wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.93668438,22.581484696</coordinates></Point><wpml:index>4</wpml:index><wpml:useGlobalHeight>1</wpml:useGlobalHeight><wpml:ellipsoidHeight>123.502672008343</wpml:ellipsoidHeight><wpml:height>127</wpml:height><wpml:useGlobalSpeed>1</wpml:useGlobalSpeed><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam><wpml:useStraightLine>1</wpml:useStraightLine><wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.937234332,22.581549609</coordinates></Point><wpml:index>5</wpml:index><wpml:useGlobalHeight>1</wpml:useGlobalHeight><wpml:ellipsoidHeight>123.504647426208</wpml:ellipsoidHeight><wpml:height>127</wpml:height><wpml:useGlobalSpeed>1</wpml:useGlobalSpeed><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useGlobalTurnParam>1</wpml:useGlobalTurnParam><wpml:useStraightLine>1</wpml:useStraightLine><wpml:useGlobalHeadingParam>1</wpml:useGlobalHeadingParam></Placemark></Folder></Document>
</kml>

2.waylines.wpml 说明

waylines.wpml是飞机直接执行的文件,它定义了明确的无人机飞行和负载动作指令,这些指令由 DJI Pilot 2、DJI Flighthub 2 或者其它软件生成,也可被开发者直接编辑开发。

waylines.wpml文件由两部分组成:

  • 任务信息:主要包含 wpml:missionConfig元素,定义航线任务的全局参数等
  • 航线信息:主要包含Folder元素,定义详细的航线信息(路径定义、动作定义等)每个Folder代表一条可执行的航线特别的,当使用“倾斜摄影”模板时,将生成5条可执行航线,对应waylines.wpml内的5个Folder元素。

示例文件
waylines.wpml示例文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.6"><Document><wpml:missionConfig><wpml:finishAction>goHome</wpml:finishAction><wpml:exitOnRCLost>goContinue</wpml:exitOnRCLost><wpml:executeRCLostAction>goBack</wpml:executeRCLostAction><wpml:takeOffSecurityHeight>20</wpml:takeOffSecurityHeight><wpml:globalTransitionalSpeed>15</wpml:globalTransitionalSpeed><wpml:globalRTHHeight>100</wpml:globalRTHHeight></wpml:missionConfig><Folder><wpml:templateId>0</wpml:templateId><wpml:autoFlightSpeed>15</wpml:autoFlightSpeed><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.935612998,22.583152266</coordinates></Point><wpml:index>0</wpml:index><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useStraightLine>1</wpml:useStraightLine><wpml:executeHeight>123.495649717576</wpml:executeHeight></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.935821421,22.582421727</coordinates></Point><wpml:index>1</wpml:index><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useStraightLine>1</wpml:useStraightLine><wpml:executeHeight>123.497734208512</wpml:executeHeight></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.936045587,22.581940624</coordinates></Point><wpml:index>2</wpml:index><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useStraightLine>1</wpml:useStraightLine><wpml:executeHeight>123.499437353844</wpml:executeHeight></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.936284025,22.581568969</coordinates></Point><wpml:index>3</wpml:index><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useStraightLine>1</wpml:useStraightLine><wpml:executeHeight>123.501001190067</wpml:executeHeight></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.93668438,22.581484696</coordinates></Point><wpml:index>4</wpml:index><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useStraightLine>1</wpml:useStraightLine><wpml:executeHeight>123.502672008343</wpml:executeHeight></Placemark><Placemark><wpml:isRisky>0</wpml:isRisky><Point><coordinates>113.937234332,22.581549609</coordinates></Point><wpml:index>5</wpml:index><wpml:waypointSpeed>10</wpml:waypointSpeed><wpml:useStraightLine>1</wpml:useStraightLine><wpml:executeHeight>123.504647426208</wpml:executeHeight></Placemark><wpml:executeHeightMode>WGS84</wpml:executeHeightMode><wpml:waylineId>0</wpml:waylineId></Folder></Document>
</kml>

二、实现

1.实现思路

大疆开发者平台上云 API 文档中详细介绍了航线文件格式标准以及文件中具体的元素、名称以及取值和释义。

本文主要介绍如何生成和解析航点飞行模板类型的 KMZ 航线文件,具体的实现思路如下:

  • KMZ 文件生成:首先生成一个 wpmz 文件夹,然后在 wpmz 文件夹下,生成 template.kml 文件和 waylines.wpml 文件,然后将 wpmz 文件夹压缩成 .zip 格式,最后修改 .zip 文件后缀为 .kmz。

  • KMZ 文件解析:上传 KMZ 文件,对 KMZ 文件解压缩,获取到 template.kml 和 waylines.wpml 文件,然后分别解析 template.kml 文件和 waylines.wpml 文件。

template.kml 和 waylines.wpml 文件都是 XML 格式,而且文件中包含多个元素标签,每个元素标签又有不同的属性,航线文件内容比较灵活,因此可以使用 Java 类库 XStream 来方便的将 Java 对象和 XML 文档相互转换,航线文件中每个元素标签可以对应一个 Java Bean,通过操作这些 Java Bean 来生成和解析 KMZ 航线文件。

2.具体实现

由于代码量较大,这里主要贴一下关键代码

2.1 引入 maven 依赖

<!--航线kmz文件生成与解析-->
<dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.20</version>
</dependency>
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.26.0</version>
</dependency>

2.2 使用 XStream 注解的 Java 类

package com.example.djpointdemo.dao.kml;import com.fasterxml.jackson.annotation.JsonInclude;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import lombok.Data;/*** @author qzz* @date 2025/8/12*/
@Data
@XStreamAlias("kml")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class KmlInfo {@XStreamAsAttribute@XStreamAlias("xmlns")private String xmlns = "http://www.opengis.net/kml/2.2";@XStreamAsAttribute@XStreamAlias("xmlns:wpml")private String wpml = "http://www.dji.com/wpmz/1.0.6";@XStreamAlias("Document")private KmlDocument document;
}

2.3 生成 KMZ 文件

    /*** 生成航线 KMZ 文件** @param fileName  文件名* @param kmlParams 参数对象* @return 本地文件路径*/public static String buildKmz(String fileName, KmlParams kmlParams) {//深度拷贝文件KmlParams templateParams = getTemplateParams(kmlParams);KmlInfo kmlInfo = buildKml(templateParams);KmlInfo wpmlInfo = buildWpml(getWayLinesParams(kmlParams));return buildKmz(fileName, kmlInfo, wpmlInfo);}
/*** 生成航线 KMZ 文件** @param fileName 文件名* @param kmlInfo  kml 文件信息* @param wpmlInfo wpml 文件信息* @return 本地文件路径*/public static String buildKmz(String fileName, KmlInfo kmlInfo, KmlInfo wpmlInfo) {XStream xStream = new XStream(new DomDriver());xStream.processAnnotations(KmlInfo.class);
//        xStream.addImplicitCollection(KmlActionGroup.class, "action");String kml = XML_HEADER + xStream.toXML(kmlInfo);String wpml = XML_HEADER + xStream.toXML(wpmlInfo);String destFilePath = LOCAL_KMZ_FILE_PATH + fileName + ".kmz";File file = new File(destFilePath);File parentFile = file.getParentFile();if (parentFile != null && !parentFile.exists()) {parentFile.mkdirs();}try (FileOutputStream fileOutputStream = new FileOutputStream(destFilePath);ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream)) {zipOutputStream.setLevel(0); // 0 表示不压缩,存储方式// 创建 wpmz 目录中的 template.kml 文件条目buildZipFile("wpmz/template.kml", zipOutputStream, kml);// 创建 wpmz 目录中的 waylines.wpml 文件条目buildZipFile("wpmz/waylines.wpml", zipOutputStream, wpml);} catch (Exception e) {throw new RuntimeException(e);}return LOCAL_KMZ_FILE_PATH + fileName + ".kmz";}

KmlParams.java

package com.example.djpointdemo.dao.kml;import lombok.Data;
import java.util.List;/*** 航线文件*/
@Data
public class KmlParams {/*** 任务信息*/private KmlMissionConfig kmlMissionConfig;/*** 模板信息:航点飞行模板信息    设置List的理由:每个Folder代表一条可执行的航线。特别的,当使用“倾斜摄影”模板时,将生成5条可执行航线,对应waylines.wpml内的5个Folder元素。*/private List<KmlFolder> folder;
}

2.4 解析 KMZ 文件

    /*** 解析kmz文件   本地文件* @param fileUrl* @return*/@Overridepublic KmlInfo parseKmzLocalFile(String fileUrl) {//File是用来操作本地文件系统路径的,不支持 HTTP URLFile file = new File(fileUrl);try (ArchiveInputStream archiveInputStream = new ZipArchiveInputStream(FileUtil.getInputStream(file))) {ArchiveEntry entry;KmlInfo kmlInfo = new KmlInfo();KmlInfo wpmlInfo = new KmlInfo();while (!Objects.isNull(entry = archiveInputStream.getNextEntry())) {String name = entry.getName();if (name.toLowerCase().endsWith(".kml")) {kmlInfo = ParseFileUtils.parseKml(archiveInputStream);} else if (name.toLowerCase().endsWith(".wpml")) {wpmlInfo = ParseFileUtils.parseKml(archiveInputStream);}}return wpmlInfo;} catch (Exception e) {e.printStackTrace();}return null;}/*** kml文件解析** @param inputStream* @return KmlInfo*/public static KmlInfo parseKml(InputStream inputStream) {XStream xStream = new XStream();xStream.allowTypes(new Class[]{KmlInfo.class, KmlWayLineCoordinateSysParam.class, KmlPoint.class});xStream.alias("kml", KmlInfo.class);xStream.processAnnotations(KmlInfo.class);xStream.autodetectAnnotations(true);xStream.ignoreUnknownElements();
//        xStream.addImplicitCollection(KmlActionGroup.class, "action");return (KmlInfo) xStream.fromXML(inputStream);}

2.5 测试

    /*** 解析kmz文件 本地文件* @param fileUrl* @return*/@GetMapping("/parseKmzLocalFile")public KmlInfo parseKmzLocalFile(@RequestParam("fileUrl") String fileUrl){return djAirLineParseService.parseKmzLocalFile(fileUrl);}/*** 生成kmz文件* @param kmlParams* @return*/@PostMapping("/createKmz")public String createKmz(@RequestBody KmlParams kmlParams){return djAirLineParseService.createKmz(kmlParams);}

请求示例:

2.5.1 生成

post请求:http://localhost:8080/createKmz
传参:

{"kmlMissionConfig":{"flyToWaylineMode": "safely","finishAction": "goHome","exitOnRCLost": "goContinue","executeRCLostAction": "goBack","takeOffSecurityHeight": "20","globalTransitionalSpeed": "15","globalRTHHeight": "100","takeOffRefPoint": "22.579831,113.937935,32.536774", // kml"takeOffRefPointAGLHeight": "0", //kml"droneInfo": {"droneEnumValue": "100","droneSubEnumValue": "0"},"payloadInfo": {"payloadEnumValue": "98","payloadPositionIndex": "0"}},"folder": [{"templateType": "waypoint",//kml"templateId": "0","autoFlightSpeed": "15","waylineCoordinateSysParam": {//kml"coordinateMode": "WGS84","heightMode": "EGM96"},"globalWaypointTurnMode": "toPointAndStopWithDiscontinuityCurvature",//kml"globalUseStraightLine": "1",//kml"gimbalPitchMode": "manual",//kml"globalHeight": "120",//kml"placemarkList": [{"isRisky": "0","kmlPoint": {"coordinates": "\n            113.935612998,22.583152266\n          "},"index": "0","useGlobalHeight": "1",//kml"ellipsoidHeight": "123.495649717576",//kml"height": "127",//kml                       "useGlobalSpeed": "1",//kml"waypointSpeed": "10","useGlobalTurnParam": "1",//kml"useStraightLine": "1","executeHeight": "123.495649717576",//wpml"useGlobalHeadingParam":1},{"isRisky": "0","kmlPoint": {"coordinates": "\n            113.935821421,22.582421727\n          "},"index": "1","useGlobalHeight": "1","ellipsoidHeight": "123.497734208512","height": "127",                       "useGlobalSpeed": "1","waypointSpeed": "10","useGlobalTurnParam": "1","useStraightLine": "1","executeHeight": "123.497734208512","useGlobalHeadingParam":1},{"isRisky": "0","kmlPoint": {"coordinates": "\n            113.936045587,22.581940624\n          "},"index": "2","useGlobalHeight": "1","ellipsoidHeight": "123.499437353844","height": "127",                       "useGlobalSpeed": "1","waypointSpeed": "10","useGlobalTurnParam": "1","useStraightLine": "1","executeHeight": "123.499437353844","useGlobalHeadingParam":1},{"isRisky": "0","kmlPoint": {"coordinates": "\n            113.936284025,22.581568969\n          "},"index": "3","useGlobalHeight": "1","ellipsoidHeight": "123.501001190067","height": "127",                       "useGlobalSpeed": "1","waypointSpeed": "10","useGlobalTurnParam": "1","useStraightLine": "1","executeHeight": "123.501001190067","useGlobalHeadingParam":1},{"isRisky": "0","kmlPoint": {"coordinates": "\n            113.93668438,22.581484696\n          "},"index": "4","useGlobalHeight": "1","ellipsoidHeight": "123.502672008343","height": "127",                       "useGlobalSpeed": "1","waypointSpeed": "10","useGlobalTurnParam": "1","useStraightLine": "1","executeHeight": "123.502672008343","useGlobalHeadingParam":1},{"isRisky": "0","kmlPoint": {"coordinates": "\n            113.937234332,22.581549609\n          "},"index": "5","useGlobalHeight": "1","ellipsoidHeight": "123.504647426208","height": "127",                       "useGlobalSpeed": "1","waypointSpeed": "10","useGlobalTurnParam": "1","useStraightLine": "1","executeHeight": "123.504647426208","useGlobalHeadingParam":1}],"executeHeightMode":"WGS84",//wpml"waylineId": "0"//wpml}]
}

2.5.2 解析

get请求:http://localhost:8080/parseKmzLocalFile?fileUrl=C://Users//Admin//Downloads//test.kmz

3.完整代码

点击此处下载

http://www.dtcms.com/a/384679.html

相关文章:

  • Mysql 主从复制、读写分离
  • Linux网络设备驱动结构
  • 第四阶段C#通讯开发-3:串口通讯之Modbus协议
  • 使用生成式 AI 和 Amazon Bedrock Data Automation 处理大规模智能文档
  • 可可图片编辑 HarmonyOS(7)图片绘画
  • django登录注册案例(上)
  • 查看iOS设备文件管理 访问iPhone用户文件、App沙盒目录 系统日志与缓存
  • 基于Echarts+HTML5可视化数据大屏展示-白茶大数据溯源平台V2
  • android 框架—网络访问Okhttp
  • CUDA 中Thrust exclusive_scan使用详解
  • Quat 四元数库使用教程:应用场景概述
  • GitHub 热榜项目 - 日榜(2025-09-15)
  • 让AI数据中心突破性能极限
  • Self-supervised Feature Adaptation for 3D Industrial Anomaly Detection 论文精读
  • 【3D图像算法技术讨论】如何给基于3dgs重建的城市街景增加碰撞的属性,满足仿真的要求?
  • numpy学习笔记
  • Oracle体系结构-归档日志文件(Archive Log Files)
  • 唐源电气:机器视觉与AI Agent引领智能运维
  • 【Python】在pycharm中使用environment.ylm文件配置虚拟环境
  • 2025前端面试题及答案-2(详细)
  • 技术突破:《Light Sci. Appl.》SH-GSL理论,为超表面提供全通道谐波调控能力
  • 2025年ASOC SCI2区TOP,多类别教学优化算法+多修剪机器人与多施肥无人机协同任务分配,深度解析+性能实测
  • 佰力博检测与您探讨高低温介电测试的应用领域
  • 网络编程-day6
  • 【04】AI辅助编程完整的安卓二次商业实战-寻找修改替换新UI首页图标-菜单图标-消息列表图标-优雅草伊凡
  • 《格式工厂FormatFactory》 [5.21.0][便携版] 下载
  • 【ubuntu24.04】安装rust
  • vue-sync修饰符解析以及切换iframe页面进行保存提示功能的思路
  • 005 Rust变量与常量
  • DOM---操作元素样式属性详解