react-native应用集成地理逆编码


title: react-native应用集成地理逆编码
date: 2018-06-24 19:13:17
tags: react-native


一、背景

最近,在项目中需要集成定位功能,在网上搜集资料之后,决定采用逆地理定位功能进行实现,那么让我们看看逆地理定位功能是怎么实现。

二、思路

主要过程分为两部分:
(1):一是获取定位点的经纬度;
(2):二是使用一步骤获取的经纬度,进行调用能够解析经纬度的接口API,进行对应地理信息的获取.
在解决上述问题的过程中使用了百度逆地理高德逆地理定位功能,如下所示分别是高德逆地理和百度逆地理的访问文档

高德地理/逆地理编码文档地址
百度地理/逆地理解析文档地址

三、解决方法

首先要解决的是公共问题,也就是经纬度的获取,在这里,我们使用react-native提供的接口Geolocation进行获取,
说到这里,我们就得去了解一下Geolocation是什么东西?

1. Geolocation信息说明

Geolocation就是react-native官方提供的用于提供基本定位信息和经纬度信息的API,使用Geolocation时需要获取相应的系统权限
(1)权限说明

  • IOS系统:
    需要在Info.plist中增加NSLocationWhenInUseUsageDescription字段来启用定位功能。如果你使用react-native init创建项目,定位会被默认启用。

  • Android系统:
    需要请求访问地理位置的权限,你需要在AndroidManifest.xml文件中加入如下一行:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

(2)方法

  • static requestAuthorization()
    根据pList上配置的密钥请求合适的位置权限。
    如果设置NSLocationAlwaysUsageDescription,它将请求始终授权,但如果设置NSLocationWhenInUseUsageDescription,它将请求InUse授权。
  • static getCurrentPosition(geo_success: Function, geo_error?: Function, geo_options?: GeoOptions)
    成功时会调用geo_success回调,参数中包含最新的位置信息。
    支持的选项:timeout (ms), maximumAge (ms), enableHighAccuracy (bool)
    选项的含义如下所示:
    • timeout:指定获取地理位置时的超时时间,默认不限时
    • maximumAge:最长有效期,此参数用来指定多久再次获取位置
    • enableHighAccuracy:指示浏览器获取高精度的位置,默认为false,开启后浏览器可能花费更长的时间获取更精确的位置数据
      对于该方法请求成功之后,会有下面返回结果:
   1.经度:coords.longitude
   2.纬度:coords.latitude
   3.精确度:coords.accurary
   4.海拔:coords.altitude
   5.海拔准确度:coords.altitudeAcurary
   6.行进方向:coords.heading
   7.地面速度:coords.speed
   8.时间戳:new Date(position.timestamp)   

当然,我们也可能遇到请求失败的问题,那么,请求失败都有下面这几种情况
1.用户拒绝定位、2.请求超时、3.暂时获取不到定位信息、4.未知错误

  • static watchPosition(success: Function, error?: Function, options?: GeoOptions)
    持续监听位置,每当位置变化之后都调用success回调。
    支持的选项:timeout (ms), maximumAge (ms), enableHighAccuracy (bool), useSignificantChanges (bool)

  • static clearWatch(watchID: number)
    清除位置监听,位置监听id可由watchPosition方法返回

  • static stopObserving()

