海量经纬度传输方案

711 阅读3分钟

背景

经纬度数据有 上千万个点,这上千万数据需要通过后端传递到前端。

数据存储

后端将数据存储在OSS上。

前端通过OSS拉去数据,以TypedArray格式存储在前端内存。

TypedArray 在大数据量的存储中可以占用更少的内存,对 GC 友好等特性也可以大幅度提升可视化应用的性能。

数据存储在OSS上的形式

一个坐标点 分为 经度和纬度 两个浮点数,将这两个浮点数扩大10的7次方倍 后省略小数位,然后我们需要找一个合适二进制数据缓存区类型数组对象(这里以JavaScript的术语说明)进行存放。

数据类型表示范围
经纬度坐标(扩大10^7后)-180*10^7 ~ 180*10^7(东西半球的坐标范围是-180到180, 南北半球的坐标范围是-90到90。)
32位二进制带符号整数-2^31 ~ (2^31)-1

可以看出 32位二进制带符号整数表示范围 正好覆盖 经纬度坐标(扩大10^7后)。

生成数据文件

以实际数据举例 经度:1.21231, 纬度:2.32131 ===》1212310023213100 ====》00B8FBDC0162342C(二进制太长,这里以16进制表示) 生成的数据文件为:查看文章底部附件

代码生成方式 NodeJS方式为例

/**
 * 将经纬度坐标写入二进制数据缓冲区
 * @param latLons 需要写入的经纬度坐标,格式为[[lat, lon], [lat, lon]...]
 * @returns {Buffer}
 */
function getBuffByLatlon(latLons = []) {
  // 创建buffer缓冲区 http://nodejs.cn/api/buffer.html#buffer_buffer
  // [[lat, lon], [lat, lon]] 故缓冲区的大小为 参数数组长度 * 2 * 4(由于每个经纬度占4个字节)
  const buff = Buffer.allocUnsafe(latLons.length * 2 * 4)
  // 当前为缓存区第几个字节
  let startIndex = 0
  latLons.forEach((latLon) => {
    const lat = latLon[0]
    const lon = latLon[1]
    // 指定使用 小端序(首位表示符号位)http://nodejs.cn/api/buffer.html#buffer_buf_writeint32be_value_offset
    buff.writeInt32LE(lat * 1e7, startIndex)
    startIndex += 4
    buff.writeInt32LE(lon * 1e7, startIndex)
    startIndex += 4
  })
  return buff
}

/**
 * 创建数据文件
 */
function creatFile() {
  const fs = require('fs')
  const buf = getBuffByLatlon([[1.21231, 2.32131]])
  fs.writeFile('./buffFile.txt', buf, (err) => {
    if (err) throw err
    console.log('文件已被保存')
  })
}

// 开始创建
creatFile()

注意: 这里字节序要使用 小端序,x86 体系的计算机都采用小端字节序,目前,所有个人电脑几乎都是小端字节序,所以 TypedArray 数组内部也采用小端字节序读写数据,或者更准确的说,按照本机操作系统设定的字节序读写数据。 Arraybuffer-字节序

前端获取数据文件

function fetchData() {
    // OSS数据地址
    var dataURL = 'xxxxxx';
    var xhr = new XMLHttpRequest();
    xhr.open('GET', dataURL, true);
    // 设置响应数据格式为 [二进制数据缓存区]
    xhr.responseType = 'arraybuffer';

    xhr.onload = function (e) {
        // 将获取到的二进制字节流 创建为 32位二进制带符号整数
        var rawData = new Int32Array(this.response);
        // 用32位IEEE浮点数 存储经纬度
        var data = new Float32Array(rawData.length);
        for (var i = 0; i < rawData.length; i += 2) {
            data[i] = rawData[i+1] / 1e7;
            data[i+1] = rawData[i] / 1e7;
        }
        // 使用echarts渲染界面
        myChart.appendData({
            seriesIndex: 0,
            data: data
        });
    }
    xhr.send();
}

本地查看数据文件

推荐使用iHex(简洁方便)进行查看。

参考

Echarts 散点图示例