航线文件:后缀以kmz结尾的文件,本质是一个压缩包,可通过压缩包解压得到
WPML 是WayPointMarkupLanguage 的缩写,即航线文件格式标准。WPML 航线文件格式标准基于 KML(Keyhole Markup Language)的定义进行扩展。WPML 航线文件遵循 KMZ 归档要求,所有航线文件以 “.kmz” 后缀结尾。WPML 航线文件格式标准作为航线数字资产的载体
解压后如下图文件:
通过go语言实现对kmz文件中wpml航线规划的解析,获取相应航线轨迹、航点动作等信息
说明:
主要功能是从指定路径下载KMZ文件,解压并解析其中的KML内容。具体步骤如下:
1. 下载KMZ文件到本地。
2. 解压KMZ文件,查找并读取特定的KML文件内容。
3. 清洗XML字符串,替换特定前缀。
4. 解析清洗后的XML字符串为WPML结构。
以下代码为解析kmz包中的waylines.wpml
是飞机直接执行的文件
package kmzPrase
import (
"archive/zip"
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
)
func TestUnzipKMZ(t *testing.T) {
rawURL := "./20250102.kmz"
filePath, err := downLoadFile(rawURL)
body, err := unzipKMZ(filePath)
if err != nil {
fmt.Println("unzipKMZ err:", err)
return
}
cleanedXmlStr := string(body)
fmt.Println(cleanedXmlStr)
cleanedXmlStr = strings.Replace(cleanedXmlStr, "xmlns:", "xmlns-", 1)
cleanedXmlStr = strings.ReplaceAll(cleanedXmlStr, "wpml:", "wpml-")
var kml WPML
if err := xml.Unmarshal([]byte(cleanedXmlStr), &kml); err != nil {
fmt.Println("解析失败:", err)
return
}
}
func downLoadFile(rawURL string) (string, error) {
localPath, err := os.Getwd()
if err != nil {
return localPath, err
}
parsedURL, err := url.Parse(rawURL)
// 获取文件名
filename := filepath.Base(parsedURL.Path)
if err != nil {
return localPath, errors.New(fmt.Sprintf("Parse raw url err: %s\n", err))
}
// Send HTTP GET request
resp, err := http.Get(rawURL)
if err != nil {
return localPath, errors.New(fmt.Sprintf("Error while making GET request: %s\n", err))
}
defer resp.Body.Close() // Ensure the body is closed when we're done with it
// Check if the request was successful (status code 200)
if resp.StatusCode != http.StatusOK {
return localPath, errors.New(fmt.Sprintf("Failed to download file: %s\n", err))
}
// Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return localPath, errors.New(fmt.Sprintf("Error reading response body: %s\n", err))
}
localPath += "/" + filename
// write it to a file:
err = os.WriteFile(filename, body, 0644) // Go 1.16+ syntax
if err != nil {
return localPath, errors.New(fmt.Sprintf("Error writing to file: %s\n", err))
} else {
return localPath, nil
}
}
func unzipKMZ(path string) ([]byte, error) {
result := []byte{}
r, err := zip.OpenReader(path)
if err != nil {
return result, err
}
defer r.Close()
for _, f := range r.File {
if f.Name == "wpmz/waylines.wpml" { // 假设KML文件名为doc.kml
rc, err := f.Open()
if err != nil {
return result, err
}
defer rc.Close()
buf := new(bytes.Buffer)
_, err = io.Copy(buf, rc)
if err != nil {
return result, err
}
kmlContent, err := ioutil.ReadAll(buf)
if err != nil {
log.Fatal(err)
}
return kmlContent, nil // 返回KML内容的字符串表示
//return buf.String(), nil // 返回KML内容的字符串表示
}
}
return result, fmt.Errorf("KML file not found in KMZ")
}
package kmzPrase
import (
"encoding/xml"
)
type WPMLHeader struct {
XMLName xml.Name `xml:"kml"`
}
type WPML struct {
XMLName xml.Name `xml:"kml"`
XMLNS string `xml:"xmlns,attr"`
XMLNSWPML string `xml:"xmlns-wpml,attr"`
Document DocumentWPML `xml:"Document"`
}
type DocumentWPML struct {
MissionConfig MissionConfigWPML `xml:"wpml-missionConfig"`
Folder FolderWPML `xml:"Folder"`
}
type MissionConfigWPML struct {
FlyToWaylineMode string `xml:"wpml-flyToWaylineMode"`
FinishAction string `xml:"wpml-finishAction"`
ExitOnRCLost string `xml:"wpml-exitOnRCLost"`
ExecuteRCLostAction string `xml:"wpml-executeRCLostAction"`
TakeOffSecurityHeight float64 `xml:"wpml-takeOffSecurityHeight"`
GlobalTransitionalSpeed float64 `xml:"wpml-globalTransitionalSpeed"`
DroneInfo DroneInfo `xml:"wpml-droneInfo"`
PayloadInfo PayloadInfo `xml:"wpml-payloadInfo"`
}
type FolderWPML struct {
TemplateID int `xml:"wpml-templateId"`
ExecuteHeightMode string `xml:"wpml-executeHeightMode"`
WaylineID int `xml:"wpml-waylineId"`
AutoFlightSpeed int `xml:"wpml-autoFlightSpeed"`
Placemarks []PlacemarkWPML `xml:"Placemark"`
}
type PlacemarkWPML struct {
IsRisky int `xml:"wpml-isRisky,omitempty"`
Point Point
Index int `xml:"wpml-index"`
ExecuteHeight int `xml:"wpml-executeHeight"`
WaypointSpeed int16 `xml:"wpml-waypointSpeed"`
WaypointHeadingParam WaypointHeadingParam `xml:"wpml-waypointHeadingParam"`
WaypointTurnParam WaypointTurnParam `xml:"wpml-waypointTurnParam"`
UseStraightLine int `xml:"wpml-useStraightLine,omitempty"`
ActionGroup *ActionGroup `xml:"wpml-actionGroup"`
}
package kmzPrase
type DroneInfo struct {
DroneEnumValue int `xml:"wpml-droneEnumValue"`
DroneSubEnumValue int `xml:"wpml-droneSubEnumValue"`
}
type PayloadInfo struct {
PayloadEnumValue int `xml:"wpml-payloadEnumValue"`
//PayloadSubEnumValue int `xml:"wpml-payloadSubEnumValue"`
PayloadPositionIndex int `xml:"wpml-payloadPositionIndex"`
}
type Point struct {
Coordinates string `xml:"coordinates"`
}
type WaypointHeadingParam struct { //UseGlobalHeadingParam为0时必有
WaypointHeadingMode string `xml:"wpml-waypointHeadingMode"`
WaypointHeadingAngle float64 `xml:"wpml-waypointHeadingAngle"`
WaypointPoiPoint string `xml:"wpml-waypointPoiPoint"` //仅当wpml-waypointHeadingMode为towardPOI时必需
WaypointHeadingPathMode string `xml:"wpml-waypointHeadingPathMode"`
}
type WaypointTurnParam struct {
WaypointTurnMode string `xml:"wpml-waypointTurnMode"`
WaypointTurnDampingDist float64 `xml:"wpml-waypointTurnDampingDist,omitempty"`
}
type PayloadParam struct {
PayloadPositionIndex int `xml:"wpml-payloadPositionIndex"`
FocusMode string `xml:"wpml-focusMode,omitempty"`
MeteringMode string `xml:"wpml-meteringMode,omitempty"`
ReturnMode string `xml:"wpml-returnMode,omitempty"`
SamplingRate int `xml:"wpml-samplingRate,omitempty"`
ScanningMode string `xml:"wpml-scanningMode,omitempty"`
ImageFormat string `xml:"wpml-imageFormat"`
}
// ActionGroup contains actions to be executed at a waypoint.
type ActionGroup struct {
ActionGroupId int `xml:"wpml-actionGroupId"`
ActionGroupStartIndex int `xml:"wpml-actionGroupStartIndex"`
ActionGroupEndIndex int `xml:"wpml-actionGroupEndIndex"`
ActionGroupMode string `xml:"wpml-actionGroupMode"`
ActionTrigger ActionTrigger `xml:"wpml-actionTrigger"`
Actions []Action `xml:"wpml-action"`
}
// ActionTrigger contains trigger information for the action group.
type ActionTrigger struct {
ActionTriggerType string `xml:"wpml-actionTriggerType"`
}
// Action represents a single action to be executed.
type Action struct {
ActionId int `xml:"wpml-actionId"`
ActionActuatorFunc string `xml:"wpml-actionActuatorFunc"`
ActionActuatorFuncParam interface{} `xml:"wpml-actionActuatorFuncParam"`
}
// ActionActuatorFuncParam contains parameters for the action actuator function.
type TakePhotoActionActuatorFuncParam struct {
PayloadPositionIndex string `xml:"wpml-payloadPositionIndex"`
FileSuffix string `xml:"wpml-fileSuffix"`
PayloadLensIndex int `xml:"wpml-payloadLensIndex"`
UseGlobalPayloadLensIndex int `xml:"wpml-useGlobalPayloadLensIndex"`
}
type StartRecordActionActuatorFuncParam struct {
PayloadPositionIndex string `xml:"wpml-payloadPositionIndex"`
FileSuffix string `xml:"wpml-fileSuffix"`
PayloadLensIndex int `xml:"wpml-payloadLensIndex"`
UseGlobalPayloadLensIndex int `xml:"wpml-useGlobalPayloadLensIndex"`
}
type StopRecordActionActuatorFuncParam struct {
PayloadPositionIndex string `xml:"wpml-payloadPositionIndex"`
FileSuffix string `xml:"wpml-payloadLensIndex"`
}
type FocusActionActuatorFuncParam struct {
PayloadPositionIndex string `xml:"wpml-payloadPositionIndex"`
IsPointFocus string `xml:"wpml-isPointFocus"`
FocusX string `xml:"wpml-focusX"`
FocusY string `xml:"wpml-focusY"`
FocusRegionWidth string `xml:"wpml-focusRegionWidth"`
FocusRegionHeight string `xml:"wpml-focusRegionHeight"`
IsInfiniteFocus string `xml:"wpml-isInfiniteFocus"`
}
type ZoomActionActuatorFuncParam struct {
PayloadPositionIndex string `xml:"wpml-payloadPositionIndex"`
FocalLength string `xml:"wpml-focalLength"`
}
type CustomDirNameActionActuatorFuncParam struct {
PayloadPositionIndex string `xml:"wpml-payloadPositionIndex"`
DirectoryName string `xml:"wpml-directoryName"`
}
type GimbalRotateActionActuatorFuncParam struct {
PayloadPositionIndex int `xml:"wpml-payloadPositionIndex"`
GimbalHeadingYawBase string `xml:"wpml-gimbalHeadingYawBase"`
GimbalRotateMode string `xml:"wpml-gimbalRotateMode"`
GimbalPitchRotateEnable bool `xml:"wpml-gimbalPitchRotateEnable"`
GimbalPitchRotateAngle float64 `xml:"wpml-gimbalPitchRotateAngle"`
GimbalRollRotateEnable bool `xml:"wpml-gimbalRollRotateEnable"`
GimbalRollRotateAngle float64 `xml:"wpml-gimbalRollRotateAngle"`
GimbalYawRotateEnable bool `xml:"wpml-gimbalYawRotateEnable"`
GimbalYawRotateAngle float64 `xml:"wpml-gimbalYawRotateAngle"`
GimbalRotateTimeEnable bool `xml:"wpml-gimbalRotateTimeEnable"`
GimbalRotateTime float64 `xml:"wpml-gimbalRotateTime"`
}
type GimbalEvenlyRotateActionActuatorFuncParam struct {
GimbalPitchRotateAngle float64 `xml:"wpml-gimbalPitchRotateAngle"`
PayloadPositionIndex int `xml:"wpml-payloadPositionIndex"`
}
type RotateYawRotateActionActuatorFuncParam struct {
AircraftHeading float64 `xml:"wpml-aircraftHeading"`
AircraftPathMode int `xml:"wpml-aircraftPathMode"`
}
type AccurateShootActionActuatorFuncParam struct {
AircraftHeading float64 `xml:"wpml-aircraftHeading"`
AircraftPathMode int `xml:"wpml-aircraftPathMode"`
}
type HoverActionActuatorFuncParam struct {
HoverTime float64 `xml:"wpml-hoverTime"`
}
type OrientedShootActionActuatorFuncParam struct {
GimbalPitchRotateAngle float64 `xml:"wpml-gimbalPitchRotateAngle"`
GimbalYawRotateAngle float64 `xml:"wpml-gimbalYawRotateAngle"`
FocusX int `xml:"wpml-focusX"`
FocusY int `xml:"wpml-focusY"`
FocusRegionWidth int `xml:"wpml-focusRegionWidth"`
FocusRegionHeight int `xml:"wpml-focusRegionHeight"`
FocalLength float64 `xml:"wpml-focalLength"`
AircraftHeading float64 `xml:"wpml-aircraftHeading"`
AccurateFrameValid bool `xml:"wpml-accurateFrameValid"`
PayloadPositionIndex int `xml:"wpml-payloadPositionIndex"`
PayloadLensIndex string `xml:"wpml-payloadLensIndex"`
UseGlobalPayloadLensIndex bool `xml:"wpml-useGlobalPayloadLensIndex"`
TargetAngle float64 `xml:"wpml-targetAngle"`
ActionUUID string `xml:"wpml-actionUUID"`
ImageWidth int `xml:"wpml-imageWidth"`
ImageHeight int `xml:"wpml-imageHeight"`
AFPos int `xml:"wpml-AFPos"`
GimbalPort int `xml:"wpml-gimbalPort"`
OrientedCameraType int `xml:"wpml-orientedCameraType"`
OrientedFilePath string `xml:"wpml-orientedFilePath"`
OrientedFileMD5 string `xml:"wpml-orientedFileMD5"`
OrientedFileSize int `xml:"wpml-orientedFileSize"`
OrientedFileSuffix string `xml:"wpml-orientedFileSuffix"`
OrientedCameraApertue int `xml:"wpml-orientedCameraApertue"`
OrientedCameraLuminance int `xml:"wpml-orientedCameraLuminance"`
OrientedCameraShutterTime float64 `xml:"wpml-orientedCameraShutterTime"`
OrientedCameraISO int `xml:"wpml-orientedCameraISO"`
OrientedPhotoMode string `xml:"wpml-orientedPhotoMode"`
}
type PanoShotActionActuatorFuncParam struct {
PayloadPositionIndex int `xml:"wpml-payloadPositionIndex"`
PayloadLensIndex string `xml:"wpml-payloadLensIndex"`
UseGlobalPayloadLensIndex bool `xml:"wpml-useGlobalPayloadLensIndex"`
PanoShotSubMode string `xml:"wpml-panoShotSubMode"`
}
type RecordPointCloudActionActuatorFuncParam struct {
PayloadPositionIndex string `xml:"wpml-payloadPositionIndex"`
RecordPointCloudOperate string `xml:"wpml-recordPointCloudOperate"`
}
wpml或kml后缀的文件内容是xml格式,但标签结构是以【:】进行分割,导致无法通过xml表解析数据,故而需要将字符串中【wpml:】替换为【wpml-】可进行解析,如下图所示:
航线文件格式标准
https://developer.dji.com/doc/cloud-api-tutorial/cn/api-reference/dji-wpml/overview.html
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。