cesium.js三维城市建筑物巡视开发

cesium.js三维城市建筑物巡视开发

一、简介

这个项目基于cesium.js和已有模型,在地图上搭建起了一座完整的建筑物,并实现了初步的用户交互,可以查看单独每一层楼内的线路结构。

项目链接https://gitee.com/hiMyon/city3d/tree/master

二、实现过程

首先参照项目链接中的readme安装nodejs和http-server,搭建环境,然后进入下一步。

(一)加载cesium.js视图

var viewer = new Cesium.Viewer('cesiumContainer', {
    shadows: false,
    shouldAnimate: true,
    animation:false,       //是否显示动画控件
    homeButton:true,       //是否显示home键
    //geocoder:false,         //是否显示地名查找控件        如果设置为true,则无法查询
    baseLayerPicker:true, //是否显示图层选择控件
    timeline:false,        //是否显示时间线控件
    fullscreenButton:true, //是否全屏显示
    scene3DOnly:true,     //如果设置为true,则所有几何图形以3D模式绘制以节约GPU资源
    infoBox:true,         //是否显示点击要素之后显示的信息
    sceneModePicker:false,  //是否显示投影方式控件  三维/二维
    navigationInstructionsInitiallyVisible:false,
    navigationHelpButton:false,     //是否显示帮助信息控件
    selectionIndicator:false,        //是否显示指示器组件
    imageryProvider : new Cesium.UrlTemplateImageryProvider({
        url: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
    })
});
viewer._cesiumWidget._creditContainer.style.display = "none";
viewer.scene.globe.enableLighting = false;

(二)加载模型

var entity;
var index = 1;
//加载四层地下
for (var i = 1; i < 5; i+=1) {
    var model = database.assets[i];
    index = addModel(model, "underground-", index);
} 
index = 1;
//加载四层裙楼
for (var i = 5; i < 9; i+=1) {
    var model = database.assets[i];
    index = addModel(model, "podium-", index);
} 
index = 5;
//加载A栋
for (var i = 9; i < 15; i+=1) {
    var model = database.assets[i];
    index = addModel(model, "A-", index);
}
index = 5;
//加载B栋
for (var i = 15; i < 17; i+=1) {
    var model = database.assets[i];
    index = addModel(model, "B-", index);
}
index = 5;
//加载C栋
for (var i = 17; i < 19; i+=1) {
    var model = database.assets[i];
    index = addModel(model, "C-", index);
}
viewer.trackedEntity = viewer.entities.getById('podium-1');

加载一个模型需要得到以下信息:

  • 模型本地资源的url
  • 模型在地图上的哪个显示图层上
  • 模型在地图上的位置,可以用经纬度和地面高度表示
  • 模型的地图上旋转的大小
  • 模型在地图上放缩的尺度

为了方便,我们将模型的以上信息统一存储在json文件中,比如地下一层可以这样表示:

{   
    "model_name": "building_ug1",
    "level": 2,
    "position": [113.34700, 23.00578, 18],
    "rotation": [55, 0, 0],
    "scale": 1, 
},

然后就可以在代码中进行加载:

//position三个参数分别是经度、纬度和离地面高度
var position = Cesium.Cartesian3.fromDegrees(model.position[0], model.position[1], model.position[2] + j * height);
var hpr = new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(model.rotation[0]), Cesium.Math.toRadians(model.rotation[1]), Cesium.Math.toRadians(model.rotation[1]));
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
//某个模型可能同时表示好几层,但是id和name要将它们区分开来
entity = viewer.entities.add({
    id: prefix + index,
    name: prefix + index,
    position: position,
    orientation: orientation,
    model: {
        uri: "models/glb/" + model.model_name + ".glb",
        scale: model.scale
    }
});

cesium.js加载模型的方式是调用viewer.entities.add方法,传入该模型的位置、尺度、旋转等信息,为了区分每一层楼,在加入时为它们设置一个唯一的id,name是模型在地图上显示的信息,可以设置和id相同。

每层楼中有不同的线路图,这些线路图同样是一些模型,可以写在json中,这里我模仿了3d-tiles的结构,将每层楼的线路模型作为该层楼模型的子结构,这样,如果给地下一层加入火线,其新表示为:

{   
    "model_name": "building_ug1",
    "level": 2,
    "position": [113.34700, 23.00578, 18],
    "rotation": [55, 0, 0],
    "scale": 1,
    "fire": [{   
                "model_name": "fire_fUg1",
                "level": 2,
                "position": [113.35070, 23.00815, 19],
                "rotation": [55, 0, 0],
                "scale": 1
            }],
},

电线和水线加入的方式与火线相同。

现在考虑更复杂一点的情况,比如有一个模型building_A5-10,它是一个只有一层的模型,但是我们自下往上重复搭建六次则可以搭出5到10层,这六个实体在地图上的区别除了名字和id外就只有离地高度了,这种情况如果在json中用六个结构体来表示就过于冗余了,只需在结构体中增加一个变量times,表示这个模型被重复搭建几次;顺带一提,还有一些模型如fire_fB15-21odd,它表示B栋15到21层的奇数层,所以从下往上搭建时要搭一层跳过一层,通过给结构体增加一个height变量表示每一层的高度,将这个值设置为正常高度的两倍即可。

所有的火线和电线都已经加到json数据中,由于水线模型很难看清以及判断摆放的位置,我只在地下四层中放置了水线,之后可以再修改。

(三)初步用户交互

这里的交互指的是用户可以单独查看每一层楼和楼内部的线路,以及控制线路的显示与否,操作方法在gitee项目的readme中有介绍。

实现其实也挺简单,就是给handler添加action,比如添加鼠标双击动作的响应:

var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(e){
    var pick = viewer.scene.pick(e.position);
    if (Cesium.defined(pick)){
        //alert('选中:'+ pick.id.name);
        console.log('选中' + pick.id.id);
        choosing_one = pick.id;
        viewer.entities.getById(pick.id.id + '-fire').show = true;
        
        for (let id of entityId_array) {
            var entity = viewer.entities.getById(id);
            entity.show = false;
        }
        pick.id.show = true; 
        if (show_fire) {
            viewer.entities.getById(pick.id.id + '-fire').show = true;
        }
        if (show_electricity) {
            viewer.entities.getById(pick.id.id + '-electricity').show = true;
        }
        if (show_water) {
            viewer.entities.getById(pick.id.id + '-water').show = true;
        }
        
        viewer.trackedEntity = pick.id;
            
    }
},Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

根据鼠标的点击位置得到被点中的模型实体,即pick,接着隐藏其他实体,切换到该层视角上,然后根据条件判断是否显示该层的某一种线路。

三、效果展示

cesium.js三维城市建筑物巡视开发

cesium.js三维城市建筑物巡视开发

上一篇:cesium去除控件及版权信息


下一篇:cesium首次加载gltf模型成功