一般情况下gpsImgDirection即为图片的方向角,但是大疆图片的方位角不是,大疆图片的方位角是通过**exif.getAttribute("Xmp")**获取到图片的xml,然后再xml里获取到的方位角
具体代码如下:
let that = this
//图片的路径
let imgPath = 'XXXX'
// 仅支持 Android 端
if (plus.os.name !== 'Android') {
console.log("仅支持 Android 端读取 EXIF 信息");
return;
}
try {
// 1. 导入 Android 原生类
const ExifInterface = plus.android.importClass('android.media.ExifInterface');
// 2. 实例化 ExifInterface(传入图片路径)
const exif = new ExifInterface(imgPath);
// 3. 定义需要读取的 EXIF 字段(可扩展)
const exifFields = {
// 基础信息
orientation: ExifInterface.TAG_ORIENTATION, // 图片旋转角度
width: ExifInterface.TAG_IMAGE_WIDTH, // 图片宽度
height: ExifInterface.TAG_IMAGE_LENGTH, // 图片高度
mimeType: ExifInterface.TAG_MIME_TYPE, // 图片格式
dateTime: ExifInterface.TAG_DATETIME, // 拍摄时间
// 相机信息
make: ExifInterface.TAG_MAKE, // 设备制造商
model: ExifInterface.TAG_MODEL, // 设备型号
exposureTime: ExifInterface.TAG_EXPOSURE_TIME, // 曝光时间
fNumber: ExifInterface.TAG_F_NUMBER, // 光圈
iso: ExifInterface.TAG_ISO_SPEED_RATINGS, // ISO
focalLength: ExifInterface.TAG_FOCAL_LENGTH, // 焦距
// GPS 信息
gpsLatitude: ExifInterface.TAG_GPS_LATITUDE, // 纬度
gpsLatitudeRef: ExifInterface.TAG_GPS_LATITUDE_REF, // 纬度方向(N/S)
gpsLongitude: ExifInterface.TAG_GPS_LONGITUDE, // 经度
gpsLongitudeRef: ExifInterface.TAG_GPS_LONGITUDE_REF, // 经度方向(E/W)
gpsAltitude: ExifInterface.TAG_GPS_ALTITUDE, // 海拔
gpsAltitudeRef: ExifInterface.TAG_GPS_ALTITUDE_REF, // 海拔参考
gpsImgDirection: ExifInterface.TAG_GPS_IMG_DIRECTION, // 拍摄方位角(真北 / 磁北)浮点数(0~360),如 90.0 表示正东
gpsImgDirectionRef: ExifInterface.TAG_GPS_IMG_DIRECTION_REF, // 方位角参考基准 T= 真北,M= 磁北
};
// 4. 解析 EXIF 字段
const exifInfo = {};
for (const [key, tag] of Object.entries(exifFields)) {
try {
// 读取字段值(部分字段返回 null 表示无该信息)
const value = exif.getAttribute(tag);
// console.log("解析出的键值对信息", `${tag}: ${value}`)
if (value !== null) {
exifInfo[key] = value;
}
} catch (e) {
exifInfo[key] = '解析失败';
}
}
// 5. 解析 GPS 坐标为标准格式(度分秒转十进制)
if (exifInfo.gpsLatitude && exifInfo.gpsLatitudeRef) {
exifInfo.gpsLatitudeDecimal = that
.convertDmsToDecimal(
exifInfo.gpsLatitude,
exifInfo.gpsLatitudeRef
);
}
if (exifInfo.gpsLongitude && exifInfo
.gpsLongitudeRef) {
exifInfo.gpsLongitudeDecimal = that
.convertDmsToDecimal(
exifInfo.gpsLongitude,
exifInfo.gpsLongitudeRef
);
}
// 解析大疆图片的方位角
const xmpRawData = exif.getAttribute("Xmp");
const gimbalYaw = that.getDJIGimbalYaw(xmpRawData);
console.log('经纬度', exifInfo.gpsLatitudeDecimal, exifInfo.gpsLongitudeDecimal)
console.log('方位角', gimbalYaw)
} catch (err) {
console.log(`解析 EXIF 失败:${err.message}`)
}
convertDmsToDecimal方法
解析 GPS 坐标为标准格式(度分秒转十进制)
convertDmsToDecimal(dmsStr, ref) {
try {
// 拆分度、分、秒(如 "30/1,12/1,3456/100" → [30, 12, 34.56])
const parts = dmsStr.split(',').map(part => {
const [num, den] = part.split('/').map(Number);
return den === 0 ? 0 : num / den;
});
const [degrees, minutes, seconds] = parts;
// 计算十进制
let decimal = degrees + minutes / 60 + seconds / 3600;
// 南纬/西经取负数
if (ref === 'S' || ref === 'W') {
decimal = -decimal;
}
return Number(decimal.toFixed(6)); // 保留 6 位小数
} catch (e) {
return null;
}
},
getDJIGimbalYaw方法
获取到大疆图片的xml文件后,解析xml里的方位角
getDJIGimbalYaw(xmpRawData) {
try {
const pureXml = xmpRawData
.replace(/<\?xpacket begin[^>]*\?>/g, "")
.replace(/<\?xpacket end[^>]*\?>/g, "")
.trim();
// 1. 导入 Android 原生 XML 解析类
const XmlPullParser = plus.android.importClass('org.xmlpull.v1.XmlPullParser');
const XmlPullParserFactory = plus.android.importClass('org.xmlpull.v1.XmlPullParserFactory');
const StringReader = plus.android.importClass('java.io.StringReader');
// 2. 创建 XML 解析工厂和解析器
const factory = XmlPullParserFactory.newInstance();
// 注意:setNamespaceAware 也需要用 invoke 调用(避免潜在报错)
plus.android.invoke(factory, "setNamespaceAware", true);
const parser = plus.android.invoke(factory, "newPullParser"); // 通过 invoke 创建 parser
const reader = new StringReader(pureXml);
// 通过 plus.android.invoke 调用 setInput 方法
plus.android.invoke(parser, "setInput", reader);
// 3. 定义需要获取的方位角字段(大疆 XMP 中常见的方位角字段)
let gimbalYaw = null;
const droneDjiNs = "http://www.dji.com/drone-dji/1.0/"; // 大疆命名空间
const rdfNs = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; // rdf命名空间
// 4. 开始解析 XML
let eventType = plus.android.invoke(parser, "getEventType");
const END_DOCUMENT = plus.android.getAttribute(XmlPullParserFactory, "END_DOCUMENT"); // 获取常量
const START_TAG = plus.android.getAttribute(XmlPullParserFactory, "START_TAG"); // 获取常量
while (eventType !== XmlPullParser.END_DOCUMENT) {
if (eventType === XmlPullParser.START_TAG) {
const tagName = plus.android.invoke(parser, "getName");
const currentNs = plus.android.invoke(parser, "getNamespace");
// 匹配rdf:Description标签(这是包含大疆属性的目标标签)
if (currentNs === rdfNs && tagName === "Description") {
// 提取drone-dji命名空间下的GimbalYawDegree属性
gimbalYaw = plus.android.invoke(parser, "getAttributeValue", droneDjiNs,
"GimbalYawDegree");
// 提取到后直接break,无需继续遍历
break;
}
}
eventType = plus.android.invoke(parser, "next"); // 遍历下一个节点
}
// 5. 处理解析结果(转为数字类型,方便后续使用)
gimbalYaw = gimbalYaw ? parseFloat(gimbalYaw) : null;
return gimbalYaw
} catch (error) {
console.error('Android 原生解析 XMP 方位角失败:', error.message);
}
},