前端定位组件更新公告:
为了提升前端定位组件定位能力,同时提升前端定位组件的体验、规范定位组件服务的使用,防止公用额度占满造成调用失效影响业务,请各位开发者尽快升级到新版本,旧版本将逐步关停;

前端定位组件

       前端定位组件,旨在优化纯HTML5 Geolocation定位能力弱,定位成功率不高的问题,提供简单、易用的接口帮助业务层获取用户当前的位置信息(需用户授权),以降低开发成本,提升定位精准度。

       说明:由于微信7.0版本升级了对https的安全限制,在微信7.0版本及以上版本使用http协议访问定位组件会导致定位失败。开发者如遇到上述问题,请切换至https协议访问。

服务依赖

前端定位组件依赖WebService API服务,在调用前请确保传入的key已分配以下WebService API服务额度;

服务名 相关功能点 官方文档
逆地址解析 由经纬度到文字地址及相关位置信息能力 https://lbs.qq.com/service/webService/webServiceGuide/address/Gcoder
IP定位 在其他定位方式失效时IP定位获取位置 https://lbs.qq.com/service/webService/webServiceGuide/position/webServiceIp
坐标转换 标准GPS坐标系转换到腾讯地图坐标系 https://lbs.qq.com/service/webService/webServiceGuide/webServiceTranslate

调用示例

调用方式一

引入封装好的静态JS文件,通过多种经纬度获取方法,并配合调用WebserviceAPI服务获取,让定位效率和准确性更出色。本方式JS文件资源形式改为了CDN维护,能够支撑更大量级的访问量;另外,本方式的调用日志更加健全,在排查问题的时候能够更加高效且精准的定位并解决问题。

代码地址

js引入地址:https://mapapi.qq.com/web/lbs/h5-components/geolocation/geolocation.min.js

参考类:window.LBS.WebComponent.Geolocation

构造函数 说明
window.LBS.WebComponent.Geolocation({key, referer, domain}) 参数说明:
- key:选填(与domain二选一必填),开发密钥(key),可在腾讯位置服务官网申请注册
- referer:必填,调用来源(应用名称),示例:referer=mapqq
- domain:选填(与key二选一必填),开发者代理服务的域名

实例方法

方法 返回值 说明
getLocation(sucCallback, errCallback, [options: {timeout?: number, highAccuracy?: boolean, maximumAge?: number}]) 获取当前所在地理位置,调用一次即重新定位一次,定位数据比较精确。
sucCallback为定位成功回调函数,必填;
errCallback为定位失败回调函数,选填,如果不填,请设为null;
options为定位选项,选填,具体可配置项如下:
● timeout:定位的超时时间(单位为毫秒),默认值为5000(即5秒);
● highAccuracy: 是否进行严格的高精度定位,若为true则在高精度获取失败时不会进行IP兜底;
● maximumAge: 浏览器原生API navigator.geolocation.getCurrentPosition配置项,用于控制获取经纬度的缓存时间,默认为0。
getIpLocation(sucCallback, [errCallback]) 立即获取当前所在地理位置,适用于非精确定位场景,是IP定位,城市级别。
sucCallback为定位成功回调函数,必填;
errCallback为定位失败回调函数,选填。
watchPosition(sucCallback, [options: {timeout: number, highAccuracy: boolean}]) 监听位置信息的改变,类似HTML5 Geolocation的watchPosition。
sucCallback为定位成功回调函数,必填;
options为定位选项,选填,具体可配置项如下:
● timeout:定位的超时时间(单位为毫秒),默认值为5000(即5秒);
● highAccuracy: 是否进行严格的高精度定位,若为true则在高精度获取失败时不会进行IP兜底。
● maximumAge: 浏览器原生API navigator.geolocation.getCurrentPosition配置项,用于控制获取经纬度的缓存时间,默认为0。
clearWatch() 清除监听,类似HTML5 Geolocation的clearWatch。

定位成功时返回的结果