如下面代码,我们能够进行获取到经纬度


  componentDidMount() {
    this._getLocation();
    this.watchPosition = this._getWatchPosition();
  }

  componentWillUnmount() {
    navigator.geolocation.clearWatch(this.watchPosition);
  }

  _getLocation = () => {
    navigator.geolocation.getCurrentPosition(
      (initialPosition) => this.setState({ initialPosition }, () => {
        this._getAddress();
      }),
      (error) => alert(error.message),
      { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
    );
  };

  _getWatchPosition = () => {
    return navigator.geolocation.watchPosition(
      (lastPosition) => {
        this.setState({ lastPosition }, () => { this._getAddress(); });
      },
      (error) => alert(error.message),
      { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
    );
  };

其中initialPositionlastPosition的对象内容一直,只不过一个是初始化的时候的经纬度信息,一个是当前最新的经纬度信息,其返回结果如下所示:

geolocation数据结构

注意:在上面代码中,navigator.geolocation指的不是导航器,而是Geolocation的一个类,能够获取到定位的经纬度信息
我分别使用代码,进行了两种api的调用,如下代码:

2.高德逆地理解析

在高德逆地理解析中,需要首先进行获取高德账号Key。
参考下面文章即可:高德key的申请及使用说明

创建界面如下所示,在创建时我们选择web服务选项:

amap生成key页面

在获取key之后,我们就可以说是所有要准备的东西都已经准备好了,也就是有了Geolocation获取的经纬度,也有了能够正常访问的逆地理解析API,那我们就将
经纬度进行介绍,
首先我们进行判断api能否使用,通过我们手动拼接URL,然后在浏览器地址栏输入,如下界面即为正常

  • 1.输入的url
https://restapi.amap.com/v3/geocode/regeo?key=高德申请的key&poitype=all&radius=3000&output=json&extensions=all&roadlevel=0&location=120.19698604,30.11286062

注意:上面的key需要进行修改为自己的在高德申请的key,才能够进行访问到数据

  • 2.回车之后获取的数据界面
    请求结果数据展示

注意:如上所示,能够通过浏览器访问到数据,则说明接口可以正常使用,也就是我们可以通过自己的请求进行请求数据了

  • 3.如下代码所示为完整请求过程
_getAddress = () => {
    // 经度:positionData.longitude
    // 纬度:positionData.latitude
    // 最后一步 todo:高德逆地理编码转
    // lat<纬度>,lng<经度>

    const aMapLocationConfig = {
      key: '这里填写你自己申请的key,我的就不让你用了',
      poitype: 'all',
      radius: 3000,
      output: 'json',
      extensions: 'all',
      roadlevel: 0,
    };
    const aMapLocationURL = 'https://restapi.amap.com/v3/geocode/regeo'; //GET请求

    // callback=renderReverse&location=35.658651,139.745415&output=json&pois=1&ak=您的ak
    // radius=3000&output=json&extensions=all&roadlevel=all&location=30.11286062,120.19698604
    // ?output=xml&location=116.310003,39.991957&key=<用户的key>&radius=1000&extensions=all
    const { lastPosition } = this.state;
    if (!lastPosition) return null;
    const { longitude, latitude } = lastPosition.coords;
    const location = longitude + ',' + latitude;
    const aMapUrl = aMapLocationURL + '?' + Qs.stringify({ ...aMapLocationConfig }) + '&location=' + location;

    InteractionManager.runAfterInteractions(() => {
      gaxios(aMapUrl)
        .then((response) => {
          this.setState({
            address: response
          }, () => {
          });
        }).catch((error) => {
          console.log('获取地址信息出错' + error);
        });
    });
  };
3.百度逆地理解析

同样的,对于百度逆地理解析也需要进行获取key值,如下面文档说明百度逆地理解析的申请及说明文档
按照说明文档中的,我们需要进行创建应用
创建界面如下所示:
baidu
在创建应用时,需要注意的是应该创建应用类型为服务端的类型,然后我们就能够进行请求api获取逆地理解析的详细信息了,如下所示:

在创建应用之后,我们就能够进行请求api获取
逆地理解析的详细信息了,代码如下所示:
首先我们进行判断api能否使用,通过我们手动拼接URL,然后在浏览器地址栏输入,如下界面即为正常

  • 1.输入的url
http://api.map.baidu.com/geocoder/v2/?location=30.1128606254,120.1969860408&output=json&pois=1&ak=百度申请的ak

注意:上面的ak需要进行修改为自己的ak,才能够进行访问到数据,同时,为了能够获取到数据,先将ip白名单设置为0.0.0.0/0,如下所示:
url地址

  • 2.回车之后获取的数据界面
    结果数据

#注意#:如上所示,能够通过浏览器访问到数据,则说明接口可以正常使用,也就是我们可以通过自己的请求进行请求数据了

  • 3.如下为完整请求过程, 在这里需要值得注意,我们这里使用的是gps定位,所以要根据自己的需要进行设置coordtype,坐标的类型,目前支持的坐标类型包括:bd09ll(百度经纬度坐标)、bd09mc(百度米制坐标)、gcj02ll(国测局经纬度坐标,仅限中国)、wgs84ll( GPS经纬度) ,因此我们需要使用wgs84ll格式的坐标类型
   _getAddress = () => {
     // 经度:positionData.longitude
     // 纬度:positionData.latitude
     // 最后一步 todo:百度地图逆地理编码转
     // lat<纬度>,lng<经度>
 
     const baiduLocationConfig = {
       // callback: 'renderReverse',
       ak: '这里填写你自己申请的ak,我的就不让你用了',
       pois: 1,
       coordtype: 'wgs84ll', // 可能会影响精度
       output: 'json',
       latest_admin: 1,
       language_auto: 1,
       extensions_town: true,
       extensions_road: true,
       radius: 1000,
     };
     const baiduLocationURL = 'https://api.map.baidu.com/geocoder/v2/'; //GET请求
     // callback=renderReverse&location=35.658651,139.745415&output=json&pois=1&ak=您的ak
 
     const { lastPosition } = this.state;
     if (!lastPosition) return null;
     const { longitude, latitude } = lastPosition && lastPosition.coords;
     const location = latitude + ',' + longitude;
     const baiduUrl = baiduLocationURL + '?' + Qs.stringify({ ...baiduLocationConfig }) + '&location=' + location;
 
     InteractionManager.runAfterInteractions(() => {
       gaxios(baiduUrl)
         .then((response) => {
           this.setState({
             address: response
           }, () => {
           });
         }).catch((err) => {
           console.log('获取地址信息出错', err);
         });
     });
   };

四、效果展示

发送请求之后,我们能够看到如下效果,在请求之后,我将请求到的数据进行了分类,进行对其中具体的字段进行了说明:

1.高德地图请求效果
2.百度地图请求效果
五 演示代码github地址

GitHub地址:https://github.com/suwu150/react-native-locations
,能够直接通过git clone进行克隆运行,运行命令如下所示:

1.克隆项目

git clone https://github.com/suwu150/react-native-locations.git

2.进入项目,安装依赖文件库

cd react-native-locations
npm install

3.运行项目
ios:

 react-native run-ios

Android:

react-native run-android

注意:在运行安卓项目的时候,必须手动打开模拟器

六、安卓中遇到的问题解决方案
1、问题描述

按道理来说,这个定位方式应该所示完美了,但是你会在实际使用中发现,这尼玛的安卓定位时灵时不灵,真是mmp的,到底是怎么回事,那就是google提供的服务不够稳定,具体来说就是下面这几个东西的选择导致不稳定性

我在实际过程中进行了测试,发现当选择高精度、仅限设备时,反而获取不到经纬度,在低耗电量时可以获取到,情况就是这么个情况,那我们进行解决问题,我们通过代码调试会发现,最终问题会定位到代码红框中位置:

但我们却发现在ios系统中是正常的,既然不能在安卓上使用,那我们就集成我们国内的定位sdk,当然又得使用百度/高德了
2、解决思路

我们打算通过使用桥接原生的方法去集成百度sdk的方法获取经纬度信息,当然也可以直接获取周边地址,但我就只获取经纬度信息了

3、具体实现

在开始之前,我们需要在百度地图开发者中心去申请Android sdk方式的apikey,获取安卓sdk的方法可参见百度地图获取安卓sdk的方法,在获取到之后,我们需要按照百度的指引,进行添加到文件AndroidManifest.xml,具体内容如下,记得替换成你自己的apikey即可

        <service
            android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote" >
        </service>
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="你的android sdk密钥" />

既然是使用原生的sdk,那么就必须得导入sdk中的内容,需要导入的东西有如下内容,导入方式可参见百度官网:
在这里插入图片描述
接着我们就可以使用桥接的方式实现,我们需要进行写原生代码,react-native官网中可参见:中文网原生 ,如下面就是需要增加的原生模块
在这里插入图片描述
同时,需要将我们新增的定位模块添加到MainActivity中,如下面代码,这样才能够起作用

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new LocationPackage()
      );
    }

具体内容在GitHub 仓库中有实现,可自行下载,这里就不贴出来了,而前端调用代码如下所示:

/**
 * Created by jkwu on 2019/01/02.
 */
import React, { Component } from 'react';
import { View, ScrollView, Text, NativeModules } from 'react-native';
const LocationModule = NativeModules.LocationModule;
export default class BaiduApi extends Component {
  constructor(props) {
    super(props);
    this.watchPosition = null;
    this.state = {coords: '' };
  }

  componentDidMount() {
    LocationModule.initLocation();
    LocationModule.getBDLocation()
      .then(bdlocation => {
        console.log(bdlocation);
        this.setState({ coords: { ...bdlocation } });
      });
  }

  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 5 }}>
        <ScrollView>
          <View style={{ flexDirection: 'column' }}>
            <Text>{JSON.stringify(this.state.coords)}</Text>
          </View>
        </ScrollView>
      </View>
    );
  }
}

现在我们就能够在安卓上面进行获取到经纬度信息了,然后再去按照之前的操作去使用即可,下面就是获取到的经纬度信息

更新编辑于2019-01-02


评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

suwu150

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值