Cesium 实战 - 气泡框跟随飞行

news2024/10/7 6:49:16

Cesium 实战 - 气泡框跟随飞行

    • Cesium 气泡框
    • 跟随气泡框
    • 在线示例

气泡框在地图中非常常用,尤其是二维地图中;而在三维地图中经常会用广告牌、标牌等作为气泡框使用。

广告牌(billboard)虽然方便,但是不支持富文本,样式比较一般,因此很多情况还是需要气泡框来实现。

普通的气泡框比较容易,互联网搜索很容易搜到完整代码,这里放上作者参考的博客地址。

后来,在实际应用中,想要展示移动中模型的信息,于是对气泡框组件进行修改,实现气泡框跟随飞行。

本文包括气泡框核心代码、跟随气泡框核心代码和在线示例三部分。


Cesium 气泡框

作者基于以下工具类进行修改,实现气泡框功能:


var BaseEvent = function () {
  this.handles = {}
  this.cached = []
}
BaseEvent.prototype.on = function (eventName, callback) {
  if (typeof callback !== 'function') return

  if (!this.handles[eventName]) {
    this.handles[eventName] = []
  }
  this.handles[eventName].push(callback)

  if (this.cached[eventName] instanceof Array) {
    //说明有缓存的 可以执行
    callback.apply(null, this.cached[eventName])
  }
}

BaseEvent.prototype.emit = function () {
  if (this.handles[arguments[0]] instanceof Array) {
    for (let i = 0; i < this.handles[arguments[0]].length; i++) {
      this.handles[arguments[0]][i](arguments[1])
    }
  }
  //默认缓存
  this.cached[arguments[0]] = Array.prototype.slice.call(arguments, 1)
}

