读书笔记——基于位置的服务(LBS)

本文介绍了基于位置的服务(LBS),讲解了如何使用LocationManager获取位置信息,包括选择位置提供器、实时更新位置。接着探讨了反向地理编码,比较了Google Geocoding API与百度地图API的使用,强调了百度地图API在中国的适用性。并详细阐述了在Android中集成百度地图,显示地图、定位及使用覆盖物增强地图功能的方法。
摘要由CSDN通过智能技术生成

简介

1、工作原理

利用无线电通讯网络或 GPS 等定位方式来确定出移动设备所在的位置

获取自己的位置——LocationManager

LocationManager 的基本用法

1、获取LocationManager

LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

2、选择一个位置提供器来确定设备当前的位置——GPS_PROVIDER、NETWORK_PROVIDER 和 PASSIVE_PROVIDER

优缺点:前两种使用的比较多,分别表示使用 GPS 定位和使用网络定位。这两种定位方式各有特点,GPS 定位的精准度比较高,但是非常耗电,而网络定位的精准度稍差,但耗电量比较少。GPS在室内几乎挂~

定位功能必须要由用户主动去启用才行,不然任何应用程序都无法获取到手机当前的位置信息。(定位服务中的使用无线网络和使用GPS卫星。)

3、将选择好的位置提供器传入到getLastKnownLocation()方法中,得到Location对象。

这个 Location 对象中包含了经度、纬度、海拔等一系列的位置信息

String provider = LocationManager.NETWORK_PROVIDER;
Location location = locationManager.getLastKnownLocation(provider);

4、如果想使用GPS位置提供器,确认GPS定位功能是都以及开启?先判断有哪些位置提供器可用。

List<String> providerList = locationManager.getProviders(true);

getProviders()方法接收一个布尔型参数,传入 true 就表示只有启用的位置提供器才会被返回。之后再从 providerList 中判断是否包含 GPS 定位的功能就行了。

5、实时更新位置信息——requestLocationUpdates()

LocationManager 还提供了一个 requestLocationUpdates()方法,只要传入一个 LocationListener 的实例,并简单配置几个参数就可以实现更新位置信息的获取了。

locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, new LocationListener() {
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }
    @Override
    public void onProviderEnabled(String provider) {
    }
    @Override
    public void onProviderDisabled(String provider) {
    }
    @Override
    public void onLocationChanged(Location location) {
    }
});

第一个参数是位置提供器的类型,第二个参数是监听位置变化的时间间隔,以毫秒为单位,第三个参数是监听位置变化的距离间隔,以米为单位,第四个参数则是 LocationListener 监听器。这样的话,LocationManager 每隔5 秒钟会检测一下位置的变化情况,当移动距离超过 10 米的时候,就会调用 LocationListener的 onLocationChanged()方法,并把新的位置信息作为参数传入。

实例

public class MainActivity extends Activity {
    private TextView positionTextView;
    private LocationManager locationManager;
    private String provider;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        positionTextView = (TextView) findViewById(R.id.position_text_view);
        locationManager = (LocationManager) getSystemService(Context. LOCATION_SERVICE);
        // 获取所有可用的位置提供器
        List<String> providerList = locationManager.getProviders(true);
        if (providerList.contains(LocationManager.GPS_PROVIDER)) {
            provider = LocationManager.GPS_PROVIDER;
        } else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {
            provider = LocationManager.NETWORK_PROVIDER;
        } else {
            // 当没有可用的位置提供器时,弹出Toast提示用户
            Toast.makeText(this, "No location provider to use",
            Toast.LENGTH_SHORT).show();
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);
        if (location != null) {
            // 显示当前设备的位置信息
            showLocation(location);
        }
        locationManager.requestLocationUpdates(provider, 5000, 1, locationListener);
    }
    protected void onDestroy() {
        super.onDestroy();
        if (locationManager != null) {
            // 关闭程序时将监听器移除
            locationManager.removeUpdates(locationListener);
        }
    }
    LocationListener locationListener = new LocationListener() {
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
        @Override
        public void onProviderEnabled(String provider) {
        }
        @Override
        public void onProviderDisabled(String provider) {
        }
        @Override
        public void onLocationChanged(Location location) {
            // 更新当前设备的位置信息
            showLocation(location);
        }
    };

    private void showLocation(Location location) {
        String currentPosition = "latitude is " + location.getLatitude() + "\n"
        + "longitude is " + location.getLongitude();
        positionTextView.setText(currentPosition);
    }
}

当程序关闭时,我们还需要调用 removeUpdates()方法来将位置监听器移除,以保证不会继续耗费手机的电量。

所需权限

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

反向地理编码,看懂位置信息(经纬度到具体位置)

直接使用Geocoding API

1、GeoCoder类
优点:可以非常简单地完成正向和反向的地理编码功能,从而轻松地将一个经纬值转换成看得懂的位置信息。
缺点:GeoCoder 长期存在着一些较为严重的 bug,在反向地理编码的时候会有一定的概率不能解析出位置的信息,这样就无法保证位置解析的稳定性

