专栏首页代码编写世界Cesium案例解析(三)——Camera相机

Cesium案例解析(三)——Camera相机

目录

1. 概述

Cesium的Camera案例,展示了其关于漫游器镜头的控制,能够调整视图的位置。这里改进了一下这个实例,使之能够展示一些自己关注的兴趣点的情况,并总结遇到的问题。

2. 实例

2.1. Camera.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <meta name="description" content="Fly to a specified location or view a geographic rectangle.">
    <meta name="cesium-sandcastle-labels" content="Beginner, Tutorials, Showcases">
    <title>Cesium Demo</title>
    <script type="text/javascript" src="../Build/Cesium/Cesium.js"></script>
    <style>
        @import url(../Build/Cesium/Widgets/widgets.css);

        html,
        body {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
            font-family: sans-serif;
            background: #000;
        }

        .fullSize {
            display: block;
            position: absolute;
            top: 0;
            left: 0;
            border: none;
            width: 100%;
            height: 100%;
        }

        #toolbar {
            margin: 5px;
            padding: 2px 5px;
            position: absolute;
        }
    </style>
</head>

<body>
    <div id="cesiumContainer" class="fullSize"></div>
    <div id="toolbar">
        <select id = "camera_select", class="cesium-button">
            <option value="undefined">
                相机选项
            </option>
            <option value="undefined">
                飞行至某一点——武汉大学
            </option>
            <option value="undefined">
                飞行至某区域——武汉市
            </option>
            <option value="undefined">
                设置相机点——华中科技大学
            </option>
            <option value="undefined">
                设置相机区域——上海市
            </option>
            <option value="undefined">
                从武大飞向华科
            </option>
        </select>
    </div>
    <script src="Camera.js"></script>
</body>

</html>

这段代码在数字地球展示组件的基础上新添加了一个视图控制的下拉列表框,选择相应的选项能够将当前的视图调整到对应的位置。

2.2. Camera.js

//Add your ion access token from cesium.com/ion/ 
Cesium.Ion.defaultAccessToken = '你在Cesium申请的key';

var tdtKey = "你在天地图申请的key";

'use strict';

//默认BING影像地图
var viewer = new Cesium.Viewer('cesiumContainer', {
    imageryProvider: Cesium.createWorldImagery({
        style: Cesium.IonWorldImageryStyle.AERIAL
    }),
    baseLayerPicker: false
});

//全球影像中文注记服务
var imageryLayers = viewer.scene.imageryLayers;
var tdtAnnoLayer = imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
    url: "http://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&tk=" + tdtKey,
    layer: "tdtAnnoLayer",
    style: "default",
    format: "image/jpeg",
    tileMatrixSetID: "GoogleMapsCompatible"
}));

var camera_select = document.getElementById("camera_select");
if (camera_select) {
    camera_select.onchange = function gradeChange() {
        switch (camera_select.selectedIndex) {
            case 1:
                viewer.camera.flyTo({
                    destination: Cesium.Cartesian3.fromDegrees(114.35231209, 30.53542614, 5000.0),
                    orientation: {
                        heading: Cesium.Math.toRadians(0.0),
                        pitch: Cesium.Math.toRadians(-90.0),
                        roll: Cesium.Math.toRadians(0.0)
                    }
                });
                break;
            case 2:
                viewer.camera.flyTo({
                    destination: Cesium.Rectangle.fromDegrees(113.683333, 29.966667, 115.083333, 31.366667)
                });
                break;
            case 3:
                viewer.camera.setView({
                    destination: Cesium.Cartesian3.fromDegrees(114.40782845, 30.51011682, 5000.0),
                    orientation: {
                        heading: Cesium.Math.toRadians(0.0),
                        pitch: Cesium.Math.toRadians(-90.0),
                        roll: Cesium.Math.toRadians(0.0)
                    }
                });
                break;
            case 4:
                viewer.camera.setView({
                    destination: Cesium.Rectangle.fromDegrees(120.86667, 30.66667, 122.2, 31.883333)
                });
                break;
            case 5: {
                var whdxOptions = {
                    destination: Cesium.Cartesian3.fromDegrees(114.35231209, 30.53542614, 5000.0),
                    duration: 5,
                    orientation: {
                        heading: Cesium.Math.toRadians(0.0),
                        pitch: Cesium.Math.toRadians(-90.0),
                        roll: Cesium.Math.toRadians(0.0)
                    }
                };
                var hzkjdxOptions = {
                    destination: Cesium.Cartesian3.fromDegrees(114.40782845, 30.51011682, 5000.0),
                    orientation: {
                        heading: Cesium.Math.toRadians(0.0),
                        pitch: Cesium.Math.toRadians(-90.0),
                        roll: Cesium.Math.toRadians(0.0)
                    },
                    duration: 5,
                   //flyOverLongitude: Cesium.Math.toRadians(60.0)
                };

                whdxOptions.complete = function () {
                    setTimeout(function () {
                        viewer.camera.flyTo(hzkjdxOptions);
                    }, 1000);
                };

                // if (adjustPitch) {
                //     tokyoOptions.pitchAdjustHeight = 1000;
                //     laOptions.pitchAdjustHeight = 1000;
                // }

                viewer.camera.flyTo(whdxOptions);
            }
            break;
        default:
            break;
        }
    }
}

