前言

低碳工业园区的建设与推广是我国推进工业低碳转型的重要举措,低碳工业园区能源与碳排放管控平台是低碳工业园区建设的关键环节。如何对园区内的企业的能源量进行采集、计量、碳排放核算,如何对能源消耗和碳排放进行实时动态监测等问题,涉及多个技术领域,专业性强。其数据不仅要求准确,更要求真实可靠(即可核查、可溯源)。这是低碳工业园区“管控平台”建设的核心任务,也是当前我国工业园区建设中需要迫切解决的主要问题之一。

http://www.hightopo.com/demo/HTBuilding/index.html

这个 gif 图中显示的是一个 2D 3D 结合而成的低碳工业园区的能源监控系统,主要对各个楼宇以及园区整体的水、电等的使用量的实时监控。

代码实现

搭建场景

要创建出一个 3D 的低碳工业园区场景并不难,但是如何在同一个界面上同时显示 2D 和 3D 的场景呢?想要做出炫酷的效果,这种方式在很多情况下是非常有用的。

整个低碳工业园区的场景是搭建在 2D 上的,我们知道,HTML 给 DOM 元素设置图片只能用传统的栅格位图,但是如果怕图片被拉伸而导致图片模糊或者变形等结果,用 json 格式的矢量图片来实现是最好的,栅格位图在拉伸放大或缩小时会出现图形模糊,线条变粗出现锯齿等问题。 而矢量图片通过点、线和多边形来描述图形,因此在无限放大和缩小图片的情况下依然能保持一致的精确度。

首先我搭建了一个 2D 的场景用来放置我们的 json 矢量图,利用 ht.Default.xhrLoad 函数将 json 矢量背景图反序列化显示在 gv 上,这个 json 矢量背景图中除了作为背景的 node 还有另外两个节点,如下图,红线框起来的比较大的这个节点是用来装 3D 场景的,而右边框起来的比较小的节点是用来放置另外一个 gv 的(暂时还用不到,后期需要添加类似 form 表单的功能,所以我需要固定位置):

ht.Default.xhrLoad('displays/background.json', function(text) {
dm.deserialize(text);// 反序列化数据到数据模型 gv.addToDOM();// 将 2D 场景添加到 body 体中
});

这个 2D 场景作为背景的部分就设置完毕,接下来看看如何在 2D 场景的基础下放上 3D 场景。

2D 中添加 3D 场景

向 2D 中添加 3D 也是非常容易,问题是如何使 3D 场景根据 2D 场景缩放和平移来进行自适应变化,使 3D 场景始终保持在 2D 场景的某个固定的位置?我是通过监听 gv 的属性变化事件,监听到 zoom、translate 等属性,对 3D 场景进行自动布局的操作:

var g3dInfo = create3D('g3dNode');
gv.mp(function(e) {// 监听 gv 的属性变化事件
if (e.property === 'zoom' || e.property === 'translateX' || e.property === 'translateY' ) {
layout(g3dInfo);
}
}); function layout(info) {
var rect = info.node.getRect(),// 获取场景依赖的节点的 矩形区域
zoom = gv.getZoom(),// 获取当前 gv 的缩放值
tx = gv.tx(),// 获取当前 gv 的水平平移值
ty = gv.ty();// 获取当前 gv 的垂直平移值 // 依赖的节点的大小根据 zoom 缩放值来进行缩放
rect.x *= zoom,
rect.y *= zoom,
rect.width *= zoom,
rect.height *= zoom; var x = rect.x + tx,
y = rect.y + ty; // 设置场景自动布局
if (info.g3d) info.g3d.layout(x, y, rect.width, rect.height);
}

眼尖的同学应该已经注意到了,我没有写出 create3D 函数的声明,就展示的效果而言,这个方法只是将场景 json 图纸反序列化到 3D 场景中,并追加了一个对象 info,将 3D 场景所依赖的 node 和 3D 场景的变量传进去:

function create3D(tag) {
var g3d = new ht.graph3d.Graph3dView();// 3D 组件
var dataModel = g3d.dm();// 获取 3D 场景的数据容器
gv.getView().appendChild(g3d.getView());// 将 3D 场景添加到 2D 场景中
ht.Default.xhrLoad('scenes/电云维.json', function(text) {// 加载 3D 场景的 json 矢量图纸
dataModel.deserialize(text);// 反序列化数据到数据模型
}); // 停止事件的传播,阻止它被分派到其他 Document 节点
g3d.getView().addEventListener('mousedown', function(e) { e.stopPropagation()});
g3d.getView().addEventListener('mousewheel', function(e) { e.stopPropagation()});
if (isFirefox=navigator.userAgent.indexOf("Firefox") > 0) {
g3d.getView().addEventListener('DOMMouseScroll', function(e) { e.stopPropagation()});
} var info = {
g3d: g3d,
node: dm.getDataByTag(tag),
};
return info;
}

2D 和 3D 在鼠标事件上有很多相同的点,但是我们并不希望在操作 3D 场景的同时 2D 场景也跟着变化,所以上面代码中禁止了鼠标按下和滚轮的事件传播。

楼宇信息显示

低碳工业园区监控系统实现的其中一个功能:点击楼宇视线移到楼宇显示到一个比较合适的位置,并且楼宇顶部显示一个面板用来展示当前楼宇的信息。这里我直接创建了一个节点,通过设置节点的 shape3d 属性为 billboard 即可显示为一个“面片”,面板非常好用,首先它只有一个面,在 3D 场景中如果需要大量的显示数据的节点,推荐用这个 billboard 类型,非常省性能。

// 创建在建筑上面的显示面板
var billboard = new ht.Node();
billboard.setScaleX(2);// 将节点 X 轴上放大 2 倍
billboard.setScaleTall(2);// 将节点 Y 轴上放大 2 倍
billboard.s({
'shape3d': 'billboard',// 此类型为一个面片
'shape3d.image': 'symbols/nodeForm.json',// 设置面片的显示图片为矢量图片
'shape3d.autorotate': true,// 始终面向相机
'shape3d.vector.dynamic': true,// 设置矢量图形
'3d.visible': false// 不可见
});
billboard.setTag('billboard');// 设置节点的 tag 唯一属性
dataModel.add(billboard);// 将节点添加到数据容器中

通过点击不同的楼宇则将信息面板展示在当前点击的楼宇上方, 并根据不同的选中情况对 billboard 进行显隐的控制:

dataModel.sm().ms(function(e) {// 监听选中变化事件
if (e.kind === 'set' || e.kind === 'append') {// 设置选中 及 追加选中
billboard.s('3d.visible', true);
var data = dataModel.sm().ld();// 获取当前选中的最后一个节点
if (!data) return;
billboard.p3(data.getPosition().x, data.getTall() + 200, data.getPosition().y);// 设置 billboard 的位置为当前选中的节点的上方
}
else if (e.kind === 'remove') {// 选中移除
var data = dataModel.sm().ld();// 获取当前最后选中的节点
if (data) {
billboard.setPosition(data.getPosition().x, data.getPosition().y);
billboard.setElevation(data.getTall() + 200);
}
else billboard.s('3d.visible', false);
}
else if (e.kind === 'clear') billboard.s('3d.visible', false);// 清除所有的选中后设置 billboard 不可见
});

(其他例子参考)

http://www.hightopo.com/demo/large-screen-photovoltaic/

至于点击楼宇,从当前视线位置推到节点位置是通过 flyTo 函数,此函数在 6.2.2  版本是有三个参数,参数一为目标节点,参数二为是否动画,参数三为眼睛跟目标节点中心距离的计算,比如下面代码设置 0.5,表示眼睛在上述方向上动态计算距离以将目标适配到屏幕 0.5 里容纳。信息面板上方显示了当前点击的楼宇的名称,我是在设计 3D 场景的图纸时给对应的楼宇设置上 displayName 属性,当前显示则根据这个 displayName 来进行显示。

g3d.mi(function(e) {// 增加交互事件监听器
if(e.kind === 'clickData'){
g3d.flyTo(e.data, true, 0.5);// 将 eye 和 center 从当前位置“飞到”目标节点的位置 第二个参数若是1 则占满全屏。 6.2.2 版本以上有此方法
var name = e.data.getDisplayName(); // 由于 3D 中不能将模型组合到一起,所以我用追加选中的方法来解决
dataModel.each(function(node) {
if(node.getDisplayName() !== name) return;// 我将同一类型的节点的 displayName 设置相同
dataModel.sm().appendSelection(node);
})
}
});

