Android 程序开发——百度地图的使用(三) 地图定位(定位+跟随+距离测量+地图自适应缩放)
前提:
1. jar包的下载
http://lbsyun.baidu.com/sdk/download?selected=mapsdk_basicmap,mapsdk_searchfunction,mapsdk_lbscloudsearch,mapsdk_calculationtool,mapsdk_radar
只需要选择:基础地图+全量地图即可
2.权限的设置
切记。。。不要忘了服务
------------------------分为初始定位+跟随+距离测量+地图自适应缩放---------------------------
@1初始定位
第一种(推荐)
1.实例化定位服务 和定位注册 一般放在onCreate()函数里
mLocationClient = new LocationClient(getApplicationContext()); //定位初始化
mLocationClient.registerLocationListener(this);//定位注册
2.和它对应的自然是注册的取消 一般放在onDestroy()函数里
mLocationClient.unRegisterLocationListener(this);
3.然后需要定位初始化 ,,把定位的参数一一列出,设置出来,一般是在onCreate()函数里进行
LocationClientOption option = new LocationClientOption();option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备option.setCoorType("bd09ll");//可选,默认gcj02,设置返回的定位结果坐标系option.setScanSpan(2000);//可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的option.setIsNeedAddress(true);//可选,设置是否需要地址信息,默认不需要option.setOpenGps(true);//可选,默认false,设置是否使用gpsoption.setLocationNotify(true);//可选,默认false,设置是否当GPS有效时按照1S/1次频率输出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仿真结果,默认需要option.setNeedDeviceDirect(true);mLocationClient.setLocOption(option);
4.此时,开启定位服务,我斟酌好久,选择放在onStart()函数里,项目需要我也放在了onResume()函数里
@Overrideprotected void onStart() {//当Activity调用onStart方法,开启定位以及开启方向传感器,即将定位的服务、方向传感器和Activity生命周期绑定在一起if (!mLocationClient.isStarted()) {mLocationClient.start();//开启定位}super.onStart();}
5.与之对应,即是关闭 关闭定位服务,我推荐onStop()函数里,项目需要 也放在了onPause()里
mLocationClient.stop();
6.如果在使用的过程还需要调用 定位 ,则需要以下代码(不需要则忽略)
mLocationClient.requestLocation();7.最重要的来了,当你注册定位服务的时候会调用一个接口,会返回一个函数,里面的东西才是最想要用的(经纬度、地区、方向。。。。。。。。。)
//位置信息监听@Overridepublic void onReceiveLocation(BDLocation bdLocation) {// 定位接口可能返回错误码,要根据结果错误码,来判断是否是正确的地址;int locType = bdLocation.getLocType();switch (locType) {case BDLocation.TypeCacheLocation:case BDLocation.TypeOffLineLocation:case BDLocation.TypeGpsLocation:case BDLocation.TypeNetWorkLocation:radius = bdLocation.getRadius();user_latitude = bdLocation.getLatitude();user_longitude = bdLocation.getLongitude();mCurrentX = bdLocation.getDirection();break;default:String s = bdLocation.getLocTypeDescription();break;}}
8.ok当前位置的信息获取完毕,当你已经获取到经纬度以后,,,,接下来就是定位当前位置
//定位到用户当前位置private void showUserLocation() {LatLng latLng = new LatLng(user_latitude, user_longitude);MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);map.animateMapStatus(msu);}9.也可以在定位的时候放大指定的倍数
//定位到用户当前位置private void showUserLocation() {LatLng latLng = new LatLng(user_latitude, user_longitude);MapStatusUpdate msu = MapStatusUpdateFactory.zoomTo(18.0f);// 设置地图放大比例map.setMapStatus(msu);msu = MapStatusUpdateFactory.newLatLng(latLng);map.animateMapStatus(msu);}
第二种,博客里面都是这一套,用户自己斟酌使用
1.定位服务实例化 + 注册监听接口 + 定位初始化,以及参数设置 + 开始定位
//实例化定位服务,LocationClient类必须在主线程中声明 mLocClient = new LocationClient(getApplicationContext()); mLocClient.registerLocationListener(new BDLocationListenerImpl());//注册定位监听接口 /** * LocationClientOption 该类用来设置定位SDK的定位方式。 */ LocationClientOption option = new LocationClientOption(); option.setOpenGps(true); //打开GPRS option.setAddrType("all");//返回的定位结果包含地址信息 option.setCoorType("bd09ll");//返回的定位结果是百度经纬度,默认值gcj02 option.setPriority(LocationClientOption.GpsFirst); // 设置GPS优先 option.setScanSpan(5000); //设置发起定位请求的间隔时间为5000ms option.disableCache(false);//禁止启用缓存定位
// option.setPoiNumber(5); //最多返回POI个数
// option.setPoiDistance(1000); //poi查询距离
// option.setPoiExtraInfo(true); //是否需要POI的电话和地址等详细信息 mLocClient.setLocOption(option); //设置定位参数 mLocClient.start(); // 调用此方法开始定位
2.一套定位返回接口,里面包含各种信息
/** * 定位接口,需要实现两个方法 * @author xiaanming * */ public class BDLocationListenerImpl implements BDLocationListener { /** * 接收异步返回的定位结果,参数是BDLocation类型参数 */ @Override public void onReceiveLocation(BDLocation location) { if (location == null) { return; } 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){ sb.append("\nspeed : "); sb.append(location.getSpeed()); sb.append("\nsatellite : "); sb.append(location.getSatelliteNumber()); } else if (location.getLocType() == BDLocation.TypeNetWorkLocation){ sb.append("\naddr : "); sb.append(location.getAddrStr()); } Log.e("log", sb.toString()); MainActivity.this.location = location; mLocData.latitude = location.getLatitude(); mLocData.longitude = location.getLongitude(); //如果不显示定位精度圈,将accuracy赋值为0即可 mLocData.accuracy = location.getRadius(); mLocData.direction = location.getDerect(); //将定位数据设置到定位图层里 myLocationOverlay.setData(mLocData); //更新图层数据执行刷新后生效 mMapView.refresh(); if(isFirstLoc || isRequest){ //将给定的位置点以动画形式移动至地图中心 mMapController.animateTo(new GeoPoint( (int) (location.getLatitude() * 1e6), (int) (location .getLongitude() * 1e6))); showPopupOverlay(location); isRequest = false; } isFirstLoc = false; } /** * 接收异步返回的POI查询结果,参数是BDLocation类型参数 */ @Override public void onReceivePoi(BDLocation poiLocation) { } } 3.经纬度获取到以后,定位到自己的位置
//定位到用户当前位置private void showUserLocation() {LatLng latLng = new LatLng(user_latitude, user_longitude);MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);map.animateMapStatus(msu);}
@2 显示跟随的箭头以及方向的切换(蓝色的小箭头)
第一种方式:百度自带(推荐)
1.设置定位支持,项目需要,我放入了onResume()周期里
map.setMyLocationEnabled(true);
2.与之对应,取消定位支持,我放入onPause()周期里
map.setMyLocationEnabled(false);
3.显示小箭头,这些操作我全部放在了onReceiveLocation(BDLocation bdLocation) 函数里,因为每次返回时得值,可以定时改变小箭头的位置
//位置信息监听@Overridepublic void onReceiveLocation(BDLocation bdLocation) {// 定位接口可能返回错误码,要根据结果错误码,来判断是否是正确的地址;int locType = bdLocation.getLocType();switch (locType) {case BDLocation.TypeCacheLocation:case BDLocation.TypeOffLineLocation:case BDLocation.TypeGpsLocation:case BDLocation.TypeNetWorkLocation:radius = bdLocation.getRadius();user_latitude = bdLocation.getLatitude();user_longitude = bdLocation.getLongitude();mCurrentX = bdLocation.getDirection();MyLocationData data = new MyLocationData.Builder().accuracy(radius).direction(mCurrentX).latitude(user_latitude).longitude(user_longitude).build();map.setMyLocationData(data);MyLocationConfiguration config = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, true, null);map.setMyLocationConfigeration(config);break;default:String s = bdLocation.getLocTypeDescription();break;}}
此方法采用百度给的方向和经纬度,显示的是百度默认的蓝色小箭头,
这里有两个点:1.蓝色小箭头的替换(自定义图片),2.LocationMode的选择
代码只需要改两句
MyLocationConfiguration config = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, true, null);map.setMyLocationConfigeration(config);原始第三个参数为null,即为默认的图片,即蓝色小箭头
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromResource(R.mipmap.icon_head);MyLocationConfiguration config = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, true, bitmapDescriptor);map.setMyLocationConfigeration(config);将null替换成一个自定义的BitmapDescriptor对象即可
2.LocationMode的选择
分为三种:普通模式,跟随模式,罗盘模式
普通模式:简单的显示箭头,以及跟随方向变化,箭头方向变化 MyLocationConfiguration.LocationMode.NORMAL(第一张图)
跟随模式:在普通模式基础之上,无论地图移动到哪里,当返回函数获取到值的时候,立即定位到当前MyLocationConfiguration.LocationMode.FOLLOW (第一张图)
罗盘模式:有指北针的的方向,类似于3D地图的视角,但不是3D的 MyLocationConfiguration.LocationMode.COMPASS (第二张图)
第二种方式:只是改动了方向的获取方式,当时忽略了百度地图,用方向传感器做的,较麻烦,不推荐,供参考
1.开启方向传感器
//开启方向传感器
// orientationListener.start();
// orientationListener.stop();//关闭方向传感器
3.方向传感器的调用
//定位结合方向传感器,从而可以实时监测到X轴坐标的变化,从而就可以检测到
// private void useLocationOrientationListener() {
// orientationListener = new MapOrientationListener(this);
// orientationListener.setMapOrientationListener(new MapOrientationListener.onOrientationListener() {
// @Override
// public void onOrientationChanged(float x) {//监听方向的改变,方向改变时,需要得到地图上方向图标的位置
// mCurrentX = x;
// }
// });
// }
4.继承传感器类,重写的方向传感器辅助类
public class MapOrientationListener implements SensorEventListener {private SensorManager mySensorManager;private Sensor mySensor;private Context myContext;private float lastX;private onOrientationListener myOrientationListener;public MapOrientationListener(Context myContext) {//方向传感器的一个构造器super();this.myContext = myContext;}/*** 开启方向传感器*/public void start() {//先通过系统服务来得到传感器管理对象mySensorManagermySensorManager = (SensorManager) myContext.getSystemService(Context.SENSOR_SERVICE);if (mySensorManager != null) {//如果传感器管理对象不为空,则可以通过传感器管理对象来获得方向传感器对象//获得方向传感器对象mySensor = mySensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);}if (mySensor != null) {//如果方向传感器不为空,则给该方向传感器注册监听事件mySensorManager.registerListener(this, mySensor, SensorManager.SENSOR_DELAY_UI);}}/*** 解除注册方向传感器监听事件*/public void stop() {mySensorManager.unregisterListener(this);}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {// TODO Auto-generated method stub}/*** 监听方向发生变化*/@Overridepublic void onSensorChanged(SensorEvent event) {//精度发生改变的时候if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {//如果是方向传感器float x = event.values[SensorManager.DATA_X];//获得传感器的X轴的坐标,可以返回3个值,即X轴的坐标,Y轴坐标,Z轴坐标,我们只需要X轴坐标if (Math.abs(x - lastX) > 1.0) {//对比本次的X坐标的变化比上一次的变化差大于1.0就说明方向发生改变if (myOrientationListener != null) {//说明主界面已经注册了事件,即不为空,然后产生一个回调将实时变化X轴的坐标传入//通过一个回调方法,通知主界面去更新UImyOrientationListener.onOrientationChanged(lastX);//就需要把上一次的X轴坐标传入,在MainActivity中的回调方法中去获取即可}}lastX = x;}}public void setMapOrientationListener(onOrientationListener myOrientationListener) {this.myOrientationListener = myOrientationListener;}//写一个接口实现方向改变的监听产生的回调public interface onOrientationListener {void onOrientationChanged(float x);//回调的方法}
}
@3 距离的测量
百度上提供了很多种,我目前在项目中使用了一种,分享一下
private final static double DEF_PI = 3.14159265359; // PIprivate final static double DEF_2PI = 6.28318530712; // 2*PIprivate final static double DEF_PI180 = 0.01745329252; // PI/180.0private final static double DEF_R = 6370693.5; // 地球半径,m
//根据球面距离计算两点直接的距离public String getLongDistance(double lat1, double lon1, double lat2, double lon2) {double ew1, ns1, ew2, ns2;double distance;// 角度转换为弧度ew1 = lon1 * DEF_PI180;ns1 = lat1 * DEF_PI180;ew2 = lon2 * DEF_PI180;ns2 = lat2 * DEF_PI180;// 求大圆劣弧与球心所夹的角(弧度)distance = Math.sin(ns1) * Math.sin(ns2) + Math.cos(ns1) * Math.cos(ns2) * Math.cos(ew1 - ew2);// 调整到[-1..1]范围内,避免溢出if (distance > 1.0) {distance = 1.0;} else if (distance < -1.0) {distance = -1.0;}// 求大圆劣弧长度distance = DEF_R * Math.acos(distance);if (distance > 1000.0) {DecimalFormat df = new DecimalFormat("0.000");String dis = df.format(distance / 1000);return dis + "km";} else {DecimalFormat df = new DecimalFormat("0.000");String dis = df.format(distance);return dis + "m";}}上述返回的String类型的,如果需要比较的话,可以转换成Double型,看代码(写的略麻烦,,唉 按需求来)
//最远点的距离(String)longDistance = getLongDistance(user_latitude, user_longitude, max_distance_lat, max_distance_lon);LogUtil.e("hello--------", "最远点的距离:" + longDistance + "\n最远点的坐标" + max_distance_lat + "," + max_distance_lon);String substring = longDistance.substring(0, longDistance.length() - 1);if (substring.substring(substring.length() - 1).equals("k")) {max_distance = Double.parseDouble(substring.substring(0, substring.length() - 1)) * 1000;} else {max_distance = Double.parseDouble(substring.substring(0, substring.length() - 1));}
@4 如何让所有的marker(标记点)“恰好” 全部显示在屏幕上,以自我为中心
1.首先说一下,百度地图的放大倍数和比例尺的关系
private int[] list = new int[]{10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000};对应的放大倍数分别为:20~3;
我的思路:1.首先定位当前 2.计算所有marker 与 自己的距离 3.选择 最大的一个纬度差和最大的一个经度差 4.这样所有的点都可以显示了
1.定位当前
private void showUserLocation() {LatLng latLng = new LatLng(user_latitude, user_longitude);MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);map.animateMapStatus(msu);}2.计算所有marker与自己的距离,获取最大纬度差和最大经度差
//判断纬度最远的距离if (Math.abs(destination_latitude - user_latitude) > sub_lat) {sub_lat = Math.abs(destination_longitude - user_longitude);max_distance_lat = destination_latitude;}//判断经度最远的距离if (Math.abs(destination_longitude - user_longitude) > sub_lon) {sub_lon = Math.abs(destination_longitude - user_longitude);max_distance_lon = destination_longitude;}
3.计算最大的距离值
//最远点的距离(String)longDistance = getLongDistance(user_latitude, user_longitude, max_distance_lat, max_distance_lon);LogUtil.e("hello--------", "最远点的距离:" + longDistance + "\n最远点的坐标" + max_distance_lat + "," + max_distance_lon);String substring = longDistance.substring(0, longDistance.length() - 1);if (substring.substring(substring.length() - 1).equals("k")) {max_distance = Double.parseDouble(substring.substring(0, substring.length() - 1)) * 1000;} else {max_distance = Double.parseDouble(substring.substring(0, substring.length() - 1));}
4.计算level,显示对应比例
for (int i = 0; i < list.length; i++) {if (max_distance > list[i]) {scale--;}}//定位当前,并放大适应的比例LatLng latLng = new LatLng(user_latitude, user_longitude);MapStatusUpdate msu = MapStatusUpdateFactory.zoomTo(scale + 2f);// 设置地图放大比例map.setMapStatus(msu);msu = MapStatusUpdateFactory.newLatLng(latLng);map.animateMapStatus(msu);
最后这个例子有一些偏差,希望会的朋友提醒一下,thanks ,官方那个setToZoom先不使用
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