2、新的一套 Geocoding API
用法稍微复杂了一些,但稳定性要比 GeoCoder 强得多。
工作原理:

在手机端我们可以向谷歌的服务器发起一条 HTTP 请求,并将经纬度的值作为参数一同传递过去,服务器帮我们将这个经纬值转换成看得懂的位置信息,再将这些信息返回给手机端,最后手机端去解析服务器返回的信息,并进行处理就可以了。

查看官网:https://developers.google.com/maps/documentation/geocoding/start
得到的数据再用解析方式解析出来。

实例
public class MainActivity extends Activity {
    public static final int SHOW_LOCATION = 0;
    ......
    private void showLocation(final Location location) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 组装反向地理编码的接口地址
                    StringBuilder url = new StringBuilder();
                    url.append("http://maps.googleapis.com/maps/api/geocode/json?latlng=");
                    url.append(location.getLatitude()).append(",")
                    url.append(location.getLongitude());
                    url.append("&sensor=false");
                    HttpClient httpClient = new DefaultHttpClient();
                    HttpGet httpGet = new HttpGet(url.toString());
                    // 在请求消息头中指定语言,保证服务器会返回中文数据
                    httpGet.addHeader("Accept-Language", "zh-CN");
                    HttpResponse httpResponse = httpClient.execute(httpGet);
                    if (httpResponse.getStatusLine().getStatusCode() == 200) {
                        HttpEntity entity = httpResponse.getEntity();
                        String response = EntityUtils.toString(entity, "utf-8");
                        JSONObject jsonObject = new JSONObject(response);
                        // 获取results节点下的位置信息
                        JSONArray resultArray = jsonObject.getJSONArray("results");
                        if (resultArray.length() > 0) {
                            JSONObject subObject = resultArray.
                            getJSONObject(0);
                            // 取出格式化后的位置信息
                            String address = subObject.getString("formatted_address");
                            Message message = new Message();
                            message.what = SHOW_LOCATION;
                            message.obj = address;
                            handler.sendMessage(message);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case SHOW_LOCATION:
                String currentPosition = (String) msg.obj;
                positionTextView.setText(currentPosition);
                break;
            default:
                break;
            }
        }
    };
}

加上权限

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

使用百度地图

自己做过,毕竟谷歌的服务在中国支持还不是很给力,而且百度这个接口做的比较方便。
1、申请 API Key
2、创建应用
填写安全码:数字签名+;+包名
数字签名指的是我们打包程序时所用 keystore 的 SHA1指纹,可以在 Eclipse 中查看到。点击 Eclipse 导航栏的 Window→Preferences→Android→Build,

注意,目前我们使用的是 debug.keystore 所生成的指纹,这是 Android 自动生成的一个用于测试的keystore。而当你的应用程序发布时还需要创建一个正式的 keystore,如果要得到它的指纹,就需要在 cmd 中输入如下命令:
keytool -list -v –keystore

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

(4)显示地图

//百度SDK自定义控件,所以使用时候要加上类的全路径
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.baidu.mapapi.map.MapView
        android:id="@+id/map_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true" />
</LinearLayout>
public class MainActivity extends Activity {
    private BMapManager manager;
    private MapView mapView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        manager = new BMapManager(this);
        manager.init("SHVPoTtIpzfonPD3HCkc5sIt", null);
        setContentView(R.layout.activity_main);
        mapView = (MapView) findViewById(R.id.map_view);
        mapView.setBuiltInZoomControls(true);
    }
    @Override
    protected void onResume() {
        mapView.onResume();
        if (manager != null) {
            manager.start();
        }
        super.onResume();
    }
    @Override
    protected void onPause() {
        mapView.onPause();
        if (manager != null) {
            manager.stop();
        }
        super.onPause();
    }
    @Override
    protected void onDestroy() {
        mapView.destroy();
        if (manager != null) {
            manager.destroy();
            manager = null;
        }
        super.onDestroy();
    }
}

代码分析:先需要创建一个 BMapManager 对象,然后调用它的 init()方法来进行初始化操作。init()方法接收两个参数,第一个参数就API Key , 第 二 个 参 数 传 入 null 即 可 。 注 意 初 始 化 操 作 一 定 要 在setContentView()方法前调用,不然的话就会出错。接下来我们获取到了 MapView 的实例,然后调用它的 setBuiltInZoomControls()方法并传入 true,表示启用内置的缩放控制功能。

另外还需要重写 onResume()、onPause()和 onDestroy()这三个方法,在这里对百度地图的 API 进行管理,以保证资源能够及时地得到释放。

(5)显示想要的位置而不是世界地图(比如自己所在位置)——MapController

//用 MapView的 getController()方法就能获取到 MapController 的实例。
//MapController地图的总控制器
MapController controller = mapView.getController();

//设置地图的缩放级别就可以这样写,取指范围3~19.级别越高,地图显示的信息越精细。
controller.setZoom(12);

