Cesium(七)交互Interactivity -1实时展示当前鼠标经纬度

742 阅读6分钟

1. 前言

本篇文章将讲述一个使用cesium实现的非常基础的小功能——鼠标悬浮在cesium椭球体上的时候,显示对应的经纬度值。通过该功能的实现,了解与其相关的构造函数和属性。

2. 在cesium中添加label标签

2.1 回顾Entity

在添加标签前,需要知道实体Entity的概念。Cesium中的所有空间数据都使用Entity API来表示。Entity API以一种有效提供灵活的可视化的方式,以便对Cesium进行渲染。

在八青妹的上篇文章Cesium(六)加载不同的矢量数据kml中浅浅提到了Entity,代码如下:

  const dataSource = await viewer.dataSources.add(
    Cesium.KmlDataSource.load("/bikeRide.kml", options)
  );
  const rider = dataSource.entities.getById("tour");
  //在 Cesium 中将视图飞到一个名为 `tour` 的实体。
  viewer.flyTo(rider).then(function () {
   ……
  });

这里用到了实体中的id对象,该id是kml文件中的,可以在添加到cesium之后,在实体中拿到,然后来使用。

2.2 实体位置 Cesium.PositionProperty()

Cesium.PositionProperty 是用于定义实体(Entity)在场景中位置的属性。它可以表示一个静态位置、一个随时间变化的位置,或者其他位置属性。使用方法如下:

 viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222),  });

2.3 实体外观之 Cesium.LabelGraphics(options)

下面介绍的是实体中的label文字标签,构造函数new Cesium.LabelGraphics(options)用来实现包含实体位置的二维标签。options的常用可选项如下:

  • text:标签显示的文本内容。
  • font:标签文本的字体样式和大小,例如 '24px sans-serif'
  • fillColor:标签文本的填充颜色。
  • outlineColor:标签文本的轮廓颜色。
  • outlineWidth:标签文本的轮廓宽度。
  • style:标签文本的样式,可以是 Cesium.LabelStyle.FILLCesium.LabelStyle.OUTLINECesium.LabelStyle.FILL_AND_OUTLINE
  • showBackground:是否显示标签的背景。
  • backgroundColor:标签背景的颜色。
  • backgroundPadding:标签背景的填充大小。
  • horizontalOrigin:标签文本的水平对齐方式,可以是 Cesium.HorizontalOrigin.LEFTCesium.HorizontalOrigin.CENTERCesium.HorizontalOrigin.RIGHT
  • verticalOrigin:标签文本的垂直对齐方式,可以是 Cesium.VerticalOrigin.TOPCesium.VerticalOrigin.CENTERCesium.VerticalOrigin.BOTTOM
  • pixelOffset:标签文本的像素偏移量,用于调整标签位置。
  • pixelOffsetScaleByDistance:根据距离缩放像素偏移量。
  • scale:标签的缩放比例。
  • translucency:标签的透明度。
  • show:是否显示标签。

2.4 实体外观之 Cesium.BillboardGraphics(options)

除了文字标签,也常常需要添加图片logo-billboard,构造函数new Cesium.BillboardGraphics(options)用于处理图像或图标的视觉元素。options的常用可选项如下:

  • image:图像的路径,可以是 URL 或 Base64 编码的图像数据。
  • show:是否显示图像。
  • scale:缩放比例。
  • horizontalOrigin:水平对齐方式,可以是 Cesium.HorizontalOrigin.LEFTCesium.HorizontalOrigin.CENTERCesium.HorizontalOrigin.RIGHT
  • verticalOrigin:垂直对齐方式,可以是 Cesium.VerticalOrigin.TOPCesium.VerticalOrigin.CENTERCesium.VerticalOrigin.BOTTOM
  • pixelOffset:像素偏移量,用于调整图像位置。
  • color:图像的颜色。
  • rotation:旋转角度(弧度)。
  • alignedAxis:旋转轴。
  • heightReference:高度参考。
  • width:图像宽度。
  • height:图像高度。
  • scaleByDistance:根据距离缩放图像大小。
  • translucency:透明度。
  • sizeInMeters:指定图像的大小是否以米为单位。
  • distanceDisplayCondition:距离显示条件,控制图像显示的距离范围。
  • imageSubRegion:图像的子区域,用于显示图像的部分区域。

2.5 静态效果

接下来,将positonlabelbillboard相结合,在指定的坐标上展示文字和图标。

// 创建一个 Cesium 场景
var viewer = new Cesium.Viewer('cesiumContainer');

// 添加一个实体用于显示标签和图标
var entity = viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222),
    label: {
        text: 'Hello, Cesium!',
        font: '24px sans-serif',
        fillColor: Cesium.Color.WHITE,
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM
    },
    billboard: {
        image: '/vite.svg', // 图标的路径
        width: 32, // 图标宽度
        height: 32, // 图标高度
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        pixelOffset: new Cesium.Cartesian2(0, -50) // 图标偏移量
    }
});

// 定位到标签位置
viewer.zoomTo(entity);

效果如下:

image.png

