平面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;
  }
}                










