Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

客户故事 | 墨迹天气是如何使用 Mapbox GL,实现高分辨率雷达图像的? #2

Open
Jing-flyloveyin opened this issue Aug 8, 2019 · 0 comments

Comments

@Jing-flyloveyin
Copy link
Contributor

Customer Story | Moji Weather’s seamless animations with Mapbox GL

客户故事 | 四步走,墨迹天气雷达数据可视化指南

谈起墨迹天气,想必人人都不陌生,除了展现空气质量指数(AQI)等天气相关信息,人们最爱它的短时临近预报。该预报精准度可以达到分钟级别,给我们的出行带来了极大的便利。

短时临近预报以雷达图像的形式展现,殊不知,雷达数据是最难在原生 App 中快速呈现的数据之一。墨迹天气却克服了这个挑战,使用 Mapbox GL 实现了在 Mapbox 中国街景地图(Mapbox Streets Chinese)的快速、流畅、高分辨率的可视化效果,并上线 Android 和 iOS App 中。

1_rhLXqBIpyGmdR1hx8HFgWg

下面我们邀请到 Mapbox 中国解决方案工程师 Macro Shen,带大家详细剖析一下技术细节吧!

传统栅格图像可视化方案

来自雷达源的数据格式通常是栅格图像(Raster Image),在对这类数据进行可视化的时候,常见的方法是在地图上添加多个图像作为栅格源,或者生成图像图块并设置每帧显示不同的图像实现具有延时效果的数据。

许多天气 App 都使用类似的办法来显示雷达数据,包括墨迹天气的历史版本。但是这样的方案有下面两个主要缺点:

  1. 在缩放放大的时候,分辨率会显得很低
  2. 动画效果不够流畅

那么 Mapbox 为墨迹天气提供了怎样的解决方案呢?

矢量瓦片可视化方案

Mapbox 提供了矢量瓦片的解决方案,使数据密集型地图能够快速渲染。因为 Mapbox 的开源特性,这个过程具备了高度的设计灵活性。

那么矢量瓦片为什么会有优势呢?

根据 Mapbox 文档所述,这些矢量瓦片数据与用于 Web 端展示的图像是等效的,但是在缓存,缩放和快速呈现地图图像方面具有优势。矢量瓦片以紧凑的结构化格式、包含了几何与元数据,比如道路名称、地名、门牌号等,只有在客户端(比如 Web 浏览器或者移动 App)请求的时候,才会呈现矢量切片。渲染过程发生在客户端(Mapbox GL JS, Mapbox iOS SDK , Mapbox Android SDK)或者动态服务器(map API)上。

相对于完整渲染的栅格图像,矢量瓦片有下面的两大优势:

  1. 样式(Styling):矢量的特性,可以在被请求的时候再进行样式化。
  2. 大小(Size):矢量瓦片非常小,能够实现高分辨率地图、快速地图加载、高效缓存。

既然如此,我们可以通过矢量瓦片可视化雷达数据。

首先,我们需要将栅格图像转换为矢量格式(GeoJSON),然后生成矢量瓦片。

第一步:矢量化雷达数据

有很多的开源工具能够矢量化栅格图像。比如 d3-contour 可以从 Node.js 环境中的图像生成轮廓多边形,或者在 Python 环境中使用 numpy / rasterio 从图像生成像素多边形。

这是一个使用 d3-contour 的示例代码:

const contours = require('d3-contour').contours;
let w = 1100;
let h = 900;
let polygons = contours()
    .size([w, h])
    .thresholds(smoothValues)
    (pixel_data);

let resultgeojson = {
    type: 'FeatureCollection',
    features: []
};
polygons.forEach((polygon) => {
    //...
    resultgeojson.features.push({
        type: 'Feature',
        properties: {
            value: polygon.value,
            idx: 0
        },
        geometry: {
            type: 'Polygon',
            coordinates: polygon.coordinates
        }
    });
});

除了矢量化之外,我们还需要在生成 GeoJSON 数据之前将坐标系从像素转换为地理坐标系。

const proj4 = require('proj4');
let pixel_position = [10, 20];
let geo_position = proj4('EPSG:3857', 'EPSG:4326',[
    minLng + (maxLng - minLng) * (p[0] / w),
    maxLat - (maxLat - minLat) * (p[1] / h)
]);

第二步:对雷达数据进行插值,以平滑过渡