除了positonlabelbillboard这个属性,还有rectangle(设置矩形)、polygon(设置多边形)、polyline(设置多线段)等多种选型可供使用。可查看cesium- Entity官网了解。

3.获取鼠标当前经纬度

在上面案例的基础上添加三个功能,一是鼠标悬浮mouse over如何在cesium上监听,二是如何获取当前鼠标位置的坐标,三是在label上显示该坐标。

3.1 添加鼠标移动事件监听器

使用 ScenescreenSpaceCameraController 来监听鼠标移动事件。

const scene = viewer.scene;
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);

3.2 获取鼠标位置的地理坐标

用户输入事件类型使用Cesium.ScreenSpaceEventType来定义。可选择的属性有(来源官网cesium/ScreenSpaceEventType):

  • LEFT_DOWN: 当左键按下时触发。
  • LEFT_UP: 当左键释放时触发。
  • LEFT_CLICK: 当左键点击时触发。
  • LEFT_DOUBLE_CLICK:当左键双击时触发。
  • RIGHT_DOWN:当右键按下时触发。
  • RIGHT_UP:当右键释放时触发。
  • RIGHT_CLICK: 当右键点击时触发。
  • MIDDLE_DOWN:当中键按下时触发。
  • MIDDLE_UP:当中键释放时触发。
  • MIDDLE_CLICK:当中键点击时触发。
  • MOUSE_MOVE: 当鼠标移动时触发。
  • WHEEL:当鼠标滚轮滚动时触发。
  • PINCH_START:当捏合手势开始时触发。
  • PINCH_END: 当捏合手势结束时触发。
  • PINCH_MOVE: 当捏合手势移动时触发。

结合上述两个概念,看下使用案例:

const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
// 鼠标移动监听
handler.setInputAction(function (event) {
    console.log('MOUSE_MOVE:',event);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

打印出来的event如下所示:

image.png 具备开始坐标和结束坐标。开始坐标指的移动前一步的坐标,结束坐标为当前坐标。

viewer.camera.pickEllipsoid(windowPosition, ellipsoid, result)是cesium相机中用户从鼠标位置获取对应地球表面(椭球体)上的三维坐标的方法。根据和]鼠标的位置和相机的视角,返回地球表面(椭球体)上的笛卡尔坐标(Cartesian3)。scene.globe.ellipsoid表示地球的椭球体对象。写法如下

const cartesian = viewer.camera.pickEllipsoid(
    event.endPosition,
    scene.globe.ellipsoid,
);

如果鼠标光标位于地球表面(椭球体)上,pickEllipsoid 会返回一个 Cartesian3 对象,包含该点的三维坐标(经度、纬度和高度),如果光标不在椭球体上,返回值将是 undefined。所以我们在显示经纬度的时候要判断下是否在椭球体上。获得Cartesian3后,需要将其转为经纬度。写法如下:

const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const longitude = Cesium.Math.toDegrees(cartographic.longitude).toFixed(2);
const latitude = Cesium.Math.toDegrees(cartographic.latitude).toFixed(2);
console.log("经度:" + longitude, "---纬度:" + latitude);

3.3 显示坐标信息

最后,显示的坐标与实体上的label标签进行绑定。

 entity.label.text =
          `经度: ${`   ${longitudeString}`.slice(-7)}\u00B0` +
          `\n纬度: ${`   ${latitudeString}`.slice(-7)}\u00B0`;

这里可以更改 entity.label上的属性信息,也可以更改entity.billboard上的属性信息,例如通过 entity.billboard.scaleentity.billboard.color等直接更改。

4. 综合

onMounted(async () => {
  const viewer = new Cesium.Viewer("cesiumContainer");

  var entity = viewer.entities.add({
    label: {
      font: "24px sans-serif",
      fillColor: Cesium.Color.WHITE,
      style: Cesium.LabelStyle.FILL_AND_OUTLINE,
      verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
    },
    billboard: {
      image: "/vite.svg", // 图标的路径
      width: 32, // 图标宽度
      height: 32, // 图标高度
      verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
      pixelOffset: new Cesium.Cartesian2(0, -50), // 图标偏移量
    },
  });
  const scene = viewer.scene;
  const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  // 鼠标移动监听
  handler.setInputAction(function (event) {
    const cartesian = viewer.camera.pickEllipsoid(
      event.endPosition,
      scene.globe.ellipsoid
    );
    if (cartesian) {//鼠标在地球上
    //获取笛卡尔坐标
      const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
      //经纬度转换
      const longitude = Cesium.Math.toDegrees(cartographic.longitude).toFixed(2);
      const latitude = Cesium.Math.toDegrees(cartographic.latitude).toFixed(2);
      entity.position = cartesian;
      entity.label.show = true;
      entity.label.text =
        `Lon: ${`   ${longitude}`.slice(-7)}\u00B0` +
        `\nLat: ${`   ${latitude}`.slice(-7)}\u00B0`;
    } else {//鼠标不在地球上
      console.log("未在地球上");
      entity.label.show = false;
    }
  }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);//鼠标移动事件监听
});

效果如下: image.png