> 技术文档 > 鸿蒙OS&UniApp 实现的地图定位与导航功能#三方框架 #Uniapp_uniapp地图定位

鸿蒙OS&UniApp 实现的地图定位与导航功能#三方框架 #Uniapp_uniapp地图定位


UniApp 实现的地图定位与导航功能

随着移动互联网的发展,地图定位与导航功能已成为众多应用的标配。本文将详细介绍如何在 UniApp 框架下实现地图定位与导航功能,并探讨如何适配鸿蒙系统,助力开发者打造更加流畅的地图体验。

前言

最近在做一个外勤签到应用,需要实现地图定位、标记和导航功能。由于产品需要覆盖安卓、iOS、鸿蒙等多个平台,我们选择了UniApp作为开发框架。其中对鸿蒙系统的适配是一个新挑战,经过一番摸索,总结出了一套可行的实现方案,今天就把这个过程分享给大家。

技术选型

在实现地图功能前,需要先明确技术选型:

  1. 地图服务商:高德地图、百度地图、腾讯地图
  2. UniApp地图组件:map组件 + plus.maps模块
  3. 鸿蒙系统适配:HarmonyOS地图服务

经过评估,我选择了高德地图作为底层服务,主要考虑因素有:

  • 高德地图在国内覆盖面广,数据相对精准
  • 接口丰富,满足定位、标记、路线规划等需求
  • 与鸿蒙系统兼容性较好,HMS Core中的地图服务与高德地图接口相似
  • uniapp对高德地图支持较好

环境准备

在正式开发前,我们需要完成以下准备工作:

  1. 创建UniApp项目
  2. 申请高德地图开发者账号并创建应用,获取Key
  3. 配置SDK权限

高德地图Key申请

  1. 注册高德开发者账号:https://lbs.amap.com/
  2. 创建应用,分别为Android和iOS平台申请Key
  3. 鸿蒙系统可以使用Android的Key,或者单独申请HMS Core地图服务的Key

项目配置

manifest.json中配置相关权限和Key:

