一、账号注册及权限申请
登录百度地图开放平台:
百度地图-百万开发者首选的地图服务商,提供专属的行业解决方案(没有百度账号可以先进行注册)
申请开发者认证:
点击右上角控制台,根据需要提交对应材料进行认证授权
需求简介:
我们以一个简单的场景为例,当商家接收到用户从A地至B地的配送请求时往往要计算距离以收取配送费,并且超出服务范围距离的不予以受理,我们在服务端可以对发货和收货点间的路线距离做一下简单的计算(注意不是两点直线距离)。
我们需要根据收发两地经纬度坐标做路线规划的起始点并获取其距离,所以本次实验我们将用到轻量级路线规划和地理编码API接口服务。
创建应用:
认证通过后进入控制台,在控制台左侧看板点击应用管理-->我的应用-->创建应用
根据开发需求选择应用类型,不同类型提供的服务有所不同,这里我们选服务端,下面的ip白名单设置如果用于学习了解的话可以直接设置为0.0.0.0/0,这样可以避免后续实验的一些错误。
注意:若如此设置请妥善保存AK,或在试验结束直接删除应用
二、环境准备
配置文件写入
便于集中管理这里我们将假设的店铺地址和AK写入项目配置文件
shop:
address: 河南省郑州市中原区(采用标准的结构化描述尽可能将地址写准确我这里省略了)
baidu:
ak: 你的AK在百度地图开放平台控制台我的应用查看
注入配置项
这里参数较少,我们直接用@Value注解,不专门去写配置类了
若不在SpringBoot项目中进行实验无法注入Value,可直接声明为字符串
@Value("${cds.shop.address}")//写为自己配置文件的命名
private String shopAddress;
@Value("${cds.baidu.ak}")
private String ak;
三、开发文档概览与调整
开发文档查看
注意:由于具体参数过多篇幅有限不在本文一一赘述,对文中出现的不解参数,大家可自行根据下方链接查看参数意义
1.地点经纬度查询
开发文档>Web服务API>地理编码>服务功能>地理编码
本页面有详细的接口说明 API服务地址 请求参数 返回参数,除此之外页面底部还提供了示例代码和返回值,在此处输入AK即可生成初始代码
2.路线规划与距离计算
开发文档>Web服务API>轻量级路线规划>服务功能>骑行路线规划
同地理编码页,这里也有详细的介绍,我们直接生成示例代码。
/**
* 选择了ak或使用IP白名单校验:
*/
package com.android.javaproject;
import org.springframework.web.util.UriUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
public class SearchHttpAK {
public static String URL = "https://api.map.baidu.com/geocoding/v3?";
public static String AK = "修改为你的AK";
public static void main(String[] args) throws Exception {
SearchHttpAK snCal = new SearchHttpAK();
Map params = new LinkedHashMap<String, String>();
params.put("address", "北京市海淀区上地十街10号");
params.put("output", "json");
params.put("ak", AK);
params.put("callback", "showLocation");
snCal.requestGetAK(URL, params);
}
/**
* 默认ak
* 选择了ak,使用IP白名单校验:
* 根据您选择的AK已为您生成调用代码
* 检测到您当前的ak设置了IP白名单校验
* 您的IP白名单中的IP非公网IP,请设置为公网IP,否则将请求失败
* 请在IP地址为0.0.0.0/0 外网IP的计算发起请求,否则将请求失败
*/
public void requestGetAK(String strUrl, Map<String, String> param) throws Exception {
if (strUrl == null || strUrl.length() <= 0 || param == null || param.size() <= 0) {
return;
}
StringBuffer queryString = new StringBuffer();
queryString.append(strUrl);
for (Map.Entry<?, ?> pair : param.entrySet()) {
queryString.append(pair.getKey() + "=");
// 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可
// queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8") + "&");
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
java.net.URL url = new URL(queryString.toString());
System.out.println(queryString.toString());
URLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.connect();
InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
BufferedReader reader = new BufferedReader(isr);
StringBuffer buffer = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
reader.close();
isr.close();
System.out.println("AK: " + buffer.toString());
}
}
根据需求调整代码
1.公共部分抽取
在网页生成示例代码后不难发现不管是地理编码还是路线规划都有相同的requestGetAK(String strUrl, Map<String, String> param)方法来进行请求和响应读取,我们稍加修改将此方法提取出来
private static JSONObject akGet(String strUrl, Map<String, String> param) throws IOException {
if (strUrl == null || strUrl.length() <= 0 || param == null || param.size() <= 0) {
return null;
}
StringBuffer queryString = new StringBuffer();
queryString.append(strUrl);
for (Map.Entry<?, ?> pair : param.entrySet()) {
queryString.append(pair.getKey() + "=");
// 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可
// queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8") + "&");
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
URL url = new URL(queryString.toString());
URLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.connect();
InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
BufferedReader reader = new BufferedReader(isr);
StringBuffer buffer = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
reader.close();
isr.close();
//将响应数据以JSONObject对象形式返回便于解析提取所需数据
return JSON.parseObject(String.valueOf(buffer));
}
这里主要改动了返回值,我们需要用到alibaba的fastjson,maven依赖坐标如下
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
2.返回值解析
根据不同需要我们对返回值的解析抽取方式不同,这里我们根据开发文档页面的返回数据示例将经纬度提取和路线规划分别来解析
- 地理编码(经纬度提取)返回数据及解析
{ "status": 0, "result": { "location": { "lng": 116.30762232672, "lat": 40.056828485961 }, "precise": 1, "confidence": 80, "comprehension": 100, "level": "门址" } }
public String requestGetLatLng(String strUrl, Map<String, String> param) throws Exception { JSONObject jsonObject = akGet(strUrl, param); if (!jsonObject.getString("status").equals("0")) { //自定义异常类,可以自行编写,或修改为打印语句 throw new OrderBusinessException("地址解析失败"); } JSONObject result = jsonObject.getJSONObject("result"); JSONObject location = result.getJSONObject("location"); double lng = location.getDoubleValue("lng"); double lat = location.getDoubleValue("lat"); //经纬度坐标 return lat + "," + lng; }
-
路线规划返回数据及解析(返回数据太长了大家可以回到第三部分开头,点击链接去开发文档页自行查看)
public void requestGetRouteDistance(String strUrl, Map<String, String> param) throws Exception { JSONObject jsonObject = akGet(strUrl, param); if (!jsonObject.getString("status").equals("0")) { //自定义异常类,可以自行编写,或修改为打印语句 throw new OrderBusinessException("路线规划失败"); } //数据解析 JSONObject result = jsonObject.getJSONObject("result"); JSONArray jsonArray = result.getJSONArray("routes"); //距离获取 Integer distance = (Integer) (jsonArray.getJSONObject(0)).get("distance"); //路线获取,用for循环处理也行 List<String[]> steps = jsonArray.stream() .flatMap(route ->((JSONObject) route).getJSONArray("steps").stream()) .map(step -> new String[]{ ((JSONObject) step).getString("instruction"), ((JSONObject) step).getString("turn_type") }) .collect(Collectors.toList()); // 打印结果 for (String[] step : steps) { System.out.println("Instruction: " + step[0] + ", Turn Type: " +step[1]); } if (distance > 5000) { //自定义异常类,可以自行编写,或修改为打印语句 //距离超过5000米 throw new OrderBusinessException("超出范围"); } }
3.传参调用测试
虽然requestGetAK(String strUrl, Map<String, String> param)方法所需参数类型相同,但根据目的和需求我们传入的API服务地址Url和param参数不尽相同
我们可以一起看下这个距离检测方法,请求参数我们只传入了需要的部分,具体可见开发文档,需要注意的是我们的解析方法是通过解析JSON数据完成的,若在参数中加入了params.put("callback", "showLocation");数据格式将变为JSONP 格式,我们可以改进解析方法从中提取出普通的 JSON 数据或者直接将该参数去除
public void checkDistance(String address) throws Exception {
//地理编码请求参数
Map lLparams = new LinkedHashMap<String, String>();
lLparams.put("address", shopAddress);
lLparams.put("output", "json");
lLparams.put("ak", ak);
// 移除 callback 参数
//params.put("callback", "showLocation");
//地理编码服务API地址
String lLUrl="https://api.map.baidu.com/geocoding/v3?";
String shopLngLat=requestGetLatLng(lLUrl,lLparams);
lLparams.put("address", address);
String userLngLat=requestGetLatLng(lLUrl,lLparams);
//路线规划请求参数
Map distanceParams = new LinkedHashMap<String, String>();
distanceParams.put("origin", shopLngLat);
distanceParams.put("destination", userLngLat);
distanceParams.put("ak", ak);
//路线规划服务API地址
String distanceUrl="https://api.map.baidu.com/directionlite/v1/riding?";
requestGetRouteDistance(distanceUrl,distanceParams);
}
最后我们调用这个方法向其中传入用户地址字符串进行测试
requestGetRouteDistance顺利接收到起始点经纬度坐标然后请求路线规划服务API
最终得到路线与距离