【前端搞地图】如何设计一张让用户过目不忘的地图

3,079 阅读7分钟

这是我参与 8 月更文挑战的第 5 天,活动详情查看: 8月更文挑战

背景

最近公司准备开发一款大屏可视化应用,主要想用来展示公司客户的一些数据情况以及用户区域分布情况。大体就是左右两边放置一些数据图表,中间放置一张科技感很强的地图。数据图表开发我想对于一般前端来说肯定都是很简单的需求了,但是中间地图,需要做到自定义。从目前团队来说,大家都比较缺乏gis开发经验,接到需求的那一刻大家也是对地图开发做了一番调研。

可视化地图开发调研

集合众人之力,一开始预定了几个方案:

  • 百度地图:可定制样式,但是可自定义样式比较少,不能满足目前需求
  • 高德地图:可定制样式,和百度地图类似
  • Echarts: 貌似效果还不错,自定义能力也挺强的
  • Leaflet: 自定义能力强,但是只支持2D地图
  • Cesium: 自定义能力强,可以加载第三方图层等,效果也比较好,但是上手难度大,支持3D
  • Mapbox: 自定义能力强,样式效果比较好看,上手简单,有很强的自定义样式的工具,支持3D
  • ...

经过比较,最终选择 Mapbox

Mapbox

MapBox 提供了多个平台的SDK以及开放式的api,可以在 Android ios web React Native Qt Unity 使用以及多种的 Rest Api. 由于项目主要是 web 端的,运行在浏览器

直接查看 Map GL JS

在使用之前记得先注册账号获取 Token

Mapbox GL JS 基本原理

让我们一起来快速了解一下 Mapbox GL JS。本指南包括 Web 地图 API 中的主要功能和常见模式,主要说明 Mapbox GL JS 与其它地图库的不同之处。用过 Leaflet, Mapbox.js,或者 OpenLayers 的用户可以在该文件了解到 Mapbox GL JS 与这几种地图库的差异。

Mapbox GL JS 的核心差异

客户端渲染

Mapbox GL JS 的核心特点是客户端渲染。在 Mapbox GL JS 中创建 Web 应用时,通过 JavaScriptWebGL 把地图作为 vector tiles 进行渲染。 相比将服务器的一系列切片图片组合起来后再显示,通过 Mapbox GL JS 渲染可以快速改变样式,使得地图的呈现更加多样化。

Camera

这里的 Camera 指的是地图视角。LeafletMapbox.js 库中的地图视角由中心点(centerpoint)和缩放级别(zoom level)决定,缩放级别通常为 022 之间的整数。Mapbox GL JS 也提供调整地图视角的参数。

  • 中心点:按经度、纬度顺序排列.
  • 缩放级别:可以是缩放范围内的任何数值。即使带有小数点,如 1.5 或 6.2,系统也可以正确渲染。
  • 方位角(bearing):地图的旋转度。地图可以旋转,右边向上旋转的时候文本标注等详细内容也会正确显示。
  • 倾斜度(tilt):地图视角倾斜时的度数。

图层(layers)

传统的 JavaScript 地图库有两种完全不同的 图层

  • 基础图层(baselayers):构成地图基础的图像切片。通常包含大量数据——街道地图包括标注、建筑、图标等细节,在浏览器上渲染的话效果不会太好。可参考 Mapbox.js 中的 L.mapbox.tileLayerLeaflet 中的 L.TileLayer
  • 叠加图层(overlays):通常包括 GeoJSON 之类的矢量数据,参考 Mapbox.js 中的L.mapbox.featureLayerLeaflet 中的 L.geoJson。和基础图层相比,叠加图层包含的信息更少,但交互性更强:可在 JavaScript 中进行修改,并且点击的时候会触发弹窗。

Mapbox GL JS 中的图层是对矢量数据或者 raster data的风格化呈现。每个图层都将规定特定数据在浏览器中该如何绘制,渲染器(renderer)会通过这些图层把地图渲染到屏幕上。

如下图所示,随着缩放级别的改变,地图细节也会变化。地形、建筑、公共交通站台和位置信息都作为其相应的图层来渲染。

由于通过Mapbox GL JS,浏览器中的所有地图内容均以矢量数据的形式加载,Mapbox GL JS 对基础图层和叠加图层不作区分。因此标签、图标等地图要素节都可以通过 JavaScript 进行修改,与以前的地图库中的叠加图层类似。当然,这也意味着更改图层样式的函数和方法会更细化一点,接下来我们会详细说明。

