使用echarts-mapmaker自定义地图
1. 地图数据来源
echarts仓库里面就有,地图数据不会经常改变,没必要在线获取,还慢
地图数据有json
和js
格式的,json
是echarts
压缩过的,js
本质就是引入json
只是包装了导出
2. 整形工具
最重要的部分就是利用工具处理这些数据,实现如扣去某块区域
、添加某块区域
、去除边界
等功能
- echarts-mapmaker压缩解码地图数据、合并拆分地图数据
- mapshaper整形地图,主要就是来去除边界
- 文档,上面有例子
- 这个仓库也有很多新的地图数据,自己可以翻看
3实战
3.1 拆分地图区域
这里我们拆分重庆各个区县为例
const path = require('path')
const fs = require('fs')
const fsp = require('fs/promises')
const distFolder = path.resolve(__dirname, './dist')
const dest = (fileName) => {
try {
fs.accessSync(distFolder, fs.constants.W_OK)
}catch(err) {
fs.mkdirSync(distFolder)
}
return path.resolve(__dirname, distFolder, fileName)
}
// 引入mapmaker
const maker = require('echarts-mapmaker/src/maker')
// 引入原始地图数据
const chongqingJson = path.resolve(__dirname, './map/province/chongqing.json')
// 解码地图数据并输出
// 解码后的文件就是GeoJson 方便后续操作
const dec_chonggqing = dest('dec_chongqing.json')
maker.decompress(chongqingJson, dec_chonggqing)
// 分割区域
maker.splitAsGeojson(dec_chonggqing, distFolder)
##3.2 合并区域 比如我需要合并中部地区、西部地区之类的,就需要合并区域
// 只需要这一句话
// 合并第一个和第二个json
maker.merge('./dist/九龙坡区.geojson', './dist/大渡口区.geojson')
这个方法有些问题:
- 无法自定义输出位置和输出文件名
- 每合并一次输出文件前面会加一个
merge_
,处理起来很麻烦 可以考虑自己写一个方法,参考源码其实不难(最好还是了解一下geojson
)
// 源码
function merge(geojson, geojsonToBeMerged){
// 读取两个文件
const data = fs.readFileSync(geojson, 'utf8');
const data2 = fs.readFileSync(geojsonToBeMerged, 'utf8');
var parent = JSON.parse(data);
var child = JSON.parse(data2);
// features要素,就是描述边界信息的一个数组
child.features.forEach(function(feature){
// 只合并不同name的区域
parent.features = parent.features.filter((featurex)=>{
return featurex.properties.name!==feature.properties.name;
});
// 直接把一个数据的边界信息放入另一个的features中
parent.features.push(feature);
});
// 写入文件
fs.writeFileSync('merged_'+path.basename(geojson), JSON.stringify(parent));
}
逻辑很简单,主要就是提取要素然后放入另一个地图里面,下面是我简单的一个封装
function mergeToOne(targetJSON, distDir, ...childrenJSONList){
// 读取目标json,其他json都往这个json合并
const data = fs.readFileSync(targetJSON, 'utf8');
const parentData = JSON.parse(data);
// 循环需要合并的数据
childrenJSONList.forEach(child => {
const childJSON= fs.readFileSync(child, 'utf8')
const childData = JSON.parse(childJSON)
// 合并
childData.features.forEach(function(feature){
parentData.features = parentData.features.filter((featurex)=>{
return featurex.properties.name!==feature.properties.name;
});
parentData.features.push(feature);
});
})
// 写文件
fs.writeFileSync(distDir, JSON.stringify(parentData));
}
// 使用
// 最后会输出一个`合并文件.json`,合并了三个区
mergeToOne('./dist/九龙坡区.geojson', './合并文件.json', './dist/大渡口区.geojson', './dist/沙坪坝区.geojson')
3.3 镶嵌区域
只用合并也可以,但是在echarts
中看起来就不大一样,使用合并高亮地图的时候看起来不是一体的,还有其他区别自行确认。
所以还要结合扣去区域cut
// 使用
maker.cut('a.json', '重庆', '沙坪坝区')
但是源码似乎有问题,反正我用开头给的地图数据是无法正确切除的
// 源码
var cutAHoleInFeatureAWithFB = (jsonFile, featureA, featureB) => {
data = fs.readFileSync(jsonFile, 'utf8');
var geojson = JSON.parse(data);
var featurea = geojson.features.find(feature => feature.properties.name === featureA);
var featureb = geojson.features.find(feature => feature.properties.name === featureB);
// https://stackoverflow.com/questions/43645172/geojson-multipolygon-with-multiple-holes
// 就是这一步 似乎不正确
featurea.geometry.coordinates.push(featureb.geometry.coordinates[0]);
fs.writeFileSync("cut_" + jsonFile, JSON.stringify(geojson));
};
查阅资料发现,geojson
挖去孔洞就是使用MultiPolygon
要素,
多边形带空洞的数据格式是一个四维数组,
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "MultiPolygon",
"coordinates":
[
[
[
// 这个就是孔洞的多边形数据
[101.6455078125,27.68352808378776],
[114.78515624999999,27.68352808378776],
[114.78515624999999,35.209721645221386],
[101.6455078125,35.209721645221386],
[101.6455078125,27.68352808378776]
],
[
[104.2822265625,30.107117887092357],
[108.896484375,30.107117887092357],
[108.896484375,33.76088200086917],
[104.2822265625,33.76088200086917],
[104.2822265625,30.107117887092357]
]
]
]
}
}
// 再简化
{
"type": "MultiPolygon",
"coordinates": [
[
{polygon},
{hole},
{hole},
{hole}
]
]
}
源码的问题就是
// coordinates层级错了
featurea.geometry.coordinates.push(featureb.geometry.coordinates[0]);
// 改为
featurea.geometry.coordinates[0].push(featureb.geometry.coordinates[0]);
下面是我修改后的
var cutArea = (jsonFile, featureA, featureB) => {
data = fs.readFileSync(jsonFile, 'utf8');
var geojson = JSON.parse(data);
var featurea = geojson.features.find(feature => feature.properties.name === featureA);
var featureb = geojson.features.find(feature => feature.properties.name === featureB);
featurea.geometry.coordinates[0].push(featureb.geometry.coordinates[0])
fs.writeFileSync("cut_" + jsonFile, JSON.stringify(geojson));
}
所以镶嵌的过程就是:
- 合并两个地图
- 扣去镶嵌的区域
mergeToOne('./dist/d_china.json', './合并文件.json', './dist/沙坪坝区.geojson')
cutArea('合并文件.json', '重庆', '沙坪坝区')
3.4 消除内部边界
主要用到mapshaper,挺复杂的一个东西,有时间可以详细研究
const maker = require('echarts-mapmaker/src/maker')
const shaper = require('mapshaper')
shaper.runCommands('./dist/d_chongqing.json -dissolve2 -o chongqing_shape_only.geojson', (err) => {
if(!err) {
maker.transform('./chongqing_shape_only.geojson', './chongqing_shape_only.echarts.json', '重慶市')
}
})
- 主要利用
mapshaper
的dissolve2
命令 - 去除边界完成后
echarts
不能直接使用,要用mapmaker
转化一次,这里和挂网的文档不一样注意使用transform
方法 - 官网文档使用的是全局安装
mapshaper
然后用命令行,我这里用的是wiki
上的变成方式,本质上也是命令行。
4 总结
以上就是基本的一些操作,通过结合上面的基本方法,我们可以随意组合想要的地图数据。