cesium实现简单自定义标签

相信所有人在 cesium 开发的过程中都会用到地图标签,用来在地图上标记东西。

常见标签

大部分人都会选择使用 cesiumbillboardlabel ,把二者搭配结合的实体 entity 添加到场景中,类似这种效果。然后修改修改标签和文字的对齐方式以及偏移距离,基本就满足了日常的需求。

javascript 复制代码
    var enetity = viewer.entities.add({
        name: '标点',
        position: Cesium.Cartesian3.fromDegrees(113.122717,23.028762,10),
        label: { //文字标签
          text: "文字标签文字标签文字标签",
          font: '500 30px Helvetica',// 15pt monospace
          scale: 0.5,
          style: Cesium.LabelStyle.FILL,
          fillColor: Cesium.Color.WHITE,
          pixelOffset: new Cesium.Cartesian2(0, -75), //偏移量
          showBackground: true,
          backgroundColor: new Cesium.Color(0.5, 0.6, 1, 1.0)
        },
        billboard:{
            image: '/images/normal_point_128.png',
            horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            scale: 0.5,
        }
    })

自定义标签

可是总有些时候上述的方法无法满足你的需求或者产品经理的需求,就比如我接下来要讲的这个标签,功能并不复杂,但就是刚好上述办法不好实现。

使用场景

在地图上测绘测量等操作完成后显示计算的结果。

分析需求

  1. 内容长度行数自定义
  2. 标签下方有竖线
  3. 需要根据距离隐藏或显示
  4. 计算改点到地球背面时需要隐藏
  5. 标签文字没有点击事件

开始

综合分析以上需求,我决定使用HTML来实现,自由度高且容易。

首先,新建一个类就叫 AnalysisBillboard ,该类具有以下属性:

  • viewer: Viewer 类型,表示一个查看器对象。
  • position: Cartesian3 类型,表示一个笛卡尔坐标系中的三维位置。
  • content: string 类型,表示广告牌的内容。
  • id: string 类型,表示广告牌的唯一标识符。
  • element: HTMLDivElement | undefined 类型,表示广告牌的 HTML 元素。
  • maxRenderDis: number 类型,表示广告牌的最大渲染距离,默认值为 500000。
  • show: boolean 类型,表示广告牌是否显示,默认值为 true。

代码如下:

js 复制代码
class AnalysisBillboard {
  protected viewer: Viewer;
  protected position: Cartesian3;
  protected content: string;
  private id: string;
  private element: HTMLDivElement | undefined;
  private maxRenderDis: number = 500000;
  private show: boolean;

  constructor(viewer: Viewer, position: GeoPositon, content: string) {
    ...
  }
 }

然后得在构造函数中,传入了三个参数:viewer、position 和 content,分别用于初始化 viewer、position 和 content 属性。同时,还计算了 maxRenderDis 属性的值(获取当前镜头的高度然后乘5,当镜头高度超过当前高度的五倍后就隐藏这个标签。),并使用 nanoid() 函数生成了一个唯一的 id。最后调用了 initBillboard() 方法进行初始化操作。

在 initBillboard() 方法中首先创建了一个 div 元素,并将其 id 设置为类实例的 id 属性。然后将该元素的样式设置为绝对定位,并给它添加了 resultMarker 类名。接着将类实例的 content 属性作为 HTML 表格插入到该元素中,最后将该元素添加到 viewer 的容器中。

接下来,该方法为 viewer 场景的 postRender 事件添加了一个监听器,当渲染完成后会自动调用 updateBillboardLocation() 方法来实时更新广告牌的位置。

js 复制代码
constructor(viewer: Viewer, position: GeoPositon, content: string) {
    this.viewer = viewer;
    this.position = GraphTransform.transformWGS84ToCartesian(position);
    this.content = content;
    this.maxRenderDis =
      Math.round(viewer.camera.positionCartographic.height) * 5;
    this.id = nanoid(10);
    this.show = true;
    this.initBillboard();
  }
 
 private initBillboard() {
    this.element = document.createElement("div");
    this.element.id = this.id;
    this.element.style.position = "absolute";
    this.element.className = "resultMarker";
    let HTMLTable = this.content;
    this.element.innerHTML = HTMLTable;
    this.viewer.cesiumWidget.container.appendChild(this.element);

    //实时更新位置
    this.viewer.scene.postRender.addEventListener(
      this.updateBillboardLocation,
      this
    );
  }

