需求:使用腾讯地图api,将百度地图经纬度转换为腾讯地图经纬度。
文档地址:腾讯地图坐标转换api文档地址
功能:实现从其它地图供应商坐标系或标准GPS坐标系,批量转换到腾讯地图坐标系。
需求:因为需要批量转换的经纬度数量过多,需要在后端分段请求接口,再拼接返回给前端。
//HTTP GET 请求 : 只支持1000字节
String url = "https://apis.map.qq.com/ws/coord/v1/translate?";
//格式:纬度前,经度后,纬度和经度之间用",“分隔,每组坐标之间使用”;"分隔;
String locations = "locations=39.12,116.83;30.21,115.43";
String type = "&type=3"; //输入的locations的坐标类型范围:0-6。3为baidu经纬度
String key = "&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77"; //开发密钥(Key)在我的应用中获得
// 请求示例
String url = https://apis.map.qq.com/ws/coord/v1/translate?locations=lng1,lat1;lng2,lat2&type=3&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77
//响应示例
{
"status": 0,
"message": "转换成功",
"locations": [
{
"lng": 116.8234023,
"lat": 39.11428542
},
{
"lng": 115.42357111,
"lat": 30.20378632
}
]
}
(1)登陆后,进入应用管理——我的应用——创建应用——获得密钥
(2)进入配额管理——我的配额,可以看到各个接口的每日最高调用量和并发量额度。
Tips:
(1)限制每秒请求次数:每秒最高的请求并发量是5次
(2)限制请求大小:批量支持坐标个数以HTTP GET方法请求上限为准:1000字节
//计算字节大小
byte[] buff = xqRange.getBytes();
int i = buff.length;
System.out.println(i);
//经过测试,大概40组经纬度可以进行一次请求不会失败。
// 提取经纬度的类
public class xxxVO {
private String lng; // 经度
private String lat; // 纬度
}
//接收坐标转换接口返回的参数类
public class HttpsResponseVO {
// 状态
private Integer status;
// 信息
private String message;
// 转换后的地址
private JSONArray locations;
}
只处理了请求的数据量,未考虑每秒最大请求数。在需要请求的次数 > 5 时,会出现请求不到数据的情况
public String baidu2tencent1() {
try {
//## 配置请求字符串
String url = "https://apis.map.qq.com/ws/coord/v1/translate?locations=";
String type = "&type=3"; //baidu地图
String key = "&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77";
String range1 = ""; //保存拼接值
String range2 = ""; //保存返回结果
//Spring3.0开始支持的一个 HTTP 请求工具,用于在代码中请求第三方接口
RestTemplate restTemplate = new RestTemplate();
//## 处理用于请求转换的经纬度数组字符串:
String range =
"[{"lng":110.355443,"lat":20.038738}
,{"lng":110.358749,"lat":20.037388}
,{"lng":110.360217,"lat":20.036845}
,{"lng":110.360968,"lat":20.03648}]";
//先转为jsonArray对象,在转为目标对象xxxVO集合
JSONArray objects = JSONUtil.parseArray(range); //hutool工具方法:JSONUtil
List<xxxVO> xxxVOList = JSONUtil.toList(objects, xxxVO.class);
// 计算数组大小
int size = xxxVOList.size();
if (size > 40) {
//需要将经纬度数组,分成多个40组的集合,分段请求多次
int requestNum = size / 40;
int add = size % 40;
if (add > 0) {
requestNum ++;
}
System.out.println("总共需要请求 = " + requestNum + "次");
//保存分段请求集合
List<List<xxxVO>> splitList = new ArrayList<List<xxxVO>>(); //分段请求的集合
int loc = 40; //记录每次分段的末尾经纬度位置
for(int i = 0; i < requestNum ; i++) {
if(loc > size) {
loc = size;
}
System.out.println("起始位置 = " + i * 40 + " —— 结束位置 = " + loc);
List<xxxVO> spList = xxxVOList.subList(i * 40 , loc);//截取集合部分
splitList.add(spList);
loc = loc + 40; //末尾位置后移
}
//遍历截取的集合,组合每段请求的参数
for(List<xxxVo> voList : splitList) {
System.out.println("组合参数 " + " 》》》》》》");
String rangeParam = ""; //保存请求的经纬度locations字段
for (xxxVo childVo : voList) {
//遍历集合,组合请求参数
rangeParam = rangeParam + childVo.getLat() + "," + childVo.getLng() + ";"
}
// 遍历结束后,切割掉末尾的";"
rangeParam = rangeParam.substring(0, rangeParam.length() - 1);
//拼接请求url
String requestUrl = url + rangeParam + type + key;
//请求接口
HttpsResponseVo forObject = restTemplate.getForObject(requestUrl, HttpsResponseVO.class);
//获取转换后的经纬度
String local = "" + forObject.getLocations(); // "[{"lng": 116.8234023,"lat": 39.11428542},{"lng": 115.42357111,"lat": 30.20378632}]"
range1 = range1 + local.substring(1, local.length() - 1) + "," // "{"lng": 116.8234023,"lat": 39.11428542},{"lng": 115.42357111,"lat": 30.20378632},"
}
//所有的集合遍历结束后,拼接需要返回的字符串
range2 = "[" + range1.substring(0, range1.length() - 1) + "]";
System.out.println("最终返回的经纬度数组 = " + range2);
} else {
//只需要请求一次
for (xxxVO vo : xxxVOList) {
range1 = range1 + vo.getLat() + "," + vo.getLng() + ";";
}
xqRange = xqRange.substring(0, xqRange.length()-1);
String requestUrl2 = url + range1 + type + key;
HttpsResponseVO forObject = restTemplate.getForObject(url3, HttpsResponseVO.class);
range2 = "" + forObject.getLocations();
}
return range2;
} catch (exception e) {
e.printStackTrace();
}
}
当一秒内请求次数大于5时,使用@Async进行异步调用处理
在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。
// 基于Java配置的启用方式:在启动项添加注解
@EnableAsync
public class IxxxProgram extends SpringBootServletInitializer {
...
}
使用@Async注解的方法,返回值的数据类型为Future,具体结果类型为AsyncResult
public String main() throws ExecutionException, InterruptedException {
//## 处理用于请求转换的经纬度数组字符串:
String range =
"[{"lng":110.355443,"lat":20.038738}
,{"lng":110.358749,"lat":20.037388}
,{"lng":110.360217,"lat":20.036845}
,{"lng":110.360968,"lat":20.03648}]";
//先转为jsonArray对象,在转为目标对象xxxVO集合
JSONArray objects = JSONUtil.parseArray(range); //hutool工具方法:JSONUtil
List<xxxVO> xxxVOList = JSONUtil.toList(objects, xxxVO.class);
Future<String> stringFuture = baidu2tencent2(xxxVOList);
//获得返回的字符串
String res = stringFutrue.get();
return res;
}
@Async
public Future<String> baidu2tencent2(List<xxxVO> xxxVOList) {
try {
//## 配置请求字符串
String url = "https://apis.map.qq.com/ws/coord/v1/translate?locations=";
String type = "&type=3"; //baidu地图
String key = "&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77";
String range1 = "";
String range2 = "";
//Spring3.0开始支持的一个 HTTP 请求工具,用于在代码中请求第三方接口
RestTemplate restTemplate = new RestTemplate();
// 计算数组大小
int size = xxxVOList.size();
if (size > 40) {
//需要将经纬度数组,分成多个40组的集合,分段请求多次
int requestNum = size / 40;
int add = size % 40;
if (add > 0) {
requestNum ++;
}
System.out.println("总共需要请求 = " + requestNum + "次");
//保存分段请求集合
List<List<xxxVO>> splitList = new ArrayList<List<xxxVO>>(); //分段请求的集合
int loc = 40; //记录每次分段的末尾经纬度位置
for(int i = 0; i < requestNum ; i++) {
if(loc > size) {
loc = size;
}
System.out.println("起始位置 = " + i * 40 + " —— 结束位置 = " + loc);
List<xxxVO> spList = xxxVOList.subList(i * 40 , loc);//截取集合部分
splitList.add(spList);
loc = loc + 40; //末尾位置后移
}
//遍历截取的集合,组合每段请求的参数
for(List<xxxVo> voList : splitList) {
System.out.println("组合参数 " + " 》》》》》》");
String rangeParam = ""; //保存请求的经纬度locations字段
for (xxxVo childVo : voList) {
//遍历集合,组合请求参数
rangeParam = rangeParam + childVo.getLat() + "," + childVo.getLng() + ";"
}
// 遍历结束后,切割掉末尾的";"
rangeParam = rangeParam.substring(0, rangeParam.length() - 1);
//拼接请求url
String requestUrl = url + rangeParam + type + key;
//请求接口
HttpsResponseVo forObject = restTemplate.getForObject(requestUrl, HttpsResponseVO.class);
//因为一秒内超过请求次数,无法获得转换数据,等待一秒后在请求一次
if (forObject.getLocations() == null) {
Thread.sleep(1000); //请求失败,等一秒
forObject = restTemplate.getForObject(requestUrl, HttpsResponseVO.class);
}
//获取转换后的经纬度
String local = "" + forObject.getLocations(); // "[{"lng": 116.8234023,"lat": 39.11428542},{"lng": 115.42357111,"lat": 30.20378632}]"
range1 = range1 + local.substring(1, local.length() - 1) + "," // "{"lng": 116.8234023,"lat": 39.11428542},{"lng": 115.42357111,"lat": 30.20378632},"
}
//所有的集合遍历结束后,拼接需要返回的字符串
range2 = "[" + range1.substring(0, range1.length() - 1) + "]";
System.out.println("最终返回的经纬度数组 = " + range2);
} else {
//只需要请求一次
for (xxxVO vo : xxxVOList) {
range1 = range1 + vo.getLat() + "," + vo.getLng() + ";";
}
xqRange = xqRange.substring(0, xqRange.length()-1);
String requestUrl2 = url + range1 + type + key;
HttpsResponseVO forObject = restTemplate.getForObject(url3, HttpsResponseVO.class);
range2 = "" + forObject.getLocations();
}
return new AsyncResult<String>(range2);
} catch (exception e) {
e.printStackTrace();
}
}
参考链接:
Spring中@Async用法详解及简单实例.
RestTemplate 用法详解.