Cesium中文教程-空间数据可视化Visualizing Spatial Data(二)
目录
(5)相机控制(Camera Controls)
(6)管理实体(Managing Entities)
挑选Picking
(7)点、广告牌、标签(Points,Billboards,and Labels)
(8)3D模型(3D Models)
(9)The Property System
(10)下一步工作(Where to go from here)
(5)相机控制(Camera Controls)
前面第一个例子中,使用zoomTo控制展示到一个特定的实体。其实也可以通过按下InfoBox左上角的camera按钮或双击一个实体来完成。还有一种flyTo的方法,可以达到相同的视图效果,采用摄像机飞行的动画来完成,而不是立即跳转。除了能捕捉到单一的实体,这两种方法都可以被传递为一个EntityCollection(之后讨论哦),一个DataSource,常用来加载通用的数据格式,如GeoJSON,或者一个标准JavaScript的实体数组。
默认情况下,任何方法都将计算出一个视角,来确保整个实体是可见得。相机将被调整为朝北,45度角直视目标物。可以通过提供的heading,pith和range来定制视角。下面的代码转动相机从东面30度来直视Wyoming,由于没有定义range,使用了计算的默认值。
var heading = Cesium.Math.toRadians(90);
var pitch = Cesium.Math.toRadians(-30);
viewer.zoomTo(wyoming, new Cesium.HeadingPitchRange(heading, pitch));
zoomTo和flyTo都是异步函数,也就是说它们不能保证在返回之前已经完成。例如,飞到一个实体发生在许多动画帧。这些函数返回一个应答(Promises),这可以让用户预订在飞行或者缩放完成后执行函数。使用下面的语句替换例子中的zoomTo,这不仅会飞到wyoming,还会在飞行完后选中它。
viewer.flyTo(wyoming).then(function(result){ if (result) { viewer.selectedEntity = wyoming; }
});
如果飞行成功完成,那么传递给回调函数的result参数是true,如果飞行被取消,即用户初始化另一个飞行或者在完成之前缩放,或因为目标没有相应的可视化功能,即无法实现缩放,那么传递给回调函数的result参数是false。
有时,在应用于时间序列的动态数据时,希望相机集中在实体而不是地球。这种情况可以设置viewer.trackedEntity属性。追踪实体需要设置position,在第一个例子中尝试这种方式,可以给wyoming增加一个位置来追踪它:
wyoming.position = Cesium.Cartesian3.fromDegrees(-107.724, 42.68);
viewer.trackedEntity = wyoming;
通过不定义viewer.trackedEntity或者点击InfoBox消息框左上角的取消按钮可以停止追踪。调用zoomTo或者flyTo也将取消追踪,viewer.trackedEntity将成为没有定义。
对于许多用例,viewer提供的专对于实体的相机功能已经足够了,如果想进一步在应用程序中定制视图,可以查看相机教程(Camera tutorial)。
(6)管理实体(Managing Entities)
EntityCollection是一个关联矩阵,可以方便地管理和监控一组实体。以viewer.entities属性的形式已经使用过它,EntityCollection即有一些传统的方法,例如增加(add)、去除(remove)、去除所有(removeAll)等,也具有实体特殊性的函数,将在接下来的章节讨论。
许多应用程序依赖于存储在服务器上的数据,直到客户机需要它为止。有时我们需要更新之前创建的实体,所有实体的唯一id属性可以用到这种情况。由于在第一个例子中没有定义标识,而Cesium为我们创建了一个全局唯一标识(GUID),其生成一个唯一字符串例如182bdba4-2b3e-47ae-bf0b-83f6fde285fd。服务器上的数据很可能具有自己唯一的标识符,使用它来替代,这样可以在实体创建时指定它。
viewer.entities.add({ id : 'uniqueId'
});
之后,可以通过getById重新获取实体,如果提供id的实体不存在,那么返回是未定义的。
var entity = viewer.entities.getById('uniqueId');
另一种情况是,如果实体存在的话就去更新它,如果不存在,那么创建一个新的。getOrCreateEntity返回一个提供id的可用实体,必要的话会创建一个新实例并增添到收集器。
var entity = viewer.entities.getOrCreateEntity('uniqueId');
最后,我们可以手动创建一个新实体,并增添(add)到实体集中。这种情况中,add检查到向它传递了一个现有的实体,并简单返回相同的实例。
var entity = new Entity({ id : 'uniqueId'
});
viewer.entities.add(entity);
EntityCollection能力开始起作用的地方是在collectionChanged Event事件中,这可以被用来知道什么时候一个实体在收集器中被增加、去除、或者更新。当构建用户界面或应用程序其它部分需要监控可能在别的地方生成的数据时,这一点特别有用。
为了看到这样的效果,使用沙堡中的Geometry Showcase示例。复制粘贴下面的代码到头部附近,当viewer创建之后立马可以看到效果。
function onChanged(collection, added, removed, changed){ var msg = 'Added ids'; for(var i = 0; i < added.length; i++) { msg += '\n' + added[i].id; } console.log(msg);
}
viewer.entities.collectionChanged.addEventListener(onChanged);
当我们运行上面的示例时,在终端中可以看到65个信息,每个都调用viewer.entitier.add(这里只是在增加实体,所以removed和changed数组一直是空的)。Cesium也在内部订阅了此事件,需要时可以用于更新可视化。当同时更新大量实体时,更有效的方式是排队改变并在最后发送一个大事件。这样提高了性能,因为cesium可以一次性处理任何需要的变化,为此,可以在更新开始时,调用viewer.entities.supendEvents,然后是viewer.entities.resumeEvents。
在第一个viewer.entities.add之前添加一个挂起的调用,在示例的最后添加一个resume恢复调用。再一次运行示例,我们获得包含65个实体的一个唯一事件。这些调用是引用计数的,因此可以嵌套多个挂起和恢复调用,而不会出现任何问题。但是,在处理结束时忘记调用resume将导致场景中不会显示任何内容,因为在每个挂起调用都有一个对应的恢复匹配的调用之前,不会发送任何消息。
挑选Picking
挑选(Picking),即通常使用鼠标在给定的屏幕坐标上挑选一个对象。其是cesium中需要与原始API(PIPA Primitive API)进行简单接口的领域之一。这将在cesium的未来版本中得到补充,引入实体特性的挑选功能。对于目前,可以使用一些低级函数,scene.pick和scene.drillPick来检索实体。下面是实体挑选的基本实现,可以使用到任何应用中。
/** * Returns the top-most Entity at the provided window coordinates * or undefined if no Entity is at that location. * * @param {Cartesian2} windowPosition The window coordinates. * @returns {Entity} The picked Entity or undefined. */
function pickEntity(viewer, windowPosition) { var picked = viewer.scene.pick(windowPosition); if (defined(picked)) { var id = Cesium.defaultValue(picked.id, picked.primitive.id); if (id instanceof Cesium.Entity) { return id; } } return undefined;
}; /** * Returns the list of Entities at the provided window coordinates. * The Entities are sorted front to back by their visual order. * * @param {Cartesian2} windowPosition The window coordinates. * @returns {Entity[]} The picked Entities or undefined. */
function drillPickEntities(viewer, windowPosition) { var i; var entity; var picked; var pickedPrimitives = viewer.scene.drillPick(windowPosition); var length = pickedPrimitives.length; var result = []; var hash = {}; for (i = 0; i < length; i++) { picked = pickedPrimitives[i]; entity = Cesium.defaultValue(picked.id, picked.primitive.id); if (entity instanceof Cesium.Entity && !Cesium.defined(hash[entity.id])) { result.push(entity); hash[entity.id] = true; } } return result;
};
虽然各种场景选择方法返回的是带有primitive信息的对象,而不是实体实例,但是实体API(EAPI Entity API)是结构化的,这样与实体相对应的每个Primitive都有该实体的id属性。我们要做的就是检查选中对象的id是否是实体(Entity)的一个实例。虽然这些函数很简单,但它们并没有包含在cesium中,因为我们计划了更强大的功能。
(7)点、广告牌、标签(Points,Billboards,and Labels)
离开形状(shapes)和容器(volumes)章节之后,来了解下如何在cesium中可视化表示兴趣点。
创建一个图形点或者标签是很简单的,仅仅需要为实体、点(point)、标签(label)等可视化实体指定一个坐标(position)。例如,我们想在最喜欢的球队主场上放置一个点。
var viewer = new Cesium.Viewer('cesiumContainer'); var citizensBankPark = viewer.entities.add({ name : 'Citizens Bank Park', position : Cesium.Cartesian3.fromDegrees(-75.166493, 39.9060534), point : { pixelSize : 5, color : Cesium.Color.RED, outlineColor : Cesium.Color.WHITE, outlineWidth : 2 }, label : { text : 'Citizens Bank Park', font : '14pt monospace', style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth : 2, verticalOrigin : Cesium.VerticalOrigin.BOTTOM, pixelOffset : new Cesium.Cartesian2(0, -9) }
}); viewer.zoomTo(viewer.entities);
这些选项大多数是自己编制的,但其中一部分需要加以说明。例如,字体设置遵循CSS compact font syntax。
默认情况下,标签在水平和垂直位置居中。由于标签和点的位置相同,它们通常会在屏幕上重叠。为了将标签绘制在该点之上,我们可以指定它的原点应该是在底部,这意味着文本的底部是接触实体位置的部分,还可以在屏幕中指定偏移量。在上面的例子中,使用(0,-9),当结合VerticalOrigin.BOTTOM时,表示标签底部位于实体上方9个像素处。值为负值,是因为想减少屏幕坐标的Y值,Y值从顶部开始,随着屏幕向下移动而增加。设置原点和偏移值,无论用户从哪个角度或旋转角度查看,都可以确保标签一直在点的上面。
当然,使用一个点不是很令人激动,所有可以用一个billboard(广告牌)代替这个点,这是一个始终面向用户的标记。
var citizensBankPark = viewer.entities.add({ position : Cesium.Cartesian3.fromDegrees(-75.166493, 39.9060534), billboard : { image : '//cesiumjs.org/tutorials/Visualizing-Spatial-Data/images/Philadelphia_Phillies.png', width : 64, height : 64 }, label : { text : 'Citizens Bank Park', font : '14pt monospace', style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineWidth : 2, verticalOrigin : Cesium.VerticalOrigin.TOP, pixelOffset : new Cesium.Cartesian2(0, 32) }
});
在上面的例子中,我们明确设置了广告牌的宽度和高度,但这不是必需的。如果省略掉,则使用文件的原有宽度和高度。
标签的广告牌还有许多选项在这里没有提到,可以在沙堡中查看他们每一个起到的作用:Labels,Billboards。
(8)3D模型(3D Models)
Cesium支持通过glTF格式的3D模型,WebGL,OpenGL ES和OpneGL运行时支持的格式,还有一些随时可用的glTF模型:转动螺旋桨的飞机,转动车轮的陆地车,一个带有皮肤的周期行走角色。这些都可以在3D models沙堡例子中看到。
加载模型与我们目前使用的其他任何类型的可视化并没有太大的不同。所需要的就是一个实体位置和一个glTF模型的uri(统一资源标识符)。
var viewer = new Cesium.Viewer('cesiumContainer');
var entity = viewer.entities.add({ position : Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706), model : { uri : '../../../../Apps/SampleData/models/GroundVehicle/GroundVehicle.glb' }
});
viewer.trackedEntity = entity;
可以选择提供一个缩放属性,该属性从模型的原始大小均匀地缩放模型以及一个minimumPixelSize(最小像素大小)属性,该属性可以防止模型看起来小于提供的像素大小。
默认情况下,模型是竖直面朝东,可以通过定义实体方位(Entity.orientation)属性四元素(Quaternion)来控制模型的方位。这比仅仅有位置的示例稍微复杂些,让我们来控制模型的俯仰、滚动、偏航(heading、pitch、roll)。复制下面的代码到沙堡中,改变它们的值,观察是如何影响方位的。
var viewer = new Cesium.Viewer('cesiumContainer');
var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706);
var heading = Cesium.Math.toRadians(45.0);
var pitch = Cesium.Math.toRadians(15.0);
var roll = Cesium.Math.toRadians(0.0);
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(heading, pitch, roll)); var entity = viewer.entities.add({ position : position, orientation : orientation, model : { uri : '../../../../Apps/SampleData/models/GroundVehicle/GroundVehicle.glb' }
});
viewer.trackedEntity = entity;
由于在cesium中需要将模型转化为gltF格式使用,所以我们提供了一个基于网络的转化器(web-based converter),其上传一个COLLADA模型、返回一个glTF格式版本的模型。
虽然实体API(Entity API)目前不支持高级模型的用例,例如节点选择或直接动画控制,但是我们可以使用基本API(Primitive API)来完成,会有单独的章节3D Model Primitive(3D模型元素)教程去介绍这种情况。我们有明确的计划来增强实体API(Entity API)以包含这些特性。Primitive教程包含调试模型的小提示,这些模型不能在Cesium中正确渲染的时候,可以调试。如果创建自己的模型,那么一定要看gITF Tips for Artists。
(9)The Property System
到目前为止,我们仅仅是给实体和图片对象赋值,而从来没有读取过任何值。实际上,如果我们真的那么做结果可能会感到惊讶。以多边形轮廓属性为例,在第一个示例中我们赋值为true。直觉告诉我们,调用console.log返回wyoming.polygon.outline的类型会简单的打印出boolean。
console.log(typeof wyoming.polygon.outline);
上面代码实际的输出是object,这是因为outline不是一个简单的boolean类型,而是ConstantProperty常量属性实例。实际上,在整个教程中,一直使用一种称为隐式属性转换的简写形式,在本例中,隐式属性转换自动获取原始值并为我们创建相应的ConstantProperty实例。如果没有简写的话,我们将不得不编写一个更长的初始化示例版本(为了简单起见,省略了位置):
var wyoming = new Cesium.Entity();
wyoming.name = 'Wyoming'; var polygon = new Cesium.PolygonGraphics();
polygon.material = new Cesium.ColorMaterialProperty(Cesium.Color.RED.withAlpha(0.5));
polygon.outline = new Cesium.ConstantProperty(true);
polygon.outlineColor = new Cesium.ConstantProperty(Cesium.Color.BLACK);
wyoming.polygon = polygon; viewer.entities.add(wyoming);
但是这些属性为什么会存在呢?原因是简单的,整个Entity API不仅有表达常量值的能力,而且还有随着时间改变的值。
所有属性完成了Property(属性)接口,在cesium中有很多不同种类的属性。本教程的第二部分将关注属性系统,并使用其创建动画和其它时间序列的可视化。目前为止,我们必须知道的事是为了读取属性的值,我们需要调用getValue函数。所以为了从多边形中得到轮廓属性,我们需要调用下面的代码,其时间是当前场景时间。
console.log(wyoming.polygon.outline.getValue(viewer.clock.currentTime));
从技术上讲,如果我们确定要查询的属性是ConstantProperty,可以避免传递时间,但是通常指定它是一种很好的实践。
(10)下一步工作(Where to go from here)
目前所了解的cesium中处理空间数据仅仅是皮毛,但是这已经展现出巨大的能力。当你在等待本教程的第二部分时,可能对Cesium的Imagery Layers和Terrain and Water感兴趣。往常一样,可以先查看Cesium教程的列表来决定下一步喜欢的学习内容。
Cesium中文教程-空间数据可视化Visualizing Spatial Data(二)相关推荐
- Cesium中文教程-Cesium Workshop(一)
欢迎来到Cesium社区,非常高兴能加入我们.为了帮助您开发自己的web地图应用程序,本教程将从头到尾介绍如何开发一款简单但有广泛影响的Cesium应用程序.本教程将接触到许多CesiumJS API ...
- Cesium中文教程-3D模型(3D Models)
目录 3D模型(3D Models) (1)快速入门(Quick start) (2)动画(Animations) (3)各取所需(Picking) (4)转化COLLADA为glTF(Convert ...
- Cesium中文教程-影像图层(Imagery Layers)
目录 快速开始(Quick start) 随时可用的影像(Ready-to-use imagery) 影像提供商(imagery providers) 跨域资源共享(Cross-origin reso ...
- 全网首发 | 第一个opencv_contrib扩展模块中文教程限时领取
点击上方"小白学视觉",选择"星标"公众号 重磅干货,第一时间送达 OpenCV是学习计算机视觉的重要工具之一,然而多年以来,在深度学习的deBuff下,Ope ...
- cesium添加填充_Cesium中级教程1 - 空间数据可视化(一)
Cesium中文网:http://cesiumcn.org/| 国内快速访问:http://cesium.coinidea.com/ 本教程将教读者如何使用Cesium的实体(Entity)API绘制 ...
- Cesium开发基础篇 | 04空间数据可视化之Entity
前面介绍了Cesium如何加载影像数据.地形数据.以及矢量数据,但是作为一个完整的三维系统,仅仅包括这些数据还是远远不够的.当然,还需要一些其他数据,比如空间可视化数据.三维数据数据等,今天我们先从空 ...
- R语言ggplot2可视化:使用堆叠的条形图(Stacked Barplot)可视化每个数据行(row)的缺失值的情况(Visualizing missing data counts in rows)
R语言ggplot2可视化:使用堆叠的条形图(Stacked Barplot)可视化每个数据行(row)的缺失值的情况(Visualizing missing data counts in rows) ...
- 【Python】scipy.spatial.Delaunay中文教程
目前介绍Delaunay的中文教程不多,基本都是零零散散的代码实例,很少有系统的函数参数介绍.晚上用了50分钟,把英文教程翻译了一遍. 函数参数: class scipy.spatial.Delaun ...
- Cesium开发高级篇 | 01空间数据可视化之Primitive
在基础篇中我们讲过空间数据可视化之Entity实体类,今天我们介绍另外一个比较接近渲染引擎底层的类Primitive,虽然两者都可用于绘制同样的几何图形,但考虑到性能问题,我们更推荐您使用Primit ...
最新文章
- 测试 csdnmakerdown语法
- SUN PORTAL 7.2安装在windows 的问题解决办法!
- mysql 集合 思想_SQL 编程思想:一切皆关系
- java集合---迭代器iterator
- 机房系统(五)——【Excel表导出】
- linux结束所有任务命令行,Linux基础命令(15)定时任务
- 性能测试流程-各阶段的工作
- java后台处理excel_java后台利用Apache poi 生成excel文档提供前台下载示例
- 流程代码中js报错,在javaScript或者jQuery中字符串比较没有equals()方法
- POJ 2798 2进制转换为16进制
- php和apache2的配置
- C++ STL list容器中元素的存取和访问
- 数据库优化常用的途径(方法)
- 贝叶斯学派与频率学派有何不同?
- DevExpress Windows Form(1) DevExpress控件之主题
- 《动手学深度学习》入门环境安装
- moment时区转换
- Bridging the Gap between Training and Inference for Neural Machine Translation翻译
- 利用卷积处理图片的实例(图像模糊,锐化...)
- uml各类图--完整全面实例