// 气泡框类
var CesiumPopup = (function () {
  // 容器
  var _panelContainer = null
  var _contentContainer = null
  var _closeBtn = null

  var _renderListener = null
  var _viewer = null
  
  var CesiumPopup = function (options) {
    //继承
    BaseEvent.call(this)

    this.className = options.className || ''
    this.title = options.title || ''
    this.offset = options.offset || [0, 0]

    // this.render = this.render.bind(this)
    this.closeHander = this.closeHander.bind(this)
  }

  CesiumPopup.prototype = new BaseEvent()
  CesiumPopup.prototype.constrctor = CesiumPopup
  
  // 添加气泡框,并且开启后处理改变气泡框位置
  CesiumPopup.prototype.addTo = function (viewer) {
    if (_viewer) this.remove()
    _viewer = viewer
    this.initPanle()
    //关闭按钮
    _closeBtn.addEventListener('click', this.closeHander, false)
    if (this.position) {
      _panelContainer.style.display = 'block'
      _renderListener = _viewer.scene.postRender.addEventListener(this.render, this)
    }

    return this
  }

  // 初始化气泡框
  CesiumPopup.prototype.initPanle = function () {
    var closeBtnIcon =
      '<svg t="1603334792546" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1328" width="32" height="32"><path d="M568.922 508.232L868.29 208.807a39.139 39.139 0 0 0 0-55.145l-1.64-1.64a39.139 39.139 0 0 0-55.09 0l-299.367 299.82-299.425-299.934a39.139 39.139 0 0 0-55.088 0l-1.697 1.64a38.46 38.46 0 0 0 0 55.09l299.48 299.594-299.424 299.48a39.139 39.139 0 0 0 0 55.09l1.64 1.696a39.139 39.139 0 0 0 55.09 0l299.424-299.48L811.56 864.441a39.139 39.139 0 0 0 55.089 0l1.696-1.64a39.139 39.139 0 0 0 0-55.09l-299.48-299.537z" p-id="1329"></path></svg>'

    _panelContainer = document.createElement('div')
    _panelContainer.classList.add('cesium-popup-panel')
    if (this.className && this.className !== '') {
      _panelContainer.classList.add(this.className)
    }

    _closeBtn = document.createElement('div')
    _closeBtn.classList.add('cesium-popup-close-btn')

    _closeBtn.innerHTML = closeBtnIcon

    // header container
    var headerContainer = document.createElement('div')
    headerContainer.classList.add('cesium-popup-header-panel')

    this.headerTitle = document.createElement('div')
    this.headerTitle.classList.add('cesium-poput-header-title')
    this.headerTitle.innerHTML = this.title

    headerContainer.appendChild(this.headerTitle)
    _panelContainer.appendChild(_closeBtn)

    _panelContainer.appendChild(headerContainer)

    // content container

    _contentContainer = document.createElement('div')
    _contentContainer.classList.add('cesium-popup-content-panel')
    _contentContainer.innerHTML = this.content

    _panelContainer.appendChild(_contentContainer)

    //tip container
    var tipContaienr = document.createElement('div')
    tipContaienr.classList.add('cesium-popup-tip-panel')

    var tipDiv = document.createElement('div')
    tipDiv.classList.add('cesium-popup-tip-bottom')

    tipContaienr.appendChild(tipDiv)

    _panelContainer.appendChild(tipContaienr)

    _panelContainer.style.display = 'none'
    // add to Viewer Container
    _viewer.cesiumWidget.container.appendChild(_panelContainer)
    this.emit('open')
  }

  // 设置气泡框内容
  CesiumPopup.prototype.setHTML = function (html) {
    if (_contentContainer) {
      _contentContainer.innerHTML = html
    }
    this.content = html
    return this
  }

  // 渲染气泡框
  CesiumPopup.prototype.render = function () {
    var geometry = this.position
    if (!geometry) return
    var position = Cesium.SceneTransforms.wgs84ToWindowCoordinates(_viewer.scene, geometry)
    if (!position) {
      return
    }
    if (_panelContainer) {
      _panelContainer.style.left = position.x - _panelContainer.offsetWidth / 2 + this.offset[0] + 'px'
      _panelContainer.style.top = position.y - _panelContainer.offsetHeight - 10 + this.offset[1] + 'px'
    }
  }

  // 设置气泡框位置
  CesiumPopup.prototype.setPosition = function (cartesian3) {
    this.position = cartesian3
    return this
  }

  // 修改气泡框样式
  CesiumPopup.prototype.addClassName = function (className) {
    if (_panelContainer) {
      _panelContainer.classList.add(className)
    }
    return this
  }

  // 移除气泡框样式
  CesiumPopup.prototype.removeClass = function (className) {
    if (_panelContainer) {
      _panelContainer.classList.remove(className)
    }
    return this
  }

  // 设置气泡框标题
  CesiumPopup.prototype.setTitle = function (title) {
    this.headerTitle.innerHTML = title

    return this
  }

  // 气泡框偏移
  CesiumPopup.prototype.setOffset = function (offset) {
    this.offset = offset
    return this
  }

  CesiumPopup.prototype.closeHander = function () {
    this.remove()
  }

  // 移除气泡框
  CesiumPopup.prototype.remove = function () {
    _closeBtn.removeEventListener('click', this.closeHander, false)

    if (_closeBtn) {
      _closeBtn.parentNode.removeChild(_closeBtn)
      _closeBtn = null
    }

    if (_contentContainer) {
      _contentContainer.parentNode.removeChild(_contentContainer)
      _contentContainer = null
    }

    if (_panelContainer) {
      _panelContainer.parentNode.removeChild(_panelContainer)
      _panelContainer = null
    }

    if (_renderListener) {
      _renderListener()
      _renderListener = null
    }

    if (_viewer) {
      _viewer = null
    }
    this.emit('close')
  }

  return CesiumPopup
})()


css 样式代码:

/* pop框css*/
.cesium-popup-panel {
  opacity: 0.8;
  width: 312px;
  position: absolute;
  z-index: 999;
  color: #00fcf9;

  background: rgba(23, 50, 108, 0.6);
  border: 1px solid #4674d6;
}

.cesium-popup-tip-panel {
  width: 40px;
  height: 20px;
  position: absolute;
  left: 50%;
  bottom: -20px;
  margin-left: -20px;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.8;
}

.cesium-popup-tip-bottom {
  width: 17px;
  background: rgba(23, 50, 108, 0.8);
  border-bottom: 1px solid #4674d6;
  height: 17px;
  padding: 1px;
  margin: -10px auto 0;
  -webkit-transform: rotate(45deg);
  -moz-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  transform: rotate(45deg);
}

.cesium-popup-header-panel {
  /* display: flex; */
  /* justify-content: space-between; */
  align-items: center;
  font-size: 14px;
  padding: 5px 15px;
  background: rgba(23, 50, 108, 0.8);

  border-bottom: 1px solid #4674d6;
}

.cesium-poput-header-title {
  font-size: 16px;
  font-family: Microsoft YaHei;
  font-weight: 400;
  color: #ffffff;
}

.cesium-popup-content-panel {
  padding: 18px;
}

.cesium-popup-close-btn {
  float: right;
  position: relative;
  right: 10px;
}

.cesium-popup-close-btn,
.cesium-popup-close-btn:focus {
  cursor: pointer;
}

cesium-popup-close-btn>svg:hover {
  color: #00fcf9 !important;
}

