化石原创文章,转载请注明来源并保留原文链接


从经纬度得到世界坐标

static Cesium.Cartesian3.fromDegrees(longitude, latitude, height, ellipsoid, result) → Cartesian3

在Cesium中,最直观的坐标是经纬度,所以跟一般的3D引擎不同,这个方法使用率非常高。它的用法是:第1、2参数分别对应经度、纬度作为输入,即可在方法返回值中得到一个笛卡尔坐标的值。这里第3、4、5参数不强制输入,所以很多情况下,我们都使用两个参数即可。有的时候要测量的点有离开地面的高度,那么就通过第三个参数输入。第4个参数是测量的球体的句柄。第5个跟返回值是一个东西。

这个方法的实际意义是:我们在现实中,用户在使用地图的过程中往往能很容易给出一个经纬度,但是我们渲染的球体实际上是在一个 使用笛卡尔坐标系的3D空间中。所以这个方法做了经纬度往笛卡尔坐标转化的桥梁。


得到转化本地坐标到世界坐标的矩阵

static Cesium.Transforms.northUpEastToFixedFrame(origin, ellipsoid, result) → Matrix4

在Cesium中,本地坐标系统用NorthUpEast(和类似的几个字眼)来表示,这个系统是以参数origin所在的地球切面上的两根互相垂直的轴为North和East,另外一根经过该点(origin),和North、East轴都垂直的轴为Up形成坐标空间。具体方向没看。这里要说的是,这个方法主要是输出一个旋转矩阵,让使用该矩阵做自身的model矩阵的entity能够正好贴合这个球面上的切面。现实的意义是,我们在地球模型上放置物体,比如一辆汽车或者一幢房子,肯定希望这个汽车亦或房子的地面正好和地面平行(高度暂时不管)。如果没有这个矩阵的帮助,我们就需要额外的手段在汽车或者房子模型放入3D地球上后,控制它(们)旋转到一定角度才能实现我们刚才说的“与地面平行”的效果。而这个方法就是帮我们干这个事情的,有了这个矩阵,代码里直接使用该矩阵作为entity的旋转矩阵就完成了一切。

注:第一个参数是一般游戏引擎不会有的一个概念:因为这个矩阵是跟球体上的具体的点相关的!


从相机和屏幕坐标得到射线

Camera.getPickRay(windowPosition, result) → Ray

这个在3D引擎中是使用太频繁的一个方法了,无论是游戏引擎还是我们这里3D地理引擎。只要输入一个屏幕坐标的位置,我们就能得到一根从屏幕坐标位置往屏幕内部伸展的射线,常常用来检测空间里的物体。

由射线得到检测到的球面上的点

Globe.pick(ray, scene, result) → Cartesian3|undefined

这个方法可以由第一参数输入的射线,得到该射线最后射到的球面上的点的位置(笛卡尔坐标值)


笛卡尔坐标转成经纬度

Globe.Ellipsoid.cartesianToCartographic(cartesian, result) → Cartographic

这个很直观了,就是笛卡尔坐标往经纬度转,不过出来的结果是3维的,第三个参数是高度。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


这篇文章集合Cesium中的术语,会逐步添加

ellipse: 椭圆, 2D 
ellipsoid: 椭球体, 3D
cartographic: 地图坐标,也就是经纬度的集合。比如cartographic(0, 0)
ECEF: Earth Centered, Earth Fixed。地球坐标系。
NEU:North East Up, 一种本地空间的定义法。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


