開箱即用的Google與百度定位坐標系轉換實例
集成谷歌定位與百度定位並在地圖上顯示
項目背景
東南亞某小國的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 百度定位坐標系轉換的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Android原生定位服務LocationManager
- Android封裝高德地圖定位工具類Util的詳細步驟
- Android Studio實現幀動畫
- Android 模擬地圖定位功能的實現
- JavaSE學習之內部類及常用API