Blog of CodeJELLOW’S ENGINEERING NOTES
← 返回文章

OpenLayers Workshop(五)Vector tiles and Mapbox styles

OpenLayers Workshop(五)Vector tiles and Mapbox styles The VectorTile layer VectorTile 图层Making things look bright 让地图看上去明亮一点 Intera…

在这个模块中我们将要学习OpenLayers所有与vector tiles相关的内容,并使用 ol-mapbox-style 工具来处理Mapbox样式的文件。

The VectorTile layer VectorTile 图层

我们现在已经知道如何去加载瓦片图片了,并且我们也见识过多中不同的方法去加载并渲染矢量的数据。但是如果我们想将瓦片快速地传输到浏览器,并动态设置样式呢?这就是vector tiles存在的意义了。OpenLayers的VectorTile图层支持矢量瓦片。

A world map rendered from vector data 用矢量数据渲染一个世界地图

我们使用和 Basics 这一节中同样的index.html文件:

TEXT
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>OpenLayers</title>
    <style>
      html, body, #map-container {
        margin: 0;
        height: 100%;
        width: 100%;
        font-family: sans-serif;
      }
    </style>
  </head>
  <body>
    <div id="map-container"></div>
  </body>
</html>

通常来说,我们将index.html文件保存在workshop文件夹的根目录中。

在这个应用中,我们在根目录中使用一个全新的mian.js文件,并添加下面的依赖:

TEXT
import 'ol/ol.css';
import {Map, View} from 'ol';
import MVT from 'ol/format/MVT';
import VectorTileLayer from 'ol/layer/VectorTile';
import VectorTileSource from 'ol/source/VectorTile';
import {fromLonLat} from 'ol/proj';

数据源将采用OpenStreetMap的街道地图数据。瓦片的提供者为了演示而提供给我们免费访问的权限,但是需要访问密钥。所以需要我们阅读https://cloud.maptiler.com/的条款,并从中获取我们自己的key。下面的代码展示了我们获取的key,并将其定义为一个常量以便后续使用。

图片暂不可用。
如何获取API Keys
TEXT
// See https://cloud.maptiler.com/ for terms and API key
const key = 'lirfd6Fegsjkvs0lshxe'; // use your own instead

和之前一样我们要创建一个地图,但是我们这次以圣地亚哥为中心并放大一点点。

TEXT
const map = new Map({
  target: 'map-container',
  view: new View({
    center: fromLonLat([-117.1625, 32.715]),
    zoom: 6
  })
});

我们要用到的图层类型是VectorTileLayer,数据源是VectorTileSource:

TEXT
const layer = new VectorTileLayer({
  source: new VectorTileSource({
    attributions: [
      '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a>',
      '<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>'
    ],
    format: new MVT(),
    url: `https://api.maptiler.com/tiles/v3/{z}/{x}/{y}.pbf?key=${key}`,
    maxZoom: 14
  })
});
map.addLayer(layer);

数据源只提供从0-14的缩放等级,所以我们需要将这一消息传送给source。矢量瓦片图层通常针对512像素的瓦片进行优化,这也是矢量瓦片数据源的瓦片网格的默认值。数据提供者要求我们必须在attributions中展示数据来源,所以我们要在source的配置中进行相应设置。

现在一个没有样式的矢量瓦片地图就做好了:

图片暂不可用。

Making things look bright 让地图看上去明亮一点

很显然我们这个地图需要一些样式。实际上用矢量瓦片做出来的世界地图只要结合样式了才能有用。

在web制图的历史中,曾多次尝试创建用于样式化地图的工具和格式。其中最流行的格式可能是SLD和CartoCSS。首先能想到的一个图形工具是Atlas Styler。但是他们都不简单易用。

Mapbox最终想出了Mapbox Studio,一个非常用户友好的样式编辑器,以及Mapbox Style格式。Mapbox Style格式对于我们来说简单易读写,并且被越来越多的应用支持。一个图形开源的编辑器 Maputnik 可以作为Mapbox Studio的独立替代品,用于创建和修改Mapbox样式文件。

Mapbox Style格式不仅仅用于样式化矢量数据。它还可以用它自己的数据源和图层以及初始的视图配置(例如中心点和缩放等级)来描述一整个地图。

 ol-mapbox-style 包为OpenLayers添加了对Mapbox Style格式的支持。了解该格式的工作原理后,让我们看看如何使用 ol-mapbox-style 为矢量切片数据提供专业的街道地图样式。

Using a Mapbox Style definition 使用Mapbox Style的定义

我们要使用的瓦片数据集同样来自于一个样式(见于https://cloud.maptiler.com/maps/bright/)。Workshop已经将这一个style文件拷贝了一份放在了data/文件夹下了。为了使用这个文件,我们要引用 ol-mapbox-style 工具类。这个包已经在workshop的依赖中包含并安装过了。如果没有被包含,可以使用以下指令安装:

TEXT
npm install ol-mapbox-style

我们将要从这个包中引入apply方法,所以我们可以轻易的得到一张包含样式文件中所有内容的地图,并将其渲染到map-container标签中。使用方式十分简单,我们整个的main.js文件可以被缩短到两行引用和一行代码:

TEXT
import 'ol/ol.css';
import {apply} from 'ol-mapbox-style';

const map = apply('map-container', './data/bright.json');

现在我们就得到了一张比较好看的地图了:

图片暂不可用。

Interact with VectorTile features 与矢量瓦片特征交互

矢量瓦片的一个好处是我们可以与特征进行交互,因为我们在客户端中有数据。需要注意的是在渲染的过程中矢量瓦片被优化了,这意味着特征只包含了那些过滤和渲染时需要的属性,并且几何图形也针对渲染的分辨率进行了优化,并在图块边界附近进行了裁剪。

在这个练习中,我们将要在鼠标悬浮在一个点的位置上方时,在其特征的周围画一个框。

Adding a vector layer for displaying bounding boxes 增加一个矢量图层用于展示框框

我们将要在单独的图层上绘制在特征上悬停时的编辑框。需要在main.js中增加以下的这些引用:

TEXT
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import {Style, Stroke} from 'ol/style';

接下来我们为创建一个图层和数据源,并分配给地图。

Interacting with the map 与地图交互

现在时候为地图增加pointermove 的监听器了,这个监听器可以获取点位置上的所有特征并将其边界框加入图层中,我们需要两个额外的引用:

TEXT
import {Feature} from 'ol';
import {fromExtent} from 'ol/geom/Polygon';

最后我们编写清除当前数据源的内容并将当前点位置的特征的边界框作为新的内容写入数据源:

TEXT
map.on('pointermove', function(event) {
  source.clear();
  map.forEachFeatureAtPixel(event.pixel, function(feature) {
    const geometry = feature.getGeometry();
    source.addFeature(new Feature(fromExtent(geometry.getExtent())));
  }, {
    hitTolerance: 2
  });
});

鼠标悬停时的效果如图:

图片暂不可用。
通过 RSS 订阅更新 →