一、创建vue3 cesium项目流程
- 在某个文件夹中cmd,输入:pnpm create vite 名称
- 选好条件后:pnpm i
- pnpm i cesium@1.99 vite-plugin-cesium
- 修改vite.config.js文件
特定位置添加:import cesium from 'vite-plugin-cesium' 和
plugins: [vue(), cesium()], - 注释掉main.js里的: import './style.css' //不要默认样式了
- 在App.vue中引入cesium:import * as Cesium from 'cesium'
在onMounted中挂载地图并设置样式:
二、初始化一个干净的地球,去除不必要的控件
const viewer = new Cesium.Viewer("cesiumContainer", {
baseLayerPicker: false, //是否显示图层选择控件
animation: false, //是否显示动画控件
timeline: false, //是否显示时间轴
fullscreenButton: false, //是否显示全屏按钮
geocoder: false, //是否显搜索框
homeButton: false, //是否显示主页按钮
sceneModePicker: false, //是否显示投影方式按钮
navigationHelpButton: false //是否显示帮助按钮
})
三、手动指定影像图层(默认使用的谷歌的影像图层)
const ersi = new Cesium.ArcGisMapServerImageryProvider({
url:"https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
})
const viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: ersi,
})
四、经纬度 转 笛卡尔坐标
const cartesian = Cesium.Cartesian3.fromDegrees(110,20,100) //参数:经度、维度、高度
笛卡尔坐标对象:
五、笛卡尔 转 经纬度坐标
// 笛卡尔转经纬度坐标(笛卡尔坐标->弧度坐标->经纬度坐标)
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
// let lon = 180 / Math.PI * cartographic.longitude
let lon = Cesium.Math.toDegrees(cartographic.longitude)
let lat = Cesium.Math.toDegrees(cartographic.latitude)
console.log(lon, lat, cartographic.height)
弧度坐标对象:
六、相机
// 相机
// setView通过定义相机飞行目的地,直接跳转到目的地
viewer.camera.setView({
destination: position,
// 方向(默认分别是 0 -90 0)
orientation: {
heading: Cesium.Math.toRadians(0), //左右看
pitch: Cesium.Math.toRadians(-90), //上下看
roll: Cesium.Math.toRadians(0), //歪头看
},
})
// flyTo有飞行动画
viewer.camera.flyTo({
destination: position,
duration: 3, //飞行时长 单位(秒)
orientation: {
heading: Cesium.Math.toRadians(0), //左右看
pitch: Cesium.Math.toRadians(0), //上下看
roll: Cesium.Math.toRadians(0), //歪头看
},
})
// lookAt将视角固定在设置的点位上
const position2 = Cesium.Cartesian3.fromDegrees(110, 20, 20000)
viewer.camera.lookAt(
position2,
new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(0),
Cesium.Math.toRadians(-90),
20000
)
)
七、常用实体
// 点
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(121, 30),
point: {
pixelSize: 20,
color: Cesium.Color.RED,
},
})
// 标注
const billboard = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(116, 40, 1000),
billboard: {
image: "/src/assets/img/点位.png",
scale: 0.9, //控制图片大小
// color: Cesium.Color.YELLOWGREEN //封装好的颜色
color: Cesium.Color.fromCssColorString("#fff"), //也可以自定义颜色
},
// 文本
label: {
text: "测试文字",
showBackground: true, //开启背景色
backgroundColor: Cesium.Color.BLACK.withAlpha(0.5), //颜色+开启透明度
pixelOffset: new Cesium.Cartesian2(0, -40),
},
})
// 线
const polyline = viewer.entities.add({
polyline: {
// positions: Cesium.Cartesian3.fromDegreesArray([100, 20, 100, 25, 115, 20]), //如果线是不需要高度的 就用这个
positions: Cesium.Cartesian3.fromDegreesArrayHeights([
100, 20, 5000, 100, 25, 5000, 115, 20, 500000,
]), //如果想让线有高度 就用这个
width: 10,
material: Cesium.Color.RED,
},
})
// 多边形
const ploygon = viewer.entities.add({
polygon: {
hierarchy: {
positions: Cesium.Cartesian3.fromDegreesArray([
101, 21, 101, 26, 116, 21,
]),
},
material: Cesium.Color.BLUE,
height: 100000,
extrudedHeight: 200000, //向上挤出高度
outline: true,
outlineColor: Cesium.Color.WHITE, //描边 外线
fill: true, //是否填充
},
})
// 盒子(立方体)
const box = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(119, 30, 3000),
box: {
dimensions: new Cesium.Cartesian3(2000, 1000, 3000),
material: new Cesium.Color(1, 1, 0),
},
})
// 椭圆
const ellipse = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(118, 30),
ellipse: {
semiMajorAxis: 500, //半长轴
semiMinorAxis: 300, //半短轴
material: Cesium.Color.YELLOW,
rotation: Math.PI / 2, //旋转角度
},
})
// 矩形
const rectangle = viewer.entities.add({
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(120, 40, 123, 45),
material: "/src/assets/img/点位.png", //也可以使用图片填充
extrudedHeight: 30000
},
})
八、动态实体
// CallbackProperty 生成一个动态实体
let lon, lat,num = 0
const line = viewer.entities.add({
polyline: {
// CallbackProperty会产生一个死循环 持续输出Cesium.Cartesian3.fromDegreesArray
positions: new Cesium.CallbackProperty(() => {
console.log(1)
num += 0.005
lon = 120 + num
lat = 30 + num
if (lon < 121) {
return Cesium.Cartesian3.fromDegreesArray([120, 30, lon, lat])
} else {
// 跳不出死循环
// return Cesium.Cartesian3.fromDegreesArray([
// 120, 30, 121, 31,
// ])
// 跳出死循环 (给positions赋值一个新的对象,不再需要CallbackProperty提供)
line.polyline.positions =
Cesium.Cartesian3.fromDegreesArray([120, 30, 121, 31])
}
}, false),
material: Cesium.Color.YELLOW,
width: 5,
},
})
九、加载不同格式的数据dataSource
// 加载geojson格式的线
const linestring = turf.lineString([
[-24, 63],
[-23, 60],
[-25, 65],
[-20, 69],
])
const promise = Cesium.GeoJsonDataSource.load(linestring)
console.log(promise)
promise.then((res) => {
const entity = res.entities.values[0]
viewer.entities.add(entity)
})
// 加载geojson格式的多条线
const multiLine = turf.multiLineString([
[
[0, 0],
[4, 4],
],
[
[6, 6],
[10, 10],
],
])
const promise = Cesium.GeoJsonDataSource.load(multiLine)
promise.then((res) => {
data = res
viewer.dataSources.add(res)
console.log(viewer.dataSources)
})
// 加载geojson格式的多边形
let polygon = turf.polygon([
[
[-5, 52],
[-4, 56],
[-2, 51],
[-5, 52],
],
])
const promise = Cesium.GeoJsonDataSource.load(polygon)
// 可以不用.then 直接加
viewer.dataSources.add(promise)
viewer.zoomTo(promise)
// 加载topojson格式数据
const promise = Cesium.GeoJsonDataSource.load("/src/assets/usa.topojson")
viewer.dataSources.add(promise)
viewer.zoomTo(promise)
// 加载Kml(kmz)格式的数据
const promise = Cesium.KmlDataSource.load("/src/assets/gdp2008.kmz")
viewer.dataSources.add(promise)
viewer.zoomTo(promise)
// 加载Czml格式的数据
const promise = Cesium.CzmlDataSource.load("/src/assets/Vehicle.czml")
promise.then((res) => {
viewer.dataSources.add(res)
let entity = res.entities.getById("Vehicle")
viewer.trackedEntity = entity // trackedEntity 可以实现一直移动相机跟踪entity目标
})
十、加载3D瓦片的两种方式
1、使用Ion资源加载3DTiles数据:
const tileset1 = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: Cesium.IonResource.fromAssetId(69380), //75343
})
)
2、加载本地中的3DTiles数据:
const tileset2 = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: '/src/assets/b3dm/tileset.json', //tileset.json是入口文件
})
)
十一、鼠标点击事件
//1、点击地图某个位置添加一个点
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((event)=>{
// 获取点击位置的笛卡尔坐标
let position = viewer.scene.pickPosition(event.position)
// 如果有这个坐标 则添加一个点
if(Cesium.defined(position)){
viewer.entities.add({
position: position,
point: {
color: Cesium.Color.RED,
pixelSize: 30
}
})
}
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
//2、点击拾取的对象,变为蓝点
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((event)=>{
let pick = viewer.scene.pick(event.position)
if(pick && pick.id){
pick.id.point.color = Cesium.Color.BLUE
}
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
//3、画线
let arr = []
const line = viewer.entities.add({
polyline: {
positions: [],
material: Cesium.Color.YELLOW,
width: 5,
clampToGround: true
}
})
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((event)=>{
let position = viewer.scene.pickPosition(event.position)
if(Cesium.defined(position)){
const point = viewer.entities.add({
position: position,
name: 'point',
point: {
color: Cesium.Color.RED,
pixelSize: 30
}
})
arr.push(position)
pointList.push(point)
line.polyline.positions = arr
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
handler.setInputAction(()=>{
// 结束所有事件 且只保留一条线
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
pointList.forEach((item) => {
viewer.entities.remove(item)
})
pointList = []
},Cesium.ScreenSpaceEventType.RIGHT_CLICK)
十二、鼠标移动事件
// 鼠标移到哪个要素上哪个要素变红
let lastPick
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction((event)=>{
let pick = viewer.scene.pick(event.endPosition)
if(pick){
if(lastPick){
lastPick.color = Cesium.Color.WHITE
}
pick.color = Cesium.Color.ORANGERED
lastPick = pick
}
},Cesium.ScreenSpaceEventType.MOUSE_MOVE)
十三、三维模型
const position = Cesium.Cartesian3.fromDegrees(114,30,300)
const orientation = Cesium.Transforms.headingPitchRollQuaternion(
position,
Cesium.HeadingPitchRoll.fromDegrees(0,0,0)
)
const entity = viewer.entities.add({
position, //位置
orientation, //方向
model: {
uri: '/src/assets/Cesium_Air.glb',
minimumPixelSize: 200 //模型最小像素
}
})
十四、粒子效果
//粒子效果
viewer.scene.primitives.add(new Cesium.ParticleSystem({
image: '/src/assets/img/fire.png',
imageSize: new Cesium.Cartesian2(20,20), //尺寸
startScale: 1.0, //初始大小
endScale: 4.0, //最后大小
particleLife: 3.0, //设置每一个粒子存在的时间
speed: 5.0, //发射粒子的速度
emitter: new Cesium.CircleEmitter(2), //设置发射器(圆形发射器)
// emitter: new Cesium.BoxEmitter(new Cesium.Cartesian3(10,10,10)),
emissionRate: 5, //粒子发射数量
modelMatrix: entity.computeModelMatrix(
viewer.clock.startTime, //时间空间中的起始时间
new Cesium.Matrix4() //4*4矩阵数据
), //设置位置
lifetime: 16, //生命期属性为所需的持续时间
// loop: false, //只循环一次
}))
十五、点聚合
let viewer,data
const toDel = () =>{
viewer.dataSources.remove(data)
}
const pinBuilder = new Cesium.PinBuilder();
const pin300 = pinBuilder
.fromText("300+", Cesium.Color.RED, 48)
.toDataURL();
const pin100 = pinBuilder
.fromText("100+", Cesium.Color.ORANGE, 48)
.toDataURL();
const pin50 = pinBuilder
.fromText("50+", Cesium.Color.YELLOW, 48)
.toDataURL();
const pin30 = pinBuilder
.fromText("30+", Cesium.Color.GREEN, 48)
.toDataURL();
const pin10 = pinBuilder
.fromText("10+", Cesium.Color.BLUE, 48)
.toDataURL();
onMounted(() => {
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4NWNkMTZkYy1hY2I5LTQwNDgtYjM5ZC0yNTY5NTc3NDUwMDAiLCJpZCI6MTgxMTUzLCJpYXQiOjE3MDExNDM4ODB9.MT-iAoFv-Ok7r1DIM-zd-8fsrah5iK2wMeNoftoPNig"
viewer = new Cesium.Viewer("cesiumContainer", {})
Cesium.GeoJsonDataSource.load('/src/assets/camera.json').then(res => {
data = res
res.clustering.enabled = true;
res.clustering.pixelRange = 30;
res.clustering.minimumClusterSize = 3;
res.clustering.clusterEvent.addEventListener((clusteredEntities, cluster)=>{
cluster.billboard.show = true
cluster.label.show = false
if (clusteredEntities.length >= 300) {
cluster.billboard.image = pin300
} else if (clusteredEntities.length >= 100) {
cluster.billboard.image = pin100
} else if (clusteredEntities.length >= 50) {
cluster.billboard.image = pin50
} else if (clusteredEntities.length >= 30) {
cluster.billboard.image = pin30
} else if (clusteredEntities.length >= 10) {
cluster.billboard.image = pin10
}
})
viewer.dataSources.add(res)
res.entities.values.forEach(item=>{
item.billboard.image = '/src/assets/img/点位.png'
// item.billboard.scale = 0.15
})
viewer.flyTo(res)
})
})