判斷手機是否安裝高德/百度地圖-經緯度座標轉換
學習資料
http://blog.csdn.net/sinat_19917631/article/details/54343791
最近需要做一個關於導航的功能,得用手機存在的地圖來開啟,然後從網上找了一篇部落格裡面只寫了怎麼判斷手機是否安裝百度地圖的,從網上各種翻,終於在高德地圖上找到一句話。
開發者在呼叫URI 之前需要先判斷是否安裝了高德地圖APP。如果沒有安裝,下載安裝高德地圖。然後呼叫相關地圖功能,如周邊實時路況資訊顯示,示例如下:
Intent intent = new Intent("android.intent.action.VIEW",
android.net.Uri.parse ("androidamap://showTraffic?sourceApplication=softname&poiid=BGVIS1&lat=36.2&lon=116.1&level=10&dev=0"));
intent.setPackage(“com.autonavi.minimap”);
startActivity(intent);
然後綜合網上那篇部落格程式碼如下,
/**
* 檢查手機上是否安裝了指定的軟體
* @param context
* @param packageName:應用包名
* @return
*/
private boolean isAvilible(Context context, String packageName){
//獲取packagemanager
final PackageManager packageManager = context.getPackageManager();
//獲取所有已安裝程式的包資訊
List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);
//用於儲存所有已安裝程式的包名
List<String> packageNames = new ArrayList<String>();
//從pinfo中將包名字逐一取出,壓入pName list中
if(packageInfos != null){
for(int i = 0; i < packageInfos.size(); i++){
String packName = packageInfos.get(i).packageName;
packageNames.add(packName);
}
}
//判斷packageNames中是否有目標程式的包名,有TRUE,沒有FALSE
return packageNames.contains(packageName);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.common_right:
//如果已安裝,
if(isAvilible(mcontext,"com.baidu.BaiduMap")) {//傳入指定應用包名
WinToast.makeText(mcontext,"即將用百度地圖開啟導航").show();
Uri mUri = Uri.parse("geo:"+jingdu+","+weidu+"?q="+title);
Intent mIntent = new Intent(Intent.ACTION_VIEW,mUri);
startActivity(mIntent);
}else if(isAvilible(mcontext,"com.autonavi.minimap")){
WinToast.makeText(mcontext,"即將用高德地圖開啟導航").show();
Uri mUri = Uri.parse("geo:"+jingdu+","+weidu+"?q="+title);
Intent intent = new Intent("android.intent.action.VIEW",mUri);
startActivity(intent);
}else {
WinToast.makeText(mcontext,"請安裝第三方地圖方可導航").show();
return;
}
break;
default:
break;
}
}
現在的問題
我們app內建了地圖,用一個經緯度,舉例子a,b 以ab設定了一個mark點,點選這個mark點,把ab傳遞給高德地圖,高德地圖出現的這個ab點不是app內建高德的ab點,於是乎,晚上寫的腦子有點蒙,第二天白天想到了,可能是經緯度的編碼格式不一樣,於是乎百度了下
學習資料
http://blog.csdn.net/a13570320979/article/details/51366355
一。在進行地圖開發過程中,我們一般能接觸到以下三種類型的地圖座標系:
1.WGS-84原始座標系,一般用國際GPS紀錄儀記錄下來的經緯度,通過GPS定位拿到的原始經緯度,Google和高德地圖定位的的經緯度(國外)都是基於WGS-84座標系的;但是在國內是不允許直接用WGS84座標系標註的,必須經過加密後才能使用;
2.GCJ-02座標系,又名“火星座標系”,是我國國測局獨創的座標體系,由WGS-84加密而成,在國內,必須至少使用GCJ-02座標系,或者使用在GCJ-02加密後再進行加密的座標系,如百度座標系。高德和Google在國內都是使用GCJ-02座標系,可以說,GCJ-02是國內最廣泛使用的座標系;
3.百度座標系:bd-09,百度座標系是在GCJ-02座標系的基礎上再次加密偏移後形成的座標系,只適用於百度地圖。(目前百度API提供了從其它座標系轉換為百度座標系的API,但卻沒有從百度座標系轉為其他座標系的API)
二。為什麼會發生偏移?
1.由於座標系之間不相容,如在百度地圖上定位的經緯度拿到高德地圖上直接描點就肯定會發生偏移;只考慮國內的情況,高德地圖和Google地圖是可以不經過轉換也能夠準確顯示的(在國內用的都是GCJ-02座標系);下面是收錄了網上的WGS-84,GCJ-02,百度座標系(bd-09)之間的相互轉換的方法,經測試,是轉換後相對準確可用的:
package com.asiabasehk.cgg.util;
/**火星座標系 (GCJ-02) 與百度座標系 (BD-09) 的互轉
* Created by macremote on 16/5/3.
*/
public class GPSUtil {
public static double pi = 3.1415926535897932384626;
public static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
public static double a = 6378245.0;
public static double ee = 0.00669342162296594323;
public static 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;
}
public static 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;
}
public static double[] transform(double lat, double lon) {
if (outOfChina(lat, lon)) {
return new double[]{lat,lon};
}
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 double[]{mgLat,mgLon};
}
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;
}
/**
* 84 to 火星座標系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
*
* @param lat
* @param lon
* @return
*/
public static double[] gps84_To_Gcj02(double lat, double lon) {
if (outOfChina(lat, lon)) {
return new double[]{lat,lon};
}
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 double[]{mgLat, mgLon};
}
/**
* * 火星座標系 (GCJ-02) to 84 * * @param lon * @param lat * @return
* */
public static double[] gcj02_To_Gps84(double lat, double lon) {
double[] gps = transform(lat, lon);
double lontitude = lon * 2 - gps[1];
double latitude = lat * 2 - gps[0];
return new double[]{latitude, lontitude};
}
/**
* 火星座標系 (GCJ-02) 與百度座標系 (BD-09) 的轉換演算法 將 GCJ-02 座標轉換成 BD-09 座標
*
* @param lat
* @param lon
*/
public static double[] gcj02_To_Bd09(double lat, double lon) {
double x = lon, y = lat;
double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
double tempLon = z * Math.cos(theta) + 0.0065;
double tempLat = z * Math.sin(theta) + 0.006;
double[] gps = {tempLat,tempLon};
return gps;
}
/**
* * 火星座標系 (GCJ-02) 與百度座標系 (BD-09) 的轉換演算法 * * 將 BD-09 座標轉換成GCJ-02 座標 * * @param
* bd_lat * @param bd_lon * @return
*/
public static double[] bd09_To_Gcj02(double lat, double lon) {
double x = lon - 0.0065, y = lat - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
double tempLon = z * Math.cos(theta);
double tempLat = z * Math.sin(theta);
double[] gps = {tempLat,tempLon};
return gps;
}
/**將gps84轉為bd09
* @param lat
* @param lon
* @return
*/
public static double[] gps84_To_bd09(double lat,double lon){
double[] gcj02 = gps84_To_Gcj02(lat,lon);
double[] bd09 = gcj02_To_Bd09(gcj02[0],gcj02[1]);
return bd09;
}
public static double[] bd09_To_gps84(double lat,double lon){
double[] gcj02 = bd09_To_Gcj02(lat, lon);
double[] gps84 = gcj02_To_Gps84(gcj02[0], gcj02[1]);
//保留小數點後六位
gps84[0] = retain6(gps84[0]);
gps84[1] = retain6(gps84[1]);
return gps84;
}
/**保留小數點後六位
* @param num
* @return
*/
private static double retain6(double num){
String result = String .format("%.6f", num);
return Double.valueOf(result);
}
}
2.在國內定位的經緯度,然後在國外網路下顯示也會發生偏移(谷歌和高德會依據網路的情況選擇使用WGS-84座標還是GCJ-02座標,百度地圖則一直使用bd-02座標系)
3.定位方式,在iOS定位的經緯度是通過GPS獲取的,在android則可以通過網路或GPS獲取經緯度。通過地圖SDK定位獲取的經緯度,地圖SDK會自動選擇加密的方式(如Google地圖會根據國內國外選擇不同的座標系)然後再將點顯示在地圖上,這個時候是沒有偏移的;如果直接將經緯度在地圖上顯示,可能就會因為地域或網路的問題導致使用的座標系不同,進而發生來偏移。
case R.id.map_gaode:
// markerOptions.position(new LatLng(34.347527, 108.945003));
Toast.makeText(MapActivity.this,"即將用高德地圖開啟導航",Toast.LENGTH_SHORT).show();
double a[]= GPSUtil.gcj02_To_Gps84(34.347527,108.945003);//lon 維度 lat 經度
double lat=a[0];
double lon=a[1];
LoggerUtil.i("latlon",lat+" "+lon);
Uri mUri = Uri.parse("geo:"+lat+""+","+lon+""+"?q="+"鵬翔駕校");
Intent mIntent = new Intent(Intent.ACTION_VIEW,mUri);
startActivity(mIntent);
break;
貼下這個類的程式碼
/**
* 高德地圖地圖頁面
* @author 馮昕睿
*/
public class MapActivity extends AppCompatActivity implements LocationSource,AMapLocationListener,View.OnClickListener {
private PopupWindow mPopWindow;
//AMap是地圖物件
private AMap aMap;
private MapView mapView;
//宣告AMapLocationClient類物件,定位發起端
private AMapLocationClient mLocationClient = null;
//宣告mLocationOption物件,定位引數
public AMapLocationClientOption mLocationOption = null;
//宣告mListener物件,定位監聽器
private OnLocationChangedListener mListener = null;
//標識,用於判斷是否只顯示一次定位資訊和使用者重新定位
private boolean isFirstLoc = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
//獲取地圖控制元件引用
mapView = (MapView) findViewById(R.id.map);
//在activity執行onCreate時執行mMapView.onCreate(savedInstanceState),建立地圖
mapView.onCreate(savedInstanceState);
if (aMap == null) {
aMap = mapView.getMap();
//設定顯示定位按鈕 並且可以點選
UiSettings settings = aMap.getUiSettings();
aMap.setLocationSource(this);//設定了定位的監聽
// 是否顯示定位按鈕
settings.setMyLocationButtonEnabled(true);
aMap.setMyLocationEnabled(true);//顯示定位層並且可以觸發定位,預設是flase
}
//開始定位
location();
// LatLng latLng = new LatLng(34.347527,108.945003);
// final Marker marker = aMap.addMarker(new MarkerOptions().position(latLng).title("鵬翔駕校").snippet("北一練車點"));
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(new LatLng(34.347527, 108.945003));
markerOptions.title("當前位置");
markerOptions.visible(true);
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.mark2));
markerOptions.icon(bitmapDescriptor);
aMap.addMarker(markerOptions);
// 繫結 Marker 被點選事件
aMap.setOnMarkerClickListener(markerClickListener);
}
private void location() {
//初始化定位
mLocationClient = new AMapLocationClient(getApplicationContext());
//設定定位回撥監聽
mLocationClient.setLocationListener(this);
//初始化定位引數
mLocationOption = new AMapLocationClientOption();
//設定定位模式為Hight_Accuracy高精度模式,Battery_Saving為低功耗模式,Device_Sensors是僅裝置模式
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
//設定是否返回地址資訊(預設返回地址資訊)
mLocationOption.setNeedAddress(true);
//設定是否只定位一次,預設為false
mLocationOption.setOnceLocation(false);
//設定是否強制重新整理WIFI,預設為強制重新整理
mLocationOption.setWifiActiveScan(true);
//設定是否允許模擬位置,預設為false,不允許模擬位置
mLocationOption.setMockEnable(false);
//設定定位間隔,單位毫秒,預設為2000ms
mLocationOption.setInterval(2000);
//給定位客戶端物件設定定位引數
mLocationClient.setLocationOption(mLocationOption);
//啟動定位
mLocationClient.startLocation();
}
@Override
protected void onDestroy() {
super.onDestroy();
//在activity執行onDestroy時執行mMapView.onDestroy(),銷燬地圖
mapView.onDestroy();
mLocationClient.stopLocation();//停止定位
mLocationClient.onDestroy();//銷燬定位客戶端。
}
@Override
protected void onResume() {
super.onResume();
//在activity執行onResume時執行mMapView.onResume (),重新繪製載入地圖
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//在activity執行onPause時執行mMapView.onPause (),暫停地圖的繪製
mapView.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//在activity執行onSaveInstanceState時執行mMapView.onSaveInstanceState (outState),儲存地圖當前的狀態
mapView.onSaveInstanceState(outState);
}
@Override
public void activate(OnLocationChangedListener onLocationChangedListener) {
mListener = onLocationChangedListener;
}
@Override
public void deactivate() {
mListener = null;
}
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
if (aMapLocation != null) {
if (aMapLocation.getErrorCode() == 0) {
//定位成功回撥資訊,設定相關訊息
aMapLocation.getLocationType();//獲取當前定位結果來源,如網路定位結果,詳見官方定位型別表
aMapLocation.getLatitude();//獲取緯度
aMapLocation.getLongitude();//獲取經度
aMapLocation.getAccuracy();//獲取精度資訊
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(aMapLocation.getTime());
df.format(date);//定位時間
aMapLocation.getAddress();//地址,如果option中設定isNeedAddress為false,則沒有此結果,網路定位結果中會有地址資訊,GPS定位不返回地址資訊。
aMapLocation.getCountry();//國家資訊
aMapLocation.getProvince();//省資訊
aMapLocation.getCity();//城市資訊
aMapLocation.getDistrict();//城區資訊
aMapLocation.getStreet();//街道資訊
aMapLocation.getStreetNum();//街道門牌號資訊
aMapLocation.getCityCode();//城市編碼
aMapLocation.getAdCode();//地區編碼
// 如果不設定標誌位,此時再拖動地圖時,它會不斷將地圖移動到當前的位置
if (isFirstLoc) {
//設定縮放級別
aMap.moveCamera(CameraUpdateFactory.zoomTo(11));
//將地圖移動到定位點
aMap.moveCamera(CameraUpdateFactory.changeLatLng(new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude())));
//點選定位按鈕 能夠將地圖的中心移動到定位點
mListener.onLocationChanged(aMapLocation);
//新增圖釘
// aMap.addMarker(getMarkerOptions(amapLocation));
// aMap.addMarker();
//獲取定位資訊
StringBuffer buffer = new StringBuffer();
buffer.append(aMapLocation.getCountry() + ""
+ aMapLocation.getProvince() + ""
+ aMapLocation.getCity() + ""
+ aMapLocation.getProvince() + ""
+ aMapLocation.getDistrict() + ""
+ aMapLocation.getStreet() + ""
+ aMapLocation.getStreetNum());
// Toast.makeText(getApplicationContext(), buffer.toString(), Toast.LENGTH_LONG).show();
isFirstLoc = false;
}
} else {
//顯示錯誤資訊ErrCode是錯誤碼,errInfo是錯誤資訊,詳見錯誤碼錶。
Log.e("AmapError", "location Error, ErrCode:"
+ aMapLocation.getErrorCode() + ", errInfo:"
+ aMapLocation.getErrorInfo());
Toast.makeText(getApplicationContext(), "定位失敗", Toast.LENGTH_LONG).show();
}
}
}
// 定義 Marker 點選事件監聽
AMap.OnMarkerClickListener markerClickListener = new AMap.OnMarkerClickListener() {
// marker 物件被點選時回撥的介面
// 返回 true 則表示介面已響應事件,否則返回false
@Override
public boolean onMarkerClick(Marker marker) {
// Toast.makeText(MapActivity.this, "666", Toast.LENGTH_SHORT).show();
showPopupWindow();
return false;
}
};
private void showPopupWindow() {
View contentView= LayoutInflater.from(MapActivity.this).inflate(R.layout.popu_map,null);
mPopWindow = new PopupWindow(contentView,
WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, true);
mPopWindow.setContentView(contentView);
View map_gaode=(View)contentView.findViewById(R.id.map_gaode);
View map_baidu=(View)contentView.findViewById(R.id.map_baidu);
map_gaode.setOnClickListener(this);
map_baidu.setOnClickListener(this);
//顯示popupwindow
View rootview=LayoutInflater.from(MapActivity.this).inflate(R.layout.activity_map,null);
mPopWindow.setAnimationStyle(R.style.contextMenuAnim);
mPopWindow.showAtLocation(rootview, Gravity.CENTER,0,0);
}
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
* (34.347527, 108.945003)
*/
@Override
public void onClick(View v) {
int id=v.getId();
switch (id){
case R.id.map_gaode:
// markerOptions.position(new LatLng(34.347527, 108.945003));
Toast.makeText(MapActivity.this,"即將用高德地圖開啟導航",Toast.LENGTH_SHORT).show();
double a[]= GPSUtil.gcj02_To_Gps84(34.347527,108.945003);//lon 維度 lat 經度
double lat=a[0];
double lon=a[1];
LoggerUtil.i("latlon",lat+" "+lon);
Uri mUri = Uri.parse("geo:"+lat+""+","+lon+""+"?q="+"鵬翔駕校");
Intent mIntent = new Intent(Intent.ACTION_VIEW,mUri);
startActivity(mIntent);
break;
case R.id.map_baidu:
// Toast.makeText(MapActivity.this ,"baidu",Toast.LENGTH_SHORT).show();
// Toast.makeText(MapActivity.this,"即將用百度地圖開啟導航",Toast.LENGTH_SHORT).show();
double b[]= GPSUtil.gcj02_To_Gps84(34.347527,108.945003);//lon 維度 lat 經度
double lat2=b[0];
double lon2=b[1];
LoggerUtil.i("latlon",lat2+" "+lon2);
Uri mUri2 = Uri.parse("geo:"+lat2+""+","+lon2+""+"?q="+"鵬翔駕校");
Intent mInten2t = new Intent(Intent.ACTION_VIEW,mUri2);
startActivity(mInten2t);
break;
default:
break;
}
}
/**
* 檢查手機上是否安裝了指定的軟體
* @param context
* @param packageName:應用包名
* @return
*/
public boolean isAvilible(Context context, String packageName){
//獲取packagemanager
final PackageManager packageManager = context.getPackageManager();
//獲取所有已安裝程式的包資訊
List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);
//用於儲存所有已安裝程式的包名
List<String> packageNames = new ArrayList<String>();
//從pinfo中將包名字逐一取出,壓入pName list中
if(packageInfos != null){
for(int i = 0; i < packageInfos.size(); i++){
String packName = packageInfos.get(i).packageName;
packageNames.add(packName);
}
}
//判斷packageNames中是否有目標程式的包名,有TRUE,沒有FALSE
return packageNames.contains(packageName);
}
}
切莫惶惶終日