那么,只有一个 billboard,我们如何让这个 billboard 根据不同的楼宇显示不同的信息?这个时候矢量图标的优势又多了一个,通过对矢量图标中的某个部分进行数据绑定进行数据的动态变化,这边我三言两语也讲不完整,我就简单提一下如何实现,剩下的可以去官网中的数据绑定手册中查阅相关资料和具体实现。

前面给 billboard 设置了一个 shape3d.image 属性,设置的图片为 nodeForm.json,这个 json 中有四行文本显示,顶部的文本用来显示当前点击的楼宇的名称。

根据手册我们知道数据绑定的格式分为两种,一种是绑定 function 类型,另一种是绑定 string 类型,如下:

也就是说如果 HT 中没有定义我们需要的属性或者说一个矢量图上有多个相同的属性需要更改为不同的值,就可以通过 attr 来自定义属性,这里我用的就是这个方法:

"text": {
"func": "attr@buildingName",
"value": "赛普健身学院学生宿舍"
}

数据绑定完成后,我们只需要根据这个绑定数据对当前引用这个 json 矢量图标的节点的业务属性变化即可:

// 不同的楼宇上显示的内容不同
billboard.a('buildingName', name);
billboard.a('electricUsage', (Math.random()*300).toFixed(2));
billboard.a('waterUsage', (Math.random()*300).toFixed(2));
billboard.a('gasUsage', (Math.random()*300).toFixed(2));

右侧数据显示

3D 场景创建完毕,接下来如何在 3D 上面再加右边的两个数据显示面板?这里我是在前面 2D json 场景中已排布好位置的节点上添加了另外一个 2D 场景,用来显示整体场景数据。因为这个 gv 上有两个信息面板,所以我直接在 graphView 上添加了两个节点,并将节点添加到这个 graphView 的 dataModel 数据容器上,其他部分我就不再做解释了,都是基础的代码:

function createGV(tag) {
var g2d = new ht.graph.GraphView();// 2D 拓扑场景
var dataModel = g2d.dm();// 获取当前拓扑场景的数据容器
gv.getView().appendChild(g2d.getView());// 将此拓扑场景添加到底层背景图上
g2d.setInteractors([]);// 清除此组件上的交互 // 添加两个节点到拓扑场景上
var node = new ht.Node();
node.setImage('symbols/form.json');
node.setPosition(0, 0);
dataModel.add(node); var node1 = new ht.Node();
node1.setImage('symbols/form1.json');
node1.setPosition(0, dm.getDataByTag(tag).getHeight()/3);
dataModel.add(node1); g2d.fitContent(); setInterval(function() {// form表单数据动态变化
node.a('electricUse', (Math.random()*300).toFixed(2));
node.a('waterUse', (Math.random()*300).toFixed(2));
node.a('gasUse', (Math.random()*300).toFixed(2));
node.a('tempUse', (RandomNumBoth(10, 40))+'');
node.a('wetUse', (Math.floor((Math.random()*100)))+'');
}, 3000); var info = {
g2d: g2d,
node: dm.getDataByTag(tag)
}
return info;
}

