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 的类型定义是自己定义的。但这些对基础功能没影响,你随便换就行。

相关推荐
_oP_i18 小时前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl
新中地GIS开发老师1 天前
WebGIS和WebGL的基本概念介绍和差异对比
学习·arcgis·webgl
_oP_i2 天前
Unity 中使用 WebGL 构建并运行时使用的图片必须使用web服务器上的
前端·unity·webgl
undefined&&懒洋洋3 天前
Cesium使用flyToBoundingSphere实现倾斜相机视角观察物体
前端·javascript·cesium·webgis
flying robot5 天前
Three.js简化 WebGL 的使用
webgl
小彭努力中6 天前
114. 精灵模型标注场景(贴图)
前端·3d·webgl·贴图
小彭努力中6 天前
109. 工厂光源(环境贴图和环境光)
前端·深度学习·3d·webgl·贴图
小彭努力中6 天前
112. gui辅助调节光源阴影
前端·深度学习·3d·webgl
GIS瞧葩菜6 天前
GeoSever发布图层(保姆姬)
wms·geoserver·cesium
新中地GIS开发老师7 天前
地理信息科学专业想搞GIS开发:学前端还是后端?
前端·javascript·arcgis·前端框架·cesium