前言
项目中行程上报数据,一般在结束的时候上报,类似keep软件的跑步功能,点击开始,本地开始每一秒钟存储当前位置、方向、速度、温度等属性,点击结束,将这些数据一次性提交。考虑到一天就可能有86400条数据,如果不压缩,post请求基本上扛不住
流程
经纬度相关数据压缩流程
1.定义本地经纬度数组list
2.调用TrjCompressor.encode(List list)函数,得到字符串A
3.gzip处理字符串A,得到最终的字符串
算法
import java.util.ArrayList;
import java.util.List;
/**
* @author Wang
* @Date 2022/9/1
*/
public class TrjCompressor {
private double factor;
public TrjCompressor(int precision) {
factor = Math.pow(10, precision);
}
public String encode(List<TripDetailData> points) {
List<Long> output = new ArrayList<>();
TripDetailData prev = new TripDetailData();
prev.setLat(0.0);
prev.setLng(0.0);
prev.setTimestamp(0L);
prev.setIdx(0L);
prev.setBearing(0.0);
prev.setSpeed(0L);
/*新增*/
prev.setRpm(0L);
prev.setAcceleration(0L);
prev.setThrottle(0.0);
prev.setWaterTemp(0.0);
prev.setCvtTemp(0.0);
prev.setBetteryVolt(0.0);
prev.setGpsHeight(0.0);
for (int i = 0; i < points.size(); i++) {
if (i > 0) {
prev = points.get(i - 1);
}
write(output, points.get(i).getLat(), prev.getLat());
write(output, points.get(i).getLng(), prev.getLng());
write(output, points.get(i).getTimestamp(), prev.getTimestamp());
write(output, points.get(i).getIdx(), prev.getIdx());
write(output, points.get(i).getBearing(), prev.getBearing());
write(output, points.get(i).getSpeed(), prev.getSpeed());
/*新增*/
write(output, points.get(i).getRpm(), prev.getRpm());
write(output, points.get(i).getAcceleration(), prev.getAcceleration());
write(output, points.get(i).getThrottle(), prev.getThrottle());
write(output, points.get(i).getWaterTemp(), prev.getWaterTemp());
write(output, points.get(i).getCvtTemp(), prev.getCvtTemp());
write(output, points.get(i).getBetteryVolt(), prev.getBetteryVolt());
write(output, points.get(i).getGpsHeight(), prev.getGpsHeight());
}
return toASCII(output);
}
public List<TripDetailData> decode(String trjCode) {
long lat = 0, lng = 0, timestamp = 0, idx = 0, bearing = 0, speed = 0, rpm = 0, acceleration = 0, throttle = 0, waterTemp = 0, cvtTemp = 0, betteryVolt = 0, gpsHeight = 0,
latD, lngD, timestampD, idxD, bearingD, speedD,
rpmD, accelerationD = 0, throttleD, waterTempD, cvtTempD, betteryVoltD, gpsHeightD;
List<TripDetailData> points = new ArrayList<>();
for (int i = 0; i < trjCode.length(); ) {
Tuple tuple = read(trjCode, i);
latD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
lngD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
timestampD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
idxD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
bearingD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
speedD = tuple.result;
i = tuple.index;
/** 新增 **/
tuple = read(trjCode, i);
rpmD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
accelerationD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
throttleD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
waterTempD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
cvtTempD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
betteryVoltD = tuple.result;
i = tuple.index;
tuple = read(trjCode, i);
gpsHeightD = tuple.result;
i = tuple.index;
lat += latD;
lng += lngD;
timestamp += timestampD;
idx += idxD;
bearing += bearingD;
speed += speedD;
/*新增*/
rpm += rpmD;
acceleration += accelerationD;
throttle += throttleD;
waterTemp += waterTempD;
cvtTemp += cvtTempD;
betteryVolt += betteryVoltD;
gpsHeight += gpsHeightD;
TripDetailData point = new TripDetailData();
point.setLat((double) lat / factor);
point.setLng((double) lng / factor);
point.setTimestamp(timestamp);
point.setIdx(idx);
point.setBearing((double) bearing / factor);
point.setSpeed(speed);
/*新增*/
point.setRpm(rpm);
point.setAcceleration(acceleration);
point.setThrottle(throttle / factor);
point.setWaterTemp(waterTemp / factor);
point.setCvtTemp(cvtTemp / factor);
point.setBetteryVolt(betteryVolt / factor);
point.setGpsHeight(gpsHeight / factor);
points.add(point);
}
return points;
}
private void write(List<Long> output, Object currValue, Object prevValue) {
long currV, prevV;
if (currValue instanceof Long) {
currV = (Long) currValue;
prevV = (Long) prevValue;
} else if (currValue instanceof Double) {
currV = getRound((Double) currValue * factor);
prevV = getRound((Double) prevValue * factor);
} else {
throw new IllegalArgumentException("The type parameters must be long or double!");
}
long offset = currV - prevV;
offset <<= 1;
if (offset < 0) {
offset = ~offset;
}
while (offset >= 0x20) {
output.add((0x20 | (offset & 0x1f)) + 63);
offset >>= 5;
}
output.add((offset + 63));
}
private Tuple read(String s, int i) {
long b = 0x20;
long result = 0, shift = 0, comp = 0;
while (b >= 0x20) {
b = s.charAt(i) - 63;
i++;
result |= (b & 0x1f) << shift;
shift += 5;
comp = result & 1;
}
result >>= 1;
if (comp == 1) {
result = ~result;
}
return new Tuple(i, result);
}
private String toASCII(List<Long> nums) {
StringBuilder result = new StringBuilder();
for (long i : nums) {
result.append((char) i);
}
return result.toString();
}
private long getRound(double x) {
return (long) Math.copySign(Math.round(Math.abs(x)), x);
}
}
class Tuple {
long result;
int index;
Tuple(int index, long result) {
this.index = index;
this.result = result;
}
}
压缩
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ZipUtil;
/**
* ClassName ZlibUtils
*
* @author wang
* Date 2021/11/10 20:08
*/
public class ZipHelper {
/**
* 压缩
* @param data
* @return
*/
public static String compress(String data) {
byte[] bytes = ZipUtil.gzip(data, "UTF8");
return Base64.encode(bytes);
}
/**
* 解压缩
* @param data
* @return
*/
public static String decompress(String data) {
byte[] plainBytes = ZipUtil.unGzip(Base64.decode(data));
return new String(plainBytes);
}
public static void main(String[] args) {
}
}
实体
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* ClassName TripDetailRequest
*
* @author wang
* Date 2022/6/13 16:58
*/
@Data
@ApiModel("行程详情传输对象")
@Accessors(chain = true)
public class TripDetailData {
@ApiModelProperty(name = "唯一标识")
private String uid;
@ApiModelProperty(name = "tripUid")
private String tripUid;
@ApiModelProperty(name = "index")
private Long idx;
@ApiModelProperty(name = "当前点纬度")
private Double lat;
@ApiModelProperty(name = "当前点经度")
private Double lng;
@ApiModelProperty(name = "当前点速度")
private Long speed;
@ApiModelProperty(name = "当前点角度")
private Double bearing;
@ApiModelProperty(name = "当前点发生时间")
private Long timestamp;
/**
* speed :long
* rpm :long
* throttle :double
* waterTemp :double
* cvtTemp :double
* betteryVolt :double
* gpsHeight :double
*/
@ApiModelProperty(name = "转速")
private Long rpm;
@ApiModelProperty(name = "加速度")
private Long acceleration ;
@ApiModelProperty(name = "油量")
private Double throttle;
@ApiModelProperty(name = "水温")
private Double waterTemp;
@ApiModelProperty(name = "变速箱温度")
private Double cvtTemp;
@ApiModelProperty(name = "电池电压")
private Double betteryVolt;
@ApiModelProperty(name = "海拔")
private Double gpsHeight;
}
测试
import cn.hutool.json.JSONUtil;
import java.util.ArrayList;
import java.util.List;
/**
* @author Wang
* @Date 2022/9/1
*/
public class Demo {
public static void main(String[] args) {
List<TripDetailData> path = new ArrayList<>();
double lat = 118.745123;
double lng = 31.970818;
for (int i = 0; i < 100; i++) {
TripDetailData tripDetailData = new TripDetailData();
lat+=0.000001;
lng+=0.000001;
tripDetailData.setLat(lat);
tripDetailData.setLng(lng);
tripDetailData.setIdx((long) (i + 1));
tripDetailData.setBearing((double) (i + 1));
tripDetailData.setSpeed((long) (i + 1));
tripDetailData.setTimestamp(1661942489000L + i);
tripDetailData.setRpm((long) (i + 100));
tripDetailData.setAcceleration((long) (i + 666));
tripDetailData.setThrottle((double) (i + 10));
tripDetailData.setWaterTemp((double) (i + 10));
tripDetailData.setCvtTemp((double) (i + 10));
tripDetailData.setBetteryVolt((double) (i + 10));
tripDetailData.setGpsHeight((double) (i + 10));
path.add(tripDetailData);
}
TrjCompressor trjCompressor = new TrjCompressor(6);
String encode = trjCompressor.encode(path);
System.out.println(encode);
String zipCompress = ZipHelper.compress(encode);
encode = ZipHelper.decompress(zipCompress);
List<TripDetailData> decode = trjCompressor.decode(encode);
System.out.println(JSONUtil.toJsonStr(decode));
}
}
性能
十万条数据,最多1M
10万条数据压缩耗时(ms):176
10万条数据解压耗时(ms):88
参考:https://github.com/guanhonly/simple-trajectory-compressor/tree/master/src/main/java