这个算是很基本的功能了,所以直接上代码,如下:

    var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    handler.setInputAction(function(click) {
        var pickedObject = viewer.scene.pick(click.position);
        if (Cesium.defined(pickedObject)) {
            var entityId = pickedObject.id._id;
            console.log("Picked entityId = " + entityId);
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

第1行代码创建了一个handler,这个handler用来跟踪canvas上的事件。第2行代码开始,设置了刚刚创建的handler要跟踪的事件的类型(第二个参数,这里跟踪的是鼠标左键的点击)和相应的事件发生后用来处理事件的代码(第一个参数对应的方法)。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


这篇文章属于在使用Cesium的时候,边做边记录下来,所以会可能增加、修改、删除部分内容。

1、屏幕坐标

对于Cesium来说,原点在左上角,x轴正方向往右,y轴正方向往下。白话一点就是,左上角的屏幕坐标是(0,0),x坐标随屏幕越往右,值越大。y坐标随屏幕越往下,值越大。下面是一段代码可以用来验证这个结果:

var viewer = new Cesium.Viewer('cesiumContainer', {
    skyAtmosphere: false,
    shouldAnimate : true,
    terrainProvider: Cesium.createWorldTerrain(),
            baseLayerPicker: false,
            timeline: false,
            fullscreenButton: false,
            homeButton: false,
            navigationHelpButton: false,
            sceneModePicker: false,
            animation: false,
            geocoder:false
});


viewer.zoomTo(viewer.entities);
viewer.entities.add({
    id: 'mou',
    label: {
        // position : Cesium.Cartesian2.ZERO, 
        show: true,
    }
});
viewer.scene.canvas.addEventListener('mousemove', function(e) {
    var entity = viewer.entities.getById('mou');
    var ellipsoid = viewer.scene.globe.ellipsoid;
    // Mouse over the globe to see the cartographic position 
    console.log('(' + e.clientX + ', ' + e.clientY + ')');
});

2、Fixed Frame

在cesium中经常出现的字眼,这个对应到一些游戏引擎或者更接地气的说法,其实就是世界坐标系统。不过在地理信息引擎这里,因为这个坐标系统是固定在地球上的,所以用了Fixed这个字眼。在WSD84标准里,这个系统的原点在球心,x轴正方向指向经纬度(0,0),y轴正向指向(0,90),z轴方向根据右手判断。

3、EastNorthUp

Cesium中经常出现的字眼,有了上面的Fixed Frame铺垫,我这里直接就把这个系统叫做“本地坐标系统”。在Cesium中,实际上用户(开发者)很容易得到的信息是位置经纬度和离地面的高度信息。通过这些信息,Cesium提供了:

var position = Cesium.Cartesian3.fromDegrees(params.tx, params.ty, params.tz);
var m = Cesium.Transforms.eastNorthUpToFixedFrame(position); 

这样的方式,能直接构造出一个基于该位置的矩阵出来。作用就是能让模型的所有的顶点和该矩阵做乘法后,呈现在该位置的样子。East、North、Up是在该经纬度出由East、North、和地面往上(up)方向构造出的三个轴形成的坐标系统。在该经纬度我们指定物体离地面高度,比如离地1米,那么上面的参数3就是给1。这一点也可以看出是一个“本地坐标”。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


这个基础功能在实际中非常有用,比如要拖拽放置物体,就必须知道鼠标的移动和相应的鼠标对应到的球体位置,所以cesium中提供了相应的实现。下面是一段demo代码:

var viewer = new Cesium.Viewer('cesiumContainer', {
    skyAtmosphere: false,
    shouldAnimate : true,
    terrainProvider: Cesium.createWorldTerrain()
});


viewer.zoomTo(viewer.entities);
viewer.entities.add({
    id: 'mou',
    label: {
        // position : Cesium.Cartesian2.ZERO, 
        show: true,
    }
});
viewer.scene.canvas.addEventListener('mousemove', function(e) {
    var entity = viewer.entities.getById('mou');
    var ellipsoid = viewer.scene.globe.ellipsoid;
    // Mouse over the globe to see the cartographic position 
    var cartesian = viewer.camera.pickEllipsoid(new Cesium.Cartesian3(e.clientX, e.clientY), ellipsoid);
    if (cartesian) {
        var cartographic = ellipsoid.cartesianToCartographic(cartesian);
        var longitudeString = Cesium.Math.toDegrees(cartographic.longitude).toFixed(10);
        var latitudeString = Cesium.Math.toDegrees(cartographic.latitude).toFixed(10);
        var height = cartographic.height.toFixed(10);
        entity.position = cartesian;
        entity.label.show = false;
        entity.label.font_style = 84;
        //entity.position= Cesium.Cartesian2.ZERO; 
        entity.label.text = '(' + longitudeString + ', ' + latitudeString + ',' + height + ')';
        var result = entity.label.text; // we can reuse this
        document.getElementById("demo").innerHTML = entity.label.text;
    } else {
        entity.label.show = false;
    }
});

这段代码在场景中放置了一个entity,所以在屏幕中间会显示鼠标对应的球体的经纬度信息,同时也会把这个信息输出到html中,就在屏幕的左下角。这个是靠下面的html div先配置好:

<style>
    @import url(../templates/bucket.css);
</style>
<div id="demo" style='z-index: 2000; position: absolute; height:100px; width:200px; right: 10px; bottom: 0px; text-color: white'></div>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar"></div>

上面的代码里的id=demo的div就是我们输出html信息的地方。

完毕。


化石原创文章,转载请注明来源并保留原文链接