cesium(五) 加载矢量数据GeoJSON和TopoJSON

1,251 阅读6分钟

1. 前言

在cesium中,除了加载图层影像,还会加载矢量数据,它可以根据位置打上相应的标签,也能让地图按照数据划分为相应的片区。加载数据在地图可视化几乎必不可少。

2. 加载geojson

2.1 cesium官网案例

在指定的地点位置或特定的建筑物上增加一个标签,最好这个标签🏷还能有个合适的简单样式。在官网上找到这个案例cesium-GeoJSON

image.png 这个资源文件的下载地址——cesium-simplestyles.geojson,将这个文件下载下来,移动到项目的public目录下。

参照上述官网提供的demo,使用Cesium.GeoJsonDataSource.load()方式将资源文件引入,再通过viewer.dataSources.add()方法将引入的资源进行加载。最后让相机视角贴合到资源文件。

  const viewer = new Cesium.Viewer("cesiumContainer", {
    infoBox: false,
    sceneMode: Cesium.SceneMode.SCENE2D,
    timeline: false,
    animation: false,
  });
  
const dataSource = await Cesium.GeoJsonDataSource.load("/simplestyles.geojson");
await viewer.dataSources.add(dataSource);
viewer.zoomTo(dataSource);

能得到官网案例中一样的视觉效果。如果想点击后有弹窗显示描述,则需要将Viewer中的infoBox: false的设置去掉,或者设置为true。

image.png

2.2 认识simplestyle数据

要探索和进一步使用GeoJSON 数据,就来认识认识simplestyle。simplestyle 是在已有的 GeoJSON 数据标准中定义样式的一组约定俗成的 "特殊值"。 因此,根据定义,执行 simplestyle 的文件是有效的 GeoJSON 文件和有效的 JSON 文件。这个规范是在创建一个可在客户端之间共享的地理空间数据样式标准。

先看下github/simplestyle官方提供的案例:

{
    "type": "FeatureCollection",
    "features": [{ "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [0, 0]
        },
        "properties": {
            "marker-size": "medium",
            "marker-symbol": "bus",
            "marker-color": "#ace"
        }
    }, {
        "type": "Feature",
        "geometry": {
            "type": "LineString",
            "coordinates": [[0, 0], [10, 10]]
        },
        "properties": {
            "stroke": "#f0f0f0",
            "stroke-width": 2
        }
    }]
}

在项目中替换下该案例代码,看下效果: image.png

出现了一个巴士的图标和一条线段。有点意思了,接下来认识下simplestyle的数据吧。

从结构上 - "type": "FeatureCollection" 表示这是一个特征集合,包含多个地理特征 - "features":[ ] :包含一个数组,每个元素是一个地理特征(Feature

地理特征描述

  • "type": "Feature": 表示这是一个地理特征。

  • "geometry":

    • "type": "Point" : 表示该特征是一个点。
    • "coordinates": [0.0, 0.0] : 点的坐标,表示地球上的特定位置(经度 0.0,纬度 0.0,即赤道与本初子午线的交点)。
  • "properties"属性:

    • title : 默认为空,当鼠标点击或者悬浮到该特征的时候显示的标题。
    • description:默认为空,当鼠标点击或者悬浮到该特征的时候显示的描述。
    • marker-size:默认为medium,该标记的大小,可选值small \ medium \ large
    • marker-symbol: 置于图标中心的符号。可选值可以是Icon ID、数字0 ~ 9、小写字母a~z
    • marker-color : 默认的颜色为"#7e7e7e"。指定标记的颜色为绿色(十六进制颜色代码)。
    • stroke:默认值"#555555",用来设置线条的颜色。
    • stroke-opacity:默认值1.0,设置线条的透明度。
    • stroke-width:默认值为2,设置线条的宽度。
    • fill:默认值"#555555",多边形内部的填充色。
    • fill-opacity:默认值0.6,多边形内部显示的透明度。

那Cesium中可用的图标有哪些呢?

Cesium中默认使用 Mapbox 图标,在这里github/cesium/maki可以看到图标对应的png的图。可以将上述marker-symbol中的图标名称替换为该目录下任意一个png文件的名称。

2.3 simplestyle数据在cesium中的简易应用

这里我们要实现的案例是在纽约柯灵顿城堡的四周进行区域高亮,用图标来标记位置,点击的时候,展示介绍,效果如下所示。 image.png

在项目的public目录下新建文件kelingdun.geojson,内容如下:

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    -74.01679470204621,
                    40.703548452001804
                ]
            },
            "properties": {
                "marker-size": "medium",
                "marker-symbol": "museum",
                "marker-color": "#4600EA"
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [
                            -74.01694964732094,
                            40.703743130036905
                        ],
                        [
                            -74.01663557181057,
                            40.70370111272185
                        ],
                        [
                            -74.01647483904938,
                            40.703391584350825
                        ],
                        [
                            -74.01667991188262,
                            40.703233318157345
                        ],
                        [
                            -74.01694979671066,
                            40.703276459121426
                        ],
                        [
                            -74.01708509784164,
                            40.70337903042805
                        ],
                        [
                            -74.01709789659726,
                            40.70351902614572
                        ],
                        [
                            -74.01694964732094,
                            40.703743130036905
                        ]
                    ]
                ]
            },
            "properties": {
                "stroke": "#f0f0f0",
                "stroke-width": 2,
                "fill": "#088",
                "title": "柯林顿城堡",
                "description": "是一座位于美国纽约曼哈顿岛南端的巴特里公园的圆形砂岩要塞。其最为人所知的历史是该城堡曾是美国的第一座移民站(早于埃利斯岛设立),在1855到1890年间有八百万人通过这一移民站到达美国"
               
            }
        }
    ]
}

