集成谷歌定位与百度定位并在地图上显示
项目背景
一. 集成谷歌定位
定位Api使用流程:
二. 集成百度定位
Application中初始化SDK
三. 坐标系转换与地图上的显示
集成谷歌定位与百度定位并在地图上显示 项目背景东南亚某小国的App,需要用到定位与地图显示,我们都知道海外人士都是喜欢谷歌地图与谷歌定位的,那么必然是要优先使用谷歌定位的,但是中国手机卖的很好,部分中国手机的海外版是没有谷歌服务的,那么我使用百度地图进行降级处理。
两者使用的定位坐标系不同,如果需要在谷歌地图上展示百度定位,需要如何转换呢?或者说谷歌的定位如何在百度地图上展示呢?
一. 集成谷歌定位跟项目下的build.gradle
别的先不说,先把谷歌服务插件给引入
dependencies {
classpath 'com.android.tools.build:gradle:7.0.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.8'
}
app模块下的build.gradle最底部 应用谷歌服务插件
apply plugin: 'com.google.gms.google-services'
然后去谷歌后台申请App-Key相关的项目,需要填写包名和一些签名文件的SHA1值,注意这里relese包的SHA1值,和谷歌市场上架的SHA1值是不同的,上架之后需要去谷歌市场把谷歌市场上的SHA1值设置进去,因为谷歌市场上架之后会把你的签名文件包装一次,完全不同的签名文件了。
完成此步之后 下载对应App的 google-services.json
文件。
此步骤完成,我们再远程依赖谷歌定位的库。location
//定位功能
api 'com.google.android.gms:play-services-location:16.0.0'
定位Api使用流程:
权限定义
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
注意使用之前要动态申请权限,这里不做演示
//初始化谷歌地图API
if (googleApiClient == null) {
googleApiClient = new GoogleApiClient.Builder(mActivity)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
//Google-Location-Api 服务连接成功回调
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.e("MapLastLocationUtils", "Google-Api服务连接成功了");
getAPILastLocation();
}
//Google-Location-Api 服务异常连接暂停
@Override
public void onConnectionSuspended(int i) {
Log.e("MapLastLocationUtils", "Google-Api服务被暂停了");
if (googleApiClient != null && !googleApiClient.isConnected())
googleApiClient.connect();
}
//Google-Location-Api 连接异常
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e("MapLastLocationUtils", "Google-Api服务连接错误");
//如果连接错误直接用百度定位
}
public void stopLocation() {
if (googleApiClient != null && googleApiClient.isConnected()){
googleApiClient.disconnect();
}
}
private void getAPILastLocation() {
FusedLocationProviderClient fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(mActivity);
fusedLocationProviderClient.getLastLocation().addOnSuccessListener(mActivity, location -> {
if (location != null && location.getLatitude() > 0 && location.getLongitude() > 0) {
double latitude = location.getLatitude();
double longitude = location.getLongitude();
Log.e("MapLastLocationUtils", "Google-API-获取到的最后的地址为:" + "Lat:" + latitude + " Lon:" + longitude);
} else {
//如果没有值直接用百度定位
}
});
}
二. 集成百度定位
我们也是需要在百度开发者平台上申请应用的App,不过这一块就比较简单,只需要验证包名就行了,然后会给你一个AppId。
我们下载定位的jar包与so动态库,导入到项目,然后再清单文件配置
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="123456789">
</meta-data>
<service
android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote">
<intent-filter>
<action android:name="com.baidu.location.service_v2.2"></action>
</intent-filter>
</service>
Application中初始化SDK
SDKInitializer.initialize(context);
判断开启谷歌定位还是百度定位的逻辑封装
public class MapLastLocationUtils implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
LifecycleObserver {
private static MapLastLocationUtils ourInstance;
private Activity mActivity;
private GoogleApiClient googleApiClient;
private LocationClient mBDLocationClient;
private LocationRequest mLocationRequest;
private boolean isNeedBackNotifyLocation = false;
public static MapLastLocationUtils getInstance(Activity context) {
if (ourInstance == null) {
ourInstance = new MapLastLocationUtils(context);
}
return ourInstance;
}
private MapLastLocationUtils(Activity context) {
mActivity = context;
//初始化谷歌地图API
if (googleApiClient == null) {
googleApiClient = new GoogleApiClient.Builder(mActivity)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
/**
* 只是获取当前的位置一次
*/
public void getLastLocation() {
int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(CommUtils.getContext());
if (code == ConnectionResult.SUCCESS) {
// 支持Google服务
getAPILastLocation();
} else {
//开启百度定位
getBDLastLocation();
}
}
//API连接成功尝试获取最后的位置
@SuppressLint("MissingPermission")
private void getAPILastLocation() {
FusedLocationProviderClient fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(mActivity);
fusedLocationProviderClient.getLastLocation().addOnSuccessListener(mActivity, location -> {
if (location != null && location.getLatitude() > 0 && location.getLongitude() > 0) {
double latitude = location.getLatitude();
double longitude = location.getLongitude();
Log.e("MapLastLocationUtils", "Google-API-获取到的最后的地址为:" + "Lat:" + latitude + " Lon:" + longitude);
if (mListener != null) {
//这里转换一下,如果国内转换为火星坐标,国外直接用
Gps gps = GPS84ToGCJ02(longitude, latitude);
mListener.onLastLocation(gps.getLatitude(), gps.getLongitude());
}
} else {
getBDLastLocation();
}
});
}
//百度定位尝试获取最后的位置
private void getBDLastLocation() {
mBDLocationClient = new LocationClient(mActivity);
//声明LocationClient类
mBDLocationClient.registerLocationListener(mBDLastLocationListener);
//配置百度定位的选项
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); // 可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
option.setCoorType("gcj02"); // 可选,默认gcj02,设置返回的定位结果坐标系,如果配合百度地图使用,建议设置为bd09ll; 国际WGS84
option.setScanSpan(0); // 可选,默认0,即仅定位一次,设置发起连续定位请求的间隔需要大于等于1000ms才是有效的
option.setIsNeedAddress(false); // 可选,设置是否需要地址信息,默认不需要
option.setIsNeedLocationDescribe(false); // 可选,设置是否需要地址描述
option.setNeedDeviceDirect(false); // 可选,设置是否需要设备方向结果
option.setLocationNotify(false); // 可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果
option.setIgnoreKillProcess(true); // 可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop
option.setIsNeedLocationDescribe(false); // 可选,默认false,设置是否需要位置语义化结果,可以在BDLocation
option.setIsNeedLocationPoiList(false); // 可选,默认false,设置是否需要POI结果,可以在BDLocation
option.SetIgnoreCacheException(false); // 可选,默认false,设置是否收集CRASH信息,默认收集
option.setOpenGps(false); // 可选,默认false,设置是否开启Gps定位
option.setIsNeedAltitude(false); // 可选,默认false,设置定位时是否需要海拔信息,默认不需要,除基础定位版本都可用
mBDLocationClient.setLocOption(option);
//开启定位
if (mBDLocationClient != null && !mBDLocationClient.isStarted()) {
mBDLocationClient.start();
}
}
//Google-Location-Api 服务连接成功回调
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.e("MapLastLocationUtils", "Google-Api服务连接成功了");
getAPILastLocation();
}
//Google-Location-Api 服务异常连接暂停
@Override
public void onConnectionSuspended(int i) {
Log.e("MapLastLocationUtils", "Google-Api服务被暂停了");
if (googleApiClient != null && !googleApiClient.isConnected())
googleApiClient.connect();
}
//Google-Location-Api 连接异常
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e("MapLastLocationUtils", "Google-Api服务连接错误");
//如果连接错误直接用百度定位吧
getBDLastLocation();
}
/**
* 停止全部的定位
*/
public void stopLocation() {
if (googleApiClient != null && googleApiClient.isConnected()) {
googleApiClient.disconnect();
}
if (mBDLocationClient != null) {
if (mBDLastLocationListener != null) {
mBDLocationClient.unRegisterLocationListener(mBDLastLocationListener);
}
YYLogUtils.w("定位stop了");
mBDLocationClient.stop();
}
}
private OnLocationCallbackListener mListener;
public void setOnLocationCallbackListener(OnLocationCallbackListener listener) {
mListener = listener;
}
public interface OnLocationCallbackListener {
void onLastLocation(double lat, double lon);
}
/**
* 百度的监听回调(最后的地址)
*/
BDAbstractLocationListener mBDLastLocationListener = new BDAbstractLocationListener() {
//百度定位成功的展示
@Override
public void onReceiveLocation(BDLocation bdLocation) {
if (bdLocation != null && bdLocation.getLocType() != BDLocation.TypeServerError &&
!(bdLocation.getLatitude() + "").equals("4.9E-324") &&
!(bdLocation.getLongitude() + "").equals("4.9E-324")) {
double latitude = bdLocation.getLatitude(); //获取纬度信息
double longitude = bdLocation.getLongitude(); //获取经度信息
Log.w("MapLastLocationUtils", "百度定位获取到的最后的地址为:" + "Lat:" + latitude + " Lon:" + longitude);
//获取到了最后的位置-直接回调
if (mListener != null) {
mListener.onLastLocation(latitude, longitude);
}
} else {
Log.e("MapLastLocationUtils", "百度定位-获取位置失败");
if (googleApiClient != null && !googleApiClient.isConnected()) {
googleApiClient.connect();
} else {
getAPILastLocation();
}
}
}
@Override
public void onConnectHotSpotMessage(String s, int i) {
super.onConnectHotSpotMessage(s, i);
}
//百度定位的错误信息展示
@Override
public void onLocDiagnosticMessage(int locType, int diagnosticType, String diagnosticMessage) {
super.onLocDiagnosticMessage(locType, diagnosticType, diagnosticMessage);
int tag = 2;
StringBuffer sb = new StringBuffer(256);
sb.append("诊断结果: ");
if (locType == BDLocation.TypeNetWorkLocation) {
if (diagnosticType == 1) {
sb.append("网络定位成功,没有开启GPS,建议打开GPS会更好");
sb.append("\n" + diagnosticMessage);
} else if (diagnosticType == 2) {
sb.append("网络定位成功,没有开启Wi-Fi,建议打开Wi-Fi会更好");
sb.append("\n" + diagnosticMessage);
}
} else if (locType == BDLocation.TypeOffLineLocationFail) {
if (diagnosticType == 3) {
sb.append("定位失败,请您检查您的网络状态");
sb.append("\n" + diagnosticMessage);
}
} else if (locType == BDLocation.TypeCriteriaException) {
if (diagnosticType == 4) {
sb.append("定位失败,无法获取任何有效定位依据");
sb.append("\n" + diagnosticMessage);
} else if (diagnosticType == 5) {
sb.append("定位失败,无法获取有效定位依据,请检查运营商网络或者Wi-Fi网络是否正常开启,尝试重新请求定位");
sb.append(diagnosticMessage);
} else if (diagnosticType == 6) {
sb.append("定位失败,无法获取有效定位依据,请尝试插入一张sim卡或打开Wi-Fi重试");
sb.append("\n" + diagnosticMessage);
} else if (diagnosticType == 7) {
sb.append("定位失败,飞行模式下无法获取有效定位依据,请关闭飞行模式重试");
sb.append("\n" + diagnosticMessage);
} else if (diagnosticType == 9) {
sb.append("定位失败,无法获取任何有效定位依据");
sb.append("\n" + diagnosticMessage);
}
} else if (locType == BDLocation.TypeServerError) {
if (diagnosticType == 8) {
sb.append("定位失败,请确认您定位的开关打开状态,是否赋予APP定位权限");
sb.append("\n" + diagnosticMessage);
}
}
Log.e("MapLastLocationUtils", sb.toString());
}
};
// ======================= update begin ↓ =========================
/**
* 开启后台的通知栏定位
*/
public void startBackNotifyLocation() {
isNeedBackNotifyLocation = true;
if (mBDLocationClient == null) {
mBDLocationClient = new LocationClient(mActivity);
}
LocationClientOption mOption = new LocationClientOption();
mOption.setScanSpan(15000);
mOption.setIsNeedAddress(false);
mOption.setOpenGps(true);
mBDLocationClient.setLocOption(mOption);
// mClient.registerLocationListener(mBDUpdateLocationListener);
Notification notification;
if (Build.VERSION.SDK_INT >= 26) {
NotificationUtils mNotificationUtils = new NotificationUtils(mActivity);
Notification.Builder builder2 = mNotificationUtils.getAndroidChannelNotification("YY Circle", "Locating in the background");
builder2.setSmallIcon(R.drawable.ic_launcher_map);
notification = builder2.build();
} else {
//获取一个Notification构造器
Notification.Builder builder = new Notification.Builder(mActivity);
builder.setContentTitle("YY Circle") // 设置下拉列表里的标题
.setSmallIcon(R.drawable.ic_launcher_map) // 设置状态栏内的小图标
.setContentText("Locating in the background") // 设置上下文内容
.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
notification = builder.build(); // 获取构建好的Notification
}
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
//开启前台通知的后台定位
mBDLocationClient.enableLocInForeground(1, notification);
mBDLocationClient.start();
}
// ======================= 转换 begin ↓ =========================
private double pi = 3.1415926535897932384626;
private double a = 6378245.0;
private double ee = 0.00669342162296594323;
/**
* 国际 GPS84 坐标系
* 转换成
* [国测局坐标系] 火星坐标系 (GCJ-02)
* <p>
* World Geodetic System ==> Mars Geodetic System
*
* @param lon 经度
* @param lat 纬度
* @return GPS实体类
*/
public Gps GPS84ToGCJ02(double lon, double lat) {
if (outOfChina(lat, lon)) {
YYLogUtils.e("GPS84ToGCJ02:国外的坐标不做处理");
return new Gps(lon, lat);
} else {
YYLogUtils.e("GPS84ToGCJ02:处理国内的坐标");
double dLat = transformLat(lon - 105.0, lat - 35.0);
double dLon = transformLon(lon - 105.0, lat - 35.0);
double radLat = lat / 180.0 * pi;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
return new Gps(mgLon, mgLat);
}
}
/**
* [国测局坐标系] 火星坐标系 (GCJ-02)
* 转换成
* 国际 GPS84 坐标系
*
* @param lon 火星经度
* @param lat 火星纬度
*/
public Gps GCJ02ToGPS84(double lon, double lat) {
Gps gps = transform(lon, lat);
double lontitude = lon * 2 - gps.getLongitude();
double latitude = lat * 2 - gps.getLatitude();
return new Gps(lontitude, latitude);
}
private Gps transform(double lon, double lat) {
if (outOfChina(lon, lat)) {
return new Gps(lon, lat);
}
double dLat = transformLat(lon - 105.0, lat - 35.0);
double dLon = transformLon(lon - 105.0, lat - 35.0);
double radLat = lat / 180.0 * pi;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
return new Gps(mgLon, mgLat);
}
/**
* 不在中国范围内
*
* @param lon 经度
* @param lat 纬度
* @return boolean值
*/
public static boolean outOfChina(double lat, double lon) {
if (lon < 72.004 || lon > 137.8347)
return true;
if (lat < 0.8293 || lat > 55.8271)
return true;
return false;
}
/**
* 纬度转化算法
*
* @param x
* @param y
* @return
*/
private double transformLat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
+ 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
return ret;
}
/**
* 经度转化算法
*
* @param x
* @param y
* @return
*/
private double transformLon(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
* Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0
* pi)) * 2.0 / 3.0;
return ret;
}
}
三. 坐标系转换与地图上的显示
网上一搜坐标系那可太多了,我们不整那么复杂,我们Android就知道2个 gcj02(中国国测局) 和 wgs84(通过坐标系) 。由于我们不是国内的应用,并且集成了非单一的定位软件,所以我们就不知道私有的坐标系如bd09之类的。
可以看到我们上面的工具类都是使用的gcj02坐标系定位的,在中国就需要gcj02定位,这样比较准确,如果要在对应的地图上展示,则需要对应的坐标系经纬度,在对应的坐标系地图上展示。
如我们的定位是gcj02的坐标系,在谷歌地图上展示则需要转换为wgs之后才对,不然会有大概800米的误差。百度地图则可以设置坐标系,设置为gcj02之后就可以在百度地图上展示。
坐标系的转换工具类在上面就有了。
以上就是开箱即用的Google与百度定位坐标系转换实例的详细内容,更多关于Google 百度定位坐标系转换的资料请关注软件开发网其它相关文章!