以上,整个低碳工业园区监控系统的实现全部结束,有问题的或者建议都可以给我留言,或者直接访问官网(http://hightopo.com/)查阅对应的资料。

基于 HTML5 WebGL 的低碳工业园区监控系统的更多相关文章

  1. 基于 H5与webGL 的低碳工业园区监控系统

    前言 低碳工业园区的建设与推广是我国推进工业低碳转型的重要举措,低碳工业园区能源与碳排放管控平台是低碳工业园区建设的关键环节.如何对园区内的企业的能源量进行采集.计量.碳排放核算,如何对能源消耗和碳排 ...

  2. 基于 HTML5 WebGL 的地铁站 3D 可视化系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...

  3. 基于 HTML5 + WebGL 实现 3D 可视化地铁系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...

  4. 基于 HTML5 + WebGL 的地铁 3D 可视化系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...

  5. 基于 HTML5 WebGL 的智慧楼宇可视化系统

    前言 可视化的智慧楼宇在 21 世纪是有急迫需求的,中国被世界称为"基建狂魔",全球高层建筑数量位居首位,所以对于楼宇的监控是必不可少.智慧楼宇可视化系统更多突出的是管理方面的功能 ...

  6. 基于 HTML5 WebGL 的发动机 3D 可视化系统

    前言     工业机械产品大多体积庞大.运输成本高,在参加行业展会或向海外客户销售时,如果没有实物展示,仅凭静态.简单的图片说明书介绍,无法让客户全面了解产品,不仅工作人员制作麻烦,客户看得也费力.如 ...

  7. 基于 HTML5 WebGL 的虚拟现实可视化培训系统

    前言 2019 年 VR, AR, XR, 5G, 工业互联网等名词频繁出现在我们的视野中,信息的分享与虚实的结合已经成为大势所趋,5G 是新一代信息通信技术升级的重要方向,工业互联网是制造业转型升级 ...

  8. 基于 HTML5 WebGL 的高炉炼铁厂可视化系统

    前言       在当今 工业4.0 新时代的推动下,不仅迎来了 工业互联网 的发展,还开启了 5G 时代的新次元.而伴随着带宽的提升,网络信息飞速发展,能源管控上与实时预警在工业互联网中也占着举足轻 ...

  9. 基于 HTML5 WebGL 的 3D SCADA 主站系统

    这个例子的初衷是模拟服务器与客户端的通信,我把整个需求简化变成了今天的这个例子.3D 的模拟一般需要鹰眼来辅助的,这样找产品以及整个空间的概括会比较明确,在这个例子中我也加了,这篇文章就算是我对这次项 ...

随机推荐

  1. FFMPEG结构体分析:AVCodec

    注:写了一系列的结构体的分析的文章,在这里列一个列表: FFMPEG结构体分析:AVFrame FFMPEG结构体分析:AVFormatContext FFMPEG结构体分析:AVCodecConte ...

  2. C++中的虚函数表是什么时期建立的?

    虚函数表是在什么时期建立的? 最近参加阿里巴巴公司的内推,面试官问了“虚函数表是在什么时期建立的?”.因为以前对虚函数表的理解不够多,所以就根据程序构建(Build)的四个过程(预编译.编译.汇编和链 ...

  3. cocos2d-x 游戏开发之有限状态机(FSM) (四)

    cocos2d-x 游戏开发之有限状态机(FSM) (四) 虽然我们了解了FSM,并且可以写自己的FSM,但是有更好的工具帮我们完成这个繁琐的工作.SMC(http://smc.sourceforge ...

  4. iOS监听模式系列之对APNs的认知与理解

    前言: APNs 协议在近两年的 WWDC 上改过两次, 15 年 12 月 17 日更是推出了革命性的新特性.但在国内传播的博客.面试题里关于 APNs 的答案全都是旧的.错的. 导航: 对 APN ...

  5. 【Qt编程】基于Qt的词典开发系列<四>--无边框窗口的缩放与拖动

    在现在,绝大多数软件都向着简洁,时尚发展.就拿有道的单词本和我做的单词本来说,绝大多数用户肯定喜欢我所做的单词本(就单单界面,关于颜色搭配和布局问题,大家就不要在意了). 有道的单词本: 我所做的单词 ...

  6. Java反编译工具(Java Decompiler)

    Java Decompiler是一种非常实用的JAVA反编译工具,可以对整个jar包进行反编译,也可以将其集成到eclipse上,非常方便的根据class文件的源码.,官网地址http://jd.be ...

  7. Android平台的Swift—Kotlin

    WeTest 导读 Kotlin 已经出来较长一段时间了,有些同学已经对Kotlin进行了深入的学习,甚至已经运用到了自己的项目当中,但是还有较多同学可能只是听过Kotlin或简单了解过,这篇文章的目 ...

  8. DB2常用命令小结

    PS:执行命令前需要进入DB2的账户下:su db2inst1 修改密码:更改相应的操作系统密码即可,windows上可以更改db2admin的密码,linux上更改db2inst1的密码即可,db2 ...

  9. 浅入javascript正则表达式的规则.

    今天在看jQuery源码的时候,到处都是正则的用法,一气之下就狠下心来.重新回顾了一下正则.下面是做的笔记.非常浅的入门. /* i:表示不区分大小写 g:表示可以全局配置 m:表示可以多行配置 */ ...

  10. for循环嵌套讲解:

    1.for循环嵌套讲解: class ForForDemo {     public static void main(String[] args)     {         //大圈套小圈思想: ...