import _ from 'lodash' import JSZIP from 'jszip' import { JSONToXML } from './kmz' import { getDateFromTimestamp } from '@/utils/date' export default function kmzFile() { // 云台信息 const droneModel = { 52: '0', 53: '1', 66: '0', 67: '1', 80: '0', 81: '1', } // 创建图斑航线 const create = async (waylineBasicInfo, lnglats, spotList) => { try { const date = new Date() const username = window.localStorage.getItem('bs_username') const updateTime = date.getTime() const fileName = '图斑航线规划_' + date.getTime() // 无人机机型、云台型号、航线高度 const { droneEnumValue, payloadEnumValue, height } = waylineBasicInfo const defaultHeight = height || 100 // 起始点位 (参考点位-具体以机场为准) const startPoint = lnglats[0] const takeOffPoint = [startPoint[1], startPoint[0], 0].join(',') // template.xml对象模版 const fileTemplate = { author: { '#text': username }, createTime: { '#text': updateTime }, updateTime: { '#text': updateTime }, missionConfig: { // 飞向首航点模式 flyToWaylineMode: { '#text': 'safely' }, // 航线结束动作 finishAction: { '#text': 'goHome' }, // 失控是否继续执行航线 exitOnRCLost: { '#text': 'goContinue' }, // 失控动作类型 executeRCLostAction: { '#text': 'goBack' }, // 安全起飞高度 takeOffSecurityHeight: { '#text': 20 }, // 参考起飞点 takeOffRefPoint: { '#text': takeOffPoint }, // 参考起飞点海拔高度 takeOffRefPointAGLHeight: { '#text': 0 }, // 全局航线过渡速度 globalTransitionalSpeed: { '#text': 10 }, // 全局返航高度 globalRTHHeight: { '#text': defaultHeight }, // 无人机参数 droneInfo: { droneEnumValue: { '#text': droneEnumValue }, droneSubEnumValue: { '#text': droneModel[payloadEnumValue], }, }, // 云台参数 payloadInfo: { payloadEnumValue: { '#text': payloadEnumValue }, payloadSubEnumValue: { '#text': droneModel[payloadEnumValue], }, payloadPositionIndex: { '#text': 0 }, }, }, Folder: { // 预定义模板类型 - 航点飞行 templateType: { '#text': 'waypoint' }, // 是否使用全局航线过渡速度 useGlobalTransitionalSpeed: { '#text': 0 }, templateId: { '#text': 0 }, // 坐标系参数信息 waylineCoordinateSysParam: { // 经纬度坐标系 - 固定值 coordinateMode: { '#text': 'WGS84' }, // 航点高程参考平面 - 相对高度 heightMode: { '#text': 'relativeToStartPoint' }, }, // 全局航线飞行速度 autoFlightSpeed: { '#text': 10 }, // 云台俯仰角控制模式 gimbalPitchMode: { '#text': 'usePointSetting' }, // 全局高度 globalHeight: { '#text': '100' }, // 全局偏航角模式参数 - 可使用当前默认值 globalWaypointHeadingParam: { waypointHeadingMode: { '#text': 'followWayline' }, waypointHeadingAngle: { '#text': 0 }, waypointPoiPoint: { '#text': '0.000000,0.000000,0.000000', }, waypointHeadingPathMode: { '#text': 'followBadArc' }, waypointHeadingPoiIndex: { '#text': 0 }, }, // 全局航点类型 - 直线飞行,飞行器到点停 globalWaypointTurnMode: { '#text': 'toPointAndStopWithDiscontinuityCurvature', }, // 全局航段轨迹是否尽量贴合直线 globalUseStraightLine: { '#text': 1 }, // 航点点位信息 - 位置、事件等 Placemark: [], // payloadParam: { // payloadPositionIndex: { '#text': 0 }, // focusMode: { '#text': 'firstPoint' }, // meteringMode: { '#text': 'average' }, // returnMode: { '#text': 'singleReturnFirst' }, // samplingRate: { '#text': '240000' }, // scanningMode: { '#text': 'repetitive' }, // imageFormat: { '#text': 'wide,zoom' }, // }, }, } // 航点创建 const polygonNo = { no: '', index: 0, } lnglats.forEach((lnglat, index) => { const obj = spotList.find(spot => spot.dkfw.find(item => _.isEqual(item, lnglat))) let fileSuffix = '' if (obj) { if (polygonNo.no === obj.dkbh) { polygonNo.index++ } else { polygonNo.no = obj.dkbh polygonNo.index = 1 } fileSuffix = `图斑航线拍摄(${obj.dkbh}-${polygonNo.index})` } else { fileSuffix = '图斑航线拍摄(起点)' } const placemark = { Point: { // 航点经纬度 coordinates: { '#text': `${lnglat[0]},${lnglat[1]}` }, }, // 航点index index: { '#text': index }, // 全局航线高度(椭球高) ellipsoidHeight: { '#text': defaultHeight }, // 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度) height: { '#text': defaultHeight }, // 航点飞行速度 waypointSpeed: { '#text': 10 }, // 沿航线方向 - 可使用当前默认值 waypointHeadingParam: { waypointHeadingMode: { '#text': 'followWayline' }, waypointHeadingAngle: { '#text': 0 }, waypointPoiPoint: { '#text': '0.000000,0.000000,0.000000', }, waypointHeadingPathMode: { '#text': 'followBadArc' }, waypointHeadingPoiIndex: { '#text': 0 }, }, // 航点类型 waypointTurnParam: { waypointTurnMode: { '#text': 'toPointAndStopWithDiscontinuityCurvature', }, waypointTurnDampingDist: { '#text': 0.2 }, }, // 是否全局参数 useGlobalHeight: { '#text': 1 }, useGlobalSpeed: { '#text': 1 }, useGlobalHeadingParam: { '#text': 1 }, useGlobalTurnParam: { '#text': 1 }, useStraightLine: { '#text': 1 }, // 事件组 actionGroup: { actionGroupId: { '#text': index }, actionGroupStartIndex: { '#text': index }, actionGroupEndIndex: { '#text': index }, actionGroupMode: { '#text': 'sequence' }, actionTrigger: { actionTriggerType: { '#text': 'reachPoint' }, }, // 单个事件 - 如果是多个事件action可改成数组 action: [{}] action: { actionId: { '#text': 0 }, actionActuatorFunc: { '#text': 'takePhoto' }, actionActuatorFuncParam: { fileSuffix: { '#text': fileSuffix }, payloadPositionIndex: { '#text': '0' }, useGlobalPayloadLensIndex: { '#text': '1' }, }, }, }, } fileTemplate.Folder.Placemark.push(placemark) }) const fileWaylines = waylinesContext(fileTemplate) return { fileName, template: fileTemplate, waylines: fileWaylines, } } catch (error) { console.log(error) return false } } // 创建正常航线 const createNoramlWaylines = (waylineBasicInfo, lnglats) => { try { const date = new Date() const username = window.localStorage.getItem('bs_username') const updateTime = date.getTime() const { year, month, day, hours, minutes } = getDateFromTimestamp(updateTime) const fileName = '新建航线_' + year + month + day + hours + minutes // 无人机机型、云台型号、航线高度 const { droneEnumValue, payloadEnumValue, height, waylineName } = waylineBasicInfo const defaultHeight = height || 100 // 起始点位 (参考点位-具体以机场为准) const startPoint = lnglats[0] const takeOffPoint = [startPoint.latitude, startPoint.longitude, 0].join(',') // template.xml对象模版 const fileTemplate = { author: { '#text': username }, createTime: { '#text': updateTime }, updateTime: { '#text': updateTime }, missionConfig: { // 飞向首航点模式 flyToWaylineMode: { '#text': 'safely' }, // 航线结束动作 finishAction: { '#text': 'goHome' }, // 失控是否继续执行航线 exitOnRCLost: { '#text': 'goContinue' }, // 失控动作类型 executeRCLostAction: { '#text': 'goBack' }, // 安全起飞高度 takeOffSecurityHeight: { '#text': 20 }, // 参考起飞点 takeOffRefPoint: { '#text': takeOffPoint }, // 参考起飞点海拔高度 takeOffRefPointAGLHeight: { '#text': 0 }, // 全局航线过渡速度 globalTransitionalSpeed: { '#text': 10 }, // 全局返航高度 globalRTHHeight: { '#text': defaultHeight }, // 无人机参数 droneInfo: { droneEnumValue: { '#text': droneEnumValue }, droneSubEnumValue: { '#text': droneModel[payloadEnumValue], }, }, // 是否自动绕行(暂未验证M30T机型使用此参数后会不会无法起飞) autoRerouteInfo: { missionAutoRerouteMode: { '#text': 1 }, transitionalAutoRerouteMode: { '#text': 1 }, }, // 云台参数 payloadInfo: { payloadEnumValue: { '#text': payloadEnumValue }, payloadSubEnumValue: { '#text': droneModel[payloadEnumValue], }, payloadPositionIndex: { '#text': 0 }, }, }, Folder: { // 预定义模板类型 - 航点飞行 templateType: { '#text': 'waypoint' }, // 是否使用全局航线过渡速度 useGlobalTransitionalSpeed: { '#text': 0 }, templateId: { '#text': 0 }, // 坐标系参数信息 waylineCoordinateSysParam: { // 经纬度坐标系 - 固定值 coordinateMode: { '#text': 'WGS84' }, // 航点高程参考平面 - 相对高度 heightMode: { '#text': 'relativeToStartPoint' }, }, // 全局航线飞行速度 autoFlightSpeed: { '#text': 10 }, // 云台俯仰角控制模式 gimbalPitchMode: { '#text': 'usePointSetting' }, // 全局高度 globalHeight: { '#text': '100' }, // 全局偏航角模式参数 - 可使用当前默认值 globalWaypointHeadingParam: { waypointHeadingMode: { '#text': 'followWayline' }, waypointHeadingAngle: { '#text': 0 }, waypointPoiPoint: { '#text': '0.000000,0.000000,0.000000', }, waypointHeadingPathMode: { '#text': 'followBadArc' }, waypointHeadingPoiIndex: { '#text': 0 }, }, // 全局航点类型 - 直线飞行,飞行器到点停 globalWaypointTurnMode: { '#text': 'toPointAndStopWithDiscontinuityCurvature', }, // 全局航段轨迹是否尽量贴合直线 globalUseStraightLine: { '#text': 1 }, // 航点点位信息 - 位置、事件等 Placemark: [], payloadParam: { payloadPositionIndex: { '#text': 0 }, focusMode: { '#text': 'firstPoint' }, meteringMode: { '#text': 'average' }, returnMode: { '#text': 'singleReturnFirst' }, samplingRate: { '#text': '240000' }, scanningMode: { '#text': 'repetitive' }, imageFormat: { '#text': 'wide,zoom' }, }, }, } lnglats.forEach((lnglat, index) => { const placemark = { Point: { // 航点经纬度 coordinates: { '#text': `${lnglat.longitude},${lnglat.latitude}`, }, }, // 航点index index: { '#text': index }, // 全局航线高度(椭球高) ellipsoidHeight: { '#text': lnglat.height }, // 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度) height: { '#text': lnglat.height }, // 航点飞行速度 waypointSpeed: { '#text': lnglat?.speed || 10 }, // 沿航线方向 - 可使用当前默认值 waypointHeadingParam: { waypointHeadingMode: { '#text': 'followWayline' }, waypointHeadingAngle: { '#text': 0 }, waypointPoiPoint: { '#text': '0.000000,0.000000,0.000000', }, waypointHeadingPathMode: { '#text': 'followBadArc' }, waypointHeadingPoiIndex: { '#text': 0 }, }, // 航点类型 waypointTurnParam: { waypointTurnMode: { '#text': 'toPointAndStopWithDiscontinuityCurvature', }, waypointTurnDampingDist: { '#text': 0.2 }, }, // 是否全局参数 useGlobalHeight: { '#text': 1 }, useGlobalSpeed: { '#text': 0 }, useGlobalHeadingParam: { '#text': 1 }, useGlobalTurnParam: { '#text': 1 }, useStraightLine: { '#text': 1 }, } if (lnglat?.actions) { const actionGroup = { actionGroupId: { '#text': index }, actionGroupStartIndex: { '#text': index }, actionGroupEndIndex: { '#text': index }, actionGroupMode: { '#text': 'sequence' }, actionTrigger: { actionTriggerType: { '#text': 'reachPoint' }, }, // 单个事件 - 如果是多个事件action可改成数组 action: [{}] // action: [{ // actionId: { '#text': 0 }, // actionActuatorFunc: { '#text': '' }, // actionActuatorFuncParam: {}, // }], action: [], } lnglat?.actions.forEach((action, index) => { const singleAction = { actionId: { '#text': index }, actionActuatorFunc: { '#text': '' }, actionActuatorFuncParam: {}, } // actionGroup.action['actionActuatorFunc'] = { '#text': action.key } // action.params.forEach(p => { // if (p.value || p.value !== '') { // actionGroup.action['actionActuatorFuncParam'][p.key] = { '#text': p.value } // } // }) singleAction['actionActuatorFunc'] = { '#text': action.key, } action.params.forEach(p => { if (p.value || p.value !== '') { singleAction['actionActuatorFuncParam'][p.key] = { '#text': p.value, } } }) if (lnglat?.actions.length > 1) { actionGroup.action.push(singleAction) } else { actionGroup.action = singleAction } }) placemark.actionGroup = actionGroup } fileTemplate.Folder.Placemark.push(placemark) }) const fileWaylines = waylinesContext(fileTemplate) return { fileName: waylineName || fileName, template: fileTemplate, waylines: fileWaylines, } } catch (error) { console.log(error) return error } } // 创建面状航线 const createPlanarWayline = (waylineBasicInfo, lnglats) => {} // 创建点状航线 const createPointWayLines = (waylineBasicInfo, points, settingInfo, dockPosition) => { try { const { droneEnumValue, payloadEnumValue, waylineName } = waylineBasicInfo const { imageFormat, heightMode, globalHeight, autoFlightSpeed, gimbalPitchMode, waypointTurnMode, waypointHeadingMode, finishAction, globalTransitionalSpeed, } = settingInfo const date = new Date() const username = window.localStorage.getItem('bs_username') const updateTime = date.getTime() const { year, month, day, hours, minutes } = getDateFromTimestamp(updateTime) const fileName = '新建航线_' + year + month + day + hours + minutes // 无人机机型、云台型号、航线高度 const defaultHeight = 100 // 起始点位 (参考点位-具体以机场为准) const takeOffPoint = [dockPosition.latitude, dockPosition.longitude, 0].join(',') // template.xml对象模版 const fileTemplate = { author: { '#text': username }, createTime: { '#text': updateTime }, updateTime: { '#text': updateTime }, missionConfig: { // 飞向首航点模式 flyToWaylineMode: { '#text': 'safely' }, // 航线结束动作 finishAction: { '#text': finishAction || 'goHome' }, // 失控是否继续执行航线 exitOnRCLost: { '#text': 'executeLostAction' }, // 失控动作类型 executeRCLostAction: { '#text': 'goBack' }, // 安全起飞高度 takeOffSecurityHeight: { '#text': 120 }, // 参考起飞点 takeOffRefPoint: { '#text': takeOffPoint }, // 参考起飞点海拔高度 takeOffRefPointAGLHeight: { '#text': 0 }, // 全局航线过渡速度 globalTransitionalSpeed: { '#text': globalTransitionalSpeed || 10 }, // 全局返航高度 globalRTHHeight: { '#text': defaultHeight }, // 无人机参数 droneInfo: { droneEnumValue: { '#text': droneEnumValue }, droneSubEnumValue: { '#text': droneModel[payloadEnumValue], }, }, // 是否自动绕行(暂未验证M30T机型使用此参数后会不会无法起飞) autoRerouteInfo: { missionAutoRerouteMode: { '#text': 1 }, transitionalAutoRerouteMode: { '#text': 1 }, }, // 云台参数 payloadInfo: { payloadEnumValue: { '#text': payloadEnumValue }, payloadSubEnumValue: { '#text': droneModel[payloadEnumValue], }, payloadPositionIndex: { '#text': 0 }, }, }, Folder: { // 预定义模板类型 - 航点飞行 templateType: { '#text': 'waypoint' }, // 是否使用全局航线过渡速度 useGlobalTransitionalSpeed: { '#text': 0 }, templateId: { '#text': 0 }, // 坐标系参数信息 waylineCoordinateSysParam: { // 经纬度坐标系 - 固定值 coordinateMode: { '#text': 'WGS84' }, // 航点高程参考平面 - 相对高度 heightMode: heightMode, }, // 全局航线飞行速度 autoFlightSpeed: { '#text': autoFlightSpeed || 10 }, // 云台俯仰角控制模式 gimbalPitchMode: { '#text': gimbalPitchMode || 'manual' }, // 全局高度 globalHeight: { '#text': globalHeight || '100' }, // 全局偏航角模式参数 - 可使用当前默认值 globalWaypointHeadingParam: { waypointHeadingMode: { '#text': waypointHeadingMode || 'followWayline' }, waypointHeadingAngle: { '#text': 0 }, waypointPoiPoint: { '#text': '0.000000,0.000000,0.000000', }, waypointHeadingPathMode: { '#text': 'followBadArc' }, waypointHeadingPoiIndex: { '#text': 0 }, }, // 全局航点类型 - 直线飞行,飞行器到点停 globalWaypointTurnMode: { '#text': waypointTurnMode || 'toPointAndStopWithDiscontinuityCurvature', }, // 全局航段轨迹是否尽量贴合直线 globalUseStraightLine: { '#text': 1 }, // 航点点位信息 - 位置、事件等 Placemark: [], payloadParam: { payloadPositionIndex: { '#text': 0 }, focusMode: { '#text': 'firstPoint' }, meteringMode: { '#text': 'average' }, returnMode: { '#text': 'singleReturnFirst' }, samplingRate: { '#text': '240000' }, scanningMode: { '#text': 'repetitive' }, imageFormat: imageFormat, }, }, } points.length && points.forEach((detail, ind) => { const { Point, index, height, ellipsoidHeight, actionGroup, waypointSpeed } = detail const placemark = { Point, // 航点index index, // 全局航线高度(椭球高) ellipsoidHeight, // 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度) height, // 航点飞行速度 waypointSpeed: { '#text': waypointSpeed || 10 }, // 沿航线方向 - 可使用当前默认值 waypointHeadingParam: { waypointHeadingMode: { '#text': waypointHeadingMode || 'followWayline', }, waypointHeadingAngle: { '#text': 0 }, waypointPoiPoint: { '#text': '0.000000,0.000000,0.000000', }, waypointHeadingPathMode: { '#text': 'followBadArc' }, waypointHeadingPoiIndex: { '#text': 0 }, }, // 航点类型 waypointTurnParam: { waypointTurnMode: { '#text': waypointTurnMode || 'toPointAndStopWithDiscontinuityCurvature', }, waypointTurnDampingDist: { '#text': 0.2 }, }, // 是否全局参数 useGlobalHeight: { '#text': 1 }, useGlobalSpeed: { '#text': 0 }, useGlobalHeadingParam: { '#text': 1 }, useGlobalTurnParam: { '#text': 1 }, useStraightLine: { '#text': 1 }, } if (actionGroup) { placemark.actionGroup = actionGroup } fileTemplate.Folder.Placemark.push(placemark) }) const fileWaylines = waylinesContext(fileTemplate) return { fileName: waylineName || fileName, template: fileTemplate, waylines: fileWaylines, } } catch (error) { console.log(error) return error } } const save = async fileInfo => { const JsZip = new JSZIP() const { fileName, template, waylines } = fileInfo const templateXml = JSONToXML(template, '', true) const waylinesWpml = JSONToXML(waylines, '', true) JsZip.file('wpmz/template.kml', templateXml) JsZip.file('wpmz/waylines.wpml', waylinesWpml) const fileBlob = await JsZip.generateAsync({ type: 'blob' }) // saveAs(kmzFile, `${fileName}.kmz`) return fileBlob } const waylinesContext = templateJson => { const waylinesObj = { // 标准模版 missionConfig: { flyToWaylineMode: null, finishAction: null, exitOnRCLost: null, executeRCLostAction: null, takeOffSecurityHeight: null, globalTransitionalSpeed: null, globalRTHHeight: null, droneInfo: null, payloadInfo: null, }, Folder: { templateId: null, executeHeightMode: null, waylineId: null, autoFlightSpeed: null, /* waylines.wpml和template.xml的Placemark通用 不同点: waylineId = templateId ellipsoidHeight、 height 替换成 executeHeight */ Placemark: null, }, } Object.keys(waylinesObj).forEach(key => { if (Object.prototype.toString.call(waylinesObj[key]) === '[object Object]') { Object.keys(waylinesObj[key]).forEach(item => { waylinesObj[key][item] = templateJson[key][item] if (item === 'executeHeightMode') { waylinesObj[key][item] = templateJson[key].waylineCoordinateSysParam.heightMode } if (item === 'waylineId') { waylinesObj[key][item] = templateJson[key].templateId } if (item === 'Placemark') { const placemarks = _.cloneDeep(templateJson[key][item]) placemarks.forEach(placemark => { placemark.executeHeight = { '#text': placemark.ellipsoidHeight?.['#text'] || '', } delete placemark.ellipsoidHeight delete placemark.height }) waylinesObj[key][item] = placemarks } }) } else { waylinesObj[key] = templateJson[key] } }) return waylinesObj } return { create, createNoramlWaylines, createPlanarWayline, createPointWayLines, save, } }