9.5.3 封装百度地图操作

接下来,将调用百度地图Place API和坐标转换相关的操作封装到BaiduMapUtil类中,该类的实现如下:

  1. package org.liufeng.course.util;
  2.  
  3. import it.sauronsoftware.base64.Base64;
  4. import java.io.BufferedReader;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.io.UnsupportedEncodingException;
  8. import java.net.HttpURLConnection;
  9. import java.net.URL;
  10. import java.net.URLEncoder;
  11. import java.util.ArrayList;
  12. import java.util.Collections;
  13. import java.util.List;
  14. import net.sf.json.JSONObject;
  15. import org.dom4j.Document;
  16. import org.dom4j.DocumentHelper;
  17. import org.dom4j.Element;
  18. import org.liufeng.course.message.resp.Article;
  19. import org.liufeng.course.pojo.BaiduPlace;
  20. import org.liufeng.course.pojo.UserLocation;
  21.  
  22. /**
  23. * 百度地图操作类
  24. *
  25. * @author liufeng
  26. * @date 2013-11-19
  27. */
  28. public class BaiduMapUtil {
  29. /**
  30. * 圆形区域检索
  31. *
  32. * @param query 检索关键词
  33. * @param lng 经度
  34. * @param lat 纬度
  35. * @return List<BaiduPlace>
  36. * @throws UnsupportedEncodingException
  37. */
  38. public static List<BaiduPlace> searchPlace(String query, String lng, String lat)
  39. throws Exception {
  40. // 拼装请求地址
  41. String requestUrl = "http://api.map.baidu.com/place/v2/search?&query=QU
  42. ERY&location=LAT,LNG&radius=2000&output=xml&scope=2&page_size=10&page_
  43. num=0&ak=CA21 bdecc75efc1664af5a195c30bb4e";
  44. requestUrl = requestUrl.replace("QUERY", URLEncoder.encode(query, "UTF-8"));
  45. requestUrl = requestUrl.replace("LAT", lat);
  46. requestUrl = requestUrl.replace("LNG", lng);
  47. // 调用Place API圆形区域检索
  48. String respXml = httpRequest(requestUrl);
  49. // 解析返回的XML
  50. List<BaiduPlace> placeList = parsePlaceXml(respXml);
  51. return placeList;
  52. }
  53.  
  54. /**
  55. * 发送HTTP请求
  56. *
  57. * @param requestUrl 请求地址
  58. * @return String
  59. */
  60. public static String httpRequest(String requestUrl) {
  61. StringBuffer buffer = new StringBuffer();
  62. try {
  63. URL url = new URL(requestUrl);
  64. HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
  65. httpUrlConn.setDoInput(true);
  66. httpUrlConn.setRequestMethod("GET");
  67. httpUrlConn.connect();
  68.  
  69. // 将返回的输入流转换成字符串
  70. InputStream inputStream = httpUrlConn.getInputStream();
  71. InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
  72. BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
  73. String str = null;
  74. while ((str = bufferedReader.readLine()) != null) {
  75. buffer.append(str);
  76. }
  77. bufferedReader.close();
  78. inputStreamReader.close();
  79. // 释放资源
  80. inputStream.close();
  81. inputStream = null;
  82. httpUrlConn.disconnect();
  83. } catch (Exception e) {
  84. e.printStackTrace();
  85. }
  86. return buffer.toString();
  87. }
  88.  
  89. /**
  90. * 根据百度地图返回的流解析出地址信息
  91. *
  92. * @param inputStream 输入流
  93. * @return List<BaiduPlace>
  94. */
  95. @SuppressWarnings("unchecked")
  96. private static List<BaiduPlace> parsePlaceXml(String xml) {
  97. List<BaiduPlace> placeList = null;
  98. try {
  99. Document document = DocumentHelper.parseText(xml);
  100. // 得到XML根元素
  101. Element root = document.getRootElement();
  102. // 从根元素获取<results>
  103. Element resultsElement = root.element("results");
  104. // 从<results>中获取<result>集合
  105. List<Element> resultElementList = resultsElement.elements("result");
  106. // 判断<result>集合的大小
  107. if (resultElementList.size() > 0) {
  108. placeList = new ArrayList<BaiduPlace>();
  109. // POI名称
  110. Element nameElement = null;
  111. // POI地址信息
  112. Element addressElement = null;
  113. // POI经纬度坐标
  114. Element locationElement = null;
  115. // POI电话信息
  116. Element telephoneElement = null;
  117. // POI扩展信息
  118. Element detailInfoElement = null;
  119. // 距离中心点的距离
  120. Element distanceElement = null;
  121. // 遍历<result>集合
  122. for (Element resultElement : resultElementList) {
  123. nameElement = resultElement.element("name");
  124. addressElement = resultElement.element("address");
  125. locationElement = resultElement.element("location");
  126. telephoneElement = resultElement.element("telephone");
  127. detailInfoElement = resultElement.element("detail_info");
  128.  
  129. BaiduPlace place = new BaiduPlace();
  130. place.setName(nameElement.getText());
  131. place.setAddress(addressElement.getText());
  132. place.setLng(locationElement.element("lng").getText());
  133. place.setLat(locationElement.element("lat").getText());
  134. // 当<telephone>元素存在时获取电话号码
  135. if (null != telephoneElement)
  136. place.setTelephone(telephoneElement.getText());
  137. // 当<detail_info>元素存在时获取<distance>
  138. if (null != detailInfoElement) {
  139. distanceElement = detailInfoElement.element("distance");
  140. if (null != distanceElement)
  141. place.setDistance(Integer.parseInt
  142. (distanceElement.getText()));
  143. }
  144. placeList.add(place);
  145. }
  146. // 按距离由近及远排序
  147. Collections.sort(placeList);
  148. }
  149. } catch (Exception e) {
  150. e.printStackTrace();
  151. }
  152. return placeList;
  153. }
  154.  
  155. /**
  156. * 根据Place组装图文列表
  157. *
  158. * @param placeList
  159. * @param bd09Lng 经度
  160. * @param bd09Lat 纬度
  161. * @return List<Article>
  162. */
  163. public static List<Article> makeArticleList(List<BaiduPlace> placeList, String
  164. bd09Lng, String bd09Lat) {
  165. // 项目的根路径
  166. String basePath = "http://0.weixinmptest.duapp.com/";
  167. List<Article> list = new ArrayList<Article>();
  168. BaiduPlace place = null;
  169. for (int i = 0; i < placeList.size(); i++) {
  170. place = placeList.get(i);
  171. Article article = new Article();
  172. article.setTitle(place.getName() + "\n距离约" + place.getDistance() + "米");
  173. // P1表示用户发送的位置(坐标转换后),p2表示当前POI所在位置
  174. article.setUrl(String.format(basePath + "route.jsp?p1=%s,%s&p2=%s,
  175. %s", bd09Lng, bd09Lat, place.getLng(), place.getLat()));
  176. // 将首条图文的图片设置为大图
  177. if (i == 0)
  178. article.setPicUrl(basePath + "images/poisearch.png");
  179. else
  180. article.setPicUrl(basePath + "images/navi.png");
  181. list.add(article);
  182. }
  183. return list;
  184. }
  185.  
  186. /**
  187. * 将微信定位的坐标转换成百度坐标(GCJ-02 -> Baidu)
  188. *
  189. * @param lng 经度
  190. * @param lat 纬度
  191. * @return UserLocation
  192. */
  193. public static UserLocation convertCoord(String lng, String lat) {
  194. // 百度坐标转换接口
  195. String convertUrl = "http://api.map.baidu.com/ag/coord/convert?from=2&to=4&x={x}&y={y}";
  196. convertUrl = convertUrl.replace("{x}", lng);
  197. convertUrl = convertUrl.replace("{y}", lat);
  198.  
  199. UserLocation location = new UserLocation();
  200. try {
  201. String jsonCoord = httpRequest(convertUrl);
  202. JSONObject jsonObject = JSONObject.fromObject(jsonCoord);
  203. // 对转换后的坐标进行Base64解码
  204. location.setBd09Lng(Base64.decode(jsonObject.getString("x"), "UTF-8"));
  205. location.setBd09Lat(Base64.decode(jsonObject.getString("y"), "UTF-8"));
  206. } catch (Exception e) {
  207. location = null;
  208. e.printStackTrace();
  209. }
  210. return location;
  211. }
  212. }

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类的定义代码如下:

  1. package org.liufeng.course.pojo;
  2.  
  3. /**
  4. * 地址信息
  5. *
  6. * @author liufeng
  7. * @date 2013-03-16
  8. */
  9. public class BaiduPlace implements Comparable<BaiduPlace> {
  10. // 名称
  11. private String name;
  12. // 详细地址
  13. private String address;
  14. // 经度
  15. private String lng;
  16. // 纬度
  17. private String lat;
  18. // 联系电话
  19. private String telephone;
  20. // 距离
  21. private int distance;
  22.  
  23. public String getName() {
  24. return name;
  25. }
  26.  
  27. public void setName(String name) {
  28. this.name = name;
  29. }
  30.  
  31. public String getAddress() {
  32. return address;
  33. }
  34.  
  35. public void setAddress(String address) {
  36. this.address = address;
  37. }
  38.  
  39. public String getLng() {
  40. return lng;
  41. }
  42.  
  43. public void setLng(String lng) {
  44. this.lng = lng;
  45. }
  46.  
  47. public String getLat() {
  48. return lat;
  49. }
  50.  
  51. public void setLat(String lat) {
  52. this.lat = lat;
  53. }
  54.  
  55. public String getTelephone() {
  56. return telephone;
  57. }
  58.  
  59. public void setTelephone(String telephone) {
  60. this.telephone = telephone;
  61. }
  62.  
  63. public int getDistance() {
  64. return distance;
  65. }
  66.  
  67. public void setDistance(int distance) {
  68. this.distance = distance;
  69. }
  70.  
  71. @Override
  72. public int compareTo(BaiduPlace baiduPlace) {
  73. return this.distance - baiduPlace.getDistance();
  74. }
  75. }