传统的雷达图像帧与帧之间的跳动非常明显,为了更加平滑的动画效果,我们需要提高数据帧的频率,因此,我们尝试创建中间雷达图像(intermediate radar images)以提高帧率,获得更流畅的动画效果。然后,我们运行处理脚本并生成更多向量,动画就可以以更多的步骤运行,看起来更流畅。

最简单的方法是在相邻帧之间插入数据,如下代码。

let current_data = [...];//An h * w array from current step of radar image
let next_data = [...];//An h * w array from next step of radar image

let new_data = [];
for (let i = 0; i < h; i++) {
    for (let j = 0; j < w; j++) {
        new_data[i * w + j] = parseInt((current_data[i * w + j] + next_data[i * w + j]) / 2);
    }
}
let polygons = contours()
    .size([w, h])
    .thresholds(smoothValues)
    (new_data);

请注意,我们在原始帧之间创建了不止一个过渡帧,以实现更流畅的动画,但代价就是数据大小会增加,这是需要权衡的。

第三步:从 GeoJSON 创建矢量瓦片

我们这里使用 tippecanoe 创建矢量瓦片,这个工具非常强大,能从大量的 GeoJSON、Geobuf 或 CSV 特征集合中创建矢量瓦片。

在 Mac OSX 系统上安装 tippecanoe 最简单的办法就是在 Terminal(终端)中输入下面的代码。

$ brew install tippecanoe

安装好 tippecanoe 之后,我们用下面的代码来生成 mbtiles(一种 OSM 中的数据格式,可以在一个单独的文件中存放地图切片)。

$ tippecanoe -o output.mbtiles -zg --drop-densest-as-needed radar.geojso

请注意,-e 可用于将 tile 写入指定的目录而不是 mbtiles 文件,想要将矢量瓦片发布到 Web 服务的开发者可以使用这个功能。

第四步:使用 Mapbox 将雷达矢量瓦片可视化

用于 Web 端的 Mapbox GL JS 和用于移动端的 Mapbox Maps SDK for iOS 和 Android 可以加载雷达矢量瓦片,并将其可视化。

下面是一个使用 Mapbox GL JS 可视化的源码,也可以参考一个使用 Maps SDK for Android 的案例

map.addSource('radar-data', {
    type: 'vector',
    url: 'mapbox://examples.dwtmhwpu'
});
map.addLayer({
    "id": "radarpolygon",
    "type": "fill",
    "source": "raddar-data",
    "source-layer": "0",
    "filter": ["==", "idx", 0],
    'layout': {
        "visibility": "visible"
    },
    'paint': {
        'fill-opacity': 0.7,
        'fill-color': [
            "step",
            ["get", "value"],
            "hsl(0, 0%, 100%)", 8,
            "hsl(202, 88%, 51%)", 18,
            "hsl(194, 88%, 51%)", 36,
            "hsl(185, 88%, 51%)", 54,
            "hsl(177, 96%, 53%)", 72,
            "hsl(157, 96%, 53%)", 90,
            "hsl(101, 94%, 65%)", 108,
            "hsl(60, 100%, 49%)", 126,
            "hsl(43, 100%, 49%)", 144,
            "hsl(26, 100%, 49%)", 162,
            "hsl(10, 100%, 49%)", 180,
            "hsl(0, 64%, 43%)", 198,
            "hsl(326, 47%, 29%)", 216,
            "hsl(274, 47%, 29%)", 234,
            "hsl(246, 56%, 35%)"
        ]
    }
}, 'place-village');

最后,我们使用 Mapbox GL 的 setFilter 功能实现雷达数据的平滑动画效果。

let theinterval = setInterval(function() {
    map.setFilter('radarpolygon', ['==', 'idx', currentidx]);
}, 500);

下面就是输出结果,具有更好的用户体验,相对于传统可视化,实现了更加平滑、分辨率更高的渲染效果。(下:栅格图像可视化,上:矢量瓦片可视化)

20190808163509
20190808163514

这就是矢量瓦片的魅力,不仅仅是雷达图,也会有很多类似的图像展示可以采用这样的方法,找一个时间试试看?如果你有更多商务方面的问题,欢迎通过 Mapbox 微信公众号(Mapbox_China)联系我们,关注回复【技术】即可。

原文:https://blog.mapbox.com/visualizing-radar-data-with-vector-tiles-117bc5ee9a5a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant