利用百度地图提供的sdk实现定位功能(3) 调用本地app导航、根据经纬度获取两个点的直线距离
参考百度地图提供的开发指南:http://lbsyun.baidu.com/index.php?title=android-locsdk/guide/getloc
上面几个文件夹中的文件放的位置可参考:对地图的基本操作,标注地址,公交步行驾车路线搜索,附近搜索等(2)
这里openUtils文件夹放的是实现公交,步行,驾车,附近搜索路线查询等 需要的根据类,这里定位不需要用到
package com.workspace.my.baidumaptest; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import com.baidu.location.BDLocation; import com.baidu.location.BDLocationListener; import com.baidu.location.LocationClient; import com.baidu.location.LocationClientOption; import com.baidu.location.Poi; import com.baidu.mapapi.SDKInitializer; import com.baidu.mapapi.map.BaiduMap; import com.baidu.mapapi.map.BitmapDescriptor; import com.baidu.mapapi.map.BitmapDescriptorFactory; import com.baidu.mapapi.map.MapStatus; import com.baidu.mapapi.map.MapStatusUpdateFactory; import com.baidu.mapapi.map.MapView; import com.baidu.mapapi.map.MarkerOptions; import com.baidu.mapapi.map.OverlayOptions; import com.baidu.mapapi.model.LatLng; import java.util.List; /** * 定位,在地图上显示 */ public class MainActivity extends AppCompatActivity { private MapView mMapView; public LocationClient mLocationClient = null; public BDLocationListener myListener = new MyLocationListener(); private BaiduMap mBaiduMap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SDKInitializer.initialize(getApplicationContext()); setContentView(R.layout.activity_main); //获取地图控件引用 mMapView = (MapView) findViewById(R.id.bmapView); mBaiduMap = mMapView.getMap(); mLocationClient = new LocationClient(getApplicationContext()); //声明LocationClient类 mLocationClient.registerLocationListener(myListener);//定位的监听回调 //配置定位SDK参数 initLocation(); } private void initLocation(){ LocationClientOption option = new LocationClientOption(); option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy );//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备 option.setCoorType("bd09ll");//可选,默认gcj02,设置返回的定位结果坐标系 int span=1000;//1秒定位一次 option.setScanSpan(span);//可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的 option.setIsNeedAddress(true);//可选,设置是否需要地址信息,默认不需要 option.setOpenGps(true);//可选,默认false,设置是否使用gps option.setLocationNotify(true);//可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果 option.setIsNeedLocationDescribe(true);//可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近” option.setIsNeedLocationPoiList(true);//可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到 option.setIgnoreKillProcess(false);//可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死 option.SetIgnoreCacheException(false);//可选,默认false,设置是否收集CRASH信息,默认收集 option.setEnableSimulateGps(false);//可选,默认false,设置是否需要过滤gps仿真结果,默认需要 mLocationClient.setLocOption(option); } /** * 定位结果监听回调的方法 */ public class MyLocationListener implements BDLocationListener { @Override public void onReceiveLocation(BDLocation location) { //Receive Location StringBuffer sb = new StringBuffer(256); sb.append("time : "); sb.append(location.getTime()); sb.append("\nerror code : "); sb.append(location.getLocType()); sb.append("\nlatitude : "); sb.append(location.getLatitude()); sb.append("\nlontitude : "); sb.append(location.getLongitude()); sb.append("\nradius : "); sb.append(location.getRadius()); if (location.getLocType() == BDLocation.TypeGpsLocation) {// GPS定位结果 sb.append("\nspeed : "); sb.append(location.getSpeed());// 单位:公里每小时 sb.append("\nsatellite : "); sb.append(location.getSatelliteNumber()); sb.append("\nheight : "); sb.append(location.getAltitude());// 单位:米 sb.append("\ndirection : "); sb.append(location.getDirection());// 单位度 sb.append("\naddr : "); sb.append(location.getAddrStr()); sb.append("\ndescribe : "); sb.append("gps定位成功"); } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) {// 网络定位结果 sb.append("\naddr : "); sb.append(location.getAddrStr()); //运营商信息 sb.append("\noperationers : "); sb.append(location.getOperators()); sb.append("\ndescribe : "); sb.append("网络定位成功"); } else if (location.getLocType() == BDLocation.TypeOffLineLocation) {// 离线定位结果 sb.append("\ndescribe : "); sb.append("离线定位成功,离线定位结果也是有效的"); } else if (location.getLocType() == BDLocation.TypeServerError) { sb.append("\ndescribe : "); sb.append("服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因"); } else if (location.getLocType() == BDLocation.TypeNetWorkException) { sb.append("\ndescribe : "); sb.append("网络不同导致定位失败,请检查网络是否通畅"); } else if (location.getLocType() == BDLocation.TypeCriteriaException) { sb.append("\ndescribe : "); sb.append("无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机"); } sb.append("\nlocationdescribe : "); sb.append(location.getLocationDescribe());// 位置语义化信息 List<Poi> list = location.getPoiList();// POI数据 if (list != null) { sb.append("\npoilist size = : "); sb.append(list.size()); for (Poi p : list) { sb.append("\npoi= : "); sb.append(p.getId() + " " + p.getName() + " " + p.getRank()); } } Log.i("BaiduLocationApiDem", sb.toString()); //将地图显示到定位的地方 MapStatus mapStatus = new MapStatus.Builder() .target(new LatLng(location.getLatitude(),location.getLongitude())) .build(); mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(mapStatus)); //为这个地点增加标注 //定义Maker坐标点 LatLng point = new LatLng(location.getLatitude(), location.getLongitude());//纬度,经度 //构建Marker图标 BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(R.drawable.icon_marka); //构建MarkerOption设置参数,用于在地图上添加Marker(标志) OverlayOptions option = new MarkerOptions() .position(point) //设置参数 .icon(bitmap);//设置图片 //在地图上添加Marker(标注),并显示 mBaiduMap.addOverlay(option); //可以在这里进行关闭定位 mLocationClient.stop(); } } /** * 单击事件,开始定位,如果不关闭,会一直刷新定位信息(在服务中执行的) * 开发者定位场景如果是单次定位的场景,在收到定位结果之后直接调用stop函数即可。 * 如果stop之后仍然想进行定位,可以再次start等待定位结果回调即可。 */ public void onStartLocation(View view){ mLocationClient.start(); } @Override protected void onDestroy() { super.onDestroy(); //在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理 mMapView.onDestroy(); } @Override protected void onResume() { super.onResume(); //在activity执行onResume时执行mMapView. onResume (),实现地图生命周期管理 mMapView.onResume(); } @Override protected void onPause() { super.onPause(); //在activity执行onPause时执行mMapView. onPause (),实现地图生命周期管理 mMapView.onPause(); } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_location" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="开始定位" android:onClick="onStartLocation" /> <com.baidu.mapapi.map.MapView android:id="@+id/bmapView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:clickable="true" /> </LinearLayout>
清单注册文件:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.workspace.my.baidumaptest" > <!--定位加显示百度地图需要的权限--> <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.GET_TASKS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <!-- 这个权限用于进行网络定位--> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission> <!-- 这个权限用于访问GPS定位--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> <!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位--> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> <!-- 获取运营商信息,用于支持提供运营商信息相关的接口--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> <!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位--> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> <!-- 用于读取手机当前的状态--> <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <!-- 访问网络,网络定位需要上网--> <uses-permission android:name="android.permission.INTERNET" /> <!-- SD卡读取权限,用户写入离线定位数据--> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!--定位需要的服务(百度sdk中要求的)--> <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="Gy5HsOUWn0M77QMNC46CYwvmxHHWvMIv" /> </application> </manifest>
效果图:
检测是否打开了GPS,并去设置:
private void initGPS() { LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); // 判断GPS模块是否开启,如果没有则开启 if (!locationManager .isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) { AlertDialog dialog = new AlertDialog.Builder(MainActivity.this) .setTitle("提示对话框").setMessage("没有打开GPS,可能导致定位失败或定位不准") .setPositiveButton("登录", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 转到手机设置界面,用户设置GPS Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivityForResult(intent, 0); // 设置完成后返回到原来的界面 //在manifest文件中将Activity的launchMode设置为了singleTask,获取的结果码会一直为0 } }).setNegativeButton("退出", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }).create(); dialog.show(); } } @Override protected void onActivityResult(int arg0, int arg1, Intent arg2) { super.onActivityResult(arg0, arg1, arg2); LocationManager locManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); if(locManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){ //开启了 mLocationClient.start(); } }
有时候需要手动去执行定位(比如,点击一个按钮去执行定位):
activity.mLocationClient.requestLocation();//去定位,如果stop了先start就行
文档是这么说的:
start:启动定位SDK。 stop:关闭定位SDK。调用start之后只需要等待定位结果自动回调即可。
开发者定位场景如果是单次定位的场景,在收到定位结果之后直接调用stop函数即可。
如果stop之后仍然想进行定位,可以再次start等待定位结果回调即可。
如果开发者想按照自己逻辑请求定位,可以在start之后按照自己的逻辑请求locationclient.requestLocation()函数,会主动触发定位SDK内部定位逻辑,等待定位回调即可。
调用本地地图实现导航:
百度地图:http://lbsyun.baidu.com/index.php?title=uri/api/android
高德地图:http://lbs.amap.com/api/uri-api/android-uri-explain/
腾讯地图:http://lbs.qq.com/uri_v1/index.html
谷歌地图:https://developers.google.com/maps/documentation/android-api/intents
代码如下:
import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.util.Log; import android.widget.Toast; import java.io.File; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; /** * 调用本地百度地图 或者 高德地图 导航 */ public class MapUtil { /* 获取安装的所有软件,并且判断是否安装了指定的软件 * @param context * @param packageName:应用包名 * @return */ public static 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); } /** * 判断是否安装目标应用 * @param packageName 目标应用安装后的包名 * @return 是否已安装目标应用 */ private boolean isInstallByread(String packageName) { return new File("/data/data/" + packageName).exists(); } /** * 调用百度地图导航 设置起点 与 终点 */ public static void toBaiDuMap(Context context, String startLat, String startLong, String endLat, String endLong){ Intent intent; if(isAvilible(context,"com.baidu.BaiduMap")){//传入指定应用包名 try { //intent = Intent.getIntent("intent://map/direction?origin=latlng:34.264642646862,108.95108518068|name:我家&destination=大雁塔&mode=driving®ion=西安&src=yourCompanyName|yourAppName#Intent;scheme=bdapp;package=com.baidu.BaiduMap;end"); intent = Intent.getIntent("intent://map/direction?" + "origin=latlng:"+startLat + "," + startLong + "|name:起点啊" + "&destination=latlng:"+endLat+","+endLong+"|name:我的目的地"+ //终点 "&mode=driving&" + //导航路线方式 driving=驾车 //"region=杭州&" + //范围 "src=事亲健康#Intent;scheme=bdapp;package=com.baidu.BaiduMap;end"); context.startActivity(intent); //启动调用 } catch (URISyntaxException e) { Log.e("intent", e.getMessage()); } }else{//未安装 //market为路径,id为包名 //显示手机上所有的market商店 Toast.makeText(context, "您尚未安装百度地图", Toast.LENGTH_LONG).show(); Uri uri = Uri.parse("market://details?id=com.baidu.BaiduMap"); intent = new Intent(Intent.ACTION_VIEW, uri); context.startActivity(intent); } } /** * 调用百度地图导航, 当前位置到目标位置 */ public static void toBaiDuMap(Context context,String endLat, String endLong){ Intent intent; if(isAvilible(context,"com.baidu.BaiduMap")){//传入指定应用包名 try { intent = Intent.getIntent("intent://map/direction?" + //"origin=latlng:"+"34.264642646862,108.95108518068&" + //起点 此处不传值默认选择当前位置 "destination=latlng:"+endLat+","+endLong+"|name:我的目的地"+ //终点显示的名称= “我的目的地” "&mode=driving&" + //导航路线方式 //"region=杭州&" + // "src=事亲健康#Intent;scheme=bdapp;package=com.baidu.BaiduMap;end"); context.startActivity(intent); //启动调用 } catch (URISyntaxException e) { Log.e("intent", e.getMessage()); } }else{//未安装 //market为路径,id为包名 //显示手机上所有的market商店 Toast.makeText(context, "您尚未安装百度地图", Toast.LENGTH_LONG).show(); Uri uri = Uri.parse("market://details?id=com.baidu.BaiduMap"); intent = new Intent(Intent.ACTION_VIEW, uri); context.startActivity(intent); } } /** * 高德地图 * @param context * @param sname 开始位置 在地图上显示的名称 * @param dname 终点位置 在地图上显示的名称 */ public static void toGaoDeMap(Context context, String startLat, String startLong, String endLat, String endLong,String sname,String dname){ Intent intent; if (isAvilible(context, "com.autonavi.minimap")) { try{ String url = "androidamap://route?sourceApplication=amap&slat="+startLat+"&slon="+startLong + "&sname=" + sname +"&dlat="+endLat+"&dlon="+endLong+"&dname="+ dname +"&dev=0&t=1"; Uri uri = Uri.parse(url); intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(uri); context.startActivity(intent); } catch (Exception e) {e.printStackTrace(); } }else{ Toast.makeText(context, "您尚未安装高德地图", Toast.LENGTH_LONG).show(); Uri uri = Uri.parse("market://details?id=com.autonavi.minimap");//去下载 intent = new Intent(Intent.ACTION_VIEW, uri); context.startActivity(intent); } } /** * 当前位置到指定位置 */ public static void toGaoDeMap(Context context,String endLat, String endLong,String dname){ Intent intent; if (isAvilible(context, "com.autonavi.minimap")) { String url = "androidamap://route?sourceApplication=amap" +"&dlat="+endLat+"&dlon="+endLong+"&dname="+ dname +"&dev=0&t=1"; Uri uri = Uri.parse(url); intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(uri); context.startActivity(intent); }else{ Toast.makeText(context, "您尚未安装高德地图", Toast.LENGTH_LONG).show(); Uri uri = Uri.parse("market://details?id=com.autonavi.minimap");//去下载 intent = new Intent(Intent.ACTION_VIEW, uri); context.startActivity(intent); } } }
测试的经纬度:
@Override public void onClick(View v) { switch (v.getId()){ case R.id.bt1: MapUtil.toGaoDeMap(this,"39.997361","116.478346","39.936915","116.402796","我是开始位置","我是终点位置"); break; case R.id.bt2: MapUtil.toGaoDeMap(this,"39.936915","116.402796","我是终点位置"); break; case R.id.bt5: MapUtil.toBaiDuMap(this,"30.195776","120.207283");//当前位置 - 滨江区滨安路467号(春波小区) break; case R.id.bt6: MapUtil.toBaiDuMap(this,"30.195776","120.207283","30.194661","120.221672"); break; } }
效果图:
当没有安装该软件时,效果图(去应用商店下载指定包名的apk)
根据两个经纬度获取两点的直线距离
/** * 根据两个经纬度计算两地距离 * @param longitude1 第一个位置的经度 * @param latitude1 纬度 * @param longitude2 * @param latitude2 * @return 返回的距离单位是米 */ public static double getDistance(double longitude1, double latitude1, double longitude2, double latitude2) { double Lat1 = rad(latitude1); double Lat2 = rad(latitude2); double a = Lat1 - Lat2; double b = rad(longitude1) - rad(longitude2); double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(Lat1) * Math.cos(Lat2) * Math.pow(Math.sin(b / 2), 2))); s = s * 6378137.0;// 6378137.0为地球半径 s = Math.round(s * 10000) / 10000; return s; } private static double rad(double d) { return d * Math.PI / 180.0; } //测试 LogUtil.e("距离:" + getDistance(120.206995,30.195124, 120.213746,30.195342));