在app.vue中调整视角,并引入上面的资源文件。修改内容如下:

  //用来设置相机的观看方向
  let initialPosition = Cesium.Cartesian3.fromDegrees(
    -74.01800002800248,
    40.70204333714821,
    80
  );

  viewer.camera.setView({
    destination: initialPosition,
    orientation: {
      // 水平方向的角度,以弧度表示
      heading: Cesium.Math.toRadians(20.0),
      // 垂直方向的角度,以弧度表示
      pitch: Cesium.Math.toRadians(-30.0),
    },
  });
  const dataSource = await Cesium.GeoJsonDataSource.load("/kelingdun.geojson");
  await viewer.dataSources.add(dataSource);

3. 加载 TopoJSON数据

3.1 展示cesium官网默认效果

TopoJSON数据是GeoJSON的扩展,TopoJSON文件中的几何图形不是以离散方式表示的,而是通过arc弧的共享线段拼接而成。 在github/cesium/……/ne_10m_us_states.topojson上下载该文件,放置于项目的public目录下,我们像加载geojson数据那样,来加载该文件,看到的效果图如下:

image.png

哇哦,别看这topojson的代码行数只有七百多行,但是却能分割出这么多片区。我们仍然可以在这个基础上,增加更多的样式来显示片区。

3.2 修改普通样式

数据中没有增加片区的填充颜色,这里给所有的数据边框增加hotpink颜色,片区内部填充透明度为0.5的粉色。

const dataSource = await Cesium.GeoJsonDataSource.load("/ne_10m_us_states.topojson", {
    stroke: Cesium.Color.HOTPINK,
    fill: Cesium.Color.PINK.withAlpha(0.5)
  });

效果如下: image.png

3.3 立体片区的设置

接下来,我们想让片区变得立体,由于该topojson中,还存在Population人口数量,根据人口数量的多少,来显示片区的位置高低。

首先,定义entities,拿到所有的实体值。

 const entities = dataSource.entities.values;

遍历每个实体,给每个实体增加一个透明度为1.0的随机颜色。有些状态有多个实体,因此我们将颜色存储在 hash 中,以便我们对整个 state 使用相同的颜色。使用entity.polygon.material=""来覆盖原来的颜色。

 // 遍历所有实体,为每个实体设置不同的颜色
  for (let i = 0; i < entities.length; i++) {
    // 获取当前实体
    const entity = entities[i];

    // 为实体设置不同的颜色
    const name = entity.name;
    let color = colorHash[name];
    // 随机生成颜色
    if (!color) {
      color = Cesium.Color.fromRandom({
        alpha: 1.0,
      });
      colorHash[name] = color;
    }
    // 为实体设置颜色
    entity.polygon.material = color; 
 }

再增加一个高度,由于人口数量巨大,因此我们除以 50。将片区的边框去掉。完整的代码

 const entities = dataSource.entities.values;
  // 遍历所有实体,为每个实体设置不同的颜色
  for (let i = 0; i < entities.length; i++) {
    // 获取当前实体
    const entity = entities[i];

    // 为实体设置不同的颜色
    const name = entity.name;
    let color = colorHash[name];
    // 随机生成颜色
    if (!color) {
      color = Cesium.Color.fromRandom({
        alpha: 1.0,
      });
      colorHash[name] = color;
    }
    // 为实体设置颜色
    entity.polygon.material = color; 
    // 隐藏实体的轮廓
    entity.polygon.outline = false;
    // 为实体设置高度
    entity.polygon.extrudedHeight = entity.properties.Population / 50.0;
 }

对于每个实体,根据状态名称创建随机颜色。有些状态有多个实体,因此我们将颜色存储在 hash 中,以便我们对整个 state 使用相同的颜色。 根据该州的人口拉伸多边形。 每个实体存储从中创建它的 GeoJSON 功能的属性,由于人口数量巨大,因此我们除以 50。

水灵灵的效果如下: topojson.png

4. 总结

  • GeoJSON 是简单和通用的地理数据格式,适合表示简单的地理特征。
  • TopoJSON 则通过引入拓扑结构和共享边界来提高数据存储效率,适用于更复杂的地理数据表示和可视化需求。选择哪种格式取决于具体的应用场景和需求。