(6)让地图定位到经纬度上——GeoPoint类

使用方式:GeoPoint 并没有什么太多的用法,主要就是用于存放经纬度值的,它的构造方法接收两个参数,第一个参数是纬度值,第二参数是经度值。但是需要注意, GeoPoint 是以微度为单位的,因此我们还要把经纬度的值乘以 10 的 6 次方再传给 GeoPoint。之后调用 MapController 的setCenter()方法,并把 GeoPoint 的实例传入就可以了

//将地图定位到北纬 39.915 度、东经 116.404 度这个位置
GeoPoint point = new GeoPoint((int) (39.915 * 1E6), (int) (116.404 * 1E6));
mMapController.setCenter(point);
实践
public class MainActivity extends Activity {
    ......
    private LocationManager locationManager;
    private String provider;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        manager = new BMapManager(this);
        manager.init("SHVPoTtIpzfonPD3HCkc5sIt", null);
        setContentView(R.layout.activity_main);
        mapView = (MapView) findViewById(R.id.map_view);
        mapView.setBuiltInZoomControls(true);
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        // 获取所有可用的位置提供器
        List<String> providerList = locationManager.getProviders(true);
        if (providerList.contains(LocationManager.GPS_PROVIDER)) {
            provider = LocationManager.GPS_PROVIDER;
        } else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {
            provider = LocationManager.NETWORK_PROVIDER;
        } else {
            // 当没有可用的位置提供器时,弹出Toast提示用户
            Toast.makeText(this, "No location provider to use", Toast.LENGTH_SHORT).show();
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);
        if (location != null) {
            navigateTo(location);
        }
    }

    private void navigateTo(Location location) {
        MapController controller = mapView.getController();
        controller.setZoom(16); // 设置缩放级别
        GeoPoint point = new GeoPoint((int) (location.getLatitude() * 1E6), (int) (location.getLongitude() * 1E6));
        controller.setCenter(point); // 设置地图中心点
    }
    ......
}
使用覆盖物来增加更多功能

所有叠加或覆盖到地图上的内容都被统称为地图覆盖物,如标注、矢量图形元素、定位图标等。覆盖物拥有自己的地理坐标,当我们拖动或缩放地图时,它们会自动进行相应地移动。

常用覆盖物MyLocationOverlay——标记当前位置(选定经纬度)

1、作用:
在地图中添加一个图层,以标注出设备当前的位置

2、用法:

MyLocationOverlay myLocationOverlay = new MyLocationOverlay(mapView);
LocationData locationData = new LocationData();
// 指定我的位置
locationData.latitude = location.getLatitude();
locationData.longitude = location.getLongitude();
myLocationOverlay.setData(locationData);
mapView.getOverlays().add(myLocationOverlay);
mapView.refresh(); // 刷新使新增覆盖物生效

首先是创建了一个 MyLocationOverlay 的实例,然后通过 LocationData对 象 指 定 了 当 前 的 经 纬 度 数 据 , 并 调 用 setData() 方 法 将 LocationData 存 放 到 了MyLocationOverlay 中。之后通过 MapView 的 getOverlays()方法可以得到一个用于管理覆盖物的集合,再调用 add()方法将 MyLocationOverlay 这个覆盖物添加到集合中。最后,还需要调用一下 MapView 的 refresh()方法使新增的覆盖物生效。

PopupOverlay

允许我们自己指定覆盖物上显示的图片,并且还可以响应图片的点击事件,每个 PopupOverlay 上最多可以显示三张图片。

public class MainActivity extends Activity {
    ......
    private void navigateTo(Location location) {
        MapController controller = mapView.getController();
        // 设置缩放级别
        controller.setZoom(16);
        GeoPoint point = new GeoPoint((int) (location.getLatitude() * 1E6), (int) (location.getLongitude() * 1E6));
        // 设置地图中心点
        controller.setCenter(point);
        ......
        PopupOverlay pop = new PopupOverlay(mapView, new PopupClickListener() {
            @Override
            public void onClickedPopup(int index) {
                // 相应图片的点击事件
                Toast.makeText(MainActivity.this, "You clicked button " + index, Toast.LENGTH_SHORT).show();
            }
        });
        // 创建一个长度为3的Bitmap数组
        Bitmap[] bitmaps = new Bitmap[3];
        try {
            // 将三张图片读取到内存中
            bitmaps[0] = BitmapFactory.decodeResource(getResources(), R.drawable.left);
            bitmaps[1] = BitmapFactory.decodeResource(getResources(), R.drawable.middle);
            bitmaps[2] = BitmapFactory.decodeResource(getResources(), R.drawable.right);
        } catch (Exception e) {
            e.printStackTrace();
        }
        pop.showPopup(bitmaps, point, 18);
    }
    ......
}

showPopup()方法
接收三个参数,第一个参数就是前面创建的 Bitmap 数组,第二个参数是一个用于指定地理位置的 GeoPoint 对象,第三个参数是覆盖物在垂直方法上的偏移距离。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值