Java实现10万条经纬度数据压缩后只有15k

前言

项目中行程上报数据,一般在结束的时候上报,类似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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁漂打工仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值