属性 类型 说明
nation string 国家
province string 省份
city string 城市
district string 区县
adcode string 行政区划代码,规则详见:腾讯位置服务行政区划文档
addr string 详细地址
lat number 纬度
lng number 经度
accuracy number 定位精确度
gcoder_request_id string 逆地址解析的请求id(非必有)
ip_request_id string IP定位的请求id(非必有)
module ‘geolocation’ 固定字符串,表示数据为前端定位组件返回值
type CoreType 获取经纬度的具体定位方式,详见CoreType
logs { status: PositionStatus, type: CoreType }[] 不同定位类型的定位情况

定位失败时返回的结果

属性 类型 说明
status PositionStatus 定位状态码,用于排查定位失败的原因
message string 错误信息,为空字符串时请根据status查看失败原因
logs { status: PositionStatus; message: string; type?: CoreType }[] 完整定位日志,会返回定位过程中各个节点的定位情况

CoreType说明

枚举值 说明
h5 基于浏览器原生API navigator.geolocation.getCurrentPosition获取的经纬度,详见:MDN文档
wx 基于微信JSSDK获取的经纬度,详见:微信文档
qq 基于QQ能力获取的经纬度
x5 基于QQ浏览器提供的API获取的经纬度
ip 基于IP定位获取的经纬度,详见:腾讯位置服务IP定位

PositionStatus说明

枚举值 说明
10000 定位成功
10001 所有获取经纬度的方式均失败
10002 所有定位途径均超时
10101 浏览器原生API - 地理位置信息的获取失败,因为该页面没有获取地理位置信息的权限
10102 浏览器原生API - 地理位置获取失败,因为至少有一个内部位置源返回一个内部错误
10103 浏览器原生API - 获取地理位置超时
10104 浏览器原生API - 所用浏览器不支持该API
10201 微信JSSDK - 定位失败
10202 微信JSSDK - 页面未加载SDK
10203 微信JSSDK - 页面已加载SDK但不支持wx.getLocation方法
10301 QQ定位 - SDK未加载成功
10302 QQ定位 - 未获取经纬度授权
10303 QQ定位 - 未开启传感器
10304 QQ定位 - 未知错误
10305 QQ定位 - 用户拒绝授权
10306 QQ定位 - SDK不支持getLocation
10307 QQ定位 - iframe加载失败
10401 QQ浏览器API - 定位失败
10402 QQ浏览器API - 定位超时
10501 IP定位 - 定位失败
10502 IP定位 - 定位超时
10601 逆地址解析服务报错
10602 坐标转换服务报错

完整示例

<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>H5定位组件Demo - 调用方式一</title>
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
    />
    <style>
      * {
        margin: 0;
        padding: 0;
        border: none;
      }

      body {
        width: 100%;
        height: 100%;
        font-size: 16px;
      }

      #map-el {
        width: 100vw;
        height: 100vh;
      }

      .buttons {
        position: absolute;
        left: 10px;
        top: 10px;
        z-index: 99999;
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
        padding: 10px;
      }

      button {
        padding: 8px 6px;
        border-radius: 8px;
        background-color: rgba(255, 132, 132, 0.9);
        color: white;
        cursor: pointer;
      }

      #watch-status {
        padding: 8px 6px;
        border-radius: 8px;
        background-color: rgba(255, 132, 132, 0.9);
        color: white;
      }
    </style>
    <script src="https://mapapi.qq.com/web/lbs/h5-components/geolocation/geolocation.min.js"></script>
    <script src="https://map.qq.com/api/gljs?v=1.exp&key=[your key]"></script>
  </head>
  <body>
    <div id="map-el"></div>

    <div class="buttons">
      <button onClick="getLocation()">getLocation</button>
      <button onClick="getIpLocation()">getIpLocation</button>
      <button onClick="watchPosition()">watchPosition</button>
      <button onClick="clearWatch()">clearWatch</button>
      <span id="watch-status">位置监听状态:关</span>
    </div>

    <script>
      /** LBS申请的key */
      const key = '[your key]';
      /** 项目标识 */
      const referer = 'h5-geolocation-demo-4';
      /** 定位组件实例 */
      const geoInstance = new window.LBS.WebComponent.Geolocation({ key, referer });
      /** 精确定位配置项 */
      const options = { timeout: 6000, highAccuracy: true };

      /** 地图实例 */
      const map = new window.TMap.Map(document.querySelector('#map-el'));
      /** 地图marker图层 */
      const markerLayer = new window.TMap.MultiMarker({ id: 'markers', map });

      /** 展示定位结果 */
      const onSuccess = (result) => {
        console.log('>>> 定位成功回调数据', result);
        const { lat, lng } = result;
        const origin = new window.TMap.LatLng(lat, lng);
        markerLayer.setGeometries([{ id: 1, position: origin }]);
        map.easeTo({ center: origin, zoom: 17 });
      };
      /** 失败回调 */
      const onError = (error) => console.log('>>> 定位失败回调数据', error);

      /** 高精定位 */
      const getLocation = () => geoInstance.getLocation(onSuccess, onError, options);
      /** IP定位 */
      const getIpLocation = () => geoInstance.getIpLocation(onSuccess, onError);

      const watchStatusDOM = document.querySelector('#watch-status');
      /** 开启定位监听 */
      const watchPosition = () => {
        geoInstance.watchPosition(onSuccess, onError, options);
        watchStatusDOM.innerHTML = '位置监听状态:开';
      };
      /** 结束定位监听 */
      const clearWatch = () => {
        geoInstance.clearWatch();
        watchStatusDOM.innerHTML = '位置监听状态:关';
      };
    </script>
  </body>
