【CesiumJS 入门】系列是将CesiumJS官网教程文章翻译为中文,同时对文章进行了一定的补充,并且提供Vue3版本的代码GitHub - cesiumjs教程的vue3版本 。希望这个系列可以帮助你更好的学习cesium。
官网地址:Creating Entities
本教程学习使用 CesiumJS 中的实体(Entity) API 来绘制空间数据以及指定材质和轮廓,例如点、标记、标签、线条、模型、形状等。 Entity内容很多,可以选择感兴趣的部分阅读。
什么是Entity API
CesiumJS 拥有丰富的空间数据 API,可分为两类:面向图形开发人员的低级Primitive API 和用于数据驱动可视化的Entity API。
低级Primitive API暴露了完成绘制所需的最小抽象。它的结构为图形开发人员提供灵活的实现方式,而不是为了API一致性。加载模型与常见广告牌不同,并且两者都与创建多边形完全不同。每种类型的可视化都有其独特的特征。此外,每种方式都有不同的性能特征,并且需要不同的最佳实践。
Entity API暴露了一组设计一致的高级对象,这些对象将相关的可视化和信息聚合到统一的数据结果中,我们将其称为实体。它让我们专注于数据的呈现,而不是可视化的底层机制。它还提供了构建复杂的、时间动态的可视化的结构,使其能够自然地与静态数据结合使用。尽管Entity API实际上在底层使用了Primitive API,但这是一个我们几乎不必关心的实现细节。通过对我们提供的数据应用各种启发式方法,Entity API 能够提供灵活、高性能的可视化,同时提供一致、易于学习和易于使用的界面。
第一个实体
从经度和纬度列表中添加美国怀俄明州的多边形。
const wyoming = viewer.entities.add({
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray([
-109.080842, 45.002073, -105.91517, 45.002073, -104.058488, 44.996596,
-104.053011, 43.002989, -104.053011, 41.003906, -105.728954, 40.998429,
-107.919731, 41.003906, -109.04798, 40.998429, -111.047063, 40.998429,
-111.047063, 42.000709, -111.047063, 44.476286, -111.05254, 45.002073,
]),
height: 0,
material: Cesium.Color.RED.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.BLACK,
},
});
viewer.zoomTo(wyoming);
上述代码创建了Viewer,它生成了地球和其他小部件。我们通过viewer.entities.add创建了一个新的实体。传递给add的对象为实体提供了初始值。返回值是实际的实体实例。最后,viewer.zoomTo将相机调整到查看实体的位置。
有很多实体选项。我们指定半透明红色作为多边形填充和黑色轮廓。
形状和立方体
官网教程是 Shapes and volumes, 如何翻译volumes, 翻译为体积不太合适,所以在本教程中叫立方体。 以下是我们可以使用 Entity API 创建的受支持的形状和立方体完整列表:
| 类型 | 代码例子 | API文档 |
|---|---|---|
| Boxes entity.box | sandcastle.cesium.com/?src=Box.ht… | cesium.com/learn/cesiu… |
| Circles and ellipses entity.ellipse | sandcastle.cesium.com/?src=Circle… | cesium.com/learn/cesiu… |
| Corridor entity.corridor | sandcastle.cesium.com/?src=Corrid… | cesium.com/learn/cesiu… |
| Cylinder and cones entity.cyclinder | sandcastle.cesium.com/?src=Cylind… | cesium.com/learn/cesiu… |
| Cylinder and cones entity.cyclinder | sandcastle.cesium.com/?src=Cylind… | cesium.com/learn/cesiu… |
| Polygons entity.polygon | sandcastle.cesium.com/?src=Polygo… | cesium.com/learn/cesiu… |
| Polylines entity.polyline | sandcastle.cesium.com/?src=Polyli… | cesium.com/learn/cesiu… |
| Polyline volumes entity.polylineVolume | sandcastle.cesium.com/?src=Polyli… | cesium.com/learn/cesiu… |
| Rectangles entity.rectangle | sandcastle.cesium.com/?src=Rectan… | cesium.com/learn/cesiu… |
| Spheres and ellipsoids entity.ellipsoid | sandcastle.cesium.com/?src=Sphere… | cesium.com/learn/cesiu… |
| Walls entity.wall | sandcastle.cesium.com/?src=Wall.h… | cesium.com/learn/cesiu… |
材质和轮廓
所有二维三维物体都有一组共同的属性来控制它们的外观。 fill 属性指定几何图形是否被填充,outline 属性指定几何图形是否被勾勒出轮廓。
const entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-103.0, 40.0),
ellipse: {
semiMinorAxis: 250000.0,
semiMajorAxis: 400000.0,
material: Cesium.Color.BLUE.withAlpha(0.5),
},
});
viewer.zoomTo(viewer.entities);
ellipse = entity.ellipse; // For upcoming examples
当 fill 为 true 时,材质属性决定填充的外观。下面创建一个半透明的蓝色椭圆。
图像
我们还可以指定图像的 url 而不是颜色。
ellipse.material = "6c6515466de37629f9b67a959c6c8ed5.jpeg";
在上述两种情况下,ColorMaterialProperty 或 ImageMaterialProperty 会在赋值时自动为我们创建。对于更复杂的材质,我们需要自己创建一个 MaterialProperty 实例。目前,实体的形状和体积支持颜色、图像、棋盘格、条纹和网格材质。
棋盘
ellipse.material = new Cesium.CheckerboardMaterialProperty({
evenColor: Cesium.Color.WHITE,
oddColor: Cesium.Color.BLACK,
repeat: new Cesium.Cartesian2(4, 4),
});
条纹
ellipse.material = new Cesium.StripeMaterialProperty({
evenColor: Cesium.Color.WHITE,
oddColor: Cesium.Color.BLACK,
repeat: 32,
});
网格
ellipse.material = new Cesium.GridMaterialProperty({
color: Cesium.Color.YELLOW,
cellAlpha: 0.2,
lineCount: new Cesium.Cartesian2(8, 8),
lineThickness: new Cesium.Cartesian2(2.0, 2.0),
});
轮廓
ellipse.fill = false;
ellipse.outline = true;
ellipse.outlineColor = Cesium.Color.YELLOW;
ellipse.outlineWidth = 2.0;
折线
折线是一种特殊情况,因为它们没有填充或轮廓属性。除了颜色,他们还依赖于特殊的材料。由于这些特殊的材料,不同宽度和轮廓宽度的折线将适用于所有系统。
const entity = viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([-77, 35, -77.1, 35]),
width: 5,
material: Cesium.Color.RED,
},
});
viewer.zoomTo(viewer.entities);
polyline = entity.polyline; // For upcoming examples
折线轮廓
polyline.material = new Cesium.PolylineOutlineMaterialProperty({
color: Cesium.Color.ORANGE,
outlineWidth: 3,
outlineColor: Cesium.Color.BLACK,
});
折线发光
polyline.material = new Cesium.PolylineOutlineMaterialProperty({
color: Cesium.Color.ORANGE,
outlineWidth: 3,
outlineColor: Cesium.Color.BLACK,
});
高度和挤压
包括走廊、椭圆、多边形和矩形在内的曲面形状可以放置在高度上或拉伸到体积中。在这两种情况下,形状或体积仍将符合WGS84椭球体的曲率。
在相应的图形对象上设置高度属性(以米为单位)。下面的代码行将多边形提升到地球上方250000米处。
wyoming.polygon.height = 250000;
要将形状变为立方体中,请设置“挤出高度”属性。体积将在高度和拉伸高度之间创建。如果高度未定义,则体积从0开始。创建一个从200000米开始延伸到250000米的体积。
wyoming.polygon.height = 200000;
wyoming.polygon.extrudedHeight = 250000;
查看器中的实体功能
选择和描述
单击查看器中的实体将在实体的位置显示SelectionIndicator小部件,并打开InfoBox以提供更多信息。我们可以设置一个名称,它决定了InfoBox的标题。我们还可以提供HTML作为Entity.description属性。
wyoming.name = "Wyoming";
wyoming.description =
'\
<img\
width="50%"\
style="float:left; margin: 0 1em 1em 0;"\
src="u=1034156886,276373436&fm=253&fmt=auto&app=138&f=JPEG.webp"/>\
<p>\
Wyoming is a state in the mountain region of the Western \
United States.\
</p>\
<p>\
Wyoming is the 10th most extensive, but the least populous \
and the second least densely populated of the 50 United \
States. The western two thirds of the state is covered mostly \
with the mountain ranges and rangelands in the foothills of \
the eastern Rocky Mountains, while the eastern third of the \
state is high elevation prairie known as the High Plains. \
Cheyenne is the capital and the most populous city in Wyoming, \
with a population estimate of 63,624 in 2017.\
</p>\
<p>\
Source: \
<a style="color: WHITE"\
target="_blank"\
href="http://en.wikipedia.org/wiki/Wyoming">Wikpedia</a>\
</p>';
InfoBox中显示的所有HTML都是沙盒。这可以防止外部数据源将恶意代码注入Cesium应用程序。要在描述中运行JavaScript或浏览器插件,请通过viewer.infoBox.frame属性访问用于沙盒的iframe。有关控制iframe沙盒的更多信息,请参阅本文。
相机控制
使用viewer.zoomTo命令查看特定实体。还有一个viewer.flyTo方法,可以执行动画摄影机飞行到实体。这两种方法都可以传递给Entity、EntityCollection、DataSource或实体数组。
两种方法都会计算所有提供实体的视图。默认情况下,相机朝向北方,从45度角向下看。通过传入HeadingPitchRange进行自定义。
const heading = Cesium.Math.toRadians(90);
const pitch = Cesium.Math.toRadians(-30);
viewer.zoomTo(wyoming, new Cesium.HeadingPitchRange(heading, pitch));
zoomTo和flyTo都是异步函数;也就是说,他们不能保证在返回之前已经完成。例如,飞行到一个实体会发生在许多动画帧上。这两个函数都返回Promises,可用于安排在飞行或缩放完成后执行的函数。将示例中的zoomTo替换为以下代码段。这架飞机飞往怀俄明州,并在飞行完成后选择它。
const result = await viewer.flyTo(wyoming);
if (result) {
viewer.selectedEntity = wyoming;
}
如果飞行成功完成,传递给我们的回调函数的结果参数将为真,如果飞行取消,即用户在此飞行完成之前启动了另一个飞行或缩放,或者因为目标没有相应的可视化,即没有什么可缩放的。
有时,特别是在处理时间动态数据时,我们希望相机保持以实体为中心,而不是以地球为中心。这是通过设置viewer.trackedEntity来实现的。跟踪实体需要设置位置。
wyoming.position = Cesium.Cartesian3.fromDegrees(-107.724, 42.68);
viewer.trackedEntity = wyoming;
通过将viewer.trackedEntity设置为undefined或双击实体来停止跟踪。调用zoomTo或flyTo也会取消跟踪。
管理实体
EntityCollection是一个用于管理和监视一组实体的关联数组。viewer.entitys是一个EntityCollection。EntityCollection包括用于管理实体的add、remove和removeAll等方法。
有时我们需要更新之前创建的实体。所有实体实例都有一个唯一的id,可用于从集合中检索实体。我们可以指定一个ID,或者自动生成一个ID。
viewer.entities.add({
id: "uniqueId"
)
使用getById检索实体。如果不存在具有提供ID的实体,则返回undefined。
const entity = viewer.entities.getById("uniqueId");
要获取实体或创建新实体(如果不存在),请使用getOrCreateEntity。
const entity = viewer.entities.getOrCreateEntity("uniqueId");
手动创建一个新实体,并使用add将其添加到集合中。如果集合中已存在具有相同id的实体,则此方法会抛出。
const entity = new Entity({
id: "uniqueId",
});
viewer.entities.add(entity);
EntityCollection的强大功能通过collectionChanged事件展现出来。这会在集合中添加、删除或更新实体时通知侦听器。
使用Sandcastle中的几何体展示示例。在创建查看器的行后粘贴以下代码。
function onChanged(collection, added, removed, changed) {
let message = "Added ids";
for (var i = 0; i < added.length; i++) {
message += "\n" + added[i].id; }
console.log(message);
}
viewer.entities.collectionChanged.addEventListener(onChanged);
运行示例时,您应该在控制台中看到大约65条消息,每次调用viewer.entitys.add都会看到一条消息。
当一次更新大量实体时,将更改排队并在最后发送一个大事件会更有效。这样,Cesium可以在一次过程中处理所需的更改。在viewer.entity.add之前调用viewer.entity.suspendEvents,并在示例末尾调用viewer.entiies.resumeEvents。当我们再次运行演示时,我们现在得到一个包含所有65个实体的单一事件。这些调用是引用计数的,因此可以嵌套多个挂起和恢复调用。
拾取
拾取(单击以选择对象)是我们需要与Primitive API进行简短交互的区域之一。使用scene.cick和scene.drillPick检索实体。
原教程中只提供了两个方法,在原教程的基础上增加如何调用两个方法。
function drillPick () {
var canvas = document.getElementById('cesiumContainer');
canvas.addEventListener('click', function(e) {
let entityId = drillPickEntities(viewer, new Cesium.Cartesian2(e.clientX, e.clientY))
console.log('entityId', entityId)
})
}
function pick () {
var canvas = document.getElementById('cesiumContainer');
canvas.addEventListener('click', function(e) {
let entityId = pickEntity(viewer, new Cesium.Cartesian2(e.clientX, e.clientY))
console.log('entityId', entityId)
})
}
/**
* 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) {
let picked, entity, i;
const pickedPrimitives = viewer.scene.drillPick(windowPosition);
const length = pickedPrimitives.length;
const result = [];
const 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;
}
/**
* 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) {
const picked = viewer.scene.pick(windowPosition);
if (Cesium.defined(picked)) {
const id = Cesium.defaultValue(picked.id, picked.primitive.id);
if (id instanceof Cesium.Entity) {
return id;
}
}
return undefined;
}
点、广告牌和标签
通过设置位置、点和标签来创建图形点或标签。例如,在我们最喜欢的棒球队的主场放置一个点。
const 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);
默认情况下,标签在水平和垂直方向上居中。由于标签和点共享相同的位置,因此它们在屏幕上重叠。为避免此问题,请指定标签原点VerticalOrigin.BOTTOM,并将像素偏移设置为(0,-9)。
用广告牌替换该点,广告牌是一个始终面向用户的标记。
const citizensBankPark = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.166493, 39.9060534),
billboard: {
image: "/docs/images/tutorials/creating-entities/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),
},
有关更多自定义选项,请参阅Sandcastle标签和广告牌Sandcastle示例。
3D models
CesiumJS通过运行时资源格式glTF支持3D模型。您可以在3D模型Sandcastle示例中找到示例模型。
const model3dEntity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706),
model: {
uri: "GroundVehicle.glb",
},
});
viewer.trackedEntity = model3dEntity;
为glTF模型设置位置和URI以创建模型实体。
默认情况下,模型的方向是垂直的,面向东方。通过为Entity.orientation属性指定四元数Quaternion来控制模型的方向。这将控制模型的航向、俯仰和横滚。
const position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706);
const heading = Cesium.Math.toRadians(45.0);
const pitch = Cesium.Math.toRadians(15.0);
const roll = Cesium.Math.toRadians(0.0);
const orientation = Cesium.Transforms.headingPitchRollQuaternion(
position,
new Cesium.HeadingPitchRoll(heading, pitch, roll)
);
const entity = viewer.entities.add({
position: position,
orientation: orientation,
model: {
uri: "GroundVehicle.glb",
},
});
viewer.trackedEntity = entity;
如果您创建了自己的模型,请务必查看我们在glTF艺术家技巧上的帖子。
属性系统
我们为实体定义的所有值都存储为 Property 对象。例如,请参阅怀俄明州大纲的值:
console.log(typeof wyoming.polygon.outline);
outline是ConstantProperty的一个实例。本教程使用一种称为隐式属性转换的简写形式,它自动获取原始值并在后台创建相应的属性。如果没有这个简写,我们就不得不写一个更长的初始示例:
const wyoming = new Cesium.Entity();
wyoming.name = "Wyoming";
const 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);
之所以使用属性,是因为实体API不仅能够表达常数值,而且能够表达随时间变化的值。请参阅回调属性和插值沙堡示例,了解一些时间动态属性。
到这里本教程就结束啦
源码可从_GitHub - cesiumjs教程的vue3版本_下载
如果文章对你有帮助,希望可以收获你的点赞收藏加关注,我会持续带来更多有价值的内容!