添加地图

每一个 Mapbox GL JS 项目的基础为mapboxgl.Map类。

var map = new mapboxgl.Map({
  // container id
   container:'map',
  // style location
   style:'mapbox://styles/mapbox/streets-',
  // starting position
   center:[-74.50, 40],
  zoom:9
});

它与 LeafletL.Map类或 Mapbox.jsL.mapbox.map相似,但有几个核心的差异:

坐标

Mapbox GL JS 将坐标处理为数组形式(如[-74.50, 40]),按照 longitude, latitude 的顺序排列(而不是 Leaflet 和 Mapbox.jslatitude, longitude的顺序)。这一顺序与 GeoJSON 中的坐标、其他地理空间格式以及数学中的 X,Y 顺序一致。

地图样式(style)

地图通过 URL mapbox://styles/mapbox/streets-v 加载样式。该 URL 链接到一个远程文件,地图将其载入后,会用其确定tilesets以及如何为终端客户呈现样式。Mapbox GL JS 在某些地方只允许使用 URLs,不允许字符数据(literal data),包括数据源。

JavaScript 库中的其它远程数据源一样,这些数据源是异步的。因此相关代码会使用事件绑定来保证地图在正确的时候做出改变。例如:

map.on('load', function() {
  map.addLayer({
    id:'terrain-data',
    type:'line',
    source:{
      type:'vector',
      url:'mapbox://mapbox.mapbox-terrain-v2'
    },
    'source-layer':'contour'
  });
});

当且仅当地图数据源加载之后(包括地图样式),上述代码会使用map.on('load', function() { 来运行map.addLayer。立即运行map.addLayer会导致错误,因为addLayer将在样式上添加一个图层,而目标样式尚未加载成功。

另外还有两种不同的方法改变地图位置:jumpToeaseTo.jumpTo和老式的地图导航工具类似:能够即刻显示另一个位置。easeTo很棒:能够智能地显示缩放方位角倾斜度等数值,方便记录地图的详细移动情况。

给地图添加数据

可使用addLayer()方法将数据作为图层添加到地图中。addLayer将对象视作包括id(由用户创建)和type属性的参数。添加图层时,需要提供source,令渲染器能够使用数据去绘图,可以选择指定layoutpaint属性,告诉渲染器如何进行绘制。

添加图层数据源(source)

可以使用 Mapbox GL JSaddSource()方法添加数据源。该备选方法可以达到同样的地图性能,但是有些时候我们更推荐保持代码的可读性。

添加新图层时需要添加数据源。图层数据源包括type和url,意味着所有的图层类型(type)都可以作为远程数据源获取(像 Map 中的style一样)。总共有五种图层类型,每种都有自己的properties:

  • vector tiles
  • raster tiles
  • GeoJSON
  • image
  • video

矢量数据源同样需要包含一个source-layer,需使用来自矢量文件数据源的图层名称(一般是初始文件的名称)。示例:

map.on('load', function() {
  map.addLayer({
    id:'rpd_parks',
    type:'fill',
    source:{
      type:'vector',
      url:'mapbox://mapbox.3o7ubwm8'
    },
    'source-layer':'RPD_Parks'
  });
});

Layout 和 paint 属性设置

图层包含两个子属性来完成数据样式的添加:Paintlayout.用于明确如何渲染地图数据。Layout属性指渲染位置和可见性,应用于渲染过程的早期阶段。paint指更精细的渲染样式,如不透明度、颜色和翻译。不需要大量处理,在后期阶段进行渲染。

下列代码展示了如何往地图中添加图层,将公园部分填充为绿色。

此外,如果使用addSource()方法添加数据源的话需要包括 id,如 addLayer()中的source一样。

map.on('load', function() {
  map.addLayer({
    id:'rpd_parks',
    type:'fill',
    source:{
      type:'vector',
      url:'mapbox://mapbox.3o7ubwm8'
    },
    'source-layer':'RPD_Parks',
    layout:{
      visibility:'visible'
    },
    paint:{
      'fill-color':'rgba(61,153,80,0.55)'
    }
  });
});

成品:缩放显示 San Francisco,和将公园填充为绿色的图层。该图层以该城市公园土地数据的矢量数据源为基础而绘制。

现在你已经掌握了 Mapbox GL JS 基础知识了,动手制作一幅自己的地图吧!

持续关注微信公众号前端开发爱好者 和我一起来前端搞地图吧!!! 下期文章我们将带来一大波案列教程,记得持续关注哦