1. 为什么UniApp定位总是不准最近在做一个打卡功能时遇到了一个让人头疼的问题明明在同一个位置iOS和Android设备获取的定位坐标却相差几百米。这让我不得不深入研究UniApp的定位机制特别是getLocation这个API在不同平台下的表现差异。经过反复测试发现当使用typewgs84时虽然iOS和Android都能返回坐标但实际位置偏差很大。而改用typegcj02后Android设备定位精准了iOS却直接报错-1504。这种跨平台的不一致性让很多开发者都踩过坑。其实问题的根源在于坐标系的不同。WGS84是国际通用的GPS坐标系而GCJ02是国内特有的加密坐标系。由于政策原因国内地图服务都必须使用GCJ02坐标系。这就导致了直接获取的WGS84坐标在国内地图上显示时会出现明显的偏移。2. 手动转换坐标系的实战方案2.1 先获取后转换的核心思路经过多次尝试我发现最稳妥的方案是先用typewgs84获取原始坐标再手动转换为GCJ02坐标系。这个方法看似绕了个弯实际上解决了跨平台的兼容性问题。具体来说这个方案有三大优势兼容性强iOS和Android都能正常获取WGS84坐标精度更高转换后的GCJ02坐标比直接获取的更精准灵活性好可以在代码层面控制转换逻辑2.2 完整代码实现下面是我在实际项目中使用的完整转换代码包含了所有必要的工具函数// 坐标转换工具类 class CoordinateConverter { static wgs84ToGcj02(lng, lat) { if (this.outOfChina(lng, lat)) { return [lng, lat] } const PI 3.1415926535897932384626 const a 6378245.0 const ee 0.00669342162296594323 let dlat this.transformLat(lng - 105.0, lat - 35.0) let dlng this.transformLng(lng - 105.0, lat - 35.0) const radlat lat / 180.0 * PI let magic Math.sin(radlat) magic 1 - ee * magic * magic const sqrtmagic Math.sqrt(magic) dlat (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI) dlng (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI) const mglat lat dlat const mglng lng dlng return [mglng, mglat] } static outOfChina(lng, lat) { return (lng 72.004 || lng 137.8347) || (lat 0.8293 || lat 55.8271) } static transformLat(lng, lat) { const PI 3.1415926535897932384626 let ret -100.0 2.0 * lng 3.0 * lat 0.2 * lat * lat 0.1 * lng * lat 0.2 * Math.sqrt(Math.abs(lng)) ret (20.0 * Math.sin(6.0 * lng * PI) 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0 ret (20.0 * Math.sin(lat * PI) 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0 ret (160.0 * Math.sin(lat / 12.0 * PI) 320.0 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0 return ret } static transformLng(lng, lat) { const PI 3.1415926535897932384626 let ret 300.0 lng 2.0 * lat 0.1 * lng * lng 0.1 * lng * lat 0.1 * Math.sqrt(Math.abs(lng)) ret (20.0 * Math.sin(6.0 * lng * PI) 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0 ret (20.0 * Math.sin(lng * PI) 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0 ret (150.0 * Math.sin(lng / 12.0 * PI) 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0 return ret } } // 在UniApp中使用 getLocation() { uni.getLocation({ type: wgs84, isHighAccuracy: true, geocode: true, success: (res) { const [longitude, latitude] CoordinateConverter.wgs84ToGcj02( res.longitude, res.latitude ) console.log(转换后坐标:, longitude, latitude) // 使用转换后的坐标进行后续操作 }, fail: (err) { console.error(获取位置失败:, err) } }) }3. 两种方案的对比测试3.1 精度对比实测为了验证手动转换方案的准确性我做了多组对比测试测试场景直接GCJ02(Android)WGS84转GCJ02实际位置偏差北京市中心116.404,39.915116.4042,39.915110米内上海陆家嘴121.499,31.239121.4989,31.239215米内广州塔113.324,23.106113.3241,23.106312米内测试结果显示手动转换后的坐标精度反而比直接获取GCJ02坐标更高这可能是由于UniApp内部对GCJ02坐标做了额外处理导致的。3.2 兼容性对比在设备兼容性方面两种方案的表现差异明显直接使用GCJ02Android正常工作iOS报错-1504无法获取位置WGS84手动转换Android正常工作iOS正常工作显然手动转换方案在跨平台兼容性上完胜。这也是我最终选择这个方案的主要原因。4. 高德地图集成的注意事项4.1 正确配置manifest.json使用高德地图时需要在manifest.json中正确配置{ app-plus: { modules: { Geolocation: {}, Maps: {} }, distribute: { android: { permissions: [ uses-permission android:name\android.permission.ACCESS_COARSE_LOCATION\/, uses-permission android:name\android.permission.ACCESS_FINE_LOCATION\/ ] }, ios: { urlschemewhitelist: [iosamap], privacyDescription: { NSLocationWhenInUseUsageDescription: 需要获取您的位置信息 } } } } }4.2 申请正确的API Key在高德开放平台申请Key时需要注意必须同时申请Android和iOS的Key确保包名和Bundle ID填写正确启用所有必要的地图服务API4.3 真机调试的坑在真机调试时有几个常见问题需要注意iOS设备需要在设置-隐私-定位服务中开启定位权限Android 10需要动态申请精确定位权限模拟器上的定位结果可能与真机有差异5. 性能优化与异常处理5.1 缓存策略优化频繁调用getLocation会影响性能建议实现坐标缓存let lastLocation null let lastUpdateTime 0 async function getCachedLocation() { const now Date.now() if (lastLocation now - lastUpdateTime 300000) { // 5分钟缓存 return lastLocation } try { const location await new Promise((resolve, reject) { uni.getLocation({ type: wgs84, success: resolve, fail: reject }) }) const converted CoordinateConverter.wgs84ToGcj02( location.longitude, location.latitude ) lastLocation { longitude: converted[0], latitude: converted[1], original: location } lastUpdateTime now return lastLocation } catch (err) { console.error(获取位置失败:, err) if (lastLocation) return lastLocation // 降级返回缓存 throw err } }5.2 完善的错误处理在实际使用中应该处理各种异常情况async function getSafeLocation() { try { const location await getCachedLocation() // 检查坐标是否有效 if (Math.abs(location.longitude) 180 || Math.abs(location.latitude) 90) { throw new Error(无效的坐标值) } return location } catch (err) { console.error(获取位置失败:, err) // 根据错误类型提供友好的用户提示 if (err.errMsg.includes(auth deny)) { uni.showToast({ title: 请开启定位权限, icon: none }) } else if (err.errMsg.includes(timeout)) { uni.showToast({ title: 定位超时请重试, icon: none }) } else { uni.showToast({ title: 获取位置失败, icon: none }) } // 返回默认位置或抛出错误 return { longitude: 116.404, latitude: 39.915 } } }这套方案在实际项目中运行稳定特别是在打卡、导航等对定位精度要求较高的场景下表现优异。经过多次迭代优化目前误差可以控制在20米以内完全满足商业应用的需求。