一、前言
智慧园区业内有较多成熟解决方案,这里仅个人纯原生Cesium开发、使用各开源免费数据做出其中部分功能。
二、获得数据
地理信息数据来源
地理空间数据云
数据资源更新比较稳定,免费数据包括Landsat系列、中巴资源卫星、MODIS数据的各种产品、DEM数字高程数据、EO-1数据、NOAAA VHRR数据产品、Sentinel数据等。账号注册,通过审核直接下载即可。
中国遥感数据共享网
国内存档周期最长的数据网站,对Landsat数据免费共享,也可订购国外商业卫星数据。注册账号,通过审核就可直接下载。
OpenStreetMap
一个开源的世界地图,可依据开放许可协议自由使用,并且可以由人们自由的进行编辑。
模型数据来源
1、通过revit、bentley、3dmax、maya等模型制作软件制作建筑模型。通过obj2gltf等格式转换工具转换成Cesium可展示格式。
2、调用Cesium Ion的OSMBuilding服务,展示全球建筑模型数据。
倾斜摄影数据来源
无人机航拍照片经过空三演算获得osgb数据。
三、功能
全景展示
需要对应园区模型数据,用Cesium对应的加载方法加载。
const tileset = new Cesium.Cesium3DTileset({
url: './NewYork/tileset.json',
})
tileset.readyPromise.then((tile) => {
tileset.style = new Cesium.Cesium3DTileStyle({
color: {
conditions: [
/* 根据模型属性分层设色 */
['true', "color('white')"],
//eg: ['${Height} >=50', "color('red')"],
],
},
})
viewer.scene.primitives.add(tileset)
image.png
多元数据叠加
路网信息
async addGeoJson() {
/**
* @description: 添加路网信息
*
* @param {*}
* @author: citrusrlia@foxmail.com
*/
const that = this
let res = await Cesium.GeoJsonDataSource.load('NYC_roads_draw.json', {
stroke: Cesium.Color.WHITE,
fill: Cesium.Color.BLUE.withAlpha(0.3),
strokeWidth: 2,
})
res.show = false
viewer.dataSources.add(res)
let entities = res.entities.values
for (let i = 0, l = entities.length; i < l; i++) {
let thisLine = entities[i]
// thisLine.nameID = 'line' + i
thisLine.polyline.width = 20
thisLine.polyline.material = new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Cesium.Color.BLUE.withAlpha(0.7),
})
}
}
image.png
管线信息
地下管线缺少数据,本来用glb矿道数据模拟,效果不佳,采用手绘动态线模拟。动态线PolylineTrailLinkMaterialProperty类在Cesium1.7版本后无法拓展至Cesium类。
管网.gif
async addPipe() {
/**
* @description: 添加管线(geojson)
* @param {*}
* @author: citrusrlia@foxmail.com
*/
const that = this
let res = await Cesium.GeoJsonDataSource.load('NYC_pipes_draw.json', {
stroke: Cesium.Color.WHITE,
fill: Cesium.Color.BLUE.withAlpha(0.3),
strokeWidth: 2,
})
res.show = false
viewer.dataSources.add(res)
let entities = res.entities.values
for (let i = 0, l = entities.length; i < l; i++) {
let thisLine = entities[i]
thisLine.polyline.width = 5
thisLine.polyline.material = new that.PolylineTrailLinkMaterialProperty(
Cesium.Color.GREEN,
3000
)
}
/* 👇若使用gltf模型 */
// const pipes = addGltf(
// [-74.01263164288234, 40.710022989658896, -250],
// 'pipe',
// 'ParcLeadMine.glb'
// )
// viewer.scene.primitives.add(pipes)
// pipes.show = false
// function addGltf(degrees, id, model_url, scale = 1) {
// /**
// * @author: citrusrlia@foxmail.com
// * @description: 添加gltf模型
// * @param {degrees:array id:string model_url:string scale:number}
// * @return {*}
// */
// let position = Cesium.Cartesian3.fromDegrees(
// degrees[0],
// degrees[1],
// degrees[2]
// )
// let heading = 90
// let pitch = 0
// let roll = 0
// let modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(
// position,
// new Cesium.HeadingPitchRoll(heading, pitch, roll)
// )
// let model = Cesium.Model.fromGltf({
// url: model_url,
// modelMatrix: modelMatrix,
// scale: scale,
// id: id + '_model',
// maximumScreenSpaceError: 16,
// show: true,
// allowPicking: true,
// })
// return model
// }
}
流动线PolylineTrailLinkMaterialProperty
initFlow() {
/**
* @description:流动线类,1.70后版本Cesium不可拓展材质类,将材质类添加至vue
* @param {color:Cesium.color duation:ms}
* @author: citrusrlia@foxmail.com
*/
function PolylineTrailLinkMaterialProperty(color, duration) {
this._definitionChanged = new Cesium.Event()
this._color = undefined
this._colorSubscription = undefined
this.color = color
this.duration = duration
this._time = new Date().getTime()
}
Object.defineProperties(PolylineTrailLinkMaterialProperty.prototype, {
isConstant: {
get: function () {
return false
},
},
definitionChanged: {
get: function () {
return this._definitionChanged
},
},
color: Cesium.createPropertyDescriptor('color'),
})
PolylineTrailLinkMaterialProperty.prototype.getType = function (time) {
return 'PolylineTrailLink'
}
PolylineTrailLinkMaterialProperty.prototype.getValue = function (
time,
result
) {
if (!Cesium.defined(result)) {
result = {}
}
result.color = Cesium.Property.getValueOrClonedDefault(
this._color,
time,
Cesium.Color.WHITE,
result.color
)
result.image = Cesium.Material.PolylineTrailLinkImage
result.time =
((new Date().getTime() - this._time) % this.duration) / this.duration
return result
}
PolylineTrailLinkMaterialProperty.prototype.equals = function (other) {
return (
this === other ||
(other instanceof PolylineTrailLinkMaterialProperty &&
Cesium.Property.equals(this._color, other._color))
)
}
this.PolylineTrailLinkMaterialProperty = PolylineTrailLinkMaterialProperty
Cesium.Material.PolylineTrailLinkType = 'PolylineTrailLink'
Cesium.Material.PolylineTrailLinkImage = 'fade_blue.jpeg'
Cesium.Material.PolylineTrailLinkSource =
'czm_material czm_getMaterial(czm_materialInput materialInput)\n\
{\n\
czm_material material = czm_getDefaultMaterial(materialInput);\n\
vec2 st = materialInput.st;\n\
vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));\n\
material.alpha = colorImage.a * color.a;\n\
material.diffuse = (colorImage.rgb+color.rgb)/2.0;\n\
return material;\n\
}'
Cesium.Material._materialCache.addMaterial(
Cesium.Material.PolylineTrailLinkType,
{
fabric: {
type: Cesium.Material.PolylineTrailLinkType,
uniforms: {
color: new Cesium.Color(1.0, 0.0, 0.0, 0.5),
image: Cesium.Material.PolylineTrailLinkImage,
time: 0,
},
source: Cesium.Material.PolylineTrailLinkSource,
},
translucent: function (material) {
return true
},
}
)
},
}
动态边界
添加动态墙显示园区边界
光墙_ss.gif
addBorder() {
/**
* @description: 添加园区动态光效墙
* @param {*}
* @author: citrusrlia@foxmail.com
*/
let alp = 1,
num = 0
const border = viewer.entities.add({
name: 'my-border',
wall: {
show: true,
positions: new Cesium.Cartesian3.fromDegreesArrayHeights([
-74.0134334564209, 40.71439496334888, 100, -74.01660919189453,
40.70500981124441, 100, -74.01418447494507, 40.70463567898069, 100,
-74.00809049606323, 40.71203660068803, 100, -74.0134334564209,
40.71439496334888, 100,
]),
material: new Cesium.ImageMaterialProperty({
image: 'fade_blue.jpeg',
transparent: true,
color: new Cesium.CallbackProperty(function () {
if (num % 2 === 0) {
alp -= 0.005
} else {
alp += 0.005
}
if (alp <= 0.3) {
num++
} else if (alp >= 1) {
num++
}
return Cesium.Color.WHITE.withAlpha(alp)
}, false),
}),
},
})
}
地标信息
添加地标label,设置对应可视距离。
addLabel() {
/**
* @description: 添加Label 设置label可见距离
* @param {*}
* @author: citrusrlia@foxmail.com
*/
const that = this
for (let i = 0, l = Object.keys(this.places).length; i < l; i++) {
viewer.entities.add({
name: Object.keys(this.places)[i],
position: Cesium.Cartesian3.fromDegrees(
this.places[Object.keys(this.places)[i]][0],
this.places[Object.keys(this.places)[i]][1],
this.places[Object.keys(this.places)[i]][2]
),
label: {
text: Object.keys(this.places)[i],
scale: 0.6,
fillColor: Cesium.Color.fromCssColorString('#ffffff'), //字体颜色
backgroundColor: Cesium.Color.fromCssColorString('#666666'), //背景颜色
showBackground: true, //是否显示背景颜色
// style: 2, //label样式
// show: false,
outlineWidth: 1,
verticalOrigin: Cesium.VerticalOrigin.CENTER, //垂直位置
horizontalOrigin: Cesium.HorizontalOrigin.LEFT, //水平位置
pixelOffset: new Cesium.Cartesian2(-50, 0), //偏移
eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 负值则在更上层
},
scaleByDistance: new Cesium.NearFarScalar(100, 2, 500, 0.0),
})
}
let labelArray = [
[-74.01319334175476, 40.708918331339284, '格林威治街给水管'],
[-74.01117454037451, 40.708399481792796, '华尔街中央给水管'],
[-74.0130122995653, 40.707738996075705, '尤特尔街给水管'],
[-74.01235941779147, 40.70907308085917, '泰晤士街给水管'],
[-74.01118964381898, 40.7063315127262, '拿索街给水管'],
]
for (let i = 0, l = labelArray.length; i < l; i++) {
viewer.entities.add({
name: 'underground_pip_' + i,
position: Cesium.Cartesian3.fromDegrees(
labelArray[i][0],
labelArray[i][1],
10
),
show: false,
label: {
text: labelArray[i][2],
scale: 0.6,
fillColor: Cesium.Color.fromCssColorString('#ffffff'), //字体颜色
backgroundColor: Cesium.Color.fromCssColorString('#666666'), //背景颜色
showBackground: true, //是否显示背景颜色
// style: 2, //label样式
// show: false,
outlineWidth: 1,
// clampToGround:true,
verticalOrigin: Cesium.VerticalOrigin.CENTER, //垂直位置
horizontalOrigin: Cesium.HorizontalOrigin.LEFT, //水平位置
pixelOffset: new Cesium.Cartesian2(-50, 0), //偏移
eyeOffset: new Cesium.Cartesian3(0, 0, -1000), // 负值则在更上层
},
})
}
}
园区漫游
自由漫游
使用wasd控制行动,鼠标左键控制方向。
自由漫游s.gif
startRoaming() {
this.roamingSettings(false);
/* 自由漫游初始点位 */
this.cameraFlyTo(
new Cesium.Cartesian3(
1333262.809154561,
-4654847.86027459,
4137655.768559768
),
0.4234449223726292,
0,
6.283167794440473
);
let that = this;
const ellipsoid = that.viewer.scene.globe.ellipsoid;
let startMousePosition;
let mousePosition;
let flags = this.roamingFlags;
flags.available = true;
this.handler = new Cesium.ScreenSpaceEventHandler(that.viewer.scene.canvas);
let handler = this.handler;
handler.setInputAction(function (movement) {
flags.looking = true;
mousePosition = startMousePosition = Cesium.Cartesian3.clone(
movement.position
);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
handler.setInputAction(function (movement) {
mousePosition = movement.endPosition;
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(function (position) {
flags.looking = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
function getFlagForKeyCode(keyCode) {
switch (keyCode) {
case "W".charCodeAt(0):
return "moveForward";
case "S".charCodeAt(0):
return "moveBackward";
case "Q".charCodeAt(0):
return "moveUp";
case "E".charCodeAt(0):
return "moveDown";
case "D".charCodeAt(0):
return "moveRight";
case "A".charCodeAt(0):
return "moveLeft";
default:
return undefined;
}
}
document.addEventListener(
"keydown",
function (e) {
let flagName = getFlagForKeyCode(e.keyCode);
if (typeof flagName !== "undefined") {
flags[flagName] = true;
}
},
false
);
document.addEventListener(
"keyup",
function (e) {
let flagName = getFlagForKeyCode(e.keyCode);
if (typeof flagName !== "undefined") {
flags[flagName] = false;
}
},
false
);
that.viewer.clock.onTick.addEventListener(function (clock) {
let camera = that.viewer.camera;
if (!flags.available) {
return;
}
if (flags.looking) {
let width = that.viewer.scene.canvas.clientWidth;
let height = that.viewer.scene.canvas.clientHeight;
/* 保证MousePositon存在 */
if (mousePosition) {
// Coordinate (0.0, 0.0) will be where the mouse was clicked.
let x = (mousePosition.x - startMousePosition.x) / width;
let y = -(mousePosition.y - startMousePosition.y) / height;
/* 转向系数 */
let lookFactor = 0.05;
camera.lookRight(x * lookFactor);
camera.lookUp(y * lookFactor);
}
}
// 移动系数
let cameraHeight = ellipsoid.cartesianToCartographic(camera.position)
.height;
let moveRate = cameraHeight / 20.0;
if (flags.moveForward) {
camera.moveForward(moveRate);
}
if (flags.moveBackward) {
camera.moveBackward(moveRate);
}
if (flags.moveUp) {
camera.moveUp(moveRate);
}
if (flags.moveDown) {
camera.moveDown(moveRate);
}
if (flags.moveLeft) {
camera.moveLeft(moveRate);
}
if (flags.moveRight) {
camera.moveRight(moveRate);
}
});
}
给定线路漫游
通过画线绘制漫游路线,通过漫游点列表编辑或删除漫游点,点击开始漫游后跟随所画线路漫游
漫游点编辑s.gif
漫游点漫游s.gif
addPlace(position) {
/**
* @description:添加漫游点
* @param {*}position:Cartisian3
* @author: citrusrlia@foxmail.com
*/
let Stop = {}
//参数传入站点变量
Stop.position = position
Stop.time = 5
Stop.name = '漫游点'
if (this.allStops.length < 1) {
Stop.heading = 0
Stop.pitch = 0
Stop.roll = 0
} else {
Stop.heading = this.getHeading(
this.allStops.slice(-1)[0].position,
position
)
Stop.pitch = this.getPitch(
this.allStops.slice(-1)[0].position,
position
)
Stop.roll = 0
}
//传入飞行站点数组
this.allStops.push(Stop)
this.$notify({
title: '成功',
message: '添加漫游点成功',
type: 'success',
})
let length = this.allStops.length
if (this.allStops.length > 1) {
// 实体路线
viewer.entities.add({
id: 'fly_line_' + (this.allStops.length + 1),
polyline: {
positions: [
this.allStops[length - 2].position,
this.allStops[length - 1].position,
],
width: 5,
material: Cesium.Color.BLUE,
},
show: this.line_bool,
})
}
/* 动态点参数 */
let x = 0
let size = 10
let isAdd = true
let isZoom = true
viewer.entities.add({
position: this.allStops[length - 1].position,
point: {
color: new Cesium.CallbackProperty(function () {
if (isAdd) {
x = x + 0.05
if (x > 1) {
isAdd = false
}
} else {
x = x - 0.05
if (x < 0) {
isAdd = true
}
}
return Cesium.Color.fromCssColorString('#ffffff')
}, false),
pixelSize: new Cesium.CallbackProperty(function () {
if (isZoom) {
size = size + 0.5
if (size > 10) {
isZoom = false
}
} else {
size = size - 0.5
if (size < 3) {
isZoom = true
}
}
return size
}, false),
outlineWidth: 1,
outlineColor: new Cesium.CallbackProperty(function () {
return Cesium.Color.fromCssColorString('#ffffff').withAlpha(0.2)
}, false),
disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
id: 'fly_nail_' + (this.allStops.length + 1),
})
},
cleanPlace() {
/**
* @description: 清除所有漫游路线
* @param {*}
* @author: citrusrlia@foxmail.com
*/
/* 清除所有线路,统一重绘 */
for (let i = 0, l = viewer.entities._entities._array.length; i < l; i++) {
if (viewer.entities._entities._array[i].id?.indexOf('fly') > -1) {
viewer.entities.remove(viewer.entities._entities._array[i])
i--
l--
}
}
},
startFly() {
/**
* @description: 开始飞行
* @param {*} this
* @author: citrusrlia@foxmail.com
*/
if (this.allStops.length < 1) {
this.$notify.error({
title: '错误',
message: '请添加至少两个漫游点',
})
return
}
const that = this
const scene = viewer.scene
this.reCalStops()
if (this.tempFlyPoint) {
/* 暂停漫游 继续 */
this.flyIndex = this.tempFlyPoint
this.tempFlyPoint = undefined
if (this.flyIndex < this.allStops.length) {
/* 关闭镜头控制 */
scene.screenSpaceCameraController.enableRotate = false
scene.screenSpaceCameraController.enableTranslate = false
scene.screenSpaceCameraController.enableZoom = false
scene.screenSpaceCameraController.enableTilt = false
scene.screenSpaceCameraController.enableLook = false
viewer.camera.setView({
destination: this.allStops[this.flyIndex - 1].position,
orientation: {
heading: this.allStops[this.flyIndex - 1].heading,
pitch: this.allStops[this.flyIndex - 1].pitch,
roll: this.allStops[this.flyIndex - 1].roll,
},
})
fly()
}
} else {
this.flyIndex = 1
if (this.flyIndex < this.allStops.length) {
scene.screenSpaceCameraController.enableRotate = false
scene.screenSpaceCameraController.enableTranslate = false
scene.screenSpaceCameraController.enableZoom = false
scene.screenSpaceCameraController.enableTilt = false
scene.screenSpaceCameraController.enableLook = false
/* 使用setView定位第一个漫游点 */
viewer.camera.setView({
destination: this.allStops[0].position,
orientation: {
heading: this.allStops[0].heading,
pitch: this.allStops[0].pitch,
roll: this.allStops[0].roll,
},
})
fly()
}
}
function changeCameraHeading(stopIndex, duration = 5) {
/**
* @description: 转向
* @param {*}
* @author: citrusrlia@foxmail.com
*/
viewer.camera.setView({
orientation: {
heading: that.allStops[stopIndex + 1].heading, // 方向
pitch: that.allStops[stopIndex + 1].pitch, // 倾斜角度
},
})
let count = 1
let thisHeadingDegree = Cesium.Math.toDegrees(viewer.camera.heading)
let nextHeadingDegree = Cesium.Math.toDegrees(
that.allStops[stopIndex + 1].heading
)
let angle = nextHeadingDegree - thisHeadingDegree
let perTurn = Cesium.Math.toRadians(angle / duration)
let turningCallback = (time, result) => {
if (count == duration) {
viewer.scene.postRender.removeEventListener(turningCallback)
return
}
viewer.camera.flyTo({
destination: that.allStops[stopIndex + 1].position,
orientation: {
heading: perTurn + viewer.camera.heading,
pitch: that.allStops[stopIndex + 1].pitch,
},
duration: 5,
})
count += 1
}
// viewer.scene.postRender.addEventListener(turningCallback)
}
function fly() {
/**
* @description: 飞行主函数
* @param {*} that
* @author: citrusrlia@foxmail.com
*/
if (that.flyIndex >= that.allStops.length) {
/* 飞行结束 */
scene.screenSpaceCameraController.enableRotate = true
scene.screenSpaceCameraController.enableTranslate = true
scene.screenSpaceCameraController.enableZoom = true
scene.screenSpaceCameraController.enableTilt = true
scene.screenSpaceCameraController.enableLook = true
MyCamera.resetCamera()
} else {
let l = that.flyIndex
if (l >= 1) {
viewer.camera.flyTo({
destination: that.allStops[l].position, // 设置位置
orientation: {
heading: that.allStops[l - 1].heading, // 方向
pitch: that.allStops[l - 1].pitch, // 倾斜角度
roll: that.allStops[l - 1].roll,
},
// easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT,
duration: 5, // 设置飞行持续时间,默认会根据距离来计算
complete: function () {
/* 飞完后执行下一站,调整镜头角度指向下一站 */
if (l + 1 < that.allStops.length) {
changeCameraHeading(l)
}
fly()
},
})
}
that.flyIndex++
}
}
},
// 飞行暂停
pauseFly() {
this.tempFlyPoint = this.flyIndex
this.flyIndex = this.allStops.length + 1
},
// 飞行停止
stopFly() {
this.flyIndex = this.allStops.length + 1
this.tempFlyPoint = undefined
viewer.scene.screenSpaceCameraController.enableRotate = true
viewer.scene.screenSpaceCameraController.enableTranslate = true
viewer.scene.screenSpaceCameraController.enableZoom = true
viewer.scene.screenSpaceCameraController.enableTilt = true
viewer.scene.screenSpaceCameraController.enableLook = true
MyCamera.resetCamera()
},
changeLine() {
viewer.entities._entities._array.forEach((res) => {
if (res.id.indexOf('fly') > -1) {
res.show = !res.show
}
})
this.tempFlyPoint = undefined
}
视频影像融合
通过video标签构建dom实体,将其设为entity的材质对象.
视频融合s.gif
// video标签,设置autoplay和display:none
<video ref="video" autoplay loop crossorigin controls style="display: none">
<source
src="https://cesium.com/public/SandcastleSampleData/big-buck-bunny_trailer.mp4"
type="video/mp4"
/>
</video>
addVideo() {
/**
* @description: 添加视频
* @param {*}
* @author: citrusrlia@foxmail.com
*/
const videoElement = this.$refs.video
viewer.showRenderLoopErrors = false
viewer.shouldAnimate = true
viewer.entities.add({
name: 'video-screen',
id: 'video_screen',
wall: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights([
-74.01595609453216, 40.706172490663725, 120, -74.0162584955552,
40.70537146061003, 120,
]),
material: videoElement,
// clampToGround: true,
},
show: false,
})
}
应急事件
在地图上显示应急事件(应是传感器实时信息),并可以定位,设置影响范围。
定位火源_s.gif
改变火源位置_s.gif
addParticleSystem() {
/**
* @description:添加粒子效果与波纹效果
* @param {*}
* @author: citrusrlia@foxmail.com
*/
const that = this
const viewModel = {
emissionRate: 200.0,
gravity: 0,
minimumParticleLife: 1.5,
maximumParticleLife: 1.8,
minimumSpeed: 7,
maximumSpeed: 9,
startScale: 3.0,
endScale: 1.5,
particleSize: 2,
}
let emitterModelMatrix = new Cesium.Matrix4()
let translation = new Cesium.Cartesian3()
let rotation = new Cesium.Quaternion()
let hpr = new Cesium.HeadingPitchRoll()
let trs = new Cesium.TranslationRotationScale()
that.entity = viewer.entities.add({
position: this.particlePoint,
})
let gravityScratch = new Cesium.Cartesian3()
function computeEmitterModelMatrix() {
//调节粒子的发射方向
hpr = Cesium.HeadingPitchRoll.fromDegrees(0.0, 0.0, 0.0, hpr)
//喷泉位置
trs.translation = Cesium.Cartesian3.fromElements(0, 0, 5.4, translation)
trs.rotation = Cesium.Quaternion.fromHeadingPitchRoll(hpr, rotation)
return Cesium.Matrix4.fromTranslationRotationScale(
trs,
emitterModelMatrix
)
}
/* 重力,火焰特效时关闭,漏水特效时开启 */
function applyGravity(p, dt) {
// We need to compute a local up vector for each particle in geocentric space.
var position = p.position
Cesium.Cartesian3.normalize(position, gravityScratch)
Cesium.Cartesian3.multiplyByScalar(
gravityScratch,
viewModel.gravity * dt,
gravityScratch
)
p.velocity = Cesium.Cartesian3.add(
p.velocity,
gravityScratch,
p.velocity
)
}
function computeModelMatrix(entity, time) {
return entity.computeModelMatrix(time, new Cesium.Matrix4())
}
const particleSystem = viewer.scene.primitives.add(
new Cesium.ParticleSystem({
show: false,
image: 'fire4.png',
startColor: new Cesium.Color(1, 1, 1, 1),
endColor: new Cesium.Color(0.5, 0, 0, 0),
startScale: viewModel.startScale,
endScale: viewModel.endScale,
minimumParticleLife: viewModel.minimumParticleLife,
maximumParticleLife: viewModel.maximumParticleLife,
minimumSpeed: viewModel.minimumSpeed,
maximumSpeed: viewModel.maximumSpeed,
imageSize: new Cesium.Cartesian2(
viewModel.particleSize,
viewModel.particleSize
),
emissionRate: viewModel.emissionRate,
lifetime: 6.0,
//粒子发射器
emitter: new Cesium.SphereEmitter(2.5),
emitterModelMatrix: computeEmitterModelMatrix(),
// updateCallback: applyGravity,
sizeInMeters: true,
performance: false,
})
)
viewer.scene.preUpdate.addEventListener(function (scene, time) {
particleSystem.modelMatrix = computeModelMatrix(that.entity, time)
// Account for any changes to the emitter model matrix.
particleSystem.emitterModelMatrix = computeEmitterModelMatrix()
})
/**
* @description: 添加应急点波纹特效
* @param {*}
* @author: citrusrlia@foxmail.com
*/
let myAxis = 0,
myAxis2 = 0
const waveCallback = new Cesium.CallbackProperty((time, result) => {
myAxis += 1
if (myAxis >= that.maxendAxis) {
myAxis = 1
return 1
}
return myAxis
}, false)
const waveCallback2 = new Cesium.CallbackProperty((time, result) => {
myAxis2 += 1
if (myAxis2 >= that.maxendAxis) {
myAxis2 = 1
return 1
}
return myAxis2
}, false)
viewer.entities.add({
id: 'waveEffect',
show: false,
ellipse: {
clampToGround: true, // 与outline无法一起启用
semiMinorAxis: waveCallback,
semiMajorAxis: waveCallback2,
// outline: true,
// outlineColor: Cesium.Color.RED,
// outlineWidth: 20,
material: Cesium.Color.RED.withAlpha(0.3),
},
position: this.particlePoint,
})
}
changeParticlePoint() {
/**
* @description: 改变粒子效果位置
* @param {*}
* @author: citrusrlia@foxmail.com
*/
const that = this
that.$notify({
title: '变更应急点',
message: '移动鼠标改变位置,点击鼠标右键确认。',
duration: 0,
})
let em_handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
em_handler.setInputAction((e) => {
let ray = viewer.scene.camera.getPickRay(e.endPosition)
let cartesian3 = viewer.scene.globe.pick(ray, viewer.scene)
that.particlePoint = cartesian3
let waveEntity = viewer.entities.getById('waveEffect')
waveEntity.position._value = cartesian3
that.entity.position._value = cartesian3
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
em_handler.setInputAction((e) => {
let ray = viewer.scene.camera.getPickRay(e.position)
let cartesian3 = viewer.scene.globe.pick(ray, viewer.scene)
that.particlePoint = cartesian3
let waveEntity = viewer.entities.getById('waveEffect')
waveEntity.position._value = cartesian3
that.entity.position._value = cartesian3
em_handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
em_handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
}
信息弹窗与边框拾取
点击模型、标签等弹出信息窗口,鼠标滑过模型有边框拾取。
handler_s.gif
init(viewer) {
/**
* @description: handler初始化
* @param {*}
* @author: citrusrlia@foxmail.com
*/
const that = this
this.viewer = viewer
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
this.handler.setInputAction((e) => {
let pickedObject = that.viewer.scene.pick(e.position)
let pickPosition = that.viewer.scene.camera.pickEllipsoid(e.position)
let cartographic = Cesium.Cartographic.fromCartesian(
pickPosition,
that.viewer.scene.globe.ellipsoid,
new Cesium.Cartographic()
)
let lat = Cesium.Math.toDegrees(cartographic.latitude)
let lng = Cesium.Math.toDegrees(cartographic.longitude)
let height = cartographic.height
console.log('[Lng=>' + lng + ',Lat=>' + lat + ',H=>' + height + ']')
/* 选择模型 */
if (pickedObject && pickedObject.content) {
that.lastPickedObject &&
(that.lastPickedObject.color = that.lastPickedColor)
that.lastPickedObject = pickedObject
that.lastPickedColor = pickedObject.color
pickedObject.color = Cesium.Color.RED
} else {
that.lastPickedObject &&
(that.lastPickedObject.color = that.lastPickedColor)
that.lastPickedObject = null
that.lastPickedColor = null
}
if (
that.viewer.scene.pickPositionSupported &&
Cesium.defined(pickedObject) &&
pickedObject.id
) {
/* todo 判断是否点击primitive */
if (pickedObject.id.constructor == String) {
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
/* 3Dtile蓝色边框 */
const silhouetteBlue =
Cesium.PostProcessStageLibrary.createEdgeDetectionStage()
silhouetteBlue.uniforms.color = Cesium.Color.BLUE
silhouetteBlue.uniforms.length = 0.01
silhouetteBlue.selected = []
const silhouetteGreen =
Cesium.PostProcessStageLibrary.createEdgeDetectionStage()
silhouetteGreen.uniforms.color = Cesium.Color.LIME
silhouetteGreen.uniforms.length = 0.01
silhouetteGreen.selected = []
viewer.scene.postProcessStages.add(
Cesium.PostProcessStageLibrary.createSilhouetteStage([
silhouetteBlue,
silhouetteGreen,
])
)
// Information about the currently selected feature
const selected = {
feature: undefined,
originalColor: new Cesium.Color(),
}
// An entity object which will hold info about the currently selected feature for infobox display
const selectedEntity = new Cesium.Entity()
this.handler.setInputAction((e) => {
silhouetteBlue.selected = []
var pickedFeature = viewer.scene.pick(e.endPosition)
if (pickedFeature !== selected.feature) {
silhouetteBlue.selected = [pickedFeature]
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
// this.initMeasure()
return this
}
四、 结语
git地址:https://gitee.com/han_wu_xian/cesium-demo.git
本例使用数据均开源合法,如果这篇文章对你有一些帮助,那我会很开心😋
网友评论