1

最近因为业务需求接触了mapbox这个插件,过程中踩了超多坑,现在就我接触到的一些资料与踩过的坑,总结一下,希望看到这篇文章的码友们能帮助到你们。

文档

官方文档:
英文版:https://docs.mapbox.com/mapbo...
中文版:https://www.mapbox.cn/mapbox-...

辅助文档:
数据交换格式: https://zh.wikipedia.org/wiki...
距离计算: https://turfjs.org/docs/

用法

具体的用法官方文档写的很清楚,请多读文档,这里只讨论页面引入js的写法。

<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.css' rel='stylesheet' />
<div id='map' style='width: 1000px; height: 600px;'></div>
<script>
    mapboxgl.accessToken = 注册登录后获取的token;
    var map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/streets-v11', 
        //这里的style是地图的样式,可以自定义,也可以使用mapbox已有的地图样式。
    });
</script>

资源本地化

上面代码运行之后,会发现所有的资源加载的是mapbox网站的资源,这样导致我们页面打开速度慢,偶尔mapbox网站崩了,我们页面也打不开,所以就需要资源加载本地化。

image.png
image.png

如上图,即mapbox://styles/mapbox/streets-v11的具体内容,先把我们的代码修改之后如下:

<script>
    mapboxgl.accessToken = 注册登录后获取的token;
    var map = new mapboxgl.Map({
        container: 'map',
        style: {
                version: 8,
                name: "myMap",
                sources: {},
                layers: [],
                center: [0, 0],
                zoom: 3,
                sprite: "http://localhost:3000/map/sprite", //此处sprite的地址必须是url的地址
                glyphs: "/map/font/{fontstack}/{range}.pbf", //fontstack 是字体的名字,也是文件名,range 是pbf的命名
                visibility: "public",
                created: "2020-04-01T00:00:00.000Z",
                modified: "2020-04-01T00:00:00.000Z",
                owner: "mapbox",
                id: "blankMap",
                draft: false,
        }
    });
</script>

/**
    *{fontstack}请求字形时,此标记将替换text-font为符号图层属性中指定的字体堆栈中以逗号分隔的字体列表。
    
    *{range}请求字形时,此标记将替换为256个Unicode代码点。
    *例如,要为Unicode Basic Latin和Basic Latin-1 Supplement块加载字形,范围将为0-255。
    *根据需要显示的文本,在运行时确定加载的实际范围。
*/

到此,资源本地化就完成了。但是我们现在渲染的地图是空白的,这是因为layers中什么都没,所以我们现在要结合天地图来渲染中文地图。

pbf
pbf全称Protocal Buffers,是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。简单来说就是结构简单、速度快,我们前面讲到的矢量切片也使用了这种格式。

我们常见的字体库例如微软雅黑的.ttf字体文件大约有15M,一次请求加载15M并且还要解析,对服务端和客户端的压力都大,所以Mapbox使用了.pbf的格式进行分批请求,下面是两种格式的对比。.pbf格式大大的减少了请求压力。

关于pbf的详细可以看看这篇文章:https://www.cnblogs.com/ATtui...


渲染地图的底图-天地图

除了天地图还可以使用百度地图,谷歌地图,高德等地图都可以,这里选择了天地图。

天地图官网:http://www.tianditu.gov.cn/
GeoGlobe JavaScript: http://jiangsu.tianditu.gov.c...

GeoGlobe JavaScript API依赖GeoGlobe Server服务管理提供的OGC标准服务和应用服务,主要实现地理信息资源的共享和应用;
GeoGlobe开发中心网站通过对GeoGlobe JS API的方法调用,进行二次开发示例的集中展现,主要实现Web浏览器端的空间信息应用的示例表现。

页面引入GeoGlobeJS.min.js

<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.css' rel='stylesheet' />
<script src="/js/GeoGlobeJS.min.js"></script>
var simple = {
    version: 8,
    name: "myMap",
    sources: {},
    layers: [],
    sprite: "http://localhost:3000/map/sprite", //此处sprite的地址必须是url的地址
    glyphs: "/map/font/{fontstack}/{range}.pbf", //fontstack 是字体的名字,也是文件名,range 是pbf的命名
    visibility: "public",
    created: "2020-04-01T00:00:00.000Z",
    modified: "2020-04-01T00:00:00.000Z",
    owner: "mapbox",
    id: "blankMap",
    draft: false,
};
var map = new GeoGlobe.Map({
    style: simple,
    container: 'map',
    zoom: 4,
    center: [0, 0],
    isAttributionControl: false
});
map.on("load", function(e){
    var layer_vtc = new GeoGlobe.TDTLayer("vec_w");
    var layer_cva = new GeoGlobe.TDTLayer("cva_w");
    map.addLayer(layer_vtc);
    map.addLayer(layer_cva);
});

