Skip to content

GeoJSON与GeoBuf的压缩解码

代码喵匠 于 2022-11-27 21:08:12 发布 阅读量1.5k 收藏 5 点赞数 文章链接:https://blog.csdn.net/weixin_59795589/article/details/128068362


项目说明

问题描述

工作中前端项目开发偶尔会遇到echarts地图展示的需求,而地图文件的大小影响着请求的速度,用户网络稍慢一点,就会有展示延迟的问题;所以地图文件的压缩再传输是很有必要的; 网络上探索了一番,最后决定用Geobuf( https://github.com/mapbox/geobuf );

地图

获取地图途径很多,这里介绍几个常用的:

  • https://hxkj.vip/demo/echartsMap/ 此网站可以一次性打包所有的地图文件(geoJson格式),可以查看打包进度和查看打包失败的文件,我用的就是这个,但是有个问题是需要多尝试几次打包,再自行整理失败的文件,因为失败的概率挺大的,文件缺失的很多;

  • http://datav.aliyun.com/portal/school/atlas/area_selector 阿里提供的自不用说,很好用,就是没有一次直接打包全部的方式,可自行写脚本跑一遍获取;

  • https://map.vanbyte.com/street.html 这个网站可以一劳永逸,获取全部阿里云提供的地图文件,有压缩版,也很方便;准确性需自行考量;

压缩方案

压缩方法有很多,网上有通过后端Java进行压缩,前端直接请求压缩版的再开发;我换了个方式,利用node写个脚本跑一遍要压缩的文件就行了,项目里可直接获取压缩后的文件进行解码使用,当然啦,实际项目不一定要这样,要不前端打包会有些大 (:

利用express开发框架配合压缩工具

npm init
npm install express --save
  • 安装压缩工具:geobuf, pbf
npm install geobuf
npm install pbf
  • package.json 所有依赖项
"dependencies": {
    "express": "^4.18.2",
    "geobuf": "^3.0.2",
    "pbf": "^3.2.1"
 }
  • 建立 index.js 运行文件,放入地图文件等,文件列表如下
Demo
├─ index.js
├─ maps
│  ├─ compress	    // 预备存放压缩文件
│  └─ origin 		// 地图源文件
├─ package-lock.json
└─ package.json
  • index.js 代码如下:
javascript
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const port = 3000;
const targetFilePath = path.resolve('./maps/origin');

// 压缩工具
const geobuf = require('geobuf');
const Pbf = require('pbf');

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

/**
 * 压缩文件
 * @param {*} filePath 压缩前路径
 * @param {*} compressFilePath 压缩后路径
 */
function compressFile(filePath, compressFilePath) {
  fs.readFile(filePath, 'utf8', function (err, data) {
    const geojsonObj = JSON.parse(data.trim());
    let buffer = geobuf.encode(geojsonObj, new Pbf());

    fs.writeFile(compressFilePath, buffer, function (error) {
      if (error) {
        console.log('error:', error);
        console.info('--Error--:' + filePath);
      } else {
        console.info('--Success--:' + filePath);
      }
    });
  });
}

/**
 * 指定目录下的文件进行压缩
 * @param {*} filePath 指定路径
 */
function fileDisplayToCompress(filePath) {
  fs.readdir(filePath, function (err, files) {
    if (err) {
      return console.error('Error:(spec)', err);
    }

    files.forEach((filename) => {
      // 获取当前文件的绝对路径
      const filedir = path.join(filePath, filename);
      const realFilePath = filedir.split('\\maps\\origin\\')[1].replace(/\\/g, '/');
      const baseOriginPath =  'maps/origin/';
      const baseCompressPath = 'maps/compress/';

      // fs.stat(path)执行后,会将stats类的实例返回给其回调函数。
      fs.stat(filedir, (eror, stats) => {
        if (eror) {
          return console.error('Error:(spec)', err);
        }

        // 是否是文件
        const isFile = stats.isFile();
        // 是否是文件夹
        const isDir = stats.isDirectory();

        if (isFile) {
          const compressFilePath = realFilePath.replace(/geoJson/, 'geobuf.bpf');
          compressFile(baseOriginPath + realFilePath, baseCompressPath + compressFilePath);
        }

        if (isDir) {
          fs.mkdir(baseCompressPath + realFilePath, { recursive: true }, (err) => {
            if (err) throw err;
            fileDisplayToCompress(filedir);
          });
        }
      });
    });
  });
}

fileDisplayToCompress(targetFilePath);
  • 命令行运行 index.js
node index.js

运行后,/maps/compress下就是所有压缩版的地图文件,文件路径都是配套的,可自行修改测试~

压缩后的文件大概是源文件的1/5大小,压缩量还是非常可观的!

解码方案

既然有了一手的压缩文件,自然得通过解码才能供开发所用;

  • 新建项目,目录如下 geobuf.js,pbf.js 文件可以在之前安装node_modules中获取; 这里拿其中一个压缩文件 100000.geobuf.bpf 进行测试;
Demo
├─ index.html
├─ js
│  ├─ geobuf.js
│  └─ pbf.js
└─ maps
   └─ 100000.geobuf.bpf 	// 一个压缩版的地图文件
  • index.html 代码如下
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>GeoJson | Geobuf</title>
</head>

<body>

  <script src="js/geobuf.js"></script>
  <script src="js/pbf.js"></script>
  <script>
    function getFile(name) {
      return fetch(`maps/${name}.geobuf.bpf`, {
        responseType: 'arraybuffer'
      }).then(res => {
        return res.arrayBuffer();
      }).then(data => {
        let geojson = geobuf.decode(new Pbf(data))
        console.info(geojson)	// geojson就是解码后的地图文件
      })
    }

    getFile('100000');
  </script>
</body>

</html>
总结

GeoBuf有很多好处:结构紧凑、文件小、方便编码和解码、能适用各种GeoJSON等等,开发者们可自行修改测试 (:

Credit @Wayne19980