这段代码首先添加了Cesium.Viewer默认的Bing影像地图和天地图的中文标注;然后根据id获取HTML页面的下拉列表框控件camera_select;最后根据选项调整相应的相机视图。这里展示了几种调整视图的方式。

2.2.1. 飞行至某一点

设置相机镜头逐渐从当前位置飞行到某一点是通过Cesium.Camera的flyTo()函数实现的,其具体的函数定义如下:

图1:Cesium.Camera的flyTo()函数定义

该函数传入了键值对配置对象,其中destination、orientation这两项,分别表示相机镜头的位置和姿态。本例中相应的代码如下:

viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(114.35231209, 30.53542614, 5000.0),
    orientation: {
        heading: Cesium.Math.toRadians(0.0),
        pitch: Cesium.Math.toRadians(-90.0),
        roll: Cesium.Math.toRadians(0.0)
    }
});

这段设置相机视图的代码意思就是,将相机的位置移动到经纬度位置(114.35231209, 30.53542614),离地面5000米的点;航向角(heading)设置为0度,俯仰角(pitch)设置为-90度,滚转角(roll)设置为0度。实际页面的显示效果为逐渐飞往某一点:

图2:飞行至武汉大学附近

此时数字地球会显示在武汉大学附近,视线看上去会垂直与地面,并且东西南北方向也基本上与常规地图一致。实际上当不设置姿态参数orientation只设置位置参数destination也能达到同样的效果,说明(0.0,-90.0,0.0)的三个姿态角是设置相机视图的默认值。在Cesium的设定中,heading、pitch、roll的定义如下:

这说明航向角(heading)是绕Z后负方向旋转的角度,俯仰角(pitch)是绕Y轴负方向旋转的角度,滚转角(roll)是绕X轴正方向旋转的角度。那么问题来了,这个定义里面的X、Y、Z轴的指的是什么呢?我这里认为这个函数蕴含了一种视图变换,使得基于相机的视空间坐标系成为一种类似于一种北东地站心坐标系(NED)坐标系,XYZ轴指的正是这个视空间坐标系的XYZ轴。在这个视空间坐标系中,Z轴垂直球面向下(Down),Y轴沿纬线指东(East),X轴沿经线向北(North),而位于视空间坐标系原点的相机的姿态为由南看向北。在这种情况下,只需要使相机绕Y轴正向旋转90度,也就是俯仰角(pitch)设为90,就可以得到视线垂直于地图,东西南北向正常的视图。

2.2.2. 飞行至某区域

flyTo()函数另外一个很有用的功能就是根据设定的范围显示视图,这在显示特定空间的视图时特别有用,例如加载的三维模型的范围,一个地区的范围等等。实现也很很简单,只需要给位置参数destination传入一个Cesium.Rectangle对象即可:

viewer.camera.flyTo({
    destination: Cesium.Rectangle.fromDegrees(113.683333, 29.966667, 115.083333, 31.366667)
});

将武汉市的经纬度范围传入,实际的显示结果如下:

图3:飞行至武汉市

2.2.3. 两地之间飞行

flyTo()函数还可以传入一个配置项complete,可以给其设定一个飞行结束后再运行的函数,通过这个配置项可以实现两地或多地飞行:

var whdxOptions = {
    destination: Cesium.Cartesian3.fromDegrees(114.35231209, 30.53542614, 5000.0),
    duration: 5,
    orientation: {
        heading: Cesium.Math.toRadians(0.0),
        pitch: Cesium.Math.toRadians(-90.0),
        roll: Cesium.Math.toRadians(0.0)
    }
};
var hzkjdxOptions = {
    destination: Cesium.Cartesian3.fromDegrees(114.40782845, 30.51011682, 5000.0),
    orientation: {
        heading: Cesium.Math.toRadians(0.0),
        pitch: Cesium.Math.toRadians(-90.0),
        roll: Cesium.Math.toRadians(0.0)
    },
    duration: 5,
    //flyOverLongitude: Cesium.Math.toRadians(60.0)
};

whdxOptions.complete = function () {
    setTimeout(function () {
        viewer.camera.flyTo(hzkjdxOptions);
    }, 1000);
};

// if (adjustPitch) {
//     tokyoOptions.pitchAdjustHeight = 1000;
//     laOptions.pitchAdjustHeight = 1000;
// }

viewer.camera.flyTo(whdxOptions);

这段代码分别定义了两个飞行配置项whdxOptions和hzkjdxOptions,并且给whdxOptions的complete项配置了一个函数,表示完成1S之后,自动进行hzkjdxOptions的飞行。运行结果如下图所示:

图4:武大飞行至华科

2.2.4. 设置视图到某一点

设置当前视图通过setView()函数实现的,它跟flyTo()最大的不同是没有持续时间,没有飞行过程,是立即生效的。其具体的配置选项也比较相似,都是需要设置位置以及姿态:

