vue3.2+ts+cesium制作3DGIS效果的大屏(下)

cesium用于三维可视化,在城市场景 工业场景 巡航等场景用处多,接下来我们初始地址是图五的效果,我们镜头跟随视角函数flyto来到山西阳泉,定位其中的一个建筑并正射这个建筑,给这个建筑打上tip控制相机视角高度为2000,我们从图一的效果执行代码

viewer.camera.flyTo( {
  destination: Cesium.Cartesian3.fromDegrees(113.572642,37.86694,2000),//山西阳泉
  orientation: {
    heading: Cesium.Math.toRadians(0),
    pitch: Cesium.Math.toRadians(-90),
    roll: Cesium.Math.toRadians(0 ),
    range:10000
  }});

然后我们定位到了我们所需要的位置,然后我们设置tip,并给tip设置开启关闭气泡弹窗功能(tip的箭头太丑了没找到合适的),此后视角无论如何转动 弹窗都跟随你的镜头

我们写一个tip实体类  里面inage属性可以放base64  我没有图片就用的自带的蓝色图标,总体代码如下  只需实列化一个对象,生产tip调用addEntity函数,参数传入弹窗所需要的数据

export default class DragEntity{
    constructor(val,Cesium){
        this.viewer = val.viewer
        this.Cesium = Cesium
    }
    addEntity(value){
        let pinBuilder = new this.Cesium.PinBuilder();
        let poin = this.viewer.entities.add({
            id:value.id,
            name: value.name,
            position: this.Cesium.Cartesian3.fromDegrees(parseFloat(value.position.x), parseFloat(value.position.y)),
            billboard: {
                image:  pinBuilder.fromColor(this.Cesium.Color.ROYALBLUE, 48).toDataURL(),
                verticalOrigin: this.Cesium.VerticalOrigin.BOTTOM,
            },
            monitoItems:{
                data:value
            },
        });
        return poin
    }

}

然后简单封装一个fetch对象函数作为我们接口的调用,大屏展示接口暂时只有get

export default function (url:string,method:string = "get",data?:Array|Object) {
    let headers:{ Authorization: string; "tenant-id": string }={
        "Authorization": opener["filter"]["Authorization"],
        "tenant-id":  opener["filter"]["tenant-id"]+""
    }
    return fetch(url, {headers,method}).then(
        (response:Response)=>{
        if(response.status === 200){
            return response.json();
        }else{
            return {}
        }
    },(error)=>{
        return {}
    })
}

在setup后面我们定义一些数据和一些响应式数据

let viewer:any=null
let map:any=null
let citys:Array=[]
let views:Array=[]
let twinkles:Array=[]
interface ttcObject { value?: boolean }
interface msgObject { value?: string|number }
interface videoP extends Object {switch: boolean; readonly type: string}
const alarmRankDataMax:number=232;
const devs: Array = reactive([])
let ttc:ttcObject=ref(false)
let msg:msgObject=ref("");
let poin:any[] =  []
let poinEntity:any={}
let poinId: string[]=[]
let bubbleBubbles: any={}

在我们实例化地球之后 我们给初始地球加载自转效果并且切换2dgis图的时候关闭效果否则会报错


window.onload=()=>{ document.oncontextmenu = (event:any)=> false }
map=new GlobeRotate(viewer)
map.start()
let drag = new DragEntity({ viewer },Cesium)
viewer.sceneModePicker.viewModel.morphTo2D.afterExecute.addEventListener(() => {    map.stop()  });
viewer.sceneModePicker.viewModel.morphToColumbusView.afterExecute.addEventListener(() => {    map.stop()  });

然后我们读取接口批量添加tip,此时tip以及添加完成了,我们根据id存储起来这些tip,

  
  api(ip).then((data:any)=>{
    if(data.code===0){
      data.data.forEach((res:any,index:number)=>{
        if(res.data.longitude && res.data.latitude)
        poin.push({
          id:res.data.id+'' ,
          name: res.data.stationName,
          type: res.data.stationType,
          devs: res.data.deviceCount,
          date: res.data.operationTime,
          fzr: res.data.stationCharge,
          phone: res.data.contactInformation,
          position: { x: res.data.longitude, y: res.data.latitude} ,
          text:"*"
        })
      })
      poin.forEach((item:any) => {
        poinEntity[item.id] = drag.addEntity(item);
      })
    }
  })

