uniapp app端解析图片的经纬度、方位角

一般情况下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);
    }
},
相关推荐
梦曦i7 小时前
@meng-xi/uni-router 未来展望:夯实基础、深化体验、探索前沿
前端·uni-app
2501_9160074712 小时前
使用Apple Dashboards显示和自定iOS应用性能指标与数据可视化指南
android·ios·小程序·https·uni-app·iphone·webview
爱折腾的编程老炮14 小时前
潮玩小程序订单模型-一张表装下所有玩法
spring boot·小程序·uni-app
两个人的幸福online14 小时前
UniApp 对接 IM 即时通讯全攻略(下篇)
uni-app
2501_9151063215 小时前
Flutter iOS混淆打包详细教程与步骤
android·flutter·ios·小程序·uni-app·iphone·webview
DK185838322521 天前
【源码开源部署】电竞代练护航陪玩小程序:全游戏服务平台完整解决方案(附宝塔部署教程)
游戏·微信小程序·uni-app·开源·php
码云数智-大飞1 天前
2026 年跨端开发决战:小程序原生 vs uni-app vs Taro 深度对比
小程序·uni-app·taro
BS30813_vx1 天前
基于 Express + uni-app 的养老院管理系统设计与实现
后端·uni-app·express
bug总结2 天前
uniapp 微信小程序请求规范
微信小程序·小程序·uni-app
梦曦i3 天前
unix-router v0.1.0 首发:uni-app x 的 vue-router 风格路由库
前端·uni-app