.cesium-popup-close-btn>svg {
  user-select: auto;
  color: #4674d6;
  cursor: pointer;
  width: 15px;
  /* height: 15px; */
}

跟随气泡框

气泡框跟随原理也比较简单,即利用渲染器获取 entity 的实时位置即可。

/**
 * @todo 获取中心点
 * @param entity
 * @param viewer
 * @return {Cartesian3}
 */
static getCenter(entity, viewer){

    let positions;
    // 获取当前时间,优先获取 viewer 时间
    const timeTemp = viewer? viewer.clock.currentTime: JulianDate.now();
    if (entity.polygon) {
        // 先获取范围,在获取中心点
        const temp = entity.polygon.hierarchy.getValue(timeTemp).positions;
        positions = temp && BoundingSphere.fromPoints(temp).center;
    } else if (entity.polyline) {
        // 先获取范围,在获取中心点
        const temp = entity.polyline.positions.getValue(timeTemp);
        positions = temp && BoundingSphere.fromPoints(temp).center;
    } else if (entity.point) {
        // 获取点的位置
        positions = entity.position._value;
    } else {
        // 如果是移动对象(一般为模型),则获取实时位置
        positions = entity._position && entity._position.getValue(timeTemp);
    }
    // 中心点
    return positions;
}

// 开启渲染后处理
this._renderListener = this._viewer.scene.postRender.addEventListener(this.render, this);

// 渲染方法
render() {
	// 获取实时位置坐标
	const geometry = CesiumUtil.getCenterByEntity(this.entity, this._viewer);
	// 获取屏幕坐标
	const cartesian2 = new Cartesian2();
	const position = this._viewer.scene.cartesianToCanvasCoordinates(geometry, cartesian2); // 笛卡尔坐标到画布坐标
	
	// 改变气泡框容器位置
	if (this._panelContainer) {
	    this._panelContainer.style.left =
	        position.x - this._panelContainer.offsetWidth / 2 + this.offset[0] + 'px';
	    this._panelContainer.style.top =
	        position.y - this._panelContainer.offsetHeight - 10 + this.offset[1] + 'px';
	}
}

在这里插入图片描述

在线示例

示例中展示了,气泡框跟随飞机飞行。

三维展示气泡框跟随模型效果


参考博客:

Cesium自定义Popup框

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/642042.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mysql数据库基础和增删改查操作(每一次「欢喜」都值得纪念)

文章目录 一、数据库基本概念数据表数据库数据库管理系统(DBMS)数据库系统 二、数据库类型和常用数据库1.关系型数据库2.非关系型数据库 三、数据库的数据类型四、SQL语句1.简介2.分类 五、SQL语句的使用1.数据库操作&#xff08;1&#xff09;创建数据库 2.数据库表操作&#…

【笔试强训选择题】Day24.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目录…

窥探系列之数字证书

资料 华为数字证书解读 彻底搞懂HTTPS的加密原理 解读 使用过程 首先&#xff0c;证书持有者拥有一对公私钥&#xff1b;CA机构验证证书申请人身份后&#xff0c;使用CA机构私钥(属于根证书&#xff0c;一般浏览器保有CA的根证书公钥&#xff09;对申请人的基本信息及公钥进…

如何管理与改进低质供应商?帮助供应商提升自身质量能力?

管理和改进低质量的供应商可能是一项具有挑战性的任务&#xff0c;但与他们合作以提高他们的能力是可能的。可以采取一些步骤来管理和帮助提高供应商的质量&#xff1a; 确定根本原因&#xff1a;了解供应商产品或服务质量低下的原因。对他们的流程、资源和能力进行全面评估&am…

测试不到3年,来面试开口要25K,面完连10K都不想给

前言 我的好朋友兼大学同学老左家庭经济情况不错&#xff0c;毕业之后没两年自己存了点钱加上家里的支持&#xff0c;自己在杭州开了一家网络公司。由于公司不是很大所以公司大部分的测试人员都是自己面试的&#xff0c;近期公司发展的不错&#xff0c;打算扩招也面试了不少人…

聊一聊近期测试行情以及个人的感受

众所周知&#xff0c;去年年底的裁员潮再加上今年的疫情影响&#xff0c;失业、找工作成为了蛮多人的当务之急。最近一些招聘网站也出现被刷爆的情况&#xff0c;其中顺利找到工作的并不多&#xff0c;说明行情很冷&#xff0c;但是总有许多人顺利跳槽。 其实对于大牛来说&…

【深度学习】日常笔记4

softmax(o)给出的分布⽅差&#xff0c;并与softmax交叉熵损失l(y, yˆ)的⼆阶导数匹配。 Softmax函数可以将一组任意实数值转换为一个概率分布&#xff0c;它的输出值是各个类别的概率估计。如果我们用softmax函数得到的概率分布与真实分布非常相似&#xff0c;那么交叉熵损失…

