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等参数详解

Cesium案例解析(三)——Camera相机的更多相关文章

  1. Cesium案例解析(四)——3DModels模型加载

    目录 1. 概述 2. 代码 3. 解析 4. 参考 1. 概述 Cesium自带的3D Models示例,展示了如何加载glTF格式三维模型数据.glTF是为WebGL量身定制的数据格式,在网络环境 ...

  2. Cesium中级教程3 - Camera - 相机(摄像机)

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Camera CesiumJS中的Camera控制场景的视图.有 ...

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

    目录 1. 概述 2. 实例 2.1. ImageryLayers.html 2.2. ImageryLayers.js 2.2.1. 代码 2.2.2. 解析 3. 结果 1. 概述 Cesium支 ...

  4. Cesium案例解析(六)——3DTilesInspector监视器

    目录 1. 概述 2. 案例 1. 概述 3D Tiles作为传输和渲染大规模3D地理空间数据的格式,应对的都是大规模数据的场景,Cesium提供了一个监视3D Tiles数据的监视器,可以通过这个监 ...

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

    目录 1. 概述 2. 案例 3. 结果 1. 概述 3D Tiles是用于传输和渲染大规模3D地理空间数据的格式,例如摄影测量,3D建筑,BIM / CAD,实例化特征和点云等.与常规的模型文件格式 ...

  6. Cesium案例解析(一)——HelloWorld

    目录 1. 概述 2. 实例 2.1. HelloWorld.html 2.2. HelloWorld.js 3. 结果 1. 概述 感觉网上已经有不少关于cesium的教程了,但是学习一个框架最快的 ...

  7. 【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸

    类的继承案例解析,python相关知识延伸 作者:白宁超 2016年10月10日22:36:57 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给 ...

  8. Android Camera 相机程序编写

    Android Camera 相机程序编写 要自己写一个相机应用直接使用相机硬件,首先应用需要一个权限设置,在AndroidManifest.xml中加上使用设备相机的权限: <uses-per ...

  9. 案例解析|政府信息化的BI建设应用 .

    一.行业背景 某建设厅综合监管信息化平台,是政企业务协同的平台之一,同时兼具协作.门户.办公应用集成.用户权限管理等多项功能.在此要求基础上,选择中间件基础技术平台,可以在最大程度满足平台功能需求的前 ...

随机推荐

  1. jquery的版本 纵多 , 各个版本的插件的融合 ,

    有些插件在哪些版本下没有 插件之间因为版本冲突 是得不偿失的事情

  2. zbar+opencv检测图片中的二维码或条形码

    zbar本身自带检测二维码条形码功能,这里使用opencv只是做一些简单的读取图片,灰度图片以及显示条形码和二维码时用到一些绘制 // barcode-qrcodescanner.cpp: 定义控制台 ...

  3. 【论文笔记系列】AutoML:A Survey of State-of-the-art (上)

    之前已经发过一篇文章来介绍我写的AutoML综述,最近把文章内容做了更新,所以这篇稍微细致地介绍一下.由于篇幅有限,下面介绍的方法中涉及到的细节感兴趣的可以移步到论文中查看. 论文地址:https:/ ...

  4. Android触摸事件传递机制,这一篇就够了

    整个触摸事件牵涉到的是,Activity,View,ViewGroup三者的传递机制. 这个触摸事件就是从外层往内层一层层的传递. 整个传递机制,分为3个步骤:分发,拦截,和消费. 1. 触摸事件的类 ...

  5. Java类加载器和双亲委派机制

    前言 之前详细介绍了Java类的整个加载过程(类加载机制详解).虽然,篇幅较长,但是也不要被内容吓到了,其实每个阶段都可以用一句话来概括. 1)加载:查找并加载类的二进制字节流数据. 2)验证:保证被 ...

  6. Java学习笔记----打印基本数据类型范围

    /** * Created by N3verL4nd on 2016/11/10. */ public class HelloWorld { public static void main(Strin ...

  7. WTL使用双缓冲避免重绘闪烁

    1.继承自CDoubleBufferImpl template <class T> class CDoubleBufferImpl { public: // Overrideables v ...

  8. LeetCode227:基本计算器II

    感觉自己的思路还不错,比较简单清晰,代码量也比较少,没有用到记录运算符的变量或栈,就想把这个思路发一下博客. 题目: 实现一个基本的计算器来计算一个简单的字符串表达式的值. 字符串表达式仅包含非负整数 ...

  9. ubuntu 如何添加alias

    公司的nx 上面一般使用gvim 编辑文件.并且为gvim 增加了alias,只要敲 g 就是gvim 的意思,这样编辑一个文件只需要 g xxx.v 就可以了.非常方便. 在自己电脑上安装了ubun ...

  10. logstash split插件的使用(将一个事件拆分成多个事件)

    kafka中的原始数据格式(1条数据) { "body": { "cwd": "/home/test/", "monitor&qu ...