鸿蒙OS&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文件:






导航功能实现

现在我们来实现导航功能,在 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项目中实现地图功能有所帮助,特别是在需要兼容鸿蒙系统的场景下。

你可能感兴趣的:(uniapp鸿蒙os,harmonyos,uni-app,华为)