之前零零碎碎学过、用过cesium,但也没做记录,现在重新整理一下,方便学习回顾。
1.cesium简介
CesiumJS是一个开源JavaScript库,用于创建具有最佳性能、精度、视觉质量和易用性的世界级3D地球仪和地图。从航空航天到智能城市再到无人机,各行各业的开发人员都使用CesiumJS创建交互式Web应用程序来共享动态地理空间数据。
cesium官网链接
可以在官网下载cesium 的源码,也可以用npm下载依赖包
npm install cesium
2.vue3.2中使用cesium
2.1初始化地球
创建vue项目,用包管理工具下载好cesium,在node_modules中找到下面的4个资源,复制粘贴到public文件夹下,当然,Widgets文件夹里都是css资源,可以放在src里面,方便后续调用。
准备一个div,给定id值,用来承接cesium的展示
<div id="cesiumContainer" ref="cesiumContainer"></div>
然后引入cesium和相关css资源
import * as Cesium from "cesium";
import "./Widgets/widgets.css";
<template>
<div id="cesiumContainer" ref="cesiumContainer"></div>
</template>
<script setup>
import * as Cesium from "cesium";
import "./Widgets/widgets.css";
import { onMounted } from "vue";
// 设置cesium token
Cesium.Ion.defaultAccessToken =
"这个token需要自己去申请,百度即可找到方法";
window.CESIUM_BASE_URL = "/";
// 设置cesium默认视角
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
// 西边的经度
89.5,
// 南边维度
20.4,
// 东边经度
110.4,
// 北边维度
61.2
);
onMounted(() => {
var viewer = new Cesium.Viewer("cesiumContainer", {
infoBox: false,
});
// 隐藏logo
viewer.cesiumWidget.creditContainer.style.display = "none";
});
展示出地球
ceisum提供了一些默认的交互方式,例如:
·按住鼠标左键拖曳:拖动相机在三维地球平面平移。
·按住鼠标右键拖曳:缩放相机。
·使用鼠标滚轮(即鼠标中键)滑动:缩放相机。
·按住鼠标滚轮拖曳:根据当前地球的屏幕中点,旋转相机。
2.2基础配置(默认控件)
cesium有一些默认的控件,我们可以去设置控件的显示隐藏。
var viewer = new Cesium.Viewer("cesiumContainer", {
// 是否显示信息窗口
infoBox: false,
// 是否显示查询按钮
geocoder: false,
// 不显示home按钮
homeButton: false,
// 控制查看器的显示模式
sceneModePicker: false,
// 是否显示图层选择
baseLayerPicker: false,
// 是否显示帮助按钮
navigationHelpButton: false,
// 是否播放动画
animation: false,
// 是否显示时间轴
timeline: false,
// 是否显示全屏按钮
fullscreenButton: false,
});
3.天空盒子
cesium中有天空盒的概念,其实就是在一个立方体盒子的六个面上贴图,当然这些贴图是同一个场景下的,然后将【相机】,也就是观察视角,放在盒子的中心位置,这样无论朝哪个方向去看,都能收获三维立体的沉浸感。
如下图所示:
如何操作天空盒?主要是用到SkyBox类
实现方法:
var viewer = new Cesium.Viewer("cesiumContainer", {
skyBox: new Cesium.SkyBox({
sources: {
positiveX: "./texture/sky/px.jpg",
negativeX: "./texture/sky/nx.jpg",
positiveY: "./texture/sky/ny.jpg",
negativeY: "./texture/sky/py.jpg",
positiveZ: "./texture/sky/pz.jpg",
negativeZ: "./texture/sky/nz.jpg",
},
}),
});
具体的参数对应关系如下
序号 | 参数 | 含义 |
---|---|---|
1 | negativeX | left |
2 | positiveX | right |
3 | negativeY | front |
4 | positiveY | back |
5 | negativeZ | down |
6 | positiveZ | up |
4.地图加载
cesium提供了非常强大且全面的API用来加载数据、地图,这个可以专门写一篇文章详细说明,这里只罗列一些常见地图的加载。可以使用imageryProvider来加载地图。
4.1天地图
加载天地图影像地图
var viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: new Cesium.WebMapTileServiceImageryProvider({
url: "http://t0.tianditu.com/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=申请的K值",
layer: "tdtBasicLayer",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "GoogleMapsCompatible",//使用谷歌的瓦片切片方式
}),
});
还可以继续加载地图(或地图注记),实现地图的叠加展示,使用viewer.imageryLayers.addImageryProvider。
//地图叠加:下面这个图层是 放在上层的图层
var imageryLayers = viewer.imageryLayers;
var layer = imageryLayers.addImageryProvider(
new Cesium.UrlTemplateImageryProvider({
url: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
layer: "tdtVecBasicLayer",
style: "default",
format: "image/png",
tileMatrixSetID: "GoogleMapsCompatible",
})
);
后加入的地图默认会覆盖前面的地图。
4.2高德地图
var viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: new Cesium.UrlTemplateImageryProvider({
url: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
layer: "tdtVecBasicLayer",
style: "default",
format: "image/png",
tileMatrixSetID: "GoogleMapsCompatible",
}),
}),
});
4.3 OSM
这里用的是Cesium.OpenStreetMapImageryProvider
var viewer = new Cesium.Viewer("cesiumContainer", {
// OSM地图,
imageryProvider: new Cesium.OpenStreetMapImageryProvider({
url: "https://a.tile.openstreetmap.org/",
}),
});
当然,还有非常多地图需要加载,这会后续可以专门总结一篇。
5.地形的加载
5.1在线加载地形
这是用到了cesium自带的方法,在线加载地形。
var viewer = new Cesium.Viewer("cesiumContainer", {
// 设置地形
terrainProvider: Cesium.createWorldTerrain();
});
5.2本地加载地形
提前准备好地形数据,这里我用的是广州某区域的地形切片。
var viewer = new Cesium.Viewer("cesiumContainer", {
//加载自己准备的数据
terrainProvider: Cesium.createWorldTerrain({
url: "./terrains/gz",
}),
});
6.坐标系与转换
cesium中常见的坐标系有这些
1.WGS-84地理坐标系
2.屏幕坐标系(笛卡尔平面直角坐标)
3.笛卡尔空间直角坐标系
6.1.WGS-84地理坐标系
通过new Cesium.Cartographic(longitude,latitude,height)来创建,三个参数分别代表经度,纬度,高度
6.2.屏幕坐标系(笛卡尔平面直角坐标)
屏幕坐标系即二维笛卡尔坐标系,Cesium中使用Cartesian2来描述,屏幕左上角为原点(0,0),单位为像素值,屏幕水平方向为X轴,向右为正,垂直方向为Y轴,向下为正。通过new Cesium.Cartesian2 (x,y)创建对象,其中的(x,y)代表平面坐标系中的坐标。
6.3.笛卡尔空间直角坐标系(世界坐标系)
又叫做世界坐标系。 标系(Cartesian3)。笛卡儿空间直角坐标系的原点就是椭球体的中心点。由于我们在计算机上绘图时不方便参照经纬度直接进行绘制,所以通常会先将坐标系转换为笛卡儿空间直角坐标系,再进行绘制。如图所示,笛卡儿空间直角坐标系的3个分量x、y、z,可 以被看作以椭球体中心点为原点的空间直角坐标系中的某一个点的坐标。
用法:
在Cesium中,使用Cartesian3类,通过new Cesium.Cartesian3 (x,y,z)创建对象,其中的(x,y,z)代表笛卡儿空间直角坐标系中的坐标。
6.4 坐标转换
1.角度与弧度的转换
WGS84坐标的经纬度有弧度、角度两种形式,可以使用cesium提供的数学方法进行转换,弧度转角度,角度转弧度。
// 角度转为弧度
var radians = Cesium.Math.toRadians(90);
console.log(radians);
// 弧度转角度
var degrees = Cesium.Math.toDegrees(2 * Math.PI);
console.log(degrees);
当然,经纬度转弧度也可以用Cesium.Cartographic.fromDegrees
//WGS84经纬度坐标 转 弧度坐标
var radians = Cesium.Cartographic.fromDegrees(
longitude,
latitude,
height
);
2.屏幕坐标转世界坐标(Cartesian2转Cartesian3)
首先要用如下代码,来实现鼠标点击获取屏幕坐标
创建变量handler,并实例化一个ScreenSpaceEventHandler对象,然后使用该对象的setInputAction方法设置要在输入事件上执行的功能。
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
//屏幕坐标
console.log("屏幕坐标:", movement.position);}
,Cesium.ScreenSpaceEventType.LEFT_CLICK);
关于世界坐标,还有一些需要区分的地方,主要是三种情况,主要看这个点位是否考虑地形、模型、倾斜摄影等等。
(1)场景坐标,包括地形、倾斜摄影模型等的坐标
Cesium中的Camera
提供了pickEllipsoid
方法
//屏幕坐标 转 世界坐标(场景坐标,包括地形、倾斜摄影模型等的坐标)
var cartesian3 = viewer.scene.pickPosition(movement.position); //注意此处的屏幕坐标一定要在球上,否则结果为undefined
console.log("屏幕坐标转世界坐标(场景):", cartesian3);
(2)地表坐标,包括地形但不包括模型、倾斜摄影等
通过相机与屏幕点位连线来求取坐标。
var ray = viewer.camera.getPickRay(movement.position);
var cartesian3 = viewer.scene.globe.pick(ray, viewer.scene);
console.log("屏幕坐标转世界坐标(地表):", cartesian3);
(3)椭球面坐标,不包括地形、模型、倾斜摄影等
var cartesian3 = viewer.scene.camera.pickEllipsoid(
movement.position
);
console.log("屏幕坐标转世界坐标(椭球面):", cartesian3);
3.世界坐标转屏幕坐标
可以通过Cesium.SceneTransforms.wgs84ToWindowCoordinates方法将世界坐标系转换为平面坐标系。该方法需要传入两个参数,分别为scene和position,其中:scene为当前场景,即viewer. scene; position为世界坐标系中的坐标。
//世界坐标 转 屏幕坐标
var cartesian2 =
Cesium.SceneTransforms.wgs84ToWindowCoordinates(
viewer.scene,
cartesian3
);
console.log("世界坐标转屏幕坐标:", cartesian2);
4.世界坐标转WGS84
//世界坐标 转 WGS84坐标,结果为弧度形式
var cartographic =
Cesium.Cartographic.fromCartesian(cartesian3);
console.log("世界坐标转WGS84弧度坐标:", cartographic);
5.WGS坐标转世界坐标
//WGS84经纬度坐标 转 世界坐标
var position = Cesium.Cartesian3.fromDegrees(
longitude,
latitude,
height
);
console.log("WGS84经纬度转世界坐标", position);
//WGS84弧度坐标 转 世界坐标
var position2 = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
cartographic.height
);
console.log("WGS84弧度转世界坐标", position2
7.相机
cesium通过相机来控制视域,当我们进行交互,实现地图的放大缩小、视角的转换 等等操作时,实际上是相机在转换、移动。
ceisum提供了一些默认的交互方式,例如:
·按住鼠标左键拖曳:拖动相机在三维地球平面平移。
·按住鼠标右键拖曳:缩放相机。
·使用鼠标滚轮(即鼠标中键)滑动:缩放相机。
·按住鼠标滚轮拖曳:根据当前地球的屏幕中点,旋转相机。
7.1相机参数
如何设置相机?主要是借助于viewer.camera.setView
var position = Cesium.Cartesian3.fromDegrees(116.393428, 39.90923, 100);
viewer.camera.setView({
// 指定相机位置
destination: position,
// 指定相机视角
orientation: {
// 指定相机的朝向,偏航角
heading: Cesium.Math.toRadians(0),
// 指定相机的俯仰角,0度是竖直向上,-90度是向下
pitch: Cesium.Math.toRadians(-20),
// 指定相机的滚转角,翻滚角
roll: 0,
},
});
pitch、heading、row三个参数表示相机的姿态,使用position表示相机位置。
heading:默认方向为正北,正角度为向东旋转,即水平旋转,也叫偏航角。
pitch:默认角度为-90,即朝向地面,正角度在平面之上,负角度为平面下,即上下旋转,也叫俯仰角。
roll:默认旋转角度为0,左右旋转,正角度向右,负角度向左,也叫翻滚角。
7.2相机动画
可以让画面飞向指定的位置,并设置好相应的视角
// flyto,让相机飞往某个地方
viewer.camera.flyTo({
destination: position,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-20),
roll: 0,
},
});
7.3 键盘操控相机
我们可以给键盘绑定一个监听函数,来控制相机的移动、旋转。这里主要用到的就是相机身上自带的一系列方法
// 通过按键移动相机
document.addEventListener("keydown", e => {
// console.log(e);
// 获取相机离地面的高度
var height = viewer.camera.positionCartographic.height;
var moveRate = height / 100;
if (e.key == "w") {
// 设置相机向前移动
viewer.camera.moveForward(moveRate);
} else if (e.key == "s") {
// 设置相机向后移动
viewer.camera.moveBackward(moveRate);
} else if (e.key == "a") {
// 设置相机向左移动
viewer.camera.moveLeft(moveRate);
} else if (e.key == "d") {
// 设置相机向右移动
viewer.camera.moveRight(moveRate);
} else if (e.key == "q") {
// 设置相机向左旋转相机
viewer.camera.lookLeft(Cesium.Math.toRadians(0.1));
} else if (e.key == "e") {
// 设置相机向右旋转相机
viewer.camera.lookRight(Cesium.Math.toRadians(0.1));
} else if (e.key == "r") {
// 设置相机向上旋转相机
viewer.camera.lookUp(Cesium.Math.toRadians(0.1));
} else if (e.key == "f") {
// 设置相机向下旋转相机
viewer.camera.lookDown(Cesium.Math.toRadians(0.1));
} else if (e.key == "g") {
// 向左逆时针翻滚
viewer.camera.twistLeft(Cesium.Math.toRadians(0.1));
} else if (e.key == "h") {
// 向右顺时针翻滚
viewer.camera.twistRight(Cesium.Math.toRadians(0.1));
}
});
8.添加物体
这一部分涉及entity和Primitive,关于这两者的联系和区别,适合单独开一篇文章详写,在这里就不过多解释,先列举一些常见小案例。
8.1添加点
使用entities.add,添加一个点实体,并配置好点的位置、大小、颜色等信息。
var point = viewer.entities.add({
// 定位点
position: Cesium.Cartesian3.fromDegrees(113.3191, 23.109, 700),
// 点
point: {
pixelSize: 10,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 4,
},
});
8.2添加一个文字标签
还是使用entities的add方法,来添加物体,其中label代表文字部分,在配置项中设置好相关属性;billboard是广告牌的意思,可以显示图片图标。
// 添加文字标签和广告牌
var label = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(113.3191, 23.109, 750),
label: {
text: "广州塔",
font: "24px sans-serif",
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 4,
// FILL填充文字,OUTLINE勾勒标签,FILL_AND_OUTLINE填充文字和勾勒标签
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
// 设置文字的偏移量
pixelOffset: new Cesium.Cartesian2(0, -24),
// 设置文字的显示位置,LEFT /RIGHT /CENTER
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
// 设置文字的显示位置
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
},
billboard: {
image: "./texture/gzt.png",
width: 50,
height: 50,
// 设置广告牌的显示位置
verticalOrigin: Cesium.VerticalOrigin.TOP,
// 设置广告牌的显示位置
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
},
});
效果如下:
8.3 添加飞机模型
添加一个3D飞机模型
// 添加3D模型
const airplane = viewer.entities.add({
name: "Airplane",
position: Cesium.Cartesian3.fromDegrees(113.3191, 23.109, 1500),
model: {
uri: "./model/Air.glb",
// 设置飞机的最小像素
minimumPixelSize: 128,
// 设置飞机的轮廓
silhouetteSize: 5,
// 设置轮廓的颜色
silhouetteColor: Cesium.Color.WHITE,
// 设置相机距离模型多远的距离显示
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
200000
),
},
});
8.4 添加一个矩形
传入2个点位参数,就能添加矩形
// 使用entity创建矩形
var rectangle = viewer.entities.add({
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(
// 西边的经度
90,
// 南边维度
20,
// 东边经度
110,
// 北边维度
30
),
material: Cesium.Color.RED.withAlpha(0.5),
},
});
8.5 Primitive绘制图形
Primitive绘制的图形由两部分组成:
第一部分为几何形状(Geometry) ,用于定义Primitive图形的绐构,如面、椭圆、线条等;
第二部分为外观(Appearance),主要用于定义Primitive图形的渲染着色,通俗 来讲,就是定义Primitive图形的外观材质。
.
下面我们先绘制一个线条:
//绘制线
//定义几何形状
var polyline = new Cesium.GeometryInstance({
geometry: new Cesium.PolylineGeometry({
positions: Cesium.Cartesian3.fromDegreesArray([
108.0, 31.0,
100.0, 36.0,
105.0, 39.0
]),
width: 2.0
})
});
//定义外观
var polylineAppearance = new Cesium.PolylineMaterialAppearance({
material: Cesium.Material.fromType('Color')
})
//创建Primitive
var addPolylineGeometry = new Cesium.Primitive({
geometryInstances: polyline,
appearance: polylineAppearance
})
//添加primitive到场景中
viewer.scene.primitives.add(addPolylineGeometry)
当然,我们可以在设置外观的时候,自定义color
//给color添加配置项
var polylineAppearance = new Cesium.PolylineMaterialAppearance({
material: Cesium.Material.fromType("Color", {
color: new Cesium.Color(1.0, 1.0, 0.0, 1.0),
}),
});
绘制一个四边形
//绘制面
//定义几何形状
var polygon = new Cesium.GeometryInstance({
geometry: new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray([
108, 45, 109, 48, 104, 48, 103, 45,
])
),
}),
});
//定义外观
var polygonAppearance = new Cesium.MaterialAppearance({
material: Cesium.Material.fromType("Dot"),
});
//创建Primitive
var addPolygonGeometry = new Cesium.Primitive({
geometryInstances: polygon,
appearance: polygonAppearance,
});
链接:https://juejin.cn/post/7259208528531324985
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。