9.5.3 封装百度地图操作
接下来,将调用百度地图Place API和坐标转换相关的操作封装到BaiduMapUtil类中,该类的实现如下:
- package org.liufeng.course.util;
- import it.sauronsoftware.base64.Base64;
- import java.io.BufferedReader;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.UnsupportedEncodingException;
- import java.net.HttpURLConnection;
- import java.net.URL;
- import java.net.URLEncoder;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import net.sf.json.JSONObject;
- import org.dom4j.Document;
- import org.dom4j.DocumentHelper;
- import org.dom4j.Element;
- import org.liufeng.course.message.resp.Article;
- import org.liufeng.course.pojo.BaiduPlace;
- import org.liufeng.course.pojo.UserLocation;
- /**
- * 百度地图操作类
- *
- * @author liufeng
- * @date 2013-11-19
- */
- public class BaiduMapUtil {
- /**
- * 圆形区域检索
- *
- * @param query 检索关键词
- * @param lng 经度
- * @param lat 纬度
- * @return List<BaiduPlace>
- * @throws UnsupportedEncodingException
- */
- public static List<BaiduPlace> searchPlace(String query, String lng, String lat)
- throws Exception {
- // 拼装请求地址
- String requestUrl = "http://api.map.baidu.com/place/v2/search?&query=QU
- ERY&location=LAT,LNG&radius=2000&output=xml&scope=2&page_size=10&page_
- num=0&ak=CA21 bdecc75efc1664af5a195c30bb4e";
- requestUrl = requestUrl.replace("QUERY", URLEncoder.encode(query, "UTF-8"));
- requestUrl = requestUrl.replace("LAT", lat);
- requestUrl = requestUrl.replace("LNG", lng);
- // 调用Place API圆形区域检索
- String respXml = httpRequest(requestUrl);
- // 解析返回的XML
- List<BaiduPlace> placeList = parsePlaceXml(respXml);
- return placeList;
- }
- /**
- * 发送HTTP请求
- *
- * @param requestUrl 请求地址
- * @return String
- */
- public static String httpRequest(String requestUrl) {
- StringBuffer buffer = new StringBuffer();
- try {
- URL url = new URL(requestUrl);
- HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
- httpUrlConn.setDoInput(true);
- httpUrlConn.setRequestMethod("GET");
- httpUrlConn.connect();
- // 将返回的输入流转换成字符串
- InputStream inputStream = httpUrlConn.getInputStream();
- InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
- BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
- String str = null;
- while ((str = bufferedReader.readLine()) != null) {
- buffer.append(str);
- }
- bufferedReader.close();
- inputStreamReader.close();
- // 释放资源
- inputStream.close();
- inputStream = null;
- httpUrlConn.disconnect();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return buffer.toString();
- }
- /**
- * 根据百度地图返回的流解析出地址信息
- *
- * @param inputStream 输入流
- * @return List<BaiduPlace>
- */
- @SuppressWarnings("unchecked")
- private static List<BaiduPlace> parsePlaceXml(String xml) {
- List<BaiduPlace> placeList = null;
- try {
- Document document = DocumentHelper.parseText(xml);
- // 得到XML根元素
- Element root = document.getRootElement();
- // 从根元素获取<results>
- Element resultsElement = root.element("results");
- // 从<results>中获取<result>集合
- List<Element> resultElementList = resultsElement.elements("result");
- // 判断<result>集合的大小
- if (resultElementList.size() > 0) {
- placeList = new ArrayList<BaiduPlace>();
- // POI名称
- Element nameElement = null;
- // POI地址信息
- Element addressElement = null;
- // POI经纬度坐标
- Element locationElement = null;
- // POI电话信息
- Element telephoneElement = null;
- // POI扩展信息
- Element detailInfoElement = null;
- // 距离中心点的距离
- Element distanceElement = null;
- // 遍历<result>集合
- for (Element resultElement : resultElementList) {
- nameElement = resultElement.element("name");
- addressElement = resultElement.element("address");
- locationElement = resultElement.element("location");
- telephoneElement = resultElement.element("telephone");
- detailInfoElement = resultElement.element("detail_info");
- BaiduPlace place = new BaiduPlace();
- place.setName(nameElement.getText());
- place.setAddress(addressElement.getText());
- place.setLng(locationElement.element("lng").getText());
- place.setLat(locationElement.element("lat").getText());
- // 当<telephone>元素存在时获取电话号码
- if (null != telephoneElement)
- place.setTelephone(telephoneElement.getText());
- // 当<detail_info>元素存在时获取<distance>
- if (null != detailInfoElement) {
- distanceElement = detailInfoElement.element("distance");
- if (null != distanceElement)
- place.setDistance(Integer.parseInt
- (distanceElement.getText()));
- }
- placeList.add(place);
- }
- // 按距离由近及远排序
- Collections.sort(placeList);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return placeList;
- }
- /**
- * 根据Place组装图文列表
- *
- * @param placeList
- * @param bd09Lng 经度
- * @param bd09Lat 纬度
- * @return List<Article>
- */
- public static List<Article> makeArticleList(List<BaiduPlace> placeList, String
- bd09Lng, String bd09Lat) {
- // 项目的根路径
- String basePath = "http://0.weixinmptest.duapp.com/";
- List<Article> list = new ArrayList<Article>();
- BaiduPlace place = null;
- for (int i = 0; i < placeList.size(); i++) {
- place = placeList.get(i);
- Article article = new Article();
- article.setTitle(place.getName() + "\n距离约" + place.getDistance() + "米");
- // P1表示用户发送的位置(坐标转换后),p2表示当前POI所在位置
- article.setUrl(String.format(basePath + "route.jsp?p1=%s,%s&p2=%s,
- %s", bd09Lng, bd09Lat, place.getLng(), place.getLat()));
- // 将首条图文的图片设置为大图
- if (i == 0)
- article.setPicUrl(basePath + "images/poisearch.png");
- else
- article.setPicUrl(basePath + "images/navi.png");
- list.add(article);
- }
- return list;
- }
- /**
- * 将微信定位的坐标转换成百度坐标(GCJ-02 -> Baidu)
- *
- * @param lng 经度
- * @param lat 纬度
- * @return UserLocation
- */
- public static UserLocation convertCoord(String lng, String lat) {
- // 百度坐标转换接口
- String convertUrl = "http://api.map.baidu.com/ag/coord/convert?from=2&to=4&x={x}&y={y}";
- convertUrl = convertUrl.replace("{x}", lng);
- convertUrl = convertUrl.replace("{y}", lat);
- UserLocation location = new UserLocation();
- try {
- String jsonCoord = httpRequest(convertUrl);
- JSONObject jsonObject = JSONObject.fromObject(jsonCoord);
- // 对转换后的坐标进行Base64解码
- location.setBd09Lng(Base64.decode(jsonObject.getString("x"), "UTF-8"));
- location.setBd09Lat(Base64.decode(jsonObject.getString("y"), "UTF-8"));
- } catch (Exception e) {
- location = null;
- e.printStackTrace();
- }
- return location;
- }
- }
BaiduMapUtil类一共封装了5个方法,它们的说明如下。
(1)httpRequest()方法
这是一个通用的HTTP接口调用方法,调用Place API和坐标转换接口都需要用到该方法。
(2)convertCoord()方法
该方法用于将微信用户发送的经纬度坐标转换成百度坐标,解析转换结果时用到了JSON-lib和Base64工具。
(3)searchPlace()方法
提供给外部调用的圆形区域检索方法,请求地址中的参数query要进行UTF-8编码。请读者牢记:不管调用任何接口,凡是通过URL传递参数,只要参数有可能为中文,就必须显式地对参数进行编码,否则很可能在本机运行正常,但部署到服务器会出现问题,因为本机与服务器的默认编码方式可能不一样。
(4)parsePlaceXml()方法
该方法用于解析调用Place API返回的XML结果,在解析完成后,使用了Collections.sort()方法对BaiduPlace集合按距离由近及远排序。在Java中,要对集合中的对象进行排序,可以使用Comparator接口或Comparable接口,笔者使用的是后者,BaiduPlace类实现Comparable接口,并实现接口的compareTo()方法。
(5)makeArticleList()方法
该方法用于组装图文消息所需的Article集合,需要注意的是,Article的URL指向的是项目根目录下的步行导航页面route.jsp,当用户点击图文消息时,就会通过微信浏览器打开百度地图,并且显示用户发送位置至目标位置的步行线路。
BaiduPlace类的定义代码如下:
- package org.liufeng.course.pojo;
- /**
- * 地址信息
- *
- * @author liufeng
- * @date 2013-03-16
- */
- public class BaiduPlace implements Comparable<BaiduPlace> {
- // 名称
- private String name;
- // 详细地址
- private String address;
- // 经度
- private String lng;
- // 纬度
- private String lat;
- // 联系电话
- private String telephone;
- // 距离
- private int distance;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getAddress() {
- return address;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- public String getLng() {
- return lng;
- }
- public void setLng(String lng) {
- this.lng = lng;
- }
- public String getLat() {
- return lat;
- }
- public void setLat(String lat) {
- this.lat = lat;
- }
- public String getTelephone() {
- return telephone;
- }
- public void setTelephone(String telephone) {
- this.telephone = telephone;
- }
- public int getDistance() {
- return distance;
- }
- public void setDistance(int distance) {
- this.distance = distance;
- }
- @Override
- public int compareTo(BaiduPlace baiduPlace) {
- return this.distance - baiduPlace.getDistance();
- }
- }