构造地图对象实例,并添加到id为map的div容器中,map加载中会添加两个图层,至此,一个中文版的地图就渲染完成。
地图底图渲染完成后,就是具体的页面逻辑操作了,此时重点就看mapbox的api了。

基本用法

使用GeoJSON在地图上画线:
map.addSource('route', {
    'type': 'geojson',
    'data': {
        'type': 'Feature',
        'properties': {},
        'geometry': {
            'type': 'LineString',
            'coordinates': [
                [-122.48369693756104, 37.83381888486939],[-122.48348236083984, 37.83317489144141],
                [-122.48339653015138, 37.83270036637107],[-122.48356819152832, 37.832056363179625],
                [-122.48404026031496, 37.83114119107971],[-122.48404026031496, 37.83049717427869],
            ]
        }
    }
});
map.addLayer({
    'id': 'route',
    'type': 'line',
    'source': 'route',
    'layout': {
        'line-join': 'round',
        'line-cap': 'round'
    },
    'paint': {
        'line-color': '#888',
        'line-width': 8
    }
});

map.removeLayer('route');
map.removeSource('route');

这一块的难点有3处:

1. GeoJSON

GeoJSON是一种基于JSON的地理空间数据交换格式,它定义了几种类型JSON对象以及它们组合在一起的方法,以表示有关地理要素、属性和它们的空间范围的数据。
GeoJSON使用唯一地理坐标参考系统WGS1984和十进制度单位,一个GeoJSON对象可以是Geometry, Feature或者FeatureCollection.
其几何对象包括有点(表示地理位置)、线(表示街道、公路、边界)、多边形(表示国家、省、领土),以及由以上类型组合成的复合几何图形。

具体详细信息请参考:https://zh.wikipedia.org/wiki...

如果你在地图上绘制图形的时候,没有报错,但是图形不显示,那一定是GeoJSON的格式问题!

2. 图层的类型

图层的类型由"type"属性指定,并且必须是One of"fill","line","symbol","circle","heatmap","fill-extrusion","raster","hillshade","background".
每个type属性,layout和paint也不尽相同。
具体请参照:
英文: https://docs.mapbox.com/mapbo...
中文: https://www.mapbox.cn/mapbox-...

3. 表达式
表达式这一块对于js基础不扎实的同学来说可能会很吃力,不过多看看,多找些资料慢慢熟悉就好了。
mapbox的文档上用法写的也蛮清楚的,这里我就不多解释了。

弹窗组件popup:
var popup = new GeoGlobe.Popup({className: 'my-class'})
  .setLngLat(e.lngLat)
  .setHTML("<h1>Hello World!</h1>")
  .setMaxWidth("300px")
  .addTo(map);
  
  //详情请具体查看官网文档
  
创建标记组件Marker:
var marker = new GeoGlobe.Marker()
  .setLngLat([30.5, 50.5])
  .addTo(map);
  

//创建多个marker,并清除:
var markers=[];
data.forEach((item,index)=>{
    var el=document.createElement('div');
    el.className = 'marker_button';
    let marker_name = `marker_${index}`;
    marker_name = new GeoGlobe.Marker(el)
                .setLngLat([30.5, 50.5])
                .addTo(map);
    markers.push(marker_name);
})

//判断marker是否存在,如果存在,就删除
if (markers.length != 0) {
    markers.map(item => {
        item.remove();
    })
}

//更多详情请具体查看官网文档

到此,mapbox的基本用法就讲完了,但是有一个重点--raster图层没有讲到,关于这个我会单独放到一篇文章中说,下篇文章见吧。
文章中如果有讲错的地方,请小伙伴们多指教,我也是第一次接触mapbox,要学习的东西还很多,欢迎小伙伴们多多留言交流。

小知识点

  • 有些mapbox的版本在map.getLayer('xx'), map.removeLayer('xx')的时候会报错, 此时试试map.style.getLayer('xx'), map.style.removeLayer('xx').
  • 地图上展示某地的标记有三种方法:

    1. 在地图上画点,layer的type为 symbol
    2. Popup弹窗组件
    3. Marker标记
  • LngLatLngLatBounds表示地理世界中的点和面 坐标。Point表示屏幕坐标系中的点。
    两个坐标的转换可用map.project()来实现

liuxk
91 声望0 粉丝