更新记录

1.1.7(2023-12-27)

  1. 优化了一点细节

1.1.6(2023-11-24)

  1. 修复定位结果中丢失属性locationQualityReport的问题。
  2. tackLocation方法新增一个参数isSimpleMode,详见字段解释。

1.1.5(2023-11-08)

1.【重要】修复Android14上定位可能失效的问题

查看更多

平台兼容性

Android Android CPU类型 iOS
适用版本区间:6.0 - 12.0 armeabi-v7a:支持,arm64-v8a:支持,x86:未测试 ×

原生插件通用使用流程:

  1. 购买插件,选择该插件绑定的项目。
  2. 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
  3. 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
  4. 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
  5. 开发完毕后正式云打包

付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios

注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择


前言

  • 插件采用高德sdk,默认使用高精度定位模式,返回结果为火星坐标系(GCJ02),恰好和uniapp需要的坐标系一致。
  • 调用方法会自动申请权限,如果用户拒绝权限会告知开发者,开发者可以自行制定用户拒绝后的流程。
  • 本插件是在安卓系统下以合法的方式提供后台定位,定位是没问题的,但是系统并不会让你在后台肆无忌惮的搞事,比如大多数系统都不允许熄屏后的后台联网行为, 所以不建议在后台实时上传位置,一种做法是:先保存定位信息到本地数据库,然后写个定时器定时查询数据库是否有记录的数据,有就取出来上传,上传成功再删掉数据;这样就算后台上传失败了,定位数据也不会丢失。 等待用户点亮屏幕后下次自动任务自然会上传成功。
  • 点击下载演示程序
  • 目前高德已经开始频繁找开发者支付授权费了,使用的时候注意调用频率不要太高!商用还是给钱吧。

    使用步骤

    1. 首先在高德开放平台申请你的appkey
    2. 在项目的manifest.json中选择APP原生插件配置,添加云端插件
    3. 在项目的manifest.json中选择APP模块配置,勾选定位和地图,填写appkey
    4. 打包自定义基座,非常重要,新手调试报错大多数都是忽略了这一步
    5. 在代码中导入插件(见下方示例),运行时选择自定义基座即可

代码示例

上述步骤完成以后,直接复制以下代码到index.vue中即可测试

<template>
    <div>
        <button type="primary" @click="onceLocation">单次定位</button>
        <button type="primary" @click="trackLocation">持续定位</button>
        <button type="primary" @click="stopLocation">停止持续定位</button>
        <button type="primary" @click="calcDistance">坐标距离计算</button>
        <button type="primary" @click="onGeoQuery">坐标反查</button>
        <button type="primary" @click="onRoutePlan">路径规划(唤起外部地图导航)</button>
        <button type="primary" @click="switchCoordType">切换坐标系,当前:{{coordType}}</button>
        <view class="">
            <text :selectable="true">{{result}}</text>
        </view>
    </div>
</template>

