使用百度地图API进行地址和经纬度之间的转换
简介
我们在项目中可能会遇到将经纬度转换成省市区的需求,这个时候需要各种地图提供的API,这里我们使用百度地图的API。百度地图开放平台,以下所指的经纬度都是百度地图的经纬度。
代码实现
添加一些工具的maven依赖,本例我们使用JDK11
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
将地理位置转换成经纬度
定义接口请求
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class GeoCodingRequest {
/**
* 待解析的地址。最多支持84个字节。
*/
private String address;
/**
* 用户申请注册的key
*/
private String ak;
/**
* 请求签名
*/
private String sn;
/**
* 输出格式为json或者xml
*/
private String output;
}
定义接口响应
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class GeoCodingInfo {
private static final Integer SUCCESS = 0;
//返回结果状态值, 成功返回0
private Integer status;
//响应结果
private GeoCodingResultInfo result;
public boolean isSuccess() {
return SUCCESS.equals(status);
}
}
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class GeoCodingResultInfo {
/**
* 经纬度信息
*/
private GeoCodingLocationInfo location;
/**
* 位置的附加信息,是否精确查找。1为精确查找,即准确打点;0为不精确,即模糊打点。
*/
private Integer precise;
/**
* 描述打点绝对精度(即坐标点的误差范围)。 confidence=100,解析误差绝对精度小于20m; confidence≥90,解析误差绝对精度小于50m;
* confidence≥80,解析误差绝对精度小于100m; confidence≥75,解析误差绝对精度小于200m; confidence≥70,解析误差绝对精度小于300m;
* confidence≥60,解析误差绝对精度小于500m; confidence≥50,解析误差绝对精度小于1000m; confidence≥40,解析误差绝对精度小于2000m;
* confidence≥30,解析误差绝对精度小于5000m; confidence≥25,解析误差绝对精度小于8000m; confidence≥20,解析误差绝对精度小于10000m;
*/
private Integer confidence;
/**
* 能精确理解的地址类型,包含:UNKNOWN、国家、省、城市、区县、乡镇、村庄、道路、地产小区、商务大厦、政府机构、交叉路口、商圈、生活服务、休闲娱乐、餐饮、宾馆、购物、金融、教育、医疗
* 、工业园区 、旅游景点 、汽车服务、火车站、长途汽车站、桥 、停车场/停车区、港口/码头、收费区/收费站、飞机场 、机场 、收费处/收费站 、加油站、绿地、门址
*/
private String level;
}
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class GeoCodingLocationInfo {
/**
* 经度
*/
private BigDecimal lng;
/**
* 维度
*/
private BigDecimal lat;
}
接下来编写访问百度地图API的工具类,这里我们使用的请求校验方式为sn校验。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
public class BaiduMapUtility {
private static final String BASE_URL = "http://api.map.baidu.com";
private static final String GEOCODING_URL = "http://api.map.baidu.com/geocoding/v3/";
private static final String REVERSE_GEOCODING_URL = "http://api.map.baidu.com/reverse_geocoding/v3/";
private static final String AK = "xxx"; // 你的ak
private static final String SK = "xxx"; // 你的sk
/**
* 地理编码,将地址转换成经纬度
*
* @return 响应
*/
public static GeoCodingInfo geoCoding(String address) throws Exception {
String url = GEOCODING_URL;
GeoCodingRequest request = new GeoCodingRequest();
request.setAddress(address);
request.setOutput("json");
request.setAk(AK);
request.setSn(generateSign(url, request));
url += getAppendUrl(request, false);
return doApiRequest(url, GeoCodingInfo.class);
}
/**
* 逆地理编码,将经纬度转换成地址
*
* @return 响应
*/
public static ReverseGeoCodingInfo reverseGeoCoding(String location) throws Exception {
String url = REVERSE_GEOCODING_URL;
ReverseGeoCodingRequest request = new ReverseGeoCodingRequest();
request.setLocation(location);
request.setOutput("json");
request.setAk(AK);
request.setSn(generateSign(url, request));
//必须
request.setLocation(encodeValue(true, location));
url += getAppendUrl(request, false);
return doApiRequest(url, ReverseGeoCodingInfo.class);
}
private static <T> T doApiRequest(String url, Class<T> clazz) throws Exception {
//创建请求客户端
HttpClient client = HttpClient.newHttpClient();
//创建GET请求
HttpRequest httpRequest = HttpRequest.newBuilder()
.GET()
.uri(URI.create(url))
.build();
//同步发送请求
String resp = client.send(httpRequest, BodyHandlers.ofString()).body();
System.out.println(resp);
return JSON.parseObject(resp, clazz);
}
/**
* 生成请求签名
*
* @param url 请求url
* @param request 请求对象
*/
private static String generateSign(String url, Object request) {
String params = getAppendUrl(request, true);
String wholeStr =
url.substring(url.lastIndexOf(BASE_URL) + BASE_URL.length()) + params + SK;
String encodedStr = encodeValue(true, wholeStr);
return DigestUtils.md5Hex(encodedStr.getBytes(StandardCharsets.UTF_8));
}
/**
* url拼接参数
*
* @param obj 参数对象
* @param encode 参数值是否编码
* @return 参数字符串
*/
private static String getAppendUrl(Object obj, boolean encode) {
Map<String, Object> map = JSON
.parseObject(JSON.toJSONString(obj), new TypeReference<LinkedHashMap<String, Object>>() {
});
if (map != null && !map.isEmpty()) {
StringBuilder buffer = new StringBuilder();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (buffer.length() == 0) {
buffer.append("?");
} else {
buffer.append("&");
}
buffer.append(entry.getKey()).append("=");
if (entry.getValue() instanceof Collection) {
Collection collection = (Collection) entry.getValue();
for (Object o : collection) {
buffer.append(encodeValue(encode, o)).append(",");
}
buffer.deleteCharAt(buffer.length() - 1);
} else {
buffer.append(encodeValue(encode, entry.getValue()));
}
}
return buffer.toString();
}
return "";
}
/**
* url编码
*
* @param encode 是否编码
* @param value 待编码值
* @return 编码后的值
*/
private static String encodeValue(boolean encode, Object value) {
return encode ? URLEncoder.encode(value.toString(), StandardCharsets.UTF_8) : value.toString();
}
}
编写客户端
public class Client {
public static void main(String[] args) throws Exception {
GeoCodingInfo geoCodingInfo = BaiduMapUtility.geoCoding("上海市闵行区");
if (geoCodingInfo.isSuccess()) {
GeoCodingLocationInfo locationInfo = geoCodingInfo.getResult().getLocation();
System.out.println(locationInfo.getLng());
System.out.println(locationInfo.getLat());
}
}
}
这里我们将上海市闵行区这个地址转换成经纬度,输出如下
121.38861193361008
31.118842580087429
将经纬度显示一下
将经纬度转换成省市区等地址
定义接口请求
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class ReverseGeoCodingRequest {
/**
* 经纬度坐标获取地址,lat<纬度>,lng<经度>
*/
private String location;
/**
* 用户申请注册的key
*/
private String ak;
/**
* 请求签名
*/
private String sn;
/**
* 输出格式为json或者xml
*/
private String output;
}
定义接口响应
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class ReverseGeoCodingInfo {
private static final Integer SUCCESS = 0;
//返回结果状态值, 成功返回0
private Integer status;
//响应结果
private ReverseGeoCodingResultInfo result;
public boolean isSuccess() {
return SUCCESS.equals(status);
}
}
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class ReverseGeoCodingResultInfo {
/**
* 经纬度信息
*/
private GeoCodingLocationInfo location;
/**
* 结构化地址信息
*/
@JSONField(name = "formatted_address")
private String formattedAddress;
}
客户端调用
public class Client2 {
public static void main(String[] args) throws Exception {
String location = "31.118842580087429,121.38861193361008";
ReverseGeoCodingInfo reverseGeoCodingInfo = BaiduMapUtility.reverseGeoCoding(location);
if (reverseGeoCodingInfo.isSuccess()) {
System.out.println(reverseGeoCodingInfo.getResult().getFormattedAddress());
}
}
}
将上一个接口响应的经纬度转换成地址信息,输出如下
上海市闵行区沪闵路6258号
遇到的问题
整个请求访问的核心就是创建请求的签名,逆地理编码的接口请求中,经纬度坐标中的逗号需要进行url编码,其他不需要。
错误的url
http://api.map.baidu.com/reverse_geocoding/v3/?ak=ak&location=31.118842580087429,121.38861193361008&output=json&sn=sn
正确的url
http://api.map.baidu.com/reverse_geocoding/v3/?ak=ak&location=31.118842580087429%2C121.38861193361008&output=json&sn=sn