</html>

调用方式二

将方式一的JS资源封装成html文件形式,方便需要在iframe中使用的场景。

HTML结构

<iframe src="https://mapapi.qq.com/web/lbs/h5-components/geolocation/geolocation.html?key=[your key]
&referer=[your referer]&immediate=[0 | 1]" width="0" height="0" frameborder="none" scrolling="no"
 style="display: none" allow="geolocation *" />
</iframe>

调用参数

参数名 必填 参数说明
key 否(domain或key必填其一) 开发密钥(key),可在腾讯位置服务官网申请注册
domain 否(domain或key必填其一) 开发者代理服务的域名
referer 调用来源,一般为您的应用名称,为了保障对您的服务,请务必填写!
immediate 传入为1时会在iframe加载完成后立刻触发一次getLocation,默认为1

调用iframe函数

父页面通过调用iframe页面的contentWindow.postMessage方法来告知iframe进行定位,本调用方式支持两种调用传值方式,具体如下:

传值方式一:入参为方法名字字符串,该方式适合在不用传入其他配置项时使用

const iframeDOM = document.querySelector('iframe');
iframeDOM.contentWindow.postMessage('getIpLocation', '*');

传值方式二:入参为Object对象,对象传递两个属性,分别是“func”用于告知iframe调用的函数名、“options”告知iframe调用的函数具体的配置项,该方式适合在需要传入函数配置项时使用

const iframeDOM = document.querySelector('iframe')
iframeDOM.contentWindow.postMessage({ func: 'getLocation', options: {timeout: 8000, highAccuracy: true} }, '*')

监听iframe函数执行回调

在完成上述“调用iframe函数”后,iframe会调用被告知的函数,并将函数执行结果通过message事件传递给父页面,因此需要在父页面对message事件进行监听处理,iframe具体会在事件的data中传递三个属性:"source"用于标识本次message事件是由定位组件触发(具体传值为’lbs-geolocation’)、"func"用于标识本次定位结果是执行哪个函数的结果、"result"用于传递本次执行的具体结果,示例代码如下:

window.addEventListener('message', ({ data }) => {
    if(data?.source !== 'lbs-geolocation' && !data?.func) {
        return;
    }
    
    switch(data.func) {
        // 返回的是getLocation方法的执行结果
        case 'getLocation': 
        // 返回的是getIpLocation方法的执行结果
        case 'getIpLocation':
        // 返回的是getIpLocation方法的执行结果
        case 'watchPosition': {
            const { result } = data
            result?.module ? onSuccess(result) : onError(result)
            break;
        }
        // 未知类型可以打印查看
        default: {
            console.log('Unknown Function: ', data.func, data);
            break;
        }
    }
})