Vue 项目使用 ECharts 使用路由或者点击浏览器前进/后退切换页面回来图表不显示

问题描述 问题1&#xff1a; 解决方案对应方案1 在 Vue 项目中使用 ECharts 时&#xff0c;一开始图表能渲染出来&#xff0c;当切换到其他页面再回来的时候&#xff0c;图表就不显示了 问题2&#xff1a; 解决方案对应方案2 Echarts 图表在子组件中&#xff0c;父组件通过传递…

【正点原子STM32连载】 第三十一章 内部温度传感器实验摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第三十…

【多线程进阶】JUC下的常用类

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 ReentrantLock Semaphore(信号量) 计数器——…

阿里发布Java岗春招面试总结(复盘版)GitHub一经上传火爆全网!

Java面试都只是背答案吗? Java 八股文面试当然要背&#xff0c;不背就通过不了面试。 因为经济环境的问题&#xff0c;互联网行业竞争越来越严峻&#xff0c;面试也是越来越难。需要我们掌握的技术也是越来越多、要求是越来越高。作为一个资深的互联网码农&#xff0c;今天给…

公司招人面了一个00后测试,可以说是内卷届的天花板.....

公司前段缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资也不低&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。令我印象最深的是一个00后测试员&#xff0c;他…

cpu、gpu的区别

1、CPU和GPU的内部构成 如上图所示&#xff0c;CPU和GPU的内部构成&#xff0c;可以看出是由不同的架构所组成 CPU&#xff1a;叫做中央处理器&#xff08;central processing unit&#xff09;作为计算机系统的运算和控制核心&#xff0c;是信息处理、程序运行的最终执行单元…

Splashtop 荣获两项“TrustRadius 2023年最受欢迎奖”

2023年5月10日 加利福尼亚州库比蒂诺 Splashtop 在简单易用的随处办公远程解决方案领域处于领先地位&#xff0c;该公司自豪地宣布&#xff0c;在远程桌面和远程支持这两个类别中荣获“TrustRadius 2023年最受欢迎奖”。这项知名奖项完全基于用户评论和客户情绪&#xff0c;强…

嵌入式软件实现定时器的两种方式

简介 在一般的嵌入式产品设计中&#xff0c;介于成本、功耗等&#xff0c;所选型的MCU基本都是资源受限的&#xff0c;而里面的定时器的数量更是有限。在我们软件设计中往往有多种定时需求&#xff0c;例如脉冲输出、按键检测、LCD切屏延时等等 &#xff0c;我们不可能让每一个…

JavaWeb__VUE

文章目录 概述快速入门Vue指令v-bindv-modelv-on条件判断v-for 概述 Vue是一套前端框架&#xff0c;免除原生JavaScript中的DOM操作&#xff0c;简化书写 基于MVVM(Model-View-ViewModel)思想&#xff0c;实现数据的双向绑定&#xff0c;将编程的关注点放在数据上。 Model是数…

ArduPilot之H743快速加减油门抬头现象分析

ArduPilot之H743快速加减油门抬头现象分析 1. 源由2. 现象分析3. 改善方法4. 验证5. 总结6. 参考资料 1. 源由 最近一直折腾再ArduCopter快速加减油四轴模型抬头的问题。 具体情况详见下面视频&#xff1a; 猛加油&#xff0c;机头后仰&#xff0c;然后点头&#xff1b; 快速…

一文搞清楚 Docker 镜像、容器、仓库

博主介绍&#xff1a; ✌博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家✌ Java知识图谱点击链接&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; &#x1f495;&#x1f495; 感兴趣的同学可以收…

【服务器数据恢复】EXT3文件系统下Oracle数据库数据恢复案例

服务器数据恢复环境&#xff1a; 华为OceanStor某型号存储&#xff0c;十几块FC硬盘组建一组RAID5磁盘阵列&#xff0c;配备了一块热备盘&#xff1b;上层使用EXT3文件系统&#xff0c;配置了oracle数据库。 服务器故障&#xff1a; 该存储RAID5中的一块硬盘未知原因离线&…

从软件测试到自动化测试,待遇翻倍,我整理的超全学习指南!

因为我最近在分享自动化测试技术&#xff0c;经常被问到&#xff1a; 功能测试想转自动化&#xff0c;请问应该怎么入手&#xff1f;有没有好的资源推荐&#xff1f; 那么&#xff0c;接下来我就结合自己的经历聊一聊我是如何在工作中做自动化测试的。&#xff08;学习路线和…