<script>
    //引入插件
    const aMapHelper = uni.requireNativePlugin("SLY-AMapHelper");
    export default {
        data() {
            return {
                result: '',
                coordTyps: ['gcj02', 'wgs84', 'bd09'],
                coordIndex: 0,
                myLng: 0,
                myLat: 0,

            }
        },
        //退出页面后停止定位
        onBackPress() {
            aMapHelper.stopLocation();
        },
        computed: {
            coordType() {
                return this.coordTyps[this.coordIndex]
            }
        },
        methods: {
            //单次定位
            onceLocation() {
                uni.showLoading({
                    title: '定位中...'
                })
                this.result = ''
                aMapHelper.getLocation({
                    coordType: this.coordType
                }, (res) => {
                    uni.hideLoading();
                    this.result = JSON.stringify(res)
                    if (res.errorCode == 0) {
                        this.myLng = res.longitude;
                        this.myLat = res.latitude;
                        this.result =
                            `
                            定位时间:${res.formatTime}
                            坐标:${res.longitude},${res.latitude}
                            坐标系:${res.coordType}
                            位置描述:${res.address}
                            `
                    } else if (res.errorCode == 12) { //用户拒绝定位权限
                        //当用户选择了“拒绝且不再询问”时,应用无法再弹出权限申请框,必须引导用户前往设置页面手动开启。
                        let deniedPermissionAndNoAsk = res.deniedPermissionAndNoAsk;
                        uni.showModal({
                            title: '提示',
                            content: res.errorInfo,
                            success: (res) => {
                                if (res.confirm && deniedPermissionAndNoAsk) {
                                    //打开设置页面
                                    aMapHelper.openSettingPage();
                                }
                            }
                        })
                    }

                })
            },
            //连续定位
            trackLocation() {
                this.result = '';
                let count = 0;
                aMapHelper.trackLocation({
                    intervalTime: 3000, //定位间隔时间
                    spacing: 0, //有效移动距离,超过大于等于该值才会回调
                    notificationTitle: "自定义Title",
                    notificationContent: "自定义Content",
                    coordType: this.coordType
                }, (res) => {
                    //调试时请以控制台日志是否持续打印为准,息屏后view页面可能不会更新result结果
                    console.log("【持续定位结果】", `时间:${res.formatTime},坐标:${res.longitude},${res.latitude}`);
                    if (res.errorCode == 0) {
                        this.result =
                            `
                            定位次数:${++count}
                            定位时间:${res.formatTime}
                            坐标:${res.longitude},${res.latitude}
                            坐标系:${res.coordType}
                            设备方向:${res.direction}
                            海拔:${res.altitude}米`

                    } else if (res.errorCode == 12) {
                        let deniedPermissionAndNoAsk = res.deniedPermissionAndNoAsk;
                        uni.showModal({
                            title: '提示',
                            content: res.errorInfo,
                            success: (res) => {
                                if (res.confirm && deniedPermissionAndNoAsk) {
                                    aMapHelper.openSettingPage();
                                }
                            }
                        })
                    }
                })
            },
            //停止定位
            stopLocation() {
                aMapHelper.stopLocation();
            },
            //计算坐标距离
            calcDistance() {
                let distance = aMapHelper.calcDistance({
                    lngLat1: [116.368904, 39.9234230],
                    lngLat2: [116.387271, 39.922501]
                })
                this.result = `距离:${distance}米`
            },
            //逆地址编码,即根据坐标反查位置信息
            onGeoQuery() {
                if (this.myLng == 0 || this.myLat == 0) {
                    uni.showToast({
                        title: '请先执行单次定位',
                        icon: 'none'
                    })
                    return;
                }
                aMapHelper.reGeoQuery({
                    lng: this.myLng,
                    lat: this.myLat,
                    radius: 200, //范围多少米
                    coordType: this.coordTyps[this.coordIndex] //火系坐标系还是GPS原生坐标系,仅支持"gcj02","wgs84"2中
                }, res => {
                    console.log("逆地址结果", res);
                    if (res.errorCode == 0) {
                        //成功,更多结果,打印res.result
                        this.result = res.result.formatAddress
                    } else {
                        //失败
                        this.result = res.msg;
                    }
                })
            },
            onRoutePlan() {
                aMapHelper.routePlan({
                    sname: "春熙路", //可选,不传默认“我的位置”
                    slat: 30.654899, //可选,不传默认当前纬度
                    slng: 104.078657, //可选,
                    dname: "天府广场", //可选,不传默认“目的地”
                    dlat: 30.657401, //必填
                    dlng: 104.065861, //必填
                })
            },
            switchCoordType() {
                this.coordIndex = (this.coordIndex + 1) % 3;
                aMapHelper.stopLocation();
            }
        }
    }
</script>

调用方法说明

方法 说明 参数说明 返回结果
getLocation(object) 单次定位 方法参数说明1 定位返回结果
trackLocation(object) 轨迹追踪/连续定位,内部自带权限申请流程 方法参数说明2 定位返回结果
stopLocation() 停止持续定位(单次定位无需调用)
trackPermissionPreGet() 持续定位所需权限预先申请,适用于想先把权限拿到手,后面在合适的地方再调用trackLocation
isTrackSettingOk() 检查后台定位所需的基本权限是否设置完毕(不包括特殊机型的手动设置项) bool
openSettingPage() 打开应用设置页面,用户永久拒绝权限后会用到
trackPermissionPreGetSimple() 同上,但只会申请“后台定位”一个权限,忽略通知权限和省电策略设置
calcDistance(object) 计算坐标距离(通用,与坐标系无关) 方法参数说明3 数字,单位米
reGeoQuery(object) 坐标反查 方法参数说明4 object,打印获取
routePlan(object) 唤起外部高德或百度地图进行路线导航(内部优先使用高德);内置导航会增加30M的体积,所以没有集成 方法参数说明4
gcj02ToWgs84(lng,lat) gcj02坐标转wgs84坐标 经度在前纬度在后 长度为2的double数组, 经度在前纬度在后
gcj02ToBd09(lng,lat) gcj02坐标转bd09坐标 同上 同上
bd09ToWgs84(lng,lat) 如题 同上 同上
bd09ToGcj02(lng,lat) 如题 同上 同上
wgs84ToGcj02(lng,lat) 如题 同上 同上
wgs84ToBd09(lng,lat) 如题 同上 同上

getLocation(object)方法的参数说明

字段 类型 必填 说明 默认值
coordType string 设置返回的坐标系类型,可选项'gcj02','wgs84','bd09' 'gcj02'

trackLocation(object)方法的参数说明

字段 类型 必填 说明 默认值
coordType string 设置返回的坐标系类型,可选项'gcj02','wgs84','bd09' 'gcj02'
intervalTime int 定位间隔时间,单位ms,最小值1000 2000
spacing int 有效移动距离,单位m,移动距离大于等于该值才会回调 0
notificationTitle string 通知栏标题 "位置追踪服务"
notificationContent string 通知栏内容 "正在跟踪你的位置"
enableTrackExplain string 开启后台定位的解释说明,对用户说明为什么需要后台定位 "后台持续定位需要打开一些权限设置,点击确定前往打开"
isSimpleMode bool 是否开启简单模式,该模式下只会申请“后台定位”一个权限,忽略通知权限和省电策略设置,不建议开启 false
isAlwaysAsk bool 目前该参数仅对华为系的机子生效,每次持续定位前都会询问是否打开了他们的专属设置,并给出了图文教程,因为无法检测他是否打开了,所以最好询问一下。如果实在不想弹窗,可以在开始定位后再在本地记录一个标识来记录用户已经看过弹窗了 true

