最近有个需求就是个根据GPS信息获得某个地方的详细信息(以街道为例, 其他属性可以见官方文档参数说明逆地理编码 rgc 反geo检索 | 百度地图API SDK),参考一些资料只有Java能跑通就用Java实现了(python会更简单),在以前的参考资料都是v2版本的接口,而现在能申请到的接口都是v3版本的接口,这个接口必须指定服务器IP或者使用SN码校验和进行请求,所以很多教程没法直接使用,这里我选择的是SN码验证。
申请百度地图开发者之后在控制台创建应用选择sn码校验后你可以获取自己的ak和sk,作为个人开发者每天有5000次的免费额度。
下面是获取对应经纬度街道信息的主方法,你需要替换自己的ak,由于我需要跑大量数据,在测试中经常会报超过最大并发量的错误,所以某些记录返回值可能是未知。
/**
* 根据给的经纬度获取对应地点的街道信息,你需要替换自己的ak,sk才能使用
* @param lng 纬度
* @param lat 经度
* @return 对应街道(路)名称
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
*/
public static String getAdressDetail(double lng,double lat) throws UnsupportedEncodingException, NoSuchAlgorithmException {
String result = "";// 访问返回结果
BufferedReader read = null;// 读取访问结果
String street = "";//城镇名称
String regionScope = "";//城镇范围
String url = "http://api.map.baidu.com/reverse_geocoding/v3/";
String param = "ak=替换为自己的ak"+
"&location="+lng+","+lat + "&output=json" + "&sn=" + getSn("ak", "替换为自己的ak",
"output", "json",
"coordtype", "wgs84ll",
"location", lng+","+lat
);
// "extensions_town", "true", &extensions_town=true"
System.out.println("访问地址:" + url + "?" + param);
try {
// 创建url
URL realurl = new URL(url + "?" + param);
// 打开连接
HttpURLConnection connection = (HttpURLConnection)realurl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍历所有的响应头字段,获取到cookies等
for (String key : map.keySet()) {
System.out.println(key + "--->" + map.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
read = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;// 循环读取
while ((line = read.readLine()) != null) {
result += line;
}
// System.out.println("............................" + connection.getResponseCode());
//不为空,解析处城镇名称
if(!"".equals(result)){
JSONObject jsonInfo = JSONObject.parseObject(result);
if(!result.equals("{\"status\":401,\"message\":\"当前并发量已经超过约定并发配额,限制访问\"}")) {
street = JSONObject.parseObject(JSONObject.parseObject(jsonInfo.getString("result")).getString("addressComponent")).getString("street").toString();
regionScope = JSONObject.parseObject(jsonInfo.getString("result")).getString("formatted_address")+street;
System.out.println(regionScope);
}else {
street = "查询失败";
}
}
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (read != null) {// 关闭流
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return street;
}
官方文档中获取sn码的方法只对正向编码是正确的,而逆地理编码中由于location参数中间存在逗号‘,’,所以会出现sn码校验失败的错误。
修改后的sn获取方法如下
/**
* 获取url的Sn,你需要替换自己的sk才能使用
* @param paras 可变参数,对应url后的参数,参数为如?location=23.453342,113.453422,则需传入两个参数:“location”, “23.453342,113.453422”
* @return sn值
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
*/
public static String getSn(String ...paras) throws UnsupportedEncodingException, {
// 计算sn跟参数对出现顺序有关,get请求请使用LinkedHashMap保存<key,value>,该方法根据key的插入顺序排序;post请使用TreeMap保存<key,value>,该方法会自动将key按照字母a-z顺序排序。所以get请求可自定义参数顺序(sn参数必须在最后)发送请求,但是post请求必须按照字母a-z顺序填充body(sn参数必须在最后)。以get请求为例:http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak,paramsMap中先放入address,再放output,然后放ak,放入顺序必须跟get请求中对应参数的出现顺序保持一致。
Map paramsMap = new TreeMap<String, String>();
for(int i = 0; i < paras.length / 2; i++) {
paramsMap.put(paras[2 * i], paras[2 * i + 1]);
}
// paramsMap.put("address", "百度大厦");
// paramsMap.put("output", "json");
// paramsMap.put("ak", "yourak");
// 调用下面的toQueryString方法,对LinkedHashMap内所有value作utf8编码,拼接返回结果address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourak
String paramsStr = toQueryString(paramsMap);
// 对paramsStr前面拼接上/geocoder/v3/?,后面直接拼接yoursk得到/geocoder/v2/?address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourakyoursk
String wholeStr = new String("/reverse_geocoding/v3/?" + paramsStr + "替换为你的sk");
System.out.println(wholeStr);
// 对上面wholeStr再作utf8编码
String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
// 调用下面的MD5方法得到最后的sn签名7de5a22212ffaa9e326444c75a58f9a0
return MD5(tempStr);
}
// 对Map内所有value作utf8编码,拼接返回结果
//针对location参数的改进
public static String toQueryString(Map<?, ?> data)
throws UnsupportedEncodingException {
StringBuffer queryString = new StringBuffer();
for (Entry<?, ?> pair : data.entrySet()) {
queryString.append(pair.getKey() + "=");
String ss[] = pair.getValue().toString().split(",");
if(ss.length>1){
for(String s:ss){
queryString.append(URLEncoder.encode(s,"UTF-8") + ",");
}
queryString.deleteCharAt(queryString.length()-1);
queryString.append("&");
}
else{
queryString.append(URLEncoder.encode((String) pair.getValue(),
"UTF-8") + "&");
}
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
return queryString.toString();
}
// 来自stackoverflow的MD5计算方法,调用了MessageDigest库函数,并把byte数组结果转换成16进制
public static String MD5(String md5) {
try {
java.security.MessageDigest md = java.security.MessageDigest
.getInstance("MD5");
byte[] array = md.digest(md5.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; ++i) {
sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100)
.substring(1, 3));
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
}
return null;
}
使用浏览器打开查询url查看全部结果