23万条数据!Cesium高性能实现区域高亮凸起

2,075 阅读4分钟

大家好,我是日拱一卒的攻城师不浪,专注可视化、数字孪生、前端提效、nodejs、AI学习、GIS等学习沉淀,这是2024年输出的第25/100篇文章。
交流合作:brown_7778

前言

在智慧城市的产品设计过程中,产品经常会提一个要求,将不同的区按照其边界进行划分,每个区块通过使用不同的颜色进行高亮凸起的一个3D效果。

这种效果通常也适用于四色安全图四色交通图

但是有一个问题,这种效果通常涉及到大批量的json数据,如果你用Entity去加载渲染的话,在数据体量足够大的情况下,性能应该不是很理想。

所以,今天我们就来聊下Cesium中如何实现3D立体面,并提高其渲染性能。

GeoJSON数据

首先我们还是需要先拿到要渲染的区域的geojson数据,之前我一直没有提及这是什么数据格式,对于初次接触的小白来讲可能不是很了解,下边来简单介绍一下:

GeoJSON 是一种基于 JSON(JavaScript Object Notation)的地理数据格式,主要用于存储和传输地理空间数据。

GeoJSON 数据格式的特点

  1. 基于 JSON:GeoJSON 是一种轻量级的地理数据交换格式,完全基于 JSON,易于人阅读和编写,同时也易于机器解析和生成。
  2. 地理空间数据:GeoJSON 可以表示各种地理空间数据,如线多边形等。
  3. 属性存储:GeoJSON 允许将属性地理特征关联,这些属性可以存储在特征对象中,通常以键值对的形式存在。
  4. 坐标系统:GeoJSON 通常使用经度和纬度坐标来表示地理空间数据,坐标系为经纬度坐标系,即WGS84

GeoJSON 数据结构

  • Feature:表示单个地理特征,如一个点、一条线或一个多边形。
  • Feature Collection:包含多个 Feature 的集合。
  • Geometry:表示地理特征的几何形状,如点、线、多边形等。
  • Properties:存储与地理特征相关的属性信息。

示例

一个简单的 GeoJSON 文件可能看起来像这样:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [102.0, 0.5]
      },
      "properties": {
        "name": "Dinagat Islands",
        "population": 4567
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [102.0, 0.0],
          [103.0, 1.0],
          [104.0, 0.0],
          [105.0, 1.0]
        ]
      },
      "properties": {
        "name": "Line"
      }
    }
  ]
}

在这个示例中:

  • FeatureCollection 是一个包含多个 Feature 的集合。
  • 每个 Feature 包含一个 geometry 和一个 properties
  • geometry 可以是 Point(点)、LineString(线)或 Polygon(多边形)等。

好了,接下来我要渲染整个青岛市10个区/县的色块,可以先看下这惊人的数据量

大概有23万条数据!

我一开始尝试了一下使用Entity去渲染,RTX4070的独显,16G的运行内存,数据是放在本地的,大约耗时6s左右才渲染完成。

然而我换成了Primitive之后,耗时在2s左右,提高了60%多的速度!

几何体构建

先渲染面PolygonGeometry

const polygon = new Cesium.PolygonGeometry({
  // 包含孔的多边形层次结构,polygonArray是我们拿到的经纬度数据
  polygonHierarchy: new Cesium.PolygonHierarchy(
    Cesium.Cartesian3.fromDegreesArray(polygonArray)
  ),
  // 计算的顶点属性,选择为每个面实例添加颜色外观
  vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
  // 设置面的拉伸高度
  extrudedHeight: 100,
});

由于我们要渲染10个区域,所以要构建10个几何体实例,使用GeometryInstance

GeometryInstance:几何实例化允许一个 Geometry 对象位于多个不同位置并具有唯一颜色。

附着颜色

// 提前预设好要附着的颜色
const colorArrs = [
  "AQUAMARINE",
  "BEIGE",
  "CORNFLOWERBLUE",
  "DARKORANGE",
  "GOLD",
  "GREENYELLOW",
  "LIGHTPINK",
  "ORANGERED",
  "YELLOWGREEN",
  "TOMATO",
];
const instances = []
const geometry = Cesium.PolygonGeometry.createGeometry(polygon);
instances.push(
  new Cesium.GeometryInstance({
    id: `polygon-${i}`,
    geometry: geometry,
    // 几何体实例属性设置
    attributes: {
      color: Cesium.ColorGeometryInstanceAttribute.fromColor(
        Cesium.Color.fromAlpha(Cesium.Color[colorArrs[i]], 0.6)
      ),
      show: new Cesium.ShowGeometryInstanceAttribute(true),
    },
  })
);

Primitive参数配置

使用primitive渲染,并且将所有区的polygon进行合并渲染,提高渲染的效率。

const primitive = new Cesium.Primitive({
    releaseGeometryInstances: true,
    geometryInstances: instances,
    appearance: new Cesium.PerInstanceColorAppearance({
      translucent: true, // 当 true 时,几何体应该是半透明的,因此 PerInstanceColorAppearance#renderState 启用了 alpha 混合。
      closed: false, // 当 true 时,几何体应该是关闭的,因此 PerInstanceColorAppearance#renderState 启用了背面剔除。
    })
  });
  viewer.scene.primitives.add(primitive);

OK,这样一个区域轮廓凸起就渲染成功了。

四色图.gif

完整代码和源数据可从下方链接获取,如果开源对你有帮助,也希望点一个star,支持我开源更多代码。

【开源地址】:github.com/tingyuxuan2…

有需要进技术产品开发交流群(可视化&GIS)可以加我:brown_7778,也欢迎数字孪生可视化领域的交流合作。

最后,如果觉得文章对你有帮助,也希望可以一键三连👏👏👏,支持我持续开源和分享~