mapbox-gl离线部署踩坑记 | 🏆 技术专题第三期征文

8,565 阅读6分钟

mapbox-gl可以说是gis可视化中的利器之一了。在这里记一次我第一次使用mapbox做项目的经历。
下面说点废话:

Q: 为什么我使用了mapbox?
A: 其实是个很有意思的原因,项目的业务需要载入一个30M的geojson。我曾请出openlayers、leaflet这两位老将,但是奈何老将不给力,地图在拖动、缩放都会卡顿。这时候想到了利用了webGL的GIS库,就是我们的精神小伙,mapbox!

咳咳~言归正传啊!
首先,先感谢猿基地提供的mapbox本地离线部署、及mapbox/node-fontnik工具使用介绍
我作为一个菜鸡,站在巨人的肩膀上,做一个mapbox离线部署的整理。

mapbox离线部署需要做什么?

我们需要对以下四个资源做本地化:

  1. sprite(图标)
  2. glyphs(地图标注字体)
  3. sources(图层数据源)
  4. layer(图层定义)

开搞开搞!

下载安装

我个人比较习惯使用npm去安装,毕竟还是要webpack帮忙打包的。

npm install mapbox-gl --save

地图初始化

当我们需要去离线使用mapbox-gl时,我们首先是不需要写accesstoken的。mapbox的官方文档中要求我们必须写accesstoken,其目的是为了能够读取到mapbox官方CDN中的资源。(在代码中如果你看到mapbox://字样的链接,那就是官方资源无误了!)

import 'mapbox-gl/dist/mapbox-gl.css'
import Mapbox from 'mapbox-gl'
import style from './style.js'
// 离线部署的话,下面这句话就不用了!
// mapboxgl.accessToken = 'pk.巴拉巴拉巴拉';
const map = new Mapbox.Map({
  container: '',
  // 下面可以设置比如中心点、缩放之类的属性
  ...,
  style
})

如果我们吸入accesstoken,那么style中要求写入的是字符串,比如mapbox://styles/mapbox/streets-v11

如果我们不写入accesstoken,那么style中要求写入的是个对象,我们就需要在地图初始化中,对style属性做一定的配置。

那么接下来,我们将重点讨论这个style.js里的内容。

版本 version [Number]

别问,问就是8。

至于为什么,我也不知道,可能他只有8

// style.js
const style = {
    version: 8
}
export default style

名字 name [String]

名字的话,大家喜欢取什么就取什么吧。

// style.js
const style = {
    version: 8,
    name: 'myStyle'
}
export default style

图标 sprite [String]

sprite的值是一个字符串,其实我们需要的是一个url的形式。然而我们并不需要另外起其他的服务来支撑这个功能,我们只需要一个json和一个雪碧图就可以解决这个问题。

我个人目前没有对图标做本地化部署,因为目前项目需求还没有进展到这一步,但是根据多方的资料得到以下信息,由于没有测试,所以如有问题,还请指出。

sprite.json

这里为json举一个例子

你也可以点击这里来查看mapbox官方给出的一个json示例

{
    "house": {
        "width"20,
        “height”: 20,
        "x": 0,
        "y": 0,
        "pixelRatio": 1,
        "visible": true
    },
    "radio": {
        "width"20,
        “height”: 20,
        "x": 20,
        "y": 0,
        "pixelRatio": 1,
        "visible": true
    },
    .....
}

sprite.png

这里为图片举一个例子,这个图片是与上面的json配套的。

雪碧图

那么当你获得了以上两个文件之后,你就可以将这两个文件放到一个文件夹中作为静态资源存放到服务器上。

你可以通过ngnix代理等方式,使得这个图片服务显得优雅一点,比如目前这个文件夹叫sprite被放在这个位置/static/img然后将static这个文件夹被代理到了localhost:8787,那么你就可以在style.js中这么写

// style.js
const style = {
    version: 8,
    name: 'myStyle'
    sprite: 'localhost:8787/img/sprite'
}
export default style

字体 glyphs [String]

mapbox的字体也是依靠服务的形式获得,所以,你需要的是很多的pbf文件,这里Mapbox给出了一个工具node-fontnik

但是,这个工具只能在Linux下运行,基本上无法在Windows下运行,别问为什么,都是辛酸泪。

如果没有Linux的话,可以考虑看下这篇文章,你将在你的windows 10中获得一个Ubuntu

node-fontnik的介绍,可以看这篇文章

我在这边直接给出解决方案,并且稍微改良了猿基地给出的脚本

如果你没有字体文件,那么可以在字客网上找一找

git clone https://github.com/mapbox/node-fontnik.git
cd node-fontnik
vi start.js

这时候,创建一个start.js

// start.js
var fontnik = require('.');
var fs = require('fs');
var path = require('path');
const dirExists = require('./dirExists')
var fontPath = process.argv[2]
var convertPath = process.argv[3]

var convert = async function(fileName, outputDir) {
    await dirExists(outputDir)
    console.log('转换中...')
    var font = fs.readFileSync(path.resolve(fileName));
    output2pbf(font, 0, 255, outputDir);
}

function output2pbf(font, start, end, outputDir) {
    if (start > 65535) {
        console.log("完事儿!");
        return;
    }
    fontnik.range({font: font, start: start, end: end}, function(err, res) {
        var outputFilePath = path.resolve(outputDir + start + "-" + end + ".pbf");
        fs.writeFile(outputFilePath, res, function(err){
            if(err) {
                console.error(err);
            } else {
                output2pbf(font, end+1, end+1+255, outputDir);
            }
        });
    });
}

convert(fontPath, convertPath);

运行这个js

node start.js [字体路径] [输出路径]

之后你就可以获得一堆pbf文件,比如我这里获得是微软雅黑,那么我把pbf文件放在YAHEI的文件夹里同样放到文件服务器上,那么你可以在style.js里这么写

// style.js
const style = {
    version: 8,
    name: 'myStyle'
    sprite: 'localhost:8787/sprite',
    source: {
    	polygonSource: {
    		type: 'geojson',
    		data: '/geojson/zhejiang.json',
    		generateId: true,
            sprite: 'localhost:8787/img/sprite',
            glyphs: 'localhost:8787/YAHEI/{fontstack}/{range}.pbf',
		}
	}
}
export default style

图层数据源 sources [Object]

source是图层数据源,在mapbox中有vector、raster、raster-dem、geojson、image、video六种类型。

你可以定义多个source来应对各种情况

在这里定义的source与map.addSource定义的source是相同的

配置项可以看官方文档

这里我举个关于geojson的例子

如果你没有geojson文件,可以在这里找一找

// style.js
const style = {
    version: 8,
    name: 'myStyle'
    sprite: 'localhost:8787/sprite',
    sources: {
    	polygonSource: {
    		type: 'geojson',
    		data: '/geojson/zhejiang.json',
    		generateId: true
		}
	}
}
export default style

source中的键名就是该source的唯一id,作为geojson,你可以在data中直接给出一个对象的形式,也可以通过一个链接引用。

generateId可以为那些没有id的区块自动编上唯一Id,方便后面对于id的操作,例如setFeatureState

图层 Layers [Array]

图层是真实展示在前端的,其实就是对于source的应用

配置项可以看官方文档

比如我们现在需要将geojson作为paint类型填充,那么你可以在style.js里这么写

// style.js
const style = {
    version: 8,
    name: 'myStyle'
    sprite: 'localhost:8787/sprite',
    source: {
    	polygonSource: {
    		type: 'geojson',
    		data: '/geojson/zhejiang.json',
    		generateId: true
		}
	},
    layers: [
        {
            id: 'polygonLayer',
            type: 'fill',
            source: 'polygonSource',  // 这里要写source的id
            paint: '#092453'
        }
    ]
}
export default style

到此为止,你就做了一个最简单的mapbox-gl的离线部署了。

那么,你还在等什么,赶紧抄起键盘开搞吧!

如果有什么问题,还请您在评论指正哦!

🏆 技术专题第三期 | 数据可视化的那些事......