这篇笔记主要记录
CesiumJS
打点,悬浮点位显示点位信息。 点位上图详解可以看另一篇笔记📒
实现上图效果,鼠标悬浮到江西省九江市
那个点位时,提示这是庐山国家公园,主要就是利用 ScreenSpaceEventType.MOUSE_MOVE
事件,获取点位信息,然后创建一个div,自定义样式把它渲染出来。
- 添加点位。
- 添加悬浮事件,获取点位信息
- 创建div,自定义样式把点位信息渲染出来
- 总结下用到的知识点关键词
- 完整代码demo 可直接运行 不用安装依赖
容易踩的坑:自定义div渲染,div布局一定要是相对于地图容器的(cesiumContainer),而不能是整个dom结构的body,html元素
添加点位
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>国家公园分布</title>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Cesium.js"></script>
<style>
@import url('https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Widgets/widgets.css');
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
Cesium.Ion.defaultAccessToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzZmM4MWYwYS0xOWM3LTQ0ZWEtYTUzNC1mMWI3ODAyODA1ZmYiLCJpZCI6NDQ2OTEsImlhdCI6MTYxNDI0NDk1OX0.5wce5JelLgCOVQ513YI9QtLDuqTA_L9Y0u_s2oFkWR4'
// 初始化
const viewer = new Cesium.Viewer('cesiumContainer')
let billboards = viewer.scene.primitives.add(
new Cesium.BillboardCollection()
)
// 要上图的点位列表
const nationalParks = [
{ name: '九寨沟国家公园', lat: 33.2554, lon: 103.918 },
{ name: '黄山国家公园', lat: 30.1313, lon: 118.1689 },
{ name: '张家界国家森林公园', lat: 29.3456, lon: 110.4859 },
{ name: '武陵源风景区', lat: 29.3472, lon: 110.5485 },
{ name: '亚丁自然保护区', lat: 29.0572, lon: 100.1852 },
{ name: '庐山国家公园', lat: 29.5803, lon: 116.0296 },
{ name: '神农架国家公园', lat: 31.592, lon: 110.4872 },
{ name: '武夷山', lat: 27.7271, lon: 117.6783 },
{ name: '长白山国家自然保护区', lat: 42.0116, lon: 128.0588 },
{ name: '普达措国家公园', lat: 27.8267, lon: 99.9885 },
]
// 点位上图
const addBillboard = (visibility = true) => {
nationalParks.forEach((park, i) => {
const billboard = billboards.add({
position: Cesium.Cartesian3.fromDegrees(park.lon, park.lat),
image: 'http://127.0.0.1:5501/yxkj.png',
scale: 0.4, // Adjust scale to maintain image quality
show: visibility,
id: {
data: {
name: park.name,
description: 'National Park ' + i,
},
},
})
})
}
addBillboard()
// 设置视角可以看到所有国家公园
const lushanCoordinates = {
lat: 29.5803,
lon: 116.0296,
height: 2915000, // Adjust height to get a better view
}
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(
lushanCoordinates.lon,
lushanCoordinates.lat,
lushanCoordinates.height
),
orientation: {
heading: Cesium.Math.toRadians(0.0), // Heading in degrees
pitch: Cesium.Math.toRadians(-90.0), // Pitch in degrees
roll: 0.0, // Roll in degrees
},
})
</script>
</body>
</html>
这样就实现点位上图了,点位上图解释可以看另一篇笔记📒
添加悬浮事件,获取点位信息
因为我们是需要悬浮显示点位信息,所以要添加MOUSE_MOVE
事件,
我们怎么判断悬浮的是不是点位呢?
cesium的scene
有一个pick
方法,看文档
传入窗口坐标,如果有点位,就会返回该坐标的第一个点位,没有就返回undefined
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
handler.setInputAction(function (movement) {
const pickedObject = viewer.scene.pick(movement.endPosition)
console.log(pickedObject, 'pickedObject')
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
动图展示--悬浮点位可以获取点位信息,悬浮没有点位的地方就显示undefined.
因为空白地方viewer.scene.pick(movement.endPosition)返回是undefined
, 再结合Cesium.defined(value)
可以检测是不是悬浮到了点位,只有Cesium.defined(value)返回true,才说明悬浮非空白区域,再加上pickedObject.primitive instanceof Cesium.Billboard
,悬浮的是billboard类型,就是我们要的悬浮点位类型
if (
Cesium.defined(pickedObject) &&
pickedObject.primitive instanceof Cesium.Billboard
) {
}
Cesium.defined(value)
文档链接
封装了一个事件类,其中的tooltip悬浮提示待会儿用到
class CesiumEventHandler {
constructor(viewer, tooltip) {
this.viewer = viewer
this.tooltip = tooltip
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
this.initEventHandlers()
}
initEventHandlers() {
// 悬浮事件
this.handler.setInputAction(
this.handleMouseMove.bind(this),
Cesium.ScreenSpaceEventType.MOUSE_MOVE
)
}
handleMouseMove(movement) {
const pickedObject = this.viewer.scene.pick(movement.endPosition)
// 判断悬浮点位
if (Cesium.defined(pickedObject) && pickedObject.primitive instanceof Cesium.Billboard) {
// 鼠标变成可点击状态
this.viewer.canvas.style.cursor = 'pointer'
const name = pickedObject.id.data.name
const canvasPosition = movement.endPosition
this.tooltip.show(name, canvasPosition)
} else {
this.viewer.canvas.style.cursor = 'default'
this.tooltip.hide()
}
this.viewer.scene.requestRender()
}
}
创建div,自定义样式把点位信息渲染出来
数据也拿到了,现在就是创建一个div渲染出来,没有用cesium的Cesium.LabelCollection
,那个没法自定义样式,比如加个渐变背景。创建div的代码,
class Tooltip {
constructor(container) {
this.container = container
this.tooltip = document.createElement('div')
this.tooltip.className = 'cesium-tooltip'
this.container.appendChild(this.tooltip)
}
// 显示提示信息 参数 显示的内容和位置
show(content, position) {
this.tooltip.style.display = 'block'
// 提示信息位置在鼠标的位置(点位的位置)向上20px
this.tooltip.style.left = `${position.x}px`
this.tooltip.style.top = `${position.y - 20}px` // 20px offset to position above the cursor
this.tooltip.innerHTML = content
}
// 隐藏提示信息
hide() {
this.tooltip.style.display = 'none'
}
}
tooltip接受一个容器作为参数,这个参数就是地图的dom节点, 布局要以地图参考,因为我们获取的坐标位置就是在地图画布的坐标
再看下前面的,拿到了数据,也有了位置,调用tooltip的show
和hide
就行了。
const name = pickedObject.id.data.name
const canvasPosition = movement.endPosition
this.tooltip.show(name, canvasPosition)
总结下用到的知识点关键词
- 事件处理器 -
new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
- 获取悬浮位置是否有点位
- 2.1
viewer.scene.pick(movement.endPosition)
- 2.2
Cesium.defined(pickedObject)
- 2.3
pickedObject.primitive instanceof Cesium.Billboard
- 2.1
- 悬浮提示的位置要是交互的位置
this.tooltip.style.left =
${position.x}px`
最后的最后,如果发现悬浮提示没出来,记得查看是否加了,自定义悬浮框样式
.cesium-tooltip {
position: absolute;
z-index: 2;
padding: 10px;
background: linear-gradient(
to right,
rgba(0, 123, 255, 0.75),
rgba(0, 80, 160, 0.75)
);
color: white;
border-radius: 5px;
pointer-events: none;
display: none;
transform: translate(
-50%,
-100%
); /* 这个属性使悬浮提示位于点位居中、 朝上的位置 */
}
完整代码demo,可以直接运行看效果
一个html
文件,,不用安装依赖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>国家公园分布</title>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Cesium.js"></script>
<style>
@import url('https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Widgets/widgets.css');
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.cesium-tooltip {
position: absolute;
z-index: 2;
padding: 10px;
background: linear-gradient(
to right,
rgba(0, 123, 255, 0.75),
rgba(0, 80, 160, 0.75)
);
color: white;
border-radius: 5px;
pointer-events: none;
display: none;
transform: translate(
-50%,
-100%
); /* 这个属性使悬浮提示位于点位居中、 朝上的位置 */
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
Cesium.Ion.defaultAccessToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzZmM4MWYwYS0xOWM3LTQ0ZWEtYTUzNC1mMWI3ODAyODA1ZmYiLCJpZCI6NDQ2OTEsImlhdCI6MTYxNDI0NDk1OX0.5wce5JelLgCOVQ513YI9QtLDuqTA_L9Y0u_s2oFkWR4'
const viewer = new Cesium.Viewer('cesiumContainer')
let billboards = viewer.scene.primitives.add(
new Cesium.BillboardCollection()
)
const nationalParks = [
{ name: '九寨沟国家公园', lat: 33.2554, lon: 103.918 },
{ name: '黄山国家公园', lat: 30.1313, lon: 118.1689 },
{ name: '张家界国家森林公园', lat: 29.3456, lon: 110.4859 },
{ name: '武陵源风景区', lat: 29.3472, lon: 110.5485 },
{ name: '亚丁自然保护区', lat: 29.0572, lon: 100.1852 },
{ name: '庐山国家公园', lat: 29.5803, lon: 116.0296 },
{ name: '神农架国家公园', lat: 31.592, lon: 110.4872 },
{ name: '武夷山', lat: 27.7271, lon: 117.6783 },
{ name: '长白山国家自然保护区', lat: 42.0116, lon: 128.0588 },
{ name: '普达措国家公园', lat: 27.8267, lon: 99.9885 },
]
class Tooltip {
constructor(container) {
this.container = container
this.tooltip = document.createElement('div')
this.tooltip.className = 'cesium-tooltip'
this.container.appendChild(this.tooltip)
}
show(content, position) {
this.tooltip.style.display = 'block'
this.tooltip.style.left = `${position.x}px`
this.tooltip.style.top = `${position.y - 20}px` // 20px offset to position above the cursor
this.tooltip.innerHTML = content
}
hide() {
this.tooltip.style.display = 'none'
}
}
class CesiumEventHandler {
constructor(viewer, tooltip) {
this.viewer = viewer
this.tooltip = tooltip
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
this.initEventHandlers()
}
initEventHandlers() {
this.handler.setInputAction(
this.handleMouseMove.bind(this),
Cesium.ScreenSpaceEventType.MOUSE_MOVE
)
}
handleMouseMove(movement) {
const pickedObject = this.viewer.scene.pick(movement.endPosition)
if (Cesium.defined(pickedObject) && pickedObject.primitive instanceof Cesium.Billboard) {
this.viewer.canvas.style.cursor = 'pointer'
const name = pickedObject.id.data.name
const canvasPosition = movement.endPosition
this.tooltip.show(name, canvasPosition)
} else {
this.viewer.canvas.style.cursor = 'default'
this.tooltip.hide()
}
this.viewer.scene.requestRender()
}
}
const tooltip = new Tooltip(document.getElementById('cesiumContainer'))
const cesiumEventHandler = new CesiumEventHandler(viewer, tooltip)
const addBillboard = (visibility = true) => {
nationalParks.forEach((park, i) => {
const billboard = billboards.add({
position: Cesium.Cartesian3.fromDegrees(park.lon, park.lat),
image: 'http://127.0.0.1:5501/yxkj.png',
scale: 0.4, // Adjust scale to maintain image quality
show: visibility,
id: {
data: {
name: park.name,
description: 'National Park ' + i,
},
},
})
})
viewer.scene.requestRender()
}
addBillboard()
// Center the camera on Lushan National Park
const lushanCoordinates = {
lat: 29.5803,
lon: 116.0296,
height: 2915000, // Adjust height to get a better view
}
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(
lushanCoordinates.lon,
lushanCoordinates.lat,
lushanCoordinates.height
),
orientation: {
heading: Cesium.Math.toRadians(0.0), // Heading in degrees
pitch: Cesium.Math.toRadians(-90.0), // Pitch in degrees
roll: 0.0, // Roll in degrees
},
})
</script>
</body>
</html>