前提
我们在校车上安装了GPS定位器,每5秒发送一次GPS信息到服务器后台,表结构是这个样子的。
名称 | 行驶日志表 | |||
代码 | log_bus_drive | |||
注释 | 维护行驶日志记录 | |||
名称 | 代码 | 数据类型 | 限定 | 注释 |
主键 | bus_drive_id | long integer | 非空 | 系统内部唯一标识 |
校车Id | bus_id | long integer | 公交车id外键 | |
行程Id | itinerary_id | long integer | 行程表id外键 | |
车速 | bus_speed | Float | 车速 km/h | |
经度 | bus_longitude | Float | 经度 | |
纬度 | bus_latitude | Float | 纬度 | |
方向 | bus_direction | Float | 方向 | |
是否启用 | is_enabled | byte | 启用状态: 0停用; 1启用 | |
创建时间 | created_date | datetime | 创建时间 |
解决思路
数据按倒序排列,从最后一条和前一条比对,取得当前的公里数,然后累加。
性能
如果一次拿完一天的数据进行计算,这个过程是很可能会出错的,所以就把一天的时间段打散,按5分钟做为切分单元,对5分钟内的里程进行里程计算并叠加,算出一天的里程。
代码实现
1,计算总里程函数主要实现部分
//要统计一天的数据,建议按照5分钟为一次计算单位来计算
Long mileageCount=0;
List<LogBusDriveEntity> logBusDriveEntityList = countMileageDao.queryBusDriveEvery5Min(busId, dateStr);
Double beLat = 0D;
Double beLng = 0D;
Double countMi = 0D;
if (logBusDriveEntityList.size() > 0 && logBusDriveEntityList.get(0) != null) {
beLat = logBusDriveEntityList.get(0).getBusLatitude();
beLng = logBusDriveEntityList.get(0).getBusLongitude();
for (int kk = 1; kk < logBusDriveEntityList.size(); kk++) {
LogBusDriveEntity logBusDriveEntity = logBusDriveEntityList.get(kk);
Double reLat = logBusDriveEntity.getBusLatitude();
Double reLng = logBusDriveEntity.getBusLongitude();
if (reLat != 1.0E-6) {
//单位是米
countMi += BaiduApi.distanceCalculationSite(beLat + "," + beLng, reLat + "," + reLng);
}
beLat = reLat;
beLng = reLng;
}
}
//换成公里数
mileageCount=countMi / 1000;
2,百度API的计算函数
public static double distanceCalculationSite(String start, String end) {
BufferedReader in = null;
String url = "https://api.map.baidu.com/routematrix/v2/driving";
String ak = "替换成你的Key";
String output = "json";
url = url+"?ak="+ak+"&output="+output+"&origins="+start+"&destinations="+end;
try {
URL tirc = new URL(url);
URLConnection connection = tirc.openConnection();
connection.setDoOutput(true);
in = new BufferedReader(new InputStreamReader(tirc.openStream(), "UTF-8"));
String res;
StringBuilder sb = new StringBuilder("");
while ((res = in.readLine()) != null) {
sb.append(res.trim());
}
String str = sb.toString();
ObjectMapper mapper = new ObjectMapper();
if (StringUtils.isNotEmpty(str)) {
JsonNode jsonNode = mapper.readTree(str);
jsonNode.findValue("status").toString();
JsonNode resultNode = jsonNode.findValue("result");
JsonNode locationNode = resultNode.findValue("distance");
return locationNode.get("value").asDouble();
}
} catch (Exception e) {
log.error("{百度地图获取两点驾驶距离}------------>"+e);
e.printStackTrace();
}
return 0;
}
3,在数据库里把一天的数据打散成每5分钟一批的纪录
<!--其中300表示300秒,也就是5*60,5分钟 -->
<select id="queryBusDriveEvery5Min" resultType="com.wntime.service.common.entity.LogBusDriveEntity">
select distinct on (CAST (extract(epoch from date_trunc('second', created_date)) AS integer) / 300) created_date
ts, CAST (extract(epoch from date_trunc('second', created_date)) AS integer) % 300 as sec,
bus_longitude, bus_latitude
from log_bus_drive
where bus_id = #{busId} and
to_char( created_date, 'yyyyMMdd' ) = #{yestodayStr}
order by CAST (extract(epoch from date_trunc('second', created_date)) AS integer) / 300 asc, created_date asc
</select>
总结:
1. 校车上安装了GPS定位器,每5秒发送一次位置信息到服务器。
2. 要统计一天的行驶里程,需要对一天的数据进行切分,比如每5分钟作为一个计算单元,计算5分钟内的里程和然后累加得到全天里程。
3. 利用百度地图API可以计算两点之间的距离,从而得到5分钟内的里程。
补充:
1. 表结构比较简单,除了基本信息如经纬度、速度外,还有创建时间用于切分计算单元。
2. 查询每5分钟的数据时,使用created_date字段进行GROUP BY切分,并在SELECT子句中使用DISTINCT ON对同一计算单元内的第一条数据进行保留。
3. 在循环计算里程时,需要初始时记录上一条数据的经纬度,然后与当前数据计算距离,并累加到总里程。循环结束后即得到5分钟内的里程。
4. 由于GPS定位存在一定误差,如果直接对一整天的数据计算里程,误差会被放大。所以,选择适当的计算单元,如5分钟,可以减小误差影响。
5. 百度地图API中,调用"驾车导航距离查询"接口,传入起点和终点的经纬度,可以得到两点之间的里程数据。总的来说,通过对一天的数据进行拆分,选择较小的计算单元,结合地图API计算单元内里程,从而得到全天较为准确的行驶里程数据。这样可以为后续的报表和分析提供较为准确的基础数据。