{ \"app-plus\": { \"modules\": { \"Maps\": {} }, \"distribute\": { \"android\": { \"permissions\": [ \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\" ], \"abiFilters\": [\"armeabi-v7a\", \"arm64-v8a\"] }, \"ios\": { \"UIBackgroundModes\": [\"location\"], \"urlSchemePrefix\": \"amap\", \"maps\": { \"amap\": { \"appkey\": \"你的iOS平台高德Key\" } } }, \"sdkConfigs\": { \"maps\": { \"amap\": { \"appkey_android\": \"你的安卓平台高德Key\", \"appkey_ios\": \"你的iOS平台高德Key\" } } } } }}

对于鸿蒙系统,还需要额外添加HMS Core相关配置:

{ \"app-plus\": { \"distribute\": { \"android\": { \"pushconfig\": { \"hms\": { \"appid\": \"你的HMS Core AppID\", \"appkey\": \"你的HMS Core AppKey\", \"clientid\": \"你的HMS Core ClientID\", \"clientsecret\": \"你的HMS Core ClientSecret\" } } } } }}

基础地图功能实现

首先,我们来实现基础的地图展示和定位功能。创建一个map.vue文件:

           export default { data() { return { // 地图中心点 latitude: 39.908823, longitude: 116.397470, // 标记点 markers: [], // 路线 polyline: [], // 是否显示路况 enableTraffic: false, // 当前系统 systemInfo: {}, // 是否为鸿蒙系统 isHarmonyOS: false }; }, onLoad() { // 获取系统信息 this.getSystemInfo(); // 初始化地图 this.initMap(); }, methods: { // 获取系统信息 getSystemInfo() { uni.getSystemInfo({ success: (res) => { this.systemInfo = res; // 检测是否为鸿蒙系统 this.isHarmonyOS = this.checkHarmonyOS(res); console.log(\'当前系统:\', this.isHarmonyOS ? \'HarmonyOS\' : res.platform); } }); }, // 检测是否为鸿蒙系统 checkHarmonyOS(info) { // 鸿蒙系统检测,目前可通过brand和model判断 const brand = (info.brand || \'\').toLowerCase(); const model = (info.model || \'\').toLowerCase(); const system = (info.system || \'\').toLowerCase(); // 华为设备且系统为鸿蒙 return (brand.indexOf(\'huawei\') !== -1 || brand.indexOf(\'honor\') !== -1) &&  (system.indexOf(\'harmony\') !== -1 || system.indexOf(\'harmonyos\') !== -1); }, // 初始化地图 async initMap() { // 请求定位权限 await this.requestLocationPermission(); // 获取当前位置 this.getCurrentLocation(); }, // 请求定位权限 requestLocationPermission() { return new Promise((resolve, reject) => { uni.authorize({ scope: \'scope.userLocation\', success: () => { console.log(\'定位权限请求成功\'); resolve(); }, fail: (err) => { console.error(\'定位权限请求失败\', err); uni.showModal({  title: \'提示\',  content: \'需要获取您的位置信息,请允许\',  success: (res) => { if (res.confirm) {  uni.openSetting(); }  } }); reject(err); } }); }); }, // 获取当前位置 getCurrentLocation() { uni.showLoading({ title: \'定位中...\' }); uni.getLocation({ type: \'gcj02\', accuracy: \'high\', success: (res) => { console.log(\'当前位置:\', res); this.latitude = res.latitude; this.longitude = res.longitude;  // 添加当前位置标记 this.addMarker({ id: 0, latitude: res.latitude, longitude: res.longitude, title: \'当前位置\', iconPath: \'/static/images/location.png\', width: 40, height: 40 });  uni.hideLoading(); }, fail: (err) => { console.error(\'获取位置失败\', err); uni.hideLoading(); uni.showToast({ title: \'定位失败,请检查定位权限\', icon: \'none\' }); } }); }, // 添加标记点 addMarker(marker) { const index = this.markers.findIndex(item => item.id === marker.id); if (index !== -1) { this.markers.splice(index, 1, marker); } else { this.markers.push(marker); } }, // 地图点击事件 onMapTap(e) { console.log(\'点击地图位置:\', e); // 添加标记点 this.addMarker({ id: this.markers.length + 1, latitude: e.detail.latitude, longitude: e.detail.longitude, title: `标记 ${this.markers.length + 1}`, iconPath: \'/static/images/marker.png\', width: 30, height: 30 }); }, // 标记点点击事件 onMarkerTap(e) { const markerId = e.detail.markerId; const marker = this.markers.find(item => item.id === markerId); if (!marker) return; uni.showActionSheet({ itemList: [\'查看详情\', \'导航到这里\'], success: (res) => { if (res.tapIndex === 0) { // 查看详情 uni.showModal({  title: marker.title,  content: `位置:${marker.latitude}, ${marker.longitude}`,  showCancel: false }); } else if (res.tapIndex === 1) { // 导航 this.openMapNavigation(marker); } } }); }, // 切换路况 toggleTraffic() { this.enableTraffic = !this.enableTraffic; } }};.map-container { position: relative; width: 100%; height: 100vh; .map { width: 100%; height: 100%; } .controls { position: absolute; right: 20rpx; bottom: 100rpx; display: flex; flex-direction: column; .control-item { width: 80rpx; height: 80rpx; background-color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 20rpx; box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.2); .iconfont { font-size: 40rpx; color: #3a86ff; } } }}

导航功能实现

现在我们来实现导航功能,在 UniApp 中可以通过调用系统地图APP或者使用高德/百度地图SDK实现导航。下面我们添加导航相关方法:

// 添加到methods中// 打开地图导航openMapNavigation(destination) { // 目标位置 const destinationLatitude = destination.latitude; const destinationLongitude = destination.longitude; const destinationName = destination.title || \'目的地\'; // 鸿蒙系统处理 if (this.isHarmonyOS) { this.openHarmonyMapNavigation(destinationLatitude, destinationLongitude, destinationName); return; } // 不同平台的处理方式 // #ifdef APP-PLUS this.openNativeMapApp(destinationLatitude, destinationLongitude, destinationName); // #endif // #ifdef H5 this.openWebMapNavigation(destinationLatitude, destinationLongitude, destinationName); // #endif // #ifdef MP // 小程序平台使用map组件的openMapApp方法 uni.openLocation({ latitude: destinationLatitude, longitude: destinationLongitude, name: destinationName, scale: 18 }); // #endif},// 打开原生地图应用(App端)openNativeMapApp(latitude, longitude, name) { // 检测手机上已安装的地图应用 plus.maps.getInstalledMaps((maps) => { console.log(\'安装的地图应用:\', maps); if (maps.length === 0) { uni.showToast({ title: \'本机未安装地图应用\', icon: \'none\' }); return; } // 优先使用高德地图 const amap = maps.find(map => map.id === \'amap\'); if (amap) { plus.maps.openMap({ latitude: latitude, longitude: longitude, name: name, scale: 16, mapType: plus.maps.MapType.NAVIGATION, dst: { latitude: latitude,  longitude: longitude, name: name }, coordType: \'gcj02\', provider: \'amap\' }); } else { // 使用第一个可用的地图应用 plus.maps.openMap({ latitude: latitude, longitude: longitude, name: name, scale: 16, dst: { latitude: latitude,  longitude: longitude, name: name } }); } });},// 打开Web地图导航(H5端)openWebMapNavigation(latitude, longitude, name) { // 检测设备类型 const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent); // 高德地图URL const amapUrl = `https://uri.amap.com/navigation?to=${longitude},${latitude},${encodeURIComponent(name)}&mode=car&callnative=1`; // 百度地图URL const bdMapUrl = `https://api.map.baidu.com/direction?destination=latlng:${latitude},${longitude}|name:${encodeURIComponent(name)}&mode=driving&origin=我的位置&coord_type=gcj02&output=html&src=webapp.baidu.openAPIdemo`; // 优先使用高德地图 window.location.href = amapUrl; // 如果5秒后还在当前页面,说明没有安装高德地图APP,尝试使用百度地图 setTimeout(() => { if (document.hidden || document.webkitHidden) return; window.location.href = bdMapUrl; }, 5000);},// 鸿蒙系统地图导航处理openHarmonyMapNavigation(latitude, longitude, name) { // #ifdef APP-PLUS // 尝试使用HMS Core地图服务 // 注意:需要先集成HMS Core SDK if (plus.hms && plus.hms.map) { plus.hms.map.openMap({ latitude: latitude, longitude: longitude, name: name, navigationMode: \'driving\' // driving, riding, walking }); return; } // 降级处理:如果HMS Core不可用,尝试使用高德地图 this.openNativeMapApp(latitude, longitude, name); // #endif}

路线规划功能

接下来,我们增加路线规划功能,实现从当前位置到目标位置的路线绘制:

// 添加到methods中// 规划路线planRoute(startPoint, endPoint) { uni.showLoading({ title: \'规划路线中...\' }); // 鸿蒙系统处理 if (this.isHarmonyOS) { this.planRouteForHarmonyOS(startPoint, endPoint); return; } // 调用高德地图API规划路线 uni.request({ url: \'https://restapi.amap.com/v3/direction/driving\', data: { key: this.amapKey, // 高德地图Web服务API的Key origin: `${startPoint.longitude},${startPoint.latitude}`, destination: `${endPoint.longitude},${endPoint.latitude}`, extensions: \'all\' }, success: (res) => { uni.hideLoading(); if (res.data && res.data.status === \'1\' && res.data.route && res.data.route.paths && res.data.route.paths.length > 0) { const path = res.data.route.paths[0]; const steps = path.steps; // 解析路线坐标点 let points = []; steps.forEach(step => { const stepPoints = step.polyline.split(\';\'); stepPoints.forEach(point => { const [lng, lat] = point.split(\',\'); points.push({  longitude: parseFloat(lng),  latitude: parseFloat(lat) }); }); }); // 绘制路线 this.drawRoute(points, path.distance); } else { uni.showToast({ title: \'路线规划失败\', icon: \'none\' }); } }, fail: (err) => { console.error(\'请求路线规划API失败\', err); uni.hideLoading(); uni.showToast({ title: \'路线规划失败\', icon: \'none\' }); } });},// 绘制路线drawRoute(points, distance) { // 清除现有路线 this.polyline = []; // 添加新路线 this.polyline.push({ points: points, color: \'#3a86ff\', width: 6, arrowLine: true, dottedLine: false }); // 设置地图可视区域,以包含路线起点和终点 // 获取 map 组件的实例 const mapContext = uni.createMapContext(\'myMap\', this); // 设置地图视野范围 mapContext.includePoints({ points: [points[0], points[points.length - 1]], padding: [80, 80, 80, 80] }); // 显示路线信息 uni.showModal({ title: \'路线信息\', content: `总距离: ${(distance / 1000).toFixed(2)} 公里`, showCancel: false });},// 鸿蒙系统路线规划planRouteForHarmonyOS(startPoint, endPoint) { // #ifdef APP-PLUS // 如果HMS Core可用,使用HMS Core地图服务规划路线 if (plus.hms && plus.hms.map && plus.hms.map.routePlan) { plus.hms.map.routePlan({ start: { latitude: startPoint.latitude, longitude: startPoint.longitude }, end: { latitude: endPoint.latitude, longitude: endPoint.longitude }, mode: \'driving\', // driving, riding, walking success: (result) => { uni.hideLoading(); // 解析HMS Core返回的路线数据,格式可能与高德略有不同 const points = result.paths[0].points.map(point => ({ longitude: point.longitude, latitude: point.latitude })); this.drawRoute(points, result.paths[0].distance); }, fail: (err) => { console.error(\'HMS Core路线规划失败\', err); uni.hideLoading(); // 降级处理:使用高德地图API this.planRoute(startPoint, endPoint); } }); return; } // HMS Core不可用,降级处理 this.planRoute(startPoint, endPoint); // #endif}

为了完整实现路线规划功能,我们还需要在界面上添加一个规划路线的按钮。在template部分添加以下内容:

<view class=\"control-item\" @tap=\"showRoutePlanDialog\"> <text class=\"iconfont icon-route\"></text></view><uni-popup ref=\"routePlanPopup\" type=\"bottom\"> <view class=\"route-plan-dialog\"> <view class=\"dialog-title\">路线规划</view> <view class=\"dialog-content\"> <view class=\"input-item\"> <view class=\"label\">起点</view> <view class=\"input\"> <input type=\"text\" v-model=\"startPoint.name\" placeholder=\"当前位置\" disabled /> </view> </view> <view class=\"input-item\"> <view class=\"label\">终点</view> <view class=\"input\"> <input type=\"text\" v-model=\"endPoint.name\" placeholder=\"请选择终点\" @tap=\"selectEndPoint\" /> </view> </view> </view> <view class=\"dialog-footer\"> <button class=\"cancel-btn\" @tap=\"closeRoutePlanDialog\">取消</button> <button class=\"confirm-btn\" @tap=\"confirmRoutePlan\" :disabled=\"!endPoint.name\">开始规划</button> </view> </view></uni-popup>

同时,我们需要在data中添加相关数据:

data() { return { // ... 现有数据 // 起点和终点 startPoint: { latitude: 0, longitude: 0, name: \'当前位置\' }, endPoint: { latitude: 0, longitude: 0, name: \'\' } };},

然后添加相关方法:

// 显示路线规划对话框showRoutePlanDialog() { // 设置起点为当前位置 this.startPoint = { latitude: this.latitude, longitude: this.longitude, name: \'当前位置\' }; // 清空终点 this.endPoint = { latitude: 0, longitude: 0, name: \'\' }; // 显示对话框 this.$refs.routePlanPopup.open();},// 关闭路线规划对话框closeRoutePlanDialog() { this.$refs.routePlanPopup.close();},// 选择终点selectEndPoint() { // 提示用户点击地图选择终点 uni.showToast({ title: \'请点击地图选择终点\', icon: \'none\' }); // 关闭对话框 this.closeRoutePlanDialog(); // 设置地图状态为选择终点 this.mapState = \'selectEndPoint\';},// 确认路线规划confirmRoutePlan() { if (!this.endPoint.name) { uni.showToast({ title: \'请先选择终点\', icon: \'none\' }); return; } // 关闭对话框 this.closeRoutePlanDialog(); // 规划路线 this.planRoute(this.startPoint, this.endPoint);}

同时,我们需要修改onMapTap方法,以支持选择终点:

// 地图点击事件onMapTap(e) { console.log(\'点击地图位置:\', e); const { latitude, longitude } = e.detail; // 如果当前状态是选择终点 if (this.mapState === \'selectEndPoint\') { // 设置终点 this.endPoint = { latitude, longitude, name: \'选定位置\' }; // 添加终点标记 this.addMarker({ id: \'endPoint\', latitude, longitude, title: \'终点\', iconPath: \'/static/images/end.png\', width: 30, height: 30 }); // 重置地图状态 this.mapState = \'\'; // 显示路线规划对话框 this.showRoutePlanDialog(); return; } // 正常添加标记点 this.addMarker({ id: this.markers.length + 1, latitude, longitude, title: `标记 ${this.markers.length + 1}`, iconPath: \'/static/images/marker.png\', width: 30, height: 30 });}

鸿蒙系统适配

鸿蒙系统(HarmonyOS)作为国产操作系统,已在越来越多的华为设备上使用。虽然目前鸿蒙系统对安卓应用有较好的兼容性,但仍有一些特殊之处需要注意。

1. 系统检测

鸿蒙系统检测是适配的第一步,我们已在代码中实现:

// 检测是否为鸿蒙系统checkHarmonyOS(info) { // 鸿蒙系统检测,目前可通过brand和model判断 const brand = (info.brand || \'\').toLowerCase(); const model = (info.model || \'\').toLowerCase(); const system = (info.system || \'\').toLowerCase(); // 华为设备且系统为鸿蒙 return (brand.indexOf(\'huawei\') !== -1 || brand.indexOf(\'honor\') !== -1) && (system.indexOf(\'harmony\') !== -1 || system.indexOf(\'harmonyos\') !== -1);}

2. HMS Core集成

鸿蒙系统使用HMS Core(Huawei Mobile Services)替代GMS(Google Mobile Services),因此需要集成HMS Core SDK。以下是基本步骤:

  1. 注册华为开发者账号,创建应用并获取AppID等信息
  2. 下载并集成HMS Core SDK
  3. 在manifest.json中配置相关信息

3. 地图服务适配

鸿蒙系统提供了内置的地图服务,但也可以使用高德地图等第三方服务。对于完全的鸿蒙适配,建议两种方式都支持:

// 鸿蒙系统地图导航处理openHarmonyMapNavigation(latitude, longitude, name) { // #ifdef APP-PLUS // 尝试使用HMS Core地图服务 if (plus.hms && plus.hms.map) { plus.hms.map.openMap({ latitude: latitude, longitude: longitude, name: name, navigationMode: \'driving\' }); return; } // 降级处理:如果HMS Core不可用,尝试使用高德地图 this.openNativeMapApp(latitude, longitude, name); // #endif}

4. 权限处理

鸿蒙系统的权限管理与安卓类似,但可能会有一些差异。对于地图定位功能,需要特别注意位置权限的申请:

// 请求定位权限requestLocationPermission() { return new Promise((resolve, reject) => { uni.authorize({ scope: \'scope.userLocation\', success: () => { console.log(\'定位权限请求成功\'); resolve(); }, fail: (err) => { console.error(\'定位权限请求失败\', err); // 对于鸿蒙系统,可能需要特殊处理 if (this.isHarmonyOS) { uni.showModal({ title: \'定位权限申请\', content: \'地图功能需要获取您的位置信息,请在弹出的对话框中点击\"允许\"\', success: (res) => {  if (res.confirm) { // 鸿蒙系统的设置页面可能与安卓有所不同 if (plus.os.name.toLowerCase() === \'android\') {  plus.runtime.openURL(\'hap://app/com.huawei.systemmanager/settingApp\'); } else {  uni.openSetting(); }  } } }); } else { uni.showModal({ title: \'提示\', content: \'需要获取您的位置信息,请允许\', success: (res) => {  if (res.confirm) { uni.openSetting();  } } }); } reject(err); } }); });}

常见问题与解决方案

在实际开发过程中,可能会遇到以下问题:

1. 定位不准确

问题描述:有时地图定位不准确,或者无法获取位置。

解决方案

  • 确保申请了高精度定位权限
  • 使用gcj02坐标系统(高德地图使用的坐标系)
  • 使用多种定位方式(GPS、网络、基站)结合
  • 在鸿蒙系统上,确保已正确配置HMS Core定位服务
// 获取高精度位置uni.getLocation({ type: \'gcj02\', altitude: true, // 获取海拔信息 accuracy: \'high\', // 高精度定位 geocode: true, // 获取地址信息 success: (res) => { console.log(\'高精度定位结果:\', res); // 处理位置信息 }, fail: (err) => { // 降级处理:尝试使用低精度定位 uni.getLocation({ type: \'gcj02\', success: (lowRes) => { console.log(\'低精度定位结果:\', lowRes); // 处理位置信息 }, fail: (lowErr) => { console.error(\'定位失败\', lowErr); } }); }});

2. 鸿蒙系统下地图显示异常

问题描述:在某些鸿蒙系统设备上,地图显示异常或功能不完整。

解决方案

  • 确认已在manifest.json中配置了正确的HMS Core服务
  • 对地图组件应用特定的样式修复
  • 使用原生插件进行深度兼容
/* 鸿蒙系统地图样式修复 */.harmony-map-fix { /* 可能需要特定设备的样式修复 */ transform: translateZ(0); -webkit-transform: translateZ(0);}

3. 导航无法打开地图应用

问题描述:点击导航按钮无法打开地图应用。

解决方案

  • 检查URL Scheme配置是否正确
  • 提供多个地图应用的支持(高德、百度、腾讯等)
  • 针对鸿蒙系统,优先使用HMS Core的地图服务
// 多地图应用支持openMapNavigation(destination) { const { latitude, longitude, name } = destination; // 检测平台 if (this.isHarmonyOS) { // 鸿蒙系统处理 this.openHarmonyMapNavigation(latitude, longitude, name); return; } // 获取已安装的地图应用 plus.maps.getInstalledMaps((maps) => { if (maps.length === 0) { // 没有安装地图应用,提供网页版导航 window.location.href = `https://uri.amap.com/navigation?to=${longitude},${latitude},${encodeURIComponent(name)}&mode=car`; return; } // 展示可用的地图应用列表 const mapList = maps.map(map => map.name); uni.showActionSheet({ itemList: mapList, success: (res) => { const selectedMap = maps[res.tapIndex]; // 打开选择的地图应用 plus.maps.openMap({ latitude: latitude, longitude: longitude, name: name, scale: 16, provider: selectedMap.id }); } }); });}

总结

通过本文,我们详细介绍了如何在UniApp框架下实现地图定位与导航功能,并针对鸿蒙系统进行了适配。主要内容包括:

  1. 基础地图组件:使用UniApp的map组件实现地图展示与交互
  2. 定位功能:获取用户当前位置并显示在地图上
  3. 标记功能:在地图上添加与管理标记点
  4. 导航功能:调用系统地图应用进行导航
  5. 路线规划:使用API规划路线并在地图上绘制
  6. 鸿蒙适配:针对鸿蒙系统的特殊处理

在实际开发中,还可以根据具体需求进一步扩展功能,例如:

  • 添加地点搜索功能
  • 实现自定义地图样式
  • 增加位置共享功能
  • 支持离线地图

希望本文对你在UniApp项目中实现地图功能有所帮助,特别是在需要兼容鸿蒙系统的场景下。