D3制作地球(一) ——TopoJSON

3,065 阅读3分钟

TopoJSON 概述

TopoJSON是一种对地理拓扑进行编码的方式,它的名字来源于拓扑的英文topology,它的创始人正是D3的作者Mike Bostock。TopoJSON是GIS信息编码标准GeoJSON的扩展。要了解TopoJSON,先来看看GeoJSON。

GeoJSON

GeoJSON是一种格式,专门对多种地理数据结构进行编码。GeoJSON可以支持点、线、多边形、多点、多线、多个多边形等几何类型。特征对象是具有一些附加属性的几何对象。特征集合通常保存在FeatureCollection对象中。 - 摘自:geojson网站

如何理解FeatureCollection呢?举例说明,在描述世界地图的GeoJSON文件中,定义所有国家的对象就是一个FeatureCollection,而每一个国家则作为Feature对象包含在FeatureCollection中。

// FeatureCollection的结构
{
    type: "FeatureCollection",
    features: 
    [
        {.....},
        {.....}
    ]
}

上图的json对象是定义所有国家(地区)的FeatureCollection,其中的features描述了177个国家(地区),它是一个关于Feature对象的数组,Feature对象的结构如下。

// Feature的结构
{
    type: "Feature",
    id: 840,
    properties: {},
    geometry: {
        type: "MultiPolygon",
        coordinates: [
            [
                [-155.54135541355413, 19.08417463450381],
                [-155.68895688956889, 18.91661133277394],
                ...
            ],
            ...
        ]
    }
}

下图截取的是描述美国版图的Feature对象。

TopoJSON 和 GeoJSON 的比较

通过对GeoJSON的分析,可以了解到GeoJSON对于地图上的各种几何类型,都是以分离的形式存储的。比如北京市和河北省的边界有一部分是相同的,也就是说描述北京市和河北省的几何图形的边有一部分是重合的,GeoJSON只是独立的描述北京市和河北省,并不去考虑有一部分边相同的问题。而TopoJSON在GeoJSON的基础上,进行了优化,相同的边只会保存一份,这样就大大减小了冗余,TopoJSON的文件体积可以比GeoJSON小80%。

而因为TopoJSON进行了如上所述的优化,它的文件格式变得非常复杂,不像我们在上面列出的GeoJSON的FeatureCollection和Feature那么容易看懂。

在实际应用中,由于TopoJSON比GeoJSON的文件体积小得多,所以如果是在浏览器端使用,那么应该首选TopoJSON,以节省带宽,提高响应速度;如果是在服务器端使用,那么应该首选GeoJSON,因为其格式简单,通俗易懂。

TopoJSON API

从服务器端获取到的地理拓扑对象,基本都是TopoJSON格式的,但D3在JavaScript中是使用GeoJSON来处理地理要素的。因此,D3提供了一些API,可以在TopoJSON和GeoJSON之间进行转化。

topojson.feature(topology, object)

这个API用来返回GeoJSON的FeatureCollection或Feature。topology(参数1)通常是一个TopoJSON对象,object(参数2)是你想要返回的对象。

async function getEarth() {
    // world是TopoJSON
    const world = await d3.json(
        "https://cdn.jsdelivr.net/npm/world-atlas@1/world/110m.json"
    ); 
    // countries 是GeoJSON的Feature类型的Array
    const countries = topojson.feature(world, world.objects.countries).features;
}

上面的代码中,通过d3.json()请求TopoJSON数据,通过topojson.feature()将TopoJSON转换成GeoJSON。其中,topojson.feature(world, world.objects.countries)返回的就是上文中提到的FeatureCollection。

// FeatureCollection的结构
{
    type: "FeatureCollection",
    features: 
    [
        {.....},
        {.....}
    ]
}

topojson.mesh(topology[, object[, filter]])

这个API用来返回相连的几何体的边。通过mesh(网)这个词,我们可以想象这个API返回的就是一张网,它能够帮我们处理网中的相邻两个几何体的共有边,让这些共有边只保留一份。这在为整个网的边上色(stroke)的时候非常有用。

async function getEarth() {
    // world是TopoJSON
    const world = await d3.json(
        "https://cdn.jsdelivr.net/npm/world-atlas@1/world/110m.json"
    ); 
    // border 是GeoJSON
    const borders = topojson.mesh(
        world,
        world.objects.countries,
        (a, b) => a !== b
      );
}

项目在GitHub上的repo:github.com/dongqiaogon…

【完】