calcDistance(object)方法的参数说明(需为同一坐标系)

字段 类型 必填 说明
lngLat1 [经度1,纬度1] 坐标点1 ,支持gcj02,wgs84,bd09
lngLat2 [经度2,纬度2] 坐标点2 ,支持gcj02,wgs84,bd09

reGeoQuery(object)方法的参数说明

字段 类型 必填 说明 默认值
lng double 经度
lat double 纬度
radius float 半径范围 200
coordType string 坐标类型,支持gcj02,wgs84,bd09 gcj02

routePlanning(object)方法的参数说明

字段 类型 必填 说明 默认值
sname String 起点名称 "我的位置"
slat double 起点纬度(gcj02) 当前位置
slng double 起点经度 当前位置
dname String 终点名称 "目的地"
dlat double 终点纬度 -
dlng double 终点经度 -

定位返回结果(部分)

字段 类型 说明
errorCode int 错误码,0成功;详情参考下方对照表
errorInfo string 错误信息
deniedPermissionAndNoAsk boolean 用户是否拒绝了定位权限且不再询问
coordType string 坐标系类型,"GCJ02","WGS84","BD09"
locationType int 定位类型,1:GPS,2:前次定位结果;详情参考下方对照表
time long 定位时间,毫秒
formatTime string 定位时间,格式化后:2021-09-17 18:00:00
longitude double 经度
latitude double 纬度
accuracy float 定位精度,单位米
speed float 移动速度,单位米/秒,室外有效
altitude double 海拔高度,单位米,室外有效
bearing float 方向角度【0,360】,其中0度表示正北方向,90度表示正东,180度表示正南,270度表示正西;室外有效
direction string 方向中文释义
address string 地址
description string 位置语义信息
country string 国家
provider string 省份
city string 城市
district string 行政区
street string 街道
cityCode int 城市编码
adCode int 地区编码
satellites int 卫星数量

更多结果可打印查看

参考链接:完整结果解释 https://amappc.cn-hangzhou.oss-pub.aliyun-inc.com/lbs/static/unzip/Android_Location_Doc/com/amap/api/location/AMapLocation.html 请下滑至方法概要

常见问题

  • Q:定位精度不够,实际位置发生较大偏移

    A:定位偏移属于正常现象,尤其在室内,在室外时偶尔也会出现点位偏移,此时可以判断返回坐标的小数点后的数字个数进行精度过滤。

    代码示例

    //查看小数点后有几位
    const longStr = String(res.longitude);
    const latStr = String(res.latitude);
    const longAccLength = longStr.substring(longStr.indexOf(".") + 1).length;
    const latAccLength = latStr.substring(latStr.indexOf(".") + 1).length;
    //只要有一个的小数位大于等于4就算合格
    const isOk = longAccLength >= 4 || latAccLength >= 4;
  • Q:开始定位后连续返回0,0坐标

    A:该现象意味定位失败,请自行过滤掉,详见高德官方解释 https://lbs.amap.com/faq/android/location-sdk/position/43313

  • Q:息屏后一段时间,后台位置数据不再更新

    A:因为息屏后各系统基本都会限制应用的后台联网行为,所以会出现定位成功,但是传不上去的情况。这样就只有每次定位后不要立马传服务器,先保存在本地数据库。然后首页写一个定时器, 每隔一段时间从本地数据库中去取坐标信息来传,定时器运行过程中如果遇到息屏无网络传不上去也就不用管它,定时器总会等到用户点亮屏幕的时候,那时就会传上去了。

  • Q:华为系手机开启了权限后,每次持续定位前还是会弹窗提示

    A:把isAlwaysAsk设为false就行了。这是因为华为系手机有个特殊的手动设置项,因为无法检测他是否打开了,所以每次持续定位前都会询问。如果实在不想弹窗, 可以在开始定位后再在本地记录一个标识来记录用户已经看过弹窗了。

坐标系小知识

因为中国国内的坐标系必须至少使用GCJ02标准,并且不得提供WGS84坐标,所以插件获取WGS84坐标的方式是内部通过网上公开算法转换而来的。经测试,转换后的wgs84坐标放到谷歌地球上是正确的

  • 地球坐标系WGS84:常见于 GPS 设备,Google 地图等国际标准的坐标体系。
  • 火星坐标系GCJ-02:中国国内使用的被强制加密后的坐标体系,高德坐标就属于该种坐标体系。
  • 百度坐标系BD-09:百度地图所使用的坐标体系,是在火星坐标系的基础上又进行了一次加密处理。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

定位权限,更改设置

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件定位功能使用高德地图定位SDK,仅仅用于获取设备位置,并不会上传数据。SDK官方网站为https://lbs.amap.com/product/locate#/,其隐私协议参考https://lbs.amap.com/pages/privacy/

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问