使用百度地圖API進行地址和經緯度之間的轉換
阿新 • • 發佈:2020-12-19
簡介
我們在專案中可能會遇到將經緯度轉換成省市區的需求,這個時候需要各種地圖提供的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