updateBillboardLocation() 更新一个广告牌的位置。 updateBillboardLocation() 方法首先检查广告牌元素是否存在,如果存在则获取 canvas 的高度,并创建一个 Cartesian2 对象来存储窗口坐标系下的位置信息。然后使用 SceneTransforms.wgs84ToWindowCoordinates() 方法将广告牌的位置从 WGS84 坐标系转换为窗口坐标系,并计算出广告牌在窗口坐标系下的纵坐标,并将其设置为广告牌元素的底部位置。接着计算广告牌元素的宽度,并将广告牌元素的左边距设置为窗口坐标系下的横坐标减去广告牌元素宽度的一半,是的自定义的标签能在定位部分居中

js 复制代码
private updateBillboardLocation() {
    if (this.element) {
      const canvasHeight = this.viewer.scene.canvas.height;
      const windowPosition = new Cartesian2();
      SceneTransforms.wgs84ToWindowCoordinates(
        this.viewer.scene,
        this.position,
        windowPosition
      );
      this.element.style.bottom = canvasHeight - windowPosition.y + "px";
      const elWidth = this.element.offsetWidth;
      this.element.style.left = windowPosition.x - elWidth / 2 + "px";

      const camerPosition = this.viewer.camera.position;
      let height =
        this.viewer.scene.globe.ellipsoid.cartesianToCartographic(
          camerPosition
        ).height;
      height += this.viewer.scene.globe.ellipsoid.maximumRadius;
      if (this.show) {
        if (
          !(Cartesian3.distance(camerPosition, this.position) > height) &&
          this.viewer.camera.positionCartographic.height < this.maxRenderDis
        ) {
          this.element.style.display = "block";
        } else {
          this.element.style.display = "none";
        }
      } else {
        this.element.style.display = "none";
      }
    }
  }

接下来,获取 viewer 相机的位置,并计算相机高度加上地球椭球体的最大半径得到新的高度。如果 show 属性为真,则根据相机与广告牌位置的距离和相机高度与最大渲染距离的大小关系来决定是否显示广告牌元素。否则直接将广告牌元素隐藏。

至此一个自定义的标签基础功能基本完成了。

使用

使用就非常简单啦,直接 new 一个就行。

js 复制代码
this.billboard = new AnalysisBillboard(this.viewer, position, content);

最后说明一下,这里面有些用到了第三方的库,或是自己封装的别的类和方法,比如 nanoid 是第三方的、 GraphTransform是自己定义的工具类,还有 GeoPositon 的类型定义是自己定义的。但这些对基础功能没影响,你随便换就行。

相关推荐
runnerdancer1 小时前
【WebGL】为什么绘制出来的纹理是上下颠倒的
webgl
IT、木易10 小时前
大白话react第十七章React 与 WebGL 项目进阶优化及拓展
javascript·react.js·webgl
歡進2 天前
开源 | Warpvas 实现扭曲的画布
javascript·webgl·canvas
伶俜monster3 天前
探秘 Threejs GUI:开启丝滑调试之旅
前端·webgl·three.js
苹果园dog3 天前
Geo3D建筑材质切换+屋顶纹理
3d·webgl·材质
IT、木易3 天前
大白话react第十六章React 与 WebGL 结合的实战项目
前端·react.js·webgl
希艾席蒂恩4 天前
四款GIS工具箱软件解析:满足企业多样化空间数据需求
信息可视化·gis·webgl·数字孪生·可视化大屏
GISBox4 天前
高斯泼溅文件如何转换成3DTiles?GISBox帮你轻松实现在Cesium上的应用
gis·cesium
eqwaak04 天前
《基于WebGPU的下一代科学可视化——告别WebGL性能桎梏》
分布式·微服务·云原生·架构·webgl