项目说明
问题描述
工作中前端项目开发偶尔会遇到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 代码如下:
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 代码如下
<!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等等,开发者们可自行修改测试 (: