一、HTML
<template>
<div class="cesium_map">
<div id="cesiumContainer"></div>
<div class="cesium_position">
<div class="position_lonlat">
<p>经度:</p>
<p>{{ location.lon.toFixed(6) }}</p>
</div>
<div class="position_lonlat">
<p>纬度:</p>
<p>{{ location.lat.toFixed(6) }}</p>
</div>
</div>
</div>
</template>
二、JavaScript
<script setup>
import * as Cesium from "cesium/Source/Cesium";
import CesiumNavigation from "cesium-navigation-es6";
import { reactive, ref, onMounted } from "vue";
// ---------------------------<<数据>>---------------------------
// 鼠标当前位置信息显示
const location = reactive({
lon: 0,
lat: 0,
height: 0,
});
// 点位数组
const pointArr = ref([
{
lon: 123.645025,
lat: 22.03158,
alt: 300,
time: null,
},
{
lon: 123.647785,
lat: 22.02946,
alt: 300,
time: null,
},
{
lon: 123.651688,
lat: 22.02946,
alt: 300,
time: null,
},
{
lon: 123.654448,
lat: 22.03158,
alt: 300,
time: null,
},
{
lon: 123.654448,
lat: 22.03519,
alt: 300,
time: null,
},
{
lon: 123.651688,
lat: 22.03754,
alt: 300,
time: null,
},
{
lon: 123.647785,
lat: 22.03754,
alt: 300,
time: null,
},
{
lon: 123.645025,
lat: 22.03519,
alt: 300,
time: null,
},
{
lon: 123.645025,
lat: 22.03158,
alt: 300,
time: null,
},
]);
// 侧向线计时器
const lineTimer = ref(null);
// 侧向线路径
const aggregate = ref(
Cesium.Cartesian3.fromDegreesArrayHeights([
123.64828, 22.032115, 0, 123.645025, 22.03158, 300,
])
);
// ---------------------------<<函数>>---------------------------
function initMap() {
// 在线地图token
Cesium.Ion.defaultAccessToken ="";
// 在线地图
let imageryProvider = new Cesium.ArcGisMapServerImageryProvider({
url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
});
// 离线地图
// var imageryProvider = new Cesium.UrlTemplateImageryProvider({
// url: "http://127.0.0.1:9001/map_9/{z}/{x}/{y}.png",
// });
var viewer = new Cesium.Viewer("cesiumContainer", {
sceneMode: Cesium.SceneMode.SCENE3D, //初始场景模式 为二维
fullscreenButton: false,
homeButton: false,
scene3DOnly: false,
creditContainer: "cesiumContainer",
vrButton: false,
skyAtmosphere: false,
infoBox: false, //是否显示点击要素之后显示的信息
imageryProvider: imageryProvider,
maximumScreenSpaceError: 16, //默认值16 用于提高细节细化级别的最大屏幕空间错误
animation: false, //是否显示动画控件 是否显示图层选择控件
geocoder: false, //是否显示地名查找控件
timeline: false, //是否显示时间线控件
sceneModePicker: true, //是否显示投影方式控件
navigationHelpButton: false, //是否显示帮助信息控件
shadows: true,
shouldAnimate: true,
baseLayerPicker: false, //是否显示图层选择控件
selectionIndicator: false, //设置绿色框框不可见
skyBox: false,
contextOption: {
webgl: {
alpha: true,
},
},
});
viewer.scene.globe.enableLighting = false;
viewer.shadows = false;
viewer.sceneModePicker.viewModel.duration = 0;
viewer.scene.globe.enableLighting = false;
Cesium.Camera.DEFAULT_VIEW_FACTOR = 0.2; // 摄像机到地图的距离放大倍数
viewer.camera.flyHome(0);
//取消双击事件
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
);
// 调整画面精细度
viewer.resolutionScale = 1; //默认值为1.0
viewer.scene.backgroundColor = Cesium.Color.TRANSPARENT;
viewer.scene.globe.baseColor = Cesium.Color.TRANSPARENT;
//是否开启抗锯齿
viewer.scene.fxaa = false;
viewer.scene.postProcessStages.fxaa.enabled = false;
viewer.scene.globe.showGroundAtmosphere = true;
//指北针
var options = {};
options.enableCompass = true; //罗盘
options.enableZoomControls = true; //缩放
options.enableDistanceLegend = true; //比例尺
options.enableCompassOuterRing = true; //指南针外环
viewer.extend(CesiumNavigation, options);
window.viewer = viewer;
// 设置相机位置
viewer.scene.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(123.652734, 22.032279, 3000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0.0,
},
});
// 开启画面帧频
// viewer.scene.debugShowFramesPerSecond = true;
// 鼠标事件
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
// 移动事件
handler.setInputAction((movement) => {
if (movement.endPosition) {
//获取点击位置的经纬度
getMovementPositiond(movement.endPosition, (position) => {
location.lon = position.lon;
location.lat = position.lat;
});
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 滚轮事件
handler.setInputAction((movement) => {
console.log("滚轮事件");
}, Cesium.ScreenSpaceEventType.WHEEL);
// 双击事件
handler.setInputAction((movement) => {
console.log("双击事件");
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
// 单击事件
handler.setInputAction((movement) => {
console.log("单击事件");
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
// 坐标转换经纬度
function getMovementPositiond(position, callback) {
var viewer = window.viewer;
var scene = viewer.scene;
var ellipsoid = scene.globe.ellipsoid;
var cartesian = scene.camera.pickEllipsoid(position, ellipsoid);
if (!cartesian) return; //鼠标在地球外
//将笛卡尔坐标转换为地理坐标
var cartographic = ellipsoid.cartesianToCartographic(cartesian);
//将弧度转为度的十进制度表示
var lon = Cesium.Math.toDegrees(cartographic.longitude);
var lat = Cesium.Math.toDegrees(cartographic.latitude);
var cameraAlt = ellipsoid.cartesianToCartographic(
viewer.camera.position
).height;
var heading = Cesium.Math.toDegrees(viewer.camera.heading);
//倾斜角度 围绕Y轴旋转
var pitch = Cesium.Math.toDegrees(viewer.camera.pitch);
//围绕X轴旋转
var roll = Cesium.Math.toDegrees(viewer.camera.roll);
let ret = {
lon,
lat,
// alt,
camera: { alt: cameraAlt, heading, pitch, roll },
};
callback && callback(ret);
return ret;
}
// 创建点位
function createPoint(ps) {
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(ps.lon, ps.lat, ps.alt),
point: {
pixelSize: 5,
// 点位颜色,fromCssColorString 可以直接使用CSS颜色
color: Cesium.Color.fromCssColorString("#ee0000"),
// 边框颜色
outlineColor: Cesium.Color.fromCssColorString("#fff"),
// 边框宽度(像素)
outlineWidth: 2,
// 显示在距相机的距离处的属性,多少区间内是可以显示的
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
15000000
),
},
});
}
// 创建线条
function createLine(ps) {
// 航迹线id
let id = `line`;
viewer.entities.add({
id, // 模型id
name: "line", // 所属的父级id
// polyline 折线
polyline: {
// 参数依次为[经度1, 纬度1, 高度1, 经度2, 纬度2, 高度2]
positions: Cesium.Cartesian3.fromDegreesArrayHeights(ps),
// 注:线条起止,可以获取鼠标点击位置的经纬度进行线条绘制
// 宽度
width: 2,
// 线的颜色
material: Cesium.Color.fromCssColorString("tomato"), // "tomato"
clampToGround: false, // 不紧贴地面
// 线的顺序,仅当`clampToGround`为true并且支持地形上的折线时才有效。
zIndex: 999,
// 显示在距相机的距离处的属性,多少区间内是可以显示的
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
15000000
),
// 是否显示
show: true,
},
});
}
// 绘制圆锥
function createCone(info) {
if (!info.angel && info.angel != 0) {
console.error("请传入圆锥方位");
return;
}
if (!info.pitch && info.pitch != 0) {
console.error("请传入圆锥俯仰");
return;
}
let {
lon, // 经度
lat, // 纬度
alt, // 高度
length, // 圆锥高度
angel,
pitch,
} = info;
// 计算角度
let params = {
lon, // 经度
lat, // 纬度
alt, // 高度
length, // 圆锥高度
startAz: angel - 5, //起始方位角
endAz: angel + 5, //终止方位角
startEl: pitch - 5, //起始俯仰角
endEl: pitch + 5, //终止俯仰角
};
var result = calcBeamScaleMatrix4(params);
//绘制圆锥几何体
var cone = new Cesium.Primitive({
show: true,
releaseGeometryInstances: false, // 默认为true,自动清空geometryInstances配置
geometryInstances: new Cesium.GeometryInstance({
id: "cone",
name: "cone",
geometry: new Cesium.CylinderGeometry({
length: 200,
topRadius: 0.0,
bottomRadius: result.bottomRadius,
}),
}),
modelMatrix: result.mm, //提供位置与姿态参数
appearance: new Cesium.EllipsoidSurfaceAppearance({
material: getRingM({ color: "#00ff00" }), // 交替
faceForward: true,
closed: true,
}),
});
viewer.scene.primitives.add(cone);
}
// 圆锥计算
function adjustAngle({ startAngle, endAngle }) {
if (endAngle < startAngle) {
if (endAngle > 180 && startAngle < 180) {
var Az = startAngle; //先大小-前后交换顺序
startAngle = endAngle;
endAngle = Az;
}
}
if (Math.abs(endAngle - startAngle) > 180) {
if (endAngle > 180) {
Az = startAngle;
startAngle = endAngle - 360;
endAngle = startAngle;
}
}
return {
startAngle,
endAngle,
};
}
function calcBeamMatrix4({ lon, lat, alt, pitch, heading, length }) {
//北天东与cesium坐标系的差距
heading += 90;
pitch = 270 - pitch;
var center = Cesium.Cartesian3.fromDegrees(lon, lat, alt);
heading = Cesium.Math.toRadians(heading);
pitch = Cesium.Math.toRadians(pitch);
var deadingPitchRoll = new Cesium.HeadingPitchRoll(heading, pitch, 0);
var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(deadingPitchRoll);
var subtract = new Cesium.Cartesian3(0, 0, -length / 2);
var rotated = Cesium.Matrix3.multiplyByVector(
hprRotation,
subtract,
new Cesium.Cartesian3()
);
var hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, rotated);
var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);
Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix);
return modelMatrix;
}
function calcBeamScaleMatrix4({
lon,
lat,
alt,
length,
startAz, //波束起始方位角
endAz, //波束终止方位角
startEl, //俯仰
endEl, //俯仰
}) {
//俯仰在正负180之间
if (startEl > 180) startEl -= 360;
if (startEl < -180) startEl += 360;
if (endEl > 180) endEl -= 360;
if (endEl < -180) endEl += 360;
var newParam = adjustAngle({
startAngle: startAz,
endAngle: endAz,
});
startAz = newParam.startAngle;
endAz = newParam.endAngle;
var newParam1 = adjustAngle({
startAngle: startEl,
endAngle: endEl,
});
startEl = newParam1.startAngle;
endEl = newParam1.endAngle;
var heading = (endAz + startAz) / 2; //圆锥的方位指向
var pitch = ((startEl + endEl) / 2) % 180; //圆锥的俯仰指向俯仰在-180到180之间
var angle0 = (endAz - startAz) / 2; //方位
var angle1 = (endEl - startEl) / 2; //俯仰
if (endAz < 0 && startAz < 0) {
//计算缩放比列,取最小角度和取绝对值
angle0 = Math.abs(((endAz % 360) - (startAz % 360)) / 2); //方位圆锥底面
angle1 = Math.abs(((endEl % 360) - (startEl % 360)) / 2); //俯仰圆锥底面
}
var angle = 0;
var scaleX = 1,
scaleY = 1;
if (angle0 < angle1) {
angle = angle1;
scaleX = angle0 / angle1; //计算x,y,z的缩放比例(以便形成椭圆面)
} else {
angle = angle0;
scaleY = angle1 / angle0; //计算x,y,z的缩放比例(以便形成椭圆面)
}
var bottomRadius = length * Math.tan(Cesium.Math.toRadians(angle)); //计算底边半径(以最小的方位或俯仰角为准)
var modelMatrix = calcBeamMatrix4({
lon,
lat,
alt,
heading,
pitch,
length,
});
var s = Cesium.Matrix3.fromScale(new Cesium.Cartesian3(scaleY, scaleX, 1.0));
var scaleMatrix = Cesium.Matrix4.fromRotationTranslation(s);
let mm = modelMatrix.clone();
Cesium.Matrix4.multiply(mm, scaleMatrix, mm);
return { mm, bottomRadius };
}
// 圆锥材质
function getRingM(params) {
var { color, thickness, repeat } = params || {};
var material = new Cesium.Material({
fabric: {
type: "VtxfShader",
uniforms: {
color: Cesium.Color.fromCssColorString(color || "rgba(255,0,0,0.3)"),
repeat: repeat || 40.0, //40.0,/重复次数
offset: 10, // 1.0,
thickness: thickness || 0.5, //环宽度比列(0, 1]
},
source: `
uniform vec4 color;
uniform float repeat;
uniform float offset;
uniform float thickness;
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
float sp = 1.0/repeat;
vec2 st = materialInput.st;
float dis = distance(st, vec2(0.5));
float m = mod(dis + offset, sp);
float a = step(sp*(1.0-thickness), m);
material.diffuse = color.rgb;
material.alpha = a * color.a;
return material;
}
`,
},
// translucent: false,
});
return material;
}
// 初始化点位/线条
function initPointAndLine() {
let temp = ref([]);
for (let i = 0; i < pointArr.value.length; i++) {
createPoint(pointArr.value[i]);
temp.value.push(
pointArr.value[i].lon,
pointArr.value[i].lat,
pointArr.value[i].alt
);
}
createLine(temp.value);
}
// 初始化圆锥
function initCone() {
let info = {
lon: 123.652734, // 经度
lat: 22.032279, // 纬度
alt: 0, // 高度
length: 200, // 圆锥高度
angel: 45, // 方位
pitch: 30, // 俯仰
};
createCone(info);
}
// 初始化侧向线
function initSideline() {
viewer.entities.add({
id: `Sideline`,
polyline: {
positions: new Cesium.CallbackProperty(() => {
return aggregate.value;
}, false),
width: 1,
material: new Cesium.StripeMaterialProperty({
// 用条纹材质设置外边颜色
evenColor: Cesium.Color.WHITE, // 偶数条纹颜色
oddColor: Cesium.Color.WHITE, // 奇数条纹颜色
repeat: 3.0, // 条纹的重复数量
}),
},
});
}
// ------------------<<飞行测试>>------------------
const start = ref(0);
const stop = ref(0);
const flyTime = ref(0);
function addModels() {
// 计算时间飞行数据
let property = computeFlight(pointArr.value);
// ++++++++++<<创建模型>>++++++++++
const entity = viewer.entities.add({
id: `testModel`, //模型id
name: "model", // 模型名称,这里用作模型类型,方便场景模型增删改查
// 和时间轴关联
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start.value,
stop: stop.value,
}),
]),
position: property, //模型位置,高度
orientation: new Cesium.VelocityOrientationProperty(property),
model: {
uri: "./test1.gltf", //模型文件
minimumPixelSize: 100, //模型最小像素大小
maximumScale: 100, //模型最大像素大小
},
});
}
// 移动
function modelMove() {
for (let i = 0; i < pointArr.value.length; i++) {
pointArr.value[i].time = flyTime.value;
flyTime.value += 2;
}
// 起始时间
start.value = Cesium.JulianDate.fromDate(new Date());
// 结束时间
stop.value = Cesium.JulianDate.addSeconds(
start.value,
pointArr.value[pointArr.value.length - 1].time,
new Cesium.JulianDate()
);
// 设置始时钟始时间
viewer.clock.startTime = start.value.clone();
// 设置时钟当前时间
viewer.clock.currentTime = start.value.clone();
// 设置始终停止时间
viewer.clock.stopTime = stop.value.clone();
// 时间速率,数字越大时间过的越快
viewer.clock.multiplier = 1;
// 时间轴
// viewer.timeline.zoomTo(start.value, stop.value);
// 循环执行,即为2,到达终止时间,重新从起点时间开始LOOP_STOP
// viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
// 创建模型
addModels();
}
// 计算飞行
function computeFlight(source) {
// 取样位置 相当于一个集合
let property = new Cesium.SampledPositionProperty();
for (let i = 0; i < source.length; i++) {
let time = Cesium.JulianDate.addSeconds(
start.value,
source[i].time,
new Cesium.JulianDate()
);
let position = Cesium.Cartesian3.fromDegrees(
source[i].lon,
source[i].lat,
source[i].alt
);
// 添加位置,和时间对应
property.addSample(time, position);
}
return property;
}
// 获取当前飞机位置
function getCurrentPosition() {
if (!lineTimer.value) {
// 定时获取实体位置
lineTimer.value = setInterval(() => {
// 获取飞机模型
let plane = viewer.entities.getById("testModel");
var planePosition = Cesium.Property.getValueOrDefault(
plane._position,
viewer.clock.currentTime,
new Cesium.Cartesian3()
);
var cartographic = Cesium.Cartographic.fromCartesian(planePosition);
var lon = Cesium.Math.toDegrees(cartographic.longitude);
var lat = Cesium.Math.toDegrees(cartographic.latitude);
aggregate.value = Cesium.Cartesian3.fromDegreesArrayHeights([
123.64828,
22.032115,
0,
lon,
lat,
300,
]);
}, 10);
}
}
// ---------------------------<<执行>>---------------------------
// 挂载后生命周期
onMounted(() => {
initMap();
initPointAndLine();
initCone();
initSideline();
getCurrentPosition();
// 测试飞行
modelMove();
});
</script>
三、CSS
<style>
.cesium_map {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
}
.cesium-widget-credits {
display: none !important;
visibility: hide !important;
}
.cesium_position {
width: 240px;
position: absolute;
display: flex;
height: 22px;
line-height: 22px;
background-color: rgba(0, 0, 0, 0.6);
padding: 0 8px;
right: 20px;
bottom: 5px;
}
.cesium_position .position_lonlat {
flex: 1;
display: flex;
color: #fff;
font-size: 12px;
}
.cesium_position .position_lonlat p:first-child {
flex: 0 0 40px;
}
.cesium_position .position_lonlat p:last-child {
flex: 1;
}
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.cesium-viewer-toolbar,
.navigation-controls,
.cesium-widget-credits,
.cesium-performanceDisplay-throttled {
display: none !important;
}
.cesium-container {
position: relative;
}
.distance-legend {
right: 20px;
}
.distance-legend-label {
font-size: 12px;
}
.gide_item_inpu {
flex: 1;
:deep(.el-switch__core) {
border: 1px solid #2fedba;
}
:deep(.el-input.is-disabled .el-input__wrapper) {
background-color: transparent;
box-shadow: unset;
}
}
</style>