如需要ws接口,根据推送告警 用id改变tip效果的代码如下,变更成红色

  if ("WebSocket" in window) {
    const ws = WebSocket && (new WebSocket(ip))
    ws.onmessage=res=>{
      (JSON.parse(res.data)||[]).forEach((data:any)=>{
        poinEntity[data.stationId].billboard.image=new Cesium.PinBuilder().fromColor(Cesium.Color.RED, 48).toDataURL();
        poinEntity[data.stationId].monitoItems.data.alarmType="告警"
      })
    }
  }else{
    ElMessageBox.alert( "当前浏览器不支持数据推送信息,请更新最新谷歌浏览器", '信息', { dangerouslyUseHTMLString: true, } )
  }

然后设置我们右键tip的点击时间和左键的笛卡尔信息查询以及清除双击事件带来的放大效果

let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  const ldk = Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK;
  viewer.screenSpaceEventHandler.removeInputAction(ldk);
  handler.setInputAction((clickEvent:any) => { }, ldk)
  const lk = Cesium.ScreenSpaceEventType.LEFT_CLICK;
  viewer.screenSpaceEventHandler.removeInputAction(lk);
  handler.setInputAction((clickEvent:any) => {
    viewer.scene.mode === 3 && map.stop()
    let pick = viewer.scene.pick(clickEvent.position);
    if (Cesium.defined(pick) && (pick.id.id) ) {
      let id= pick.id.id;
      if(poinId.indexOf(id)<0 || poinId.length===0){
        poinId.push(id)
        bubbleBubbles[id] = new Bubble(Object.assign(poinEntity[id],{ viewer }))
      }else if(poinId.indexOf(id) !== -1){
        bubbleBubbles[id].vmInstance.$el.remove();
        delete poinId[poinId.indexOf(id)]
      }
    }
  }, lk)
  const rk = Cesium.ScreenSpaceEventType.RIGHT_CLICK;
  handler.removeInputAction(rk);
  handler.setInputAction((e:any) => {
    let Cartesian3D=viewer.camera.pickEllipsoid(e.position,viewer.scene.globe.ellipsoid)
    if(Cartesian3D){
      let cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(Cartesian3D);
      let lat = Cesium.Math.toDegrees(cartographic.latitude).toFixed(8);
      let lng = Cesium.Math.toDegrees(cartographic.longitude).toFixed(8);
      let height = (viewer.camera.positionCartographic.height).toFixed(2);
      ElMessageBox.alert(
          ` 笛卡尔坐标${Cartesian3D}
定位坐标${e.position}
地理坐标:
经度:${lng}
纬度:${lat}
高度:${height}米`, '信息', { dangerouslyUseHTMLString: true, } ) } }, rk)

我们点击出弹窗效果 由于vue3没有extend函数我们用createapp来代替

import { createApp, h } from "vue";
import Label from "./index.vue";
export default class Bubble {
    constructor(val) {
        this.viewer = val.viewer;
        this.position = val.position._value;
        let dataVal = val.monitoItems.data;
        const parent = document.createElement('div')
        const app = createApp({ render() { return  h(Label) }})
        this.vmInstance = app.mount(parent)
        /*这里写入点击tip带来的参数(dataVal)写入生成的组件里面 方式很多用组件的传参或者el等等 都可以*/
        val.viewer.cesiumWidget.container.appendChild(this.vmInstance.$el);
        this.addPostRender();
    }
    addPostRender() {
        this.viewer.scene.postRender.addEventListener(this.postRender, this);
    }
    postRender() {
        if (!this.vmInstance.$el || !this.vmInstance.$el.style) return;
        const canvasHeight = this.viewer.scene.canvas.height;
        const windowPosition = new Cesium.Cartesian2();
        Cesium.SceneTransforms.wgs84ToWindowCoordinates(
            this.viewer.scene,this.position,windowPosition
        );
        this.vmInstance.$el.style.bottom = canvasHeight - windowPosition.y  +260+ "px";
        const elWidth = this.vmInstance.$el.offsetWidth;
        this.vmInstance.$el.style.left = windowPosition.x - elWidth / 2 +110 + "px";
        const camerPosition = this.viewer.camera.position;
        let height = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(camerPosition).height;
        height += this.viewer.scene.globe.ellipsoid.maximumRadius;
        if((!(Cesium.Cartesian3.distance(camerPosition,this.position) > height))&&this.viewer.camera.positionCartographic.height<50000000){
            this.vmInstance.$el.style.display = "block";
        }else{
            this.vmInstance.$el.style.display = "none";
        }
    }
}

然后就是弹窗的组件由上一个类进行引入




然后我们对于大屏中应用cesium 3dgis描述的效果已经全部完成了  之后我们可以把已完成3dgis效果封成组件给我们大屏界面进行调用

后续对于模型效果精细化 使用vue3写一个cesium模型的调整的编辑器 再会另写 关于地形模型 漫游 栅格层 雷达到等效果文章

本文完结,如需要源码或者效果指导请找我描述

你可能感兴趣的:(GIS,typescript,javascript,前端)