完整示例

<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>H5定位组件Demo - 调用方式二</title>
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
    />
    <style>
      * {
        margin: 0;
        padding: 0;
        border: none;
      }

      body {
        width: 100%;
        height: 100%;
        font-size: 16px;
      }

      #map-el {
        width: 100vw;
        height: 100vh;
      }

      .buttons {
        position: absolute;
        left: 10px;
        top: 10px;
        z-index: 99999;
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
        padding: 10px;
      }

      button {
        padding: 8px 6px;
        border-radius: 8px;
        background-color: rgba(255, 132, 132, 0.9);
        color: white;
        cursor: pointer;
      }

      #watch-status {
        padding: 8px 6px;
        border-radius: 8px;
        background-color: rgba(255, 132, 132, 0.9);
        color: white;
      }
    </style>
    <script src="https://map.qq.com/api/gljs?v=1.exp&key=[your key]"></script>
  </head>
  <body>
    <div id="map-el"></div>

    <div class="buttons">
      <button onClick="getLocation()">getLocation</button>
      <button onClick="getIpLocation()">getIpLocation</button>
      <button onClick="watchPosition()">watchPosition</button>
      <button onClick="clearWatch()">clearWatch</button>
      <span id="watch-status">位置监听状态:关</span>
    </div>

    <script>
      /** 地图实例 */
      const map = new window.TMap.Map(document.querySelector('#map-el'));
      /** 地图marker图层 */
      const markerLayer = new window.TMap.MultiMarker({ id: 'markers', map });

      /** LBS申请的key */
      const key = '[your key]';
      /** 项目标识 */
      const referer = 'h5-geolocation-demo-4';
      /** 立即触发标识 */
      const immediate = 1;
      /** iframeDOM */
      const iframeDOM = document.createElement('iframe');
      iframeDOM.width = 0;
      iframeDOM.height = 0;
      iframeDOM.src = `https://mapapi.qq.com/web/lbs/h5-components/geolocation/geolocation.html?key=${key}&referer=${referer}&immediate=${immediate}&v=${Date.now()}`;
      iframeDOM.allow = 'geolocation *';

      /** 展示定位结果 */
      const onSuccess = (result) => {
        console.log('>>> 定位成功回调数据', result);
        const { lat, lng } = result;
        const origin = new window.TMap.LatLng(lat, lng);
        markerLayer.setGeometries([{ id: 1, position: origin }]);
        map.easeTo({ center: origin, zoom: 17 });
      };
      /** 失败回调 */
      const onError = (error) => console.log('>>> 定位失败回调数据', error);

      window.addEventListener('message', ({ data }) => {
        if (data?.source !== 'lbs-geolocation' && !data?.func) {
          return;
        }

        switch (data.func) {
          // 返回的是getLocation方法的结果
          case 'getLocation':
          // 返回的是getIpLocation的结果
          case 'getIpLocation':
          // 返回的是watchPosition的结果
          case 'watchPosition': {
            const { result } = data;
            result?.module ? onSuccess(result) : onError(result);
            break;
          }
          // 未知类型可以打印查看
          default: {
            console.log('data.func', data.func);
            break;
          }
        }
      });
      document.body.appendChild(iframeDOM);

      const onPost = (payload) => iframeDOM.contentWindow.postMessage(payload, '*');

      const getLocation = () => onPost({ func: 'getLocation', options: { timeout: 5000, highAccuracy: true } });
      const getIpLocation = () => onPost('getIpLocation');
      const watchStatusDOM = document.querySelector('#watch-status');
      const watchPosition = () => {
        onPost('watchPosition');
        watchStatusDOM.innerHTML = '位置监听状态:开';
      };
      const clearWatch = () => {
        onPost('clearWatch');
        watchStatusDOM.innerHTML = '位置监听状态:关';
      };
    </script>
  </body>
</html>