viewer.camera.setView({
    destination: Cesium.Cartesian3.fromDegrees(114.40782845, 30.51011682, 5000.0),
    orientation: {
        heading: Cesium.Math.toRadians(0.0),
        pitch: Cesium.Math.toRadians(-90.0),
        roll: Cesium.Math.toRadians(0.0)
    }
});

2.2.5. 设置视图到某区域

设置具体的显示范围,也是立即生效,这两个部分因为与flyTo()函数比较类似,就不再具体讲解了。

viewer.camera.setView({
    destination: Cesium.Rectangle.fromDegrees(120.86667, 30.66667, 122.2, 31.883333)
});

3. 其他

3.1. 事件及相应函数

Cesium.Camera还提供了当前视图发生变化的事件changed、视图发生移动的事件moveStart/moveEnd,它们都可以通过addEventListener()给其添加相应的响应函数。

3.2. setReferenceFrame

自带案例Camera中还提供了另外一种视图控制方式:

function setReferenceFrame() {
    Sandcastle.declare(setReferenceFrame);

    var center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);
    var transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);

    // View in east-north-up frame
    var camera = viewer.camera;
    camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z;
    camera.lookAtTransform(transform, new Cesium.Cartesian3(-120000.0, -120000.0, 120000.0));    

    // Show reference frame.  Not required.
    referenceFramePrimitive = scene.primitives.add(new Cesium.DebugModelMatrixPrimitive({
        modelMatrix: transform,
        length: 100000.0
    }));
}

这段代码的意思是选定一个经纬度的点,可以计算出以该点为中心的东北天(ENU)站心坐标系与地心坐标系的转换矩阵,将这个矩阵传入给Cesium.Camera的lookAtTransform函数,从而达到设置视图的目的。但是这样做会导致当前世界坐标系发生变化,当前漫游器的键鼠交互操作不再以地心坐标系原点为中心,而以站心坐标系的原点为中心,导致这个时候的键鼠交互操作难以操作。

3.3. viewInICRF

Cesium默认是基于ITRF,也就是国际地球地心参考框架。自带案例还提供了一种将其转换为ICRF参考框架的视图设置方式。关于ICRF我也不是很了解,查阅网上资料只知道是一种原点在太阳系的质心的天文参考框架,留待以后需要用到的时候再研究。

4. 参考

[1]. 北东地/东北天两种导航坐标系与姿态转换 [2]. Cesium中的相机—HeadingPitchRoll [3]. Cesium类HeadingPitchRoll及heading、pitch、roll等参数详解

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Cesium案例解析(二)——ImageryLayers影像图层

    Cesium支持加载影像图层,主要是各种地图服务。这里就通过Cesium实现添加影像地图和中文注记。

    charlee44
  • Cesium案例解析(五)——3DTilesPhotogrammetry摄影测量3DTiles数据

    3D Tiles是用于传输和渲染大规模3D地理空间数据的格式,例如摄影测量,3D建筑,BIM / CAD,实例化特征和点云等。与常规的模型文件格式相比,最大的特...

    charlee44
  • GDAL集成对KML文件的支持

    GDAL可以支持将KML作为矢量文件文件读取,但是需要在编译的时候添加第三方库的支持,否则默认的编译结果是还是会不识别这种格式。

    charlee44
  • Cesium 画贴地线

    使用Cesium加载线数据时,在关闭地形的情况下毫无问题,但在开启地形加载地形数据以后,由于地面将不再是平平整整,而是可能会坑坑洼洼,在这种情况下,因为线数据中...

    Melody132
  • Cesium案例解析(二)——ImageryLayers影像图层

    Cesium支持加载影像图层,主要是各种地图服务。这里就通过Cesium实现添加影像地图和中文注记。

    charlee44
  • Cesium基础使用介绍

    前言 最近折腾了一下三维地球,本文简单为大家介绍一款开源的三维地球软件——Cesium,以及如何快速上手Cesium。当然三维地球重要的肯定不是数据显示,这只是...

    魏守峰
  • Git知识总览(四) git分支管理之rebase 以及 cherry-pick相关操作

    上篇博客聊了《Git知识总览(三) 分支的创建、删除、切换、合并以及冲突解决》,本篇博客我们主要来看一下 rebase 变基相关的操作。rebase 操作和 m...

    lizelu
  • Android自定义View之仿QQ未读消息拖拽效果

    绘制以上两条贝塞尔曲线和直线需要五个点:P1,P2,P3,P4,M,其中P1,P2,P3,P4是圆的切点,现在只知道两个圆的中心圆点O1和O2,那么怎么根据这两...

    Rouse
  • 机器人都会做饭了,还要男朋友作甚?

    镁客网
  • 日本正在建设机器人城市

    据《机器人趋势》网站报道,日本的一家新创公司Cyberdyne正在建设机器人城市,旨在将机器人助手用于医疗、工业和农业。 这座名为Cybernic的机器人之城将...

    人工智能快报

扫码关注云+社区

领取腾讯云代金券