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);
    }
},
相关推荐
头发还在的女程序员1 天前
基于 Spring Boot + Vue 3 + uni-app 的灵活用工系统:四端架构与业务流程设计
vue.js·spring boot·uni-app·灵活用工·灵工系统
Dumbness3 天前
订阅消息弹窗必须在用户点击里调:联调实验室里 timing 和 Mock 支付通知踩的坑
javascript·uni-app
码间留白4 天前
uni-app 鸿蒙离线打包:一个改动从源码到真机要穿过几条链
uni-app·鸿蒙·deveco·离线打包·uts插件
多客:duokelijie5 天前
代练订单系统选型思路:跨品类游戏接入、订单分层管理、自动结算逻辑拆解!
数据库·mysql·微信小程序·前端框架·uni-app·php·持续部署
码间留白5 天前
鸿蒙端对接 BLE 血压计:从一次握手到读数入库(二)设备篇
物联网·uni-app·鸿蒙·蓝牙ble·血压计
华玥作者6 天前
Vue 3 + TypeScript + uni-app 组件库推荐:hy-app(华玥组件库)
vue.js·typescript·uni-app
FinelyYang6 天前
uniapp使用kkfileview在线预览ppt,excel,word,pdf类型文件
pdf·uni-app·powerpoint
2501_916008896 天前
iPhone 设备信息查看,用 KeyMob 看型号、序列号、电池健康和 UDID
android·ios·小程序·https·uni-app·iphone·webview
ModyQyW7 天前
vite-plugin-uni-manifest 0.7:在 uni-app 读取之前生成
前端·uni-app
lyz2468597 天前
uniapp vue3 小程序开发注意事项
uni-app