平面GIS中,点击地图出气泡是非常普遍的操作。三维地图框架Cesium里面,当然也支持,只不过样式和内容不一定符合我们要求就是了。
Cesium是默认支持弹出气泡的,只要在初始化时不明确将infoBox置为false,点击地图都会弹出气泡:
var viewer = new Cesium.Viewer("cesiumViewer",{
imageryProvider : new Cesium.ArcGisMapServerImageryProvider({
url:"http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer"//午夜
}),
//点击后显示详细信息
infoBox: true,
……
});
但如上所述,气泡的样式和内容,在实际应用中,我们需要进行设置。但问题是,在运行过程中,viewer初始化后,默认的infoBox是只读的,样式设置无效,内容也添加不进去。怎么办?这时候需要隐藏默认的infoBox,然后构造新的infoBox。
一、隐藏默认infoBox,构造新infoBox
默认的infoBox是只读的,可以说任何设置对它都无效。那如何隐藏?方法是设置infoBox对应的css样式,添加一个display=none的属性。然后再构造一个新的infoBox。
<style>
.cesium-infoBox.hidden{
display:none!important;
}
</style>
//得到当前默认的infoBox
var curInfoBox = $(".cesium-infoBox");
//隐藏
curInfoBox.addClass("hidden");
//找到infoBox所在容器
var container = document.getElementsByClassName("cesium-viewer-infoBoxContainer")[0];
if(!container){//如果容器不存在,则构造一个。不存在的情况,估计是viewer初始化时,infoBox=false
container = document.createElement('div');
container.className = 'cesium-viewer-infoBoxContainer';
viewer.container.appendChild(container);
}
//构造infoBox
var infoBox = new Cesium.InfoBox(container);
viewModel = infoBox.viewModel;//只要设置这个viewModel,就能控制气泡的显隐
viewModel.closeClicked.addEventListener(function(){//点击气泡右上角的关闭按钮
viewModel.showInfo = false;//关闭气泡
});
二、点击时,设置内容并弹出infoBox
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
let pick = viewer.scene.pick(movement.position);
if (Cesium.defined(pick)) {
setView(pick);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);//鼠标左键点击
function setView(pick){//展示气泡
//pick是被点击的对象。pick.id不是一个数值,而是一个json对象,它会自动将被点击对象的属性包含在内。我们可以将关键性信息存放在这里。
let name = pick.id.name;
if(name == 'node'){
showNode();
} else if(name == 'fnode'){
。。。
}
viewModel.showInfo = true;//显示气泡
}
function showNode(){//装配气泡
let n = ...;
if(n){
viewModel.titleText = n.cname + "(" + n.code + ")";//气泡标题
let nd = n.nd;
let wc = nd.windCircle;
let desc = TIPSTYLE + getTplNode(nd,wc);//气泡具体内容,见下面
viewModel.description = desc;
}
let TIPSTYLE = `<style>
.tfhidden{visibility:hidden;}
t{width:120px;text-align:right;font-weight:bold;margin-left:3px;margin-right:7px;color:#ccc;}
table,th,td{font-size:12px!important}
th{color:#ccc;}
</style>`;
function getTplNode(nd,wc){
return `<div><t>过去时间:</t>${getDateStr(nd.date)}</div>
<div><t>中心位置:</t>${nd.y}N/${nd.x}E</div>
<div><t>最大风速:</t>${nd.maxWindSpeed}米/秒</div>
<div><t>中心气压:</t>${nd.centerPower}百帕</div>
<div><t>移动方向:</t>${nd.direction}</div>
<div><t>移动速度:</t>${nd.speed}公里/小时</div>
<div><t>类<span class='tfhidden'>类型</span>型:</t>${nd.type.name}</div>
<div><table><thead><tr><th>风圈半径</th><th>东北</th><th>东南</th><th>西南</th><th>西北</th><th style='width:25px;'></th></tr></thead>
<tr><td>7级</td><td>${wc.c7.EN||''}</td><td>${wc.c7.ES||''}</td><td>${wc.c7.WS||''}</td><td>${wc.c7.WN||''}</td><td>KM</td></tr>
<tr><td>10级</td><td>${wc.c10.EN||''}</td><td>${wc.c10.ES||''}</td><td>${wc.c10.WS||''}</td><td>${wc.c10.WN||''}</td><td>KM</td></tr>
<tr><td>12级</td><td>${wc.c12.EN||''}</td><td>${wc.c12.ES||''}</td><td>${wc.c12.WS||''}</td><td>${wc.c12.WN||''}</td><td>KM</td></tr>
</table></div>`;
}
}
//某对象,鼠标点击它出气泡
function getNode(nd,index,tfId){
var point = Cesium.Cartesian3.fromDegrees(nd.x, nd.y,HEIGHT);
return viewer.entities.add({//以下属性,都会在pick.id里出现
id : 'n-' + tfId + '-' + index,
name : 'node',
position : point,
point : {
pixelSize: 10,
...
}
});
}
效果见下图右上角
三、简易提示
气泡点击时弹出。但动静比较大,我们可以设置鼠标划过一些对象时,弹出一些简易的提示,如上图下方。
简易提示主要使用 Label。用法也比较简单:
function TipLabel(td){//小提示
let scene = viewer.scene;
let label = getLable();
let handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
var pick = viewer.scene.pick(movement.endPosition);
if (pick && Cesium.defined(pick)) {
show(pick,movement);
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
function show(pick,movement) {
var cartesian = viewer.camera.pickEllipsoid(movement.endPosition,scene.globe.ellipsoid);
if (cartesian) {
let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
let longitudeString = Cesium.Math.toDegrees(cartographic.longitude).toFixed(2);
let latitudeString = Cesium.Math.toDegrees(cartographic.latitude).toFixed(2);
let d = td.point(pick.id.id);
label.position = cartesian;
label.label.show = true;
if(d.type == 'c'){
label.label.text = getTplCircleSimple(d.nd,d.wc);
} else if(d.type == 'f'){
...
} else {
...
}
}
}
function getTplCircleSimple(nd,wcLevel){
let wc = nd.windCircle;
let wn = wc['c' + wcLevel].WN||'';
let en = wc['c' + wcLevel].EN||'';
let ws = wc['c' + wcLevel].WS||'';
let es = wc['c' + wcLevel].ES||'';
return `时 间:${getDateStr(nd.date)}\n风圈级别:${wcLevel}级\n风圈半径:\n西北${wn}公里 | 东北${en}公里\n-------------------------\n西南${ws}公里 | 东南${es}公里`;
}
function getLable() {//构造一个label
return viewer.entities.add({
label: {
show: false,
showBackground: true,
font: "14px monospace",
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.TOP,
pixelOffset: new Cesium.Cartesian2(5, 0),
eyeOffset: new Cesium.Cartesian3(0.0, 0.0, 45000.0),
},
});
}
function hideLabel(){//隐藏简易提示
label.label.show = false;
}
}